Project_WL/Assets/WL/Tools/WLLeadAutoEnter.cs

116 lines
7.2 KiB
C#
Raw Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLLeadAutoEnter.cs — Lead 자동 검증용 타이틀 자동 진입 (원본 0줄 · 표식이 있을 때만)
//
// 2026-09-17 PD 「네가 Play 테스트할 때 타이틀 화면에 계속 대기하지 않도록 진입 프로세스를 개선해」.
// Lead 파이프라인 스크립트가 Play 직전에 PlayerPrefs `WL_LeadAutoEnter = 1` 을 세우면, 타이틀 씬에서 이 컴포넌트가
// ① 로그인 버튼이 활성화될 때까지 기다렸다가 `OnClick_DevLogin()`(ID 비어 있으면 TestID)
// ② `AllComplete` 가 되면 `OnClick_Screen()`(인게임 로드)
// ③ 로그인 실패(`LoginFail`)면 3 s 뒤 재시도 · 120 s 넘으면 포기
// 를 대신 한다. 표식은 소비 즉시 지운다(PD 의 일반 Play 에는 영향 0). 원본 `TitleInfo` 의 private 필드는 리플렉션으로 읽기만 한다.
// ─────────────────────────────────────────────────────────────────────────────
using System.Collections;
using System.Reflection;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace WL.Tools
{
public sealed class WLLeadAutoEnter : MonoBehaviour
{
public const string PrefKey = "WL_LeadAutoEnter";
public static string LastLog = "";
public static bool Running;
/// <summary>로그인 완료(AllComplete) 뒤 인게임으로 넘기기까지 기다리는 시간(s) — 사람이 화면을 터치하는 간격.
/// 🔴 2026-09-17 실측: 완료 직후 바로 넘기면 원본 로그인 사슬의 **두 번째** 완료 응답이 타이틀 씬이 내려간 뒤 도착해
/// `TitleInfo.ServerComplete` 가 지워진 글자를 만져 MissingReferenceException 창이 떴다(재시도 0회에서도 재현).</summary>
public static float EnterDelay = 8f;
public static float AllCompleteAt = -1f;
// 🔴 2026-09-17 실측: 표식(Boot)으로 만든 인스턴스와 프로브가 직접 만든 인스턴스가 **둘 다** DevLogin 을 불러 로그인 사슬이 2개 →
// 두 번째 사슬의 토큰 저장이 씬 전환 뒤 실패해 「서버 에러 · 토큰 저장 실패」 창(원본 모달)이 인게임에 떴다. 인스턴스는 항상 하나만.
static WLLeadAutoEnter s_instance;
public static bool Exists { get { return s_instance != null; } }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void Boot()
{
if (PlayerPrefs.GetInt(PrefKey, 0) == 0) return;
PlayerPrefs.SetInt(PrefKey, 0); PlayerPrefs.Save(); // 1회 소비
Create();
}
/// <summary>프로브가 직접 부를 때도 여기로 — 이미 있으면 만들지 않는다.</summary>
public static bool Create()
{
if (s_instance != null) return false;
var go = new GameObject("~WL_LeadAutoEnter");
go.hideFlags = HideFlags.DontSave;
DontDestroyOnLoad(go);
s_instance = go.AddComponent<WLLeadAutoEnter>();
return true;
}
void Start()
{
if (s_instance != null && s_instance != this) { Destroy(gameObject); return; }
s_instance = this;
StartCoroutine(Run());
}
void OnDestroy() { if (s_instance == this) s_instance = null; }
IEnumerator Run()
{
Running = true;
float t0 = Time.realtimeSinceStartup;
var bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
FieldInfo fActive = typeof(TitleInfo).GetField("activeButtons", bf), fAll = typeof(TitleInfo).GetField("AllComplete", bf),
fFail = typeof(TitleInfo).GetField("LoginFail", bf), fInput = typeof(TitleInfo).GetField("Input_ID", bf);
bool loginCalled = false; float lastLogin = -99f;
while (Time.realtimeSinceStartup - t0 < 120f)
{
if (SceneManager.GetActiveScene().name != "Title") { LastLog = "타이틀 아님(" + SceneManager.GetActiveScene().name + ") — 종료"; break; }
var title = FindFirstObjectByType<TitleInfo>();
if (title == null) { yield return new WaitForSeconds(0.5f); continue; }
bool all = fAll != null && (bool)fAll.GetValue(title);
if (all)
{
if (AllCompleteAt < 0f)
{
AllCompleteAt = Time.realtimeSinceStartup;
LastLog = "로그인 완료(" + (AllCompleteAt - t0).ToString("F1") + " s) → " + EnterDelay.ToString("F0") + " s 뒤 진입";
}
if (Time.realtimeSinceStartup - AllCompleteAt < EnterDelay) { yield return new WaitForSeconds(0.5f); continue; }
title.OnClick_Screen();
LastLog = "로그인 완료 " + (AllCompleteAt - t0).ToString("F1") + " s · 화면 터치 → 인게임 로드(" + (Time.realtimeSinceStartup - t0).ToString("F1") + " s)";
yield return new WaitForSeconds(2f);
if (SceneManager.GetActiveScene().name != "Title") break;
continue;
}
bool active = fActive != null && (bool)fActive.GetValue(title);
int fail = fFail != null ? (int)fFail.GetValue(title) : 0;
// 🔴 실측: 스토어 정보 대기 중에는 activeButtons 가 오래 false 인데 OnClick_DevLogin 은 그와 무관하게 동작한다(이전 수동 진입과 동일)
// → 타이틀이 보이면 1 s 뒤 바로 호출한다.
// 🔴 2026-09-17 실측: 8 s 마다 다시 부르면 서버 응답(ServerComplete)이 여러 번 오고, 마지막 응답이 인게임 전환 **뒤**에 도착해
// 원본 TitleInfo 가 지워진 타이틀 글자를 만져 MissingReferenceException 창이 떴다 → 재시도는 실패 표시가 있거나 90 s 무응답일 때만
// (실측: 개발 로그인 완료까지 40~45 s 걸린다 — 40 s 재시도도 같은 창을 띄웠다).
bool first = !loginCalled && Time.realtimeSinceStartup - t0 > 1f;
bool retry = loginCalled && (fail > 0 || Time.realtimeSinceStartup - lastLogin > 90f);
if (first || retry)
{
var input = fInput != null ? fInput.GetValue(title) as TMPro.TMP_InputField : null;
if (input != null && string.IsNullOrEmpty(input.text)) input.text = "TestID";
title.OnClick_DevLogin();
loginCalled = true; lastLogin = Time.realtimeSinceStartup;
LastLog = "DevLogin 호출(" + (lastLogin - t0).ToString("F1") + " s · 버튼 " + active + " · 실패 " + fail + ")";
}
yield return new WaitForSeconds(0.5f);
}
Running = false;
Destroy(gameObject);
}
}
}