Implement FlxStreamBuffer and beginnings of ConsoleCommands in unreal

This commit is contained in:
2023-10-24 01:44:09 -04:00
parent 9dc9d31d54
commit 5381b5708a
6 changed files with 106 additions and 219 deletions

View File

@@ -36,28 +36,28 @@ static bool SetProperty(const FString& prefix, UObject* obj, const FlxAnimationF
FString sname(field.Name.size(), (const UTF8CHAR*)field.Name.data());
FName nname(prefix + sname);
switch (field.Type) {
case ElxAnimValueType::STRING: {
case SimpleDynamicTag::STRING: {
FStrProperty* fprop = FindFProperty<FStrProperty>(uclass, nname);
if (fprop == nullptr) return false;
FString* pptr = fprop->ContainerPtrToValuePtr<FString>(obj);
*pptr = FString(field.S.size(), (const UTF8CHAR*)field.S.data());
return true;
}
case ElxAnimValueType::NUMBER: {
case SimpleDynamicTag::NUMBER: {
FDoubleProperty* fprop = FindFProperty<FDoubleProperty>(uclass, nname);
if (fprop == nullptr) return false;
double* pptr = fprop->ContainerPtrToValuePtr<double>(obj);
*pptr = field.X;
return true;
}
case ElxAnimValueType::BOOLEAN: {
case SimpleDynamicTag::BOOLEAN: {
FBoolProperty* fprop = FindFProperty<FBoolProperty>(uclass, nname);
if (fprop == nullptr) return false;
uint8* pptr = fprop->ContainerPtrToValuePtr<uint8>(obj);
fprop->SetPropertyValue(pptr, (field.X == 1.0));
return true;
}
case ElxAnimValueType::XYZ: {
case SimpleDynamicTag::VECTOR: {
FStructProperty* fprop = FindFProperty<FStructProperty>(uclass, nname);
if (fprop == nullptr) return false;
if (fprop->Struct != TBaseStructure<FVector>::Get()) return false;
@@ -84,7 +84,7 @@ bool FlxAnimationStep::Unpack(const FString& prefix, UObject* into) const {
FlxAnimationField field;
field.Name = "action";
field.Persistent = false;
field.Type = ElxAnimValueType::STRING;
field.Type = SimpleDynamicTag::STRING;
field.S = "idle";
ok &= SetProperty(prefix, into, field);
}
@@ -110,7 +110,7 @@ static FlxAnimationField FindAnimationFieldLL(const FlxAnimationStep& step, std:
return result;
}
}
result.Type = ElxAnimValueType::INVALID;
result.Type = SimpleDynamicTag::UNINITIALIZED;
return result;
}
@@ -122,21 +122,21 @@ static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const
void FlxAnimationStep::AutoUpdateXYZ(AActor *actor) const {
FlxAnimationField xyz = FindAnimationFieldLL(*this, "xyz");
if (xyz.Type == ElxAnimValueType::XYZ) {
if (xyz.Type == SimpleDynamicTag::VECTOR) {
actor->SetActorLocation(FVector(xyz.X, xyz.Y, xyz.Z));
}
}
void FlxAnimationStep::AutoUpdateFacing(AActor *actor) const {
FlxAnimationField facing = FindAnimationFieldLL(*this, "facing");
if (facing.Type == ElxAnimValueType::NUMBER) {
if (facing.Type == SimpleDynamicTag::NUMBER) {
actor->SetActorRotation(FRotator(0, facing.X, 0));
}
}
void FlxAnimationStep::AutoUpdatePlane(FName *planep) const {
FlxAnimationField plane = FindAnimationFieldLL(*this, "plane");
if (plane.Type == ElxAnimValueType::STRING) {
if (plane.Type == SimpleDynamicTag::STRING) {
FString pname(plane.S.size(), (const UTF8CHAR*)(plane.S.data()));
*planep = FName(pname);
}
@@ -152,13 +152,13 @@ bool UlxAnimationStepLibrary::AnimationStepIsIdle(const FlxAnimationStep &step)
FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, name);
if (field.Type != ElxAnimValueType::XYZ) return FVector(0, 0, 0);
if (field.Type != SimpleDynamicTag::VECTOR) return FVector(0, 0, 0);
return FVector(field.X, field.Y, field.Z);
}
double UlxAnimationStepLibrary::AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, name);
if (field.Type != ElxAnimValueType::NUMBER) return 0.0;
if (field.Type != SimpleDynamicTag::NUMBER) return 0.0;
return field.X;
}
@@ -167,58 +167,44 @@ FString UlxAnimationStepLibrary::AnimationStepGetString(const FlxAnimationStep&
return TEXT("idle");
}
FlxAnimationField field = FindAnimationField(step, name);
if (field.Type != ElxAnimValueType::STRING) return TEXT("");
if (field.Type != SimpleDynamicTag::STRING) return TEXT("");
return FString(field.S.size(), (const UTF8CHAR*)(field.S.data()));
}
bool UlxAnimationStepLibrary::AnimationStepGetBool(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, name);
if (field.Type != ElxAnimValueType::BOOLEAN) return false;
if (field.Type != SimpleDynamicTag::BOOLEAN) return false;
return field.X == 1.0;
}
FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
FlxAnimationStepView result;
result.Hash = Decoder.read_uint64();
result.Body = Decoder.read_string_view();
return result;
}
uint64 FlxAnimQueueDecoder::PeekHash() {
std::string_view rest = Decoder.GetRest();
uint64 result = Decoder.read_uint64();
Decoder.Reset(rest, false);
return result;
}
FlxAnimationField FlxAnimationStepDecoder::ReadField() {
FlxAnimationField result;
result.Name = Decoder.read_string_view();
result.Persistent = Decoder.read_bool();
result.Type = (ElxAnimValueType)Decoder.read_uint8();
result.Type = (SimpleDynamicTag)Decoder.read_uint8();
switch (result.Type) {
case ElxAnimValueType::STRING: {
case SimpleDynamicTag::STRING: {
result.S = Decoder.read_string_view();
break;
}
case ElxAnimValueType::NUMBER: {
case SimpleDynamicTag::NUMBER: {
result.X = Decoder.read_double();
break;
}
case ElxAnimValueType::BOOLEAN: {
case SimpleDynamicTag::BOOLEAN: {
result.X = Decoder.read_bool() ? 1.0 : 0.0;
break;
}
case ElxAnimValueType::XYZ: {
case SimpleDynamicTag::VECTOR: {
result.X = Decoder.read_double();
result.Y = Decoder.read_double();
result.Z = Decoder.read_double();
break;
}
default: {
Decoder.set_at_eof();
result.Type = ElxAnimValueType::BOOLEAN;
result.X = 0;
Decoder.read_bytes(Decoder.fill());
result.Type = SimpleDynamicTag::UNINITIALIZED;
break;
}
}
@@ -239,16 +225,16 @@ FString FlxAnimationStepDecoder::DebugString(bool injectidle, bool persistentonl
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
switch (field.Type) {
case ElxAnimValueType::STRING:
case SimpleDynamicTag::STRING:
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
break;
case ElxAnimValueType::NUMBER:
case SimpleDynamicTag::NUMBER:
result.Appendf(TEXT("%lf"), field.X);
break;
case ElxAnimValueType::BOOLEAN:
case SimpleDynamicTag::BOOLEAN:
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
break;
case ElxAnimValueType::XYZ:
case SimpleDynamicTag::VECTOR:
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
break;
}
@@ -256,6 +242,20 @@ FString FlxAnimationStepDecoder::DebugString(bool injectidle, bool persistentonl
return result;
}
FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
FlxAnimationStepView result;
result.Hash = Decoder.read_uint64();
result.Body = Decoder.read_string_view();
return result;
}
uint64 FlxAnimQueueDecoder::PeekHash() {
int64_t read_count = Decoder.total_reads();
uint64 result = Decoder.read_uint64();
Decoder.unread_to(read_count);
return result;
}
FlxAnimQueueDecoder::FlxAnimQueueDecoder(std::string_view queue) : Decoder(queue) {
SizeLimit = Decoder.read_uint8();
ActualSize = Decoder.read_uint8();

View File

@@ -5,20 +5,6 @@
#include "Containers/Deque.h"
#include "AnimQueue.generated.h"
////////////////////////////////////////////////
//
// This is copied over from Luprex source. Not ideal.
//
////////////////////////////////////////////////
enum class ElxAnimValueType {
STRING,
NUMBER,
BOOLEAN,
XYZ,
INVALID
};
////////////////////////////////////////////////
//
// An single animation step.
@@ -161,7 +147,7 @@ struct FlxAnimationStepView {
struct FlxAnimationField {
std::string_view Name;
bool Persistent;
ElxAnimValueType Type;
SimpleDynamicTag Type;
double X, Y, Z;
std::string_view S;
};
@@ -179,7 +165,7 @@ struct FlxAnimationField {
class FlxAnimQueueDecoder {
private:
FlxStringDecoder Decoder;
FlxStreamBuffer Decoder;
// These values are immediately read from the header.
//
@@ -201,7 +187,7 @@ public:
// Return true if the parser has reached the end of the string.
//
bool AtEOF() { return Decoder.at_eof(); }
bool AtEOF() { return Decoder.empty(); }
// Read one animation step.
//
@@ -229,7 +215,7 @@ public:
class FlxAnimationStepDecoder {
private:
FlxStringDecoder Decoder;
FlxStreamBuffer Decoder;
public:
// Initialize the FlxAnimationStepDecoder from the FlxAnimationStepView.
@@ -238,7 +224,7 @@ public:
// Return true if the parser has reached the end of the string.
//
bool AtEOF() { return Decoder.at_eof(); }
bool AtEOF() { return Decoder.empty(); }
// Read one field.
//

View File

@@ -132,24 +132,41 @@ void AIntegrationGameModeBase::UpdateTangibles() {
TangibleManager->DeleteFarawayTangibles();
}
void AIntegrationGameModeBase::ExecuteDebuggingCommand(const FString &fs) {
if (fs == "\\invokeplayer") {
// FlxLockedWrapper w(LockableWrapper);
// int64 player = w.GetActor();
// w->play_invoke_player(w.Get(), player, datapk);
} else {
ConsoleOutput.AppendLine(TEXT("Unknown Command"));
}
}
void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs)
{
FlxLockedWrapper w(LockableWrapper);
if (w->engine != nullptr)
{
const TCHAR* fstchar = *fs;
if (sizeof(TCHAR) == 2)
{
ConsoleOutput.AppendLine(FString("> ") + fs);
std::u16string_view fsview((const char16_t*)fstchar, fs.Len());
std::string utf8 = drvutil::utf16_to_utf8(fsview);
utf8 = utf8 + "\n";
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str());
ConsoleOutput.AppendLine(FString("> ") + fs);
// This is a bad way to do this. The problem is that if some
// lua code contains '\\', we'll catch it instead of passing it
// through. There's no simple solution, though.
if (fs[0] == '\\') {
ExecuteDebuggingCommand(fs);
} else {
const TCHAR* fstchar = *fs;
if (sizeof(TCHAR) == 2)
{
std::u16string_view fsview((const char16_t*)fstchar, fs.Len());
std::string utf8 = drvutil::utf16_to_utf8(fsview);
utf8 = utf8 + "\n";
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str());
}
}
}
}
void AIntegrationGameModeBase::Tick(float deltaseconds)
{
Super::Tick(deltaseconds);

View File

@@ -43,6 +43,9 @@ public:
UPROPERTY(EditDefaultsOnly, Category = TangibleClasses)
TSubclassOf<AActor> ClassTangibleActor;
// Execute a debugging command, typed on the GUI.
void ExecuteDebuggingCommand(const FString &fs);
// Transfer console output from the Luprex engine to unreal.
void UpdateConsoleOutput();

View File

@@ -1,38 +1,2 @@
#include "StringDecoder.h"
FlxStringDecoder::FlxStringDecoder(std::string_view s) {
Text = s.data();
Size = s.size();
ErrBeyondEOF = false;
ErrStringTooLong = false;
}
void FlxStringDecoder::Reset(std::string_view s, bool clear) {
Text = s.data();
Size = s.size();
if (clear) {
ErrBeyondEOF = false;
ErrStringTooLong = false;
}
}
std::string_view FlxStringDecoder::GetRest() {
return std::string_view(Text, Size);
}
std::string_view FlxStringDecoder::read_string_view() {
size_t length = read_length();
if (length > Size) {
ErrBeyondEOF = true;
return std::string_view();
}
std::string_view result(Text, length);
Text += length;
Size -= length;
return result;
}
void FlxStringDecoder::set_at_eof() {
Text += Size;
Size = 0;
}

View File

@@ -3,125 +3,42 @@
#include "CoreMinimal.h"
#include "lpx-basebuffer.hpp"
/////////////////////////////////////////////////////
//
// FlxStringDecoder
//
// This class is used to decipher 8-bit strings that
// contain packed ints, strings, and other data.
// The typical example of usage is to decipher the
// serialized animation queues fed to us by Luprex.
//
// The FlxStringDecoder doesn't make a copy of the string
// you pass in, instead, it stores a pointer to it.
// So be sure not to free the string until you're
// done analyzing it.
//
// You'll note that some of the function names are
// lowercase, with underscores. That's because they're
// inherited from Luprex classes, and luprex classes
// use that naming convention. There's not any easy
// workaround for that.
//
/////////////////////////////////////////////////////
class FlxStringDecoder : public BaseReader<FlxStringDecoder> {
class FlxStreamBufferCore {
private:
const char* Text;
size_t Size;
bool err_eof_on_read_;
bool err_string_too_long_;
bool err_integer_truncated_;
protected:
public:
// You can check and clear these error flags at will.
//
bool ErrBeyondEOF;
bool ErrStringTooLong;
void *basebuffer_malloc(size_t size) { return malloc(size); }
void basebuffer_free(void *p) { free(p); }
void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; }
void raise_eof_on_read() { err_eof_on_read_ = true; }
void raise_string_too_long() { err_string_too_long_ = true; }
void raise_integer_truncated() { err_integer_truncated_ = true; }
public:
// This function is required by BaseReader.
// It's not meant for end users.
//
void read_bytes_into(char* buffer, size_t size) {
if (size > Size) {
memset(buffer, 0, size);
ErrBeyondEOF = true;
set_at_eof();
}
else {
memcpy(buffer, Text, size);
Text += size;
Size -= size;
}
}
// This function is required by BaseReader.
// It's not meant for end users.
//
void raise_string_too_long() {
ErrStringTooLong = true;
}
public:
// The type returned by read_string.
//
using read_string_type = std::string;
// Initialize the string decoder with a text to analyze.
//
FlxStringDecoder(std::string_view s);
// Get the remaining text as a string_view.
//
std::string_view GetRest();
// Reinitialize with a new string_view.
//
// If clear is true, clears the error flags.
//
void Reset(std::string_view s, bool clear);
// Get the size of the remaining text.
//
size_t get_size() { return Size; }
// Return true if the remaining text is empty.
//
bool at_eof() { return Size == 0; }
// Move the cursor to EOF.
//
void set_at_eof();
// Read a string as a string_view
//
// This reads a string from the source, returning
// it as a string_view that points into the buffer.
// Naturally, if you release the buffer, the
// string_view is invalidated.
//
// This is considerably faster than read_string.
//
std::string_view read_string_view();
// Inherited Methods:
//
// The following methods are inherited from BaseReader:
//
// uint8_t read_uint8();
// uint16_t read_uint16();
// uint32_t read_uint32();
// uint64_t read_uint64();
// int8_t read_int8();
// int16_t read_int16();
// int32_t read_int32();
// int64_t read_int64();
// bool read_bool();
// char read_char();
// float read_float();
// double read_double();
// size_t read_length();
// std::string read_string_limit(uint64_t size);
// std::string read_string();
//
bool get_err_eof_on_read() const { return err_eof_on_read_; }
bool get_err_string_too_long() const { return err_string_too_long_; }
bool get_err_integer_truncated() const { return err_integer_truncated_; }
bool any_error() const { return err_eof_on_read_ || err_string_too_long_ || err_integer_truncated_; }
};
class FlxStreamBuffer : public BaseBuffer<FlxStreamBufferCore, std::string> {
public:
using BaseBuffer::BaseBuffer;
void write_dxyz(const FVector &xyz) {
write_double(xyz.X);
write_double(xyz.Y);
write_double(xyz.Z);
}
FVector read_dxyz() {
double x = read_double();
double y = read_double();
double z = read_double();
return FVector(x, y, z);
}
};