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 "BlueprintErrors.h"
#include "StringDecoder.h"
#include "Internationalization/TextFormatter.h" #include "Internationalization/TextFormatter.h"
#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetSystemLibrary.h"
#include "Kismet2/KismetDebugUtilities.h" #include "Kismet2/KismetDebugUtilities.h"
@@ -209,6 +210,15 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTra
return Result; 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 UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name)
{ {
FFormatArgumentData Result; FFormatArgumentData Result;

View File

@@ -14,6 +14,8 @@
#include "BlueprintErrors.generated.h" #include "BlueprintErrors.generated.h"
class UlxLuaValues;
/* /*
* enum class ElxLogVerbosity, below, contains all the same error severity levels * enum class ElxLogVerbosity, below, contains all the same error severity levels
* as ELogVerbosity, but in a form that the blueprint editor can manipulate. * 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") UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name); 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. /* 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 "LockedWrapper.h"
#include "DebugPrint.h" #include "LuprexGameModeBase.h"
#include "lpx-drvutil.hpp" #include "lpx-drvutil.hpp"
#include "lpx-paths.hpp" #include "lpx-paths.hpp"
using namespace CommonTypes; 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() { void FlxLockedWrapper::InitWrapper() {
if (Lockable.Wrapper.play_initialize != nullptr) { if (Lockable.Wrapper.play_initialize != nullptr) {
// Already initialized. // Already initialized.
return; return;
} }
FString dll((const UTF8CHAR*)LUPREX_DLL_PATH); 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); void* DLL = FPlatformProcess::GetDllHandle(*dll);
if (DLL != nullptr) { if (DLL != nullptr) {
using InitFn = void (*)(EngineWrapper*); using InitFn = void (*)(EngineWrapper*);
InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper")); InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper"));
if (init != nullptr) { if (init != nullptr) {
init(&Lockable.Wrapper); init(&Lockable.Wrapper);
Lockable.Wrapper.hook_dprint(DebugPrint::DPrint); Lockable.Wrapper.hook_dprint(DPrintHook);
} }
} }
} }
@@ -27,16 +34,9 @@ void FlxLockedWrapper::InitWrapper() {
FString FlxLockedWrapper::FetchStdout() { FString FlxLockedWrapper::FetchStdout() {
uint32_t ndata; const char* data; uint32_t ndata; const char* data;
Lockable.Wrapper.get_outgoing(Get(), 0, &ndata, &data); Lockable.Wrapper.get_outgoing(Get(), 0, &ndata, &data);
FString result(ndata, (const UTF8CHAR *)data);
if (ndata == 0) { Lockable.Wrapper.play_sent_outgoing(Get(), 0, ndata);
return FString(); return result;
}
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]));
} }
int64 FlxLockedWrapper::GetActor() { int64 FlxLockedWrapper::GetActor() {

View File

@@ -4,6 +4,7 @@
#include "lpx-enginewrapper.hpp" #include "lpx-enginewrapper.hpp"
#include "CommonTypes.h" #include "CommonTypes.h"
// Class FlxLockableWrapper // Class FlxLockableWrapper
// //
// Contains the EngineWrapper and a Mutex to lock it. // Contains the EngineWrapper and a Mutex to lock it.
@@ -31,11 +32,17 @@ class FlxLockedWrapper {
private: private:
FlxLockableWrapper& Lockable; 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: public:
// Import these types into our Namespace. // Import these types into our Namespace.
using IdArray = CommonTypes::IdArray; using IdArray = CommonTypes::IdArray;
using IdView = CommonTypes::IdView; using IdView = CommonTypes::IdView;
using StringViewVec = CommonTypes::StringViewVec; using StringViewVec = CommonTypes::StringViewVec;
public: public:
// The constructor of the FlxLockedWrapper claims the mutex. // The constructor of the FlxLockedWrapper claims the mutex.

View File

@@ -1,6 +1,8 @@
#include "LuaCall.h" #include "LuaCall.h"
#include "LuprexGameModeBase.h" #include "LuprexGameModeBase.h"
#include "StringDecoder.h"
#include "EdGraphSchema_K2.h" #include "EdGraphSchema_K2.h"
static void FatalBlueprintError(const TCHAR *message) { 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); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
mode->LuaCallBegin(); 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); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); 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); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); CheckNotEmpty(sb);
mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); mode->LuaCallEnd(InvocationKind::LUA_PROBE, place);
} }
UlxLuaValues *UlxLuaCallLibrary::LuaCallGetRest(UObject *context)
{
// ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
// ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult();
// FlxStreamBuffer &sb = mode->LuaCallGetResult(); UlxLuaValues *Values = NewObject<UlxLuaValues>(context);
// if (sb.empty()) return ELpxSimpleDynamicTag::None; if (!Values->Initialize(sb.view()))
// int64_t total_reads = sb.total_reads(); {
// SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); UE_LOG(LogBlueprint, Error, TEXT("Lua call returned corrupt data"));
// sb.unread_to(total_reads); return nullptr;
// switch (tag) { }
// case SimpleDynamicTag::STRING: return ELpxSimpleDynamicTag::String; return Values;
// 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;
// }
// }
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// //

View File

@@ -2,8 +2,10 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphPin.h"
#include "LuaCall.generated.h" #include "LuaCall.generated.h"
class UlxLuaValues;
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// //
@@ -119,6 +121,9 @@ public:
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function")
static void LuaCallProbe(UObject *context, AActor *Place); 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. // Functions that pack arguments into the call buffer.
// //

View File

@@ -2,6 +2,7 @@
#include "LuaCallNode.h" #include "LuaCallNode.h"
#include "StringDecoder.h"
#include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h" #include "BlueprintNodeSpawner.h"
@@ -40,59 +41,41 @@
#define LOCTEXT_NAMESPACE "LuaCall" #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:" // 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]; TCHAR pname[FName::StringBufferSize];
Pin->PinName.ToString(pname); Pin->PinName.ToString(pname);
return pname[0] == 'A' && pname[1] == ':'; return pname[0] == Prefix && pname[1] == ':';
} }
static FName ArgumentNameAddPrefix(const FString &name) { FName UK2Node_LuaCall::AddPrefix(const FString &Name, char Prefix)
FString Prefixed = FString("A:") + name; {
TCHAR PrefixChars[3];
PrefixChars[0] = Prefix;
PrefixChars[1] = ':';
PrefixChars[2] = 0;
FString Prefixed = PrefixChars + Name;
return FName(*Prefixed); return FName(*Prefixed);
} }
static FString ArgumentNameRemovePrefix(const FName &name) { FString UK2Node_LuaCall::RemovePrefix(FName Name, char Prefix)
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) {
TCHAR pname[FName::StringBufferSize]; TCHAR pname[FName::StringBufferSize];
Pin->PinName.ToString(pname); Name.ToString(pname);
return pname[0] == 'R' && pname[1] == ':'; 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) static FEdGraphPinType GetPinType(const FProperty *Property)
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
@@ -107,7 +90,6 @@ static FEdGraphPinType GetPinType(const FProperty *Property)
} }
UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer) UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) : Super(ObjectInitializer)
{ {
@@ -154,29 +136,6 @@ void UK2Node_LuaCall::CreateCorrectPins()
LuaFunctionPrototype = TEXT("class.func(int arg1, int arg2) : int ret1, int ret2"); 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. // Parse the lua function prototype.
FlxParsedProto ParsedProto(LuaFunctionPrototype); FlxParsedProto ParsedProto(LuaFunctionPrototype);
if (!ParsedProto.ErrorMessage.IsEmpty()) 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); 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. // Transfer all pins to the old pins map and clear the pin list.
TMap<FName, UEdGraphPin *> OldArgumentPins; TMap<FName, UEdGraphPin *> OldPins;
TMap<FName, UEdGraphPin *> OldReturnValuePins;
for (auto It = Pins.CreateIterator(); It; ++It) for (auto It = Pins.CreateIterator(); It; ++It)
{ {
UEdGraphPin* CheckPin = *It; UEdGraphPin* CheckPin = *It;
if (IsArgumentPin(CheckPin)) { OldPins.Add(CheckPin->PinName, CheckPin);
OldArgumentPins.Add(CheckPin->PinName, CheckPin); }
It.RemoveCurrent(); Pins.Empty();
}
if (IsReturnValuePin(CheckPin)) { // KeepPin is a function that moves a pin from the old pins
OldReturnValuePins.Add(CheckPin->PinName, CheckPin); // map back onto the pins list.
It.RemoveCurrent(); 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. // Create Argument pins in the correct order, reusing old pins where possible.
for (const FlxParsedProto::Pin & Pin : ParsedProto.Arguments) for (const FlxParsedProto::Pin & Pin : ParsedProto.Arguments)
{ {
FName PrefixedName = ArgumentNameAddPrefix(Pin.Name); FName PrefixedName = AddPrefix(Pin.Name, 'A');
UFunction *Accessor = UlxLuaCallLibrary::GetArgumentPacker(Pin.Type); UFunction *Accessor = UlxLuaCallLibrary::GetArgumentPacker(Pin.Type);
if (Accessor == nullptr) { if (Accessor == nullptr) {
bHasCompilerMessage = true; bHasCompilerMessage = true;
@@ -214,11 +207,8 @@ void UK2Node_LuaCall::CreateCorrectPins()
continue; continue;
} }
FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Value"))); FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Value")));
UEdGraphPin **OldPin = OldArgumentPins.Find(PrefixedName); if (!KeepPin(PrefixedName, PinType))
if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { {
Pins.Emplace(*OldPin);
OldArgumentPins.Remove(PrefixedName);
} else {
CreatePin(EGPD_Input, PinType, PrefixedName); 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. // Create ReturnValue pins in the correct order, reusing old pins where possible.
for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues) for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues)
{ {
FName PrefixedName = ReturnValueNameAddPrefix(Pin.Name); FName PrefixedName = AddPrefix(Pin.Name, 'R');
UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type); UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type);
if (Accessor == nullptr) { if (Accessor == nullptr) {
bHasCompilerMessage = true; bHasCompilerMessage = true;
@@ -235,28 +225,27 @@ void UK2Node_LuaCall::CreateCorrectPins()
continue; continue;
} }
FEdGraphPinType PinType = GetPinType(Accessor->GetReturnProperty()); FEdGraphPinType PinType = GetPinType(Accessor->GetReturnProperty());
UEdGraphPin **OldPin = OldReturnValuePins.Find(PrefixedName); if (!KeepPin(PrefixedName, PinType))
if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { {
Pins.Emplace(*OldPin);
OldReturnValuePins.Remove(PrefixedName);
} else {
CreatePin(EGPD_Output, PinType, PrefixedName); 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. // Delete any unused pins.
for (auto &iter : OldArgumentPins) for (auto &iter : OldPins)
{ {
iter.Value->Modify(); iter.Value->Modify();
iter.Value->MarkAsGarbage(); iter.Value->MarkAsGarbage();
} }
for (auto &iter : OldReturnValuePins) OldPins.Empty();
{
iter.Value->Modify();
iter.Value->MarkAsGarbage();
}
OldArgumentPins.Empty();
OldReturnValuePins.Empty();
} }
@@ -274,7 +263,7 @@ FText UK2Node_LuaCall::GetPinDisplayName(const UEdGraphPin* Pin) const
} }
// Many pins can go unlabeled if they have default values. // 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) 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. // For argument pins, we must strip off the Argument Pin Prefix.
if (IsArgumentPin(Pin)) { if (IsPrefix(Pin, 'A'))
return FText::FromString(ArgumentNameRemovePrefix(Pin->PinName)); {
return FText::FromString(RemovePrefix(Pin->PinName, 'A'));
} }
// For return value pins, we must strip off the Return Value Pin Prefix. // For return value pins, we must strip off the Return Value Pin Prefix.
if (IsReturnValuePin(Pin)) { if (IsPrefix(Pin, 'R'))
return FText::FromString(ReturnValueNameRemovePrefix(Pin->PinName)); {
return FText::FromString(RemovePrefix(Pin->PinName, 'R'));
} }
// Otherwise, just return the Pin Name the normal way. // 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) void UK2Node_LuaCall::PinDefaultValueChanged(UEdGraphPin* Pin)
{ {
if(IsFunctionPin(Pin)) if(Pin->PinName == FunctionPinName)
{ {
LuaFunctionPrototype = Pin->DefaultValue; LuaFunctionPrototype = Pin->DefaultValue;
CreateCorrectPins(); CreateCorrectPins();
@@ -356,7 +347,7 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext,
// Add Packing operations for all argument pins. // Add Packing operations for all argument pins.
for (const FlxParsedProto::Pin &PinInfo : ParsedProto.Arguments) 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); UFunction *PackingFunc = UlxLuaCallLibrary::GetArgumentPacker(PinInfo.Type);
if (PackingFunc == nullptr) if (PackingFunc == nullptr)
{ {
@@ -378,10 +369,10 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext,
PrevNode->GetThenPin()->MakeLinkTo(ActionNode->GetExecPin()); PrevNode->GetThenPin()->MakeLinkTo(ActionNode->GetExecPin());
PrevNode = ActionNode; PrevNode = ActionNode;
// Add Unpacking operations for all argument pins. // Add Unpacking operations for all return value pins.
for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues) 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); UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type);
if (UnpackingFunc == nullptr) if (UnpackingFunc == nullptr)
{ {
@@ -395,16 +386,26 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext,
PrevNode = UnpackNode; PrevNode = UnpackNode;
} }
// Make sure we didn't have return values for an invoke. // If there is an extra results pin, hook it up.
if (IsInvoke && (ParsedProto.ReturnValues.Num() > 0)) 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. // Link up the Exec pins.
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin()); CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin());
CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *PrevNode->GetThenPin()); 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(); 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 bool UK2Node_LuaCall::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{ {
// The function pin cannot be connected. // 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(); OutReason = LOCTEXT("Error_FunctionPrototypeMustBeHardwired", "Lua function prototype must be a hardwired constant.").ToString();
return true; return true;
} }
// The invoke-or-probe pin cannot be connected. // 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(); OutReason = LOCTEXT("Error_InvokeOrProbeMustBeHardwired", "Invoke vs Probe must be a hardwired constant.").ToString();
return true; return true;

View File

@@ -69,9 +69,21 @@ class UK2Node_LuaCall : public UK2Node
//~ End UK2Node Interface. //~ End UK2Node Interface.
private: private:
/** Create all necessary pins. */ /** Create all necessary pins. */
void CreateCorrectPins(); 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: private:
/** The lua function prototype, which must be saved as a property **/ /** The lua function prototype, which must be saved as a property **/
UPROPERTY() UPROPERTY()

View File

@@ -3,7 +3,7 @@
#include "LuprexGameModeBase.h" #include "LuprexGameModeBase.h"
#include "lpx-drvutil.hpp" #include "lpx-drvutil.hpp"
#include "lpx-paths.hpp" #include "lpx-paths.hpp"
#include "DebugPrint.h" #include "ConsoleOutput.h"
#include "Tangible.h" #include "Tangible.h"
#include "TangibleManager.h" #include "TangibleManager.h"
#include "CommonTypes.h" #include "CommonTypes.h"
@@ -11,9 +11,11 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
using namespace DebugPrint;
using namespace CommonTypes; using namespace CommonTypes;
DEFINE_LOG_CATEGORY(LogLuprex);
DEFINE_LOG_CATEGORY(LogLuprexIntegration);
ALuprexGameModeBase::ALuprexGameModeBase() ALuprexGameModeBase::ALuprexGameModeBase()
{ {
TangibleManager = NewObject<UlxTangibleManager>(); TangibleManager = NewObject<UlxTangibleManager>();
@@ -24,7 +26,6 @@ ALuprexGameModeBase::ALuprexGameModeBase()
//PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong //PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong
SetActorTickEnabled(true); SetActorTickEnabled(true);
SetActorTickInterval(0.0f); SetActorTickInterval(0.0f);
DebugPrintControl::EnableCollection();
ResetToInitialState(); ResetToInitialState();
OnWorldPreActorTickHandle = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPreActorTick); OnWorldPreActorTickHandle = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPreActorTick);
OnWorldPostActorTickHandle = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPostActorTick); OnWorldPostActorTickHandle = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPostActorTick);
@@ -103,12 +104,6 @@ void ALuprexGameModeBase::UpdateConsoleOutput() {
ConsoleOutput.Append(lockedwrap.FetchStdout()); 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 the Console text has changed, update the widget.
if (ConsoleOutput.IsDirty()) { if (ConsoleOutput.IsDirty()) {
ConsoleSetOutput(ConsoleOutput.Get()); ConsoleSetOutput(ConsoleOutput.Get());
@@ -192,21 +187,7 @@ void ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, AActor *place) {
} }
void ALuprexGameModeBase::ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs) { void ALuprexGameModeBase::ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs) {
if (fs == "\\invokeplayer") { // Nothing here right now.
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"));
}
} }
void ALuprexGameModeBase::ConsoleSendInput(const FString& fs) void ALuprexGameModeBase::ConsoleSendInput(const FString& fs)
@@ -262,16 +243,13 @@ void ALuprexGameModeBase::Tick(float deltaseconds)
void ALuprexGameModeBase::BeginPlay() 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(); ResetToInitialState();
InitializeGlobalState();
Super::BeginPlay();
}
// Now we're just going to claim the wrapper void ALuprexGameModeBase::InitializeGlobalState()
// lock for the remainder. When we create the thread, {
// the thread will hang until we release this lock.
FlxLockedWrapper w(LockableWrapper); FlxLockedWrapper w(LockableWrapper);
// Sanity checks. Make sure everything is clean. // 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 we failed to initialize the wrapper, print an error message.
if (w->play_initialize == nullptr) 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. // 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); std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak);
if (!srcpakerr.empty()) 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 else
{ {
@@ -304,10 +283,11 @@ void ALuprexGameModeBase::BeginPlay()
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), ""); w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
if (w->error[0]) 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) { if (w->engine != nullptr) {
DPrint("Luprex initialize success"); UE_LOG(LogLuprexIntegration, Verbose, TEXT("Luprex initialization success."));
Playing = true; Playing = true;
} }
} }

