From e3e435d12e381f87a08ee74ec8d1bd96f3e3a930 Mon Sep 17 00:00:00 2001 From: Le Duc Anh Date: Mon, 8 Sep 2025 16:28:54 +0700 Subject: [PATCH] Unity Game Session MVP --- .../Debug/netstandard2.1/CSNetwork.dll | Bin 346624 -> 346624 bytes Assets/PerfectWorld/Scene.meta | 8 + Assets/PerfectWorld/Scene/Boostrap.unity | 261 +++ Assets/PerfectWorld/Scene/Boostrap.unity.meta | 7 + Assets/PerfectWorld/Scene/LoginScene.unity | 1995 +++++++++++++++++ .../PerfectWorld/Scene/LoginScene.unity.meta | 7 + Assets/PerfectWorld/Scripts/Boostrap.meta | 8 + .../Boostrap/BoostrapSceneController.cs | 23 + .../Boostrap/BoostrapSceneController.cs.meta | 2 + Assets/PerfectWorld/Scripts/Common.meta | 3 + .../Scripts/Common/AutoInitializer.cs | 89 + .../Scripts/Common/AutoInitializer.cs.meta | 3 + .../Scripts/Common/CoroutineRunner.cs | 9 + .../Scripts/Common/CoroutineRunner.cs.meta | 2 + .../Scripts/Common/IAutoInitialize.cs | 7 + .../Scripts/Common/IAutoInitialize.cs.meta | 3 + Assets/PerfectWorld/Scripts/Common/Logger.cs | 32 + .../Scripts/Common/Logger.cs.meta | 2 + .../Scripts/Common/MonoSingleton.cs | 38 + .../Scripts/Common/MonoSingleton.cs.meta | 2 + .../Scripts/Managers/EC_ManPlayer.cs | 12 +- .../Scripts/Network/UnityGameSession.cs | 103 + .../Scripts/Network/UnityGameSession.cs.meta | 2 + Assets/PerfectWorld/Scripts/UI.meta | 8 + Assets/PerfectWorld/Scripts/UI/Login.meta | 8 + .../Scripts/UI/Login/LoginScreenUI.cs | 122 + .../Scripts/UI/Login/LoginScreenUI.cs.meta | 2 + Assets/Scripts/CanvasController.cs | 1 + Assets/Scripts/SelecScreenCharacter.cs | 12 +- Packages/manifest.json | 1 + Packages/packages-lock.json | 9 + 31 files changed, 2776 insertions(+), 5 deletions(-) create mode 100644 Assets/PerfectWorld/Scene.meta create mode 100644 Assets/PerfectWorld/Scene/Boostrap.unity create mode 100644 Assets/PerfectWorld/Scene/Boostrap.unity.meta create mode 100644 Assets/PerfectWorld/Scene/LoginScene.unity create mode 100644 Assets/PerfectWorld/Scene/LoginScene.unity.meta create mode 100644 Assets/PerfectWorld/Scripts/Boostrap.meta create mode 100644 Assets/PerfectWorld/Scripts/Boostrap/BoostrapSceneController.cs create mode 100644 Assets/PerfectWorld/Scripts/Boostrap/BoostrapSceneController.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Common.meta create mode 100644 Assets/PerfectWorld/Scripts/Common/AutoInitializer.cs create mode 100644 Assets/PerfectWorld/Scripts/Common/AutoInitializer.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Common/CoroutineRunner.cs create mode 100644 Assets/PerfectWorld/Scripts/Common/CoroutineRunner.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs create mode 100644 Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Common/Logger.cs create mode 100644 Assets/PerfectWorld/Scripts/Common/Logger.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs create mode 100644 Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs create mode 100644 Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/UI.meta create mode 100644 Assets/PerfectWorld/Scripts/UI/Login.meta create mode 100644 Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs create mode 100644 Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs.meta diff --git a/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll b/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll index 47ff33e0aaea4b343f9711f7c0bfc0aad653ce48..692e72020585a95324a9e1ea3fb323ffeed23310 100644 GIT binary patch delta 126 zcmZpeBHA!TbV3J9hwJsbjXkYBjIBLPtv$@GJuF*$SpSF%2-U4)**vkvf8rX}L!ZPC z_-vmc!K$Ij!v5mni|I!kSpx*NELyUHF{;mFvA5d3zs!?NrmHxyCM!TiBOXIVLF#8M Vk_viizJxU?;DqJ&;PWFHXr~1 delta 126 zcmZpeBHA!TbV3J9w&$PcjXkYBjIBLPtv$@GJuF*$SpSF%JU_q?ZF + { + + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/CoroutineRunner.cs.meta b/Assets/PerfectWorld/Scripts/Common/CoroutineRunner.cs.meta new file mode 100644 index 0000000000..05800c1fc7 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/CoroutineRunner.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c8a86796c56d63447aa1df962fb3abf6 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs b/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs new file mode 100644 index 0000000000..d879983ebe --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs @@ -0,0 +1,7 @@ +namespace BrewMonster +{ + public interface IAutoInitialize + { + void Initialize(); + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs.meta b/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs.meta new file mode 100644 index 0000000000..7c8cf1fdd9 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/IAutoInitialize.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 64cd9342d8974df18056b12aeb6f2651 +timeCreated: 1757239258 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/Logger.cs b/Assets/PerfectWorld/Scripts/Common/Logger.cs new file mode 100644 index 0000000000..593174c789 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/Logger.cs @@ -0,0 +1,32 @@ +#define ENALBE_LOGGING +using UnityEngine; + + +namespace BrewMonster +{ + public class Logger + { + public static void Log(string message) + { + #if ENALBE_LOGGING + Debug.Log(message); + #endif + } + + public static void LogError(string message) + { + #if ENALBE_LOGGING + Debug.LogError(message); + #endif + } + + public static void LogWarning(string message) + { + #if ENALBE_LOGGING + Debug.LogWarning(message); + #endif + } + + + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/Logger.cs.meta b/Assets/PerfectWorld/Scripts/Common/Logger.cs.meta new file mode 100644 index 0000000000..b067f1111d --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/Logger.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4e4b9f6cd855f4a4c9b02f681b12af2d \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs b/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs new file mode 100644 index 0000000000..752c9cb5ee --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs @@ -0,0 +1,38 @@ +using UnityEngine; + +namespace BrewMonster +{ + public class MonoSingleton : MonoBehaviour where T : MonoBehaviour + { + private static T _instance; + public static T Instance + { + get + { + if (_instance == null) + { + _instance = FindFirstObjectByType(); + + if (_instance == null) + { + GameObject obj = new GameObject(typeof(T).Name); + _instance = obj.AddComponent(); + } + } + return _instance; + } + } + + protected virtual void Awake() + { + _instance = this as T; + Initialize(); + } + + /// Override this method to initialize the singleton + protected virtual void Initialize() + { + + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs.meta b/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs.meta new file mode 100644 index 0000000000..feedcb1ce0 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/MonoSingleton.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 139eb655406d7d447ba2d30309a84e00 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs b/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs index 270bfbf291..42d90b7139 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs @@ -1,17 +1,25 @@ -using CSNetwork; +using BrewMonster; +using CSNetwork; namespace PerfectWorld.Scripts.Managers { namespace BrewMonster.Managers { - public class EC_ManPlayer : IMsgHandler + public class EC_ManPlayer : IMsgHandler, IAutoInitialize { + public void Initialize() + { + EC_ManMessage.RegisterHandler(this); // add to the message handler list + } + + public int HandlerId => MANAGER_INDEX.MAN_PLAYER; public bool ProcessMessage(ECMSG Msg) { return true; } + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs new file mode 100644 index 0000000000..50b75f8658 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -0,0 +1,103 @@ +using CSNetwork; +using CSNetwork.Protocols; +using System; +using BrewMonster; +using CSNetwork.Security; +using System.Threading.Tasks; +using System.Collections.Generic; +using CSNetwork.Protocols.RPCData; + +namespace BrewMonster.Network +{ + // How to connect to the server: + // 1. Set the connection info + // 2. Login + public class UnityGameSession : MonoSingleton + { + private GameSession _gameSession; + + private bool _isInitialized = false; + private string _ip = ""; + private int _port = 0; + private string _username = ""; + private string _password = ""; + + /// + /// Send a + /// + /// + /// + public static void SendProtocol(Protocol protocol, Action complete = null) + { + if (!Instance._isInitialized) + { + return; + } + Instance._gameSession.SendProtocol(protocol, complete); + } + + /// Set the connection info. This MUST be called call before login + public static void SetConnectionInfo(string ip, int port) + { + Logger.Log($"Set connection info {ip} {port}"); + Instance._ip = ip; + Instance._port = port; + } + + public static async Task Login(string username, string password, Action onLoginComplete = null) + { + Instance._username = username; + Instance._password = password; + + if (Instance._ip == "" || Instance._port == 0) + { + Logger.LogError($"IP or port is not set {Instance._ip} {Instance._port}"); + onLoginComplete?.Invoke(false); + return; + } + + await Instance.ConnectAsync(Instance._ip, Instance._port); + + if (!Instance._gameSession.IsConnected) + { + Logger.LogError($"Failed to connect to {Instance._ip} {Instance._port}"); + onLoginComplete?.Invoke(false); + return; + } + + Instance._gameSession.LoginAsync(username, password, onLoginComplete); + } + + protected override void Initialize() + { + BaseSecurity.Initizalize(); + ProtocolFactory.RegisterAllProtocols(); + _gameSession = new GameSession(); + _isInitialized = true; + + DontDestroyOnLoad(gameObject); + } + + /// Make sure username and password is set before calling this method + private async Task ConnectAsync(string ip, int port) + { + if (!Instance._isInitialized) + { + Logger.LogError("GameSession is not initialized"); + return; + } + await Instance._gameSession.ConnectAsync(ip, port); + } + + /// Get the list of created characters + public static void GetRoleListAsync(Action> callback = null) + { + Instance._gameSession.GetRoleListAsync(callback); + } + + public static void SelectRoleAsync(RoleInfo roleInfo, Action callback = null) + { + Instance._gameSession.SelectRoleAsync(roleInfo, callback); + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs.meta b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs.meta new file mode 100644 index 0000000000..60c5f705b1 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c9177aa9fcd28a94b9042f8a89fdac2d \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI.meta b/Assets/PerfectWorld/Scripts/UI.meta new file mode 100644 index 0000000000..4b7e243071 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7104ee5d1d322d64fb82447c7a9ab4c1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/UI/Login.meta b/Assets/PerfectWorld/Scripts/UI/Login.meta new file mode 100644 index 0000000000..cf5809f6d5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5410bc4cbeb43b748a536a3de8683843 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs new file mode 100644 index 0000000000..dcce0334d0 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using BrewMonster.Network; +using CSNetwork.Protocols; +using CSNetwork.Protocols.RPCData; +using TMPro; +using UnityEngine; +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; + + void Start() + { + _loginButton.onClick.AddListener(OnLoginButtonClicked); + } + + // Update is called once per frame + void Update() + { + if (_roleInfos != null) + { + _selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter); + _roleInfos = null; + } + } + + public async void OnLoginButtonClicked() + { + Logger.Log("OnLoginButtonClicked"); + string username = _usernameInputField.text; + string password = _passwordInputField.text; + UnityGameSession.SetConnectionInfo("103.182.22.52", 29000); + await UnityGameSession.Login(username, password, OnLoginComplete); + } + + /// + /// Callback when the login is complete. + /// Then get the list of characters + /// + private void OnLoginComplete(bool result) + { + if (!result) + { + Logger.LogError("Login failed"); + return; + } + UnityGameSession.GetRoleListAsync(OnGetRoleListComplete); + } + + /// + /// Callback when the list of characters is retrieved. + /// Then move to the select character screen + /// + private void OnGetRoleListComplete(List roleInfos) + { + Logger.Log($"OnGetRoleListComplete {roleInfos.Count}"); + + _roleInfos = roleInfos; + } + + private void OnClickSelectCharacter(RoleInfo roleInfo) + { + Logger.Log($"OnClickSelectCharacter {roleInfo.name}"); + UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete); + } + + private void OnSelectRoleComplete(RoleInfo roleInfo) + { + Logger.Log($"OnSelectRoleComplete {roleInfo.name} - {roleInfo.roleid}"); + + // now we have to enter the world + UnityGameSession.SendProtocol( + new enterworld() + { + Roleid = roleInfo.roleid, + Provider_link_id = 0, + L_timeout = 0, + Localsid = 0, + Locktime = 0, + Settime = 0, + Timeout = 0 + } + ); + } + + #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