Unknown mess
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "AnimQueue.h"
|
||||
#include "Common.h"
|
||||
#include "UtilityLibrary.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/MeshComponent.h"
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
|
||||
#include "AnimQueue.generated.h"
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// An single animation step.
|
||||
//
|
||||
// This struct contains an entire animation step. The
|
||||
// key-value pairs are stored in an encoded form, which is not
|
||||
// directly accessible to blueprints. To read these key-value
|
||||
// pairs, blueprints will need to use UnpackAnimationStep or
|
||||
// AnimationStepGetXXX.
|
||||
// FlxAnimationStep
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
// A single animation step. The key-value pairs
|
||||
// are stored in an encoded form, which is not
|
||||
// directly accessible to blueprints. To read
|
||||
// them, use UnpackAnimationStep or the
|
||||
// AnimationStepGetXXX functions.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INTEGRATION_API FlxAnimationStep {
|
||||
@@ -28,14 +28,16 @@ public:
|
||||
UPROPERTY()
|
||||
bool Finished;
|
||||
|
||||
// The hash of the animation step, a 63-bit unique identifier.
|
||||
// The hash of the animation step, a 63-bit
|
||||
// unique identifier.
|
||||
//
|
||||
UPROPERTY()
|
||||
int64 Hash;
|
||||
|
||||
// The Body contains all the key-value pairs in an encoded form. To
|
||||
// obtain these in a useful form, you will need to use
|
||||
// UnpackAnimationStep or AnimationStepGetXXX.
|
||||
// The Body contains all the key-value pairs
|
||||
// in an encoded form. To obtain these in a
|
||||
// useful form, use UnpackAnimationStep or the
|
||||
// AnimationStepGetXXX functions.
|
||||
//
|
||||
UPROPERTY()
|
||||
TArray<uint8> Body;
|
||||
@@ -48,25 +50,26 @@ public:
|
||||
|
||||
// Auto-Execute
|
||||
//
|
||||
// These functions automatically update certain actor
|
||||
// properties:
|
||||
// These functions automatically update certain
|
||||
// actor properties:
|
||||
//
|
||||
// AutoUpdateXYZ - uses 'xyz' keyword
|
||||
// AutoUpdateFacing - uses 'facing' keyword
|
||||
// AutoUpdatePlane - uses 'plane' keyword
|
||||
//
|
||||
// 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;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This UClass is never instantiated. It exists to
|
||||
// expose certain static functions to the blueprint
|
||||
// library.
|
||||
// UlxAnimationStepLibrary
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
// Blueprint function library for reading and
|
||||
// applying animation steps.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS()
|
||||
class INTEGRATION_API UlxAnimationStepLibrary : public UBlueprintFunctionLibrary
|
||||
@@ -77,26 +80,23 @@ public:
|
||||
UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
|
||||
static FString AnimationStepDebugString(const FlxAnimationStep& step);
|
||||
|
||||
// Stores the key-value pairs in properties of the target object.
|
||||
// Stores the key-value pairs in properties of the
|
||||
// target object.
|
||||
//
|
||||
// The animation step contains key-value pairs. The goal of this function
|
||||
// is to store these in properties of the target. The prefix parameter
|
||||
// is prepended to the property names.
|
||||
// The prefix parameter is prepended to the property
|
||||
// names. For example, if the pairs are "color=blue,
|
||||
// speed=20" and the prefix is "aq", then "aq Color" is
|
||||
// set to "blue" and "aq Speed" is set to 20. A
|
||||
// property "aq Animation Step" is also written,
|
||||
// containing the entire animation step.
|
||||
//
|
||||
// For example, suppose the key-value pairs are "color=blue, speed=20",
|
||||
// and suppose the prefix is "aq". In that case, two properties would
|
||||
// be written: "aq Color" would be set to "blue", and "aq Speed" would be
|
||||
// set to 20. In addition, a property "aq Animation Step" would
|
||||
// be written, containing the entire animation step.
|
||||
// If a key has no corresponding property, it is
|
||||
// silently ignored. If a property has no corresponding
|
||||
// key, it is cleared.
|
||||
//
|
||||
// If the step contains a key-value pair, but there is no corresponding
|
||||
// property in the target, then the key-value pair is silently
|
||||
// ignored. If the target contains properties that begin with the prefix,
|
||||
// but which don't have any corresponding key-value pair, those properties
|
||||
// are cleared.
|
||||
//
|
||||
// Returns 'Changed' if the hash-value of the "aq Animation Step" property
|
||||
// has changed. Returns 'Action' string from the action=xxx property.
|
||||
// Returns bChanged=true if the hash of the "aq
|
||||
// Animation Step" property has changed. Returns the
|
||||
// Action string from action=xxx.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Step")
|
||||
static void UnpackAnimationStep(bool &bChanged, FString &Action, const FlxAnimationStep& step, UObject* target, const FString& VariableNamePrefix = TEXT("aq"));
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
|
||||
static bool AnimationStepIsIdle(const FlxAnimationStep &step);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
|
||||
static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name);
|
||||
|
||||
@@ -125,29 +125,55 @@ public:
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintAutocast), Category = "Luprex|Animation Step")
|
||||
static int64 AnimationStepID(const FlxAnimationStep& step) { return step.Hash; }
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Using mat_xxxx values from the animation step, update the actor's
|
||||
// material parameters. Doing this may involve creating or replacing
|
||||
// dynamic material instances for the actor.
|
||||
=======
|
||||
// Scan an animation step for key-value pairs of the
|
||||
// form mat_XXXX={x,y,z}. For each match, create a
|
||||
// dynamic material instance on the actor's mesh
|
||||
// components and set the vector parameter XXXX.
|
||||
// Materials are restored to their base (non-dynamic)
|
||||
// state before applying, so parameters from previous
|
||||
// calls do not persist.
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "actor"), Category = "Luprex|Animation Step")
|
||||
static void AnimationStepApplyMaterials(const FlxAnimationStep& step, AActor* actor);
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Look for a mesh=name key-value pair in the animation step.
|
||||
// If found, load the named mesh and apply it to the actor's
|
||||
// mesh component. The actor must have exactly one mesh component.
|
||||
// If FallbackToBP is true, and mesh=name is not present, looks
|
||||
// for a bp=name pair instead.
|
||||
=======
|
||||
// Look for a mesh=name key-value pair. If found, load
|
||||
// the named mesh and apply it to the actor's mesh
|
||||
// component. The actor must have exactly one mesh
|
||||
// component.
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "actor"), Category = "Luprex|Animation Step")
|
||||
static void AnimationStepApplyMesh(const FlxAnimationStep& step, bool FallbackToBP, AActor* actor);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
<<<<<<< HEAD
|
||||
// An animation step that doesn't actually store the step,
|
||||
// it just contains a pointer to the string.
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
=======
|
||||
// FlxAnimationStepView
|
||||
//
|
||||
// A lightweight, non-owning view of an animation
|
||||
// step (hash + body as a string_view).
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
|
||||
struct FlxAnimationStepView {
|
||||
int64 Hash;
|
||||
@@ -157,19 +183,17 @@ struct FlxAnimationStepView {
|
||||
FlxAnimationStepView(int64 h, std::string_view b) : Hash(h), Body(b) {}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A single animation field.
|
||||
//
|
||||
// A field consists of a variable name,
|
||||
// a persistent flag, a type, and some fields
|
||||
// to hold the values.
|
||||
// FlxAnimationField
|
||||
//
|
||||
// If the value is boolean, it is stored in
|
||||
// X, as either 1 or 0. If the value is double,
|
||||
// it is stored in X.
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
// A single field from an animation step: a variable name, a
|
||||
// persistent flag, a type, and value storage.
|
||||
//
|
||||
// Boolean values are stored in X as 1 or 0. Double values
|
||||
// are stored in X.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
struct FlxAnimationField {
|
||||
std::string_view Name;
|
||||
@@ -179,48 +203,47 @@ struct FlxAnimationField {
|
||||
std::string_view S;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// An Animation Queue Decoder.
|
||||
// FlxAnimQueueDecoder
|
||||
//
|
||||
// This acts a lot like a stream reader,
|
||||
// it reads one AnimEntry at a time from
|
||||
// the animation queue until you reach
|
||||
// 'end-of-file'.
|
||||
// Stream reader for animation queues. Reads one
|
||||
// FlxAnimationStepView at a time until EOF.
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxAnimQueueDecoder {
|
||||
private:
|
||||
FlxStreamBuffer Decoder;
|
||||
|
||||
// These values are immediately read from the header.
|
||||
|
||||
// Read from the header immediately on
|
||||
// construction.
|
||||
//
|
||||
int SizeLimit;
|
||||
int ActualSize;
|
||||
|
||||
public:
|
||||
// Initialize the FlxAnimQueueDecoder with the encoded animation queue.
|
||||
// Initialize with an encoded animation queue.
|
||||
//
|
||||
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.
|
||||
// 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 EOF.
|
||||
//
|
||||
bool AtEOF() { return Decoder.empty(); }
|
||||
|
||||
// Read one animation step.
|
||||
//
|
||||
//
|
||||
FlxAnimationStepView ReadStep();
|
||||
|
||||
// Peek at the hash of the next animation step.
|
||||
// Peek at the hash of the next step.
|
||||
//
|
||||
int64 PeekHash();
|
||||
|
||||
@@ -229,115 +252,106 @@ public:
|
||||
// static FString DebugString(std::string_view s);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// An Animation Step Decoder.
|
||||
// FlxAnimationStepDecoder
|
||||
//
|
||||
// This acts a lot like a stream reader,
|
||||
// it reads one FlxAnimationField at a time from
|
||||
// the animation queue until you reach
|
||||
// 'end-of-file'.
|
||||
// Stream reader for a single animation step. Reads one
|
||||
// FlxAnimationField at a time until EOF.
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxAnimationStepDecoder {
|
||||
private:
|
||||
FlxStreamBuffer Decoder;
|
||||
|
||||
public:
|
||||
<<<<<<< HEAD
|
||||
// Initialize the FlxAnimationStepDecoder.
|
||||
//
|
||||
=======
|
||||
// Initialize from an encoded step body.
|
||||
//
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
FlxAnimationStepDecoder(std::string_view body) : Decoder(body) {}
|
||||
|
||||
// Return true if the parser has reached the end of the string.
|
||||
// Return true if the parser has reached EOF.
|
||||
//
|
||||
bool AtEOF() { return Decoder.empty(); }
|
||||
|
||||
// Read one field.
|
||||
//
|
||||
//
|
||||
FlxAnimationField ReadField();
|
||||
|
||||
// Convert an AnimStep to an FString.
|
||||
// Convert an animation step to a debug string.
|
||||
//
|
||||
static FString DebugString(bool finished, int64 hash, std::string_view body);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FlxAnimTracker
|
||||
//
|
||||
// This class monitors the animation queue for a single
|
||||
// tangible. It can identify when a new animation has
|
||||
// appeared on the animation queue, and when animations have
|
||||
// been removed from the animation queue. It also
|
||||
// keeps track of which animations have been started.
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
// Monitors the animation queue for a single tangible.
|
||||
// Identifies when animations appear or are removed, and
|
||||
// tracks which ones have been marked as finished.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxAnimTracker {
|
||||
public:
|
||||
// Our own copy of the animation queue. We only
|
||||
// store the hashes, not the steps. The First element
|
||||
// of the queue is the oldest item.
|
||||
// Our copy of the animation queue. The first element
|
||||
// is the oldest item.
|
||||
//
|
||||
TArray<FlxAnimationStep> AQ;
|
||||
|
||||
// True if something has recently changed.
|
||||
//
|
||||
bool Changed;
|
||||
|
||||
|
||||
public:
|
||||
// Construct a tracker.
|
||||
//
|
||||
// Initially, the tracker is in the empty (Clear) state.
|
||||
// Construct a tracker in the empty (Clear) state.
|
||||
//
|
||||
FlxAnimTracker();
|
||||
|
||||
// Clear everything, reset to the initial state.
|
||||
// Clear everything, reset to initial state.
|
||||
//
|
||||
void Clear();
|
||||
|
||||
// Update from the specified animation queue.
|
||||
//
|
||||
// After the update is done, AQ will be a copy
|
||||
// of the animation queue that is passed in.
|
||||
// Update from the specified animation queue. After the
|
||||
// update, AQ will be a copy of the queue that was
|
||||
// passed in.
|
||||
//
|
||||
void Update(std::string_view encqueue);
|
||||
|
||||
// Get the current blueprint name, as a string.
|
||||
// Get the current blueprint name.
|
||||
//
|
||||
FString GetCurrentBlueprintName();
|
||||
|
||||
// Get the current animation step.
|
||||
// Get the current animation step. This is the step
|
||||
// that the blueprint should currently be playing.
|
||||
//
|
||||
// Get the current animation step. This is the step that the
|
||||
// blueprint should currently be playing.
|
||||
//
|
||||
FlxAnimationStep GetCurrentAnimation();
|
||||
|
||||
// Declare that an animation is finished.
|
||||
// Declare that an animation is finished. The blueprint
|
||||
// calls this to indicate that it is done playing the
|
||||
// specified animation. This causes GetCurrentAnimation
|
||||
// to advance to the next step.
|
||||
//
|
||||
// The blueprint uses this function call to indicate that it
|
||||
// is done playing the specified animation. This will cause the
|
||||
// animation to be marked as finished, which in turn causes
|
||||
// 'GetCurrentStep' to advance to the next animation.
|
||||
//
|
||||
void FinishedAnimation(int64 Hash);
|
||||
|
||||
// Return true if an animation step is marked finished.
|
||||
//
|
||||
// Also return true if the animation step is not found.
|
||||
// Also returns true if the step is not found.
|
||||
//
|
||||
bool IsFinished(int64 Hash);
|
||||
|
||||
// Skip to the end of the animation queue.
|
||||
//
|
||||
// This is equivalent to calling 'FinishedHash' on every
|
||||
// animation in the entire queue.
|
||||
|
||||
// Skip to the end of the animation queue. Equivalent to
|
||||
// calling FinishedAnimation on every animation in the
|
||||
// queue.
|
||||
//
|
||||
void SkipToEnd();
|
||||
|
||||
// Get all the hashes of all the animation steps.
|
||||
// Get the hashes of all animation steps.
|
||||
//
|
||||
TArray<int64> GetHashes();
|
||||
|
||||
@@ -349,24 +363,25 @@ public:
|
||||
//
|
||||
const FlxAnimationStep *LastFinished() const;
|
||||
|
||||
// Return the first animation with the specified hash.
|
||||
// Return the first animation with the
|
||||
// specified hash.
|
||||
//
|
||||
const FlxAnimationStep *FindAnimation(int64 hash) const;
|
||||
|
||||
// Clear the 'Changed' flag.
|
||||
// Clear the Changed flag.
|
||||
//
|
||||
void ClearChanged() { Changed = false; }
|
||||
|
||||
// Get the 'Changed' flag.
|
||||
// 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.
|
||||
// Set to true whenever the animation queue changes, or
|
||||
// when FinishedAnimation marks a step. Only cleared by
|
||||
// ClearChanged.
|
||||
//
|
||||
bool IsChanged() const { return Changed; }
|
||||
|
||||
// Return a debug string for the entire animation tracker.
|
||||
// Return a debug string for the entire animation
|
||||
// tracker.
|
||||
//
|
||||
FString DebugString() const;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AssetLookup.h
|
||||
//
|
||||
// Provides asset loading by short name. At
|
||||
// initialization, scans asset directories and builds
|
||||
// a lookup table mapping (class, short name) pairs
|
||||
// to full asset paths. Blueprint-callable functions
|
||||
// let blueprints load assets by short name.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -14,64 +25,68 @@ class UStaticMesh;
|
||||
class USkeletalMesh;
|
||||
class UAnimSequence;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UlxAssetLookup
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class UlxAssetLookup : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
//
|
||||
// At initialization time, we scan the asset directories
|
||||
// to see what assets are present. These maps store the
|
||||
// result of the scan. Each entry in the map contains a
|
||||
// classname, a short name, and a path:
|
||||
//
|
||||
// <UAnimSequence, Jump> -> "/Game/AnimSequences/SEQ_Jump"
|
||||
//
|
||||
TMap<TTuple<FString, FName>, FString> AssetPaths;
|
||||
TMap<TTuple<FString, FName>, FString> AssetPaths;
|
||||
|
||||
private:
|
||||
|
||||
void AddAssets(const TCHAR *Path, UClass *Class, const TCHAR *NamePrefix);
|
||||
void AddAssets(const TCHAR *Path, UClass *Class, const TCHAR *NamePrefix);
|
||||
|
||||
static UObject *LoadAsset(const UObject *Context, UClass *Class, UClass *ChildOf, const FString &Name, bool ErrorIfNotFound);
|
||||
static UObject *LoadAsset(const UObject *Context, UClass *Class, UClass *ChildOf, const FString &Name, bool ErrorIfNotFound);
|
||||
|
||||
public:
|
||||
void RebuildIndex();
|
||||
void RebuildIndex();
|
||||
|
||||
// Get a static mesh by name
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadStaticMeshAsset(
|
||||
UStaticMesh *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get a skeletal mesh by name
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadSkeletalMeshAsset(
|
||||
USkeletalMesh *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
// Get a static mesh by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadStaticMeshAsset(
|
||||
UStaticMesh *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get an animation sequence by name.
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadAnimSequenceAsset(
|
||||
UAnimSequence *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
// Get a skeletal mesh by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadSkeletalMeshAsset(
|
||||
USkeletalMesh *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get a tangible class by name
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadTangibleBlueprintAsset(
|
||||
TSubclassOf<AActor> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
// Get an animation sequence by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadAnimSequenceAsset(
|
||||
UAnimSequence *&Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get a widget blueprint by name
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadUserWidgetAsset(
|
||||
TSubclassOf<UUserWidget> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
// Get a tangible class by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadTangibleBlueprintAsset(
|
||||
TSubclassOf<AActor> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get a look-at widget blueprint by name
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadLuaWidgetAsset(
|
||||
TSubclassOf<UlxLuaWidget> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
// Get a widget blueprint by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadUserWidgetAsset(
|
||||
TSubclassOf<UUserWidget> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
|
||||
// Get a look-at widget blueprint by name.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context", ExpandEnumAsExecs="ReturnValue"), Category = "Luprex|Asset Loading")
|
||||
static ElxValidOrNotValid LoadLuaWidgetAsset(
|
||||
TSubclassOf<UlxLuaWidget> &Result,
|
||||
const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
|
||||
};
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
|
||||
#include "BlueprintErrors.h"
|
||||
#include "Blueprint/BlueprintExceptionInfo.h"
|
||||
#include "LuaCall.h"
|
||||
#include "Internationalization/TextFormatter.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Kismet2/KismetDebugUtilities.h"
|
||||
#include "Kismet/KismetTextLibrary.h"
|
||||
#include "AnimQueue.h"
|
||||
|
||||
ELogVerbosity::Type UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(ElxLogVerbosity Verbosity) {
|
||||
switch (Verbosity) {
|
||||
case ElxLogVerbosity::Error: return ELogVerbosity::Error;
|
||||
case ElxLogVerbosity::Warning: return ELogVerbosity::Warning;
|
||||
case ElxLogVerbosity::Display: return ELogVerbosity::Display;
|
||||
case ElxLogVerbosity::Log: return ELogVerbosity::Log;
|
||||
case ElxLogVerbosity::Verbose: return ELogVerbosity::Verbose;
|
||||
case ElxLogVerbosity::VeryVerbose: return ELogVerbosity::VeryVerbose;
|
||||
case ElxLogVerbosity::Fatal: return ELogVerbosity::Fatal;
|
||||
}
|
||||
}
|
||||
|
||||
void UlxBlueprintErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
|
||||
{
|
||||
// Generate the formatted string.
|
||||
//
|
||||
FText InPatternText(FText::FromString(InPattern));
|
||||
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
|
||||
FString MessageString = Message.ToString();
|
||||
|
||||
// Get the blueprint name.
|
||||
//
|
||||
// Normally, the log function expects you to pass in a filename, and a log
|
||||
// category name. We use the blueprint name for both.
|
||||
//
|
||||
// Using the blueprint name as a log category name is not technically
|
||||
// correct. However, there is no correct way to create log categories
|
||||
// from inside of blueprints. Doing it this way at least produces a reasonable
|
||||
// message inside the log. What doesn't work correctly is the log message
|
||||
// suppression system. Ie, console commands like 'log <category> verbose'
|
||||
// don't have any effect here. The design of the log message suppression
|
||||
// system is such that there just is no reasonable way to hook into it from
|
||||
// inside of blueprints.
|
||||
//
|
||||
FString BlueprintNameString = Context->GetClass()->GetName();
|
||||
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
|
||||
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
|
||||
|
||||
// Output to Log
|
||||
//
|
||||
ELogVerbosity::Type VerbosityValue = ConvertElxLogVerbosity(Verbosity);
|
||||
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
|
||||
{
|
||||
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
|
||||
}
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Float;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueFloat = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Double;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueDouble = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataKey(FKey Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value.GetFName());
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Gender;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueGender = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::FromString(Value->DebugString());
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &Value, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::FromString(UlxAnimationStepLibrary::AnimationStepDebugString(Value));
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText();
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject)
|
||||
{
|
||||
const UEnum *Enum = Cast<const UEnum>(PinSubCategoryObject);
|
||||
FFormatArgumentData Result;
|
||||
if (Enum == nullptr)
|
||||
{
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value));
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
FlxDebugBlueprintErrorsOutputDevice::FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef)
|
||||
: Sensitivity(SensitivityRef)
|
||||
{
|
||||
GLog->AddOutputDevice(this);
|
||||
}
|
||||
|
||||
FlxDebugBlueprintErrorsOutputDevice::~FlxDebugBlueprintErrorsOutputDevice()
|
||||
{
|
||||
GLog->RemoveOutputDevice(this);
|
||||
}
|
||||
|
||||
namespace UBreakPoint {
|
||||
static volatile int V;
|
||||
FORCENOINLINE static void OnLogFatal() {
|
||||
V = 0;
|
||||
}
|
||||
FORCENOINLINE static void OnLogError() {
|
||||
V = 1;
|
||||
OnLogFatal();
|
||||
}
|
||||
FORCENOINLINE static void OnLogWarning() {
|
||||
V = 2;
|
||||
OnLogError();
|
||||
}
|
||||
}
|
||||
|
||||
static const FName LogBlueprintDebugName(TEXT("LogBlueprintDebug"));
|
||||
|
||||
|
||||
void FlxDebugBlueprintErrorsOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category)
|
||||
{
|
||||
// If the error isn't serious enough, do nothing.
|
||||
//
|
||||
if (Verbosity > UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(Sensitivity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the Category is LogBlueprintDebug, then we're inside the debugger already.
|
||||
//
|
||||
if (Category == LogBlueprintDebugName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find out if we're running in a blueprint thread. If not, return.
|
||||
//
|
||||
FFrame* Frame = FFrame::GetThreadLocalTopStackFrame();
|
||||
if (Frame == nullptr) return;
|
||||
UObject *TopObject = Frame->Object;
|
||||
if (TopObject == nullptr) return;
|
||||
|
||||
// Notify the debugger that there's been an exception.
|
||||
//
|
||||
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
|
||||
FBlueprintCoreDelegates::ThrowScriptException(TopObject, *Frame, ExceptionInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
//
|
||||
// BlueprintErrors: Better error handling for blueprint errors.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/Array.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "Misc/OutputDeviceError.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "BlueprintErrors.generated.h"
|
||||
|
||||
class UlxLuaValues;
|
||||
struct FlxAnimationStep;
|
||||
|
||||
/*
|
||||
* enum class ElxLogVerbosity, below, contains all the same error severity levels
|
||||
* as ELogVerbosity, but in a form that the blueprint editor can manipulate.
|
||||
*
|
||||
* We deliberately moved 'Fatal' to the end of the list, and made 'Error' option 0.
|
||||
* We did that because we want the editor to default to 'Error' in most cases.
|
||||
* Unfortunately, that means the numeric values of the two enums don't match up,
|
||||
* so we will need a conversion function.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/** Log Verbosity: The importance of an error message, which affects which logs the error
|
||||
* message gets written to, and how that message gets filtered.
|
||||
*
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxLogVerbosity : uint8 {
|
||||
|
||||
/* Prints an error to the console and log file. The editor collects and reports errors. */
|
||||
Error,
|
||||
|
||||
/* Prints a warning to the console and log file. The editor collects and report warnings. */
|
||||
Warning,
|
||||
|
||||
/* Prints a message to the console and log file. */
|
||||
Display,
|
||||
|
||||
/* Prints a message to the log file, however, it does not print to the console. */
|
||||
Log,
|
||||
|
||||
/* Prints a message to a log file only if Verbose logging is enabled for the given category. This is usually used for detailed logging. */
|
||||
Verbose,
|
||||
|
||||
/* Prints a message to a log file. If VeryVerbose logging is enabled, then this is used for detailed logging that would otherwise spam output. */
|
||||
VeryVerbose,
|
||||
|
||||
/* Danger! Prints a fatal error to the console and log file, then crashes (this crashes the editor too). */
|
||||
Fatal,
|
||||
};
|
||||
|
||||
/* A library containing assorted useful functions for blueprint error handling.
|
||||
*
|
||||
*/
|
||||
UCLASS(MinimalAPI)
|
||||
class UlxBlueprintErrorLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// The Format Error Message blueprint node macroexpands, the following
|
||||
// function is the core of the expansion. The actual K2Node itself is in
|
||||
// its own source file.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
|
||||
static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
|
||||
|
||||
// A formatting routine for pins that were never connected.
|
||||
//
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataBlank(const FString &Name);
|
||||
|
||||
// A specialized formatting routine for pins of enum types.
|
||||
//
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject);
|
||||
|
||||
// Convert an ElxLogVerbosity to an ELogVerbosity::Type
|
||||
//
|
||||
static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity);
|
||||
};
|
||||
|
||||
/*
|
||||
* A library that contains functions that convert data into FFormatArgumentData
|
||||
* structs, so that the data can be passed to FText::Format.
|
||||
*/
|
||||
UCLASS(MinimalAPI)
|
||||
class UlxFormatDataLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataBool(bool Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataByte(uint8 Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataInt(int Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataInt64(int64 Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataFloat(float Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataDouble(double Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataText(FText Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataString(FString Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataName(FName Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataKey(FKey Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataGender(ETextGender Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataObject(UObject *Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataVector(const FVector &Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataRotator(const FRotator &Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &Value, const FString &Name);
|
||||
};
|
||||
|
||||
/* Debug Blueprint Errors output device.
|
||||
*
|
||||
* When an error message gets written to the log, using "Format Error Message,"
|
||||
* or any other means that writes an error message to the log,
|
||||
* we can optionally notify the blueprint debugger to pause execution.
|
||||
* This only affects errors that are generated during blueprint execution.
|
||||
* Errors in other threads do not pause the blueprint.
|
||||
*
|
||||
*/
|
||||
struct FlxDebugBlueprintErrorsOutputDevice : public FOutputDevice
|
||||
{
|
||||
public:
|
||||
// The constructor and destructor automatically register this output device with GLog.
|
||||
//
|
||||
// This struct doesn't store the sensitivity threshold. It relies on some blueprint
|
||||
// class to do that, so that the threshold can be easily edited with the blueprint
|
||||
// editor. This struct must be initialized with a reference to the threshold variable.
|
||||
//
|
||||
FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef);
|
||||
~FlxDebugBlueprintErrorsOutputDevice();
|
||||
|
||||
// Inspect a log message.
|
||||
//
|
||||
INTEGRATION_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
|
||||
|
||||
// If the device is marked 'CanBeUsedOnMultipleThreads,' then UE_LOG will
|
||||
// call Serialize from the current thread, otherwise, it will call Serialize from
|
||||
// the logging thread. Using the logging thread would defeat the purpose of this
|
||||
// device, so it's imperative that we set this flag.
|
||||
//
|
||||
INTEGRATION_API virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||
|
||||
private:
|
||||
const ElxLogVerbosity &Sensitivity;
|
||||
};
|
||||
397
Source/Integration/BlueprintExporter.cpp
Normal file
397
Source/Integration/BlueprintExporter.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "BlueprintExporter.h"
|
||||
#include "Common.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "K2Node_Knot.h"
|
||||
#include "EdGraphNode_Comment.h"
|
||||
#include "K2Node_VariableGet.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_FunctionEntry.h"
|
||||
|
||||
FlxBlueprintExporter::FlxBlueprintExporter(UEdGraph* InGraph)
|
||||
: Graph(InGraph)
|
||||
{
|
||||
SortNodes();
|
||||
AssignNodeNames();
|
||||
EmitLocalVariables();
|
||||
EmitNodeList();
|
||||
EmitGraph();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// General utilities for manipulating UEdGraph nodes.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
FString FlxBlueprintExporter::SanitizeName(const FString& Title)
|
||||
{
|
||||
FString Result = Title.TrimStartAndEnd().Replace(TEXT(" "), TEXT("_"));
|
||||
return Result.IsEmpty() ? TEXT("_") : Result;
|
||||
}
|
||||
|
||||
FString FlxBlueprintExporter::FormatPinType(const FEdGraphPinType& PinType)
|
||||
{
|
||||
if (UObject* SubObj = PinType.PinSubCategoryObject.Get())
|
||||
{
|
||||
return SubObj->GetName();
|
||||
}
|
||||
FString Type = PinType.PinCategory.ToString();
|
||||
Type[0] = FChar::ToUpper(Type[0]);
|
||||
return Type;
|
||||
}
|
||||
|
||||
FString FlxBlueprintExporter::FormatPinType(UEdGraphPin* Pin)
|
||||
{
|
||||
return FormatPinType(Pin->PinType);
|
||||
}
|
||||
|
||||
FString FlxBlueprintExporter::FormatNodeBaseName(UEdGraphNode* Node)
|
||||
{
|
||||
FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString();
|
||||
int32 NewlineIdx;
|
||||
if (Title.FindChar(TEXT('\n'), NewlineIdx))
|
||||
Title.LeftInline(NewlineIdx);
|
||||
return SanitizeName(Title);
|
||||
}
|
||||
|
||||
UEdGraphPin* FlxBlueprintExporter::GetLinkedTo(UEdGraphPin* Pin)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (Pin == nullptr) return nullptr;
|
||||
if (Pin->LinkedTo.IsEmpty()) return nullptr;
|
||||
UEdGraphPin *LinkedTo = Pin->LinkedTo[0];
|
||||
if (!LinkedTo->GetOwningNode()->IsA<UK2Node_Knot>()) return LinkedTo;
|
||||
Pin = FindFirstPin(LinkedTo->GetOwningNode(), Pin->Direction);
|
||||
}
|
||||
}
|
||||
|
||||
bool FlxBlueprintExporter::IsDefaultToSelf(UEdGraphPin* Pin)
|
||||
{
|
||||
// Only valid call-function nodes can have default-to-self.
|
||||
UK2Node_CallFunction* CallNode = Cast<UK2Node_CallFunction>(Pin->GetOwningNode());
|
||||
if (!CallNode) return false;
|
||||
UFunction* Function = CallNode->GetTargetFunction();
|
||||
if (!Function) return false;
|
||||
|
||||
// In a call function node, the pin name 'self' is reserved
|
||||
// for the C++ 'this' pointer. This always has 'DefaultToSelf'
|
||||
// behavior. Note that 'self' in other nodes is not special.
|
||||
if (Pin->PinName == UEdGraphSchema_K2::PN_Self) return true;
|
||||
|
||||
// For any other pin name, we have to check for
|
||||
// the presence of meta = (DefaultToSelf = "Pin")
|
||||
const FString& DefaultToSelfPinName = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
|
||||
return Pin->PinName.ToString() == DefaultToSelfPinName;
|
||||
}
|
||||
|
||||
TArray<UEdGraphPin*> FlxBlueprintExporter::FilterPins(UEdGraphNode* Node, EEdGraphPinDirection Direction, FName Category)
|
||||
{
|
||||
TArray<UEdGraphPin*> Result;
|
||||
for (UEdGraphPin* Pin : Node->Pins)
|
||||
{
|
||||
if (Direction != EGPD_MAX && Pin->Direction != Direction) continue;
|
||||
if (!Category.IsNone() && Pin->PinType.PinCategory != Category) continue;
|
||||
Result.Add(Pin);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool FlxBlueprintExporter::HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
|
||||
{
|
||||
return FilterPins(Node, Direction, UEdGraphSchema_K2::PC_Exec).Num() > 0;
|
||||
}
|
||||
|
||||
UEdGraphPin* FlxBlueprintExporter::FindFirstPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
|
||||
{
|
||||
for (UEdGraphPin* Pin : Node->Pins)
|
||||
{
|
||||
if (Pin->Direction == Direction) return Pin;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UEdGraphPin* FlxBlueprintExporter::BestMatchPin(UEdGraphNode* Node, EEdGraphPinDirection Direction, bool Exec, const FString& Name)
|
||||
{
|
||||
if (Name == TEXT("_")) return nullptr;
|
||||
|
||||
UEdGraphPin* DisplayMatch = nullptr;
|
||||
int32 DisplayCount = 0;
|
||||
UEdGraphPin* RawMatch = nullptr;
|
||||
int32 RawCount = 0;
|
||||
|
||||
for (UEdGraphPin* Pin : Node->Pins)
|
||||
{
|
||||
if (Pin->Direction != Direction) continue;
|
||||
bool PinIsExec = (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec);
|
||||
if (PinIsExec != Exec) continue;
|
||||
|
||||
if (Name == SanitizeName(Node->GetPinDisplayName(Pin).ToString()))
|
||||
{
|
||||
DisplayMatch = Pin;
|
||||
DisplayCount++;
|
||||
}
|
||||
|
||||
if (Name == SanitizeName(Pin->PinName.ToString()))
|
||||
{
|
||||
RawMatch = Pin;
|
||||
RawCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (DisplayCount == 1) return DisplayMatch;
|
||||
if (RawCount == 1) return RawMatch;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString FlxBlueprintExporter::FormatPinName(UEdGraphPin *Pin)
|
||||
{
|
||||
UEdGraphNode* Node = Pin->GetOwningNode();
|
||||
bool Exec = (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec);
|
||||
|
||||
// Try sanitized display name first.
|
||||
FString SanitizedDisplay = SanitizeName(Node->GetPinDisplayName(Pin).ToString());
|
||||
if (BestMatchPin(Node, Pin->Direction, Exec, SanitizedDisplay) == Pin)
|
||||
return SanitizedDisplay;
|
||||
|
||||
// Try sanitized raw name.
|
||||
FString SanitizedRaw = SanitizeName(Pin->PinName.ToString());
|
||||
if (BestMatchPin(Node, Pin->Direction, Exec, SanitizedRaw) == Pin)
|
||||
return SanitizedRaw;
|
||||
|
||||
// No unambiguous name found.
|
||||
UE_LOG(LogLuprexIntegration, Warning, TEXT("Blueprint export: ambiguous pin name '%s' on node '%s'"),
|
||||
*Pin->PinName.ToString(), *Node->GetNodeTitle(ENodeTitleType::ListView).ToString());
|
||||
return FString::Printf(TEXT("?%s"), *SanitizedRaw);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// Traverse and Emit the Nodes.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
FString FlxBlueprintExporter::FormatNodeName(UEdGraphNode* Node)
|
||||
{
|
||||
FString* Name = NodeNames.Find(Node);
|
||||
return Name ? *Name : FormatNodeBaseName(Node);
|
||||
}
|
||||
|
||||
FString FlxBlueprintExporter::FormatPinSource(UEdGraphPin* Pin)
|
||||
{
|
||||
// If connected, show source node.pin
|
||||
UEdGraphPin* LinkedTo = GetLinkedTo(Pin);
|
||||
if (LinkedTo != nullptr)
|
||||
{
|
||||
UEdGraphNode* LinkedToNode = LinkedTo->GetOwningNode();
|
||||
|
||||
// For variable get nodes, just show the variable name.
|
||||
if (UK2Node_VariableGet* VarGet = Cast<UK2Node_VariableGet>(LinkedToNode))
|
||||
return SanitizeName(VarGet->GetVarNameString());
|
||||
|
||||
FString PinLabel = FormatPinName(LinkedTo);
|
||||
return FString::Printf(TEXT("%s.%s"), *FormatNodeName(LinkedToNode), *PinLabel);
|
||||
}
|
||||
|
||||
// String pins: always show in quotes (even if empty).
|
||||
FName Category = Pin->PinType.PinCategory;
|
||||
if (Category == UEdGraphSchema_K2::PC_String ||
|
||||
Category == UEdGraphSchema_K2::PC_Name ||
|
||||
Category == UEdGraphSchema_K2::PC_Text)
|
||||
{
|
||||
return FString::Printf(TEXT("\"%s\""), *Pin->DefaultValue);
|
||||
}
|
||||
|
||||
// If has a non-empty default, show it.
|
||||
if (!Pin->DefaultValue.IsEmpty())
|
||||
{
|
||||
return Pin->DefaultValue;
|
||||
}
|
||||
|
||||
if (Pin->DefaultObject)
|
||||
{
|
||||
return Pin->DefaultObject->GetName();
|
||||
}
|
||||
|
||||
if (!Pin->DefaultTextValue.IsEmpty())
|
||||
{
|
||||
return FString::Printf(TEXT("\"%s\""), *Pin->DefaultTextValue.ToString());
|
||||
}
|
||||
|
||||
if (IsDefaultToSelf(Pin))
|
||||
{
|
||||
return TEXT("<self>");
|
||||
}
|
||||
else
|
||||
{
|
||||
return TEXT("<default>");
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::Traverse(UEdGraphNode* Node)
|
||||
{
|
||||
if (Visited.Contains(Node)) return;
|
||||
Visited.Add(Node);
|
||||
|
||||
// First, traverse input nodes
|
||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input))
|
||||
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
|
||||
Traverse(LinkedPin->GetOwningNode());
|
||||
|
||||
// Add this node to the sorted list.
|
||||
SortedNodes.Add(Node);
|
||||
|
||||
// Then, traverse exec output nodes only.
|
||||
// Data outputs are not followed — data nodes get pulled in
|
||||
// through their consumers' input traversal.
|
||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Output, UEdGraphSchema_K2::PC_Exec))
|
||||
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
|
||||
Traverse(LinkedPin->GetOwningNode());
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::SortNodes()
|
||||
{
|
||||
// Find starter nodes: have exec output but no exec input.
|
||||
TArray<UEdGraphNode*> Starters;
|
||||
for (UEdGraphNode* Node : Graph->Nodes)
|
||||
{
|
||||
if (HasExecPin(Node, EGPD_Output) && !HasExecPin(Node, EGPD_Input))
|
||||
{
|
||||
Starters.Add(Node);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort starters by Y position.
|
||||
Starters.Sort([](const UEdGraphNode& A, const UEdGraphNode& B)
|
||||
{
|
||||
return A.NodePosY < B.NodePosY;
|
||||
});
|
||||
|
||||
// Traverse from each starter.
|
||||
for (UEdGraphNode* Starter : Starters)
|
||||
{
|
||||
Traverse(Starter);
|
||||
}
|
||||
|
||||
// Traverse all nodes.
|
||||
for (UEdGraphNode* Node : Graph->Nodes)
|
||||
{
|
||||
Traverse(Node);
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::AssignNodeNames()
|
||||
{
|
||||
TMap<FString, int32> NextIndex;
|
||||
|
||||
for (UEdGraphNode* Node : SortedNodes)
|
||||
{
|
||||
FString Base = FormatNodeBaseName(Node);
|
||||
int32& Idx = NextIndex.FindOrAdd(Base, 0);
|
||||
FString Name = (Idx == 0) ? Base : FString::Printf(TEXT("%s_%d"), *Base, Idx + 1);
|
||||
NodeNames.Add(Node, Name);
|
||||
Idx++;
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitNode(UEdGraphNode* Node)
|
||||
{
|
||||
if (Node->IsA<UEdGraphNode_Comment>())
|
||||
{
|
||||
Output.Appendf(TEXT("\n// %s\n"), *Node->NodeComment);
|
||||
return;
|
||||
}
|
||||
|
||||
Output.Appendf(TEXT("\nnode %s\n"), *FormatNodeName(Node));
|
||||
|
||||
// Emit input data pins.
|
||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input))
|
||||
{
|
||||
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue;
|
||||
if (Pin->bHidden) continue;
|
||||
|
||||
Output.Appendf(TEXT(" input %s %s = %s\n"),
|
||||
*FormatPinType(Pin),
|
||||
*FormatPinName(Pin),
|
||||
*FormatPinSource(Pin));
|
||||
}
|
||||
|
||||
// Emit output data pins as a return line.
|
||||
FString ReturnPins;
|
||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Output))
|
||||
{
|
||||
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue;
|
||||
if (Pin->bHidden) continue;
|
||||
if (!ReturnPins.IsEmpty()) ReturnPins += TEXT(", ");
|
||||
ReturnPins += FormatPinName(Pin);
|
||||
}
|
||||
if (!ReturnPins.IsEmpty())
|
||||
{
|
||||
Output.Appendf(TEXT(" return %s\n"), *ReturnPins);
|
||||
}
|
||||
|
||||
// Emit output exec pins as goto statements.
|
||||
TArray<UEdGraphPin*> ExecOuts = FilterPins(Node, EGPD_Output, UEdGraphSchema_K2::PC_Exec);
|
||||
for (UEdGraphPin* Pin : ExecOuts)
|
||||
{
|
||||
UEdGraphPin* LinkedTo = GetLinkedTo(Pin);
|
||||
if (!LinkedTo) continue;
|
||||
|
||||
FString Target = FormatNodeName(LinkedTo->GetOwningNode());
|
||||
|
||||
if (ExecOuts.Num() == 1)
|
||||
Output.Appendf(TEXT(" goto %s\n"), *Target);
|
||||
else
|
||||
Output.Appendf(TEXT(" goto-if %s %s\n"), *FormatPinName(Pin), *Target);
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitLocalVariables()
|
||||
{
|
||||
for (UEdGraphNode* Node : Graph->Nodes)
|
||||
{
|
||||
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(Node);
|
||||
if (!EntryNode) continue;
|
||||
|
||||
for (const FBPVariableDescription& Var : EntryNode->LocalVariables)
|
||||
{
|
||||
FString Default = Var.DefaultValue.IsEmpty() ? TEXT("<default>") : Var.DefaultValue;
|
||||
Output.Appendf(TEXT("local %s %s = %s\n"),
|
||||
*FormatPinType(Var.VarType),
|
||||
*SanitizeName(Var.VarName.ToString()),
|
||||
*Default);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitGraph()
|
||||
{
|
||||
for (UEdGraphNode* Node : SortedNodes)
|
||||
{
|
||||
if (Node->IsA<UK2Node_Knot>()) continue;
|
||||
if (Node->IsA<UK2Node_VariableGet>()) continue;
|
||||
EmitNode(Node);
|
||||
}
|
||||
}
|
||||
|
||||
void FlxBlueprintExporter::EmitNodeList()
|
||||
{
|
||||
for (UEdGraphNode* Node : SortedNodes)
|
||||
{
|
||||
if (Node->IsA<UK2Node_Knot>()) continue;
|
||||
if (Node->IsA<UEdGraphNode_Comment>()) continue;
|
||||
if (Node->IsA<UK2Node_VariableGet>()) continue;
|
||||
Details.Appendf(TEXT("%s = %s\n"),
|
||||
*FormatNodeName(Node), *Node->NodeGuid.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
128
Source/Integration/BlueprintExporter.h
Normal file
128
Source/Integration/BlueprintExporter.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
|
||||
class FlxBlueprintExporter
|
||||
{
|
||||
public:
|
||||
FlxBlueprintExporter(UEdGraph* InGraph);
|
||||
|
||||
const FString GetOutput() { return Output.ToString(); }
|
||||
const FString GetDetails() { return Details.ToString(); }
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// General utilities for manipulating UEdGraph nodes.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
// Sanitize a name: trim whitespace, and replace space
|
||||
// with underscores.
|
||||
//
|
||||
static FString SanitizeName(const FString& Title);
|
||||
|
||||
// Get the pin type as a string. This is lossy,
|
||||
// we don't differentiate between object reference
|
||||
// or struct, for example. But that's OK, we don't
|
||||
// need precise type info, we just need readability.
|
||||
//
|
||||
static FString FormatPinType(const FEdGraphPinType& PinType);
|
||||
static FString FormatPinType(UEdGraphPin* Pin);
|
||||
|
||||
// Get the node base name as a sanitized string.
|
||||
// Later, we may append a number to this base name
|
||||
// in order to turn it into a unique string.
|
||||
//
|
||||
static FString FormatNodeBaseName(UEdGraphNode* Node);
|
||||
|
||||
// Get the pin that this pin is linked to. If the
|
||||
// pin is linked to multiple pins, returns the first.
|
||||
// If the pin is linked to a knot node, follow the
|
||||
// chain of knot nodes and find the pin at the other
|
||||
// end of the chain. Returns nullptr if this pin
|
||||
// is not linked to anything.
|
||||
//
|
||||
static UEdGraphPin* GetLinkedTo(UEdGraphPin *Pin);
|
||||
|
||||
// Return true if the pin in question defaults
|
||||
// to self.
|
||||
//
|
||||
static bool IsDefaultToSelf(UEdGraphPin* Pin);
|
||||
|
||||
// Get a subset of the pins in the node, filtered
|
||||
// by direction, category, or both.
|
||||
//
|
||||
static TArray<UEdGraphPin*> FilterPins(UEdGraphNode* Node,
|
||||
EEdGraphPinDirection Direction = EGPD_MAX, FName Category = FName());
|
||||
|
||||
// Return true if the node has an exec pin that points
|
||||
// in the specified direction.
|
||||
//
|
||||
static bool HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction);
|
||||
|
||||
// Find the first pin that points in the specified direction.
|
||||
//
|
||||
static UEdGraphPin* FindFirstPin(UEdGraphNode* Node, EEdGraphPinDirection Direction);
|
||||
|
||||
// Given a sanitized pin display name or a sanitized pin
|
||||
// name, find the one pin that matches. If the string
|
||||
// provided doesn't match any pin, or if it matches
|
||||
// multiple pins ambiguously, returns nullptr. If the
|
||||
// string is the sanitized empty string, returns nullptr
|
||||
// even if that matches a pin.
|
||||
//
|
||||
static UEdGraphPin* BestMatchPin(UEdGraphNode* Node, EEdGraphPinDirection Direction, bool Exec, const FString& Name);
|
||||
|
||||
// Returns either the sanitized display name or
|
||||
// sanitized pin name. Chooses the one that
|
||||
// unambiguously identifies the pin. If neither is
|
||||
// ambiguous, prefers the display name. If both are
|
||||
// ambiguous, returns the display name with a question
|
||||
// mark prepended to indicate that it doesn't uniquely
|
||||
// identify the pin.
|
||||
//
|
||||
static FString FormatPinName(UEdGraphPin *Pin);
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// Traverse and Emit the Nodes.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
FString FormatNodeName(UEdGraphNode* Node);
|
||||
FString FormatPinSource(UEdGraphPin* Pin);
|
||||
void Traverse(UEdGraphNode* Node);
|
||||
void SortNodes();
|
||||
void AssignNodeNames();
|
||||
void EmitNode(UEdGraphNode* Node);
|
||||
void EmitLocalVariables();
|
||||
void EmitGraph();
|
||||
void EmitNodeList();
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// Values recorded during traversal.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
UEdGraph* Graph;
|
||||
|
||||
// Data populated by passes.
|
||||
TMap<UEdGraphNode*, FString> NodeNames;
|
||||
TArray<UEdGraphNode*> SortedNodes;
|
||||
TSet<UEdGraphNode*> Visited;
|
||||
|
||||
// Output buffers.
|
||||
TStringBuilder<4096> Output;
|
||||
TStringBuilder<4096> Details;
|
||||
};
|
||||
|
||||
#endif
|
||||
76
Source/Integration/BreakToDebugger.cpp
Normal file
76
Source/Integration/BreakToDebugger.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
#include "BreakToDebugger.h"
|
||||
#include "Blueprint/BlueprintExceptionInfo.h"
|
||||
#include "Kismet2/KismetDebugUtilities.h"
|
||||
|
||||
ELogVerbosity::Type FlxBreakToDebuggerOutputDevice::ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity) {
|
||||
switch (Verbosity) {
|
||||
case ElxBreakToDebuggerThreshold::Error: return ELogVerbosity::Error;
|
||||
case ElxBreakToDebuggerThreshold::Warning: return ELogVerbosity::Warning;
|
||||
case ElxBreakToDebuggerThreshold::Display: return ELogVerbosity::Display;
|
||||
case ElxBreakToDebuggerThreshold::Log: return ELogVerbosity::Log;
|
||||
case ElxBreakToDebuggerThreshold::Verbose: return ELogVerbosity::Verbose;
|
||||
case ElxBreakToDebuggerThreshold::VeryVerbose: return ELogVerbosity::VeryVerbose;
|
||||
case ElxBreakToDebuggerThreshold::Fatal: return ELogVerbosity::Fatal;
|
||||
}
|
||||
}
|
||||
|
||||
FlxBreakToDebuggerOutputDevice::FlxBreakToDebuggerOutputDevice(const ElxBreakToDebuggerThreshold &SensitivityRef)
|
||||
: Sensitivity(SensitivityRef)
|
||||
{
|
||||
GLog->AddOutputDevice(this);
|
||||
}
|
||||
|
||||
FlxBreakToDebuggerOutputDevice::~FlxBreakToDebuggerOutputDevice()
|
||||
{
|
||||
GLog->RemoveOutputDevice(this);
|
||||
}
|
||||
|
||||
namespace UBreakPoint {
|
||||
static volatile int V;
|
||||
FORCENOINLINE static void OnLogFatal() {
|
||||
V = 0;
|
||||
}
|
||||
FORCENOINLINE static void OnLogError() {
|
||||
V = 1;
|
||||
OnLogFatal();
|
||||
}
|
||||
FORCENOINLINE static void OnLogWarning() {
|
||||
V = 2;
|
||||
OnLogError();
|
||||
}
|
||||
}
|
||||
|
||||
static const FName LogBlueprintDebugName(TEXT("LogBlueprintDebug"));
|
||||
|
||||
|
||||
void FlxBreakToDebuggerOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category)
|
||||
{
|
||||
// If the error isn't serious enough, do nothing.
|
||||
//
|
||||
if (Verbosity > ConvertThreshold(Sensitivity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the Category is LogBlueprintDebug, then we're inside the debugger already.
|
||||
//
|
||||
if (Category == LogBlueprintDebugName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find out if we're running in a blueprint thread. If not, return.
|
||||
//
|
||||
FFrame* Frame = FFrame::GetThreadLocalTopStackFrame();
|
||||
if (Frame == nullptr) return;
|
||||
UObject *TopObject = Frame->Object;
|
||||
if (TopObject == nullptr) return;
|
||||
|
||||
// Notify the debugger that there's been an exception.
|
||||
//
|
||||
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
|
||||
FBlueprintCoreDelegates::ThrowScriptException(TopObject, *Frame, ExceptionInfo);
|
||||
}
|
||||
|
||||
|
||||
113
Source/Integration/BreakToDebugger.h
Normal file
113
Source/Integration/BreakToDebugger.h
Normal file
@@ -0,0 +1,113 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// BreakToDebugger
|
||||
//
|
||||
// When an error message gets written to UE_LOG, we can
|
||||
// optionally trigger the blueprint debugger.
|
||||
//
|
||||
// This only affects UE_LOG messages that are generated
|
||||
// during blueprint execution. Log messages from other
|
||||
// threads do not trigger the debugger.
|
||||
//
|
||||
// The following explains how we trigger the blueprint
|
||||
// debugger on UE_LOG messages. Log messages are sent to a
|
||||
// long list of output devices, including: the visual studio
|
||||
// output window, the unreal editor output window, the log
|
||||
// file, and so forth. We add another pseudo output device.
|
||||
// This output device doesn't actually send the log message
|
||||
// anywhere, instead, it just activates the blueprint
|
||||
// debugger.
|
||||
//
|
||||
// UE_LOG messages can be generated from any thread. The
|
||||
// pseudo output device checks what thread it is running in,
|
||||
// and if it's not the blueprint thread, it does nothing at
|
||||
// all. If it is the blueprint thread, it's safe to trigger
|
||||
// the blueprint debugger.
|
||||
//
|
||||
// One annoying limitation of this design is that our output
|
||||
// device ends up early in the list, so the debugger runs
|
||||
// *before* the message shows up in visual studio or the
|
||||
// unreal editor. As a result, when you are in the
|
||||
// debugger, the message won't be in your output window.
|
||||
// Pressing 'single step' always reveals the message.
|
||||
//
|
||||
// The blueprint node "Format Log Message" uses UE_LOG
|
||||
// internally, so therefore, that too can trigger the
|
||||
// blueprint debugger.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/Array.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "Misc/OutputDeviceError.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "BreakToDebugger.generated.h"
|
||||
|
||||
// ElxBreakToDebuggerThreshold:
|
||||
//
|
||||
// Controls the sensitivity level at which UE_LOG messages
|
||||
// trigger the blueprint debugger.
|
||||
//
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxBreakToDebuggerThreshold : uint8 {
|
||||
|
||||
/** Break on errors. */
|
||||
Error,
|
||||
|
||||
/** Break on warnings and above. */
|
||||
Warning,
|
||||
|
||||
/** Break on display messages and above. */
|
||||
Display,
|
||||
|
||||
/** Break on log messages and above. */
|
||||
Log,
|
||||
|
||||
/** Break on verbose messages and above. */
|
||||
Verbose,
|
||||
|
||||
/** Break on all messages. */
|
||||
VeryVerbose,
|
||||
|
||||
/** Break on fatal errors only (ie, never break -- the process crashes instead). */
|
||||
Fatal,
|
||||
};
|
||||
|
||||
|
||||
struct FlxBreakToDebuggerOutputDevice : public FOutputDevice
|
||||
{
|
||||
public:
|
||||
// The constructor and destructor automatically register
|
||||
// this output device with GLog.
|
||||
//
|
||||
// This struct doesn't store the sensitivity threshold.
|
||||
// It relies on the LuprexGameMode class to do that, so
|
||||
// that the threshold can be easily edited with the
|
||||
// blueprint editor. This struct must be initialized
|
||||
// with a reference to the threshold variable.
|
||||
//
|
||||
FlxBreakToDebuggerOutputDevice(const ElxBreakToDebuggerThreshold &SensitivityRef);
|
||||
~FlxBreakToDebuggerOutputDevice();
|
||||
|
||||
// Inspect a log message.
|
||||
//
|
||||
INTEGRATION_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
|
||||
|
||||
// If the device is marked 'CanBeUsedOnMultipleThreads,'
|
||||
// then UE_LOG will call Serialize from the current
|
||||
// thread, otherwise, it will call Serialize from the
|
||||
// logging thread. Using the logging thread would
|
||||
// defeat the purpose of this device, so it's imperative
|
||||
// that we set this flag.
|
||||
//
|
||||
INTEGRATION_API virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||
|
||||
private:
|
||||
static ELogVerbosity::Type ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity);
|
||||
const ElxBreakToDebuggerThreshold &Sensitivity;
|
||||
};
|
||||
@@ -1,37 +1,50 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Common.h
|
||||
//
|
||||
// Simple data types used throughout the Unreal
|
||||
// interface to Luprex: type aliases, blueprint
|
||||
// enums for branching, and log categories.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "Common.generated.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A bunch of simple data types that are used throughout the
|
||||
// unreal interface to Luprex.
|
||||
// LpxCommonTypes
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Type aliases used throughout the integration.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
namespace LpxCommonTypes {
|
||||
// Array of tangible IDs.
|
||||
//
|
||||
using IdArray = TArray<int64>;
|
||||
|
||||
// View of Array of tangible IDs.
|
||||
//
|
||||
using IdView = TArrayView<const int64>;
|
||||
|
||||
// Array of std::string_view
|
||||
// Array of std::string_view.
|
||||
//
|
||||
using StringViewVec = TArray<std::string_view>;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Luprex exports a header-only library called "base-buffer.hpp",
|
||||
// which includes a "struct LuaValueHolder" which contains a tag
|
||||
// field of type "enum LuaValueType." The following enum is a
|
||||
// direct copy of that enum, and the values must match one-for-one.
|
||||
// Note that "token" has been renamed to "name", those are
|
||||
// synonymous.
|
||||
// ElxLuaValueType
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Mirror of LuaValueType from base-buffer.hpp.
|
||||
// Values must match one-for-one. Note that "token"
|
||||
// has been renamed to "name" (synonymous).
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxLuaValueType : uint8 {
|
||||
@@ -43,13 +56,14 @@ enum class ElxLuaValueType : uint8 {
|
||||
Vector
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A variety of boolean-like results that can be conveniently
|
||||
// be used in conjunction with 'ExpandEnumAsExecs' to create
|
||||
// branching functions.
|
||||
// Branching Enums
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Boolean-like results used with ExpandEnumAsExecs
|
||||
// to create branching blueprint functions.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxSuccessOrError : uint8 {
|
||||
@@ -81,16 +95,39 @@ enum class ElxSuccessOrWrongType : uint8 {
|
||||
WrongType,
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Two log categories that are used throughout the Unreal
|
||||
// Luprex integration.
|
||||
// ElxLuaSyntaxCheck
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Classifies console commands syntactically:
|
||||
//
|
||||
// SlashCommand: starts with a slash, not lua
|
||||
// Whitespace: the input only contains whitespace
|
||||
// ValidLua: the input is valid lua code
|
||||
// TruncatedLua: the input is truncated
|
||||
// InvalidLua: invalid lua
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxLuaSyntaxCheck : uint8 {
|
||||
SlashCommand,
|
||||
Whitespace,
|
||||
ValidLua,
|
||||
TruncatedLua,
|
||||
InvalidLua,
|
||||
};
|
||||
|
||||
// Messages that come from inside the Luprex Core.
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Log Categories
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// Messages from inside the Luprex Core.
|
||||
//
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Display, All);
|
||||
|
||||
// Messages that pertain to our Luprex integration with Unreal.
|
||||
// Messages about the Luprex integration with Unreal.
|
||||
//
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All);
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ConsoleOutput.h
|
||||
//
|
||||
// Optional helper for the console window. The
|
||||
// GameMode blueprint can use this class to store
|
||||
// console text as one big string with newlines.
|
||||
//
|
||||
// A dirty bit is set whenever text is appended,
|
||||
// so the blueprint only needs to update the widget
|
||||
// when the text has actually changed.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
|
||||
#include "ConsoleOutput.generated.h"
|
||||
|
||||
<<<<<<< HEAD
|
||||
//////////////////////////////////////////////////////////////
|
||||
=======
|
||||
////////////////////////////////////////////////////////////
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
//
|
||||
// ConsoleOutput
|
||||
// UlxConsoleOutput
|
||||
//
|
||||
<<<<<<< HEAD
|
||||
// When the lua code executes a print statement, the text
|
||||
// eventually gets passed to the GameMode blueprint: see
|
||||
// Docs/Print-Statement-Handling.md for more information.
|
||||
@@ -34,6 +53,9 @@
|
||||
// implement the virtual console, that's perfectly fine.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
=======
|
||||
////////////////////////////////////////////////////////////
|
||||
>>>>>>> 9b1dd00a45a7b17c3546f8574d00e5ec78f17c75
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class UlxConsoleOutput : public UObject
|
||||
@@ -45,36 +67,46 @@ private:
|
||||
bool Dirty;
|
||||
|
||||
private:
|
||||
// Truncate the console to a reasonable number of
|
||||
// lines. The length is hardwired.
|
||||
// Truncate the console to a reasonable number of lines.
|
||||
// The length is hardwired.
|
||||
//
|
||||
void Truncate();
|
||||
|
||||
// Add a newline if there isn't one. Returns true if it changed anything.
|
||||
// Add a newline if there isn't one.
|
||||
//
|
||||
// Returns true if it changed anything.
|
||||
//
|
||||
bool MaybeAppendNewline();
|
||||
|
||||
// Append text. Returns true if it changed anything.
|
||||
// Append text.
|
||||
//
|
||||
// Returns true if it changed anything.
|
||||
//
|
||||
bool MaybeAppendText(const FString& text);
|
||||
|
||||
public:
|
||||
// Append a line of text to the console.
|
||||
// Append text to the console.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void Append(const FString& text);
|
||||
|
||||
// Append a line of text to the console on a line by itself.
|
||||
// Append text on a line by itself.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void AppendLine(const FString& text);
|
||||
|
||||
// Get the console text as a string.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable)
|
||||
const FString& Get() const { return Content; }
|
||||
|
||||
|
||||
// Return if the dirty flag is set.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool IsDirty() const { return Dirty; }
|
||||
|
||||
// Clear the dirty flag.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void ClearDirty() { Dirty = false; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
205
Source/Integration/FormatDataLibrary.cpp
Normal file
205
Source/Integration/FormatDataLibrary.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
#include "FormatDataLibrary.h"
|
||||
#include "LuaCall.h"
|
||||
#include "AnimQueue.h"
|
||||
#include "MovementComponentState.h"
|
||||
#include "Kismet/KismetTextLibrary.h"
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Float;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueFloat = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Double;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueDouble = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataKey(FKey AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue.GetFName());
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Gender;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueGender = AutoConvertedValue;
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(AutoConvertedValue);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::FromString(AutoConvertedValue->DebugString());
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::FromString(UlxAnimationStepLibrary::AnimationStepDebugString(AutoConvertedValue));
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::FromString(UlxMovementComponentStateLibrary::DebugString(AutoConvertedValue));
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString &Name)
|
||||
{
|
||||
FFormatArgumentData Result;
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText();
|
||||
return Result;
|
||||
}
|
||||
|
||||
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject)
|
||||
{
|
||||
const UEnum *Enum = Cast<const UEnum>(PinSubCategoryObject);
|
||||
FFormatArgumentData Result;
|
||||
if (Enum == nullptr)
|
||||
{
|
||||
Result.ArgumentValueType = EFormatArgumentType::Int;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValueInt = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.ArgumentValueType = EFormatArgumentType::Text;
|
||||
Result.ArgumentName = Name;
|
||||
Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value));
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
110
Source/Integration/FormatDataLibrary.h
Normal file
110
Source/Integration/FormatDataLibrary.h
Normal file
@@ -0,0 +1,110 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FormatDataLibrary.h
|
||||
//
|
||||
// Functions that convert data into
|
||||
// FFormatArgumentData structs, so that the data
|
||||
// can be passed to FText::Format.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "FormatDataLibrary.generated.h"
|
||||
|
||||
class UlxLuaValues;
|
||||
struct FlxAnimationStep;
|
||||
struct FlxMovementComponentState;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UlxFormatDataLibrary
|
||||
//
|
||||
// The FormatLogMessage K2Node scans this library using
|
||||
// reflection, looking for functions that have a parameter
|
||||
// named "AutoConvertedValue". It uses the type of that
|
||||
// parameter to determine which function to call for a given
|
||||
// pin type.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class UlxFormatDataLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataByte(uint8 AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataInt(int AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataInt64(int64 AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataFloat(float AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataDouble(double AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataText(FText AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataString(FString AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataName(FName AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataKey(FKey AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataGender(ETextGender AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataObject(UObject *AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataVector(const FVector &AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataVector2D(const FVector2D &AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataRotator(const FRotator &AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name);
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name);
|
||||
|
||||
// For pins that were never connected.
|
||||
//
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataBlank(const FString &Name);
|
||||
|
||||
// For pins of enum types.
|
||||
//
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
|
||||
static FFormatArgumentData FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject);
|
||||
};
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BlueprintErrors.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "Internationalization/Text.h"
|
||||
#include "K2Node.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "BlueprintErrors.h"
|
||||
|
||||
#include "FormatError.generated.h"
|
||||
|
||||
class FBlueprintActionDatabaseRegistrar;
|
||||
class FString;
|
||||
class UEdGraph;
|
||||
class UObject;
|
||||
|
||||
//
|
||||
// FormatMessage and FormatErrorMessage
|
||||
//
|
||||
// This file defines two K2Nodes: FormatMessage, and FormatErrorMessage. The
|
||||
// only difference between them is that the former outputs the message as an
|
||||
// output pin. The latter outputs the message to the log instead.
|
||||
//
|
||||
// To implement code reuse, we put all the code into FormatMessage, and made
|
||||
// FormatErrorMessage a derived class of FormatMessage. The derived class
|
||||
// doesn't override anything - all it does is set a flag, the flag changes
|
||||
// the behavior of FormatMessage.
|
||||
//
|
||||
//
|
||||
UCLASS(MinimalAPI)
|
||||
class UK2Node_FormatMessage : public UK2Node
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
//~ Begin UObject Interface
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
//~ End UObject Interface
|
||||
|
||||
//~ Begin UEdGraphNode Interface.
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
virtual bool ShouldShowNodeProperties() const override { return true; }
|
||||
virtual void PinConnectionListChanged(UEdGraphPin* Pin) override;
|
||||
virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
|
||||
virtual void PinTypeChanged(UEdGraphPin* Pin) override;
|
||||
virtual FText GetTooltipText() const override;
|
||||
virtual FText GetPinDisplayName(const UEdGraphPin* Pin) const override;
|
||||
//~ End UEdGraphNode Interface.
|
||||
|
||||
//~ Begin UK2Node Interface.
|
||||
virtual bool IsNodePure() const override { return false; }
|
||||
virtual void PostReconstructNode() override;
|
||||
virtual bool NodeCausesStructuralBlueprintChange() const override { return true; }
|
||||
virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
|
||||
virtual ERedirectType DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const override;
|
||||
virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
|
||||
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
|
||||
virtual FText GetMenuCategory() const override;
|
||||
virtual int32 GetNodeRefreshPriority() const override { return EBaseNodeRefreshPriority::Low_UsesDependentWildcard; }
|
||||
//~ End UK2Node Interface.
|
||||
|
||||
protected:
|
||||
/** Create all necessary pins */
|
||||
void CreateCorrectPins();
|
||||
|
||||
/** Synchronize the type of the given argument pin with the type its connected to, or reset it to a wildcard pin if there's no connection */
|
||||
void SynchronizeArgumentPinType(UEdGraphPin* Pin);
|
||||
|
||||
/** Our derived class will set this to true, altering the behavior of this K2Node. **/
|
||||
virtual bool IsFormatErrorMessage() const { return false; }
|
||||
|
||||
protected:
|
||||
/** When adding arguments to the node, their names are placed here and are generated as pins during construction */
|
||||
UPROPERTY()
|
||||
TArray<FString> PinNames;
|
||||
|
||||
/** Tooltip text for this node. */
|
||||
FText NodeTooltip;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// This derives from FormatMessage.
|
||||
//
|
||||
UCLASS(MinimalAPI)
|
||||
class UK2Node_FormatErrorMessage : public UK2Node_FormatMessage
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
// Setting this flag alters the behavior of FormatMessage, making it
|
||||
// output to the log instead of to a pin.
|
||||
//
|
||||
virtual bool IsFormatErrorMessage() const override { return true; }
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "FormatError.h"
|
||||
#include "FormatMessage.h"
|
||||
|
||||
#include "Internationalization/TextFormatter.h"
|
||||
#include "BlueprintActionDatabaseRegistrar.h"
|
||||
#include "BlueprintNodeSpawner.h"
|
||||
#include "Containers/EnumAsByte.h"
|
||||
@@ -107,7 +108,7 @@ void UK2Node_FormatMessage::CreateCorrectPins()
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
if (FindPin(VerbosityPinName, EGPD_Input) == nullptr) {
|
||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxLogVerbosity>(), VerbosityPinName);
|
||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxFormatLogVerbosity>(), VerbosityPinName);
|
||||
P->DefaultValue = TEXT("Error");
|
||||
P->AutogeneratedDefaultValue = P->DefaultValue;
|
||||
}
|
||||
@@ -301,7 +302,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
|
||||
//
|
||||
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && AllowWild)
|
||||
{
|
||||
return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataBlank));
|
||||
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank));
|
||||
}
|
||||
|
||||
// Try to find a match in the UlxFormatDataLibrary.
|
||||
@@ -309,7 +310,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
|
||||
for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It)
|
||||
{
|
||||
UFunction* Function = *It;
|
||||
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("Value"));
|
||||
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("AutoConvertedValue"));
|
||||
FEdGraphPinType ValuePinType;
|
||||
bool Convertible = Schema->ConvertPropertyToPinType(ValueProperty, ValuePinType);
|
||||
if (!Convertible) continue;
|
||||
@@ -322,7 +323,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
|
||||
//
|
||||
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast<const UEnum>(PinType.PinSubCategoryObject)))
|
||||
{
|
||||
return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataEnum));
|
||||
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataEnum));
|
||||
}
|
||||
|
||||
// A case for subclasses of 'Object' which are not exactly 'Object'
|
||||
@@ -359,7 +360,7 @@ void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerCon
|
||||
UFunction *FormatFunction;
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
FormatFunction = UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatErrorInternal));
|
||||
FormatFunction = UK2Node_FormatMessage::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UK2Node_FormatMessage, FormatLogMessageInternal));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -399,7 +400,7 @@ void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerCon
|
||||
ConvertNode->SetFromFunction(Converter);
|
||||
ConvertNode->AllocateDefaultPins();
|
||||
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(ConvertNode, this);
|
||||
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("Value"));
|
||||
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("AutoConvertedValue"));
|
||||
UEdGraphPin *NamePin = ConvertNode->FindPinChecked(TEXT("Name"));
|
||||
UEdGraphPin *SubCategoryObjectPin = ConvertNode->FindPin(TEXT("PinSubCategoryObject"));
|
||||
|
||||
@@ -559,7 +560,7 @@ UK2Node_FormatMessage::UK2Node_FormatMessage(const FObjectInitializer& ObjectIni
|
||||
);
|
||||
}
|
||||
|
||||
UK2Node_FormatErrorMessage::UK2Node_FormatErrorMessage(const FObjectInitializer& ObjectInitializer)
|
||||
UK2Node_FormatLogMessage::UK2Node_FormatLogMessage(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
NodeTooltip = LOCTEXT("NodeTooltip",
|
||||
@@ -572,4 +573,70 @@ UK2Node_FormatErrorMessage::UK2Node_FormatErrorMessage(const FObjectInitializer&
|
||||
);
|
||||
}
|
||||
|
||||
ELogVerbosity::Type UK2Node_FormatMessage::ConvertElxFormatLogVerbosity(ElxFormatLogVerbosity Verbosity) {
|
||||
switch (Verbosity) {
|
||||
case ElxFormatLogVerbosity::Error: return ELogVerbosity::Error;
|
||||
case ElxFormatLogVerbosity::Warning: return ELogVerbosity::Warning;
|
||||
case ElxFormatLogVerbosity::Display: return ELogVerbosity::Display;
|
||||
case ElxFormatLogVerbosity::Log: return ELogVerbosity::Log;
|
||||
case ElxFormatLogVerbosity::ThrottledDisplay: return ELogVerbosity::Display;
|
||||
case ElxFormatLogVerbosity::ThrottledLog: return ELogVerbosity::Log;
|
||||
case ElxFormatLogVerbosity::Verbose: return ELogVerbosity::Verbose;
|
||||
case ElxFormatLogVerbosity::VeryVerbose: return ELogVerbosity::VeryVerbose;
|
||||
case ElxFormatLogVerbosity::Fatal: return ELogVerbosity::Fatal;
|
||||
}
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::FormatLogMessageInternal(UObject *Context, ElxFormatLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
|
||||
{
|
||||
// For throttled verbosity levels, suppress repeated messages with the
|
||||
// same format pattern. We key on the blueprint name + format pattern,
|
||||
// and allow at most one message per second per key.
|
||||
//
|
||||
if (Verbosity == ElxFormatLogVerbosity::ThrottledDisplay || Verbosity == ElxFormatLogVerbosity::ThrottledLog)
|
||||
{
|
||||
static TMap<FString, double> LastLogTime;
|
||||
double Now = FPlatformTime::Seconds();
|
||||
FString Key = Context->GetClass()->GetName() + TEXT("::") + InPattern;
|
||||
double &Last = LastLogTime.FindOrAdd(Key, 0.0);
|
||||
if (Now - Last < 1.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Last = Now;
|
||||
}
|
||||
|
||||
// Generate the formatted string.
|
||||
//
|
||||
FText InPatternText(FText::FromString(InPattern));
|
||||
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
|
||||
FString MessageString = Message.ToString();
|
||||
|
||||
// Get the blueprint name.
|
||||
//
|
||||
// Normally, the log function expects you to pass in a filename, and a log
|
||||
// category name. We use the blueprint name for both.
|
||||
//
|
||||
// Using the blueprint name as a log category name is not technically
|
||||
// correct. However, there is no correct way to create log categories
|
||||
// from inside of blueprints. Doing it this way at least produces a reasonable
|
||||
// message inside the log. What doesn't work correctly is the log message
|
||||
// suppression system. Ie, console commands like 'log <category> verbose'
|
||||
// don't have any effect here. The design of the log message suppression
|
||||
// system is such that there just is no reasonable way to hook into it from
|
||||
// inside of blueprints.
|
||||
//
|
||||
FString BlueprintNameString = Context->GetClass()->GetName();
|
||||
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
|
||||
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
|
||||
|
||||
// Output to Log
|
||||
//
|
||||
ELogVerbosity::Type VerbosityValue = ConvertElxFormatLogVerbosity(Verbosity);
|
||||
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
|
||||
{
|
||||
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
174
Source/Integration/FormatMessage.h
Normal file
174
Source/Integration/FormatMessage.h
Normal file
@@ -0,0 +1,174 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FormatMessage.h
|
||||
//
|
||||
// Two K2Nodes: FormatMessage and FormatLogMessage.
|
||||
// FormatMessage outputs a formatted string as a pin.
|
||||
// FormatLogMessage outputs it to the log instead.
|
||||
//
|
||||
// FormatLogMessage is a derived class that just sets
|
||||
// a flag to alter the base class behavior.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BreakToDebugger.h"
|
||||
#include "FormatDataLibrary.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "Internationalization/Text.h"
|
||||
#include "K2Node.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
|
||||
#include "FormatMessage.generated.h"
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ElxFormatLogVerbosity
|
||||
//
|
||||
// Controls the ELogVerbosity of the UE_LOG directive inside
|
||||
// FormatLogMessage. Also controls the throttling of log
|
||||
// messages.
|
||||
//
|
||||
// Fatal is deliberately placed at the end so that the
|
||||
// editor defaults to Error (value 0) when the dropdown is
|
||||
// uninitialized. The numeric values don't match
|
||||
// ELogVerbosity, so a conversion function is needed.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxFormatLogVerbosity : uint8 {
|
||||
|
||||
/** Prints an error to the console and log file. The editor collects and reports errors. */
|
||||
Error,
|
||||
|
||||
/** Prints a warning to the console and log file. The editor collects and reports warnings. */
|
||||
Warning,
|
||||
|
||||
/** Prints a message to the console and log file. */
|
||||
Display,
|
||||
|
||||
/** Prints a message to the log file, but not to the console. */
|
||||
Log,
|
||||
|
||||
/** Like Display, but suppresses repeated messages with the same format pattern (at most once per second). */
|
||||
ThrottledDisplay,
|
||||
|
||||
/** Like Log, but suppresses repeated messages with the same format pattern (at most once per second). */
|
||||
ThrottledLog,
|
||||
|
||||
/** Prints a message to the log file only if Verbose logging is enabled for the given category. */
|
||||
Verbose,
|
||||
|
||||
/** Prints a message to the log file only if VeryVerbose logging is enabled. */
|
||||
VeryVerbose,
|
||||
|
||||
/** Prints a fatal error to the console and log file, then crashes (this crashes the editor too). */
|
||||
Fatal,
|
||||
};
|
||||
|
||||
class FBlueprintActionDatabaseRegistrar;
|
||||
class FString;
|
||||
class UEdGraph;
|
||||
class UObject;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UK2Node_FormatMessage
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class UK2Node_FormatMessage : public UK2Node
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
//~ Begin UObject Interface
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
//~ End UObject Interface
|
||||
|
||||
//~ Begin UEdGraphNode Interface.
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
virtual bool ShouldShowNodeProperties() const override { return true; }
|
||||
virtual void PinConnectionListChanged(UEdGraphPin* Pin) override;
|
||||
virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
|
||||
virtual void PinTypeChanged(UEdGraphPin* Pin) override;
|
||||
virtual FText GetTooltipText() const override;
|
||||
virtual FText GetPinDisplayName(const UEdGraphPin* Pin) const override;
|
||||
//~ End UEdGraphNode Interface.
|
||||
|
||||
//~ Begin UK2Node Interface.
|
||||
virtual bool IsNodePure() const override { return false; }
|
||||
virtual void PostReconstructNode() override;
|
||||
virtual bool NodeCausesStructuralBlueprintChange() const override { return true; }
|
||||
virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
|
||||
virtual ERedirectType DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const override;
|
||||
virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
|
||||
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
|
||||
virtual FText GetMenuCategory() const override;
|
||||
virtual int32 GetNodeRefreshPriority() const override { return EBaseNodeRefreshPriority::Low_UsesDependentWildcard; }
|
||||
//~ End UK2Node Interface.
|
||||
|
||||
protected:
|
||||
// Create all necessary pins.
|
||||
//
|
||||
void CreateCorrectPins();
|
||||
|
||||
// Synchronize the type of the given argument pin
|
||||
// with the type its connected to, or reset it to
|
||||
// a wildcard pin if there's no connection.
|
||||
//
|
||||
void SynchronizeArgumentPinType(UEdGraphPin* Pin);
|
||||
|
||||
// Derived class sets this to true, altering
|
||||
// the behavior of this K2Node.
|
||||
//
|
||||
virtual bool IsFormatErrorMessage() const { return false; }
|
||||
|
||||
// When IsFormatErrorMessage is true, the K2Node
|
||||
// macroexpands to call this function, which
|
||||
// formats the message and outputs it to the log.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
|
||||
static void FormatLogMessageInternal(UObject *Context, ElxFormatLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
|
||||
|
||||
private:
|
||||
static ELogVerbosity::Type ConvertElxFormatLogVerbosity(ElxFormatLogVerbosity Verbosity);
|
||||
|
||||
protected:
|
||||
// Argument names added to the node, generated as pins
|
||||
// during construction.
|
||||
//
|
||||
UPROPERTY()
|
||||
TArray<FString> PinNames;
|
||||
|
||||
// Tooltip text for this node.
|
||||
//
|
||||
FText NodeTooltip;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UK2Node_FormatLogMessage
|
||||
//
|
||||
// Derives from FormatMessage. Sets a flag to make
|
||||
// the base class output to the log instead of to
|
||||
// a pin.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class UK2Node_FormatLogMessage : public UK2Node_FormatMessage
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
virtual bool IsFormatErrorMessage() const override { return true; }
|
||||
};
|
||||
@@ -1,6 +1,63 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Integration.h"
|
||||
#include "Common.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, Integration, "Integration" );
|
||||
#if WITH_EDITOR
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "UObject/SavePackage.h"
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "BlueprintExporter.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#endif
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE(FlxIntegrationModuleImpl, Integration, "Integration");
|
||||
|
||||
void FlxIntegrationModuleImpl::StartupModule()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
OnAssetSavedHandle = UPackage::PackageSavedWithContextEvent.AddRaw(
|
||||
this, &FlxIntegrationModuleImpl::OnAssetSaved);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlxIntegrationModuleImpl::ShutdownModule()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
UPackage::PackageSavedWithContextEvent.Remove(OnAssetSavedHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void FlxIntegrationModuleImpl::OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context)
|
||||
{
|
||||
if (!Package) return;
|
||||
|
||||
ForEachObjectWithPackage(Package, [&](UObject* Object)
|
||||
{
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(Object))
|
||||
{
|
||||
FString BPDir = FPaths::ProjectDir() / TEXT("Saved") / TEXT("BlueprintExports") / BP->GetName();
|
||||
|
||||
IFileManager::Get().DeleteDirectory(*BPDir, false, true);
|
||||
|
||||
TArray<UEdGraph*> AllGraphs;
|
||||
BP->GetAllGraphs(AllGraphs);
|
||||
|
||||
for (UEdGraph* Graph : AllGraphs)
|
||||
{
|
||||
FlxBlueprintExporter Exporter(Graph);
|
||||
|
||||
FString FilePath = BPDir / Graph->GetName() + TEXT(".txt");
|
||||
FString DetailsPath = BPDir / TEXT("DETAILS") / Graph->GetName() + TEXT(".txt");
|
||||
FFileHelper::SaveStringToFile(Exporter.GetOutput(), *FilePath);
|
||||
FFileHelper::SaveStringToFile(Exporter.GetDetails(), *DetailsPath);
|
||||
UE_LOG(LogLuprexIntegration, Warning, TEXT("Blueprint export: %s"), *FilePath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3,4 +3,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
|
||||
class FlxIntegrationModuleImpl : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
#if WITH_EDITOR
|
||||
void OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context);
|
||||
FDelegateHandle OnAssetSavedHandle;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -83,3 +83,42 @@ StringViewVec FlxLockedWrapper::GetAnimationQueues(IdView ids) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ElxLuaSyntaxCheck FlxLockedWrapper::ValidateLuaExpr(const FString &Code, FString &ErrorMessage) {
|
||||
FTCHARToUTF8 UCode(*Code);
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
Lockable.Wrapper.play_access(Get(), AccessKind::VALIDATE_LUA_EXPR, 0, UCode.Length(), UCode.Get(), &retpklen, &retpk);
|
||||
ErrorMessage = FString(retpklen, (const UTF8CHAR*)retpk);
|
||||
if (ErrorMessage.IsEmpty())
|
||||
return ElxLuaSyntaxCheck::ValidLua;
|
||||
if (ErrorMessage == TEXT("slash command"))
|
||||
return ElxLuaSyntaxCheck::SlashCommand;
|
||||
if (ErrorMessage == TEXT("white space"))
|
||||
return ElxLuaSyntaxCheck::Whitespace;
|
||||
if (ErrorMessage == TEXT("truncated lua"))
|
||||
return ElxLuaSyntaxCheck::TruncatedLua;
|
||||
return ElxLuaSyntaxCheck::InvalidLua;
|
||||
}
|
||||
|
||||
void FlxLockedWrapper::ProbeLuaFunction(std::string_view datapk, int64 place_id, TFunction<void(std::string_view)> OnResult) {
|
||||
if (place_id == 0) place_id = GetActor();
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
Lockable.Wrapper.play_access(Get(), AccessKind::PROBE_LUA_CALL, place_id, datapk.size(), datapk.data(), &retpklen, &retpk);
|
||||
OnResult(std::string_view(retpk, retpklen));
|
||||
}
|
||||
|
||||
void FlxLockedWrapper::InvokeLuaFunction(std::string_view datapk, int64 place_id) {
|
||||
if (place_id == 0) place_id = GetActor();
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
Lockable.Wrapper.play_access(Get(), AccessKind::INVOKE_LUA_CALL, place_id, datapk.size(), datapk.data(), &retpklen, &retpk);
|
||||
}
|
||||
|
||||
void FlxLockedWrapper::InvokeLuaExpr(const FString &Code) {
|
||||
FTCHARToUTF8 UCode(*Code);
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
Lockable.Wrapper.play_access(Get(), AccessKind::INVOKE_LUA_EXPR, 0, UCode.Length(), UCode.Get(), &retpklen, &retpk);
|
||||
}
|
||||
|
||||
@@ -1,77 +1,99 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LockedWrapper.h
|
||||
//
|
||||
// Mutex-guarded access to the EngineWrapper.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "lpx-enginewrapper.hpp"
|
||||
#include "Common.h"
|
||||
|
||||
|
||||
// Class FlxLockableWrapper
|
||||
//
|
||||
// Contains the EngineWrapper and a Mutex to lock it.
|
||||
// This class has no methods. To access the EngineWrapper,
|
||||
// construct a FlxLockedWrapper, and then dereference it
|
||||
// using operator right arrow.
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FlxLockableWrapper
|
||||
//
|
||||
// Contains the EngineWrapper and a Mutex to lock it.
|
||||
// This class has no methods. To access the
|
||||
// EngineWrapper, construct a FlxLockedWrapper.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxLockableWrapper {
|
||||
private:
|
||||
FCriticalSection Mutex;
|
||||
EngineWrapper Wrapper;
|
||||
|
||||
// Temporary buffers. These are only used
|
||||
// inside wrapper methods. There's nothing
|
||||
// persistent in these.
|
||||
// Temporary buffers used only inside wrapper
|
||||
// methods. Nothing persistent in these.
|
||||
//
|
||||
TArray<uint32> AQLenBuffer;
|
||||
TArray<const char*> AQStrBuffer;
|
||||
|
||||
|
||||
public:
|
||||
friend class FlxLockedWrapper;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FlxLockedWrapper
|
||||
//
|
||||
// RAII lock guard. The constructor claims the mutex,
|
||||
// the destructor releases it. Use operator-> to
|
||||
// access the EngineWrapper.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxLockedWrapper {
|
||||
private:
|
||||
FlxLockableWrapper& Lockable;
|
||||
|
||||
// This function is called by luprex to output debugging
|
||||
// messages. It is a thin wrapper around UE_LOG.
|
||||
// Called by luprex to output debugging messages.
|
||||
// A thin wrapper around UE_LOG.
|
||||
//
|
||||
static void DPrintHook(const char *Msg, size_t Size);
|
||||
|
||||
public:
|
||||
// Import these types into our Namespace.
|
||||
// Import these types into our namespace.
|
||||
//
|
||||
using IdArray = LpxCommonTypes::IdArray;
|
||||
using IdView = LpxCommonTypes::IdView;
|
||||
using StringViewVec = LpxCommonTypes::StringViewVec;
|
||||
|
||||
|
||||
public:
|
||||
// The constructor of the FlxLockedWrapper claims the mutex.
|
||||
// The constructor claims the mutex.
|
||||
//
|
||||
FlxLockedWrapper(FlxLockableWrapper& w) : Lockable(w) {
|
||||
Lockable.Mutex.Lock();
|
||||
}
|
||||
|
||||
// The destructor of the FlxLockedWrapper releases the mutex.
|
||||
// The destructor releases the mutex.
|
||||
//
|
||||
~FlxLockedWrapper() {
|
||||
Lockable.Mutex.Unlock();
|
||||
}
|
||||
|
||||
// Operator right arrow accesses the EngineWrapper.
|
||||
// Operator-> accesses the EngineWrapper.
|
||||
//
|
||||
EngineWrapper* operator ->() {
|
||||
return &Lockable.Wrapper;
|
||||
}
|
||||
|
||||
// Get a pointer to the enginewrapper. This is not
|
||||
// very safe because you could keep the pointer after
|
||||
// the LockedWrapper is destroyed. Don't do that.
|
||||
// Get a pointer to the EngineWrapper. Not very
|
||||
// safe because you could keep the pointer after
|
||||
// the LockedWrapper is destroyed. Don't do that.
|
||||
//
|
||||
EngineWrapper* Get() {
|
||||
return &Lockable.Wrapper;
|
||||
}
|
||||
|
||||
// Initialize the engine wrapper if it's not already.
|
||||
//
|
||||
// All this does is open the DLL and hook up all
|
||||
// the function pointers in the wrapper to point into
|
||||
// the DLL.
|
||||
// Initialize the engine wrapper if it's not
|
||||
// already. All this does is open the DLL and
|
||||
// hook up all the function pointers in the
|
||||
// wrapper to point into the DLL.
|
||||
//
|
||||
void InitWrapper();
|
||||
|
||||
@@ -85,9 +107,9 @@ public:
|
||||
|
||||
// Get the list of tangibles near the actor.
|
||||
//
|
||||
// This function is fast but not free. You should fetch this
|
||||
// once per frame and then store the IdView somewhere (like
|
||||
// in the TangibleManager, for example).
|
||||
// This function is fast but not free. You should
|
||||
// fetch this once per frame and then store the
|
||||
// IdView somewhere (like in the TangibleManager).
|
||||
//
|
||||
IdView GetNear(int64 id, double rx, double ry, double rz);
|
||||
|
||||
@@ -97,4 +119,28 @@ public:
|
||||
// next time you call this.
|
||||
//
|
||||
StringViewVec GetAnimationQueues(IdView ids);
|
||||
|
||||
// Call a Lua function. The datapk contains the
|
||||
// serialized class name, function name, and
|
||||
// arguments. If place_id is 0, defaults to the
|
||||
// current actor. The OnResult callback receives
|
||||
// the raw return data while the lock is held;
|
||||
// use it to copy the data before it goes away.
|
||||
//
|
||||
void ProbeLuaFunction(std::string_view datapk, int64 place_id, TFunction<void(std::string_view)> OnResult);
|
||||
|
||||
// Invoke a Lua function (fire-and-forget, no
|
||||
// return values).
|
||||
//
|
||||
void InvokeLuaFunction(std::string_view datapk, int64 place_id);
|
||||
|
||||
// Validate a Lua expression. Returns a syntax
|
||||
// classification and an error message. The error
|
||||
// message is empty if the code is valid.
|
||||
//
|
||||
ElxLuaSyntaxCheck ValidateLuaExpr(const FString &Code, FString &ErrorMessage);
|
||||
|
||||
// Execute a Lua expression.
|
||||
//
|
||||
void InvokeLuaExpr(const FString &Code);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include "LuaCall.h"
|
||||
#include "LuprexGameModeBase.h"
|
||||
#include "Tangible.h"
|
||||
#include "StringDecoder.h"
|
||||
|
||||
#include "EdGraphSchema_K2.h"
|
||||
@@ -196,51 +197,30 @@ FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix)
|
||||
return Result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// General Lua-Callable functions of the Lua Call Library.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
void UlxLuaCallLibrary::ValidateLuaExpr(
|
||||
ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
ErrorMessage = mode->ValidateLuaExpr(Code);
|
||||
if (ErrorMessage.IsEmpty())
|
||||
{
|
||||
Status = ElxLuaSyntaxCheck::ValidLua;
|
||||
}
|
||||
else if (ErrorMessage == TEXT("slash command"))
|
||||
{
|
||||
Status = ElxLuaSyntaxCheck::SlashCommand;
|
||||
}
|
||||
else if (ErrorMessage == TEXT("white space"))
|
||||
{
|
||||
Status = ElxLuaSyntaxCheck::Whitespace;
|
||||
}
|
||||
else if (ErrorMessage == TEXT("truncated lua"))
|
||||
{
|
||||
Status = ElxLuaSyntaxCheck::TruncatedLua;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = ElxLuaSyntaxCheck::InvalidLua;
|
||||
}
|
||||
}
|
||||
|
||||
void UlxLuaCallLibrary::InvokeLuaExpr(UObject *context, const FString &Code)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
mode->InvokeLuaExpr(Code);
|
||||
FlxLockedWrapper w(mode->GetLockableWrapper());
|
||||
w.InvokeLuaExpr(Code);
|
||||
}
|
||||
|
||||
// Resolve an AActor to a tangible place_id.
|
||||
// Returns 0 if place is null (meaning "use current
|
||||
// actor"), or the tangible ID if found. Returns -1
|
||||
// if place is non-null but has no tangible.
|
||||
//
|
||||
static int64 ResolvePlaceId(AActor *place)
|
||||
{
|
||||
if (place == nullptr) return 0;
|
||||
UlxTangible *tan = UlxTangible::GetActorTangibleOrLog(place);
|
||||
return tan ? tan->TangibleId : -1;
|
||||
}
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
mode->LuaCallBegin();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
sb.clear();
|
||||
sb.write_string(cname);
|
||||
sb.write_string(fname);
|
||||
}
|
||||
@@ -248,19 +228,33 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con
|
||||
void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
mode->LuaCallEnd(AccessKind::INVOKE_LUA_CALL, place);
|
||||
int64 place_id = ResolvePlaceId(place);
|
||||
if (place_id < 0) { sb.clear(); return; }
|
||||
FlxLockedWrapper w(mode->GetLockableWrapper());
|
||||
w.InvokeLuaFunction(sb.view(), place_id);
|
||||
sb.clear();
|
||||
}
|
||||
|
||||
|
||||
bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValues *&ReturnArray)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return false;
|
||||
ReturnArray = mode->LuaCallEnd(AccessKind::PROBE_LUA_CALL, place);
|
||||
if ((ReturnArray == nullptr) || (ReturnArray->Length() < 1))
|
||||
int64 place_id = ResolvePlaceId(place);
|
||||
if (place_id < 0) {
|
||||
sb.clear();
|
||||
ReturnArray = NewObject<UlxLuaValues>(mode);
|
||||
return false;
|
||||
}
|
||||
ReturnArray = NewObject<UlxLuaValues>(mode);
|
||||
FlxLockedWrapper w(mode->GetLockableWrapper());
|
||||
w.ProbeLuaFunction(sb.view(), place_id, [&](std::string_view retpk) {
|
||||
ReturnArray->Initialize(retpk);
|
||||
});
|
||||
sb.clear();
|
||||
if (ReturnArray->Length() < 1)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Error, TEXT("corruption in lua_probe"));
|
||||
ReturnArray = nullptr;
|
||||
@@ -292,7 +286,7 @@ bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValu
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::STRING);
|
||||
sb.write_string(pstring);
|
||||
@@ -307,7 +301,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam
|
||||
UE_LOG(LogBlueprint, Error, TEXT("Names passed to lua must be short, and must contain only lowercase and digits"));
|
||||
}
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::TOKEN);
|
||||
sb.write_string(namestr);
|
||||
@@ -315,7 +309,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::NUMBER);
|
||||
sb.write_double(pfloat);
|
||||
@@ -323,7 +317,7 @@ void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) {
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::NUMBER);
|
||||
sb.write_double(value);
|
||||
@@ -331,7 +325,7 @@ void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) {
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::VECTOR);
|
||||
sb.write_fvector(pvector);
|
||||
@@ -339,7 +333,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::VECTOR);
|
||||
sb.write_double(pvector.X);
|
||||
@@ -349,7 +343,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector
|
||||
|
||||
void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) {
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
|
||||
FlxStreamBuffer &sb = mode->GetLuaCallBuffer();
|
||||
if (NotInitialized(sb)) return;
|
||||
sb.write_simple_dynamic_tag(LuaValueType::BOOLEAN);
|
||||
sb.write_bool(pbool);
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LuaCall.h
|
||||
//
|
||||
// Low-level functions for calling Lua from blueprints.
|
||||
// The "Call Lua" K2Node macroexpands into sequences
|
||||
// of these functions.
|
||||
//
|
||||
// Also contains UlxLuaValues, which stores return
|
||||
// values from Lua calls, and FlxParsedProto, which
|
||||
// parses Lua function prototype strings.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
@@ -9,46 +23,20 @@
|
||||
|
||||
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
|
||||
// FlxParsedProto
|
||||
//
|
||||
UENUM(BlueprintType)
|
||||
enum class ElxLuaSyntaxCheck : uint8 {
|
||||
SlashCommand,
|
||||
Whitespace,
|
||||
ValidLua,
|
||||
TruncatedLua,
|
||||
InvalidLua,
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// The first argument to LuaCallNode is a function
|
||||
// prototype of the form:
|
||||
//
|
||||
// These are the types that can actually be packed into
|
||||
// a serialized buffer.
|
||||
// class.name(int arg1, int arg2) : int ret1
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Return values can be omitted. The last return
|
||||
// value can be an ellipsis. The class name can be
|
||||
// asterisk.
|
||||
//
|
||||
// This is a little parser that parses Lua function 'prototypes'.
|
||||
// The prototypes look like this:
|
||||
//
|
||||
// class.name(int arg1, int arg2) : int ret1, int ret2
|
||||
//
|
||||
// The return values can be omitted if the function has no return
|
||||
// values. Optionally, there can be one last return value which
|
||||
// is just an ellipsis. The class name can be asterisk.
|
||||
//
|
||||
// For more information about the meaning of all this, see the docs
|
||||
// for the 'Call Lua' blueprint node.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
class FlxParsedProto
|
||||
{
|
||||
@@ -60,21 +48,33 @@ public:
|
||||
Pin(const FString &T, const FString &N) : Type(T), Name(N) {}
|
||||
};
|
||||
|
||||
// If parsing generated an error, the error
|
||||
// message is stored here.
|
||||
//
|
||||
FString ErrorMessage;
|
||||
TArray<FString> Tokens;
|
||||
int NextToken;
|
||||
|
||||
// The results of parsing the prototype.
|
||||
//
|
||||
FString ClassName;
|
||||
FString FunctionName;
|
||||
TArray<Pin> Arguments;
|
||||
TArray<Pin> ReturnValues;
|
||||
bool ExtraReturnValues;
|
||||
|
||||
// Used internally to help generate the error
|
||||
// message.
|
||||
//
|
||||
TArray<FString> Tokens;
|
||||
int NextToken;
|
||||
|
||||
private:
|
||||
// Check the next token to see if it's exactly equal to text.
|
||||
// Check the next token to see if it's exactly
|
||||
// equal to text.
|
||||
//
|
||||
bool IsLiteral(const TCHAR *text);
|
||||
|
||||
// Check the next token to see if it's an identifier.
|
||||
// Check the next token to see if it's an
|
||||
// identifier.
|
||||
//
|
||||
bool IsIdent();
|
||||
|
||||
@@ -96,26 +96,24 @@ public:
|
||||
FlxParsedProto(const FString &str) { Parse(str); }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// To make a Lua Call from inside of a blueprint, use the convenient "Call Lua"
|
||||
// blueprint node. This node macroexpands into a sequence of low-level
|
||||
// function calls. This library contains the low-level functions that
|
||||
// "Call Lua" macroexpands into.
|
||||
// UlxLuaCallLibrary
|
||||
//
|
||||
// The procedure for making a lua call using the low-level functions is as follows:
|
||||
// Low-level functions that LuaCallNode macroexpands
|
||||
// into. Every LuaCallNode expands into the following
|
||||
// sequence of functions:
|
||||
//
|
||||
// * Use LuaCallBegin to put the class name and function name into the argument buffer.
|
||||
// * Use LuaCallArgumentXXX to put arguments into the argument buffer.
|
||||
// * Use LuaCallInvoke or LuaCallProbe to actually make the call.
|
||||
// * Use LuaCallReturnValueXXX to fetch return values from the return buffer.
|
||||
// * LuaCallBegin to set class/function name.
|
||||
// * LuaCallArgument_XXX to pack arguments.
|
||||
// * LuaCallInvoke or LuaCallProbe to call.
|
||||
// * LuaCallReturnValue_XXX to fetch results.
|
||||
//
|
||||
// The two buffers are basically global variables, they are part of the
|
||||
// LuprexGameModeBase. This is okay because blueprint is single-threaded.
|
||||
// The buffers used by these functions are global
|
||||
// variables in LuprexGameModeBase. It is ok to use
|
||||
// globals, because blueprint is single-threaded.
|
||||
//
|
||||
// The following three libraries contain all the low-level functions.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS()
|
||||
class INTEGRATION_API UlxLuaCallLibrary : public UObject
|
||||
@@ -123,40 +121,43 @@ class INTEGRATION_API UlxLuaCallLibrary : public UObject
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Get an argument packing function or a return value unpacking function.
|
||||
// Get an argument packing function or a return
|
||||
// value unpacking function.
|
||||
//
|
||||
static UFunction *GetArgumentPacker(const FString &Type);
|
||||
static UFunction *GetReturnValueUnpacker(const FString &Type);
|
||||
|
||||
// Get the types supported for arguments and return values, as a documentation string.
|
||||
// Get the types supported for arguments and
|
||||
// return values, as a documentation string.
|
||||
//
|
||||
static FString AllFunctionsWithPrefix(const TCHAR *Prefix);
|
||||
static FString AllKnownArgumentTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallArgument_")); }
|
||||
static FString AllKnownReturnValueTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallReturnValue_")); }
|
||||
|
||||
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.
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function")
|
||||
static void ValidateLuaExpr(ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code);
|
||||
|
||||
// Functions that initiate and complete the lua call.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function")
|
||||
static void InvokeLuaExpr(UObject *context, const FString &Code);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function")
|
||||
static void LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function")
|
||||
static void LuaCallInvoke(UObject *context, AActor *Place);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", ExpandBoolAsExecs="ReturnValue", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function")
|
||||
static bool LuaCallProbe(UObject *context, AActor *Place, UlxLuaValues *&ReturnArray);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// Functions that pack arguments into the call buffer.
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function")
|
||||
static void LuaCallArgument_string(UObject *context, const FString &Value);
|
||||
@@ -180,14 +181,15 @@ public:
|
||||
static void LuaCallArgument_boolean(UObject *context, bool Value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This class stores an array of values that were returned by Lua.
|
||||
// UlxLuaValues
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Stores an array of values returned by Lua.
|
||||
// Provides a cursor-based API for reading values
|
||||
// one at a time with type checking.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class INTEGRATION_API UlxLuaValues : public UObject
|
||||
@@ -203,7 +205,8 @@ private:
|
||||
//
|
||||
TArray<ElxLuaValueType> Types;
|
||||
|
||||
// For each chunk, a pointer to the serialized data.
|
||||
// For each chunk, a pointer to the serialized
|
||||
// data.
|
||||
//
|
||||
TArray<std::string_view> Data;
|
||||
|
||||
@@ -216,16 +219,18 @@ private:
|
||||
//
|
||||
void Empty();
|
||||
|
||||
// Compare two types. If they aren't equal, possibly log an error, and return a status code.
|
||||
// Compare two types. If they aren't equal,
|
||||
// possibly log an error, and return a status
|
||||
// code.
|
||||
//
|
||||
static ElxSuccessOrWrongType CheckType(bool LogErrorOnWrongType, ElxLuaValueType Type, ElxLuaValueType Desired);
|
||||
|
||||
public:
|
||||
UlxLuaValues() { Cursor = 0; }
|
||||
|
||||
// Copies the data, and then preprocesses it to make sure
|
||||
// it's all valid. If it's not valid, returns false.
|
||||
//
|
||||
// Copies the data, then preprocesses it to make
|
||||
// sure it's all valid. Returns false if invalid.
|
||||
//
|
||||
bool Initialize(std::string_view data);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array")
|
||||
@@ -245,19 +250,19 @@ public:
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
|
||||
ElxLuaValueType NextType() const;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array")
|
||||
bool IsNextType(ElxLuaValueType Type) const { return NextType() == Type; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "ReturnValue"), Category = "Luprex|Lua Value Array")
|
||||
ElxLuaValueType SwitchNextType() { return NextType(); }
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
|
||||
void ReadString(ElxSuccessOrWrongType &Status, FString &Result, bool LogErrorOnWrongType = false);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
|
||||
void ReadName(ElxSuccessOrWrongType &Status, FName &Result, bool LogErrorOnWrongType = false);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
|
||||
void ReadFloat(ElxSuccessOrWrongType &Status, double &Result, bool LogErrorOnWrongType = false);
|
||||
|
||||
@@ -272,4 +277,4 @@ public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
|
||||
void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnWrongType = false);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "LuaCallNode.h"
|
||||
#include "StringDecoder.h"
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "BlueprintActionDatabaseRegistrar.h"
|
||||
#include "BlueprintNodeSpawner.h"
|
||||
#include "Containers/EnumAsByte.h"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BlueprintErrors.h"
|
||||
#include "Containers/Array.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
@@ -13,7 +11,6 @@
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "BlueprintErrors.h"
|
||||
|
||||
#include "LuaCallNode.generated.h"
|
||||
|
||||
|
||||
@@ -187,58 +187,6 @@ void ALuprexGameModeBase::UpdatePossessedTangible() {
|
||||
}
|
||||
}
|
||||
|
||||
UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(AccessKind kind, int64 place_id) {
|
||||
std::string_view datapk = LuaCallBuffer.view();
|
||||
FlxLockedWrapper w(LockableWrapper);
|
||||
if (place_id == 0) place_id = w.GetActor();
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
w->play_access(w.Get(), kind, place_id, datapk.size(), datapk.data(), &retpklen, &retpk);
|
||||
if (kind == AccessKind::PROBE_LUA_CALL)
|
||||
{
|
||||
UlxLuaValues *Result = NewObject<UlxLuaValues>(this);
|
||||
Result->Initialize(std::string_view(retpk, retpklen));
|
||||
return Result;
|
||||
}
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(AccessKind kind) {
|
||||
return LuaCallEnd(kind, int64(0));
|
||||
}
|
||||
|
||||
UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(AccessKind kind, AActor *place) {
|
||||
if (place == nullptr) {
|
||||
return LuaCallEnd(kind, int64(0));
|
||||
} else {
|
||||
UlxTangible *tan = UlxTangible::GetActorTangibleOrLog(place);
|
||||
if (tan == nullptr) {
|
||||
return NewObject<UlxLuaValues>(this);
|
||||
} else {
|
||||
return LuaCallEnd(kind, tan->TangibleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FString ALuprexGameModeBase::ValidateLuaExpr(const FString &Code)
|
||||
{
|
||||
FTCHARToUTF8 UCode(*Code);
|
||||
FlxLockedWrapper w(LockableWrapper);
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
w->play_access(w.Get(), AccessKind::VALIDATE_LUA_EXPR, 0, UCode.Length(), UCode.Get(), &retpklen, &retpk);
|
||||
FString Result(retpklen, (const UTF8CHAR*)retpk);
|
||||
return Result;
|
||||
}
|
||||
|
||||
void ALuprexGameModeBase::InvokeLuaExpr(const FString &Code)
|
||||
{
|
||||
FTCHARToUTF8 UCode(*Code);
|
||||
FlxLockedWrapper w(LockableWrapper);
|
||||
uint32_t retpklen;
|
||||
const char *retpk;
|
||||
w->play_access(w.Get(), AccessKind::INVOKE_LUA_EXPR, 0, UCode.Length(), UCode.Get(), &retpklen, &retpk);
|
||||
}
|
||||
|
||||
void ALuprexGameModeBase::OnWorldPreActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds)
|
||||
{
|
||||
@@ -336,7 +284,7 @@ void ALuprexGameModeBase::InitializeGlobalState()
|
||||
|
||||
// If somebody generates a log message that's severe enough, break to debugger.
|
||||
BreakToDebuggerLogVerbosityDevice.Reset(
|
||||
new FlxDebugBlueprintErrorsOutputDevice(BreakToDebuggerLogVerbosity));
|
||||
new FlxBreakToDebuggerOutputDevice(BreakToDebuggerLogVerbosity));
|
||||
}
|
||||
|
||||
void ALuprexGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "AssetLookup.h"
|
||||
#include "LuprexSockets.h"
|
||||
#include "TriggeredTask.h"
|
||||
#include "BlueprintErrors.h"
|
||||
#include "BreakToDebugger.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
#include "CommonActivatableWidget.h"
|
||||
@@ -82,38 +82,12 @@ public:
|
||||
void LookAtChanged();
|
||||
|
||||
|
||||
// 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".
|
||||
// The Lua Call Assembly Buffer. Used by
|
||||
// UlxLuaCallLibrary to build up a call across
|
||||
// multiple UFUNCTION invocations.
|
||||
//
|
||||
// At this level, the process of calling Lua is:
|
||||
//
|
||||
// * Use LuaCallBegin
|
||||
// * Get the lua call buffer:
|
||||
// - add a class name
|
||||
// - add a function name
|
||||
// - add function parameters
|
||||
// * Use LuaCallEnd.
|
||||
// * Process any return values in the UlxLuaValues array.
|
||||
//
|
||||
FlxStreamBuffer &LuaCallBegin() { LuaCallBuffer.clear(); return LuaCallBuffer; }
|
||||
FlxStreamBuffer &LuaCallGetBuffer() { return LuaCallBuffer; }
|
||||
UlxLuaValues *LuaCallEnd(AccessKind kind);
|
||||
UlxLuaValues *LuaCallEnd(AccessKind kind, int64 place_id);
|
||||
UlxLuaValues *LuaCallEnd(AccessKind kind, AActor *place);
|
||||
void LuaCallClear() { LuaCallBuffer.clear(); }
|
||||
FlxStreamBuffer &GetLuaCallBuffer() { return LuaCallBuffer; }
|
||||
|
||||
// Validate some lua code. Returns an error message.
|
||||
// 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 ValidateLuaExpr(const FString &Code);
|
||||
|
||||
// Invoke some lua code.
|
||||
//
|
||||
void InvokeLuaExpr(const FString &Code);
|
||||
|
||||
// Get the Asset Lookup table.
|
||||
const UlxAssetLookup *GetAssetLookup() const { return AssetLookup; }
|
||||
@@ -160,12 +134,17 @@ public:
|
||||
|
||||
// The sensitivity level at which a log message triggers a debugger breakpoint.
|
||||
UPROPERTY(EditAnywhere, Category="Debugging Tools")
|
||||
ElxLogVerbosity BreakToDebuggerLogVerbosity;
|
||||
ElxBreakToDebuggerThreshold BreakToDebuggerLogVerbosity;
|
||||
|
||||
// The Luprex EngineWrapper, with a Mutex to protect it.
|
||||
// To access it, construct a FlxLockedWrapper.
|
||||
//
|
||||
FlxLockableWrapper LockableWrapper;
|
||||
|
||||
// Get the LockableWrapper.
|
||||
//
|
||||
FlxLockableWrapper& GetLockableWrapper() { return LockableWrapper; }
|
||||
|
||||
// The Lua Call Assembly Buffer.
|
||||
FlxStreamBuffer LuaCallBuffer;
|
||||
|
||||
@@ -195,5 +174,5 @@ public:
|
||||
FDelegateHandle OnWorldPostActorTickHandle;
|
||||
|
||||
// The device that implements BreakToDebuggerLogVerbosity, above.
|
||||
TUniquePtr<FlxDebugBlueprintErrorsOutputDevice> BreakToDebuggerLogVerbosityDevice;
|
||||
TUniquePtr<FlxBreakToDebuggerOutputDevice> BreakToDebuggerLogVerbosityDevice;
|
||||
};
|
||||
|
||||
64
Source/Integration/MovementComponentState.cpp
Normal file
64
Source/Integration/MovementComponentState.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "MovementComponentState.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Tangible.h"
|
||||
|
||||
FlxMovementComponentState::FlxMovementComponentState(UCharacterMovementComponent *CMC)
|
||||
{
|
||||
Velocity = CMC->Velocity;
|
||||
bIsAccelerating = !CMC->GetCurrentAcceleration().IsNearlyZero();
|
||||
MaxWalkSpeed = CMC->MaxWalkSpeed;
|
||||
MovementMode = CMC->MovementMode;
|
||||
bIsFalling = CMC->IsFalling();
|
||||
bIsCrouching = CMC->IsCrouching();
|
||||
}
|
||||
|
||||
FString UlxMovementComponentStateLibrary::DebugString(const FlxMovementComponentState &State)
|
||||
{
|
||||
const UEnum *ModeEnum = StaticEnum<EMovementMode>();
|
||||
FString ModeName = ModeEnum ? ModeEnum->GetNameStringByValue(State.MovementMode.GetValue()) : FString::FromInt(State.MovementMode.GetValue());
|
||||
ModeName.RemoveFromStart(TEXT("MOVE_"));
|
||||
return FString::Printf(TEXT("Vel=(%.1f, %.1f, %.1f) MaxWalk=%.1f Mode=%s Accel=%s Fall=%s Crouch=%s"),
|
||||
State.Velocity.X, State.Velocity.Y, State.Velocity.Z,
|
||||
State.MaxWalkSpeed,
|
||||
*ModeName,
|
||||
State.bIsAccelerating ? TEXT("true") : TEXT("false"),
|
||||
State.bIsFalling ? TEXT("true") : TEXT("false"),
|
||||
State.bIsCrouching ? TEXT("true") : TEXT("false"));
|
||||
}
|
||||
|
||||
bool UlxMovementComponentStateLibrary::GetShouldMove(const FlxMovementComponentState &State)
|
||||
{
|
||||
return State.bIsAccelerating && State.Velocity.Size2D() >= 3.0;
|
||||
}
|
||||
|
||||
FlxMovementComponentState UlxMovementComponentStateLibrary::GetMovementComponentState(UAnimInstance *AnimInstance)
|
||||
{
|
||||
if (!AnimInstance) return FlxMovementComponentState();
|
||||
|
||||
AActor *Actor = AnimInstance->GetOwningActor();
|
||||
if (!Actor) return FlxMovementComponentState();
|
||||
|
||||
UCharacterMovementComponent *CMC = Actor->FindComponentByClass<UCharacterMovementComponent>();
|
||||
if (CMC && CMC->MovementMode != MOVE_None) return FlxMovementComponentState(CMC);
|
||||
|
||||
UlxTangible *Tangible = UlxTangible::GetActorTangibleQuiet(Actor);
|
||||
if (Tangible) return Tangible->FakeMovementComponentState;
|
||||
|
||||
if (CMC) return FlxMovementComponentState(CMC);
|
||||
return FlxMovementComponentState();
|
||||
}
|
||||
|
||||
FlxMovementComponentState UlxMovementComponentStateLibrary::SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State)
|
||||
{
|
||||
if (!Actor) return State;
|
||||
UlxTangible *Tangible = UlxTangible::GetActorTangibleOrLog(Actor);
|
||||
if (Tangible)
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("SetFakeMovementComponentState(%s): %s"), *Actor->GetName(), *DebugString(State));
|
||||
Tangible->FakeMovementComponentState = State;
|
||||
}
|
||||
return State;
|
||||
}
|
||||
129
Source/Integration/MovementComponentState.h
Normal file
129
Source/Integration/MovementComponentState.h
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "MovementComponentState.generated.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Very often, the animation blueprint of a character will
|
||||
// want to know the state of the character movement
|
||||
// component, including such things as Velocity,
|
||||
// Acceleration, IsFalling, etc. However, the movement
|
||||
// component cannot be accessed directly from the
|
||||
// animation graph, since it runs on a worker thread.
|
||||
// By copying the data into this plain struct, the
|
||||
// animation graph can safely read it.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INTEGRATION_API FlxMovementComponentState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
FVector Velocity = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
bool bIsAccelerating = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
float MaxWalkSpeed = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
TEnumAsByte<EMovementMode> MovementMode = MOVE_None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
bool bIsFalling = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
|
||||
bool bIsCrouching = false;
|
||||
|
||||
FlxMovementComponentState() = default;
|
||||
explicit FlxMovementComponentState(class UCharacterMovementComponent *CMC);
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class INTEGRATION_API UlxMovementComponentStateLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// True field getters.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static FVector GetVelocity(const FlxMovementComponentState &State) { return State.Velocity; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static bool GetIsAccelerating(const FlxMovementComponentState &State) { return State.bIsAccelerating; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static float GetMaxWalkSpeed(const FlxMovementComponentState &State) { return State.MaxWalkSpeed; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static EMovementMode GetMovementMode(const FlxMovementComponentState &State) { return State.MovementMode; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static bool GetIsFalling(const FlxMovementComponentState &State) { return State.bIsFalling; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static bool GetIsCrouching(const FlxMovementComponentState &State) { return State.bIsCrouching; }
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// These are not true getters, but actually calculate
|
||||
// simple values from the existing fields.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static float GetGroundSpeed(const FlxMovementComponentState &State) { return State.Velocity.Size2D(); }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
|
||||
static bool GetShouldMove(const FlxMovementComponentState &State);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Debugging.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State")
|
||||
static FString DebugString(const FlxMovementComponentState &State);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Other operations.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// Get a snapshot the Movement Component State.
|
||||
//
|
||||
// Normally, this just pulls data directly from the
|
||||
// character movement component. However, we sometimes
|
||||
// need to disable the movement component, especially
|
||||
// during cutscenes. We provide an alternative 'Fake
|
||||
// Movement Component State' stored on the actor's
|
||||
// UlxTangible. When the movement component's mode is
|
||||
// MOVE_None, this function returns the fake movement
|
||||
// component state instead. The fake movement component
|
||||
// state can be updated manually in blueprints.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "AnimInstance"))
|
||||
static FlxMovementComponentState GetMovementComponentState(UAnimInstance *AnimInstance);
|
||||
|
||||
// Update the fake movement component state stored on
|
||||
// the actor's tangible. The fake movement component
|
||||
// state is usually read by the animation blueprint
|
||||
// when the real movement component is disabled.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "Actor", AutoCreateRefTerm = "State"))
|
||||
static FlxMovementComponentState SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State);
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
// 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);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// 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;
|
||||
|
||||
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SampleEmptyClass.h"
|
||||
|
||||
SampleEmptyClass::SampleEmptyClass()
|
||||
{
|
||||
}
|
||||
|
||||
SampleEmptyClass::~SampleEmptyClass()
|
||||
{
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class INTEGRATION_API SampleEmptyClass
|
||||
{
|
||||
public:
|
||||
SampleEmptyClass();
|
||||
~SampleEmptyClass();
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SampleUObject.h"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "SampleUObject.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class INTEGRATION_API USampleUObject : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
@@ -234,7 +234,7 @@ class INTEGRATION_API UlxScriptedAnimationLibrary : public UBlueprintFunctionLib
|
||||
public:
|
||||
// Get all the major 'World Clocks' in a single struct.
|
||||
//
|
||||
UFUNCTION(BlueprintPure, Category = "Utilities|Time", meta=(WorldContext = "WorldContextObject"))
|
||||
UFUNCTION(BlueprintCallable, Category = "Utilities|Time", meta=(WorldContext = "WorldContextObject"))
|
||||
static FlxWorldClocks GetAllWorldClocks(const UObject *WorldContextObject);
|
||||
|
||||
// Get the data to drive Sequence Evaluators and Multi Blend
|
||||
|
||||
@@ -253,7 +253,7 @@ void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step
|
||||
tan->AnimTracker.FinishedAnimation(step.Hash);
|
||||
if (AutoUpdate) tan->AutoUpdatePosition();
|
||||
FString DebugString = UlxAnimationStepLibrary::AnimationStepDebugString(step);
|
||||
UE_LOG(LogLuprex, Display, TEXT("Animation Finished: %s"), *DebugString);
|
||||
// UE_LOG(LogLuprex, Display, TEXT("FinishedAnimation: %s"), *DebugString);
|
||||
}
|
||||
|
||||
bool UlxTangible::AnimationStepIsFinished(AActor *target, const FlxAnimationStep &step)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "AnimQueue.h"
|
||||
#include "MovementComponentState.h"
|
||||
#include "ScriptedAnimation.h"
|
||||
#include "Tangible.generated.h"
|
||||
|
||||
@@ -55,10 +56,14 @@ public:
|
||||
UPROPERTY()
|
||||
FString ActorBlueprintName;
|
||||
|
||||
// This is
|
||||
// Every tangible can store a set of scripted animations.
|
||||
UPROPERTY()
|
||||
UlxScriptedAnimations *ScriptedAnimations = nullptr;
|
||||
|
||||
// Every tangible can store a fake movement component state.
|
||||
UPROPERTY()
|
||||
FlxMovementComponentState FakeMovementComponentState;
|
||||
|
||||
// Animation tracker
|
||||
FlxAnimTracker AnimTracker;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
#include "UtilityLibrary.h"
|
||||
#include "LuprexGameModeBase.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
@@ -239,3 +240,23 @@ FKey UlxUtilityLibrary::GetKeyByNameString(const FString &Name)
|
||||
FKey Key = FKey(FName(*Name));
|
||||
return Key.IsValid() ? Key : FKey();
|
||||
}
|
||||
|
||||
FVector UlxUtilityLibrary::GetActorForwardVelocity(const AActor *Actor, double Speed, bool bSnapToXY)
|
||||
{
|
||||
if (!Actor) return FVector::ZeroVector;
|
||||
FVector Forward = Actor->GetActorForwardVector();
|
||||
if (bSnapToXY)
|
||||
{
|
||||
Forward.Z = 0.0;
|
||||
if (!Forward.Normalize()) return FVector::ZeroVector;
|
||||
}
|
||||
return Forward * Speed;
|
||||
}
|
||||
|
||||
void UlxUtilityLibrary::ValidateLuaExpr(
|
||||
ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code)
|
||||
{
|
||||
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
|
||||
FlxLockedWrapper w(mode->GetLockableWrapper());
|
||||
Status = w.ValidateLuaExpr(Code, ErrorMessage);
|
||||
}
|
||||
|
||||
@@ -158,4 +158,18 @@ public:
|
||||
//
|
||||
UFUNCTION(BlueprintPure, Category = "Input|Key")
|
||||
static FKey GetKeyByNameString(const FString &Name);
|
||||
|
||||
// Get the actor's forward vector multiplied by a speed.
|
||||
// If SnapToXY is true, the forward vector is projected
|
||||
// onto the XY plane and renormalized before scaling.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Utility", meta = (DefaultToSelf = "Actor"))
|
||||
static FVector GetActorForwardVelocity(const AActor *Actor, double Speed = 1.0, bool bSnapToXY = false);
|
||||
|
||||
// Syntactically validate lua code. Parses the
|
||||
// code and returns an error message. If the code
|
||||
// is error-free, the error message is empty.
|
||||
//
|
||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Utility")
|
||||
static void ValidateLuaExpr(ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user