116 lines
7.2 KiB
C#
116 lines
7.2 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
|
// 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);
|
|
}
|
|
}
|
|
}
|