feat(admin-tools): consumidor client do painel debug (F8 + UZeusAOIComponent)

- UZeusAOIComponent (Game/Network): componente de gameplay do player local com
  plus de debug. Overlays via DrawDebug* (esfera AOI real; spawn/cell/handoff
  placeholders) dirigidos pela UI via IZeusAOIDebugTarget + console vars. Estado
  persiste no componente entre aberturas do menu (IsOverlayEnabled).
- AZeusCharacter: anexa o componente + abre/fecha o painel no F8 (UIOnly trava o
  jogo; Esc/F8 fecham); LoadClass lazy do painel no content do PLUGIN
  (/ZeusAdminTools/UI/WBP_AdminToolsAIO).
- ZMMO.Build.cs: depende de ZeusAdminToolsRuntime.
- UIProgressBar_Base: rename EaseOutCubic -> EaseOutCubicPB (fix unity-build).
- ZMMO.uproject: remove entrada NwiroIntegrationKit.

A UI e o C++ do plugin ZeusAdminTools vivem no repo Server (plugin autocontido).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 16:07:26 -03:00
parent 16de963301
commit cda5fea776
7 changed files with 283 additions and 8 deletions

View File

@@ -20,7 +20,10 @@
#include "GameplayTagContainer.h" #include "GameplayTagContainer.h"
#include "GameplayTagsManager.h" #include "GameplayTagsManager.h"
#include "ZeusGASComponent.h" #include "ZeusGASComponent.h"
#include "ZeusAOIComponent.h"
#include "ZeusPlayerState.h" #include "ZeusPlayerState.h"
#include "Blueprint/UserWidget.h"
#include "GameFramework/PlayerController.h"
#include "ZeusWorldSubsystem.h" #include "ZeusWorldSubsystem.h"
#include "ZeusNetworkSubsystem.h" #include "ZeusNetworkSubsystem.h"
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h" #include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
@@ -57,6 +60,14 @@ AZeusCharacter::AZeusCharacter()
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false; FollowCamera->bUsePawnControlRotation = false;
// AOI do cliente (+ overlays de debug). Implementa IZeusAOIDebugTarget; o
// Zeus Admin Tools acha este componente no pawn e dirige os overlays.
AOIComponent = CreateDefaultSubobject<UZeusAOIComponent>(TEXT("ZeusAOIComponent"));
// AdminPanelClass NAO e' resolvido aqui via FClassFinder: na criacao do CDO
// (load do modulo) o asset registry pode nao estar pronto e o static cacheia
// null pra sempre. Resolvido lazy em ToggleAdminPanel (LoadClass em runtime).
// AttributeComponent migrou pro PlayerState (AZeusPlayerState + Component // AttributeComponent migrou pro PlayerState (AZeusPlayerState + Component
// Registry config-driven). Pawn fica leve — so' movement/input/camera. // Registry config-driven). Pawn fica leve — so' movement/input/camera.
@@ -181,6 +192,61 @@ void AZeusCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCompo
{ {
UE_LOG(LogZeusPlayer, Error, TEXT("'%s' Failed to find an Enhanced Input component."), *GetNameSafe(this)); UE_LOG(LogZeusPlayer, Error, TEXT("'%s' Failed to find an Enhanced Input component."), *GetNameSafe(this));
} }
// Debug: F8 abre/fecha o Zeus Admin Tools (AIO Debug). Legacy BindKey
// convive com Enhanced Input. Trocar a tecla aqui se conflitar.
PlayerInputComponent->BindKey(EKeys::F8, IE_Pressed, this, &AZeusCharacter::ToggleAdminPanel);
}
void AZeusCharacter::ToggleAdminPanel()
{
APlayerController* PC = Cast<APlayerController>(GetController());
if (!PC)
{
return;
}
// Aberto -> fecha + devolve input pro jogo.
if (AdminPanelInstance && AdminPanelInstance->IsInViewport())
{
AdminPanelInstance->RemoveFromParent();
PC->SetInputMode(FInputModeGameOnly());
PC->bShowMouseCursor = false;
return;
}
// Lazy load (runtime): no F8 o asset ja' esta carregavel; evita o pitfall do
// FClassFinder na criacao do CDO. Path do generated class (_C).
// A UI vive no CONTENT DO PLUGIN (mount /ZeusAdminTools/), nao no /Game do projeto:
// o plugin ZeusAdminTools e' autocontido (C++ + UI no repo Server).
if (!AdminPanelClass)
{
AdminPanelClass = LoadClass<UUserWidget>(nullptr,
TEXT("/ZeusAdminTools/UI/WBP_AdminToolsAIO.WBP_AdminToolsAIO_C"));
}
if (!AdminPanelClass)
{
UE_LOG(LogZeusPlayer, Warning, TEXT("[AdminPanel] AdminPanelClass nao resolvido (WBP_AdminToolsAIO ausente?)."));
return;
}
if (!AdminPanelInstance)
{
AdminPanelInstance = CreateWidget<UUserWidget>(PC, AdminPanelClass);
}
if (AdminPanelInstance)
{
AdminPanelInstance->AddToViewport(1000);
// UIOnly: trava a acao do jogo enquanto o menu esta aberto e garante que
// os cliques vao 100% pra UI (no GameAndUI o viewport rouba o clique).
// O mundo continua renderizando + os overlays de debug seguem desenhando.
FInputModeUIOnly Mode;
Mode.SetWidgetToFocus(AdminPanelInstance->TakeWidget());
Mode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
PC->SetInputMode(Mode);
PC->bShowMouseCursor = true;
}
} }
void AZeusCharacter::Move(const FInputActionValue& Value) void AZeusCharacter::Move(const FInputActionValue& Value)

