Lua Console Overhaul in progress
This commit is contained in:
BIN
Content/Luprex/lxGameMode.uasset
LFS
BIN
Content/Luprex/lxGameMode.uasset
LFS
Binary file not shown.
Binary file not shown.
@@ -5,12 +5,12 @@
|
|||||||
//
|
//
|
||||||
// ConsoleOutput
|
// ConsoleOutput
|
||||||
//
|
//
|
||||||
// Storing the text that goes in the unreal console.
|
// Storing the text that goes in the command console.
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
void FlxConsoleOutput::Truncate() {
|
void UlxConsoleOutput::Truncate() {
|
||||||
int lines = 50;
|
int lines = 50;
|
||||||
int csize = Content.Len();
|
int csize = Content.Len();
|
||||||
int total = 0;
|
int total = 0;
|
||||||
@@ -25,7 +25,7 @@ void FlxConsoleOutput::Truncate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlxConsoleOutput::MaybeAppendNewline() {
|
bool UlxConsoleOutput::MaybeAppendNewline() {
|
||||||
int csize = Content.Len();
|
int csize = Content.Len();
|
||||||
if ((csize > 0) && (Content[csize - 1] != '\n')) {
|
if ((csize > 0) && (Content[csize - 1] != '\n')) {
|
||||||
Content += TEXT("\n");
|
Content += TEXT("\n");
|
||||||
@@ -36,7 +36,7 @@ bool FlxConsoleOutput::MaybeAppendNewline() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlxConsoleOutput::MaybeAppendText(const FString& text) {
|
bool UlxConsoleOutput::MaybeAppendText(const FString& text) {
|
||||||
if (!text.IsEmpty()) {
|
if (!text.IsEmpty()) {
|
||||||
Content += text;
|
Content += text;
|
||||||
return true;
|
return true;
|
||||||
@@ -46,7 +46,7 @@ bool FlxConsoleOutput::MaybeAppendText(const FString& text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlxConsoleOutput::Append(const FString& text) {
|
void UlxConsoleOutput::Append(const FString& text) {
|
||||||
bool modified = MaybeAppendText(text);
|
bool modified = MaybeAppendText(text);
|
||||||
if (modified) {
|
if (modified) {
|
||||||
Dirty = true;
|
Dirty = true;
|
||||||
@@ -54,7 +54,7 @@ void FlxConsoleOutput::Append(const FString& text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlxConsoleOutput::AppendLine(const FString& text) {
|
void UlxConsoleOutput::AppendLine(const FString& text) {
|
||||||
bool modified = MaybeAppendNewline();
|
bool modified = MaybeAppendNewline();
|
||||||
modified |= MaybeAppendText(text);
|
modified |= MaybeAppendText(text);
|
||||||
modified |= MaybeAppendNewline();
|
modified |= MaybeAppendNewline();
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include "Containers/UnrealString.h"
|
#include "Containers/UnrealString.h"
|
||||||
|
|
||||||
|
#include "ConsoleOutput.generated.h"
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// ConsoleOutput
|
// ConsoleOutput
|
||||||
@@ -19,11 +22,16 @@
|
|||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class FlxConsoleOutput {
|
UCLASS(BlueprintType)
|
||||||
|
class UlxConsoleOutput : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FString Content;
|
FString Content;
|
||||||
bool Dirty;
|
bool Dirty;
|
||||||
|
|
||||||
|
private:
|
||||||
// Truncate the console to a reasonable number of
|
// Truncate the console to a reasonable number of
|
||||||
// lines. The length is hardwired.
|
// lines. The length is hardwired.
|
||||||
void Truncate();
|
void Truncate();
|
||||||
@@ -36,18 +44,23 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Append a line of text to the console.
|
// Append a line of text to the console.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
void Append(const FString& text);
|
void Append(const FString& text);
|
||||||
|
|
||||||
// Append a line of text to the console on a line by itself.
|
// Append a line of text to the console on a line by itself.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
void AppendLine(const FString& text);
|
void AppendLine(const FString& text);
|
||||||
|
|
||||||
// Get the console text as a string.
|
// Get the console text as a string.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
const FString& Get() const { return Content; }
|
const FString& Get() const { return Content; }
|
||||||
|
|
||||||
// Return if the dirty flag is set.
|
// Return if the dirty flag is set.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
bool IsDirty() const { return Dirty; }
|
bool IsDirty() const { return Dirty; }
|
||||||
|
|
||||||
// Clear the dirty flag.
|
// Clear the dirty flag.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
void ClearDirty() { Dirty = false; }
|
void ClearDirty() { Dirty = false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,37 @@ FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix)
|
|||||||
//
|
//
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void UlxLuaCallLibrary::ValidateLua(
|
||||||
|
ElxLuaSyntaxCheck &Result, FString &ErrorMessage, UObject *context, const FString &Code)
|
||||||
|
{
|
||||||
|
if (Code.StartsWith(TEXT("/")))
|
||||||
|
{
|
||||||
|
ErrorMessage = "SlashCommand";
|
||||||
|
Result = ElxLuaSyntaxCheck::SlashCommand;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Code.TrimStart().IsEmpty())
|
||||||
|
{
|
||||||
|
ErrorMessage = "";
|
||||||
|
Result = ElxLuaSyntaxCheck::Whitespace;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||||
|
ErrorMessage = mode->LuaValidate(Code);
|
||||||
|
if (ErrorMessage.IsEmpty())
|
||||||
|
{
|
||||||
|
Result = ElxLuaSyntaxCheck::ValidLua;
|
||||||
|
}
|
||||||
|
else if (ErrorMessage.Contains(TEXT("<eof>")))
|
||||||
|
{
|
||||||
|
Result = ElxLuaSyntaxCheck::TruncatedLua;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = ElxLuaSyntaxCheck::InvalidLua;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -211,7 +242,6 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con
|
|||||||
sb.write_string(fname);
|
sb.write_string(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place)
|
void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place)
|
||||||
{
|
{
|
||||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||||
|
|||||||
@@ -9,6 +9,23 @@
|
|||||||
|
|
||||||
class UlxLuaValues;
|
class UlxLuaValues;
|
||||||
|
|
||||||
|
// Classify lua code syntactically:
|
||||||
|
//
|
||||||
|
// SlashCommand: starts with a slash, therefore, not lua
|
||||||
|
// WhitespaceOnly: the input only contains whitespace
|
||||||
|
// ValidSyntax: the input is valid lua code
|
||||||
|
// TruncatedCode: the input is truncated
|
||||||
|
// InvalidSyntax: invalid lua
|
||||||
|
//
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ElxLuaSyntaxCheck : uint8 {
|
||||||
|
SlashCommand,
|
||||||
|
Whitespace,
|
||||||
|
ValidLua,
|
||||||
|
TruncatedLua,
|
||||||
|
InvalidLua,
|
||||||
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// These are the types that can actually be packed into
|
// These are the types that can actually be packed into
|
||||||
@@ -118,9 +135,12 @@ public:
|
|||||||
static FString AllKnownReturnValueTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallReturnValue_")); }
|
static FString AllKnownReturnValueTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallReturnValue_")); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Syntactically validate lua code. Parses the code and
|
||||||
|
// returns an error message. If the code is error-free, the
|
||||||
|
// error message is the empty string.
|
||||||
//
|
//
|
||||||
// Functions that do miscellaneous things.
|
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", ExpandEnumAsExecs="Result"), Category = "Luprex|Call Lua Function")
|
||||||
//
|
static void ValidateLua(ElxLuaSyntaxCheck &Result, FString &ErrorMessage, UObject *context, const FString &Code);
|
||||||
|
|
||||||
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 LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName);
|
static void LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "LuprexGameModeBase.h"
|
#include "LuprexGameModeBase.h"
|
||||||
#include "lpx-drvutil.hpp"
|
#include "lpx-drvutil.hpp"
|
||||||
#include "lpx-paths.hpp"
|
#include "lpx-paths.hpp"
|
||||||
#include "ConsoleOutput.h"
|
|
||||||
#include "Tangible.h"
|
#include "Tangible.h"
|
||||||
#include "TangibleManager.h"
|
#include "TangibleManager.h"
|
||||||
#include "LuaCall.h"
|
#include "LuaCall.h"
|
||||||
@@ -124,18 +123,15 @@ void ALuprexGameModeBase::ResetToInitialState()
|
|||||||
NextRotateCube = 1.0;
|
NextRotateCube = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ALuprexGameModeBase::UpdateConsoleOutput() {
|
void ALuprexGameModeBase::UpdateConsoleOutput() {
|
||||||
// Copy Luprex Stdout into the console.
|
// Copy Luprex Stdout into the console.
|
||||||
FlxLockedWrapper lockedwrap(LockableWrapper);
|
FlxLockedWrapper lockedwrap(LockableWrapper);
|
||||||
if (Playing) {
|
if (Playing) {
|
||||||
ConsoleOutput.Append(lockedwrap.FetchStdout());
|
FString Text = lockedwrap.FetchStdout();
|
||||||
|
if (!Text.IsEmpty())
|
||||||
|
{
|
||||||
|
ConsoleAddOutput(Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the Console text has changed, update the widget.
|
|
||||||
if (ConsoleOutput.IsDirty()) {
|
|
||||||
ConsoleSetOutput(ConsoleOutput.Get());
|
|
||||||
ConsoleOutput.ClearDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,32 +223,15 @@ UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(AccessKind kind, AActor *place) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALuprexGameModeBase::ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs) {
|
FString ALuprexGameModeBase::LuaValidate(const FString &Code)
|
||||||
// Nothing here right now.
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALuprexGameModeBase::ConsoleSendInput(const FString& fs)
|
|
||||||
{
|
{
|
||||||
if (fs.IsEmpty()) {
|
FTCHARToUTF8 UCode(*Code);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlxLockedWrapper w(LockableWrapper);
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
if (w->engine != nullptr)
|
uint32_t retpklen;
|
||||||
{
|
const char *retpk;
|
||||||
ConsoleOutput.AppendLine(FString("> ") + fs);
|
w->play_access(w.Get(), AccessKind::VALIDATE_LUA, 0, UCode.Length(), UCode.Get(), &retpklen, &retpk);
|
||||||
// This is a bad way to do this. The problem is that if some
|
FString Result(retpklen, (const UTF8CHAR*)retpk);
|
||||||
// lua code contains '\\', we'll catch it instead of passing it
|
return Result;
|
||||||
// through. There's no simple solution, though.
|
|
||||||
if (fs[0] == '\\') {
|
|
||||||
ExecuteDebuggingCommand(w, fs);
|
|
||||||
} else {
|
|
||||||
FTCHARToUTF8 utf8fs(fs);
|
|
||||||
std::string ufs(utf8fs.Get(), utf8fs.Length());
|
|
||||||
ufs = ufs + "\n";
|
|
||||||
w->play_recv_incoming(w.Get(), 0, ufs.size(), ufs.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALuprexGameModeBase::OnWorldPreActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds)
|
void ALuprexGameModeBase::OnWorldPreActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/GameModeBase.h"
|
#include "GameFramework/GameModeBase.h"
|
||||||
#include "lpx-enginewrapper.hpp"
|
#include "lpx-enginewrapper.hpp"
|
||||||
#include "ConsoleOutput.h"
|
|
||||||
#include "StringDecoder.h"
|
#include "StringDecoder.h"
|
||||||
#include "TangibleManager.h"
|
#include "TangibleManager.h"
|
||||||
#include "AssetLookup.h"
|
#include "AssetLookup.h"
|
||||||
@@ -59,11 +58,7 @@ public:
|
|||||||
|
|
||||||
// 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 ConsoleAddOutput(const FString& text);
|
||||||
|
|
||||||
// This is called by the GUI whenever the user hits enter.
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous")
|
|
||||||
void ConsoleSendInput(const FString& text);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous")
|
UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous")
|
||||||
int64 GetPlayerId();
|
int64 GetPlayerId();
|
||||||
@@ -94,7 +89,11 @@ public:
|
|||||||
void LookAtChanged();
|
void LookAtChanged();
|
||||||
|
|
||||||
|
|
||||||
// Assemble a lua call. To call into lua:
|
// Assemble a lua call. Note that this is the lowest-level interface.
|
||||||
|
// These functions are wrapped by the functions in UlxLuaCallLibrary,
|
||||||
|
// and those in turn are wrapped by the K2Node "LuaInvoke" and "LuaProbe".
|
||||||
|
//
|
||||||
|
// At this level, the process of calling Lua is:
|
||||||
//
|
//
|
||||||
// * Use LuaCallBegin
|
// * Use LuaCallBegin
|
||||||
// * Get the lua call buffer:
|
// * Get the lua call buffer:
|
||||||
@@ -102,8 +101,7 @@ public:
|
|||||||
// - add a function name
|
// - add a function name
|
||||||
// - add function parameters
|
// - add function parameters
|
||||||
// * Use LuaCallEnd.
|
// * Use LuaCallEnd.
|
||||||
// * Get the lua call result.
|
// * Process any return values in the UlxLuaValues array.
|
||||||
// - parse out any return values
|
|
||||||
//
|
//
|
||||||
FlxStreamBuffer &LuaCallBegin() { LuaCallBuffer.clear(); return LuaCallBuffer; }
|
FlxStreamBuffer &LuaCallBegin() { LuaCallBuffer.clear(); return LuaCallBuffer; }
|
||||||
FlxStreamBuffer &LuaCallGetBuffer() { return LuaCallBuffer; }
|
FlxStreamBuffer &LuaCallGetBuffer() { return LuaCallBuffer; }
|
||||||
@@ -112,8 +110,13 @@ public:
|
|||||||
UlxLuaValues *LuaCallEnd(AccessKind kind, AActor *place);
|
UlxLuaValues *LuaCallEnd(AccessKind kind, AActor *place);
|
||||||
void LuaCallClear() { LuaCallBuffer.clear(); }
|
void LuaCallClear() { LuaCallBuffer.clear(); }
|
||||||
|
|
||||||
// Execute a debugging command, typed on the GUI.
|
// Validate some lua code. Returns an error message.
|
||||||
void ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs);
|
// If the lua is well-formed, the error message is the
|
||||||
|
// empty string. The syntax of the code is checked using
|
||||||
|
// an otherwise empty lua interpreter, so this is purely
|
||||||
|
// a syntax check.
|
||||||
|
//
|
||||||
|
FString LuaValidate(const FString &Code);
|
||||||
|
|
||||||
// Get the Asset Lookup table.
|
// Get the Asset Lookup table.
|
||||||
const UlxAssetLookup *GetAssetLookup() const { return AssetLookup; }
|
const UlxAssetLookup *GetAssetLookup() const { return AssetLookup; }
|
||||||
@@ -162,9 +165,6 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, Category="Debugging Tools")
|
UPROPERTY(EditAnywhere, Category="Debugging Tools")
|
||||||
ElxLogVerbosity BreakToDebuggerLogVerbosity;
|
ElxLogVerbosity BreakToDebuggerLogVerbosity;
|
||||||
|
|
||||||
// This stores the entire text currently visible in the console.
|
|
||||||
FlxConsoleOutput ConsoleOutput;
|
|
||||||
|
|
||||||
// The Luprex EngineWrapper, with a Mutex to protect it.
|
// The Luprex EngineWrapper, with a Mutex to protect it.
|
||||||
// To access it, construct a FlxLockedWrapper.
|
// To access it, construct a FlxLockedWrapper.
|
||||||
FlxLockableWrapper LockableWrapper;
|
FlxLockableWrapper LockableWrapper;
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ public:
|
|||||||
}
|
}
|
||||||
case AccessKind::VALIDATE_LUA: {
|
case AccessKind::VALIDATE_LUA: {
|
||||||
LuaVar closure;
|
LuaVar closure;
|
||||||
LuaDefStack LS(lua_syntax_checker_, closure);
|
LuaExtStack LS(lua_syntax_checker_, closure);
|
||||||
eng::string errmsg = LS.load(closure, datapk, "stdin");
|
eng::string errmsg = LS.load(closure, datapk, "stdin");
|
||||||
retpk->write_bytes(errmsg);
|
retpk->write_bytes(errmsg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ public:
|
|||||||
}
|
}
|
||||||
case AccessKind::VALIDATE_LUA: {
|
case AccessKind::VALIDATE_LUA: {
|
||||||
LuaVar closure;
|
LuaVar closure;
|
||||||
LuaDefStack LS(lua_syntax_checker_, closure);
|
LuaExtStack LS(lua_syntax_checker_, closure);
|
||||||
eng::string errmsg = LS.load(closure, datapk, "stdin");
|
eng::string errmsg = LS.load(closure, datapk, "stdin");
|
||||||
retpk->write_bytes(errmsg);
|
retpk->write_bytes(errmsg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user