#if UNITY_EDITOR || DEVELOPMENT_BUILD
using Unity.Pipeline.Commands;
using UnityEngine;
using UnityEngine.SceneManagement;
using WL.Player;
namespace WL.Diagnostics
{
///
/// WL 전용 Pipeline 커스텀 명령 — Unity CLI 에서 unity command wl_* 로 호출한다.
/// 에디터·개발 빌드 전용(릴리즈 빌드에서는 컴파일되지 않음). 등록 코드는 없다:
/// static 메서드에 [CliCommand] 만 붙이면 리컴파일 후 자동으로 명령 목록에 뜬다.
/// 인자는 [CliArg("이름", "설명")] — CLI 에서 --이름 값 으로 넘긴다.
/// 규칙: 파괴적 동작은 confirm/dry_run 인자로 게이트 · 게임 데이터 값은 상수로 두지 않는다(C45 — 설정 에셋을 읽는다).
/// 문서: Library/PackageCache/com.unity.pipeline@*/Documentation~/creating-commands.md
///
internal static class WLPipelineCommands
{
[CliCommand("wl_info", "WL 런타임 요약: 씬 · Play 상태 · timeScale · FPS · 플레이어(이름/위치/HP/상태) · 적 수(생존/전체)", Tags = new[] { "wl" })]
public static object Info()
{
var scene = SceneManager.GetActiveScene();
var switcher = Object.FindFirstObjectByType();
var pc = switcher != null ? switcher.Current : Object.FindFirstObjectByType();
int alive = 0;
foreach (var e in Enemy.All)
if (e != null && e.IsAlive) alive++;
return new
{
scene = scene.name,
isPlaying = Application.isPlaying,
timeScale = Time.timeScale,
fps = Application.isPlaying && Time.unscaledDeltaTime > 0f ? 1f / Time.unscaledDeltaTime : 0f,
player = pc == null ? null : new
{
name = switcher != null ? switcher.CurrentDisplayName : pc.name,
index = switcher != null ? switcher.CurrentIndex : -1,
position = Vec(pc.transform.position),
hp = pc.CurrentHp,
maxHp = pc.MaxHp,
wakeState = pc.CurrentWakeState.ToString(),
actState = pc.CurrentActState.ToString(),
speed = pc.CurrentSpeed,
target = pc.CurrentTarget != null ? pc.CurrentTarget.name : null
},
enemies = new { alive, total = Enemy.All.Count }
};
}
[CliCommand("wl_timescale", "Time.timeScale 변경 (0 = 일시정지 · 1 = 정상). 에디터 서버에서 Play 중 바로 적용된다 — 런타임 서버 없이도 동작", Tags = new[] { "wl" })]
public static object SetTimeScale(
[CliArg("scale", "배속 (0 ~ 100)", Required = true)] float scale)
{
if (scale < 0f || scale > 100f)
throw new System.ArgumentException("scale 은 0~100 범위여야 합니다: " + scale);
var before = Time.timeScale;
Time.timeScale = scale;
return new { before, after = Time.timeScale, isPlaying = Application.isPlaying };
}
[CliCommand("wl_teleport_player", "현재 플레이어를 월드 좌표로 이동 (CharacterController 를 잠시 꺼서 안전하게). Play 모드에서만 동작", Tags = new[] { "wl" })]
public static object TeleportPlayer(
[CliArg("x", "월드 X", Required = true)] float x,
[CliArg("y", "월드 Y", Required = true)] float y,
[CliArg("z", "월드 Z", Required = true)] float z)
{
if (!Application.isPlaying)
throw new System.InvalidOperationException("Play 모드에서만 동작합니다 (editor_play 후 재시도)");
var switcher = Object.FindFirstObjectByType();
var pc = switcher != null ? switcher.Current : Object.FindFirstObjectByType();
if (pc == null)
throw new System.InvalidOperationException("씬에 PlayerController 가 없습니다");
var cc = pc.GetComponent();
var from = pc.transform.position;
if (cc != null) cc.enabled = false;
pc.transform.position = new Vector3(x, y, z);
if (cc != null) cc.enabled = true;
return new { player = pc.name, from = Vec(from), to = Vec(pc.transform.position) };
}
// Vector3 를 그대로 반환하면 JSON 직렬화가 normalized 자기참조로 실패하므로 x/y/z 로 풀어 준다.
private static object Vec(Vector3 v) => new { x = v.x, y = v.y, z = v.z };
}
}
#endif