View File

@@ -11,6 +11,8 @@ class USpringArmComponent;
class UCameraComponent; class UCameraComponent;
class UInputAction; class UInputAction;
class UZeusNetworkSubsystem; class UZeusNetworkSubsystem;
class UZeusAOIComponent;
class UUserWidget;
struct FInputActionValue; struct FInputActionValue;
DECLARE_LOG_CATEGORY_EXTERN(LogZeusPlayer, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogZeusPlayer, Log, All);
@@ -46,6 +48,10 @@ class ZMMO_API AZeusCharacter : public ACharacter, public IZeusEntityInterface
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true")) UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera; UCameraComponent* FollowCamera;
/** AOI do cliente (+ overlays de debug dirigidos pelo Zeus Admin Tools). */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Zeus|AOI", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UZeusAOIComponent> AOIComponent;
// GAS Component vive no PlayerState (AZeusPlayerState + Component Registry). // GAS Component vive no PlayerState (AZeusPlayerState + Component Registry).
// Acesso: GetPlayerState<AZeusPlayerState>()->GetZeusComponent<UZeusGASComponent>(). // Acesso: GetPlayerState<AZeusPlayerState>()->GetZeusComponent<UZeusGASComponent>().
@@ -67,6 +73,11 @@ protected:
UPROPERTY(EditAnywhere, Category = "Input") UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* DashAction; UInputAction* DashAction;
/** Painel Zeus Admin Tools (AIO Debug), aberto/fechado por F8. Default =
* WBP_AdminToolsAIO (resolvido no construtor). */
UPROPERTY(EditAnywhere, Category = "Zeus|Admin")
TSubclassOf<UUserWidget> AdminPanelClass;
/** Cadencia de envio dos pacotes `C_INPUT_AXIS` (Hz). */ /** Cadencia de envio dos pacotes `C_INPUT_AXIS` (Hz). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Zeus|Networking", meta = (ClampMin = "1", ClampMax = "120")) UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Zeus|Networking", meta = (ClampMin = "1", ClampMax = "120"))
int32 InputSendRateHz = 30; int32 InputSendRateHz = 30;
@@ -114,6 +125,9 @@ protected:
/// + responde S_ABILITY_ACTIVATED (HP/SP atualizado, montage roda local). /// + responde S_ABILITY_ACTIVATED (HP/SP atualizado, montage roda local).
void OnDashTriggered(); void OnDashTriggered();
/** F8: cria/mostra ou esconde o painel Zeus Admin Tools (input mode UI + cursor). */
void ToggleAdminPanel();
public: public:
/** Wrappers expostos a Blueprint (UI / mobile / automation). */ /** Wrappers expostos a Blueprint (UI / mobile / automation). */
UFUNCTION(BlueprintCallable, Category = "Input") UFUNCTION(BlueprintCallable, Category = "Input")
@@ -158,6 +172,10 @@ private:
UPROPERTY() UPROPERTY()
TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork; TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;
/** Instancia viva do painel admin (criada lazy no primeiro F8). */
UPROPERTY(Transient)
TObjectPtr<UUserWidget> AdminPanelInstance;
bool bSpawnDelegateBound = false; bool bSpawnDelegateBound = false;
/** Identidade autoritativa atribuida quando o servidor envia S_SPAWN_PLAYER local. */ /** Identidade autoritativa atribuida quando o servidor envia S_SPAWN_PLAYER local. */

