Checking in what I found on my hard drive

This commit is contained in:
2024-01-22 10:53:01 -05:00
26 changed files with 1008 additions and 575 deletions

1
.gitattributes vendored
View File

@@ -1,5 +1,6 @@
* text=auto * text=auto
*.bat text eol=crlf *.bat text eol=crlf
*.sh text eol=lf
*.lib filter=lfs diff=lfs merge=lfs -text *.lib filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text
*.a filter=lfs diff=lfs merge=lfs -text *.a filter=lfs diff=lfs merge=lfs -text

7
.gitignore vendored
View File

@@ -19,6 +19,13 @@ gmon.out
*.pdb *.pdb
*.sln *.sln
*.vcproj *.vcproj
.ignore
Integration.code-workspace
Makefile
Source/Integration/lpx-*.hpp
Source/Integration/lpx-*.cpp
.vscode/** .vscode/**
Saved/** Saved/**

BIN
Content/MeshStruct.uasset LFS Normal file

Binary file not shown.

BIN
Content/NameToMeshTable.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -2,9 +2,11 @@
#include "AnimQueue.h" #include "AnimQueue.h"
FlxAnimationStep::FlxAnimationStep(uint64 hash, std::string_view body) { FlxAnimationStep::FlxAnimationStep(uint64 hash, std::string_view body) {
Finished = false;
Hash = hash; Hash = hash;
Body.SetNum(body.size()); Body.SetNum(body.size());
memcpy(Body.GetData(), body.data(), body.size()); memcpy(Body.GetData(), body.data(), body.size());
Blueprint = UlxAnimationStepLibrary::AnimationStepGetString(*this, "bp");
} }
static bool ClearProperties(const FString& prefix, UObject* obj) { static bool ClearProperties(const FString& prefix, UObject* obj) {
@@ -27,32 +29,35 @@ static bool ClearProperties(const FString& prefix, UObject* obj) {
return true; return true;
} }
static bool SetProperty(const FString& name, UObject* obj, const FlxAnimationField& field) { #pragma optimize("", off)
static bool SetProperty(const FString& prefix, UObject* obj, const FlxAnimationField& field) {
UClass* uclass = obj->GetClass(); UClass* uclass = obj->GetClass();
FName nname(name); FString sname(field.Name.size(), (const UTF8CHAR*)field.Name.data());
FName nname(prefix + sname);
switch (field.Type) { switch (field.Type) {
case ElxAnimValueType::STRING: { case SimpleDynamicTag::STRING: {
FStrProperty* fprop = FindFProperty<FStrProperty>(uclass, nname); FStrProperty* fprop = FindFProperty<FStrProperty>(uclass, nname);
if (fprop == nullptr) return false; if (fprop == nullptr) return false;
FString* pptr = fprop->ContainerPtrToValuePtr<FString>(obj); FString* pptr = fprop->ContainerPtrToValuePtr<FString>(obj);
*pptr = FString(field.S.size(), (const UTF8CHAR*)field.S.data()); *pptr = FString(field.S.size(), (const UTF8CHAR*)field.S.data());
return true; return true;
} }
case ElxAnimValueType::NUMBER: { case SimpleDynamicTag::NUMBER: {
FDoubleProperty* fprop = FindFProperty<FDoubleProperty>(uclass, nname); FDoubleProperty* fprop = FindFProperty<FDoubleProperty>(uclass, nname);
if (fprop == nullptr) return false; if (fprop == nullptr) return false;
double* pptr = fprop->ContainerPtrToValuePtr<double>(obj); double* pptr = fprop->ContainerPtrToValuePtr<double>(obj);
fprop->SetPropertyValue(pptr, field.X); *pptr = field.X;
return true; return true;
} }
case ElxAnimValueType::BOOLEAN: { case SimpleDynamicTag::BOOLEAN: {
FBoolProperty* fprop = FindFProperty<FBoolProperty>(uclass, nname); FBoolProperty* fprop = FindFProperty<FBoolProperty>(uclass, nname);
if (fprop == nullptr) return false; if (fprop == nullptr) return false;
uint8* pptr = fprop->ContainerPtrToValuePtr<uint8>(obj); uint8* pptr = fprop->ContainerPtrToValuePtr<uint8>(obj);
fprop->SetPropertyValue(pptr, (field.X == 1.0)); fprop->SetPropertyValue(pptr, (field.X == 1.0));
return true; return true;
} }
case ElxAnimValueType::XYZ: { case SimpleDynamicTag::VECTOR: {
FStructProperty* fprop = FindFProperty<FStructProperty>(uclass, nname); FStructProperty* fprop = FindFProperty<FStructProperty>(uclass, nname);
if (fprop == nullptr) return false; if (fprop == nullptr) return false;
if (fprop->Struct != TBaseStructure<FVector>::Get()) return false; if (fprop->Struct != TBaseStructure<FVector>::Get()) return false;
@@ -64,73 +69,179 @@ static bool SetProperty(const FString& name, UObject* obj, const FlxAnimationFie
return false; return false;
} }
bool FlxAnimationStep::Unpack(const FString& prefix, UObject* into, bool preclear) const { #pragma optimize("", off)
bool FlxAnimationStep::Unpack(const FString& prefix, UObject* into) const {
UClass* uclass = into->GetClass(); UClass* uclass = into->GetClass();
std::string_view body((const char*)(Body.GetData()), Body.Num()); std::string_view body((const char*)(Body.GetData()), Body.Num());
FlxAnimationStepDecoder decoder(body); FlxAnimationStepDecoder decoder(body);
bool ok = true; bool ok = ClearProperties(prefix, into);
if (preclear) {
ok &= ClearProperties(prefix, into);
}
while (!decoder.AtEOF()) { while (!decoder.AtEOF()) {
FlxAnimationField field = decoder.ReadField(); FlxAnimationField field = decoder.ReadField();
FString sname(field.Name.size(), (const UTF8CHAR*)field.Name.data()); if (Finished && !field.Persistent) continue;
ok &= SetProperty(prefix + sname, into, field); ok &= SetProperty(prefix, into, field);
}
if (Finished) {
FlxAnimationField field;
field.Name = "action";
field.Persistent = false;
field.Type = SimpleDynamicTag::STRING;
field.S = "idle";
ok &= SetProperty(prefix, into, field);
} }
return ok; return ok;
} }
FString UlxAnimationStepLibrary::AnimationStepDebugString(const FlxAnimationStep& step) { FString UlxAnimationStepLibrary::AnimationStepDebugString(const FlxAnimationStep& step) {
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num()); std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
return FlxAnimationStepDecoder::DebugString(step.Hash, body); return FlxAnimationStepDecoder::DebugString(step.Finished, step.Finished, step.Hash, body);
} }
void UlxAnimationStepLibrary::UnpackAnimationStep(const FlxAnimationStep& step, void UlxAnimationStepLibrary::UnpackAnimationStep(UObject* into, const FlxAnimationStep& step, const FString& prefix) {
const FString& prefix, UObject* into) { step.Unpack(prefix, into);
step.Unpack(prefix, into, true); }
};
static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const FString& name) { static FlxAnimationField FindAnimationFieldLL(const FlxAnimationStep& step, std::string_view name) {
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num()); std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
FTCHARToUTF8 utf8name(name);
std::string_view uname(utf8name.Get(), utf8name.Length());
FlxAnimationStepDecoder decoder(body); FlxAnimationStepDecoder decoder(body);
FlxAnimationField result; FlxAnimationField result;
while (!decoder.AtEOF()) { while (!decoder.AtEOF()) {
result = decoder.ReadField(); result = decoder.ReadField();
if (result.Name == uname) { if (result.Name == name) {
return result; return result;
} }
} }
result.Type = ElxAnimValueType::INVALID; result.Type = SimpleDynamicTag::UNINITIALIZED;
return result; return result;
} }
static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const FString& name) {
FTCHARToUTF8 utf8name(name);
std::string_view uname(utf8name.Get(), utf8name.Length());
return FindAnimationFieldLL(step, uname);
}
void FlxAnimationStep::AutoUpdateXYZ(AActor *actor) const {
FlxAnimationField xyz = FindAnimationFieldLL(*this, "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 == SimpleDynamicTag::NUMBER) {
actor->SetActorRotation(FRotator(0, facing.X, 0));
}
}
void FlxAnimationStep::AutoUpdatePlane(FName *planep) const {
FlxAnimationField plane = FindAnimationFieldLL(*this, "plane");
if (plane.Type == SimpleDynamicTag::STRING) {
FString pname(plane.S.size(), (const UTF8CHAR*)(plane.S.data()));
*planep = FName(pname);
}
}
bool UlxAnimationStepLibrary::AnimationStepEqual(const FlxAnimationStep &stepa, const FlxAnimationStep &stepb) {
return (stepa.Finished == stepb.Finished) && (stepa.Hash == stepb.Hash);
}
bool UlxAnimationStepLibrary::AnimationStepIsIdle(const FlxAnimationStep &step) {
return step.Finished;
}
FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) { FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, 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); return FVector(field.X, field.Y, field.Z);
} }
double UlxAnimationStepLibrary::AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name) { double UlxAnimationStepLibrary::AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, 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; return field.X;
} }
#pragma optimize("", off)
FString UlxAnimationStepLibrary::AnimationStepGetString(const FlxAnimationStep& step, const FString& name) { FString UlxAnimationStepLibrary::AnimationStepGetString(const FlxAnimationStep& step, const FString& name) {
if (step.Finished && (name == TEXT("action"))) {
return TEXT("idle");
}
FlxAnimationField field = FindAnimationField(step, name); 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())); return FString(field.S.size(), (const UTF8CHAR*)(field.S.data()));
} }
bool UlxAnimationStepLibrary::AnimationStepGetBool(const FlxAnimationStep& step, const FString& name) { bool UlxAnimationStepLibrary::AnimationStepGetBool(const FlxAnimationStep& step, const FString& name) {
FlxAnimationField field = FindAnimationField(step, 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; return field.X == 1.0;
} }
FlxAnimationField FlxAnimationStepDecoder::ReadField() {
FlxAnimationField result;
result.Name = Decoder.read_string_view();
result.Persistent = Decoder.read_bool();
result.Type = (SimpleDynamicTag)Decoder.read_uint8();
switch (result.Type) {
case SimpleDynamicTag::STRING: {
result.S = Decoder.read_string_view();
break;
}
case SimpleDynamicTag::NUMBER: {
result.X = Decoder.read_double();
break;
}
case SimpleDynamicTag::BOOLEAN: {
result.X = Decoder.read_bool() ? 1.0 : 0.0;
break;
}
case SimpleDynamicTag::VECTOR: {
result.X = Decoder.read_double();
result.Y = Decoder.read_double();
result.Z = Decoder.read_double();
break;
}
default: {
Decoder.read_bytes(Decoder.fill());
result.Type = SimpleDynamicTag::UNINITIALIZED;
break;
}
}
return result;
}
FString FlxAnimationStepDecoder::DebugString(bool injectidle, bool persistentonly, uint64 hash, std::string_view body) {
FString result;
FlxAnimationStepDecoder decoder(body);
result.Appendf(TEXT("Hash=%016llx"), hash);
if (injectidle) {
result.Appendf(TEXT(" action=idle"));
}
while (!decoder.AtEOF()) {
FlxAnimationField field = decoder.ReadField();
if (persistentonly && !field.Persistent) continue;
result.Append(TEXT(" "));
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
switch (field.Type) {
case SimpleDynamicTag::STRING:
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
break;
case SimpleDynamicTag::NUMBER:
result.Appendf(TEXT("%lf"), field.X);
break;
case SimpleDynamicTag::BOOLEAN:
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
break;
case SimpleDynamicTag::VECTOR:
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
break;
}
}
return result;
}
FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() { FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
FlxAnimationStepView result; FlxAnimationStepView result;
result.Hash = Decoder.read_uint64(); result.Hash = Decoder.read_uint64();
@@ -138,65 +249,16 @@ FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
return result; return result;
} }
FlxAnimationField FlxAnimationStepDecoder::ReadField() { uint64 FlxAnimQueueDecoder::PeekHash() {
FlxAnimationField result; int64_t read_count = Decoder.total_reads();
result.Name = Decoder.read_string_view(); uint64 result = Decoder.read_uint64();
result.Persistent = Decoder.read_bool(); Decoder.unread_to(read_count);
result.Type = (ElxAnimValueType)Decoder.read_uint8();
switch (result.Type) {
case ElxAnimValueType::STRING: {
result.S = Decoder.read_string_view();
break;
}
case ElxAnimValueType::NUMBER: {
result.X = Decoder.read_double();
break;
}
case ElxAnimValueType::BOOLEAN: {
result.X = Decoder.read_bool() ? 1.0 : 0.0;
break;
}
case ElxAnimValueType::XYZ: {
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;
break;
}
}
return result; return result;
} }
FString FlxAnimationStepDecoder::DebugString(uint64 hash, std::string_view body) { FlxAnimQueueDecoder::FlxAnimQueueDecoder(std::string_view queue) : Decoder(queue) {
FString result; SizeLimit = Decoder.read_uint8();
FlxAnimationStepDecoder decoder(body); ActualSize = Decoder.read_uint8();
result.Appendf(TEXT("Hash=%016llx"), hash);
while (!decoder.AtEOF()) {
FlxAnimationField field = decoder.ReadField();
result.Append(TEXT(" "));
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
switch (field.Type) {
case ElxAnimValueType::STRING:
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
break;
case ElxAnimValueType::NUMBER:
result.Appendf(TEXT("%lf"), field.X);
break;
case ElxAnimValueType::BOOLEAN:
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
break;
case ElxAnimValueType::XYZ:
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
break;
}
}
return result;
} }
FString FlxAnimQueueDecoder::DebugString(std::string_view queue) { FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
@@ -204,42 +266,62 @@ FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
FlxAnimQueueDecoder decoder(queue); FlxAnimQueueDecoder decoder(queue);
while (!decoder.AtEOF()) { while (!decoder.AtEOF()) {
FlxAnimationStepView step = decoder.ReadStep(); FlxAnimationStepView step = decoder.ReadStep();
FString stepdebug = FlxAnimationStepDecoder::DebugString(step.Hash, step.Body); FString stepdebug = FlxAnimationStepDecoder::DebugString(false, false, step.Hash, step.Body);
result.Appendf(TEXT("%s\n"), *stepdebug); result.Appendf(TEXT("%s\n"), *stepdebug);
} }
return result; return result;
} }
FlxAnimTracker::FlxAnimTracker() { FlxAnimTracker::FlxAnimTracker() {
Clear();
}
void FlxAnimTracker::Clear() {
AQ.Empty(); AQ.Empty();
FirstSeqno = 0; Changed = true;
HashToSeqno.Empty(); }
UnstartedSeqno = 0;
PlaybackMode = ElxAnimationMode::INVALID; void FlxAnimTracker::FinishedAnimation(uint64 hash) {
AbortedHashes.Empty(); for (int i = 0; i < AQ.Num(); i++) {
if (AQ[i].Hash == hash) {
AQ[i].Finished = true;
Changed = true;
}
}
}
void FlxAnimTracker::SkipToEnd() {
for (int i = 0; i < AQ.Num(); i++) {
if (!AQ[i].Finished) {
AQ[i].Finished = true;
Changed = true;
}
}
} }
void FlxAnimTracker::Update(std::string_view encqueue) { void FlxAnimTracker::Update(std::string_view encqueue) {
// Check for an exact match. If the most recent entry check(!encqueue.empty());
// in encqueue has the same hash as the most recent
// entry in AQ, then that's an exact match. Or, // If the first hash matches, we don't bother updating at all.
// if both are empty, that's also an exact match.
// In case of exact match, abort early, there's no
// further work needed.
// //
if (encqueue.empty()) { FlxAnimQueueDecoder decoder(encqueue);
if (AQ.IsEmpty()) { if (!AQ.IsEmpty()) {
if (decoder.PeekHash() == AQ.Last().Hash) {
return; return;
} }
} else { }
if (!AQ.IsEmpty()) {
FlxStringDecoder qdecoder(encqueue);
uint64 hash = qdecoder.read_uint64(); // The Changed flag is set whenever there is any change to
if (hash == AQ.Last().Hash) { // the animation queue of any kind.
return; //
} Changed = true;
}
// Build a map indexing the steps in AQ.
//
TMap<uint64, int32> HashToIndex;
for (int i = 0; i < AQ.Num(); i++) {
HashToIndex.Emplace(AQ[i].Hash, i);
} }
// Search for a Luprex animation record that matches // Search for a Luprex animation record that matches
@@ -249,108 +331,59 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
// At the same time, push all non-matching records // At the same time, push all non-matching records
// after the matching record onto a stack of new records. // after the matching record onto a stack of new records.
// //
FlxAnimQueueDecoder decoder(encqueue);
TArray<FlxAnimationStepView> newsteps; TArray<FlxAnimationStepView> newsteps;
int32 matchingseqno = -1; int32 matchingindex = -1;
while (!decoder.AtEOF()) { while (!decoder.AtEOF()) {
FlxAnimationStepView step = decoder.ReadStep(); FlxAnimationStepView step = decoder.ReadStep();
int32* stepseq = HashToSeqno.Find(step.Hash); int32* indexp = HashToIndex.Find(step.Hash);
if (stepseq == nullptr) { if (indexp == nullptr) {
newsteps.Emplace(step); newsteps.Emplace(step);
} else { } else {
matchingseqno = *stepseq; matchingindex = *indexp;
break; break;
} }
} }
// Remove all animations after the most recent matching // Remove all animations after the most recent matching
// record. If we remove a 'started' animation, add that // record.
// animation to the list of aborted animations.
// //
int32 nremove = (FirstSeqno + AQ.Num()) - (matchingseqno + 1); int32 nremove = (AQ.Num() - (matchingindex + 1));
check((nremove >= 0) && (nremove <= AQ.Num())); check((nremove >= 0) && (nremove <= AQ.Num()));
for (int32 i = 0; i < nremove; i++) { for (int32 i = 0; i < nremove; i++) {
uint64 lasthash = AQ.Last().Hash;
int32 seqno = FirstSeqno + AQ.Num() - 1;
HashToSeqno.Remove(lasthash);
if (seqno < UnstartedSeqno) {
AbortedHashes.Emplace(lasthash);
}
AQ.PopLast(); AQ.PopLast();
} }
// If we aborted a 'started' animation, we have to fix
// up the Unstarted animation pointer.
//
// Note: this could leave the Unstarted pointer at
// seqno less than FirstSeqno. We will fix that state
// of affairs up later.
//
if (UnstartedSeqno > (FirstSeqno + AQ.Num())) {
UnstartedSeqno = matchingseqno;
PlaybackMode = ElxAnimationMode::BlendToFinal;
}
// Transfer the new animations onto the queue. // Transfer the new animations onto the queue.
// //
while (!newsteps.IsEmpty()) { while (!newsteps.IsEmpty()) {
FlxAnimationStepView step = newsteps.Pop(); FlxAnimationStepView step = newsteps.Pop();
int32 seqno = FirstSeqno + AQ.Num();
AQ.EmplaceLast(step.Hash, step.Body); AQ.EmplaceLast(step.Hash, step.Body);
HashToSeqno.Emplace(step.Hash, seqno);
} }
// If there are too many animations in AQ, discard // If there are too many animations in AQ, discard
// any very old ones. // any very old ones.
// //
if (AQ.Num() > 10) { // TODO: this is hardwired to keep 10. Instead, we
int ndiscard = AQ.Num() - 10; // should keep the number specified in the queue.
for (int i = 0; i < ndiscard; i++) {
uint64 hash = AQ.First().Hash;
HashToSeqno.Remove(hash);
AQ.PopFirst();
FirstSeqno += 1;
}
}
// If UnstartedSeqno is before the live range,
// then the whole queue has to be replayed. Don't
// do that: just fast skip to the end.
// //
if (UnstartedSeqno <= FirstSeqno) { int limit = 10;
if (AQ.Num() == 0) { int ndiscard = AQ.Num() - limit;
UnstartedSeqno = FirstSeqno; if (ndiscard > 0) {
} else { for (int i = 0; i < ndiscard; i++) {
UnstartedSeqno = FirstSeqno + AQ.Num() - 1; AQ.PopFirst();
} }
PlaybackMode = ElxAnimationMode::WarpToFinal;
} }
} }
TArray<uint64> FlxAnimTracker::GetAborted() { FlxAnimationStep FlxAnimTracker::GetCurrentAnimation() {
TArray<uint64> result; FlxAnimationStep result;
result = std::move(AbortedHashes); for (int i = 0; i < AQ.Num(); i++) {
AbortedHashes.Empty(); if (!AQ[i].Finished) {
return AQ[i];
}
}
result = AQ.Last();
result.Hash = 0;
return result; return result;
} }
ElxAnimationMode FlxAnimTracker::GetNextStep(FlxAnimationStep &step) {
int offset = UnstartedSeqno - FirstSeqno;
if (offset < AQ.Num()) {
step = AQ[offset];
return PlaybackMode;
} else {
step.Hash = 0;
step.Body.Empty();
return ElxAnimationMode::INVALID;
}
}
void FlxAnimTracker::StartedStep(uint64 hash) {
int offset = UnstartedSeqno - FirstSeqno;
check(offset < AQ.Num());
check(AQ[offset].Hash == hash);
UnstartedSeqno += 1;
PlaybackMode = ElxAnimationMode::StartAnimation;
}

View File

@@ -5,46 +5,6 @@
#include "Containers/Deque.h" #include "Containers/Deque.h"
#include "AnimQueue.generated.h" #include "AnimQueue.generated.h"
////////////////////////////////////////////////
//
// This is copied over from Luprex source. Not ideal.
//
////////////////////////////////////////////////
enum class ElxAnimValueType {
STRING,
NUMBER,
BOOLEAN,
XYZ,
INVALID
};
////////////////////////////////////////////////
//
// Playback Modes
//
// There are three different ways to play an animation:
//
// 1. Start Animation. This is the normal way to play an animation.
//
// 2. Warp to Final. Skip the actual animation, and jump
// instantaneously to the final state of the animation.
//
// 3. Blend to Final. Skip the actual animation, and blend
// smoothly to the final state of the animation. If the current
// state is very far from the final state, then maybe
// jump instantaneously instead.
//
////////////////////////////////////////////////
UENUM(BlueprintType)
enum class ElxAnimationMode : uint8 {
StartAnimation,
WarpToFinal,
BlendToFinal,
INVALID,
};
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// An single animation step. // An single animation step.
@@ -61,13 +21,19 @@ struct INTEGRATION_API FlxAnimationStep {
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY()
bool Finished;
UPROPERTY() UPROPERTY()
uint64 Hash; uint64 Hash;
UPROPERTY() UPROPERTY()
TArray<uint8> Body; TArray<uint8> Body;
FlxAnimationStep() : Hash(0), Body() {} UPROPERTY()
FString Blueprint;
FlxAnimationStep() : Finished(false), Hash(0), Body(), Blueprint() {}
FlxAnimationStep(uint64 h, std::string_view b); FlxAnimationStep(uint64 h, std::string_view b);
// Unpack an AnimStep into a UObject // Unpack an AnimStep into a UObject
@@ -78,6 +44,14 @@ public:
// routine tries to find a string property "color" in the // routine tries to find a string property "color" in the
// UObject, and then it sets that property to "blue." // UObject, and then it sets that property to "blue."
// //
// The prefix is prepended to the key names. For example,
// if one of the key-value pairs is "color=blue", and the
// prefix is "aq", then the property "aqColor=blue" will be
// stored in the UObject.
//
// All properties of the UObject starting with the specified
// prefix are cleared before unpacking the animation step.
//
// Returns true if all of the key-value pairs in the // Returns true if all of the key-value pairs in the
// animation step could be unpacked into fields of the UObject. // animation step could be unpacked into fields of the UObject.
// This could fail, for instance, if the UObject just doesn't // This could fail, for instance, if the UObject just doesn't
@@ -85,16 +59,23 @@ public:
// fail if there's a type mismatch. For example, "color=blue" // fail if there's a type mismatch. For example, "color=blue"
// cannot be stored in a property "color" of type int. // cannot be stored in a property "color" of type int.
// //
// The prefix is prepended to the key names. For example, // Automatically injects a boolean property "idle" representing
// if one of the key-value pairs is "color=blue", and the // whether the property's finished flag is set.
// prefix is "aq", then the property "aqColor=blue" will be
// stored in the UObject.
// //
// If pre-clear is true, then all properties of the UObject bool Unpack(const FString& prefix, UObject* into) const;
// starting with the specified prefix are cleared before
// unpacking the animation step. // Auto-Execute
// //
bool Unpack(const FString& prefix, UObject* into, bool preclear = true) const; // These functions automatically update certain actor
// properties:
//
// AutoUpdateXYZ(AActor *actor); // uses 'xyz' keyword
// AutoUpdateFacing(AActor *actor); // uses 'facing' keyword.
// AutoUpdatePlane(FName *plane); // uses 'plane' keyword
//
void AutoUpdateXYZ(AActor *actor) const;
void AutoUpdateFacing(AActor *actor) const;
void AutoUpdatePlane(FName *plane) const;
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -114,9 +95,14 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category = Luprex) UFUNCTION(BlueprintCallable, BlueprintPure, Category = Luprex)
static FString AnimationStepDebugString(const FlxAnimationStep& step); static FString AnimationStepDebugString(const FlxAnimationStep& step);
UFUNCTION(BlueprintCallable, Category = Luprex) UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "into"), Category = Luprex)
static void UnpackAnimationStep(const FlxAnimationStep& step, static void UnpackAnimationStep(UObject* into, const FlxAnimationStep& step, const FString& VariableNamePrefix = TEXT("aq"));
const FString& VariableNamePrefix, UObject* into);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
static bool AnimationStepEqual(const FlxAnimationStep &StepA, const FlxAnimationStep &StepB);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
static bool AnimationStepIsIdle(const FlxAnimationStep &step);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex") UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name); static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name);
@@ -129,18 +115,13 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex") UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name); static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name);
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// Exposing functions to blueprints.
// //
//////////////////////////////////////////////// ////////////////////////////////////////////////
//UFUNCTION(BlueprintCallable)
//void Unpack(const FString& prefix, UObject* into, bool preclear = true);
struct FlxAnimationStepView { struct FlxAnimationStepView {
uint64 Hash; uint64 Hash;
std::string_view Body; std::string_view Body;
@@ -166,7 +147,7 @@ struct FlxAnimationStepView {
struct FlxAnimationField { struct FlxAnimationField {
std::string_view Name; std::string_view Name;
bool Persistent; bool Persistent;
ElxAnimValueType Type; SimpleDynamicTag Type;
double X, Y, Z; double X, Y, Z;
std::string_view S; std::string_view S;
}; };
@@ -184,21 +165,38 @@ struct FlxAnimationField {
class FlxAnimQueueDecoder { class FlxAnimQueueDecoder {
private: private:
FlxStringDecoder Decoder; FlxStreamBuffer Decoder;
// These values are immediately read from the header.
//
int SizeLimit;
int ActualSize;
public: public:
// Initialize the FlxAnimQueueDecoder with the encoded animation queue. // Initialize the FlxAnimQueueDecoder with the encoded animation queue.
// //
FlxAnimQueueDecoder(std::string_view s) : Decoder(s) {} FlxAnimQueueDecoder(std::string_view s);
// Get the size limit of the animation queue.
//
int GetSizeLimit() const { return SizeLimit; }
// Get the Actual Size of the animation queue.
//
int GetActualSize() const { return ActualSize; }
// Return true if the parser has reached the end of the string. // 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. // Read one animation step.
// //
FlxAnimationStepView ReadStep(); FlxAnimationStepView ReadStep();
// Peek at the hash of the next animation step.
//
uint64 PeekHash();
// Convert an AnimQueue to an FString. // Convert an AnimQueue to an FString.
// //
static FString DebugString(std::string_view s); static FString DebugString(std::string_view s);
@@ -217,7 +215,7 @@ public:
class FlxAnimationStepDecoder { class FlxAnimationStepDecoder {
private: private:
FlxStringDecoder Decoder; FlxStreamBuffer Decoder;
public: public:
// Initialize the FlxAnimationStepDecoder from the FlxAnimationStepView. // Initialize the FlxAnimationStepDecoder from the FlxAnimationStepView.
@@ -226,7 +224,7 @@ public:
// Return true if the parser has reached the end of the string. // 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. // Read one field.
// //
@@ -234,7 +232,7 @@ public:
// Convert an AnimStep to an FString. // Convert an AnimStep to an FString.
// //
static FString DebugString(uint64 hash, std::string_view body); static FString DebugString(bool injectidle, bool persistentonly, uint64 hash, std::string_view body);
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -257,26 +255,11 @@ public:
// //
TDeque<FlxAnimationStep> AQ; TDeque<FlxAnimationStep> AQ;
// The sequence number of the first item in AQ. // True if something has recently changed.
// //
int32 FirstSeqno; bool Changed;
// Map from hash to sequence number.
//
TMap<uint64, int32> HashToSeqno;
// The sequence number of the first unstarted animation.
//
int32 UnstartedSeqno;
// Indicates whether the unstarted animation should be played or otherwise.
//
ElxAnimationMode PlaybackMode;
// Array of recently-aborted hash values.
//
TArray<uint64> AbortedHashes;
private:
public: public:
// Construct a tracker. // Construct a tracker.
// //
@@ -284,6 +267,10 @@ public:
// //
FlxAnimTracker(); FlxAnimTracker();
// Clear everything, reset to the initial state.
//
void Clear();
// Update from the specified animation queue. // Update from the specified animation queue.
// //
// After the update is done, AQ will be a copy // After the update is done, AQ will be a copy
@@ -291,24 +278,39 @@ public:
// //
void Update(std::string_view encqueue); void Update(std::string_view encqueue);
// Fetch the aborted hash values. // Get the current animation step.
// //
// This gets the array of aborted hashes and clears // Get the current animation step. This is the step that the
// the stored array. // blueprint should currently be playing.
// //
TArray<uint64> GetAborted(); FlxAnimationStep GetCurrentAnimation();
// Get the next unstarted animation step. // Declare that an animation is finished.
// //
// Get the next animation step. Returns the next step and the // The blueprint uses this function call to indicate that it
// playback mode. If the playback mode is INVALID then there is // is done playing the specified animation. This will cause the
// no next step to play // animation to be marked as finished, which in turn causes
// 'GetCurrentStep' to advance to the next animation.
// //
ElxAnimationMode GetNextStep(FlxAnimationStep& step); void FinishedAnimation(uint64 Hash);
// Declare that an animation has been started. // Skip to the end of the animation queue.
// //
// After starting an animation, you should call this. // This is equivalent to calling 'FinishedHash' on every
// animation in the entire queue.
// //
void StartedStep(uint64 Hash); void SkipToEnd();
// Clear the 'Changed' flag.
//
void ClearChanged() { Changed = false; }
// Get the 'Changed' flag.
//
// The changed flag is set to true whenever the Luprex animation
// queue changes from its previous state. The changed flag is also
// set to true whenever 'SetFinished' marks an animation as finished.
// The changed flag can only be set to false by 'ClearChanged,' above.
//
bool IsChanged() const { return Changed; }
}; };

View File

@@ -2,10 +2,10 @@
#include "IntegrationGameModeBase.h" #include "IntegrationGameModeBase.h"
#include "lpx-drvutil.hpp" #include "lpx-drvutil.hpp"
#include "lpx-paths.hpp"
#include "DebugPrint.h" #include "DebugPrint.h"
#include "Tangible.h" #include "Tangible.h"
#include "TangibleManager.h" #include "TangibleManager.h"
#include "TangibleInterface.h"
#include "CommonTypes.h" #include "CommonTypes.h"
#include "AnimQueue.h" #include "AnimQueue.h"
#include <string> #include <string>
@@ -16,6 +16,7 @@ using namespace CommonTypes;
AIntegrationGameModeBase::AIntegrationGameModeBase() AIntegrationGameModeBase::AIntegrationGameModeBase()
{ {
TangibleManager = NewObject<UlxTangibleManager>();
EngineSeconds = 0.0; EngineSeconds = 0.0;
NextThreadTrigger = 1.0; NextThreadTrigger = 1.0;
//PrimaryActorTick.bCanEverTick = true; // Probably wrong //PrimaryActorTick.bCanEverTick = true; // Probably wrong
@@ -47,6 +48,11 @@ void AIntegrationGameModeBase::ResetToInitialState()
{ {
Playing = false; Playing = false;
if (TangibleManager != nullptr) {
TangibleManager->ConditionalBeginDestroy();
TangibleManager = nullptr;
}
// Shut down the thread // Shut down the thread
LuprexUpdateTask.Shutdown(); LuprexUpdateTask.Shutdown();
@@ -104,67 +110,73 @@ void AIntegrationGameModeBase::MaybeTriggerUpdateTask(float deltaseconds) {
} }
} }
//#pragma optimize( "", off )
//void SetLocal(UObject* obj, const char *name, int value) {
// FString sname((const UTF8CHAR *)name); #pragma optimize("", off)
// FName nname(sname);
// UClass* uclass = obj->GetClass();
// FProperty* fprop = FindFProperty<FProperty>(uclass, nname);
// FStructProperty* sprop = FindFProperty<FStructProperty>(uclass, nname);
//}
void AIntegrationGameModeBase::UpdateTangibles() { void AIntegrationGameModeBase::UpdateTangibles() {
double radius = 1000.0; // Hardwired for now.
using TanArray = UlxTangibleManager::TanArray;
if (!Playing) return; if (!Playing) return;
FlxLockedWrapper w(LockableWrapper); FlxLockedWrapper w(LockableWrapper);
int64 actor = w.GetActor(); int64 player = w.GetActor();
TangibleManager.SetActor(actor); IdView nearids = w.GetNear(player, radius, radius, radius);
TangibleManager.SetNear(w.GetNear(actor, 100, 100, 100)); TangibleManager->UpdateNearAccordingToLuprex(nearids);
for (int64 id : TangibleManager.GetNear()) { TanArray alltans = TangibleManager->GetAllTangibles();
TangibleManager.MakeTangible(id); IdArray allids = TangibleManager->GetIds(alltans);
StringViewVec allqueues = w.GetAnimationQueues(allids);
for (int i = 0; i < alltans.Num(); i++) {
alltans[i]->UpdateAnimationQueue(allqueues[i]);
} }
// Update animation queues of live tangibles. TangibleManager->RecalcNearAccordingToUnreal(player, radius);
IdArray tanids = TangibleManager.GetLive(); TangibleManager->DeleteFarawayTangibles();
StringViewVec aqueues = w.GetAnimationQueues(tanids); }
for (int i = 0; i < tanids.Num(); i++) {
uint64_t tanid = tanids[i];
std::string_view aqueue = aqueues[i];
UlxTangible* t = TangibleManager.GetTangible(tanid);
check(t != nullptr);
t->AnimTracker.Update(aqueue);
TArray<uint64> aborted = t->AnimTracker.GetAborted(); void AIntegrationGameModeBase::ExecuteDebuggingCommand(const FString &fs) {
for (uint64 hash : aborted) { if (fs == "\\invokeplayer") {
IlxTangibleInterface::Execute_AbortAnimation(t->Actor, hash); DPrint(TEXT("Trying to invoke 'myfunction' in lua"));
} FlxLockedWrapper w(LockableWrapper);
FlxAnimationStep step; FlxStreamBuffer sb;
ElxAnimationMode mode = t->AnimTracker.GetNextStep(step); sb.write_string("myfunction");
if (mode != ElxAnimationMode::INVALID) { sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
bool started = IlxTangibleInterface::Execute_StartAnimation(t->Actor, mode, step); sb.write_double(3.0);
if (started) { sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING);
t->AnimTracker.StartedStep(step.Hash); sb.write_string("Howdy");
} sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR);
} sb.write_fvector(FVector(2,3,4));
std::string_view datapk = sb.view();
int64 player = w.GetActor();
w->play_invoke_engio(w.Get(), player, datapk.size(), datapk.data());
} else {
ConsoleOutput.AppendLine(TEXT("Unknown Command"));
} }
} }
void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs) void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs)
{ {
FlxLockedWrapper w(LockableWrapper); FlxLockedWrapper w(LockableWrapper);
if (w->engine != nullptr) if (w->engine != nullptr)
{ {
const TCHAR* fstchar = *fs; ConsoleOutput.AppendLine(FString("> ") + fs);
if (sizeof(TCHAR) == 2) // 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
ConsoleOutput.AppendLine(FString("> ") + fs); // through. There's no simple solution, though.
std::u16string_view fsview((const char16_t*)fstchar, fs.Len()); if (fs[0] == '\\') {
std::string utf8 = drvutil::utf16_to_utf8(fsview); ExecuteDebuggingCommand(fs);
utf8 = utf8 + "\n"; } else {
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str()); 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) void AIntegrationGameModeBase::Tick(float deltaseconds)
{ {
Super::Tick(deltaseconds); Super::Tick(deltaseconds);
@@ -209,22 +221,25 @@ void AIntegrationGameModeBase::BeginPlay()
if (w->play_initialize != nullptr) if (w->play_initialize != nullptr)
{ {
drvutil::ostringstream srcpak; drvutil::ostringstream srcpak;
std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak); std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak);
if (!srcpakerr.empty()) if (!srcpakerr.empty())
{ {
DPrint(srcpakerr.c_str()); DPrint(srcpakerr.c_str());
} }
std::string_view srcpakv = srcpak.view(); else
char* argv[1];
argv[0] = const_cast<char*>("lpxserver");
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
if (w->error[0])
{ {
DPrint(w->error); std::string_view srcpakv = srcpak.view();
} char* argv[1];
if (w->engine != nullptr) { argv[0] = const_cast<char*>("lpxserver");
DPrint("Luprex initialize success"); w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
Playing = true; if (w->error[0])
{
DPrint(w->error);
}
if (w->engine != nullptr) {
DPrint("Luprex initialize success");
Playing = true;
}
} }
} }
@@ -237,7 +252,8 @@ void AIntegrationGameModeBase::BeginPlay()
} }
// Initialize the tangible manager. // Initialize the tangible manager.
TangibleManager.Init(GetWorld(), ClassTangibleActor); TangibleManager = NewObject<UlxTangibleManager>();
TangibleManager->Init(GetWorld(), ClassTangibleActor);
} }
void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason) void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
@@ -245,4 +261,3 @@ void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
ResetToInitialState(); ResetToInitialState();
} }

View File

@@ -43,6 +43,9 @@ public:
UPROPERTY(EditDefaultsOnly, Category = "Luprex") UPROPERTY(EditDefaultsOnly, Category = "Luprex")
TSubclassOf<AActor> ClassTangibleActor; 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. // Transfer console output from the Luprex engine to unreal.
void UpdateConsoleOutput(); void UpdateConsoleOutput();
@@ -57,7 +60,7 @@ public:
virtual uint32 Run() override; virtual uint32 Run() override;
UPROPERTY() UPROPERTY()
FTangibleManager TangibleManager; UlxTangibleManager *TangibleManager;
// This stores the entire text currently visible in the console. // This stores the entire text currently visible in the console.
FlxConsoleOutput ConsoleOutput; FlxConsoleOutput ConsoleOutput;

View File

@@ -2,6 +2,7 @@
#include "LockedWrapper.h" #include "LockedWrapper.h"
#include "DebugPrint.h" #include "DebugPrint.h"
#include "lpx-drvutil.hpp" #include "lpx-drvutil.hpp"
#include "lpx-paths.hpp"
using namespace CommonTypes; using namespace CommonTypes;
@@ -10,7 +11,9 @@ void FlxLockedWrapper::InitWrapper() {
// Already initialized. // Already initialized.
return; return;
} }
void* DLL = FPlatformProcess::GetDllHandle(TEXT("c:\\Luprex\\build\\visual\\luprexlib.dll")); FString dll((const UTF8CHAR*)LUPREX_DLL_PATH);
DebugPrint::DPrint(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"));
@@ -43,7 +46,7 @@ int64 FlxLockedWrapper::GetActor() {
IdView FlxLockedWrapper::GetNear(int64 id, double rx, double ry, double rz) { IdView FlxLockedWrapper::GetNear(int64 id, double rx, double ry, double rz) {
uint32 size; uint32 size;
int64* data; int64* data;
Lockable.Wrapper.get_tangibles_near(Get(), id, rx, ry, rz, &size, &data); Lockable.Wrapper.get_tangibles_near(Get(), id, rx, ry, rz, &size, (int64_t**)&data);
return IdView(data, size); return IdView(data, size);
} }
@@ -64,7 +67,7 @@ StringViewVec FlxLockedWrapper::GetAnimationQueues(IdView ids) {
const char** StrBuf = Lockable.AQStrBuffer.GetData(); const char** StrBuf = Lockable.AQStrBuffer.GetData();
// Get the animation queues into the static buffers. // Get the animation queues into the static buffers.
Lockable.Wrapper.get_animation_queues(Get(), num, ids.GetData(), LenBuf, StrBuf); Lockable.Wrapper.get_animation_queues(Get(), num, (const int64_t *)ids.GetData(), LenBuf, StrBuf);
// Transfer data from static buffers into an array of string_view // Transfer data from static buffers into an array of string_view
StringViewVec result; StringViewVec result;

View File

@@ -23,9 +23,13 @@ THIRD_PARTY_INCLUDES_START
THIRD_PARTY_INCLUDES_END THIRD_PARTY_INCLUDES_END
#undef UI #undef UI
#ifdef __linux__
#else
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <wincrypt.h> #include <wincrypt.h>
#endif
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <string_view> #include <string_view>
@@ -241,6 +245,26 @@ public:
// //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
#ifdef __linux__
inline static void strerror_helper(int status, int errnum, char errbuf[256]) {
if (status != 0) {
snprintf(errbuf, 256, "unknown errno %d", errnum);
}
}
inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) {
if (result != errbuf) {
snprintf(errbuf, 256, "%s", result);
}
}
static std::string strerror_str(int errnum) {
char buf[256];
auto rval = strerror_r(errnum, buf, 256);
strerror_helper(rval, errnum, buf);
return buf;
}
#else
static std::string strerror_str(int errnum) { static std::string strerror_str(int errnum) {
char buf[256]; char buf[256];
int status = strerror_s(buf, 256, errnum); int status = strerror_s(buf, 256, errnum);
@@ -250,6 +274,8 @@ static std::string strerror_str(int errnum) {
} }
return buf; return buf;
} }
#endif
static FSocket* OpenConnection(ISocketSubsystem *subsys, const std::string& host, const std::string& port, std::string& err) static FSocket* OpenConnection(ISocketSubsystem *subsys, const std::string& host, const std::string& port, std::string& err)
{ {
@@ -401,6 +427,12 @@ static SSL_CTX* SSLNewContext(int verify, const SSL_METHOD *method, BIO *tracebi
return ctx; return ctx;
} }
#ifdef __linux__
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
check(SSL_CTX_set_default_verify_paths(ctx) == 1);
return "";
}
#else
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) { static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT"); HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
@@ -427,6 +459,7 @@ static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
CertCloseStore(hStore, 0); CertCloseStore(hStore, 0);
return ""; return "";
} }
#endif
static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) { static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) {
SSLClearErrors(); SSLClearErrors();
@@ -747,7 +780,7 @@ void FLpxChannel::Advance()
AdvanceReadWrite(); AdvanceReadWrite();
break; break;
default: default:
checkf(false, L"EChanState is invalid"); checkf(false, TEXT("EChanState is invalid"));
break; break;
} }

View File

@@ -0,0 +1,34 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "SampleActorComponent.h"
// Sets default values for this component's properties
USampleActorComponent::USampleActorComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void USampleActorComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void USampleActorComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}

View File

@@ -0,0 +1,28 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SampleActorComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class INTEGRATION_API USampleActorComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
USampleActorComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

View File

@@ -1,26 +1,2 @@
#include "StringDecoder.h" #include "StringDecoder.h"
FlxStringDecoder::FlxStringDecoder(std::string_view s) {
Text = s.data();
Size = s.size();
ErrBeyondEOF = false;
ErrStringTooLong = false;
}
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

@@ -1,116 +1,46 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "lpx-basewriter.hpp" #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> { using FlxSimpleDynamic = SimpleDynamic<std::string>;
class FlxStreamBufferCore {
private: private:
const char* Text; bool err_eof_on_read_;
size_t Size; bool err_string_too_long_;
bool err_integer_truncated_;
protected:
public: void *basebuffer_malloc(size_t size) { return malloc(size); }
// You can check and clear these error flags at will. void basebuffer_free(void *p) { free(p); }
// void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; }
bool ErrBeyondEOF; void raise_eof_on_read() { err_eof_on_read_ = true; }
bool ErrStringTooLong; void raise_string_too_long() { err_string_too_long_ = true; }
void raise_integer_truncated() { err_integer_truncated_ = true; }
public: bool get_err_eof_on_read() const { return err_eof_on_read_; }
// This function is required by BaseReader. bool get_err_string_too_long() const { return err_string_too_long_; }
// It's not meant for end users. 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_; }
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 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();
//
}; };
class FlxStreamBuffer : public BaseBuffer<FlxStreamBufferCore, std::string> {
public:
using BaseBuffer::BaseBuffer;
void write_fvector(const FVector &xyz) {
write_double(xyz.X);
write_double(xyz.Y);
write_double(xyz.Z);
}
FVector read_fvector() {
double x = read_double();
double y = read_double();
double z = read_double();
return FVector(x, y, z);
}
};

View File

@@ -2,4 +2,132 @@
#include "Tangible.h" #include "Tangible.h"
#include "TangibleManager.h"
UlxTangible::UlxTangible()
{
Manager = nullptr;
TangibleId = -1;
CurrentActor = nullptr;
ActorBlueprint = nullptr;
NearAccordingToLuprex = false;
NearAccordingToUnreal = false;
}
void UlxTangible::Init(UlxTangibleManager* tm, int64 id)
{
Manager = tm;
TangibleId = id;
}
bool UlxTangible::BlueprintIsTangible(TSubclassOf<AActor> bp) {
if (bp == nullptr) return true;
return bp->ImplementsInterface(UlxTangibleInterface::StaticClass());
}
void UlxTangible::SetActorBlueprintClass(TSubclassOf<AActor> bp) {
// If we're already of the right class, do nothing.
if (ActorBlueprint == bp) {
return;
}
// Sanity check the blueprint. Nullptr is allowed.
check(BlueprintIsTangible(bp));
// If there's already an actor, delete it.
if (CurrentActor != nullptr) {
// Remove the tangible component. This is probably
// unnecessary, but it makes it more likely that we'll
// catch bugs early.
UlxTangibleComponent* comp = CurrentActor->GetComponentByClass<UlxTangibleComponent>();
if (comp != nullptr) {
comp->DestroyComponent();
}
// Now destroy the actor itself. According to various
// documents I've read online, it may be necessary to take
// further steps to delete the object. Not clear.
CurrentActor->Destroy();
}
// Update the blueprint reference.
ActorBlueprint = bp;
// Now create a new actor, unless the BP is nullptr.
if (ActorBlueprint != nullptr) {
// Create the actor.
FActorSpawnParameters params;
FVector location(0, 0, 0);
FRotator rotation(0, 0, 0);
UWorld* w = Manager->GetWorld();
AActor* a = w->SpawnActor(ActorBlueprint, &location, &rotation, params);
// Insert a TangibleComponent into the actor.
UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false);
UlxTangibleComponent* tc = Cast<UlxTangibleComponent>(ac);
check(tc != nullptr);
// Make the tangible point to the actor and vice versa.
tc->Tangible = this;
CurrentActor = a;
}
}
void UlxTangible::UpdateAnimationQueue(std::string_view aq) {
AnimTracker.Update(aq);
int limit = 3;
while (AnimTracker.IsChanged()) {
if (limit == 0) break;
limit -= 1;
AnimTracker.ClearChanged();
IlxTangibleInterface::Execute_AnimationStateChanged(GetActor());
}
}
FVector UlxTangible::GetLocation() const {
if (CurrentActor == nullptr) {
return FVector(0,0,0);
} else {
return CurrentActor->GetActorLocation();
}
}
void UlxTangible::Destroy() {
SetActorBlueprintClass(nullptr);
Manager = nullptr;
TangibleId = -1;
CurrentActor = nullptr;
ActorBlueprint = nullptr;
AnimTracker.Clear();
NearAccordingToLuprex = false;
NearAccordingToUnreal = false;
}
static UlxTangible *GetActorTangible(AActor *actor) {
UlxTangibleComponent* comp = actor->GetComponentByClass<UlxTangibleComponent>();
check(comp != nullptr);
UlxTangible *result = comp->Tangible.Get();
check(result != nullptr);
return result;
}
void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) {
step = GetActorTangible(target)->AnimTracker.GetCurrentAnimation();
}
void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool autoxyz, bool autofacing, bool autoplane) {
UlxTangible *tan = GetActorTangible(target);
if (autoxyz) step.AutoUpdateXYZ(target);
if (autofacing) step.AutoUpdateFacing(target);
if (autoplane) step.AutoUpdatePlane(&(tan->Plane));
tan->AnimTracker.FinishedAnimation(step.Hash);
}
FString UlxTangible::GetTangiblePlane(AActor* target) {
return GetActorTangible(target)->Plane.ToString();
}
void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) {
GetActorTangible(target)->Plane = FName(plane);
}

View File

@@ -3,27 +3,203 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "UObject/NoExportTypes.h" #include "Components/ActorComponent.h"
#include "AnimQueue.h" #include "AnimQueue.h"
#include "TangibleInterface.h"
#include "Tangible.generated.h" #include "Tangible.generated.h"
class UlxTangibleManager;
// This class does not need to be modified.
UINTERFACE(Blueprintable)
class UlxTangibleInterface : public UInterface
{
GENERATED_BODY()
};
/** /**
*
* IlxTangibleInterface
*
* This class implements the interface that an Actor must implement
* in order for that Actor to be usable as a Tangible.
* *
*/ */
class INTEGRATION_API IlxTangibleInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
bool AnimationStateChanged();
};
/**
*
* UlxTangible
*
* The Tangible stores all the data we need for a tangible,
* such as the animation queue and so forth.
*
* From time to time, a tangible can change its blueprint class.
* To do that, we have to delete and recreate the actor. This
* is all set up so that it is possible to do that.
*
* The tangible has a place to store an Actor pointer. This
* actor pointer is allowed to be nullptr, especially in the
* case that the blueprint hasn't been set yet.
*
* This also serves as a repository for blueprint functions
* that operate on tangible actors.
*
*/
UCLASS() UCLASS()
class INTEGRATION_API UlxTangible : public UObject class INTEGRATION_API UlxTangible : public UObject
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY() UlxTangible();
AActor* Actor;
// My Tangible Manager.
UPROPERTY()
TObjectPtr<UlxTangibleManager> Manager;
// The tangible ID.
UPROPERTY()
uint64 TangibleId;
UPROPERTY()
TWeakObjectPtr<AActor> CurrentActor;
// The blueprint class of the actor.
UPROPERTY()
TSubclassOf<AActor> ActorBlueprint;
// Animation tracker
FlxAnimTracker AnimTracker; FlxAnimTracker AnimTracker;
void Init(AActor* a) { // Current Plane.
Actor = a; FName Plane;
}
// True if luprex thinks this object is Near the player.
bool NearAccordingToLuprex;
// True if unreal thinks this object is Near the player.
bool NearAccordingToUnreal;
public:
// Initialize a new tangible.
//
// This links the tangible to its TangibleManager and
// sets the tangible's ID.
//
void Init(UlxTangibleManager *tm, int64 id);
// Destroy a tangible.
//
// Delete the actor associated with the tangible, if any,
// and clean up everything else.
//
void Destroy();
// Get the actor associated with this tangible.
//
// Note that this may return nullptr: it is valid for a
// tangible to have no actor associated with it. This
// is most commonly the case if the blueprint class of
// the tangible is set to nullptr.
//
// Also bear in mind that if a tangible changes its blueprint
// class, then the actor will have to be deleted and
// recreated. In that case, GetActor will return a different
// pointer than before the blueprint change.
//
AActor* GetActor() const { return CurrentActor.Get(); }
// Get the location of the tangible.
//
// Note that if the actor is nullptr, the location is always
// at the origin.
//
FVector GetLocation() const;
// Check a blueprint class to see if it is valid as a Tangible.
//
// In order for a blueprint class to be used as a tangible,
// it must implement the interface IlxTangibleInterface.
// This function also returns true for nullptr.
//
static bool BlueprintIsTangible(TSubclassOf<AActor> bp);
// Change the blueprint class of the tangible.
//
// This requires the deletion and recreation of the Actor.
// The blueprint class must satisfy 'BlueprintIsTangible' above.
//
// It is legal to pass in nullptr for the blueprint class.
// Whenever the blueprint class is nullptr, the Actor is
// also nullptr. Ie, a tangible will have no Actor associated
// with it if its blueprint class is nullptr.
//
void SetActorBlueprintClass(TSubclassOf<AActor> bp);
// Update the animation queue from Luprex.
//
// This reads the animation queue, and then based on
// what is new in the animation queue, it calls into
// the Actor's TangibleInterface, calling methods such
// as 'StartAnimation' and 'AbortAnimation' as necessary.
//
// If the animation queue specifies a blueprint change,
// this function will eventually implement that by calling
// SetActorBlueprintClass above. This is not implemented
// yet.
//
void UpdateAnimationQueue(std::string_view aq);
public:
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
static void GetCurrentAnimation(AActor *target, FlxAnimationStep &step);
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
static void FinishedAnimation(AActor *target, const FlxAnimationStep &step,
bool AutoUpdateXYZ = true, bool AutoUpdateFacing = true, bool AutoUpdatePlane = true);
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
static FString GetTangiblePlane(AActor* target);
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
static void SetTangiblePlane(AActor* target, const FString& plane);
}; };
/**
*
* UlxTangibleComponent
*
* The TangibleComponent holds a pointer to the Tangible.
* This is the only purpose it serves: to make it possible to
* have the Actor point to its corresponding Tangible.
*
* The TangibleComponent is procedurally inserted into the Actor.
* The TangibleComponent is not visible to blueprints.
*
*/
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class INTEGRATION_API UlxTangibleComponent : public UActorComponent
{
GENERATED_BODY()
public:
UlxTangibleComponent() : Tangible(nullptr) {}
// The actor that we're a part of.
UPROPERTY()
TWeakObjectPtr<UlxTangible> Tangible;
};

