using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using BrewMonster.Network; using BrewMonster.Scripts; using CSNetwork.Protocols; using CSNetwork.Protocols.RPCData; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace BrewMonster.UI { /// /// Login Flow: /// 1. Enter username and password /// 2. Click login button /// 3. Login success, get the list of characters /// 4. Open the select character screen /// public class LoginScreenUI : MonoBehaviour { [SerializeField] private TMP_InputField _usernameInputField; [SerializeField] private TMP_InputField _passwordInputField; [SerializeField] private Button _loginButton; [SerializeField] private SelecScreenCharacter _selectCharacterScreen; private List _roleInfos; private List _currentRoles; private RoleInfo _pendingCreatedRole; private bool _loginInProgress; bool isDoneWorldRender = false; bool isDoneNPCRender = false; private SynchronizationContext context; [Header("Audio")] [Tooltip("Set 3 clips to pick a random login theme.")] [SerializeField] private AudioClip[] _loginBGMs; void Awake() { // Ensure wrapper created early (Tech3C SDK). _ = Tech3CSDKWrapper.Instance; #if UNITY_EDITOR _usernameInputField.gameObject.SetActive(true); _passwordInputField.gameObject.SetActive(true); #else _usernameInputField.gameObject.SetActive(false); _passwordInputField.gameObject.SetActive(false); #endif } void OnEnable() { Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback); Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback); HostPlayerPortraitCapture.Instance?.ClearPortrait(); if(PlayerModelPreview.Instance != null) { PlayerModelPreview.Instance.HideAllPlayerModels(); } } private void OnDisable() { Tech3CSDKWrapper.Instance.RemoveLoginCallback(); Tech3CSDKWrapper.Instance.RemoveLogoutCallback(); } void Start() { var chosenBgm = PickLoginBGM(); if (chosenBgm != null && AudioManager.Instance != null) { AudioManager.Instance.PlayBGM(chosenBgm, 1.5f); } _loginButton.onClick.AddListener(OnLoginButtonClicked); context = SynchronizationContext.Current; #if UNITY_EDITOR // only load the username and password from the player prefs if in editor _usernameInputField.text = PlayerPrefs.GetString("username", ""); _passwordInputField.text = PlayerPrefs.GetString("password", ""); #endif // Default is login UI, unless a logout flow asked us to return directly to role select. ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry()); } private AudioClip PickLoginBGM() { if (_loginBGMs != null && _loginBGMs.Length > 0) { // UnityEngine.Random is deterministic per session and good enough here. int idx = UnityEngine.Random.Range(0, _loginBGMs.Length); return _loginBGMs[idx]; } return null; } // Update is called once per frame void Update() { if (_roleInfos != null) { _selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete, OnExitCharacterSelect); _roleInfos = null; } #if UNITY_EDITOR if (Input.GetKeyUp(KeyCode.LeftAlt)) { _usernameInputField.text = "test016"; _passwordInputField.text = "123456"; OnLoginButtonClicked(); } if (Input.GetKeyUp(KeyCode.Tab)) { _usernameInputField.text = "test017"; _passwordInputField.text = "123456"; OnLoginButtonClicked(); } #endif } public async void OnLoginButtonClicked() { if (_loginInProgress) { BMLogger.LogWarning("[LoginScreenUI] Login already in progress (ignored click)."); return; } _loginInProgress = true; if (_loginButton != null) _loginButton.interactable = false; bool sdkReady = Tech3CSDKWrapper.Instance.EnsureInitialized(); // If username or password is empty, use Tech3C SDK login UI. if (string.IsNullOrEmpty(_usernameInputField.text) || string.IsNullOrEmpty(_passwordInputField.text)) { if (sdkReady) { // Use Tech3C SDK login UI. bool started = Tech3CSDKWrapper.Instance.Login(); if (!started) { // Fallback: manual username/password login (useful in dev if SDK not configured). BMLogger.LogWarning("[LoginScreenUI] Tech3CSDKWrapper.Login() failed, fallback to manual login."); await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text); } } else { string savedUsername = PlayerPrefs.GetString("username", ""); string savedPassword = PlayerPrefs.GetString("password", ""); if (!string.IsNullOrEmpty(savedUsername) && !string.IsNullOrEmpty(savedPassword)) { BMLogger.LogWarning("[LoginScreenUI] Tech3C SDK not ready, using saved username/password for login."); await BeginGameLoginAsync(savedUsername, savedPassword); } else { BMLogger.LogError("[LoginScreenUI] Tech3C SDK not ready and no saved username/password."); _loginInProgress = false; if (_loginButton != null) _loginButton.interactable = true; } } } else { // otherwise use manual username/password login. BMLogger.LogError("[LoginScreenUI] Username/password empty."); await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text); } } private async Task BeginGameLoginAsync(string username, string password) { if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { BMLogger.LogError("[LoginScreenUI] Username/password empty."); _loginInProgress = false; if (_loginButton != null) _loginButton.interactable = true; return; } BMLogger.Log("OnLoginButtonClicked"); UnityGameSession.SetConnectionInfo("103.51.120.195", 29000); PlayerPrefs.SetString("username", username); PlayerPrefs.SetString("password", password); PlayerPrefs.Save(); BMLogger.Log($"[LoginScreenUI] Connecting+login start user='{username}'"); await UnityGameSession.Login(username, password, OnLoginComplete); } public async void ApplyLoginEntry(LogoutFlowState.LoginEntryTarget entryTarget) { if (entryTarget != LogoutFlowState.LoginEntryTarget.SelectRole) { if (_selectCharacterScreen != null) _selectCharacterScreen.gameObject.SetActive(false); _loginInProgress = false; if (_loginButton != null) _loginButton.interactable = true; return; } if (_selectCharacterScreen != null) _selectCharacterScreen.gameObject.SetActive(true); _loginInProgress = true; if (_loginButton != null) _loginButton.interactable = false; var session = UnityGameSession.Instance?.GameSession; if (session != null && session.IsConnected) { UnityGameSession.GetRoleListAsync(OnGetRoleListComplete); return; } string username = PlayerPrefs.GetString("username", ""); string password = PlayerPrefs.GetString("password", ""); if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { await BeginGameLoginAsync(username, password); return; } OnExitCharacterSelect(); } /// /// Callback when the login is complete. /// Then get the list of characters /// private void OnLoginComplete(bool result) { BMLogger.Log($"[LoginScreenUI] OnLoginComplete result={result}"); if (!result) { BMLogger.LogError("Login failed"); if (_selectCharacterScreen != null) _selectCharacterScreen.gameObject.SetActive(false); _loginInProgress = false; if (_loginButton != null) _loginButton.interactable = true; return; } if (_selectCharacterScreen != null) _selectCharacterScreen.gameObject.SetActive(true); UnityGameSession.GetRoleListAsync(OnGetRoleListComplete); } /// /// Callback when the list of characters is retrieved. /// Then move to the select character screen /// private void OnGetRoleListComplete(List roleInfos) { if (roleInfos == null) { BMLogger.LogError("OnGetRoleListComplete: roleInfos is null"); // Keep whatever is currently shown; don't overwrite UI state with null. _loginInProgress = false; if (_loginButton != null) _loginButton.interactable = true; return; } // Merge pending created role in case backend list hasn't updated yet. if (_pendingCreatedRole != null) { bool exists = false; for (int i = 0; i < roleInfos.Count; i++) { if (roleInfos[i].roleid == _pendingCreatedRole.roleid) { exists = true; break; } } if (!exists) { // Copy list so we don't mutate a list owned elsewhere. var merged = new List(roleInfos.Count + 1); merged.AddRange(roleInfos); merged.Add(_pendingCreatedRole); roleInfos = merged; } else { // Backend now includes the role; clear pending. _pendingCreatedRole = null; } } BMLogger.Log($"OnGetRoleListComplete: roles={roleInfos.Count}"); _roleInfos = roleInfos; _currentRoles = roleInfos; // Login flow finished; keep login button disabled (origin-like) once you're at select role. _loginInProgress = false; } private void OnClickSelectCharacter(RoleInfo roleInfo) { LoadingSceneController.Instance.ShowLoadingScene(true); UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete); } /// /// Callback when a new character is created. /// Refreshes the role list and keeps the character selection screen visible. /// private void OnCreateCharacterComplete(RoleInfo createdRole) { BMLogger.Log("Character created, refreshing role list..."); if (_selectCharacterScreen != null) { _selectCharacterScreen.gameObject.SetActive(true); } // Ensure the newly created role is visible immediately even if the server role list // hasn't updated yet. if (createdRole != null) { _pendingCreatedRole = createdRole; if (_currentRoles == null) { _currentRoles = new List(); } bool exists = false; for (int i = 0; i < _currentRoles.Count; i++) { if (_currentRoles[i].roleid == createdRole.roleid) { exists = true; break; } } if (!exists) { _currentRoles.Add(createdRole); } _roleInfos = _currentRoles; } else { BMLogger.LogError("OnCreateCharacterComplete: createdRole is null (create-role callback returned null)"); } // NOTE: // Immediately requesting the role list after create has been observed to disconnect // in some server builds. We rely on the createdRole callback to update UI instantly. // A server sync can be done later (e.g., next time you open this screen / re-login). } private void OnSelectRoleComplete(RoleInfo roleInfo) { context.Post(_ => { isDoneWorldRender = false; isDoneNPCRender = false; Action actLoadChar = () => { if (!isDoneNPCRender || !isDoneWorldRender) { return; } }; SceneLoader.SceneLoadProcess = SceneLoadProcess.Loading; SceneLoader.LoadingProgress = 0; #if TESTFAST string nameScene = "LoginScene"; SceneManager.UnloadSceneAsync(nameScene); isDoneNPCRender = true; isDoneWorldRender = true; actLoadChar?.Invoke(); UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete); #else string nameScene = UnityGameSession.Instance.GetWorldInstanceName(); UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single, (progress) => { LoadingSceneController.Instance.SetProgress(progress); }, (value) => { isDoneWorldRender = value; isDoneNPCRender = true; isDoneWorldRender = true; actLoadChar?.Invoke(); AudioManager.Instance.StopBGM(1f); WorldMusicController.Instance.InitForWorld(roleInfo.worldtag); UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete); }); #endif }, null); } private async void OnEnterWorldComplete() { // initialize the mini map CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan(); if (pGameUI != null) { pGameUI.m_pDlgMiniMap.InitializeMiniMap(); } await Task.Delay(2000); // Request all known packages: 0=Inventory,1=Equipment,2=Task UnityGameSession.RequestAllInventoriesAsync(() => { /*BMLogger.Log("Sent Inventory Detail Requests (all packs)");*/ }, 0, 1, 2); await Task.Delay(1000); UnityGameSession.RequestCheckSecurityPassWd(""); // C++ friend_GetList(); required before Add Friend await Task.Delay(1000); UnityGameSession.Friend_GetList(); } //private void OnInventoryReceived(List inventoryData) //{ // _inventoryUI.DisplayInventory(inventoryData); //} #if UNITY_EDITOR private void OnValidate() { if (_usernameInputField == null) { // find childrend with name "username" _usernameInputField = transform.Find("username").GetComponent(); } if (_passwordInputField == null) { // find childrend with name "password" _passwordInputField = transform.Find("password").GetComponent(); } if (_loginButton == null) { // find childrend with name "LoginBtn" _loginButton = transform.Find("LoginBtn").GetComponent