diff --git a/.claude/skills/ue-api/SKILL.md b/.claude/skills/ue-api/SKILL.md
deleted file mode 100644
index fcf21b1..0000000
--- a/.claude/skills/ue-api/SKILL.md
+++ /dev/null
@@ -1,109 +0,0 @@
----
-name: ue-api
-description: >
- Use this skill when the user asks anything about Unreal Engine C++ APIs,
- class hierarchies, system flows, or how engine subsystems work together.
- Trigger phrases include: "how does X work in UE", "what is the flow for",
- "which class handles", "what virtual functions", "UE API for", "Unreal Engine
- architecture", and any question that names UE types like ACharacter,
- UActorComponent, APlayerController, UBehaviorTreeComponent, APawn, etc.
- Always use this skill for Unreal Engine questions — don't rely on training
- data alone when local documentation is available.
----
-
-# UE API Skill
-
-Answer questions about Unreal Engine C++ APIs and system flows using the local
-documentation corpus as the fast primary source, then source headers for depth.
-
-## Configuration
-
-Two environment variables control where to look:
-
-| Variable | Purpose | Example |
-|---|---|---|
-| `UE_DOCS_PATH` | Root of the generated documentation | `/home/user/ue-docs` |
-| `UE_ENGINE_ROOT` | UE engine source root (for header fallback) | `/home/user/UnrealEngine` |
-
-**Resolve them at the start of every query:**
-
-```bash
-echo "$UE_DOCS_PATH"
-echo "$UE_ENGINE_ROOT"
-```
-
-If `UE_DOCS_PATH` is unset, ask the user where their generated docs are before
-proceeding. If `UE_ENGINE_ROOT` is unset, only ask when a question actually
-requires source headers — don't interrupt doc-only queries.
-
-The type index is always at `$UE_DOCS_PATH/type-index.txt`.
-
-## Step 1 — Identify types, resolve paths
-
-Extract UE type names from the question (e.g. `APlayerController`, `APawn`,
-`UBehaviorTreeComponent`). Resolve all of them in a single grep:
-
-```bash
-grep "^APlayerController:\|^APawn:\|^ACharacter:" "$UE_DOCS_PATH/type-index.txt"
-```
-
-Paths in the index are relative to `$UE_DOCS_PATH` — prepend it when reading:
-
-```bash
-# index returns: AController: Engine/Classes/GameFramework/Controller.md
-# read as:
-Read "$UE_DOCS_PATH/Engine/Classes/GameFramework/Controller.md"
-```
-
-The `.md` files are compact by design — only items with C++ doc comments,
-no deprecated entries, enums collapsed when undescribed.
-
-## Step 2 — Follow the trail
-
-Inline links in `*Inherits*:` lines and function signatures point to related
-types. Follow them when the question spans multiple classes. A second grep on
-`type-index.txt` is always cheaper than guessing paths.
-
-## Step 3 — Escalate to source headers when docs aren't enough
-
-The docs only surface items with C++ doc comments. Go to `.h` files when:
-
-- The exact call order or implementation logic isn't described in any comment
-- A function or member is absent from `.md` files (no doc comment)
-- The question involves macros: `UCLASS`, `UPROPERTY`, `UFUNCTION`,
- `DECLARE_DELEGATE_*`, `GENERATED_BODY`, etc.
-- Private or protected members are relevant to the answer
-- The user asks about edge-case behaviour ("what happens when X is null?")
-
-Search under `$UE_ENGINE_ROOT/Engine/Source/` — e.g.:
-
-```bash
-Glob("**/*Controller*.h", path="$UE_ENGINE_ROOT/Engine/Source/Runtime/Engine")
-Grep("void Possess", path="$UE_ENGINE_ROOT/Engine/Source")
-```
-
-## Output format
-
-Lead with the direct answer or a concise flow description. For multi-step
-flows use an ASCII sequence or numbered list. For single-class API questions,
-a brief prose answer with the key function signatures is enough.
-
-Cite every substantive claim — `(Controller.md)` for docs, `(Controller.h:142)`
-for source. Mark source-derived facts as *implementation detail* since they can
-change across engine versions; doc-derived facts reflect the stable API contract.
-
-## Examples
-
-**"How does APlayerController possess APawn?"**
-→ check `$UE_DOCS_PATH` → grep type-index for AController, APawn,
-APlayerController, ACharacter → read the four `.md` files → ASCII diagram
-
-**"What virtual functions does ACharacter expose for movement?"**
-→ grep for ACharacter, UCharacterMovementComponent → read docs → list virtuals
-
-**"What does UFUNCTION(BlueprintCallable) expand to?"**
-→ docs won't help (macro) → search `$UE_ENGINE_ROOT` for `UFUNCTION` definition
-
-**"How does the behavior tree pick which task to run?"**
-→ grep for UBehaviorTreeComponent, UBTCompositeNode, UBTDecorator → read docs
-→ if execution order is still unclear, escalate to `BehaviorTreeComponent.h`
diff --git a/.mcp.json b/.mcp.json
new file mode 100644
index 0000000..89193fc
--- /dev/null
+++ b/.mcp.json
@@ -0,0 +1,8 @@
+{
+ "mcpServers": {
+ "ue-docs": {
+ "command": "python",
+ "args": ["ue_mcp_server.py"]
+ }
+ }
+}
diff --git a/generate.py b/generate.py
index 9ff5b2e..c068cf5 100644
--- a/generate.py
+++ b/generate.py
@@ -3,9 +3,10 @@
generate.py — CLI for UnrealDocGenerator.
Usage:
- python generate.py
+ python generate.py [input2 ...]
- can be a single .h file or a directory (processed recursively).
+Each can be a single .h file or a directory (processed recursively).
+The last argument is always the output directory.
Two-pass pipeline:
Pass 1 — parse every header, build a corpus-wide type index
@@ -20,24 +21,36 @@ from ue_parser import parse_header, ParsedHeader
from ue_markdown import render_header
+# ---------------------------------------------------------------------------
+# Input collection
+# ---------------------------------------------------------------------------
+
+def collect_headers(input_arg: Path) -> list[tuple[Path, Path]]:
+ """
+ Returns a list of (header_path, base_path) pairs for the given input.
+ base_path is used to compute relative output paths.
+ """
+ if input_arg.is_file():
+ return [(input_arg, input_arg.parent)]
+ elif input_arg.is_dir():
+ return [(h, input_arg) for h in sorted(input_arg.rglob('*.h'))]
+ else:
+ print(f"Error: {input_arg} is not a file or directory", file=sys.stderr)
+ return []
+
+
# ---------------------------------------------------------------------------
# Type index
# ---------------------------------------------------------------------------
-def build_type_index(parsed_list: list[tuple[Path, ParsedHeader]],
- input_base: Path) -> dict[str, str]:
+def build_type_index(parsed_list: list[tuple[Path, Path, ParsedHeader]]) -> dict[str, str]:
"""
Returns {TypeName: md_path_relative_to_docs_root} for every
class, struct, enum, and delegate in the corpus.
"""
index: dict[str, str] = {}
- for h, parsed in parsed_list:
- try:
- rel = h.relative_to(input_base)
- except ValueError:
- rel = Path(h.name)
- md_rel = str(rel.with_suffix('.md'))
-
+ for h, base, parsed in parsed_list:
+ md_rel = _md_rel(h, base)
for ci in parsed.classes:
index[ci.name] = md_rel
for ei in parsed.enums:
@@ -48,6 +61,15 @@ def build_type_index(parsed_list: list[tuple[Path, ParsedHeader]],
return index
+def _md_rel(h: Path, base: Path) -> str:
+ """Relative .md path for header h given its input base."""
+ try:
+ rel = h.relative_to(base)
+ except ValueError:
+ rel = Path(h.name)
+ return str(rel.with_suffix('.md'))
+
+
# ---------------------------------------------------------------------------
# Type index file
# ---------------------------------------------------------------------------
@@ -72,51 +94,43 @@ def write_type_index(type_index: dict[str, str], output_dir: Path) -> None:
def main():
if len(sys.argv) < 3:
- print("Usage: python generate.py ", file=sys.stderr)
+ print("Usage: python generate.py [input2 ...] ", file=sys.stderr)
sys.exit(1)
- input_arg = Path(sys.argv[1])
- output_dir = Path(sys.argv[2])
+ *input_args, output_arg = sys.argv[1:]
+ output_dir = Path(output_arg)
output_dir.mkdir(parents=True, exist_ok=True)
- # Collect input files
- if input_arg.is_file():
- headers = [input_arg]
- input_base = input_arg.parent
- elif input_arg.is_dir():
- headers = sorted(input_arg.rglob('*.h'))
- input_base = input_arg
- else:
- print(f"Error: {input_arg} is not a file or directory", file=sys.stderr)
- sys.exit(1)
+ # Collect (header, base) pairs from all inputs
+ header_pairs: list[tuple[Path, Path]] = []
+ for arg in input_args:
+ pairs = collect_headers(Path(arg))
+ if not pairs:
+ print(f"Warning: no .h files found in {arg}", file=sys.stderr)
+ header_pairs.extend(pairs)
- if not headers:
+ if not header_pairs:
print("No .h files found.", file=sys.stderr)
sys.exit(1)
# --- Pass 1: parse all ---
- parsed_list: list[tuple[Path, ParsedHeader]] = []
- for h in headers:
+ parsed_list: list[tuple[Path, Path, ParsedHeader]] = []
+ for h, base in header_pairs:
print(f"Parsing {h} ...")
try:
parsed = parse_header(str(h))
- parsed_list.append((h, parsed))
+ parsed_list.append((h, base, parsed))
except Exception as exc:
print(f" ERROR parsing {h}: {exc}", file=sys.stderr)
# --- Build corpus-wide type index ---
- type_index = build_type_index(parsed_list, input_base)
+ type_index = build_type_index(parsed_list)
# --- Pass 2: render all ---
success = 0
- for h, parsed in parsed_list:
+ for h, base, parsed in parsed_list:
print(f"Rendering {h} ...")
- try:
- rel = h.relative_to(input_base)
- except ValueError:
- rel = Path(h.name)
-
- current_md = str(rel.with_suffix('.md'))
+ current_md = _md_rel(h, base)
out_path = output_dir / current_md
out_path.parent.mkdir(parents=True, exist_ok=True)
diff --git a/possess_flow.png b/possess_flow.png
new file mode 100644
index 0000000..8af0f2e
Binary files /dev/null and b/possess_flow.png differ
diff --git a/samples/AIController.h b/samples/AIController.h
deleted file mode 100644
index cb74dad..0000000
--- a/samples/AIController.h
+++ /dev/null
@@ -1,465 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "UObject/ObjectMacros.h"
-#include "UObject/UObjectGlobals.h"
-#include "Templates/SubclassOf.h"
-#include "EngineDefines.h"
-#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4
-#include "NavFilters/NavigationQueryFilter.h"
-#endif
-#include "AITypes.h"
-#include "GameplayTaskOwnerInterface.h"
-#include "GameplayTask.h"
-#include "GameFramework/Pawn.h"
-#include "GameFramework/Controller.h"
-#include "Perception/AIPerceptionListenerInterface.h"
-#include "GenericTeamAgentInterface.h"
-#include "VisualLogger/VisualLoggerDebugSnapshotInterface.h"
-#include "AIController.generated.h"
-
-class FDebugDisplayInfo;
-class UAIPerceptionComponent;
-class UBehaviorTree;
-class UBlackboardComponent;
-class UBlackboardData;
-class UBrainComponent;
-class UCanvas;
-class UGameplayTaskResource;
-class UGameplayTasksComponent;
-class UPathFollowingComponent;
-
-namespace EPathFollowingRequestResult { enum Type : int; }
-namespace EPathFollowingResult { enum Type : int; }
-namespace EPathFollowingStatus { enum Type : int; }
-
-#if ENABLE_VISUAL_LOG
-struct FVisualLogEntry;
-#endif // ENABLE_VISUAL_LOG
-struct FPathFindingQuery;
-struct FPathFollowingRequestResult;
-struct FPathFollowingResult;
-
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAIMoveCompletedSignature, FAIRequestID, RequestID, EPathFollowingResult::Type, Result);
-
-// the reason for this being namespace instead of a regular enum is
-// so that it can be expanded in game-specific code
-// @todo this is a bit messy, needs to be refactored
-namespace EAIFocusPriority
-{
- typedef uint8 Type;
-
- inline const Type Default = 0;
- inline const Type Move = 1;
- inline const Type Gameplay = 2;
-
- inline const Type LastFocusPriority = Gameplay;
-}
-
-struct FFocusKnowledge
-{
- struct FFocusItem
- {
- TWeakObjectPtr Actor;
- FVector Position;
-
- FFocusItem()
- {
- Actor = nullptr;
- Position = FAISystem::InvalidLocation;
- }
- };
-
- TArray Priorities;
-};
-
-//~=============================================================================
-/**
- * AIController is the base class of controllers for AI-controlled Pawns.
- *
- * Controllers are non-physical actors that can be attached to a pawn to control its actions.
- * AIControllers manage the artificial intelligence for the pawns they control.
- * In networked games, they only exist on the server.
- *
- * @see https://docs.unrealengine.com/latest/INT/Gameplay/Framework/Controller/
- */
-
-UCLASS(ClassGroup = AI, BlueprintType, Blueprintable, MinimalAPI)
-class AAIController : public AController, public IAIPerceptionListenerInterface, public IGameplayTaskOwnerInterface, public IGenericTeamAgentInterface, public IVisualLoggerDebugSnapshotInterface
-{
- GENERATED_BODY()
-
- FGameplayResourceSet ScriptClaimedResources;
-protected:
- FFocusKnowledge FocusInformation;
-
- /** By default AI's logic does not start when controlled Pawn is possessed. Setting this flag to true
- * will make AI logic start when pawn is possessed */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bStartAILogicOnPossess : 1;
-
- /** By default AI's logic gets stopped when controlled Pawn is unpossessed. Setting this flag to false
- * will make AI logic persist past losing control over a pawn */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bStopAILogicOnUnposses : 1;
-
-public:
- /** used for alternating LineOfSight traces */
- UPROPERTY()
- mutable uint32 bLOSflag : 1;
-
- /** Skip extra line of sight traces to extremities of target being checked. */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bSkipExtraLOSChecks : 1;
-
- /** Is strafing allowed during movement? */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bAllowStrafe : 1;
-
- /** Specifies if this AI wants its own PlayerState. */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bWantsPlayerState : 1;
-
- /** Copy Pawn rotation to ControlRotation, if there is no focus point. */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- uint32 bSetControlRotationFromPawnOrientation:1;
-
-private:
-
- /** Component used for moving along a path. */
- UPROPERTY(VisibleDefaultsOnly, Category = AI)
- TObjectPtr PathFollowingComponent;
-
-public:
-
- /** Component responsible for behaviors. */
- UPROPERTY(BlueprintReadWrite, Category = AI)
- TObjectPtr BrainComponent;
-
- UPROPERTY(VisibleDefaultsOnly, Category = AI)
- TObjectPtr PerceptionComponent;
-
-protected:
- /** blackboard */
- UPROPERTY(BlueprintReadOnly, Category = AI, meta = (AllowPrivateAccess = "true"))
- TObjectPtr Blackboard;
-
- UPROPERTY()
- TObjectPtr CachedGameplayTasksComponent;
-
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
- TSubclassOf DefaultNavigationFilterClass;
-
-public:
-
- AIMODULE_API AAIController(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
-
- AIMODULE_API virtual void SetPawn(APawn* InPawn) override;
-
- /** Makes AI go toward specified Goal actor (destination will be continuously updated), aborts any active path following
- * @param AcceptanceRadius - finish move if pawn gets close enough
- * @param bStopOnOverlap - add pawn's radius to AcceptanceRadius
- * @param bUsePathfinding - use navigation data to calculate path (otherwise it will go in straight line)
- * @param bCanStrafe - set focus related flag: bAllowStrafe
- * @param FilterClass - navigation filter for pathfinding adjustments. If none specified DefaultNavigationFilterClass will be used
- * @param bAllowPartialPath - use incomplete path when goal can't be reached
- * @note AcceptanceRadius has default value or -1 due to Header Parser not being able to recognize UPathFollowingComponent::DefaultAcceptanceRadius
- */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation", Meta = (AdvancedDisplay = "bStopOnOverlap,bCanStrafe,bAllowPartialPath"))
- AIMODULE_API EPathFollowingRequestResult::Type MoveToActor(AActor* Goal, float AcceptanceRadius = -1, bool bStopOnOverlap = true,
- bool bUsePathfinding = true, bool bCanStrafe = true,
- TSubclassOf FilterClass = {}, bool bAllowPartialPath = true);
-
- /** Makes AI go toward specified Dest location, aborts any active path following
- * @param AcceptanceRadius - finish move if pawn gets close enough
- * @param bStopOnOverlap - add pawn's radius to AcceptanceRadius
- * @param bUsePathfinding - use navigation data to calculate path (otherwise it will go in straight line)
- * @param bProjectDestinationToNavigation - project location on navigation data before using it
- * @param bCanStrafe - set focus related flag: bAllowStrafe
- * @param FilterClass - navigation filter for pathfinding adjustments. If none specified DefaultNavigationFilterClass will be used
- * @param bAllowPartialPath - use incomplete path when goal can't be reached
- * @note AcceptanceRadius has default value or -1 due to Header Parser not being able to recognize UPathFollowingComponent::DefaultAcceptanceRadius
- */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation", Meta = (AdvancedDisplay = "bStopOnOverlap,bCanStrafe,bAllowPartialPath"))
- AIMODULE_API EPathFollowingRequestResult::Type MoveToLocation(const FVector& Dest, float AcceptanceRadius = -1, bool bStopOnOverlap = true,
- bool bUsePathfinding = true, bool bProjectDestinationToNavigation = false, bool bCanStrafe = true,
- TSubclassOf FilterClass = {}, bool bAllowPartialPath = true);
-
- /** Makes AI go toward specified destination
- * @param MoveRequest - details about move
- * @param OutPath - optional output param, filled in with assigned path
- * @return struct holding MoveId and enum code
- */
- AIMODULE_API virtual FPathFollowingRequestResult MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath = nullptr);
-
- /** Passes move request and path object to path following */
- AIMODULE_API virtual FAIRequestID RequestMove(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr Path);
-
- /** Finds path for given move request
- * @param MoveRequest - details about move
- * @param Query - pathfinding query for navigation system
- * @param OutPath - generated path
- */
- AIMODULE_API virtual void FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) const;
-
- /** Helper function for creating pathfinding query for this agent from move request data */
- AIMODULE_API bool BuildPathfindingQuery(const FAIMoveRequest& MoveRequest, FPathFindingQuery& OutQuery) const;
-
- /** Helper function for creating pathfinding query for this agent from move request data and starting location */
- AIMODULE_API bool BuildPathfindingQuery(const FAIMoveRequest& MoveRequest, const FVector& StartLocation, FPathFindingQuery& OutQuery) const;
-
- UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use FindPathForMoveRequest() for adjusting Query or BuildPathfindingQuery() for getting one.")
- AIMODULE_API virtual bool PreparePathfinding(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query);
-
- UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use FindPathForMoveRequest() for adjusting pathfinding or path postprocess.")
- AIMODULE_API virtual FAIRequestID RequestPathAndMove(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query);
-
- /** if AI is currently moving due to request given by RequestToPause, then the move will be paused */
- AIMODULE_API bool PauseMove(FAIRequestID RequestToPause);
-
- /** resumes last AI-performed, paused request provided it's ID was equivalent to RequestToResume */
- AIMODULE_API bool ResumeMove(FAIRequestID RequestToResume);
-
- /** Aborts the move the controller is currently performing */
- AIMODULE_API virtual void StopMovement() override;
-
- /** Called on completing current movement request */
- AIMODULE_API virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result);
-
- UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use version with EPathFollowingResultDetails parameter.")
- AIMODULE_API virtual void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result);
-
- /** Returns the Move Request ID for the current move */
- AIMODULE_API FAIRequestID GetCurrentMoveRequestID() const;
-
- /** Blueprint notification that we've completed the current movement request */
- UPROPERTY(BlueprintAssignable, meta = (DisplayName = "MoveCompleted"))
- FAIMoveCompletedSignature ReceiveMoveCompleted;
-
- TSubclassOf GetDefaultNavigationFilterClass() const { return DefaultNavigationFilterClass; }
-
- /** Returns status of path following */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
- AIMODULE_API EPathFollowingStatus::Type GetMoveStatus() const;
-
- /** Returns true if the current PathFollowingComponent's path is partial (does not reach desired destination). */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
- AIMODULE_API bool HasPartialPath() const;
-
- /** Returns position of current path segment's end. */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
- AIMODULE_API FVector GetImmediateMoveDestination() const;
-
- /** Updates state of movement block detection. */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
- AIMODULE_API void SetMoveBlockDetection(bool bEnable);
-
- /** Starts executing behavior tree. */
- UFUNCTION(BlueprintCallable, Category = "AI")
- AIMODULE_API virtual bool RunBehaviorTree(UBehaviorTree* BTAsset);
-
-protected:
- AIMODULE_API virtual void CleanupBrainComponent();
-
- /** Merges the remaining points of InitialPath, with the points of InOutMergedPath. The resulting merged path is outputted into InOutMergedPath. */
- AIMODULE_API virtual void MergePaths(const FNavPathSharedPtr& InitialPath, FNavPathSharedPtr& InOutMergedPath) const;
-
-public:
- /**
- * Makes AI use the specified Blackboard asset & creates a Blackboard Component if one does not already exist.
- * @param BlackboardAsset The Blackboard asset to use.
- * @param BlackboardComponent The Blackboard component that was used or created to work with the passed-in Blackboard Asset.
- * @return true if we successfully linked the blackboard asset to the blackboard component.
- */
- UFUNCTION(BlueprintCallable, Category = "AI")
- AIMODULE_API bool UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent);
-
- /** does this AIController allow given UBlackboardComponent sync data with it */
- AIMODULE_API virtual bool ShouldSyncBlackboardWith(const UBlackboardComponent& OtherBlackboardComponent) const;
-
- UFUNCTION(BlueprintCallable, Category = "AI|Tasks")
- AIMODULE_API void ClaimTaskResource(TSubclassOf ResourceClass);
-
- UFUNCTION(BlueprintCallable, Category = "AI|Tasks")
- AIMODULE_API void UnclaimTaskResource(TSubclassOf ResourceClass);
-
-protected:
- UFUNCTION(BlueprintImplementableEvent)
- AIMODULE_API void OnUsingBlackBoard(UBlackboardComponent* BlackboardComp, UBlackboardData* BlackboardAsset);
-
- AIMODULE_API virtual bool InitializeBlackboard(UBlackboardComponent& BlackboardComp, UBlackboardData& BlackboardAsset);
-
-public:
- /** Retrieve the final position that controller should be looking at. */
- UFUNCTION(BlueprintCallable, Category = "AI")
- AIMODULE_API FVector GetFocalPoint() const;
-
- AIMODULE_API FVector GetFocalPointForPriority(EAIFocusPriority::Type InPriority) const;
-
- /** Retrieve the focal point this controller should focus to on given actor. */
- UFUNCTION(BlueprintCallable, Category = "AI")
- AIMODULE_API virtual FVector GetFocalPointOnActor(const AActor *Actor) const;
-
- /** Set the position that controller should be looking at. */
- UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "SetFocalPoint", ScriptName = "SetFocalPoint", Keywords = "focus"))
- AIMODULE_API void K2_SetFocalPoint(FVector FP);
-
- /** Set Focus for actor, will set FocalPoint as a result. */
- UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "SetFocus", ScriptName = "SetFocus"))
- AIMODULE_API void K2_SetFocus(AActor* NewFocus);
-
- /** Get the focused actor. */
- UFUNCTION(BlueprintCallable, Category = "AI")
- AIMODULE_API AActor* GetFocusActor() const;
-
- inline AActor* GetFocusActorForPriority(EAIFocusPriority::Type InPriority) const { return FocusInformation.Priorities.IsValidIndex(InPriority) ? FocusInformation.Priorities[InPriority].Actor.Get() : nullptr; }
-
- /** Clears Focus, will also clear FocalPoint as a result */
- UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "ClearFocus", ScriptName = "ClearFocus"))
- AIMODULE_API void K2_ClearFocus();
-
-
- /**
- * Computes a launch velocity vector to toss a projectile and hit the given destination.
- * Performance note: Potentially expensive. Nonzero CollisionRadius and bOnlyTraceUp=false are the more expensive options.
- *
- * @param OutTossVelocity - out param stuffed with the computed velocity to use
- * @param Start - desired start point of arc
- * @param End - desired end point of arc
- * @param TossSpeed - Initial speed of the theoretical projectile. Assumed to only change due to gravity for the entire lifetime of the projectile
- * @param CollisionSize (optional) - is the size of bounding box of the tossed actor (defaults to (0,0,0)
- * @param bOnlyTraceUp (optional) - when true collision checks verifying the arc will only be done along the upward portion of the arc
- * @return - true if a valid arc was computed, false if no valid solution could be found
- */
- AIMODULE_API bool SuggestTossVelocity(FVector& OutTossVelocity, FVector Start, FVector End, float TossSpeed, bool bPreferHighArc, float CollisionRadius = 0, bool bOnlyTraceUp = false);
-
- //~ Begin AActor Interface
- AIMODULE_API virtual void Tick(float DeltaTime) override;
- AIMODULE_API virtual void PostInitializeComponents() override;
- AIMODULE_API virtual void PostRegisterAllComponents() override;
- //~ End AActor Interface
-
- //~ Begin AController Interface
-protected:
- AIMODULE_API virtual void OnPossess(APawn* InPawn) override;
- AIMODULE_API virtual void OnUnPossess() override;
-
-public:
- AIMODULE_API virtual bool ShouldPostponePathUpdates() const override;
- AIMODULE_API virtual void DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos) override;
-
-#if ENABLE_VISUAL_LOG
- AIMODULE_API virtual void GrabDebugSnapshot(FVisualLogEntry* Snapshot) const override;
-#endif
-
- AIMODULE_API virtual void Reset() override;
-
- /**
- * Checks line to center and top of other actor
- * @param Other is the actor whose visibility is being checked.
- * @param ViewPoint is eye position visibility is being checked from. If vect(0,0,0) passed in, uses current viewtarget's eye position.
- * @param bAlternateChecks used only in AIController implementation
- * @return true if controller's pawn can see Other actor.
- */
- AIMODULE_API virtual bool LineOfSightTo(const AActor* Other, FVector ViewPoint = FVector(ForceInit), bool bAlternateChecks = false) const override;
- //~ End AController Interface
-
- /** Notifies AIController of changes in given actors' perception */
- AIMODULE_API virtual void ActorsPerceptionUpdated(const TArray& UpdatedActors);
-
- /** Update direction AI is looking based on FocalPoint */
- AIMODULE_API virtual void UpdateControlRotation(float DeltaTime, bool bUpdatePawn = true);
-
- /** Set FocalPoint for given priority as absolute position or offset from base. */
- AIMODULE_API virtual void SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority = EAIFocusPriority::Gameplay);
-
- /* Set Focus actor for given priority, will set FocalPoint as a result. */
- AIMODULE_API virtual void SetFocus(AActor* NewFocus, EAIFocusPriority::Type InPriority = EAIFocusPriority::Gameplay);
-
- /** Clears Focus for given priority, will also clear FocalPoint as a result
- * @param InPriority focus priority to clear. If you don't know what to use you probably mean EAIFocusPriority::Gameplay*/
- AIMODULE_API virtual void ClearFocus(EAIFocusPriority::Type InPriority);
-
- AIMODULE_API void SetPerceptionComponent(UAIPerceptionComponent& InPerceptionComponent);
- //----------------------------------------------------------------------//
- // IAIPerceptionListenerInterface
- //----------------------------------------------------------------------//
- virtual UAIPerceptionComponent* GetPerceptionComponent() override { return GetAIPerceptionComponent(); }
-
- //----------------------------------------------------------------------//
- // INavAgentInterface
- //----------------------------------------------------------------------//
- AIMODULE_API virtual bool IsFollowingAPath() const override;
- AIMODULE_API virtual IPathFollowingAgentInterface* GetPathFollowingAgent() const override;
-
- //----------------------------------------------------------------------//
- // IGenericTeamAgentInterface
- //----------------------------------------------------------------------//
-private:
- FGenericTeamId TeamID;
-public:
- AIMODULE_API virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
- virtual FGenericTeamId GetGenericTeamId() const override { return TeamID; }
-
- //----------------------------------------------------------------------//
- // IGameplayTaskOwnerInterface
- //----------------------------------------------------------------------//
- virtual UGameplayTasksComponent* GetGameplayTasksComponent(const UGameplayTask& Task) const override { return GetGameplayTasksComponent(); }
- virtual AActor* GetGameplayTaskOwner(const UGameplayTask* Task) const override { return const_cast(this); }
- virtual AActor* GetGameplayTaskAvatar(const UGameplayTask* Task) const override { return GetPawn(); }
- virtual uint8 GetGameplayTaskDefaultPriority() const { return FGameplayTasks::DefaultPriority - 1; }
-
- inline UGameplayTasksComponent* GetGameplayTasksComponent() const { return CachedGameplayTasksComponent; }
-
- // add empty overrides to fix linker errors if project implements a child class without adding GameplayTasks module dependency
- virtual void OnGameplayTaskInitialized(UGameplayTask& Task) override {}
- virtual void OnGameplayTaskActivated(UGameplayTask& Task) override {}
- virtual void OnGameplayTaskDeactivated(UGameplayTask& Task) override {}
-
- UFUNCTION()
- AIMODULE_API virtual void OnGameplayTaskResourcesClaimed(FGameplayResourceSet NewlyClaimed, FGameplayResourceSet FreshlyReleased);
-
- //----------------------------------------------------------------------//
- // debug/dev-time
- //----------------------------------------------------------------------//
- AIMODULE_API virtual FString GetDebugIcon() const;
-
- // Cheat/debugging functions
- static void ToggleAIIgnorePlayers() { bAIIgnorePlayers = !bAIIgnorePlayers; }
- static bool AreAIIgnoringPlayers() { return bAIIgnorePlayers; }
-
- /** If true, AI controllers will ignore players. */
- static AIMODULE_API bool bAIIgnorePlayers;
-
-public:
- /** Returns PathFollowingComponent subobject **/
- UFUNCTION(BlueprintCallable, Category="AI|Navigation")
- UPathFollowingComponent* GetPathFollowingComponent() const { return PathFollowingComponent; }
- UFUNCTION(BlueprintPure, Category = "AI|Perception")
- UAIPerceptionComponent* GetAIPerceptionComponent() { return PerceptionComponent; }
-
- const UAIPerceptionComponent* GetAIPerceptionComponent() const { return PerceptionComponent; }
-
- UBrainComponent* GetBrainComponent() const { return BrainComponent; }
- const UBlackboardComponent* GetBlackboardComponent() const { return Blackboard; }
- UBlackboardComponent* GetBlackboardComponent() { return Blackboard; }
-
- /** Note that this function does not do any pathfollowing state transfer.
- * Intended to be called as part of initialization/setup process */
- UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
- AIMODULE_API void SetPathFollowingComponent(UPathFollowingComponent* NewPFComponent);
-};
-
-//----------------------------------------------------------------------//
-// forceinlines
-//----------------------------------------------------------------------//
-namespace FAISystem
-{
- inline bool IsValidControllerAndHasValidPawn(const AController* Controller)
- {
- return Controller != nullptr && Controller->IsPendingKillPending() == false
- && Controller->GetPawn() != nullptr && Controller->GetPawn()->IsPendingKillPending() == false;
- }
-}
diff --git a/samples/GameplayTagsManager.h b/samples/GameplayTagsManager.h
deleted file mode 100644
index 68eb7c9..0000000
--- a/samples/GameplayTagsManager.h
+++ /dev/null
@@ -1,1010 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include "AssetRegistry/AssetData.h"
-#include "CoreMinimal.h"
-#include "Stats/Stats.h"
-#include "UObject/ObjectMacros.h"
-#include "UObject/Object.h"
-#include "UObject/ScriptMacros.h"
-#include "GameplayTagContainer.h"
-#include "Engine/DataTable.h"
-#include "Templates/UniquePtr.h"
-#include "Misc/ScopeLock.h"
-#include "Misc/TransactionallySafeCriticalSection.h"
-#if WITH_EDITOR
-#include "Hash/Blake3.h"
-#endif
-
-#include "GameplayTagsManager.generated.h"
-
-class UGameplayTagsList;
-struct FStreamableHandle;
-class FNativeGameplayTag;
-
-#if WITH_EDITOR
-namespace UE::Cook { class FCookDependency; }
-namespace UE::Cook { class ICookInfo; }
-#endif
-
-/** Simple struct for a table row in the gameplay tag table and element in the ini list */
-USTRUCT()
-struct FGameplayTagTableRow : public FTableRowBase
-{
- GENERATED_USTRUCT_BODY()
-
- /** Tag specified in the table */
- UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=GameplayTag)
- FName Tag;
-
- /** Developer comment clarifying the usage of a particular tag, not user facing */
- UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=GameplayTag)
- FString DevComment;
-
- /** Constructors */
- FGameplayTagTableRow() {}
- FGameplayTagTableRow(FName InTag, const FString& InDevComment = TEXT("")) : Tag(InTag), DevComment(InDevComment) {}
- GAMEPLAYTAGS_API FGameplayTagTableRow(FGameplayTagTableRow const& Other);
-
- /** Assignment/Equality operators */
- GAMEPLAYTAGS_API FGameplayTagTableRow& operator=(FGameplayTagTableRow const& Other);
- GAMEPLAYTAGS_API bool operator==(FGameplayTagTableRow const& Other) const;
- GAMEPLAYTAGS_API bool operator!=(FGameplayTagTableRow const& Other) const;
- GAMEPLAYTAGS_API bool operator<(FGameplayTagTableRow const& Other) const;
-};
-
-/** Simple struct for a table row in the restricted gameplay tag table and element in the ini list */
-USTRUCT()
-struct FRestrictedGameplayTagTableRow : public FGameplayTagTableRow
-{
- GENERATED_USTRUCT_BODY()
-
- /** Tag specified in the table */
- UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = GameplayTag)
- bool bAllowNonRestrictedChildren;
-
- /** Constructors */
- FRestrictedGameplayTagTableRow() : bAllowNonRestrictedChildren(false) {}
- FRestrictedGameplayTagTableRow(FName InTag, const FString& InDevComment = TEXT(""), bool InAllowNonRestrictedChildren = false) : FGameplayTagTableRow(InTag, InDevComment), bAllowNonRestrictedChildren(InAllowNonRestrictedChildren) {}
- GAMEPLAYTAGS_API FRestrictedGameplayTagTableRow(FRestrictedGameplayTagTableRow const& Other);
-
- /** Assignment/Equality operators */
- GAMEPLAYTAGS_API FRestrictedGameplayTagTableRow& operator=(FRestrictedGameplayTagTableRow const& Other);
- GAMEPLAYTAGS_API bool operator==(FRestrictedGameplayTagTableRow const& Other) const;
- GAMEPLAYTAGS_API bool operator!=(FRestrictedGameplayTagTableRow const& Other) const;
-};
-
-UENUM()
-enum class EGameplayTagSourceType : uint8
-{
- Native, // Was added from C++ code
- DefaultTagList, // The default tag list in DefaultGameplayTags.ini
- TagList, // Another tag list from an ini in tags/*.ini
- RestrictedTagList, // Restricted tags from an ini
- DataTable, // From a DataTable
- Invalid, // Not a real source
-};
-
-UENUM()
-enum class EGameplayTagSelectionType : uint8
-{
- None,
- NonRestrictedOnly,
- RestrictedOnly,
- All
-};
-
-/** Struct defining where gameplay tags are loaded/saved from. Mostly for the editor */
-USTRUCT()
-struct FGameplayTagSource
-{
- GENERATED_USTRUCT_BODY()
-
- /** Name of this source */
- UPROPERTY()
- FName SourceName;
-
- /** Type of this source */
- UPROPERTY()
- EGameplayTagSourceType SourceType;
-
- /** If this is bound to an ini object for saving, this is the one */
- UPROPERTY()
- TObjectPtr SourceTagList;
-
- /** If this has restricted tags and is bound to an ini object for saving, this is the one */
- UPROPERTY()
- TObjectPtr SourceRestrictedTagList;
-
- FGameplayTagSource()
- : SourceName(NAME_None), SourceType(EGameplayTagSourceType::Invalid), SourceTagList(nullptr), SourceRestrictedTagList(nullptr)
- {
- }
-
- FGameplayTagSource(FName InSourceName, EGameplayTagSourceType InSourceType, UGameplayTagsList* InSourceTagList = nullptr, URestrictedGameplayTagsList* InSourceRestrictedTagList = nullptr)
- : SourceName(InSourceName), SourceType(InSourceType), SourceTagList(InSourceTagList), SourceRestrictedTagList(InSourceRestrictedTagList)
- {
- }
-
- /** Returns the config file that created this source, if valid */
- GAMEPLAYTAGS_API FString GetConfigFileName() const;
-
- static GAMEPLAYTAGS_API FName GetNativeName();
-
- static GAMEPLAYTAGS_API FName GetDefaultName();
-
-#if WITH_EDITOR
- static GAMEPLAYTAGS_API FName GetFavoriteName();
-
- static GAMEPLAYTAGS_API void SetFavoriteName(FName TagSourceToFavorite);
-
- static GAMEPLAYTAGS_API FName GetTransientEditorName();
-#endif
-};
-
-/** Struct describing the places to look for ini search paths */
-struct FGameplayTagSearchPathInfo
-{
- /** Which sources should be loaded from this path */
- TArray SourcesInPath;
-
- /** Config files to load from, will normally correspond to FoundSources */
- TArray TagIniList;
-
- /** True if this path has already been searched */
- bool bWasSearched = false;
-
- /** True if the tags in sources have been added to the current tree */
- bool bWasAddedToTree = false;
-
- inline void Reset()
- {
- SourcesInPath.Reset();
- TagIniList.Reset();
- bWasSearched = false;
- bWasAddedToTree = false;
- }
-
- inline bool IsValid()
- {
- return bWasSearched && bWasAddedToTree;
- }
-};
-
-/** Simple tree node for gameplay tags, this stores metadata about specific tags */
-USTRUCT()
-struct FGameplayTagNode
-{
- GENERATED_USTRUCT_BODY()
- FGameplayTagNode(){};
-
- /** Simple constructor, passing redundant data for performance */
- FGameplayTagNode(FName InTag, FName InFullTag, TSharedPtr InParentNode, bool InIsExplicitTag, bool InIsRestrictedTag, bool InAllowNonRestrictedChildren);
-
- /** Returns a correctly constructed container with only this tag, useful for doing container queries */
- inline const FGameplayTagContainer& GetSingleTagContainer() const { return CompleteTagWithParents; }
-
- /**
- * Get the complete tag for the node, including all parent tags, delimited by periods
- *
- * @return Complete tag for the node
- */
- inline const FGameplayTag& GetCompleteTag() const { return CompleteTagWithParents.Num() > 0 ? CompleteTagWithParents.GameplayTags[0] : FGameplayTag::EmptyTag; }
- inline FName GetCompleteTagName() const { return GetCompleteTag().GetTagName(); }
- inline FString GetCompleteTagString() const { return GetCompleteTag().ToString(); }
-
- /**
- * Get the simple tag for the node (doesn't include any parent tags)
- *
- * @return Simple tag for the node
- */
- inline FName GetSimpleTagName() const { return Tag; }
-
- /**
- * Get the children nodes of this node
- *
- * @return Reference to the array of the children nodes of this node
- */
- inline TArray< TSharedPtr >& GetChildTagNodes() { return ChildTags; }
-
- /**
- * Get the children nodes of this node
- *
- * @return Reference to the array of the children nodes of this node
- */
- inline const TArray< TSharedPtr >& GetChildTagNodes() const { return ChildTags; }
-
- /**
- * Get the parent tag node of this node
- *
- * @return The parent tag node of this node
- */
- inline TSharedPtr GetParentTagNode() const { return ParentNode; }
-
- /**
- * Get the net index of this node
- *
- * @return The net index of this node
- */
- inline FGameplayTagNetIndex GetNetIndex() const { check(NetIndex != INVALID_TAGNETINDEX); return NetIndex; }
-
- /** Reset the node of all of its values */
- GAMEPLAYTAGS_API void ResetNode();
-
- /** Returns true if the tag was explicitly specified in code or data */
- inline bool IsExplicitTag() const {
-#if WITH_EDITORONLY_DATA
- return bIsExplicitTag;
-#else
- return true;
-#endif
- }
-
- /** Returns true if the tag is a restricted tag and allows non-restricted children */
- inline bool GetAllowNonRestrictedChildren() const {
-#if WITH_EDITORONLY_DATA
- return bAllowNonRestrictedChildren;
-#else
- return true;
-#endif
- }
-
- /** Returns true if the tag is a restricted tag */
- inline bool IsRestrictedGameplayTag() const {
-#if WITH_EDITORONLY_DATA
- return bIsRestrictedTag;
-#else
- return true;
-#endif
- }
-
-#if WITH_EDITORONLY_DATA
- FName GetFirstSourceName() const { return SourceNames.Num() == 0 ? NAME_None : SourceNames[0]; }
- const TArray& GetAllSourceNames() const { return SourceNames; }
-#endif
-
-#if WITH_EDITORONLY_DATA
- /** Returns the Comment for this tag */
- FString GetDevComment() const { return DevComment; }
-#endif
-
-#if WITH_EDITOR
- /**
- * Update the hasher with a deterministic hash of the data on this. Used for e.g. IncrementalCook keys.
- * Does not include data from this node's child or parent nodes.
- */
- GAMEPLAYTAGS_API void Hash(FBlake3& Hasher);
-#endif
-
-private:
- /** Raw name for this tag at current rank in the tree */
- FName Tag;
-
- /** This complete tag is at GameplayTags[0], with parents in ParentTags[] */
- FGameplayTagContainer CompleteTagWithParents;
-
- /** Child gameplay tag nodes */
- TArray< TSharedPtr > ChildTags;
-
- /** Owner gameplay tag node, if any */
- TSharedPtr ParentNode;
-
- /** Net Index of this node */
- FGameplayTagNetIndex NetIndex;
-
-#if WITH_EDITORONLY_DATA
- /** Module or Package or config file this tag came from. If empty this is an implicitly added tag */
- TArray SourceNames;
-
- /** Comment for this tag */
- FString DevComment;
-
- /** If this is true then the tag can only have normal tag children if bAllowNonRestrictedChildren is true */
- uint8 bIsRestrictedTag : 1;
-
- /** If this is true then any children of this tag must come from the restricted tags */
- uint8 bAllowNonRestrictedChildren : 1;
-
- /** If this is true then the tag was explicitly added and not only implied by its child tags */
- uint8 bIsExplicitTag : 1;
-
- /** If this is true then at least one tag that inherits from this tag is coming from multiple sources. Used for updating UI in the editor. */
- uint8 bDescendantHasConflict : 1;
-
- /** If this is true then this tag is coming from multiple sources. No descendants can be changed on this tag until this is resolved. */
- uint8 bNodeHasConflict : 1;
-
- /** If this is true then at least one tag that this tag descends from is coming from multiple sources. This tag and it's descendants can't be changed in the editor. */
- uint8 bAncestorHasConflict : 1;
-#endif
-
- friend class UGameplayTagsManager;
- friend class SGameplayTagWidget;
- friend class SGameplayTagPicker;
-};
-
-/** Holds data about the tag dictionary, is in a singleton UObject */
-UCLASS(config=Engine, MinimalAPI)
-class UGameplayTagsManager : public UObject
-{
- GENERATED_UCLASS_BODY()
-
- /** Destructor */
- GAMEPLAYTAGS_API ~UGameplayTagsManager();
-
- /** Returns the global UGameplayTagsManager manager */
- inline static UGameplayTagsManager& Get()
- {
- if (SingletonManager == nullptr)
- {
- InitializeManager();
- }
-
- return *SingletonManager;
- }
-
- /** Returns possibly nullptr to the manager. Needed for some shutdown cases to avoid reallocating. */
- inline static UGameplayTagsManager* GetIfAllocated() { return SingletonManager; }
-
- /**
- * Adds the gameplay tags corresponding to the strings in the array TagStrings to OutTagsContainer
- *
- * @param TagStrings Array of strings to search for as tags to add to the tag container
- * @param OutTagsContainer Container to add the found tags to.
- * @param ErrorIfNotfound: ensure() that tags exists.
- *
- */
- GAMEPLAYTAGS_API void RequestGameplayTagContainer(const TArray& TagStrings, FGameplayTagContainer& OutTagsContainer, bool bErrorIfNotFound=true) const;
-
- /**
- * Gets the FGameplayTag that corresponds to the TagName
- *
- * @param TagName The Name of the tag to search for
- * @param ErrorIfNotfound: ensure() that tag exists.
- *
- * @return Will return the corresponding FGameplayTag or an empty one if not found.
- */
- GAMEPLAYTAGS_API FGameplayTag RequestGameplayTag(FName TagName, bool ErrorIfNotFound=true) const;
-
- /**
- * Returns true if this is a valid gameplay tag string (foo.bar.baz). If false, it will fill
- * @param TagString String to check for validity
- * @param OutError If non-null and string invalid, will fill in with an error message
- * @param OutFixedString If non-null and string invalid, will attempt to fix. Will be empty if no fix is possible
- * @return True if this can be added to the tag dictionary, false if there's a syntax error
- */
- GAMEPLAYTAGS_API bool IsValidGameplayTagString(const TCHAR* TagString, FText* OutError = nullptr, FString* OutFixedString = nullptr);
- GAMEPLAYTAGS_API bool IsValidGameplayTagString(const FString& TagString, FText* OutError = nullptr, FString* OutFixedString = nullptr);
- GAMEPLAYTAGS_API bool IsValidGameplayTagString(const FStringView& TagString, FText* OutError = nullptr, FStringBuilderBase* OutFixedString = nullptr);
-
- /**
- * Searches for a gameplay tag given a partial string. This is slow and intended mainly for console commands/utilities to make
- * developer life's easier. This will attempt to match as best as it can. If you pass "A.b" it will match on "A.b." before it matches "a.b.c".
- */
- GAMEPLAYTAGS_API FGameplayTag FindGameplayTagFromPartialString_Slow(FString PartialString) const;
-
- /**
- * Registers the given name as a gameplay tag, and tracks that it is being directly referenced from code
- * This can only be called during engine initialization, the table needs to be locked down before replication
- *
- * @param TagName The Name of the tag to add
- * @param TagDevComment The developer comment clarifying the usage of the tag
- *
- * @return Will return the corresponding FGameplayTag
- */
- GAMEPLAYTAGS_API FGameplayTag AddNativeGameplayTag(FName TagName, const FString& TagDevComment = TEXT("(Native)"));
-
-private:
- // Only callable from FNativeGameplayTag, these functions do less error checking and can happen after initial tag loading is done
- GAMEPLAYTAGS_API void AddNativeGameplayTag(FNativeGameplayTag* TagSource);
- GAMEPLAYTAGS_API void RemoveNativeGameplayTag(const FNativeGameplayTag* TagSource);
-
-public:
- /** Call to flush the list of native tags, once called it is unsafe to add more */
- GAMEPLAYTAGS_API void DoneAddingNativeTags();
-
- /** This is a delegate that is called during initialization/initial loading and signals the last chance to add tags before we are considered to be fully loaded (all tags registered). */
- static GAMEPLAYTAGS_API FSimpleMulticastDelegate& OnLastChanceToAddNativeTags();
-
- /**
- * Register a callback for when native tags are done being added (this is also a safe point to consider that the gameplay tags have fully been initialized).
- * Or, if the native tags have already been added (and thus we have registered all valid tags), then execute this Delegate immediately.
- * This is useful if your code is potentially executed during load time, and therefore any tags in your block of code could be not-yet-loaded, but possibly valid after being loaded.
- */
- GAMEPLAYTAGS_API FDelegateHandle CallOrRegister_OnDoneAddingNativeTagsDelegate(const FSimpleMulticastDelegate::FDelegate& Delegate) const;
-
- /**
- * Gets a Tag Container containing the supplied tag and all of its parents as explicit tags.
- * For example, passing in x.y.z would return a tag container with x.y.z, x.y, and x.
- * This will only work for tags that have been properly registered.
- *
- * @param GameplayTag The tag to use at the child most tag for this container
- *
- * @return A tag container with the supplied tag and all its parents added explicitly, or an empty container if that failed
- */
- GAMEPLAYTAGS_API FGameplayTagContainer RequestGameplayTagParents(const FGameplayTag& GameplayTag) const;
-
- /**
- * Fills in an array of gameplay tags with all of tags that are the parents of the passed in tag.
- * For example, passing in x.y.z would add x.y and x to UniqueParentTags if they was not already there.
- * This is used by the GameplayTagContainer code and may work for unregistered tags depending on serialization settings.
- *
- * @param GameplayTag The gameplay tag to extract parent tags from
- * @param UniqueParentTags A list of parent tags that will be added to if necessary
- *
- * @return true if any tags were added to UniqueParentTags
- */
- GAMEPLAYTAGS_API bool ExtractParentTags(const FGameplayTag& GameplayTag, TArray& UniqueParentTags) const;
-
- /**
- * Gets a Tag Container containing the all tags in the hierarchy that are children of this tag. Does not return the original tag
- *
- * @param GameplayTag The Tag to use at the parent tag
- *
- * @return A Tag Container with the supplied tag and all its parents added explicitly
- */
- GAMEPLAYTAGS_API FGameplayTagContainer RequestGameplayTagChildren(const FGameplayTag& GameplayTag) const;
-
- /** Returns direct parent GameplayTag of this GameplayTag, calling on x.y will return x */
- GAMEPLAYTAGS_API FGameplayTag RequestGameplayTagDirectParent(const FGameplayTag& GameplayTag) const;
-
- UE_DEPRECATED(5.4, "This function is not threadsafe, use FindTagNode or FGameplayTag::GetSingleTagContainer")
- inline const FGameplayTagContainer* GetSingleTagContainer(const FGameplayTag& GameplayTag) const
- {
- return GetSingleTagContainerPtr(GameplayTag);
- }
-
- /**
- * Checks node tree to see if a FGameplayTagNode with the tag exists
- *
- * @param TagName The name of the tag node to search for
- *
- * @return A shared pointer to the FGameplayTagNode found, or NULL if not found.
- */
- inline TSharedPtr FindTagNode(const FGameplayTag& GameplayTag) const
- {
- UE::TScopeLock Lock(GameplayTagMapCritical);
-
- const TSharedPtr* Node = GameplayTagNodeMap.Find(GameplayTag);
-
- if (Node)
- {
- return *Node;
- }
-#if WITH_EDITOR
- // Check redirector
- if (GIsEditor && GameplayTag.IsValid())
- {
- FGameplayTag RedirectedTag = GameplayTag;
-
- RedirectSingleGameplayTag(RedirectedTag, nullptr);
-
- Node = GameplayTagNodeMap.Find(RedirectedTag);
-
- if (Node)
- {
- return *Node;
- }
- }
-#endif
- return nullptr;
- }
-
- /**
- * Checks node tree to see if a FGameplayTagNode with the name exists
- *
- * @param TagName The name of the tag node to search for
- *
- * @return A shared pointer to the FGameplayTagNode found, or NULL if not found.
- */
- inline TSharedPtr FindTagNode(FName TagName) const
- {
- FGameplayTag PossibleTag(TagName);
- return FindTagNode(PossibleTag);
- }
-
- /** Loads the tag tables referenced in the GameplayTagSettings object */
- GAMEPLAYTAGS_API void LoadGameplayTagTables(bool bAllowAsyncLoad = false);
-
- /** Loads tag inis contained in the specified path, passes an optional PluginConfigCache to speed up disk searches */
- GAMEPLAYTAGS_API void AddTagIniSearchPath(const FString& RootDir, const TSet* PluginConfigsCache = nullptr);
-
- /** Tries to remove the specified search path, will return true if anything was removed */
- GAMEPLAYTAGS_API bool RemoveTagIniSearchPath(const FString& RootDir);
-
- /** Gets all the current directories to look for tag sources in */
- GAMEPLAYTAGS_API void GetTagSourceSearchPaths(TArray& OutPaths);
-
- /** Gets the number of tag source search paths */
- GAMEPLAYTAGS_API int32 GetNumTagSourceSearchPaths();
-
- /** Helper function to construct the gameplay tag tree */
- GAMEPLAYTAGS_API void ConstructGameplayTagTree();
-
- /** Helper function to destroy the gameplay tag tree */
- GAMEPLAYTAGS_API void DestroyGameplayTagTree();
-
- /** Splits a tag such as x.y.z into an array of names {x,y,z} */
- GAMEPLAYTAGS_API void SplitGameplayTagFName(const FGameplayTag& Tag, TArray& OutNames) const;
-
- /** Gets the list of all registered tags, setting OnlyIncludeDictionaryTags will exclude implicitly added tags if possible */
- GAMEPLAYTAGS_API void RequestAllGameplayTags(FGameplayTagContainer& TagContainer, bool OnlyIncludeDictionaryTags) const;
-
- /** Returns true if if the passed in name is in the tag dictionary and can be created */
- GAMEPLAYTAGS_API bool ValidateTagCreation(FName TagName) const;
-
- /** Returns the tag source for a given tag source name and type, or null if not found */
- GAMEPLAYTAGS_API const FGameplayTagSource* FindTagSource(FName TagSourceName) const;
-
- /** Returns the tag source for a given tag source name and type, or null if not found */
- GAMEPLAYTAGS_API FGameplayTagSource* FindTagSource(FName TagSourceName);
-
- /** Fills in an array with all tag sources of a specific type */
- GAMEPLAYTAGS_API void FindTagSourcesWithType(EGameplayTagSourceType TagSourceType, TArray& OutArray) const;
-
- GAMEPLAYTAGS_API void FindTagsWithSource(FStringView PackageNameOrPath, TArray& OutTags) const;
-
- /**
- * Check to see how closely two FGameplayTags match. Higher values indicate more matching terms in the tags.
- *
- * @param GameplayTagOne The first tag to compare
- * @param GameplayTagTwo The second tag to compare
- *
- * @return the length of the longest matching pair
- */
- GAMEPLAYTAGS_API int32 GameplayTagsMatchDepth(const FGameplayTag& GameplayTagOne, const FGameplayTag& GameplayTagTwo) const;
-
- /** Returns the number of parents a particular gameplay tag has. Useful as a quick way to determine which tags may
- * be more "specific" than other tags without comparing whether they are in the same hierarchy or anything else.
- * Example: "TagA.SubTagA" has 2 Tag Nodes. "TagA.SubTagA.LeafTagA" has 3 Tag Nodes.
- */
- GAMEPLAYTAGS_API int32 GetNumberOfTagNodes(const FGameplayTag& GameplayTag) const;
-
- /** Returns true if we should import tags from UGameplayTagsSettings objects (configured by INI files) */
- GAMEPLAYTAGS_API bool ShouldImportTagsFromINI() const;
-
- /** Should we print loading errors when trying to load invalid tags */
- bool ShouldWarnOnInvalidTags() const
- {
- return bShouldWarnOnInvalidTags;
- }
-
- /** Should we clear references to invalid tags loaded/saved in the editor */
- UE_DEPRECATED(5.5, "We should never clear invalid tags as we're not guaranteed the required plugin has loaded")
- bool ShouldClearInvalidTags() const
- {
- return false;
- }
-
- /** Should use fast replication */
- bool ShouldUseFastReplication() const
- {
- return bUseFastReplication;
- }
-
- /** Should use dynamic replication (Gameplay Tags need not match between client/server) */
- bool ShouldUseDynamicReplication() const
- {
- return !bUseFastReplication && bUseDynamicReplication;
- }
-
- /** If we are allowed to unload tags */
- GAMEPLAYTAGS_API bool ShouldUnloadTags() const;
-
- /** Pushes an override that supersedes bShouldAllowUnloadingTags to allow/disallow unloading of GameplayTags in controlled scenarios */
- GAMEPLAYTAGS_API void SetShouldUnloadTagsOverride(bool bShouldUnloadTags);
-
- /** Clears runtime overrides, reverting to bShouldAllowUnloadingTags when determining GameplayTags unload behavior */
- GAMEPLAYTAGS_API void ClearShouldUnloadTagsOverride();
-
- /** Pushes an override that suppresses calls to HandleGameplayTagTreeChanged that would result in a complete rebuild of the GameplayTag tree */
- GAMEPLAYTAGS_API void SetShouldDeferGameplayTagTreeRebuilds(bool bShouldDeferRebuilds);
-
- /** Stops suppressing GameplayTag tree rebuilds and (optionally) rebuilds the tree */
- GAMEPLAYTAGS_API void ClearShouldDeferGameplayTagTreeRebuilds(bool bRebuildTree);
-
- /** Returns the hash of NetworkGameplayTagNodeIndex */
- uint32 GetNetworkGameplayTagNodeIndexHash() const { VerifyNetworkIndex(); return NetworkGameplayTagNodeIndexHash; }
-
- /** Returns a list of the ini files that contain restricted tags */
- GAMEPLAYTAGS_API void GetRestrictedTagConfigFiles(TArray& RestrictedConfigFiles) const;
-
- /** Returns a list of the source files that contain restricted tags */
- GAMEPLAYTAGS_API void GetRestrictedTagSources(TArray& Sources) const;
-
- /** Returns a list of the owners for a restricted tag config file. May be empty */
- GAMEPLAYTAGS_API void GetOwnersForTagSource(const FString& SourceName, TArray& OutOwners) const;
-
- /** Notification that a tag container has been loaded via serialize */
- GAMEPLAYTAGS_API void GameplayTagContainerLoaded(FGameplayTagContainer& Container, FProperty* SerializingProperty) const;
-
- /** Notification that a gameplay tag has been loaded via serialize */
- GAMEPLAYTAGS_API void SingleGameplayTagLoaded(FGameplayTag& Tag, FProperty* SerializingProperty) const;
-
- /** Handles redirectors for an entire container, will also error on invalid tags */
- GAMEPLAYTAGS_API void RedirectTagsForContainer(FGameplayTagContainer& Container, FProperty* SerializingProperty) const;
-
- /** Handles redirectors for a single tag, will also error on invalid tag. This is only called for when individual tags are serialized on their own */
- GAMEPLAYTAGS_API void RedirectSingleGameplayTag(FGameplayTag& Tag, FProperty* SerializingProperty) const;
-
- /** Handles establishing a single tag from an imported tag name (accounts for redirects too). Called when tags are imported via text. */
- GAMEPLAYTAGS_API bool ImportSingleGameplayTag(FGameplayTag& Tag, FName ImportedTagName, bool bImportFromSerialize = false) const;
-
- /** Gets a tag name from net index and vice versa, used for replication efficiency */
- GAMEPLAYTAGS_API FName GetTagNameFromNetIndex(FGameplayTagNetIndex Index) const;
- GAMEPLAYTAGS_API FGameplayTagNetIndex GetNetIndexFromTag(const FGameplayTag &InTag) const;
-
- /** Cached number of bits we need to replicate tags. That is, Log2(Number of Tags). Will always be <= 16. */
- int32 GetNetIndexTrueBitNum() const { VerifyNetworkIndex(); return NetIndexTrueBitNum; }
-
- /** The length in bits of the first segment when net serializing tags. We will serialize NetIndexFirstBitSegment + 1 bit to indicatore "more" (more = second segment that is NetIndexTrueBitNum - NetIndexFirstBitSegment) */
- int32 GetNetIndexFirstBitSegment() const { VerifyNetworkIndex(); return NetIndexFirstBitSegment; }
-
- /** This is the actual value for an invalid tag "None". This is computed at runtime as (Total number of tags) + 1 */
- FGameplayTagNetIndex GetInvalidTagNetIndex() const { VerifyNetworkIndex(); return InvalidTagNetIndex; }
-
- const TArray>& GetNetworkGameplayTagNodeIndex() const { VerifyNetworkIndex(); return NetworkGameplayTagNodeIndex; }
-
- DECLARE_TS_MULTICAST_DELEGATE_OneParam(FOnGameplayTagLoaded, const FGameplayTag& /*Tag*/)
- FOnGameplayTagLoaded OnGameplayTagLoadedDelegate;
-
- /** Numbers of bits to use for replicating container size. This can be set via config. */
- int32 NumBitsForContainerSize;
-
- GAMEPLAYTAGS_API void PushDeferOnGameplayTagTreeChangedBroadcast();
- GAMEPLAYTAGS_API void PopDeferOnGameplayTagTreeChangedBroadcast();
-
-private:
- /** Cached number of bits we need to replicate tags. That is, Log2(Number of Tags). Will always be <= 16. */
- int32 NetIndexTrueBitNum;
-
- /** The length in bits of the first segment when net serializing tags. We will serialize NetIndexFirstBitSegment + 1 bit to indicatore "more" (more = second segment that is NetIndexTrueBitNum - NetIndexFirstBitSegment) */
- int32 NetIndexFirstBitSegment;
-
- /** This is the actual value for an invalid tag "None". This is computed at runtime as (Total number of tags) + 1 */
- FGameplayTagNetIndex InvalidTagNetIndex;
-
-public:
-
-#if WITH_EDITOR
- /** Gets a Filtered copy of the GameplayRootTags Array based on the comma delimited filter string passed in */
- GAMEPLAYTAGS_API void GetFilteredGameplayRootTags(const FString& InFilterString, TArray< TSharedPtr >& OutTagArray) const;
-
- /** Returns "Categories" meta property from given handle, used for filtering by tag widget */
- GAMEPLAYTAGS_API FString GetCategoriesMetaFromPropertyHandle(TSharedPtr PropertyHandle) const;
-
- /** Helper function, made to be called by custom OnGetCategoriesMetaFromPropertyHandle handlers */
- static GAMEPLAYTAGS_API FString StaticGetCategoriesMetaFromPropertyHandle(TSharedPtr PropertyHandle);
-
- /** Returns "Categories" meta property from given field, used for filtering by tag widget */
- template
- static FString GetCategoriesMetaFromField(TFieldType* Field)
- {
- check(Field);
- if (Field->HasMetaData(NAME_Categories))
- {
- return Field->GetMetaData(NAME_Categories);
- }
- else if (Field->HasMetaData(NAME_GameplayTagFilter))
- {
- return Field->GetMetaData(NAME_GameplayTagFilter);
- }
- return FString();
- }
-
- /** Returns "GameplayTagFilter" meta property from given function, used for filtering by tag widget for any parameters of the function that end up as BP pins */
- static GAMEPLAYTAGS_API FString GetCategoriesMetaFromFunction(const UFunction* Func, FName ParamName = NAME_None);
-
- /** Gets a list of all gameplay tag nodes added by the specific source */
- GAMEPLAYTAGS_API void GetAllTagsFromSource(FName TagSource, TArray< TSharedPtr >& OutTagArray) const;
-
- /** Returns true if this tag was explicitly registered, this is false for implictly added parent tags */
- GAMEPLAYTAGS_API bool IsDictionaryTag(FName TagName) const;
-
- /** Returns information about tag. If not found return false */
- GAMEPLAYTAGS_API bool GetTagEditorData(FName TagName, FString& OutComment, FName &OutFirstTagSource, bool& bOutIsTagExplicit, bool &bOutIsRestrictedTag, bool &bOutAllowNonRestrictedChildren) const;
-
- /** Returns information about tag. If not found return false */
- GAMEPLAYTAGS_API bool GetTagEditorData(FName TagName, FString& OutComment, TArray& OutTagSources, bool& bOutIsTagExplicit, bool &bOutIsRestrictedTag, bool &bOutAllowNonRestrictedChildren) const;
-
- /** This is called after EditorRefreshGameplayTagTree. Useful if you need to do anything editor related when tags are added or removed */
- static GAMEPLAYTAGS_API FSimpleMulticastDelegate OnEditorRefreshGameplayTagTree;
-
- /** Refresh the gameplaytag tree due to an editor change */
- GAMEPLAYTAGS_API void EditorRefreshGameplayTagTree();
-
- /** Suspends EditorRefreshGameplayTagTree requests */
- GAMEPLAYTAGS_API void SuspendEditorRefreshGameplayTagTree(FGuid SuspendToken);
-
- /** Resumes EditorRefreshGameplayTagTree requests; triggers a refresh if a request was made while it was suspended */
- GAMEPLAYTAGS_API void ResumeEditorRefreshGameplayTagTree(FGuid SuspendToken);
-
- /** Gets a Tag Container containing all of the tags in the hierarchy that are children of this tag, and were explicitly added to the dictionary */
- GAMEPLAYTAGS_API FGameplayTagContainer RequestGameplayTagChildrenInDictionary(const FGameplayTag& GameplayTag) const;
-#if WITH_EDITORONLY_DATA
- /** Gets a Tag Container containing all of the tags in the hierarchy that are children of this tag, were explicitly added to the dictionary, and do not have any explicitly added tags between them and the specified tag */
- GAMEPLAYTAGS_API FGameplayTagContainer RequestGameplayTagDirectDescendantsInDictionary(const FGameplayTag& GameplayTag, EGameplayTagSelectionType SelectionType) const;
-#endif // WITH_EDITORONLY_DATA
-
-
- DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGameplayTagDoubleClickedEditor, FGameplayTag, FSimpleMulticastDelegate& /* OUT */)
- FOnGameplayTagDoubleClickedEditor OnGatherGameplayTagDoubleClickedEditor;
-
- /** Chance to dynamically change filter string based on a property handle */
- DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGetCategoriesMetaFromPropertyHandle, TSharedPtr, FString& /* OUT */)
- FOnGetCategoriesMetaFromPropertyHandle OnGetCategoriesMetaFromPropertyHandle;
-
- /** Allows dynamic hiding of gameplay tags in SGameplayTagWidget. Allows higher order structs to dynamically change which tags are visible based on its own data */
- DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnFilterGameplayTagChildren, const FString& /** FilterString */, TSharedPtr& /* TagNode */, bool& /* OUT OutShouldHide */)
- FOnFilterGameplayTagChildren OnFilterGameplayTagChildren;
-
- /*
- * This is a container to filter out gameplay tags when they are invalid or when they don't meet the filter string
- * If used from editor to filter out tags when picking them the FilterString is optional and the ReferencingPropertyHandle is required
- * If used to validate an asset / assets you can provide the TagSourceAssets. The FilterString and ReferencingPropertyHandle is optional
- */
- struct FFilterGameplayTagContext
- {
- const FString& FilterString;
- const TSharedPtr& TagNode;
- const FGameplayTagSource* TagSource;
- const TSharedPtr ReferencingPropertyHandle;
- const TArray TagSourceAssets;
-
- FFilterGameplayTagContext(const FString& InFilterString, const TSharedPtr& InTagNode, const FGameplayTagSource* InTagSource, const TSharedPtr& InReferencingPropertyHandle)
- : FilterString(InFilterString), TagNode(InTagNode), TagSource(InTagSource), ReferencingPropertyHandle(InReferencingPropertyHandle)
- {}
-
- FFilterGameplayTagContext(const TSharedPtr& InTagNode, const FGameplayTagSource* InTagSource, const TArray& InTagSourceAssets, const FString& InFilterString = FString())
- : FilterString(InFilterString), TagNode(InTagNode), TagSource(InTagSource), TagSourceAssets(InTagSourceAssets)
- {}
- };
-
- /*
- * Allows dynamic hiding of gameplay tags in SGameplayTagWidget. Allows higher order structs to dynamically change which tags are visible based on its own data
- * Applies to all tags, and has more context than OnFilterGameplayTagChildren
- */
- DECLARE_MULTICAST_DELEGATE_TwoParams(FOnFilterGameplayTag, const FFilterGameplayTagContext& /** InContext */, bool& /* OUT OutShouldHide */)
- FOnFilterGameplayTag OnFilterGameplayTag;
-
- GAMEPLAYTAGS_API void NotifyGameplayTagDoubleClickedEditor(FString TagName);
-
- GAMEPLAYTAGS_API bool ShowGameplayTagAsHyperLinkEditor(FString TagName);
-
- /**
- * Used for incremental cooking. Create an FCookDependency that reports tags that have been read from ini.
- * Packages that pass this dependency to AddCookLoadDependency or AddCookSaveDependency in their OnCookEvent or
- * (if Ar.IsCooking()) Serialize function will be invalidated and recooked by the incremental cook whenever those
- * tags change.
- */
- GAMEPLAYTAGS_API UE::Cook::FCookDependency CreateCookDependency();
-
- /** Implementation of console command GameplayTags.DumpSources */
- void DumpSources(FOutputDevice& Out) const;
-#endif //WITH_EDITOR
-
- GAMEPLAYTAGS_API void PrintReplicationIndices();
- int32 GetNumGameplayTagNodes() const { return GameplayTagNodeMap.Num(); }
-
-#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
- /** Mechanism for tracking what tags are frequently replicated */
-
- GAMEPLAYTAGS_API void PrintReplicationFrequencyReport();
- GAMEPLAYTAGS_API void NotifyTagReplicated(FGameplayTag Tag, bool WasInContainer);
-
- TMap ReplicationCountMap;
- TMap ReplicationCountMap_SingleTags;
- TMap ReplicationCountMap_Containers;
-#endif
-
-private:
-
- /** Initializes the manager */
- static GAMEPLAYTAGS_API void InitializeManager();
-
- /** finished loading/adding native tags */
- static GAMEPLAYTAGS_API FSimpleMulticastDelegate& OnDoneAddingNativeTagsDelegate();
-
- /** The Tag Manager singleton */
- static GAMEPLAYTAGS_API UGameplayTagsManager* SingletonManager;
-
- friend class FGameplayTagTest;
- friend class FGameplayEffectsTest;
- friend class FGameplayTagsModule;
- friend class FGameplayTagsEditorModule;
- friend class UGameplayTagsSettings;
- friend class SAddNewGameplayTagSourceWidget;
- friend class FNativeGameplayTag;
-
- /**
- * Helper function to get the stored TagContainer containing only this tag, which has searchable ParentTags
- * NOTE: This function is not threadsafe and should only be used in code that locks the tag map critical section
- * @param GameplayTag Tag to get single container of
- * @return Pointer to container with this tag
- */
- inline const FGameplayTagContainer* GetSingleTagContainerPtr(const FGameplayTag& GameplayTag) const
- {
- // Doing this with pointers to avoid a shared ptr reference count change
- const TSharedPtr* Node = GameplayTagNodeMap.Find(GameplayTag);
-
- if (Node)
- {
- return &(*Node)->GetSingleTagContainer();
- }
-#if WITH_EDITOR
- // Check redirector
- if (GIsEditor && GameplayTag.IsValid())
- {
- FGameplayTag RedirectedTag = GameplayTag;
-
- RedirectSingleGameplayTag(RedirectedTag, nullptr);
-
- Node = GameplayTagNodeMap.Find(RedirectedTag);
-
- if (Node)
- {
- return &(*Node)->GetSingleTagContainer();
- }
- }
-#endif
- return nullptr;
- }
-
-
- /**
- * Helper function to insert a tag into a tag node array
- *
- * @param Tag Short name of tag to insert
- * @param FullTag Full tag, passed in for performance
- * @param ParentNode Parent node, if any, for the tag
- * @param NodeArray Node array to insert the new node into, if necessary (if the tag already exists, no insertion will occur)
- * @param SourceName File tag was added from
- * @param DevComment Comment from developer about this tag
- * @param bIsExplicitTag Is the tag explicitly defined or is it implied by the existence of a child tag
- * @param bIsRestrictedTag Is the tag a restricted tag or a regular gameplay tag
- * @param bAllowNonRestrictedChildren If the tag is a restricted tag, can it have regular gameplay tag children or should all of its children be restricted tags as well?
- *
- * @return Index of the node of the tag
- */
- GAMEPLAYTAGS_API int32 InsertTagIntoNodeArray(FName Tag, FName FullTag, TSharedPtr ParentNode, TArray< TSharedPtr >& NodeArray, FName SourceName, const FString& DevComment, bool bIsExplicitTag, bool bIsRestrictedTag, bool bAllowNonRestrictedChildren);
-
- /** Helper function to populate the tag tree from each table */
- GAMEPLAYTAGS_API void PopulateTreeFromDataTable(class UDataTable* Table);
-
- GAMEPLAYTAGS_API void AddTagTableRow(const FGameplayTagTableRow& TagRow, FName SourceName, bool bIsRestrictedTag = false);
-
- GAMEPLAYTAGS_API void AddChildrenTags(FGameplayTagContainer& TagContainer, TSharedPtr GameplayTagNode, bool RecurseAll=true, bool OnlyIncludeDictionaryTags=false) const;
-
- GAMEPLAYTAGS_API void AddRestrictedGameplayTagSource(const FString& FileName);
-
- GAMEPLAYTAGS_API void AddTagsFromAdditionalLooseIniFiles(const TArray& IniFileList);
-
- /**
- * Helper function for GameplayTagsMatch to get all parents when doing a parent match,
- * NOTE: Must never be made public as it uses the FNames which should never be exposed
- *
- * @param NameList The list we are adding all parent complete names too
- * @param GameplayTag The current Tag we are adding to the list
- */
- GAMEPLAYTAGS_API void GetAllParentNodeNames(TSet& NamesList, TSharedPtr GameplayTag) const;
-
- /** Returns the tag source for a given tag source name, or null if not found */
- GAMEPLAYTAGS_API FGameplayTagSource* FindOrAddTagSource(FName TagSourceName, EGameplayTagSourceType SourceType, const FString& RootDirToUse = FString());
-
- /** Constructs the net indices for each tag */
- GAMEPLAYTAGS_API void ConstructNetIndex();
-
- /** Marks all of the nodes that descend from CurNode as having an ancestor node that has a source conflict. */
- GAMEPLAYTAGS_API void MarkChildrenOfNodeConflict(TSharedPtr CurNode);
-
- void VerifyNetworkIndex() const
- {
- if (!bUseFastReplication)
- {
- UE_LOG(LogGameplayTags, Warning, TEXT("%hs called when not using FastReplication (not rebuilding the fast replication cache)"), __func__);
- }
- else if (bNetworkIndexInvalidated)
- {
- const_cast(this)->ConstructNetIndex();
- }
- }
-
- void InvalidateNetworkIndex() { bNetworkIndexInvalidated = true; }
-
- /** Called in both editor and game when the tag tree changes during startup or editing */
- GAMEPLAYTAGS_API void BroadcastOnGameplayTagTreeChanged();
-
- /** Call after modifying the tag tree nodes, this will either call the full editor refresh or a limited game refresh */
- GAMEPLAYTAGS_API void HandleGameplayTagTreeChanged(bool bRecreateTree);
-
-#if WITH_EDITOR
- void UpdateIncrementalCookHash(UE::Cook::ICookInfo& CookInfo);
-#endif
-
- // Tag Sources
- ///////////////////////////////////////////////////////
-
- /** These are the old native tags that use to be resisted via a function call with no specific site/ownership. */
- TSet LegacyNativeTags;
-
- /** Map of all config directories to load tag inis from */
- TMap RegisteredSearchPaths;
-
- /** Roots of gameplay tag nodes */
- TSharedPtr GameplayRootTag;
-
- /** Map of Tags to Nodes - Internal use only. FGameplayTag is inside node structure, do not use FindKey! */
- TMap> GameplayTagNodeMap;
-
- /** Our aggregated, sorted list of commonly replicated tags. These tags are given lower indices to ensure they replicate in the first bit segment. */
- TArray CommonlyReplicatedTags;
-
- /** Map of gameplay tag source names to source objects */
- UPROPERTY()
- TMap TagSources;
-
- TSet RestrictedGameplayTagSourceNames;
-
- bool bIsConstructingGameplayTagTree = false;
-
- /** Cached runtime value for whether we are using fast replication or not. Initialized from config setting. */
- bool bUseFastReplication;
-
- /** Cached runtime value for whether we are using dynamic replication or not. Initialized from the config setting. */
- bool bUseDynamicReplication;
-
- /** Cached runtime value for whether we should warn when loading invalid tags */
- bool bShouldWarnOnInvalidTags;
-
- /** Cached runtime value for whether we should allow unloading of tags */
- bool bShouldAllowUnloadingTags;
-
- /** Augments usage of bShouldAllowUnloadingTags to allow runtime overrides to allow/disallow unloading of GameplayTags in controlled scenarios */
- TOptional ShouldAllowUnloadingTagsOverride;
-
- /** Used to suppress calls to HandleGameplayTagTreeChanged that would result in a complete rebuild of the GameplayTag tree*/
- TOptional ShouldDeferGameplayTagTreeRebuilds;
-
- /** True if native tags have all been added and flushed */
- bool bDoneAddingNativeTags;
-
- int32 bDeferBroadcastOnGameplayTagTreeChanged = 0;
- bool bShouldBroadcastDeferredOnGameplayTagTreeChanged = false;
-
- /** If true, an action that would require a tree rebuild was performed during initialization **/
- bool bNeedsTreeRebuildOnDoneAddingGameplayTags = false;
-
- /** String with outlawed characters inside tags */
- FString InvalidTagCharacters;
-
- // This critical section is to handle an issue where tag requests come from another thread when async loading from a background thread in FGameplayTagContainer::Serialize.
- // This class is not generically threadsafe, but this should be locked by any operation that could update something read by a background thread.
- mutable FTransactionallySafeCriticalSection GameplayTagMapCritical;
-
-#if WITH_EDITOR
- // Transient editor-only tags to support quick-iteration PIE workflows
- TSet TransientEditorTags;
-
- TSet EditorRefreshGameplayTagTreeSuspendTokens;
- bool bEditorRefreshGameplayTagTreeRequestedDuringSuspend = false;
-
- FBlake3Hash IncrementalCookHash;
-#endif //if WITH_EDITOR
-
- /** Sorted list of nodes, used for network replication */
- TArray> NetworkGameplayTagNodeIndex;
-
- uint32 NetworkGameplayTagNodeIndexHash;
-
- bool bNetworkIndexInvalidated = true;
-
- /** Holds all of the valid gameplay-related tags that can be applied to assets */
- UPROPERTY()
- TArray> GameplayTagTables;
-
- GAMEPLAYTAGS_API const static FName NAME_Categories;
- GAMEPLAYTAGS_API const static FName NAME_GameplayTagFilter;
-
- friend class UGameplayTagsManagerIncrementalCookFunctions;
-};
diff --git a/samples/GeomUtils.h b/samples/GeomUtils.h
deleted file mode 100644
index e661ad5..0000000
--- a/samples/GeomUtils.h
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include "Containers/ContainersFwd.h"
-#include "Math/UnrealMathSSE.h"
-#include "Math/Vector.h"
-#include "Math/Vector2D.h"
-
-namespace UE::AI
-{
- /** @return 2D cross product. Using X and Y components of 3D vectors. */
- inline FVector::FReal Cross2D(const FVector& A, const FVector& B)
- {
- return A.X * B.Y - A.Y * B.X;
- }
-
- /** @return 2D cross product. */
- inline FVector2D::FReal Cross2D(const FVector2D& A, const FVector2D& B)
- {
- return A.X * B.Y - A.Y * B.X;
- }
-
- /** @return 2D area of triangle. Using X and Y components of 3D vectors. */
- inline FVector::FReal TriArea2D(const FVector& A, const FVector& B, const FVector& C)
- {
- const FVector AB = B - A;
- const FVector AC = C - A;
- return (AC.X * AB.Y - AB.X * AC.Y) * 0.5;
- }
-
- /** @return 2D area of triangle. */
- inline FVector2D::FReal TriArea2D(const FVector2D& A, const FVector2D& B, const FVector2D& C)
- {
- const FVector2D AB = B - A;
- const FVector2D AC = C - A;
- return (AC.X * AB.Y - AB.X * AC.Y) * 0.5;
- }
-
- /** @return value in range [0..1] of the 'Point' project on segment 'Start-End'. Using X and Y components of 3D vectors. */
- inline FVector2D::FReal ProjectPointOnSegment2D(const FVector Point, const FVector Start, const FVector End)
- {
- using FReal = FVector::FReal;
-
- const FVector2D Seg(End - Start);
- const FVector2D Dir(Point - Start);
- const FReal D = Seg.SquaredLength();
- const FReal T = FVector2D::DotProduct(Seg, Dir);
-
- if (T < 0.0)
- {
- return 0.0;
- }
- else if (T > D)
- {
- return 1.0;
- }
-
- return D > UE_KINDA_SMALL_NUMBER ? (T / D) : 0.0;
- }
-
- /** @return value of the 'Point' project on infinite line defined by segment 'Start-End'. Using X and Y components of 3D vectors. */
- inline FVector::FReal ProjectPointOnLine2D(const FVector Point, const FVector Start, const FVector End)
- {
- using FReal = FVector::FReal;
-
- const FVector2D Seg(End - Start);
- const FVector2D Dir(Point - Start);
- const FReal D = Seg.SquaredLength();
- const FReal T = FVector2D::DotProduct(Seg, Dir);
- return D > UE_KINDA_SMALL_NUMBER ? (T / D) : 0.0;
- }
-
- /** @return signed distance of the 'Point' to infinite line defined by segment 'Start-End'. Using X and Y components of 3D vectors. */
- inline FVector::FReal SignedDistancePointLine2D(const FVector Point, const FVector Start, const FVector End)
- {
- using FReal = FVector::FReal;
-
- const FVector2D Seg(End - Start);
- const FVector2D Dir(Point - Start);
- const FReal Nom = Cross2D(Seg, Dir);
- const FReal Den = Seg.SquaredLength();
- const FReal Dist = Den > UE_KINDA_SMALL_NUMBER ? (Nom / FMath::Sqrt(Den)) : 0.0;
- return Dist;
- }
-
- /**
- * Intersects infinite lines defined by segments A and B in 2D. Using X and Y components of 3D vectors.
- * @param StartA start point of segment A
- * @param EndA end point of segment A
- * @param StartB start point of segment B
- * @param EndB end point of segment B
- * @param OutTA intersection value along segment A
- * @param OutTB intersection value along segment B
- * @return if segments A and B intersect in 2D
- */
- inline bool IntersectLineLine2D(const FVector& StartA, const FVector& EndA, const FVector& StartB, const FVector& EndB, FVector2D::FReal& OutTA, FVector2D::FReal& OutTB)
- {
- using FReal = FVector::FReal;
-
- const FVector U = EndA - StartA;
- const FVector V = EndB - StartB;
- const FVector W = StartA - StartB;
-
- const FReal D = Cross2D(U, V);
- if (FMath::Abs(D) < UE_KINDA_SMALL_NUMBER)
- {
- OutTA = 0.0;
- OutTB = 0.0;
- return false;
- }
-
- OutTA = Cross2D(V, W) / D;
- OutTB = Cross2D(U, W) / D;
-
- return true;
- }
-
- /**
- * Calculates intersection of segment Start-End with convex polygon Poly in 2D. Using X and Y components of 3D vectors.
- * @param Start start point of the segment
- * @param End end point of the segment
- * @param Poly convex polygon
- * @param OutTMin value along the segment of the first intersection point [0..1]
- * @param OutTMax value along the segment of the second intersection point [0..1]
- * @param OutSegMin index of the polygon segment of the first intersection point
- * @param OutSegMax index of the polygon segment of the second intersection point
- * @return true if the segment inside or intersects with the polygon.
- */
- extern AIMODULE_API bool IntersectSegmentPoly2D(const FVector& Start, const FVector& End, TConstArrayView Poly,
- FVector2D::FReal& OutTMin, FVector2D::FReal& OutTMax, int32& OutSegMin, int32& OutSegMax);
-
- /**
- * Interpolates bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
- * @param UV interpolation coordinates [0..1] range
- * @param VertexA first corner
- * @param VertexB second corner
- * @param VertexC third corner
- * @param VertexD fourth corner
- * @return interpolated value.
- */
- inline FVector Bilinear(const FVector2D UV, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD)
- {
- const FVector AB = FMath::Lerp(VertexA, VertexB, UV.X);
- const FVector CD = FMath::Lerp(VertexD, VertexC, UV.X);
- return FMath::Lerp(AB, CD, UV.Y);
- }
-
- /**
- * Finds the UV coordinates of the 'Point' on bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
- * @param Point location inside or close to the bilinear patch
- * @param VertexA first corner
- * @param VertexB second corner
- * @param VertexC third corner
- * @param VertexD fourth corner
- * @return UV interpolation coordinates of the 'Point'.
- */
- extern AIMODULE_API FVector2D InvBilinear2D(const FVector Point, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD);
-
- /**
- * Finds the UV coordinates of the 'Point' on bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
- * The UV coordinate is clamped to [0..1] range after inversion.
- * @param Point location inside or close to the bilinear patch
- * @param VertexA first corner
- * @param VertexB second corner
- * @param VertexC third corner
- * @param VertexD fourth corner
- * @return UV interpolation coordinates of the 'Point' in [0..1] range.
- */
- inline FVector2D InvBilinear2DClamped(const FVector Point, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD)
- {
- return InvBilinear2D(Point, VertexA, VertexB, VertexC, VertexD).ClampAxes(0.0, 1.0);
- }
-
-}; // UE::AI
\ No newline at end of file
diff --git a/ue_mcp_server.py b/ue_mcp_server.py
new file mode 100644
index 0000000..881ac45
--- /dev/null
+++ b/ue_mcp_server.py
@@ -0,0 +1,213 @@
+"""MCP server exposing UE documentation tools at item granularity."""
+
+import os
+import re
+import subprocess
+from pathlib import Path
+
+from mcp.server.fastmcp import FastMCP
+
+mcp = FastMCP("ue-docs")
+
+
+# ---------------------------------------------------------------------------
+# Env helpers
+# ---------------------------------------------------------------------------
+
+def _docs_root() -> Path:
+ p = os.environ.get("UE_DOCS_PATH", "")
+ if not p:
+ raise RuntimeError("UE_DOCS_PATH environment variable not set")
+ return Path(p)
+
+
+def _engine_root() -> Path:
+ p = os.environ.get("UE_ENGINE_ROOT", "")
+ if not p:
+ raise RuntimeError("UE_ENGINE_ROOT environment variable not set")
+ return Path(p)
+
+
+def _load_type_index() -> dict[str, str]:
+ """Returns {TypeName: relative_md_path}."""
+ idx = (_docs_root() / "type-index.txt").read_text()
+ result = {}
+ for line in idx.splitlines():
+ if ": " in line:
+ name, path = line.split(": ", 1)
+ result[name.strip()] = path.strip()
+ return result
+
+
+# ---------------------------------------------------------------------------
+# Section extraction helpers
+# ---------------------------------------------------------------------------
+
+def _extract_class_section(text: str, class_name: str) -> str:
+ """Return the markdown section for class_name (### `ClassName` … next ### `)."""
+ # Match the heading for this class
+ pattern = rf'^### `{re.escape(class_name)}`'
+ m = re.search(pattern, text, re.MULTILINE)
+ if not m:
+ return ""
+ start = m.start()
+ # Find the next same-level heading
+ next_h3 = re.search(r'^### `', text[m.end():], re.MULTILINE)
+ if next_h3:
+ end = m.end() + next_h3.start()
+ else:
+ end = len(text)
+ return text[start:end]
+
+
+def _build_overview(section: str) -> str:
+ """Compact overview: header block + name lists for Properties and Functions."""
+ lines = section.splitlines()
+
+ prop_names: list[str] = []
+ func_names: list[str] = [] # deduped
+ seen_funcs: set[str] = set()
+
+ header_lines: list[str] = []
+ in_props = False
+ in_funcs = False
+
+ for line in lines:
+ if line.startswith("#### Properties"):
+ in_props = True
+ in_funcs = False
+ continue
+ if line.startswith("#### Functions"):
+ in_funcs = True
+ in_props = False
+ continue
+ if line.startswith("#### ") or line.startswith("### "):
+ # Some other subsection — stop collecting
+ in_props = False
+ in_funcs = False
+
+ if in_props:
+ m = re.match(r"- `(\w+)`", line)
+ if m:
+ prop_names.append(m.group(1))
+ elif in_funcs:
+ m = re.match(r"##### `(\w+)\(", line)
+ if m:
+ name = m.group(1)
+ if name not in seen_funcs:
+ seen_funcs.add(name)
+ func_names.append(name)
+ else:
+ header_lines.append(line)
+
+ # Trim trailing blank lines from header
+ while header_lines and not header_lines[-1].strip():
+ header_lines.pop()
+
+ parts = ["\n".join(header_lines)]
+ if prop_names:
+ parts.append(f"\nProperties ({len(prop_names)}): {', '.join(prop_names)}")
+ if func_names:
+ parts.append(f"Functions ({len(func_names)}): {', '.join(func_names)}")
+
+ return "\n".join(parts)
+
+
+def _extract_member(section: str, member_name: str) -> str:
+ """Return the full doc entry for member_name (all overloads for functions)."""
+ escaped = re.escape(member_name)
+
+ # Try function first (##### `Name(`)
+ pattern = rf'^##### `{escaped}\('
+ m = re.search(pattern, section, re.MULTILINE)
+ if m:
+ start = m.start()
+ # Collect until the next ##### ` heading
+ next_h5 = re.search(r'^##### `', section[m.end():], re.MULTILINE)
+ if next_h5:
+ end = m.end() + next_h5.start()
+ else:
+ end = len(section)
+ return section[start:end].rstrip()
+
+ # Try property (- `Name`)
+ m = re.search(rf'^- `{escaped}`', section, re.MULTILINE)
+ if m:
+ return section[m.start():section.index("\n", m.start())].rstrip()
+
+ return ""
+
+
+# ---------------------------------------------------------------------------
+# Tools
+# ---------------------------------------------------------------------------
+
+@mcp.tool()
+def search_types(pattern: str) -> str:
+ """Search type-index.txt for UE types whose name matches pattern (case-insensitive).
+ Returns matching 'TypeName: path/to/File.md' lines (up to 50).
+ Use this to locate a type before calling get_class_overview or get_file."""
+ idx = _docs_root() / "type-index.txt"
+ lines = [l for l in idx.read_text().splitlines()
+ if re.search(pattern, l, re.IGNORECASE)]
+ return "\n".join(lines[:50]) or "(no matches)"
+
+
+@mcp.tool()
+def get_class_overview(class_name: str) -> str:
+ """Compact overview of a UE class/struct: description, base classes,
+ and flat lists of property and function names. Use get_member() for
+ full signatures and docs. Use get_file() if you need delegates/enums too."""
+ index = _load_type_index()
+ if class_name not in index:
+ return f"'{class_name}' not in type index. Try search_types('{class_name}')."
+ text = (_docs_root() / index[class_name]).read_text()
+ section = _extract_class_section(text, class_name)
+ if not section:
+ return f"Section for '{class_name}' not found in {index[class_name]}."
+ return _build_overview(section)
+
+
+@mcp.tool()
+def get_member(class_name: str, member_name: str) -> str:
+ """Full documentation entry for one function or property in a UE class.
+ For overloaded functions, returns all overloads together."""
+ index = _load_type_index()
+ if class_name not in index:
+ return f"'{class_name}' not found."
+ text = (_docs_root() / index[class_name]).read_text()
+ section = _extract_class_section(text, class_name)
+ if not section:
+ return f"'{class_name}' section missing."
+ entry = _extract_member(section, member_name)
+ return entry or f"'{member_name}' not found in {class_name}."
+
+
+@mcp.tool()
+def get_file(relative_path: str) -> str:
+ """Full content of a documentation .md file.
+ relative_path is relative to $UE_DOCS_PATH, as returned by search_types().
+ Use this when you need delegates, top-level enums, or multiple classes from one file."""
+ path = _docs_root() / relative_path
+ if not path.exists():
+ return f"File not found: {relative_path}"
+ return path.read_text()
+
+
+@mcp.tool()
+def search_source(pattern: str, path_hint: str = "") -> str:
+ """Grep UE source .h files for pattern. Requires $UE_ENGINE_ROOT.
+ path_hint: optional subdirectory under Engine/Source/ (e.g. 'Runtime/Engine').
+ Returns up to 40 matching lines with file:line context."""
+ root = _engine_root() / "Engine" / "Source"
+ if path_hint:
+ root = root / path_hint
+ result = subprocess.run(
+ ["grep", "-rn", "--include=*.h", "-m", "40", pattern, str(root)],
+ capture_output=True, text=True, timeout=15
+ )
+ return result.stdout or "(no matches)"
+
+
+if __name__ == "__main__":
+ mcp.run()