View File

@@ -1,6 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "TangibleInterface.h"
// Add default functionality here for any IlxTangibleInterface functions that are not pure virtual.

View File

@@ -1,31 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "AnimQueue.h"
#include "TangibleInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(Blueprintable)
class UlxTangibleInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class INTEGRATION_API IlxTangibleInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION(BlueprintImplementableEvent, Category = "Luprex")
bool StartAnimation(ElxAnimationMode mode, const FlxAnimationStep& step);
UFUNCTION(BlueprintImplementableEvent, Category = "Luprex")
bool AbortAnimation(int64 hash);
};

View File

@@ -2,27 +2,25 @@
#include "TangibleManager.h" #include "TangibleManager.h"
#include "TangibleInterface.h" #include "Tangible.h"
#include "DebugPrint.h" #include "DebugPrint.h"
using namespace DebugPrint; using namespace DebugPrint;
using TanArray = UlxTangibleManager::TanArray;
using IdArray = UlxTangibleManager::IdArray;
FTangibleManager::FTangibleManager() { UlxTangibleManager::UlxTangibleManager() {
World = nullptr; World = nullptr;
ClassTangibleActor = nullptr; ClassTangibleActor = nullptr;
Actor = 0;
Near = IdView();
} }
void FTangibleManager::Init(UWorld *world, UClass* tanact) { void UlxTangibleManager::Init(UWorld* world, UClass* tanact) {
World = world; World = world;
ClassTangibleActor = tanact; ClassTangibleActor = tanact;
Actor = 0;
Near = IdView();
} }
UlxTangible *FTangibleManager::GetTangible(int64 id) { UlxTangible* UlxTangibleManager::GetTangible(int64 id) const {
UlxTangible **p = IdToTangible.Find(id); UlxTangible*const* p = IdToTangible.Find(id);
if (p == nullptr) { if (p == nullptr) {
return nullptr; return nullptr;
} else { } else {
@@ -30,31 +28,90 @@ UlxTangible *FTangibleManager::GetTangible(int64 id) {
} }
} }
UlxTangible *FTangibleManager::MakeTangible(int64 id) { #pragma optimize("", off)
UlxTangible *& p = IdToTangible.FindOrAdd(id); UlxTangible* UlxTangibleManager::MakeTangible(int64 id) {
if (p == nullptr) { UlxTangible*& t = IdToTangible.FindOrAdd(id);
FVector location(0,0,0); if (t == nullptr) {
FRotator rotation(0, 0, 0); t = NewObject<UlxTangible>();
FActorSpawnParameters params; t->Init(this, id);
AActor* a = World->SpawnActor(ClassTangibleActor, &location, &rotation, params);
check(a != nullptr); // TODO: fix this. The blueprint needs to be assigned
check(a->GetClass()->ImplementsInterface(UlxTangibleInterface::StaticClass())); // during animation queue parsing, based on the animation
p = NewObject<UlxTangible>(); // queue keyword 'bp'.
p->Init(a); t->SetActorBlueprintClass(ClassTangibleActor);
} }
return p; return t;
} }
void FTangibleManager::DeleteTangible(int64 id) { void UlxTangibleManager::DeleteTangible(int64 id) {
// IMPLEMENT ME // IMPLEMENT ME
} }
FTangibleManager::IdArray FTangibleManager::GetLive() { TanArray UlxTangibleManager::GetAllTangibles() const {
IdArray result; TanArray result;
result.SetNum(IdToTangible.Num()); result.SetNum(IdToTangible.Num());
int next = 0; int next = 0;
for (auto &pair : IdToTangible) { for (auto& pair : IdToTangible) {
result[next++] = pair.Key; result[next++] = pair.Value;
} }
return result; return result;
} }
#pragma optimize("", off)
void UlxTangibleManager::UpdateNearAccordingToLuprex(IdView near) {
// Clear all the 'NearAccordingToLuprex' flags.
for (const auto& pair : IdToTangible) {
pair.Value->NearAccordingToLuprex = false;
}
// For every ID on the list, create it if it doesn't exist,
// mark it, and return it.
for (int64 id : near) {
UlxTangible* tan = MakeTangible(id);
tan->NearAccordingToLuprex = true;
}
}
#pragma optimize("", off)
void UlxTangibleManager::RecalcNearAccordingToUnreal(int64 player, double radius) {
UlxTangible *p = GetTangible(player);
check (p != nullptr);
FVector playerpos = p->CurrentActor->GetActorLocation();
FName playerplane = p->Plane;
double radiussq = radius * radius;
for (const auto& pair : IdToTangible) {
UlxTangible *tan = pair.Value;
if (tan->Plane != playerplane) {
tan->NearAccordingToUnreal = false;
} else {
FVector pos = tan->GetLocation();
double distsq = FVector::DistSquared(pos, playerpos);
tan->NearAccordingToUnreal = (distsq <= radiussq);
}
}
}
void UlxTangibleManager::DeleteFarawayTangibles() {
// Make a list of tangibles that need to be deleted.
TanArray faraway;
for (const auto& pair : IdToTangible) {
UlxTangible *tan = pair.Value;
if (!(tan->NearAccordingToLuprex || tan->NearAccordingToUnreal)) {
faraway.Add(tan);
}
}
for (UlxTangible *tan : faraway) {
IdToTangible.Remove(tan->TangibleId);
tan->Destroy(); // Remove the actor from the scene.
}
}
IdArray UlxTangibleManager::GetIds(const TanArray &arr) {
IdArray result;
result.SetNum(arr.Num());
for (int i = 0; i < arr.Num(); i++) {
result[i] = arr[i]->TangibleId;
}
return result;
}

View File

@@ -8,62 +8,77 @@
#include "Tangible.h" #include "Tangible.h"
#include "TangibleManager.generated.h" #include "TangibleManager.generated.h"
USTRUCT() UCLASS()
struct INTEGRATION_API FTangibleManager class INTEGRATION_API UlxTangibleManager : public UObject
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
// Import these types into our Namespace. // Types used frequently.
using IdArray = CommonTypes::IdArray;
using IdView = CommonTypes::IdView; using IdView = CommonTypes::IdView;
using IdArray = CommonTypes::IdArray;
using TanArray = TArray<UlxTangible*>;
// A pointer to the UWorld. // A pointer to our world.
UWorld* World; UPROPERTY()
TWeakObjectPtr<UWorld> World;
// A pointer to uclass TangibleActor. // A pointer to uclass TangibleActor. This is the class
// of actors that we create (for now).
UPROPERTY() UPROPERTY()
TSubclassOf<AActor> ClassTangibleActor; TSubclassOf<AActor> ClassTangibleActor;
// Given a tangible ID, look up actor pointer (or NULL if actor was deleted) // Given a tangible ID, look up the TangibleComponent of that actor.
UPROPERTY() UPROPERTY()
TMap<int64, UlxTangible*> IdToTangible; TMap<int64, UlxTangible*> IdToTangible;
// Actor tangible Id.
int64 Actor;
// Tangibles near the actor.
IdView Near;
public: public:
FTangibleManager(); UlxTangibleManager();
// Initialize the tangible manager. // Initialize the tangible manager.
// //
void Init(UWorld *world, UClass* tanact); void Init(UWorld *world, UClass* tanact);
// Get a pointer to our world.
//
UWorld* GetWorld() const override { return World.Get(); }
// Get the tangible if it exists, otherwise return NULL // Get the tangible if it exists, otherwise return NULL
UlxTangible* GetTangible(int64 id); //
UlxTangible* GetTangible(int64 id) const;
// Get the tangible if it exists, otherwise create it. // Get the tangible if it exists, otherwise create it.
//
UlxTangible* MakeTangible(int64 id); UlxTangible* MakeTangible(int64 id);
// Delete the tangible. // Delete the tangible.
//
void DeleteTangible(int64 id); void DeleteTangible(int64 id);
// Get/Set the Id of the actor. // Get an array of all tangibles.
// //
int64 GetActor() const { return Actor; }; TanArray GetAllTangibles() const;
void SetActor(int64 id) { Actor = id; }
// Get/Set the list of tangibles near the player, according to Luprex. // Update the 'NearAccordingToLuprex' flags.
// //
IdView GetNear() const { return Near; } // Also creates stub tangibles for every Id in the list.
void SetNear(IdView near) { Near = near; } //
void UpdateNearAccordingToLuprex(IdView near);
// Get the Live list. // Recalculate the 'NearAccordingToUnreal' flags.
// //
// Efficiency note: this makes a copy of the array. void RecalcNearAccordingToUnreal(int64 player, double radius);
// Delete Far Tangibles.
// //
IdArray GetLive(); // Any tangible whose 'NearAccordingToLuprex' and 'NearAccordingToUnreal'
// flags are both false is deleted. You probably want to update both
// flags by calling the two routines above before calling this.
//
void DeleteFarawayTangibles();
// Given an array of tangibles, return an array of tangible Ids.
//
static IdArray GetIds(const TanArray &tans);
}; };