View File

@@ -5,7 +5,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h" #include "GameFramework/GameModeBase.h"
#include "lpx-enginewrapper.hpp" #include "lpx-enginewrapper.hpp"
#include "DebugPrint.h" #include "ConsoleOutput.h"
#include "StringDecoder.h" #include "StringDecoder.h"
#include "TangibleManager.h" #include "TangibleManager.h"
#include "LuprexSockets.h" #include "LuprexSockets.h"
@@ -13,6 +13,11 @@
#include "BlueprintErrors.h" #include "BlueprintErrors.h"
#include "LuprexGameModeBase.generated.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; class LookAtDetector;
@@ -35,6 +40,9 @@ public:
// includes: the Luprex engine, the thread, and the socket state. // includes: the Luprex engine, the thread, and the socket state.
void ResetToInitialState(); void ResetToInitialState();
// Initialize the Luprex DLL, and do other global initialization.
void InitializeGlobalState();
// Set the entire contents of the console output box. // Set the entire contents of the console output box.
UFUNCTION(BlueprintImplementableEvent, Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintImplementableEvent, Category = "Luprex|Miscellaneous")
void ConsoleSetOutput(const FString& text); void ConsoleSetOutput(const FString& text);

View File

@@ -1,14 +1,13 @@
#include "LuprexSockets.h" #include "LuprexSockets.h"
#include "LuprexGameModeBase.h"
#include "lpx-enginewrapper.hpp" #include "lpx-enginewrapper.hpp"
#include "lpx-drvutil.hpp" #include "lpx-drvutil.hpp"
#include "DebugPrint.h"
#include "Sockets.h" #include "Sockets.h"
#include "SocketTypes.h" #include "SocketTypes.h"
#include "SocketSubsystem.h" #include "SocketSubsystem.h"
#include "AddressInfoTypes.h" #include "AddressInfoTypes.h"
using namespace DebugPrint;
#define UI UI_ST #define UI UI_ST
THIRD_PARTY_INCLUDES_START THIRD_PARTY_INCLUDES_START
@@ -215,9 +214,9 @@ public:
FlxSocketsI(FlxLockedWrapper &w); FlxSocketsI(FlxLockedWrapper &w);
virtual ~FlxSocketsI() override; virtual ~FlxSocketsI() override;
// Copy the trace to the DPrint output. // Copy the trace to UE_LOG.
void DPrintTrace(); void LogTrace();
// Error handling. // Error handling.
void SetError(const std::string& s); void SetError(const std::string& s);
virtual std::string GetError() override { return FatalError; } virtual std::string GetError() override { return FatalError; }
@@ -686,7 +685,7 @@ void FLpxChannel::AdvanceAccepting()
{ {
CloseChannelIfSSLErrorIsSerious( retval); CloseChannelIfSSLErrorIsSerious( retval);
} }
LSI->DPrintTrace(); LSI->LogTrace();
} }
void FLpxChannel::AdvanceReadWrite() void FLpxChannel::AdvanceReadWrite()
@@ -933,12 +932,13 @@ FlxSocketsI::~FlxSocketsI()
// TODO: Be more thorough. // TODO: Be more thorough.
} }
void FlxSocketsI::DPrintTrace() void FlxSocketsI::LogTrace()
{ {
char* data; char* data;
int ndata = BIO_get_mem_data(TraceBIO, &data); int ndata = BIO_get_mem_data(TraceBIO, &data);
if (ndata == 0) return; 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); BIO_reset(TraceBIO);
} }