View File

@@ -0,0 +1,134 @@
// Copyright Zeus Server Engine. All rights reserved.
#include "ZeusAOIComponent.h"
#include "DrawDebugHelpers.h"
#include "GameFramework/Actor.h"
// Console vars — espelham as flags do painel (overlay liga se flag OU CVar).
static TAutoConsoleVariable<int32> CVarShowAOI(
TEXT("zeus.debug.aoi"), 0, TEXT("Desenha a esfera de AOI do player local."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowSpawn(
TEXT("zeus.debug.spawn"), 0, TEXT("Desenha a zona de spawn (placeholder)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowCell(
TEXT("zeus.debug.cell"), 0, TEXT("Desenha os bounds da celula (placeholder)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowProxy(
TEXT("zeus.debug.proxy"), 0, TEXT("Desenha labels de proxy (TODO)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowHandoff(
TEXT("zeus.debug.handoff"), 0, TEXT("Desenha a linha de handoff (placeholder)."), ECVF_Default);
static TAutoConsoleVariable<int32> CVarShowDrift(
TEXT("zeus.debug.drift"), 0, TEXT("Desenha o drift autoritativo (TODO)."), ECVF_Default);
namespace
{
const FColor kColorAOI(93, 213, 255);
const FColor kColorSpawn(122, 219, 138);
const FColor kColorCell(232, 192, 96);
const FColor kColorHandoff(255, 155, 190);
}
UZeusAOIComponent::UZeusAOIComponent()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
SetIsReplicatedByDefault(false); // debug/cliente; gameplay AOI real entra depois
}
void UZeusAOIComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (AnyOverlayActive())
{
DrawOverlays();
}
}
bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
{
switch (Overlay)
{
case EZeusAOIOverlay::AOIRadius: return bShowAOIRadius || CVarShowAOI.GetValueOnGameThread() > 0;
case EZeusAOIOverlay::SpawnZone: return bShowSpawnZone || CVarShowSpawn.GetValueOnGameThread() > 0;
case EZeusAOIOverlay::CellBounds: return bShowCellBounds || CVarShowCell.GetValueOnGameThread() > 0;
case EZeusAOIOverlay::ProxyLabels: return bShowProxyLabels || CVarShowProxy.GetValueOnGameThread() > 0;
case EZeusAOIOverlay::HandoffLine: return bShowHandoffLine || CVarShowHandoff.GetValueOnGameThread() > 0;
case EZeusAOIOverlay::AuthDrift: return bShowAuthDrift || CVarShowDrift.GetValueOnGameThread() > 0;
default: return false;
}
}
bool UZeusAOIComponent::AnyOverlayActive() const
{
return ResolveOverlay(EZeusAOIOverlay::AOIRadius)
|| ResolveOverlay(EZeusAOIOverlay::SpawnZone)
|| ResolveOverlay(EZeusAOIOverlay::CellBounds)
|| ResolveOverlay(EZeusAOIOverlay::ProxyLabels)
|| ResolveOverlay(EZeusAOIOverlay::HandoffLine)
|| ResolveOverlay(EZeusAOIOverlay::AuthDrift);
}
void UZeusAOIComponent::DrawOverlays()
{
const AActor* Owner = GetOwner();
UWorld* World = GetWorld();
if (!Owner || !World) { return; }
const FVector Loc = Owner->GetActorLocation();
if (ResolveOverlay(EZeusAOIOverlay::AOIRadius))
{
DrawDebugSphere(World, Loc, AOIRadiusCm, 32, kColorAOI, false, -1.0f, 0, LineThickness);
}
if (ResolveOverlay(EZeusAOIOverlay::SpawnZone))
{
// PLACEHOLDER: box em volta do player. TODO spawn zones reais do servidor.
DrawDebugBox(World, Loc, FVector(SpawnZoneExtentCm), kColorSpawn, false, -1.0f, 0, LineThickness);
}
if (ResolveOverlay(EZeusAOIOverlay::CellBounds))
{
// PLACEHOLDER: box grande centrada no player. TODO bounds reais da cell.
DrawDebugBox(World, Loc, FVector(CellBoundsExtentCm, CellBoundsExtentCm, 2000.0f),
kColorCell, false, -1.0f, 0, LineThickness);
}
if (ResolveOverlay(EZeusAOIOverlay::HandoffLine))
{
// PLACEHOLDER: vetor pra frente. TODO apontar pro centro da cell vizinha.
const FVector End = Loc + Owner->GetActorForwardVector() * AOIRadiusCm;
DrawDebugDirectionalArrow(World, Loc, End, 200.0f, kColorHandoff, false, -1.0f, 0, LineThickness);
}
// ProxyLabels / AuthDrift: TODO (precisam do registry de proxies/snapshots).
}
void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay, bool bEnabled)
{
switch (Overlay)
{
case EZeusAOIOverlay::AOIRadius: bShowAOIRadius = bEnabled; break;
case EZeusAOIOverlay::SpawnZone: bShowSpawnZone = bEnabled; break;
case EZeusAOIOverlay::CellBounds: bShowCellBounds = bEnabled; break;
case EZeusAOIOverlay::ProxyLabels: bShowProxyLabels = bEnabled; break;
case EZeusAOIOverlay::HandoffLine: bShowHandoffLine = bEnabled; break;
case EZeusAOIOverlay::AuthDrift: bShowAuthDrift = bEnabled; break;
default: break;
}
}
void UZeusAOIComponent::SetAllOverlays_Implementation(bool bEnabled)
{
bShowAOIRadius = bShowSpawnZone = bShowCellBounds = bEnabled;
bShowProxyLabels = bShowHandoffLine = bShowAuthDrift = bEnabled;
}
bool UZeusAOIComponent::IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const
{
// Reflete o estado real (flag OU CVar) p/ a UI mostrar o que esta' desenhando.
return ResolveOverlay(Overlay);
}
int32 UZeusAOIComponent::GetEnabledOverlayCount() const
{
return (bShowAOIRadius ? 1 : 0) + (bShowSpawnZone ? 1 : 0) + (bShowCellBounds ? 1 : 0)
+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0);
}

View File

@@ -0,0 +1,60 @@
// Copyright Zeus Server Engine. All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "ZeusAOIDebugTarget.h" // plugin ZeusAdminToolsRuntime: interface + EZeusAOIOverlay
#include "ZeusAOIComponent.generated.h"
/**
* UZeusAOIComponent (cliente / jogo)
*
* Componente de gameplay do player local. Dominio principal: Area of Interest
* do cliente (proxies, despawn coordenado, interpolacao — ver
* [[project_aoi_client_component]]). AQUI, alem disso, expoe um "plus" de
* DEBUG: overlays via DrawDebug* dirigidos pelo painel admin (plugin) atraves
* de IZeusAOIDebugTarget, e/ou por console vars (zeus.debug.aoi etc.).
*
* Vive na src do ZMMO (nao no plugin): e' um componente do JOGO. O plugin
* ZeusAdminTools apenas COMUNICA com ele via a interface — direcao da
* dependencia: ZMMO -> plugin (implementa a interface).
*/
UCLASS(ClassGroup = (Zeus), meta = (BlueprintSpawnableComponent), DisplayName = "Zeus AOI Component")
class ZMMO_API UZeusAOIComponent : public UActorComponent, public IZeusAOIDebugTarget
{
GENERATED_BODY()
public:
UZeusAOIComponent();
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
// === IZeusAOIDebugTarget (dirigido pelo painel admin) ===
virtual void SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay, bool bEnabled) override;
virtual void SetAllOverlays_Implementation(bool bEnabled) override;
virtual bool IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const override;
// === Flags de debug por overlay ===
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAOIRadius = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowSpawnZone = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowCellBounds = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowProxyLabels = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowHandoffLine = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAuthDrift = false;
// === Parametros ===
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float AOIRadiusCm = 3000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float SpawnZoneExtentCm = 800.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float CellBoundsExtentCm = 50000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Params") float LineThickness = 2.0f;
UFUNCTION(BlueprintPure, Category = "Zeus|AOI Debug")
int32 GetEnabledOverlayCount() const;
private:
void DrawOverlays();
bool ResolveOverlay(EZeusAOIOverlay Overlay) const;
bool AnyOverlayActive() const;
};

