Merge branch 'develop' of https://git.brew.monster/Unity/perfect-world-unity into update-in-game

This commit is contained in:
Chomper9981
2026-01-23 09:32:13 +07:00
19 changed files with 7784 additions and 1746 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: cfa464036967d9746abcfa0608211b2e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f3553c5fdd68230c6a591df1fde22cf360896fc818007787595189f0cc036b5c
size 69312
oid sha256:eadad766c90d9a2f8f33468f1b6b21e6fd78e55f555200887eb9f6198720424a
size 76688
File diff suppressed because it is too large Load Diff
@@ -1,23 +1,27 @@
using System;
using System.Collections.Generic;
using CSNetwork.Protocols.RPCData;
namespace CSNetwork.Protocols
{
public class createrole : Protocol
{
public int Userid { get; set; }
public int Localsid { get; set; }
public uint Localsid { get; set; }
public RoleInfo Roleinfo { get; set; }
public Octets Referid { get; set; }
public createrole() : base(ProtocolType.PROTOCOL_CREATEROLE)
{
Referid = new Octets();
Roleinfo = new RoleInfo();
}
public override Protocol Clone() => new createrole
{
Userid = Userid,
Localsid = Localsid,
Roleinfo = Roleinfo?.Clone(),
Referid = new Octets(Referid.ToArray())
};
@@ -25,13 +29,23 @@ namespace CSNetwork.Protocols
{
os.Write(Userid);
os.Write(Localsid);
if (Roleinfo != null)
{
Roleinfo.Marshal(os);
}
else
{
new RoleInfo().Marshal(os);
}
os.Write(Referid);
}
public override void Unmarshal(OctetsStream os)
{
Userid = os.ReadInt32();
Localsid = os.ReadInt32();
Localsid = os.ReadUInt32();
Roleinfo = new RoleInfo();
Roleinfo.Unmarshal(os);
Referid = os.ReadOctets();
}
@@ -0,0 +1,58 @@
using System;
using CSNetwork.Protocols.RPCData;
namespace CSNetwork.Protocols
{
public class createrole_re : Protocol
{
public int result { get; set; }
public int roleid { get; set; }
public uint localsid { get; set; }
public RoleInfo roleinfo { get; set; }
public int refretcode { get; set; }
public createrole_re() : base(ProtocolType.PROTOCOL_CREATEROLE_RE)
{
roleinfo = new RoleInfo();
}
public override Protocol Clone() => new createrole_re
{
result = result,
roleid = roleid,
localsid = localsid,
roleinfo = roleinfo?.Clone(),
refretcode = refretcode
};
public override void Marshal(OctetsStream os)
{
os.Write(result);
os.Write(roleid);
os.Write(localsid);
if (roleinfo != null)
{
roleinfo.Marshal(os);
}
else
{
new RoleInfo().Marshal(os);
}
os.Write(refretcode);
}
public override void Unmarshal(OctetsStream os)
{
result = os.ReadInt32();
roleid = os.ReadInt32();
localsid = os.ReadUInt32();
roleinfo = new RoleInfo();
roleinfo.Unmarshal(os);
refretcode = os.ReadInt32();
}
public override int PriorPolicy() => 101;
public override bool SizePolicy(int size) => size <= 8192;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3640555d366b4e34bbefbbf040f7f339
@@ -35,7 +35,8 @@ namespace CSNetwork.Protocols.RPCData
os.Write(pos);
os.Write(count);
os.Write(max_count);
os.Write(data);
// Server-side expects an Octets field here; null will crash encoding.
os.Write(data ?? new Octets());
os.Write(proctype);
os.Write(expire_date);
os.Write(guid1);
@@ -69,9 +69,10 @@ namespace CSNetwork.Protocols.RPCData
os.Write(occupation);
os.Write(level);
os.Write(level2);
os.Write(name);
os.Write(custom_data);
os.WriteList(equipment);
// Avoid null Octets/List crashing protocol.Encode()
os.Write(name ?? new Octets());
os.Write(custom_data ?? new Octets());
os.WriteList(equipment ?? new List<GRoleInventory>());
os.Write(status);
os.Write(delete_time);
os.Write(create_time);
@@ -80,12 +81,12 @@ namespace CSNetwork.Protocols.RPCData
os.Write(posy);
os.Write(posz);
os.Write(worldtag);
os.Write(custom_status);
os.Write(charactermode);
os.Write(custom_status ?? new Octets());
os.Write(charactermode ?? new Octets());
os.Write(referrer_role);
os.Write(cash_add);
os.Write(reincarnation_data);
os.Write(realm_data);
os.Write(reincarnation_data ?? new Octets());
os.Write(realm_data ?? new Octets());
}
public void Unmarshal(OctetsStream os)
@@ -1,4 +1,4 @@
using BrewMonster;
using BrewMonster;
using BrewMonster.Common;
using CSNetwork;
using CSNetwork.C2SCommand;
@@ -171,6 +171,12 @@ namespace BrewMonster.Network
{
Instance._gameSession.SelectRoleAsync(roleInfo, callback);
}
public static void CreateRoleAsync(RoleInfo roleInfo, Octets referId, Action<RoleInfo> callback = null)
{
Instance._gameSession.CreateRoleAsync(roleInfo, referId, callback);
}
public static void EnterWorldAsync(RoleInfo roleInfo, Action callback = null)
{
Debug.Log("EnterWorldAsync !!!!! nay ");
@@ -0,0 +1,249 @@
using System;
using System.Text;
using BrewMonster.Network;
using CSNetwork;
using CSNetwork.GPDataType;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using BrewMonster.Scripts;
namespace BrewMonster.UI
{
/// <summary>
/// UI screen for creating a new character.
/// Equivalent to CDlgCreateGenderName + CDlgCreateProfession in C++.
/// </summary>
public class CreateCharacterScreen : MonoBehaviour
{
[SerializeField] private GameObject professionSelectionPanel;
[SerializeField] private Button[] professionButtons;
[SerializeField] private Button maleGenderButton;
[SerializeField] private Button femaleGenderButton;
[SerializeField] private TMP_InputField nameInputField;
[SerializeField] private Button confirmButton;
[SerializeField] private Button cancelButton;
[SerializeField] private Button backButton;
private int _currentProfession = -1;
private int _currentGender = -1;
private Action<RoleInfo> _onCreateComplete;
private Action _onCancel;
// Static array matching s_bShowMale[NUM_PROFESSION] from original C++ code (EC_ProfConfigs.cpp)
// true = show male, false = show female
private static readonly bool[] s_bShowMale = new bool[]
{
true, // 0: Warrior (武侠)
false, // 1: Mage (法师)
false, // 2: Priest (巫师)
false, // 3: Assassin (妖精)
true, // 4: Orc (妖兽)
true, // 5: Monk (刺客)
true, // 6: Elf (羽芒)
false, // 7: Elf (羽灵)
true, // 8: Ling (剑灵)
false, // 9: Ling (魅灵)
true, // 10: Oboro (夜影)
false, // 11: Oboro (月仙)
};
private void Start()
{
if (confirmButton != null)
confirmButton.onClick.AddListener(OnConfirmClicked);
if (cancelButton != null)
cancelButton.onClick.AddListener(OnCancelClicked);
if (backButton != null)
backButton.onClick.AddListener(OnCancelClicked);
if (maleGenderButton != null)
maleGenderButton.onClick.AddListener(() => OnGenderSelected(GENDER.GENDER_MALE));
if (femaleGenderButton != null)
femaleGenderButton.onClick.AddListener(() => OnGenderSelected(GENDER.GENDER_FEMALE));
// Setup profession buttons
if (professionButtons != null)
{
for (int i = 0; i < professionButtons.Length && i < 12; i++)
{
int prof = i; // Capture for closure
if (professionButtons[i] != null)
professionButtons[i].onClick.AddListener(() => OnProfessionSelected(prof));
}
}
if (nameInputField != null)
{
nameInputField.onSubmit.AddListener((text) => { if (CanConfirm()) OnConfirmClicked(); });
}
}
public void Show(Action<RoleInfo> onCreateComplete, Action onCancel)
{
_onCreateComplete = onCreateComplete;
_onCancel = onCancel;
_currentProfession = -1;
_currentGender = -1;
gameObject.SetActive(true);
if (nameInputField != null)
{
nameInputField.text = "";
nameInputField.Select();
}
UpdateConfirmButtonState();
}
public void Hide()
{
gameObject.SetActive(false);
}
private void OnProfessionSelected(int profession)
{
if (profession < 0 || profession >= (int)Profession.NUM_PROFESSION)
return;
_currentProfession = profession;
// Update UI to show selected profession
if (professionButtons != null)
{
for (int i = 0; i < professionButtons.Length; i++)
{
if (professionButtons[i] != null)
{
// Visual feedback for selected profession
var colors = professionButtons[i].colors;
colors.normalColor = (i == profession) ? Color.yellow : Color.white;
professionButtons[i].colors = colors;
}
}
}
// Auto-select gender based on profession (matching original C++ logic from EC_ProfConfigs.cpp)
// This matches the s_bShowMale array in CanShowOnCreate function
int autoGender = GetDefaultGenderForProfession(profession);
OnGenderSelected(autoGender);
UpdateConfirmButtonState();
}
/// <summary>
/// Gets the default gender for a profession based on the original C++ logic.
/// Matches the s_bShowMale array from EC_ProfConfigs.cpp CanShowOnCreate function.
/// </summary>
private int GetDefaultGenderForProfession(int profession)
{
if (profession >= 0 && profession < s_bShowMale.Length)
{
return s_bShowMale[profession] ? GENDER.GENDER_MALE : GENDER.GENDER_FEMALE;
}
// Fallback to male if profession is invalid
return GENDER.GENDER_MALE;
}
private void OnGenderSelected(int gender)
{
if (gender != GENDER.GENDER_MALE && gender != GENDER.GENDER_FEMALE)
return;
_currentGender = gender;
// Update UI to show selected gender
if (maleGenderButton != null)
{
var colors = maleGenderButton.colors;
colors.normalColor = (gender == GENDER.GENDER_MALE) ? Color.yellow : Color.white;
maleGenderButton.colors = colors;
}
if (femaleGenderButton != null)
{
var colors = femaleGenderButton.colors;
colors.normalColor = (gender == GENDER.GENDER_FEMALE) ? Color.yellow : Color.white;
femaleGenderButton.colors = colors;
}
UpdateConfirmButtonState();
}
private void OnConfirmClicked()
{
if (!CanConfirm())
return;
string characterName = nameInputField != null ? nameInputField.text : "";
if (string.IsNullOrWhiteSpace(characterName))
{
Debug.LogWarning("Character name cannot be empty");
return;
}
// Create RoleInfo using helper method
RoleInfo roleInfo = GameSession.CreateNewRoleInfo(characterName, _currentProfession, _currentGender);
// Create role via network
Debug.Log($"Calling CreateRoleAsync for character: {characterName}, profession: {_currentProfession}, gender: {_currentGender}");
UnityGameSession.CreateRoleAsync(roleInfo, new Octets(), (createdRole) =>
{
if (createdRole != null)
{
Debug.Log($"Character created successfully: {characterName}, RoleID: {createdRole.roleid}");
Hide();
_onCreateComplete?.Invoke(createdRole);
}
else
{
Debug.LogError($"Failed to create character: {characterName}. Check GameSession logs for error details.");
// TODO: Show error message to user
}
});
}
private void OnCancelClicked()
{
Hide();
_onCancel?.Invoke();
}
private bool CanConfirm()
{
if (_currentProfession < 0 || _currentProfession >= (int)Profession.NUM_PROFESSION)
return false;
if (_currentGender != GENDER.GENDER_MALE && _currentGender != GENDER.GENDER_FEMALE)
return false;
string name = nameInputField != null ? nameInputField.text : "";
if (string.IsNullOrWhiteSpace(name))
return false;
return true;
}
private void UpdateConfirmButtonState()
{
if (confirmButton != null)
{
confirmButton.interactable = CanConfirm();
}
}
private void Update()
{
// Update confirm button state in case name changes
if (nameInputField != null && nameInputField.isFocused)
{
UpdateConfirmButtonState();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aa9de023137e92348983cee3c59d620b
@@ -1,193 +1,282 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Network;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace BrewMonster.UI
{
/// <summary>
/// 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
/// </summary>
public class LoginScreenUI : MonoBehaviour
{
[SerializeField] private TMP_InputField _usernameInputField;
[SerializeField] private TMP_InputField _passwordInputField;
[SerializeField] private Button _loginButton;
[SerializeField] private SelecScreenCharacter _selectCharacterScreen;
private List<RoleInfo> _roleInfos;
bool isDoneWorldRender = false;
bool isDoneNPCRender = false;
private SynchronizationContext context;
public AudioClip loginBGM;
void Start()
{
AudioManager.Instance.PlayBGM(loginBGM, 1.5f);
_loginButton.onClick.AddListener(OnLoginButtonClicked);
context = SynchronizationContext.Current;
_usernameInputField.text = PlayerPrefs.GetString("username", "");
_passwordInputField.text = PlayerPrefs.GetString("password", "");
}
// Update is called once per frame
void Update()
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter);
_roleInfos = null;
}
#if UNITY_EDITOR
if (Input.GetKeyUp(KeyCode.LeftAlt))
{
_usernameInputField.text = "test004";
_passwordInputField.text = "123456";
}
if (Input.GetKeyUp(KeyCode.Tab))
{
_usernameInputField.text = "test002";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
#endif
}
public async void OnLoginButtonClicked()
{
BMLogger.Log("OnLoginButtonClicked");
string username = _usernameInputField.text;
string password = _passwordInputField.text;
// UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", username);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
await UnityGameSession.Login(username, password, OnLoginComplete);
_selectCharacterScreen.gameObject.SetActive(true);
}
/// <summary>
/// Callback when the login is complete.
/// Then get the list of characters
/// </summary>
private void OnLoginComplete(bool result)
{
if (!result)
{
BMLogger.LogError("Login failed");
return;
}
UnityGameSession.GetRoleListAsync(OnGetRoleListComplete);
}
/// <summary>
/// Callback when the list of characters is retrieved.
/// Then move to the select character screen
/// </summary>
private void OnGetRoleListComplete(List<RoleInfo> roleInfos)
{
_roleInfos = roleInfos;
}
private void OnClickSelectCharacter(RoleInfo roleInfo)
{
UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete);
}
private void OnSelectRoleComplete(RoleInfo roleInfo)
{
context.Post(_ =>
{
isDoneWorldRender = false;
isDoneNPCRender = false;
Action actLoadChar = () =>
{
if (!isDoneNPCRender || !isDoneWorldRender)
{
return;
}
};
SceneLoader.SceneLoadProcess = SceneLoadProcess.Loading;
SceneLoader.LoadingProgress = 0;
LoadingSceneController.Instance.ShowLoadingScene(true);
#if TESTFAST
string nameScene = "LoginScene";
SceneManager.UnloadSceneAsync(nameScene);
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
#else
string nameScene = "NPCRender";
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single, (value) =>
{
isDoneNPCRender = value;
actLoadChar?.Invoke();
});
nameScene = "a61";
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Additive, (value) =>
{
isDoneWorldRender = value;
actLoadChar?.Invoke();
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
});
#endif
}, null);
}
private async void OnEnterWorldComplete()
{
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("");
await Task.Delay(1000);
}
//private void OnInventoryReceived(List<InventoryItem> inventoryData)
//{
// _inventoryUI.DisplayInventory(inventoryData);
//}
#if UNITY_EDITOR
private void OnValidate()
{
if (_usernameInputField == null)
{
// find childrend with name "username"
_usernameInputField = transform.Find("username").GetComponent<TMP_InputField>();
}
if (_passwordInputField == null)
{
// find childrend with name "password"
_passwordInputField = transform.Find("password").GetComponent<TMP_InputField>();
}
if (_loginButton == null)
{
// find childrend with name "LoginBtn"
_loginButton = transform.Find("LoginBtn").GetComponent<Button>();
}
}
#endif
}
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Network;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace BrewMonster.UI
{
/// <summary>
/// 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
/// </summary>
public class LoginScreenUI : MonoBehaviour
{
[SerializeField] private TMP_InputField _usernameInputField;
[SerializeField] private TMP_InputField _passwordInputField;
[SerializeField] private Button _loginButton;
[SerializeField] private SelecScreenCharacter _selectCharacterScreen;
private List<RoleInfo> _roleInfos;
private List<RoleInfo> _currentRoles;
private RoleInfo _pendingCreatedRole;
bool isDoneWorldRender = false;
bool isDoneNPCRender = false;
private SynchronizationContext context;
public AudioClip loginBGM;
void Start()
{
AudioManager.Instance.PlayBGM(loginBGM, 1.5f);
_loginButton.onClick.AddListener(OnLoginButtonClicked);
context = SynchronizationContext.Current;
_usernameInputField.text = PlayerPrefs.GetString("username", "");
_passwordInputField.text = PlayerPrefs.GetString("password", "");
}
// Update is called once per frame
void Update()
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete);
_roleInfos = null;
}
#if UNITY_EDITOR
if (Input.GetKeyUp(KeyCode.LeftAlt))
{
_usernameInputField.text = "test004";
_passwordInputField.text = "123456";
}
if (Input.GetKeyUp(KeyCode.Tab))
{
_usernameInputField.text = "test002";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
#endif
}
public async void OnLoginButtonClicked()
{
BMLogger.Log("OnLoginButtonClicked");
string username = _usernameInputField.text;
string password = _passwordInputField.text;
// UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", username);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
await UnityGameSession.Login(username, password, OnLoginComplete);
_selectCharacterScreen.gameObject.SetActive(true);
}
/// <summary>
/// Callback when the login is complete.
/// Then get the list of characters
/// </summary>
private void OnLoginComplete(bool result)
{
if (!result)
{
BMLogger.LogError("Login failed");
return;
}
UnityGameSession.GetRoleListAsync(OnGetRoleListComplete);
}
/// <summary>
/// Callback when the list of characters is retrieved.
/// Then move to the select character screen
/// </summary>
private void OnGetRoleListComplete(List<RoleInfo> roleInfos)
{
if (roleInfos == null)
{
BMLogger.LogError("OnGetRoleListComplete: roleInfos is null");
// Keep whatever is currently shown; don't overwrite UI state with null.
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<RoleInfo>(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;
}
private void OnClickSelectCharacter(RoleInfo roleInfo)
{
UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete);
}
/// <summary>
/// Callback when a new character is created.
/// Refreshes the role list and keeps the character selection screen visible.
/// </summary>
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<RoleInfo>();
}
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;
LoadingSceneController.Instance.ShowLoadingScene(true);
#if TESTFAST
string nameScene = "LoginScene";
SceneManager.UnloadSceneAsync(nameScene);
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
#else
string nameScene = "NPCRender";
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single, (value) =>
{
isDoneNPCRender = value;
actLoadChar?.Invoke();
});
nameScene = "a61";
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Additive, (value) =>
{
isDoneWorldRender = value;
actLoadChar?.Invoke();
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
});
#endif
}, null);
}
private async void OnEnterWorldComplete()
{
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("");
await Task.Delay(1000);
}
//private void OnInventoryReceived(List<InventoryItem> inventoryData)
//{
// _inventoryUI.DisplayInventory(inventoryData);
//}
#if UNITY_EDITOR
private void OnValidate()
{
if (_usernameInputField == null)
{
// find childrend with name "username"
_usernameInputField = transform.Find("username").GetComponent<TMP_InputField>();
}
if (_passwordInputField == null)
{
// find childrend with name "password"
_passwordInputField = transform.Find("password").GetComponent<TMP_InputField>();
}
if (_loginButton == null)
{
// find childrend with name "LoginBtn"
_loginButton = transform.Find("LoginBtn").GetComponent<Button>();
}
}
#endif
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fd9306e92f60c254aba22e7fa622d485
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+4 -1
View File
@@ -578,7 +578,10 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
characterItemPrefab: {fileID: 5263746738484752443, guid: 726ee9eade6587245ac1b55d2335e9b9, type: 3}
addCharacterItemPrefab: {fileID: 5263746738484752443, guid: a0b31ed0940ec9942b243a0b8cec8ad3, type: 3}
parentItems: {fileID: 2643174602035272289}
createCharacterButton: {fileID: 0}
createCharacterScreen: {fileID: 0}
--- !u!1 &7510180475820570348
GameObject:
m_ObjectHideFlags: 0
@@ -717,7 +720,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &6444858493086001938
RectTransform:
m_ObjectHideFlags: 0
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a0b31ed0940ec9942b243a0b8cec8ad3
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+111 -5
View File
@@ -2,6 +2,7 @@ using CSNetwork.Protocols.RPCData;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using BrewMonster;
namespace BrewMonster.UI
@@ -9,16 +10,121 @@ namespace BrewMonster.UI
public class SelecScreenCharacter : MonoBehaviour
{
[SerializeField] private GameObject characterItemPrefab;
[SerializeField] private GameObject addCharacterItemPrefab;
[SerializeField] private RectTransform parentItems;
[SerializeField] private Button createCharacterButton;
[SerializeField] private CreateCharacterScreen createCharacterScreen;
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar)
private Action<RoleInfo> _onClickItemChar;
private Action<RoleInfo> _onCreateCharacterComplete;
private void Start()
{
foreach (RoleInfo info in roleInfos)
if (createCharacterButton != null)
{
CharacterItemUI item = Instantiate(characterItemPrefab, parentItems).GetComponent<CharacterItemUI>();
item.InitItem(info, OnClickItemChar);
createCharacterButton.onClick.AddListener(OnCreateCharacterClicked);
}
}
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar, Action<RoleInfo> onCreateCharacterComplete = null)
{
_onClickItemChar = OnClickItemChar;
_onCreateCharacterComplete = onCreateCharacterComplete;
// Clear existing items
if (parentItems != null)
{
foreach (Transform child in parentItems)
{
Destroy(child.gameObject);
}
}
// Create character items
if (roleInfos != null)
{
foreach (RoleInfo info in roleInfos)
{
if (characterItemPrefab != null && parentItems != null)
{
CharacterItemUI item = Instantiate(characterItemPrefab, parentItems).GetComponent<CharacterItemUI>();
item.InitItem(info, OnClickItemChar);
}
}
// If number of roles < 8, spawn addCharacterItemPrefab and hide createCharacterButton
if (roleInfos.Count < 8)
{
// Hide the createCharacterButton
if (createCharacterButton != null)
{
createCharacterButton.gameObject.SetActive(false);
}
// Spawn addCharacterItemPrefab
if (addCharacterItemPrefab != null && parentItems != null)
{
GameObject addCharacterItem = Instantiate(addCharacterItemPrefab, parentItems);
// Set up click handler for the add character item
Button addButton = addCharacterItem.GetComponent<Button>();
if (addButton == null)
{
addButton = addCharacterItem.GetComponentInChildren<Button>();
}
if (addButton != null)
{
addButton.onClick.RemoveAllListeners();
addButton.onClick.AddListener(OnCreateCharacterClicked);
}
}
}
else
{
// Show the createCharacterButton if we have 8 or more roles
if (createCharacterButton != null)
{
createCharacterButton.gameObject.SetActive(true);
}
}
}
else
{
// If roleInfos is null, show createCharacterButton
if (createCharacterButton != null)
{
createCharacterButton.gameObject.SetActive(true);
}
}
}
private void OnCreateCharacterClicked()
{
if (createCharacterScreen != null)
{
gameObject.SetActive(false);
createCharacterScreen.Show(OnCreateCharacterComplete, OnCreateCharacterCancel);
}
}
private void OnCreateCharacterComplete(RoleInfo newRole)
{
if (createCharacterScreen != null)
{
createCharacterScreen.Hide();
}
gameObject.SetActive(true);
_onCreateCharacterComplete?.Invoke(newRole);
}
private void OnCreateCharacterCancel()
{
if (createCharacterScreen != null)
{
createCharacterScreen.Hide();
}
gameObject.SetActive(true);
}
}
}