View File

@@ -22,14 +22,14 @@ bool UlxLuaValues::Initialize(std::string_view data)
SimpleDynamicTag Tag = Decoder.read_simple_dynamic_tag(); SimpleDynamicTag Tag = Decoder.read_simple_dynamic_tag();
int64 Pos = Decoder.total_reads(); int64 Pos = Decoder.total_reads();
ELxLuaValueType Type; ElxLuaValueType Type;
switch (Tag) switch (Tag)
{ {
case SimpleDynamicTag::BOOLEAN: Type=ELxLuaValueType::Boolean; Decoder.read_bool(); break; case SimpleDynamicTag::BOOLEAN: Type=ElxLuaValueType::Boolean; Decoder.read_bool(); break;
case SimpleDynamicTag::NUMBER: Type=ELxLuaValueType::Float; Decoder.read_double(); break; case SimpleDynamicTag::NUMBER: Type=ElxLuaValueType::Float; Decoder.read_double(); break;
case SimpleDynamicTag::STRING: Type=ELxLuaValueType::String; Decoder.read_string_view(); 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::TOKEN: Type=ElxLuaValueType::Name; Decoder.read_string_view(); break;
case SimpleDynamicTag::VECTOR: Type=ELxLuaValueType::Vector; Decoder.read_fvector(); break; case SimpleDynamicTag::VECTOR: Type=ElxLuaValueType::Vector; Decoder.read_fvector(); break;
default: { default: {
Empty(); Empty();
return false; return false;
@@ -42,22 +42,57 @@ bool UlxLuaValues::Initialize(std::string_view data)
return true; 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) if (Type != Desired)
{ {
FString TypeName = StaticEnum<ELxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString(); FString TypeName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
FString DesiredName = StaticEnum<ELxLuaValueType>()->GetDisplayNameTextByValue(int64(Desired)).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); UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName);
return false; return false;
} }
return true; return true;
} }
ELxLuaValueType UlxLuaValues::GetType(int n) const ElxLuaValueType UlxLuaValues::GetType(int n) const
{ {
if (n < 0) return ELxLuaValueType::None; if (n < 0) return ElxLuaValueType::None;
if (n >= Types.Num()) return ELxLuaValueType::None; if (n >= Types.Num()) return ElxLuaValueType::None;
return Types[Cursor]; return Types[Cursor];
} }
@@ -69,42 +104,42 @@ ELxLuaValueType UlxLuaValues::GetType(int n) const
FString UlxLuaValues::GetString(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]); FlxStreamBuffer Decoder(Data[n]);
return Decoder.read_fstring(); return Decoder.read_fstring();
} }
FName UlxLuaValues::GetName(int n) const 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]); FlxStreamBuffer Decoder(Data[n]);
return Decoder.read_fname(); return Decoder.read_fname();
} }
double UlxLuaValues::GetFloat(int n) const 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]); FlxStreamBuffer Decoder(Data[n]);
return Decoder.read_double(); return Decoder.read_double();
} }
int UlxLuaValues::GetInt(int n) const 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]); FlxStreamBuffer Decoder(Data[n]);
return int(Decoder.read_double()); return int(Decoder.read_double());
} }
FVector UlxLuaValues::GetVector(int n) const 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]); FlxStreamBuffer Decoder(Data[n]);
return Decoder.read_fvector(); return Decoder.read_fvector();
} }
FVector2D UlxLuaValues::GetVector2D(int n) const 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]); FlxStreamBuffer Decoder(Data[n]);
FVector v = Decoder.read_fvector(); FVector v = Decoder.read_fvector();
return FVector2D(v.X, v.Y); return FVector2D(v.X, v.Y);
@@ -112,7 +147,7 @@ FVector2D UlxLuaValues::GetVector2D(int n) const
bool UlxLuaValues::GetBoolean(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]); FlxStreamBuffer Decoder(Data[n]);
return Decoder.read_bool(); return Decoder.read_bool();
} }