View File

@@ -47,7 +47,7 @@ namespace
static const FName P_EdgeGlowWidth("EdgeGlowWidth"); static const FName P_EdgeGlowWidth("EdgeGlowWidth");
static const FName P_EdgeGlowIntensity("EdgeGlowIntensity"); static const FName P_EdgeGlowIntensity("EdgeGlowIntensity");
FORCEINLINE float EaseOutCubic(float Alpha) FORCEINLINE float EaseOutCubicPB(float Alpha)
{ {
return 1.f - FMath::Pow(1.f - Alpha, 3.f); return 1.f - FMath::Pow(1.f - Alpha, 3.f);
} }
@@ -195,7 +195,7 @@ void UUIProgressBar_Base::NativeTick(const FGeometry& MyGeometry, float InDeltaT
PrimaryElapsed += InDeltaTime; PrimaryElapsed += InDeltaTime;
const float Duration = FMath::Max(PrimaryAnimationSeconds, MinAnimSeconds); const float Duration = FMath::Max(PrimaryAnimationSeconds, MinAnimSeconds);
const float Alpha = FMath::Clamp(PrimaryElapsed / Duration, 0.f, 1.f); const float Alpha = FMath::Clamp(PrimaryElapsed / Duration, 0.f, 1.f);
CurrentPrimaryLevel = FMath::Lerp(PrimaryStart, TargetPrimaryLevel, EaseOutCubic(Alpha)); CurrentPrimaryLevel = FMath::Lerp(PrimaryStart, TargetPrimaryLevel, EaseOutCubicPB(Alpha));
ApplyPrimaryToMID(CurrentPrimaryLevel); ApplyPrimaryToMID(CurrentPrimaryLevel);
if (Alpha >= 1.f) if (Alpha >= 1.f)
{ {
@@ -210,7 +210,7 @@ void UUIProgressBar_Base::NativeTick(const FGeometry& MyGeometry, float InDeltaT
SecondaryElapsed += InDeltaTime; SecondaryElapsed += InDeltaTime;
const float Duration = FMath::Max(SecondaryAnimationSeconds, MinAnimSeconds); const float Duration = FMath::Max(SecondaryAnimationSeconds, MinAnimSeconds);
const float Alpha = FMath::Clamp(SecondaryElapsed / Duration, 0.f, 1.f); const float Alpha = FMath::Clamp(SecondaryElapsed / Duration, 0.f, 1.f);
CurrentSecondaryLevel = FMath::Lerp(SecondaryStart, TargetSecondaryLevel, EaseOutCubic(Alpha)); CurrentSecondaryLevel = FMath::Lerp(SecondaryStart, TargetSecondaryLevel, EaseOutCubicPB(Alpha));
ApplySecondaryToMID(CurrentSecondaryLevel); ApplySecondaryToMID(CurrentSecondaryLevel);
if (Alpha >= 1.f) if (Alpha >= 1.f)
{ {

View File

@@ -21,7 +21,8 @@ public class ZMMO : ModuleRules
"GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData "GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData
"ZeusNetwork", "ZeusNetwork",
"ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot "ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot
"ZeusJobs" "ZeusJobs",
"ZeusAdminToolsRuntime" // IZeusAOIDebugTarget + EZeusAOIOverlay (admin panel dirige o componente)
}); });
PrivateDependencyModuleNames.AddRange(new string[] { PrivateDependencyModuleNames.AddRange(new string[] {

View File

@@ -76,10 +76,6 @@
{ {
"Name": "CommonUI", "Name": "CommonUI",
"Enabled": true "Enabled": true
},
{
"Name": "NwiroIntegrationKit",
"Enabled": false
} }
], ],
"AdditionalPluginDirectories": [ "AdditionalPluginDirectories": [