7a96c7e252
# Conflicts: # Assets/AddressableAssetsData/AddressableAssetSettings.asset # Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset # Assets/PerfectWorld/Scripts/MainFiles/EC_Game.cs # Assets/PerfectWorld/Scripts/Move/CECPlayer.cs # Assets/PerfectWorld/Scripts/NPC/CECNPC.cs # Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs # Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs # Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs # Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs # Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs # Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPetList.cs # Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPetList.cs.meta # Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs # Assets/Scenes/a61.unity # Assets/Scripts/CECGameRun.cs # Assets/Scripts/CECHostPlayer.cs # Assets/Scripts/CECUIManager.cs
617 lines
23 KiB
C#
617 lines
23 KiB
C#
using BrewMonster;
|
||
using BrewMonster.Scripts.Task;
|
||
using CSNetwork;
|
||
using Cysharp.Threading.Tasks;
|
||
using ModelRenderer.Scripts.GameData;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Globalization;
|
||
using System.IO;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEngine.AddressableAssets;
|
||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||
using CSNetwork.GPDataType;
|
||
|
||
namespace BrewMonster.Network
|
||
{
|
||
public partial class EC_Game
|
||
{
|
||
#region Fields
|
||
|
||
private static CECFactionMan m_pFactionMan; // Faction manager
|
||
public static bool g_bEnableFortressDeclareWar = false;
|
||
private static ATaskTemplMan m_pTaskMan; // Task template manager
|
||
private static elementdataman m_pElementDataMan; // global element templates manager
|
||
private static CECGameRun m_pGameRun => CECGameRun.Instance; // Game running object
|
||
private static CECGFXCaster m_pGFXCaster; // GFX caster
|
||
|
||
private static BrewMonster.CECStringTab m_FixedMsgs; // Fixed message table
|
||
private static BrewMonster.CECStringTab m_ItemDesc; // Item desciption string table
|
||
private static BrewMonster.CECStringTab m_ItemExtDesc; // Item extend description string table
|
||
private static BrewMonster.CECStringTab m_ItemExtProp; // Item extend prop string table
|
||
private static BrewMonster.CECStringTab ItemColTab; // Item color string table
|
||
private static Dictionary<int, int> m_SuiteEquipTab; // Item suite string table
|
||
private static BrewMonster.CECStringTab m_SkillDesc = new CECStringTab(); // Skill description string table
|
||
private static BrewMonster.CECStringTab m_BuffDesc; // Buff description string table
|
||
|
||
private static Dictionary<int, ItemMsgMapEntry> m_ItemMsgMap; // TemplateId -> (MessageId, DisplayMode)
|
||
private static CECConfigs m_pConfigs;
|
||
private static int m_iCurCursor; // Current cursor
|
||
private static List<int> m_PetAutoSkills = new List<int>();
|
||
|
||
#endregion
|
||
|
||
#region Properties
|
||
|
||
public static ATaskTemplMan GetTaskTemplateMan()
|
||
{
|
||
return m_pTaskMan;
|
||
}
|
||
|
||
public static elementdataman GetElementDataMan()
|
||
{
|
||
return m_pElementDataMan;
|
||
}
|
||
|
||
// String table getters
|
||
public static CECFactionMan GetFactionMan() { return m_pFactionMan; }
|
||
public static BrewMonster.CECStringTab GetFixedMsgs()
|
||
{
|
||
return m_FixedMsgs;
|
||
}
|
||
|
||
public static BrewMonster.CECStringTab GetItemDesc()
|
||
{
|
||
return m_ItemDesc;
|
||
}
|
||
|
||
public static BrewMonster.CECStringTab GetItemExtDesc()
|
||
{
|
||
return m_ItemExtDesc;
|
||
}
|
||
|
||
public static BrewMonster.CECStringTab GetSkillDesc()
|
||
{
|
||
return m_SkillDesc;
|
||
}
|
||
|
||
public static BrewMonster.CECStringTab GetBuffDesc()
|
||
{
|
||
return m_BuffDesc;
|
||
}
|
||
public static BrewMonster.CECStringTab GetItemExtProp()
|
||
{
|
||
return m_ItemExtProp;
|
||
}
|
||
public static BrewMonster.CECStringTab GetItemColTab()
|
||
{
|
||
return ItemColTab;
|
||
}
|
||
public static Dictionary<int, int> GetSuiteEquipTab()
|
||
{
|
||
return m_SuiteEquipTab;
|
||
}
|
||
public static bool TryGetItemMsg(int templateId, out int messageId, out int displayMode)
|
||
{
|
||
messageId = 0;
|
||
displayMode = 0;
|
||
if (m_ItemMsgMap != null && m_ItemMsgMap.TryGetValue(templateId, out var entry))
|
||
{
|
||
messageId = entry.MessageId;
|
||
displayMode = entry.DisplayMode;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Public Methods
|
||
|
||
public static bool Init()
|
||
{
|
||
m_pElementDataMan = ElementDataManProvider.GetElementDataMan();
|
||
|
||
// Load task templates
|
||
// if (m_pTaskMan == null) m_pTaskMan = new ATaskTemplMan();
|
||
m_pTaskMan = new ATaskTemplMan();
|
||
m_pTaskMan.Init(m_pElementDataMan);
|
||
m_pConfigs = new CECConfigs(); /*ElementClient.g_GameCfgs*/;
|
||
if (!m_pTaskMan.InitStorageTask())
|
||
{
|
||
BMLogger.LogError("[Dat]- CECGame::Init, Storage task Init Failed!");
|
||
// return false;
|
||
}
|
||
|
||
// Create GFX caster
|
||
if (m_pGFXCaster == null)
|
||
{
|
||
m_pGFXCaster = new CECGFXCaster();
|
||
// return false;
|
||
}
|
||
GetGameRun().Init();
|
||
InitializeStringTables();
|
||
|
||
// Load coord_data.txt (C++: Configs/Coord_data.txt) for clickable task links auto-move.
|
||
// 加载 coord_data.txt(C++:Configs/Coord_data.txt)用于任务可点击链接的自动移动。
|
||
LoadObjectCoord();
|
||
LoadPetAutoSkill();
|
||
return true;
|
||
}
|
||
public static CECConfigs GetConfigs() { return m_pConfigs; }
|
||
//todo release?
|
||
|
||
/// <summary>
|
||
/// Initialize all string tables with their respective data files
|
||
/// </summary>
|
||
private static void InitializeStringTables()
|
||
{
|
||
// Initialize string table instances
|
||
m_FixedMsgs = new BrewMonster.CECStringTab();
|
||
m_ItemDesc = new BrewMonster.CECStringTab();
|
||
m_ItemExtDesc = new BrewMonster.CECStringTab();
|
||
m_SkillDesc = new BrewMonster.CECStringTab();
|
||
m_BuffDesc = new BrewMonster.CECStringTab();
|
||
m_ItemExtProp = new BrewMonster.CECStringTab();
|
||
ItemColTab = new BrewMonster.CECStringTab();
|
||
m_SuiteEquipTab = new Dictionary<int, int>();
|
||
try
|
||
{
|
||
// Addressables-only loading (no StreamingAssets/configs file IO).
|
||
// These must match the Addressables "Address" values configured in `Assets/AddressableAssetsData/...`.
|
||
Addressables.InitializeAsync().WaitForCompletion();
|
||
|
||
var fixedMsgTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/fixed_msg.txt").WaitForCompletion();
|
||
if (!m_FixedMsgs.InitFromTextAsset(fixedMsgTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load fixed_msg.txt");
|
||
}
|
||
|
||
var itemDescTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/item_desc.txt").WaitForCompletion();
|
||
if (!m_ItemDesc.InitFromTextAsset(itemDescTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load item_desc.txt");
|
||
}
|
||
|
||
var itemExtDescTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/item_ext_desc.txt").WaitForCompletion();
|
||
if (!m_ItemExtDesc.InitFromTextAsset(itemExtDescTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load item_ext_desc.txt");
|
||
}
|
||
|
||
var skillStrTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/skillstr.txt").WaitForCompletion();
|
||
if (!m_SkillDesc.InitFromTextAsset(skillStrTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load skillstr.txt");
|
||
}
|
||
var itemExtPropTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/item_ext_prop.txt").WaitForCompletion();
|
||
if (!m_ItemExtProp.InitFromTextAsset(itemExtPropTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load item_ext_prop.txt");
|
||
}
|
||
var itemColTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/item_col.txt").WaitForCompletion();
|
||
if (!ItemColTab.InitFromTextAsset(itemColTa, true))
|
||
{
|
||
Debug.LogWarning("[EC_Game] Failed to load item_col.txt");
|
||
}
|
||
|
||
// Note: There's no buff_desc.txt file in the configs folder
|
||
// You may need to create this file or use a different source for buff descriptions
|
||
// (If you add it to Addressables later, load it here.)
|
||
// BuildSuiteEquipTab() is now called from ElementDataManProvider after data is loaded
|
||
// BuildSuiteEquipTab() 现在在 ElementDataManProvider 数据加载完成后调用
|
||
// Load item message map (template -> message id)
|
||
LoadItemMsgMap();
|
||
|
||
Debug.Log("[EC_Game] String tables initialized successfully");
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"[EC_Game] Error initializing string tables: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private struct ItemMsgMapEntry
|
||
{
|
||
public int MessageId;
|
||
public int DisplayMode;
|
||
}
|
||
|
||
private static void LoadItemMsgMap()
|
||
{
|
||
m_ItemMsgMap = new Dictionary<int, ItemMsgMapEntry>();
|
||
try
|
||
{
|
||
Addressables.InitializeAsync().WaitForCompletion();
|
||
var mapTa = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/item_msg_map.txt").WaitForCompletion();
|
||
if (mapTa == null || string.IsNullOrEmpty(mapTa.text))
|
||
{
|
||
Debug.LogWarning(
|
||
"[EC_Game] item_msg_map.txt not found; descriptions will fall back to template IDs");
|
||
return;
|
||
}
|
||
|
||
using var sr = new StringReader(mapTa.text);
|
||
string raw;
|
||
while ((raw = sr.ReadLine()) != null)
|
||
{
|
||
var line = raw.Trim();
|
||
if (line.Length == 0) continue;
|
||
if (line.StartsWith("//")) continue;
|
||
|
||
// Expect: templateId <ws> messageId <ws> displayMode
|
||
var parts = line.Split(new char[] { '\t', ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
|
||
if (parts.Length < 2) continue;
|
||
if (!int.TryParse(parts[0], out int templateId)) continue;
|
||
if (!int.TryParse(parts[1], out int messageId)) continue;
|
||
int displayMode = 0;
|
||
if (parts.Length >= 3) int.TryParse(parts[2], out displayMode);
|
||
|
||
m_ItemMsgMap[templateId] = new ItemMsgMapEntry { MessageId = messageId, DisplayMode = displayMode };
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogWarning($"[EC_Game] Failed to load item_msg_map: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public static CECGameRun GetGameRun()
|
||
{
|
||
return m_pGameRun;
|
||
}
|
||
|
||
public static CECGFXCaster GetGFXCaster()
|
||
{
|
||
if (m_pGFXCaster == null) m_pGFXCaster = new CECGFXCaster();
|
||
return m_pGFXCaster;
|
||
}
|
||
|
||
// Change current cursor
|
||
public static int ChangeCursor(int iCursor)
|
||
{
|
||
if (iCursor == m_iCurCursor)
|
||
return iCursor;
|
||
|
||
// if (m_aCursors[iCursor])
|
||
// m_pA3DDevice->SetCursor(m_aCursors[iCursor]);
|
||
//
|
||
// // force show this cursor
|
||
// ShowCursor(g_pGame->GetA3DDevice()->GetShowCursor());
|
||
//
|
||
// if( l_idMainThread != GetCurrentThreadId() )
|
||
// {
|
||
// // ::SetCursor must be called from main thread to take effects, so here we should post a WM_SETCURSOR message
|
||
// // to ensure the main thread receive WM_SETCURSOR and update the cursor again
|
||
// PostMessage(m_GameInit.hWnd, WM_SETCURSOR, (WPARAM)m_GameInit.hWnd, MAKELPARAM(HTCLIENT, WM_MOUSEMOVE));
|
||
// }
|
||
|
||
int iOldCursor = m_iCurCursor;
|
||
m_iCurCursor = iCursor;
|
||
return iOldCursor;
|
||
}
|
||
|
||
// Get server GMT(UTC) time
|
||
public static int GetServerGMTTime()
|
||
{
|
||
long unixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
return (int)unixTime + m_iTimeError;
|
||
}
|
||
|
||
#region Dummy Methods for itemdataman
|
||
public static int addon_generate_arg(DATA_TYPE type, addon_data data, int arg_num/*��ʼ�IJ�������*/)
|
||
{
|
||
return arg_num;
|
||
}
|
||
public static void get_item_guid(uint id, out int g1, out int g2)
|
||
{
|
||
g1 = 0;
|
||
g2 = 1;
|
||
}
|
||
public static int addon_update_ess_data(addon_data data, object essence,int ess_size, prerequisition require)
|
||
{
|
||
return 0;
|
||
}
|
||
public static void update_require_data(ref prerequisition require)
|
||
{
|
||
require.durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
|
||
require.max_durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
|
||
}
|
||
public static void set_to_classid(DATA_TYPE type, byte[] data, int major_type)
|
||
{
|
||
}
|
||
#endregion
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Coord_data.txt support (C++: Configs/Coord_data.txt, CECGame::LoadObjectCoord/GetObjectCoord)
|
||
//
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
private static readonly Dictionary<string, List<OBJECT_COORD>> m_CoordTab =
|
||
new Dictionary<string, List<OBJECT_COORD>>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
private static bool m_bCoordLoaded = false;
|
||
|
||
public static bool LoadObjectCoord()
|
||
{
|
||
if (m_bCoordLoaded)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
try
|
||
{
|
||
Addressables.InitializeAsync().WaitForCompletion();
|
||
var ta = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/coord_data.txt").WaitForCompletion();
|
||
if (ta == null)
|
||
{
|
||
Debug.LogError("[EC_Game] LoadObjectCoord: failed to load Addressable 'Assets/Addressable/coord_data.txt'");
|
||
return false;
|
||
}
|
||
|
||
ParseCoordDataText(ta.text);
|
||
m_bCoordLoaded = true;
|
||
Debug.Log($"[EC_Game] LoadObjectCoord: loaded {m_CoordTab.Count} coord keys from coord_data.txt");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"[EC_Game] LoadObjectCoord exception: {ex}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static void ParseCoordDataText(string text)
|
||
{
|
||
m_CoordTab.Clear();
|
||
if (string.IsNullOrEmpty(text))
|
||
{
|
||
return;
|
||
}
|
||
|
||
using var sr = new StringReader(text);
|
||
string line;
|
||
int lineNo = 0;
|
||
|
||
while ((line = sr.ReadLine()) != null)
|
||
{
|
||
lineNo++;
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
line = line.Trim();
|
||
if (line.StartsWith("#", StringComparison.Ordinal) || line.StartsWith("//", StringComparison.Ordinal))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (lineNo == 1 && line.StartsWith("ID", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
string[] parts = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
|
||
if (parts.Length < 5)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
string key = parts[0];
|
||
string map = parts[1];
|
||
|
||
if (!float.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out float x) ||
|
||
!float.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out float y) ||
|
||
!float.TryParse(parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out float z))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var coord = new OBJECT_COORD
|
||
{
|
||
strMap = map,
|
||
vPos = new A3DVECTOR3(x, y, z),
|
||
};
|
||
|
||
if (!m_CoordTab.TryGetValue(key, out var list))
|
||
{
|
||
list = new List<OBJECT_COORD>(1);
|
||
m_CoordTab[key] = list;
|
||
}
|
||
|
||
list.Add(coord);
|
||
}
|
||
}
|
||
|
||
public static bool TryGetFirstObjectCoord(string targetId, out Vector3 pos, out string map)
|
||
{
|
||
pos = default;
|
||
map = null;
|
||
|
||
if (string.IsNullOrWhiteSpace(targetId))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (!m_bCoordLoaded)
|
||
{
|
||
LoadObjectCoord();
|
||
}
|
||
|
||
if (!m_CoordTab.TryGetValue(targetId, out var list) || list == null || list.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
pos = new Vector3(list[0].vPos.x, list[0].vPos.y, list[0].vPos.z);
|
||
map = list[0].strMap;
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Build suite equip tab mapping. Must be called after m_pElementDataMan is initialized and data is loaded.
|
||
/// 构建套装装备表映射。必须在 m_pElementDataMan 初始化且数据加载完成后调用。
|
||
/// </summary>
|
||
public static void BuildSuiteEquipTab()
|
||
{
|
||
if (m_pElementDataMan == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Ensure m_SuiteEquipTab is initialized
|
||
// 确保 m_SuiteEquipTab 已初始化
|
||
if (m_SuiteEquipTab == null)
|
||
{
|
||
m_SuiteEquipTab = new Dictionary<int, int>();
|
||
}
|
||
|
||
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
|
||
elementdataman _edm = ElementDataManProvider.GetElementDataMan();
|
||
for (int i = 0; i < _edm.essence_id_data_type_map.Count; i++)
|
||
{
|
||
uint tid = _edm.get_data_id(ID_SPACE.ID_SPACE_ESSENCE, i, ref DataType);
|
||
object pData = _edm.get_data_ptr(tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||
switch (DataType)
|
||
{
|
||
case DATA_TYPE.DT_SUITE_ESSENCE:
|
||
SUITE_ESSENCE pSuiteEss = (SUITE_ESSENCE)pData;
|
||
pSuiteEss.max_equips = 0;
|
||
for (int j=0; j<12; j++)
|
||
{
|
||
if( pSuiteEss.equipments[j].id != 0 )
|
||
{
|
||
pSuiteEss.max_equips ++;
|
||
int index = (int)pSuiteEss.equipments[j].id;
|
||
m_SuiteEquipTab[index] = (int)tid;
|
||
}
|
||
}
|
||
break;
|
||
case DATA_TYPE.DT_POKER_SUITE_ESSENCE:
|
||
POKER_SUITE_ESSENCE pPokerSuiteEss = (POKER_SUITE_ESSENCE)pData;
|
||
for (int j=0; j<6; j++)
|
||
{
|
||
if( pPokerSuiteEss.list[j] != 0 )
|
||
{
|
||
int index = (int)pPokerSuiteEss.list[j];
|
||
m_SuiteEquipTab[index] = (int)tid;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case DATA_TYPE.DT_FASHION_SUITE_ESSENCE:
|
||
FASHION_SUITE_ESSENCE pFashionSuiteEss = (FASHION_SUITE_ESSENCE)pData;
|
||
for (int j=0; j<6; j++)
|
||
{
|
||
if( pFashionSuiteEss.list[j] != 0 )
|
||
{
|
||
int index = (int)pFashionSuiteEss.list[j];
|
||
m_SuiteEquipTab[index] = (int)tid;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
public static int GetItemNameColorIdx(int tid, int iDefIndex = 0)
|
||
{
|
||
int iIndex = iDefIndex;
|
||
string color = ItemColTab.GetWideString(tid);
|
||
if (!string.IsNullOrEmpty(color))
|
||
iIndex = color.GetHashCode();
|
||
if (iIndex < 0 || iIndex >= 10)
|
||
{
|
||
iIndex = 0;
|
||
}
|
||
return iIndex;
|
||
}
|
||
|
||
public static int GetObjectCoord(string strTargetID, ref List<OBJECT_COORD> TargetCoord)
|
||
{
|
||
int count = 0;
|
||
foreach(var coord in TargetCoord)
|
||
{
|
||
if(coord.strMap == strTargetID)
|
||
{
|
||
count++;
|
||
TargetCoord.Add(coord);
|
||
}
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
public static bool IsPetAutoSkill(int skill_id)
|
||
{
|
||
return m_PetAutoSkills.Contains(skill_id);
|
||
}
|
||
|
||
// Load the pet auto skill table
|
||
public static async UniTask<bool> LoadPetAutoSkill()
|
||
{
|
||
string pszFilename = "Assets/Addressable/petautoskill.txt";
|
||
var ta = await LoadStringTableTextAssetByAddressables(pszFilename);
|
||
if (ta == null || string.IsNullOrEmpty(ta.text))
|
||
{
|
||
BMLogger.LogError($"[AUIManager] ImportStringTable failed: cannot load Addressables TextAsset for key='{pszFilename}'");
|
||
return false;
|
||
}
|
||
|
||
using (var sr = new StringReader(ta.text))
|
||
{
|
||
string line;
|
||
while ((line = sr.ReadLine()) != null)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
continue;
|
||
|
||
if (int.TryParse(line, out int key))
|
||
{
|
||
m_PetAutoSkills.Add(key);
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private static async UniTask<TextAsset> LoadStringTableTextAssetByAddressables(string key)
|
||
{
|
||
try
|
||
{
|
||
// Initialize Addressables if not already initialized (Unity-safe)
|
||
await Addressables.InitializeAsync().ToUniTask();
|
||
|
||
// Load using Addressables directly with WaitForCompletion (Unity-safe, won't deadlock)
|
||
// This matches the pattern used in EC_Game.cs
|
||
var handle = Addressables.LoadAssetAsync<TextAsset>(key);
|
||
var textAsset = await handle.ToUniTask();
|
||
|
||
if (handle.Status == AsyncOperationStatus.Succeeded && textAsset != null)
|
||
{
|
||
// Keep the handle valid; string tables are used for the whole session
|
||
// Note: We don't release the handle here to keep the asset loaded
|
||
return textAsset;
|
||
}
|
||
|
||
if (handle.IsValid())
|
||
{
|
||
Addressables.Release(handle);
|
||
}
|
||
|
||
BMLogger.LogError($"[AUIManager] Failed to load TextAsset for key='{key}'");
|
||
return null;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
BMLogger.LogError($"[AUIManager] LoadStringTableTextAssetByAddressables exception for key='{key}': {e}");
|
||
return null;
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
} |