View File

@@ -1 +0,0 @@
#include "c:/luprex/ext/base-writer.hpp"

View File

@@ -1 +0,0 @@
#include "c:/luprex/cpp/drv/drvutil.cpp"

View File

@@ -1 +0,0 @@
#include "c:/luprex/cpp/drv/drvutil.hpp"

View File

@@ -1 +0,0 @@
#include "c:/luprex/cpp/core/enginewrapper.hpp"

27
luprex-install.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/sh
#
#
# Install symbolic links to the Luprex source and the Luprex DLL.
#
# These symbolic links are needed to be able to build integration.
#
#
if [ -d c:/luprex ] ; then
LUPREX=c:/luprex
DLL=$LUPREX/build/visual/luprexlib.dll
else
LUPREX=$HOME/luprex
DLL=$LUPREX/build/linux/luprexlib.so
fi
rm -f Source/Integration/lpx-*.hpp
rm -f Source/Integration/lpx-*.cpp
echo '#include "'$LUPREX'/ext/base-buffer.hpp"' > Source/Integration/lpx-basebuffer.hpp
echo '#include "'$LUPREX'/cpp/drv/drvutil.hpp"' > Source/Integration/lpx-drvutil.hpp
echo '#include "'$LUPREX'/cpp/drv/drvutil.cpp"' > Source/Integration/lpx-drvutil.cpp
echo '#include "'$LUPREX'/cpp/core/enginewrapper.hpp"' > Source/Integration/lpx-enginewrapper.hpp
echo '#define LUPREX_DLL_PATH "'$DLL'"' > Source/Integration/lpx-paths.hpp
echo '#define LUPREX_ROOT_PATH "'$LUPREX'"' >> Source/Integration/lpx-paths.hpp