View File

@@ -77,7 +77,7 @@ public:
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
UENUM(BlueprintType) UENUM(BlueprintType)
enum class ELxLuaValueType : uint8 { enum class ElxLuaValueType : uint8 {
None, None,
String, String,
Name, Name,
@@ -105,7 +105,7 @@ private:
// For each chunk, the type of the chunk. // For each chunk, the type of the chunk.
// //
TArray<ELxLuaValueType> Types; TArray<ElxLuaValueType> Types;
// For each chunk, a pointer to the serialized data. // For each chunk, a pointer to the serialized data.
// //
@@ -120,16 +120,21 @@ private:
// //
void Empty(); 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 // Copies the data, and then preprocesses it to make sure
// it's all valid. If it's not valid, returns false. // it's all valid. If it's not valid, returns false.
// //
bool Initialize(std::string_view data); bool Initialize(std::string_view data);
// Compare two types. If they aren't equal, log an error and return false. UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
// FString DebugString() const;
static bool CheckType(ELxLuaValueType Type, ELxLuaValueType Desired);
public:
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
int Length() const { return Types.Num(); } int Length() const { return Types.Num(); }
@@ -139,16 +144,17 @@ public:
UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array")
void SetCursor(int n) { Cursor = n; } void SetCursor(int n) { Cursor = n; }
private: private:
// //
// Functions that get values from the array, random access. // Functions that get values from the array, random access.
// //
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
ELxLuaValueType GetType(int n) const; ElxLuaValueType GetType(int n) const;
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") 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") UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
FString GetString(int n) const; FString GetString(int n) const;
@@ -177,10 +183,10 @@ private:
public: public:
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") 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") 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") UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array")
FString ReadString() { return GetString(Cursor++); } FString ReadString() { return GetString(Cursor++); }

