ResourceCollection.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. //------------------------------------------------------------
  2. // Game Framework
  3. // Copyright © 2013-2021 loyalsoft. All rights reserved.
  4. // Homepage: http://www.game7000.com/
  5. // Feedback: http://www.game7000.com/
  6. //------------------------------------------------------------
  7. using GameFramework;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text.RegularExpressions;
  13. using System.Xml;
  14. using UnityEditor;
  15. using UnityEngine;
  16. namespace UnityGameFramework.Editor.ResourceTools
  17. {
  18. /// <summary>
  19. /// 资源集合。
  20. /// </summary>
  21. public sealed class ResourceCollection
  22. {
  23. private const string SceneExtension = ".unity";
  24. private static readonly Regex ResourceNameRegex = new Regex(@"^([A-Za-z0-9\._-]+/)*[A-Za-z0-9\._-]+$");
  25. private static readonly Regex ResourceVariantRegex = new Regex(@"^[a-z0-9_-]+$");
  26. private readonly string m_ConfigurationPath;
  27. private readonly SortedDictionary<string, Resource> m_Resources;
  28. private readonly SortedDictionary<string, Asset> m_Assets;
  29. public ResourceCollection()
  30. {
  31. m_ConfigurationPath = Type.GetConfigurationPath<ResourceCollectionConfigPathAttribute>() ?? Utility.Path.GetRegularPath(Path.Combine(Application.dataPath, "GameFramework/Configs/ResourceCollection.xml"));
  32. m_Resources = new SortedDictionary<string, Resource>(StringComparer.Ordinal);
  33. m_Assets = new SortedDictionary<string, Asset>(StringComparer.Ordinal);
  34. }
  35. public int ResourceCount
  36. {
  37. get
  38. {
  39. return m_Resources.Count;
  40. }
  41. }
  42. public int AssetCount
  43. {
  44. get
  45. {
  46. return m_Assets.Count;
  47. }
  48. }
  49. public event GameFrameworkAction<int, int> OnLoadingResource = null;
  50. public event GameFrameworkAction<int, int> OnLoadingAsset = null;
  51. public event GameFrameworkAction OnLoadCompleted = null;
  52. public void Clear()
  53. {
  54. m_Resources.Clear();
  55. m_Assets.Clear();
  56. }
  57. public bool Load()
  58. {
  59. Clear();
  60. if (!File.Exists(m_ConfigurationPath))
  61. {
  62. return false;
  63. }
  64. try
  65. {
  66. XmlDocument xmlDocument = new XmlDocument();
  67. xmlDocument.Load(m_ConfigurationPath);
  68. XmlNode xmlRoot = xmlDocument.SelectSingleNode("UnityGameFramework");
  69. XmlNode xmlCollection = xmlRoot.SelectSingleNode("ResourceCollection");
  70. XmlNode xmlResources = xmlCollection.SelectSingleNode("Resources");
  71. XmlNode xmlAssets = xmlCollection.SelectSingleNode("Assets");
  72. XmlNodeList xmlNodeList = null;
  73. XmlNode xmlNode = null;
  74. int count = 0;
  75. xmlNodeList = xmlResources.ChildNodes;
  76. count = xmlNodeList.Count;
  77. for (int i = 0; i < count; i++)
  78. {
  79. if (OnLoadingResource != null)
  80. {
  81. OnLoadingResource(i, count);
  82. }
  83. xmlNode = xmlNodeList.Item(i);
  84. if (xmlNode.Name != "Resource")
  85. {
  86. continue;
  87. }
  88. string name = xmlNode.Attributes.GetNamedItem("Name").Value;
  89. string variant = xmlNode.Attributes.GetNamedItem("Variant") != null ? xmlNode.Attributes.GetNamedItem("Variant").Value : null;
  90. string fileSystem = xmlNode.Attributes.GetNamedItem("FileSystem") != null ? xmlNode.Attributes.GetNamedItem("FileSystem").Value : null;
  91. byte loadType = 0;
  92. if (xmlNode.Attributes.GetNamedItem("LoadType") != null)
  93. {
  94. byte.TryParse(xmlNode.Attributes.GetNamedItem("LoadType").Value, out loadType);
  95. }
  96. bool packed = false;
  97. if (xmlNode.Attributes.GetNamedItem("Packed") != null)
  98. {
  99. bool.TryParse(xmlNode.Attributes.GetNamedItem("Packed").Value, out packed);
  100. }
  101. string[] resourceGroups = xmlNode.Attributes.GetNamedItem("ResourceGroups") != null ? xmlNode.Attributes.GetNamedItem("ResourceGroups").Value.Split(',') : null;
  102. if (!AddResource(name, variant, fileSystem, (LoadType)loadType, packed, resourceGroups))
  103. {
  104. Debug.LogWarning(Utility.Text.Format("Can not add resource '{0}'.", GetResourceFullName(name, variant)));
  105. continue;
  106. }
  107. }
  108. xmlNodeList = xmlAssets.ChildNodes;
  109. count = xmlNodeList.Count;
  110. for (int i = 0; i < count; i++)
  111. {
  112. if (OnLoadingAsset != null)
  113. {
  114. OnLoadingAsset(i, count);
  115. }
  116. xmlNode = xmlNodeList.Item(i);
  117. if (xmlNode.Name != "Asset")
  118. {
  119. continue;
  120. }
  121. string guid = xmlNode.Attributes.GetNamedItem("Guid").Value;
  122. string name = xmlNode.Attributes.GetNamedItem("ResourceName").Value;
  123. string variant = xmlNode.Attributes.GetNamedItem("ResourceVariant") != null ? xmlNode.Attributes.GetNamedItem("ResourceVariant").Value : null;
  124. if (!AssignAsset(guid, name, variant))
  125. {
  126. Debug.LogWarning(Utility.Text.Format("Can not assign asset '{0}' to resource '{1}'.", guid, GetResourceFullName(name, variant)));
  127. continue;
  128. }
  129. }
  130. if (OnLoadCompleted != null)
  131. {
  132. OnLoadCompleted();
  133. }
  134. return true;
  135. }
  136. catch
  137. {
  138. File.Delete(m_ConfigurationPath);
  139. if (OnLoadCompleted != null)
  140. {
  141. OnLoadCompleted();
  142. }
  143. return false;
  144. }
  145. }
  146. public bool Save()
  147. {
  148. try
  149. {
  150. XmlDocument xmlDocument = new XmlDocument();
  151. xmlDocument.AppendChild(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", null));
  152. XmlElement xmlRoot = xmlDocument.CreateElement("UnityGameFramework");
  153. xmlDocument.AppendChild(xmlRoot);
  154. XmlElement xmlCollection = xmlDocument.CreateElement("ResourceCollection");
  155. xmlRoot.AppendChild(xmlCollection);
  156. XmlElement xmlResources = xmlDocument.CreateElement("Resources");
  157. xmlCollection.AppendChild(xmlResources);
  158. XmlElement xmlAssets = xmlDocument.CreateElement("Assets");
  159. xmlCollection.AppendChild(xmlAssets);
  160. XmlElement xmlElement = null;
  161. XmlAttribute xmlAttribute = null;
  162. foreach (Resource resource in m_Resources.Values)
  163. {
  164. xmlElement = xmlDocument.CreateElement("Resource");
  165. xmlAttribute = xmlDocument.CreateAttribute("Name");
  166. xmlAttribute.Value = resource.Name;
  167. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  168. if (resource.Variant != null)
  169. {
  170. xmlAttribute = xmlDocument.CreateAttribute("Variant");
  171. xmlAttribute.Value = resource.Variant;
  172. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  173. }
  174. if (resource.FileSystem != null)
  175. {
  176. xmlAttribute = xmlDocument.CreateAttribute("FileSystem");
  177. xmlAttribute.Value = resource.FileSystem;
  178. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  179. }
  180. xmlAttribute = xmlDocument.CreateAttribute("LoadType");
  181. xmlAttribute.Value = ((byte)resource.LoadType).ToString();
  182. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  183. xmlAttribute = xmlDocument.CreateAttribute("Packed");
  184. xmlAttribute.Value = resource.Packed.ToString();
  185. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  186. string[] resourceGroups = resource.GetResourceGroups();
  187. if (resourceGroups.Length > 0)
  188. {
  189. xmlAttribute = xmlDocument.CreateAttribute("ResourceGroups");
  190. xmlAttribute.Value = string.Join(",", resourceGroups);
  191. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  192. }
  193. xmlResources.AppendChild(xmlElement);
  194. }
  195. foreach (Asset asset in m_Assets.Values)
  196. {
  197. xmlElement = xmlDocument.CreateElement("Asset");
  198. xmlAttribute = xmlDocument.CreateAttribute("Guid");
  199. xmlAttribute.Value = asset.Guid;
  200. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  201. xmlAttribute = xmlDocument.CreateAttribute("ResourceName");
  202. xmlAttribute.Value = asset.Resource.Name;
  203. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  204. if (asset.Resource.Variant != null)
  205. {
  206. xmlAttribute = xmlDocument.CreateAttribute("ResourceVariant");
  207. xmlAttribute.Value = asset.Resource.Variant;
  208. xmlElement.Attributes.SetNamedItem(xmlAttribute);
  209. }
  210. xmlAssets.AppendChild(xmlElement);
  211. }
  212. string configurationDirectoryName = Path.GetDirectoryName(m_ConfigurationPath);
  213. if (!Directory.Exists(configurationDirectoryName))
  214. {
  215. Directory.CreateDirectory(configurationDirectoryName);
  216. }
  217. xmlDocument.Save(m_ConfigurationPath);
  218. AssetDatabase.Refresh();
  219. return true;
  220. }
  221. catch
  222. {
  223. if (File.Exists(m_ConfigurationPath))
  224. {
  225. File.Delete(m_ConfigurationPath);
  226. }
  227. return false;
  228. }
  229. }
  230. public Resource[] GetResources()
  231. {
  232. return m_Resources.Values.ToArray();
  233. }
  234. public Resource GetResource(string name, string variant)
  235. {
  236. if (!IsValidResourceName(name, variant))
  237. {
  238. return null;
  239. }
  240. Resource resource = null;
  241. if (m_Resources.TryGetValue(GetResourceFullName(name, variant).ToLowerInvariant(), out resource))
  242. {
  243. return resource;
  244. }
  245. return null;
  246. }
  247. public bool HasResource(string name, string variant)
  248. {
  249. if (!IsValidResourceName(name, variant))
  250. {
  251. return false;
  252. }
  253. return m_Resources.ContainsKey(GetResourceFullName(name, variant).ToLowerInvariant());
  254. }
  255. public bool AddResource(string name, string variant, string fileSystem, LoadType loadType, bool packed)
  256. {
  257. return AddResource(name, variant, fileSystem, loadType, packed, null);
  258. }
  259. public bool AddResource(string name, string variant, string fileSystem, LoadType loadType, bool packed, string[] resourceGroups)
  260. {
  261. if (!IsValidResourceName(name, variant))
  262. {
  263. return false;
  264. }
  265. if (!IsAvailableResourceName(name, variant, null))
  266. {
  267. return false;
  268. }
  269. if (fileSystem != null && !ResourceNameRegex.IsMatch(fileSystem))
  270. {
  271. return false;
  272. }
  273. Resource resource = Resource.Create(name, variant, fileSystem, loadType, packed, resourceGroups);
  274. m_Resources.Add(resource.FullName.ToLowerInvariant(), resource);
  275. return true;
  276. }
  277. public bool RenameResource(string oldName, string oldVariant, string newName, string newVariant)
  278. {
  279. if (!IsValidResourceName(oldName, oldVariant) || !IsValidResourceName(newName, newVariant))
  280. {
  281. return false;
  282. }
  283. Resource resource = GetResource(oldName, oldVariant);
  284. if (resource == null)
  285. {
  286. return false;
  287. }
  288. if (oldName == newName && oldVariant == newVariant)
  289. {
  290. return true;
  291. }
  292. if (!IsAvailableResourceName(newName, newVariant, resource))
  293. {
  294. return false;
  295. }
  296. m_Resources.Remove(resource.FullName.ToLowerInvariant());
  297. resource.Rename(newName, newVariant);
  298. m_Resources.Add(resource.FullName.ToLowerInvariant(), resource);
  299. return true;
  300. }
  301. public bool RemoveResource(string name, string variant)
  302. {
  303. if (!IsValidResourceName(name, variant))
  304. {
  305. return false;
  306. }
  307. Resource resource = GetResource(name, variant);
  308. if (resource == null)
  309. {
  310. return false;
  311. }
  312. Asset[] assets = resource.GetAssets();
  313. resource.Clear();
  314. m_Resources.Remove(resource.FullName.ToLowerInvariant());
  315. foreach (Asset asset in assets)
  316. {
  317. m_Assets.Remove(asset.Guid);
  318. }
  319. return true;
  320. }
  321. public bool SetResourceLoadType(string name, string variant, LoadType loadType)
  322. {
  323. if (!IsValidResourceName(name, variant))
  324. {
  325. return false;
  326. }
  327. Resource resource = GetResource(name, variant);
  328. if (resource == null)
  329. {
  330. return false;
  331. }
  332. if ((loadType == LoadType.LoadFromBinary || loadType == LoadType.LoadFromBinaryAndQuickDecrypt || loadType == LoadType.LoadFromBinaryAndDecrypt) && resource.GetAssets().Length > 1)
  333. {
  334. return false;
  335. }
  336. resource.LoadType = loadType;
  337. return true;
  338. }
  339. public bool SetResourcePacked(string name, string variant, bool packed)
  340. {
  341. if (!IsValidResourceName(name, variant))
  342. {
  343. return false;
  344. }
  345. Resource resource = GetResource(name, variant);
  346. if (resource == null)
  347. {
  348. return false;
  349. }
  350. resource.Packed = packed;
  351. return true;
  352. }
  353. public Asset[] GetAssets()
  354. {
  355. return m_Assets.Values.ToArray();
  356. }
  357. public Asset[] GetAssets(string name, string variant)
  358. {
  359. if (!IsValidResourceName(name, variant))
  360. {
  361. return new Asset[0];
  362. }
  363. Resource resource = GetResource(name, variant);
  364. if (resource == null)
  365. {
  366. return new Asset[0];
  367. }
  368. return resource.GetAssets();
  369. }
  370. public Asset GetAsset(string guid)
  371. {
  372. if (string.IsNullOrEmpty(guid))
  373. {
  374. return null;
  375. }
  376. Asset asset = null;
  377. if (m_Assets.TryGetValue(guid, out asset))
  378. {
  379. return asset;
  380. }
  381. return null;
  382. }
  383. public bool HasAsset(string guid)
  384. {
  385. if (string.IsNullOrEmpty(guid))
  386. {
  387. return false;
  388. }
  389. return m_Assets.ContainsKey(guid);
  390. }
  391. public bool AssignAsset(string guid, string name, string variant)
  392. {
  393. if (string.IsNullOrEmpty(guid))
  394. {
  395. return false;
  396. }
  397. if (!IsValidResourceName(name, variant))
  398. {
  399. return false;
  400. }
  401. Resource resource = GetResource(name, variant);
  402. if (resource == null)
  403. {
  404. return false;
  405. }
  406. string assetName = AssetDatabase.GUIDToAssetPath(guid);
  407. if (string.IsNullOrEmpty(assetName))
  408. {
  409. return false;
  410. }
  411. Asset[] assetsInResource = resource.GetAssets();
  412. foreach (Asset assetInResource in assetsInResource)
  413. {
  414. if (assetInResource.Name == assetName)
  415. {
  416. continue;
  417. }
  418. if (assetInResource.Name.ToLowerInvariant() == assetName.ToLowerInvariant())
  419. {
  420. return false;
  421. }
  422. }
  423. bool isScene = assetName.EndsWith(SceneExtension, StringComparison.Ordinal);
  424. if (isScene && resource.AssetType == AssetType.Asset || !isScene && resource.AssetType == AssetType.Scene)
  425. {
  426. return false;
  427. }
  428. Asset asset = GetAsset(guid);
  429. if (resource.IsLoadFromBinary && assetsInResource.Length > 0 && asset != assetsInResource[0])
  430. {
  431. return false;
  432. }
  433. if (asset == null)
  434. {
  435. asset = Asset.Create(guid);
  436. m_Assets.Add(asset.Guid, asset);
  437. }
  438. resource.AssignAsset(asset, isScene);
  439. return true;
  440. }
  441. public bool UnassignAsset(string guid)
  442. {
  443. if (string.IsNullOrEmpty(guid))
  444. {
  445. return false;
  446. }
  447. Asset asset = GetAsset(guid);
  448. if (asset != null)
  449. {
  450. asset.Resource.UnassignAsset(asset);
  451. m_Assets.Remove(asset.Guid);
  452. }
  453. return true;
  454. }
  455. private string GetResourceFullName(string name, string variant)
  456. {
  457. return !string.IsNullOrEmpty(variant) ? Utility.Text.Format("{0}.{1}", name, variant) : name;
  458. }
  459. private bool IsValidResourceName(string name, string variant)
  460. {
  461. if (string.IsNullOrEmpty(name))
  462. {
  463. return false;
  464. }
  465. if (!ResourceNameRegex.IsMatch(name))
  466. {
  467. return false;
  468. }
  469. if (variant != null && !ResourceVariantRegex.IsMatch(variant))
  470. {
  471. return false;
  472. }
  473. return true;
  474. }
  475. private bool IsAvailableResourceName(string name, string variant, Resource current)
  476. {
  477. Resource found = GetResource(name, variant);
  478. if (found != null && found != current)
  479. {
  480. return false;
  481. }
  482. string[] foundPathNames = name.Split('/');
  483. foreach (Resource resource in m_Resources.Values)
  484. {
  485. if (current != null && resource == current)
  486. {
  487. continue;
  488. }
  489. if (resource.Name == name)
  490. {
  491. if (resource.Variant == null && variant != null)
  492. {
  493. return false;
  494. }
  495. if (resource.Variant != null && variant == null)
  496. {
  497. return false;
  498. }
  499. }
  500. if (resource.Name.Length > name.Length
  501. && resource.Name.IndexOf(name, StringComparison.CurrentCultureIgnoreCase) == 0
  502. && resource.Name[name.Length] == '/')
  503. {
  504. return false;
  505. }
  506. if (name.Length > resource.Name.Length
  507. && name.IndexOf(resource.Name, StringComparison.CurrentCultureIgnoreCase) == 0
  508. && name[resource.Name.Length] == '/')
  509. {
  510. return false;
  511. }
  512. string[] pathNames = resource.Name.Split('/');
  513. for (int i = 0; i < foundPathNames.Length - 1 && i < pathNames.Length - 1; i++)
  514. {
  515. if (foundPathNames[i].ToLowerInvariant() != pathNames[i].ToLowerInvariant())
  516. {
  517. break;
  518. }
  519. if (foundPathNames[i] != pathNames[i])
  520. {
  521. return false;
  522. }
  523. }
  524. }
  525. return true;
  526. }
  527. }
  528. }