View File

@@ -5,7 +5,7 @@
#include "TangibleManager.h" #include "TangibleManager.h"
#include "LuprexGameModeBase.h" #include "LuprexGameModeBase.h"
#define DEFAULT_BLUEPRINT (TEXT("TangibleStaticMesh")) #define DEFAULT_BLUEPRINT (TEXT("StaticMesh"))
UlxTangible::UlxTangible() UlxTangible::UlxTangible()
{ {
@@ -24,17 +24,18 @@ void UlxTangible::Init(UlxTangibleManager* tm, int64 id)
} }
#pragma optimize("", off) #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 we're already of the right class, do nothing.
if (ActorBlueprintName == name) { if (ActorBlueprintName == Name) {
return; return;
} }
// Get the blueprint. // Get the blueprint.
// If the blueprint doesn't exist, or doesn't implement UClass *blueprint = Manager->GetTangibleClass(Name);
// TangibleInterface, then use the fallback blueprint. if (blueprint == nullptr)
UClass *blueprint = Manager->GetTangibleClass(name); {
if ((!name.IsEmpty()) && (blueprint == nullptr)) {
blueprint = Manager->GetTangibleClass(DEFAULT_BLUEPRINT); blueprint = Manager->GetTangibleClass(DEFAULT_BLUEPRINT);
check(blueprint != nullptr); check(blueprint != nullptr);
} }
@@ -62,13 +63,16 @@ void UlxTangible::SetActorBlueprint(const FString &name) {
} }
// Update the blueprint name // Update the blueprint name
ActorBlueprintName = name; ActorBlueprintName = Name;
// Now create a new actor, unless the BP is nullptr. // Now create a new actor, unless the BP is nullptr.
if (blueprint != nullptr) { if (blueprint != nullptr) {
UWorld* w = Manager->GetWorld(); UWorld* w = Manager->GetWorld();
FActorSpawnParameters params; 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. // 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 // We should spawn at the actor's current location. I'll get to it
// eventually. // eventually.
@@ -86,6 +90,9 @@ void UlxTangible::SetActorBlueprint(const FString &name) {
AActor* a = w->SpawnActor(blueprint, &transform, params); AActor* a = w->SpawnActor(blueprint, &transform, params);
check(a != nullptr); check(a != nullptr);
// Make sure the label and the name are the same.
a->SetActorLabel(params.Name.ToString());
// Insert a TangibleComponent into the actor. // Insert a TangibleComponent into the actor.
// Link the actor to its tangible, and the tangible to its actor. // Link the actor to its tangible, and the tangible to its actor.
UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false); UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false);

View File

@@ -124,6 +124,7 @@ public:
private: private:
// Set the actor's blueprint, and recreate the actor if necessary. // Set the actor's blueprint, and recreate the actor if necessary.
// //
// If the blueprint is the empty string, deletes the actor. // If the blueprint is the empty string, deletes the actor.

View File

@@ -3,11 +3,8 @@
#include "TangibleManager.h" #include "TangibleManager.h"
#include "Tangible.h" #include "Tangible.h"
#include "DebugPrint.h"
#include "LuprexGameModeBase.h" #include "LuprexGameModeBase.h"
using namespace DebugPrint;
UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) { UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) {
UFunction *result = uclass->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); UFunction *result = uclass->FindFunctionByName(FName(TEXT("Animation Queue Changed")));
if (result == nullptr) return nullptr; if (result == nullptr) return nullptr;
@@ -16,6 +13,8 @@ UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) {
} }
UClass *UlxTangibleManager::GetTangibleClass(const FString &name) { 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()) { if (name.IsEmpty()) {
return nullptr; return nullptr;
} }
@@ -24,8 +23,10 @@ UClass *UlxTangibleManager::GetTangibleClass(const FString &name) {
} }
FString path(TEXT("/Game/Tangibles/")); FString path(TEXT("/Game/Tangibles/"));
path += TEXT("Tan");
path += name; path += name;
path += TCHAR('.'); path += TCHAR('.');
path += TEXT("Tan");
path += name; path += name;
path += TCHAR('_'); path += TCHAR('_');
path += TCHAR('C'); path += TCHAR('C');

View File

@@ -1,7 +1,4 @@
#include "TriggeredTask.h" #include "TriggeredTask.h"
#include "DebugPrint.h"
using namespace DebugPrint;
FTriggeredTask::FTriggeredTask() { FTriggeredTask::FTriggeredTask() {
Client = nullptr; Client = nullptr;
@@ -16,7 +13,6 @@ uint32 FTriggeredTask::Run() {
{ {
CallEvent->Wait(); CallEvent->Wait();
if (ThreadStopRequested) { if (ThreadStopRequested) {
DPrint("Thread stopping as requested");
break; break;
} }
// The payload. // The payload.

View File

@@ -328,7 +328,7 @@ LuaDefine(tangible_build, "config",
if (!LS.isnil(bp)) { if (!LS.isnil(bp)) {
state.set_string("bp", LS.ckstring(bp)); state.set_string("bp", LS.ckstring(bp));
} else { } else {
state.set_string("bp", LS.ckstring(classname)); state.set_string("bp", LS.classname(classtab));
} }
if (!LS.isnil(plane)) { if (!LS.isnil(plane)) {
state.set_string("plane", LS.ckstring(plane)); state.set_string("plane", LS.ckstring(plane));

View File

@@ -1,21 +1,14 @@
makeclass('login') makeclass('login')
makeclass("engio") makeclass("engio")
makeclass('tree') makeclass('cube')
makeclass('sphere')
function tree.init(self, config)
print("In tree.init:")
pprint{self=self, config=config}
wait(1)
print("tick");
wait(1)
end
function login.init() function login.init()
local actor = tangible.actor() local actor = tangible.actor()
dprint("login.init:", actor) dprint("login.init:", actor)
local x = math.random(1, 100) tangible.animinit(actor, {bp="character", plane="earth", xyz={0, 0, 90}})
local y = math.random(1, 100) tangible.build{class=cube, xyz={500,-100,0}}
tangible.animate(actor, nil, {bp="tangiblecharacter", action="warpto", plane="earth", xyz={x, y, 90}}) tangible.build{class=sphere, xyz={500,100,0}}
end end
function engio.move(action, xyz, facing) 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) pprint("Hi there", a1, a2, a3, a4, a5)
end end
function engio.retthree(a1, a2) function engio.retmany()
return 7, vec(8,9,10), "Yo" return 7, vec(8,9,10), "Yo", "Banana", 13.2, vec(2,3,4), "Hi"
end end