Compare commits

...

10 Commits

Author SHA1 Message Date
64e0bf2282 修复异步中没有yield return的错误 2025-10-18 02:47:55 +08:00
41d502b239 异步更新 2025-10-13 19:47:39 +08:00
6271171ab4 Save 2025-10-10 01:00:53 +08:00
7906203f84 开放ES3JsonReader构造函数供Interaction直接使用 2025-10-09 20:00:42 +08:00
dfa00340f5 EP Interaction 2025-10-09 14:15:28 +08:00
9b9264a6cc 修复了ConventValue中Parse的错误 2025-10-08 21:43:14 +08:00
3c962267ab 新增PropertyListItem强制刷新 2025-10-06 16:08:27 +08:00
344260266c 更改AB包导出位置 2025-10-05 02:10:18 +08:00
040f167b6d 修复了窗口预制体中的错误 2025-10-04 23:10:34 +08:00
7887695a67 PlatformIndicator提升 2025-10-04 19:22:52 +08:00
11 changed files with 844 additions and 76 deletions

View File

@@ -1,7 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.IO; using System.IO;
using Convention.SO;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@@ -12,7 +9,7 @@ namespace Convention
[MenuItem("Convention/AssetBundle/Create for Android")] [MenuItem("Convention/AssetBundle/Create for Android")]
static void CreatAssetBundle() static void CreatAssetBundle()
{ {
string path = Path.Combine(Application.streamingAssetsPath, "AssetBundle", "Android"); string path = Path.Combine(Application.dataPath, "../", "AssetBundle", "Android");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
@@ -24,7 +21,7 @@ namespace Convention
[MenuItem("Convention/AssetBundle/Create for IOS")] [MenuItem("Convention/AssetBundle/Create for IOS")]
static void BuildAllAssetBundlesForIOS() static void BuildAllAssetBundlesForIOS()
{ {
string dirName = "AssetBundles/IOS/IOS"; string dirName = "AssetBundles/IOS/";
if (!Directory.Exists(dirName)) if (!Directory.Exists(dirName))
{ {
Directory.CreateDirectory(dirName); Directory.CreateDirectory(dirName);
@@ -37,7 +34,7 @@ namespace Convention
[MenuItem("Convention/AssetBundle/Create for Windows")] [MenuItem("Convention/AssetBundle/Create for Windows")]
static void CreatPCAssetBundleForwINDOWS() static void CreatPCAssetBundleForwINDOWS()
{ {
string path = Path.Combine(Application.streamingAssetsPath, "AssetBundle", "Windows"); string path = Path.Combine(Application.dataPath, "../", "AssetBundle", "Windows");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
@@ -49,7 +46,7 @@ namespace Convention
[MenuItem("Convention/AssetBundle/Create for Mac")] [MenuItem("Convention/AssetBundle/Create for Mac")]
static void CreatPCAssetBundleForMac() static void CreatPCAssetBundleForMac()
{ {
string path = Path.Combine(Application.streamingAssetsPath, "AssetBundle", "Mac"); string path = Path.Combine(Application.dataPath, "../", "AssetBundle", "Mac");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);

View File

@@ -96,8 +96,10 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_CurrentContextKey: 0 m_CurrentContextKey: 0
m_AllContextPlane: [] m_AllContextPlane:
WindowPlane: {fileID: 6262932319296346077} - plane: {fileID: 1743752460056994028}
root: {fileID: 6898088878883608404}
WindowPlane: {fileID: 2851218872878303137}
WindowBar: {fileID: 5431686027440357681} WindowBar: {fileID: 5431686027440357681}
--- !u!114 &8120993653923732271 --- !u!114 &8120993653923732271
MonoBehaviour: MonoBehaviour:
@@ -350,6 +352,7 @@ MonoBehaviour:
anchorMin: {x: 0, y: 0} anchorMin: {x: 0, y: 0}
pivot: {x: 0, y: 0} pivot: {x: 0, y: 0}
BeforeMaximizeWindowBackgroundColorA: 1 BeforeMaximizeWindowBackgroundColorA: 1
IsMaximizeWindowMode: 0
AdjustSizeToContainsRect: {fileID: 0} AdjustSizeToContainsRect: {fileID: 0}
--- !u!114 &6852005934204649840 --- !u!114 &6852005934204649840
MonoBehaviour: MonoBehaviour:
@@ -707,6 +710,17 @@ PrefabInstance:
addedObject: {fileID: 1225826108170619254} addedObject: {fileID: 1225826108170619254}
m_AddedComponents: [] m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 9f83398a7d855e44493acb9b240c01ca, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 9f83398a7d855e44493acb9b240c01ca, type: 3}
--- !u!114 &2851218872878303137 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6525725186002079832, guid: 9f83398a7d855e44493acb9b240c01ca, type: 3}
m_PrefabInstance: {fileID: 9007647915676817401}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 999be728ad5e8324baf45ccaf0f9c3d2, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &6898088878883608404 stripped --- !u!224 &6898088878883608404 stripped
RectTransform: RectTransform:
m_CorrespondingSourceObject: {fileID: 2502725167534981293, guid: 9f83398a7d855e44493acb9b240c01ca, type: 3} m_CorrespondingSourceObject: {fileID: 2502725167534981293, guid: 9f83398a7d855e44493acb9b240c01ca, type: 3}

View File

@@ -1085,6 +1085,7 @@ MonoBehaviour:
anchorMin: {x: 0, y: 0} anchorMin: {x: 0, y: 0}
pivot: {x: 0, y: 0} pivot: {x: 0, y: 0}
BeforeMaximizeWindowBackgroundColorA: 1 BeforeMaximizeWindowBackgroundColorA: 1
IsMaximizeWindowMode: 0
AdjustSizeToContainsRect: {fileID: 0} AdjustSizeToContainsRect: {fileID: 0}
--- !u!114 &3897188892033144510 --- !u!114 &3897188892033144510
MonoBehaviour: MonoBehaviour:

View File

@@ -1459,6 +1459,7 @@ MonoBehaviour:
anchorMin: {x: 0, y: 0} anchorMin: {x: 0, y: 0}
pivot: {x: 0, y: 0} pivot: {x: 0, y: 0}
BeforeMaximizeWindowBackgroundColorA: 1 BeforeMaximizeWindowBackgroundColorA: 1
IsMaximizeWindowMode: 0
AdjustSizeToContainsRect: {fileID: 0} AdjustSizeToContainsRect: {fileID: 0}
--- !u!114 &682559958368963457 --- !u!114 &682559958368963457
MonoBehaviour: MonoBehaviour:

View File

@@ -22,7 +22,7 @@ namespace ES3Internal
public StreamReader baseReader; public StreamReader baseReader;
internal ES3JSONReader(Stream stream, ES3Settings settings, bool readHeaderAndFooter = true) : base(settings, readHeaderAndFooter) public ES3JSONReader(Stream stream, ES3Settings settings, bool readHeaderAndFooter = true) : base(settings, readHeaderAndFooter)
{ {
this.baseReader = new StreamReader(stream); this.baseReader = new StreamReader(stream);

View File

@@ -13,6 +13,7 @@ using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.Scripting;
using UnityEngine.UI; using UnityEngine.UI;
namespace UnityEditor namespace UnityEditor
@@ -56,6 +57,19 @@ namespace Convention
public static string PersistentDataPath => Application.persistentDataPath; public static string PersistentDataPath => Application.persistentDataPath;
public static string DataPath => Application.dataPath; public static string DataPath => Application.dataPath;
public static string CurrentWorkPath => Environment.CurrentDirectory;
public static string Combine(string a, string b) => Path.Combine(a, b);
public static string CombineAsDir(string a, string b)
{
string name = b.Replace("\\", "/");
if (name.EndsWith("/") == false)
{
name += "/";
}
return Path.Combine(a, name);
}
} }
public static partial class Utility public static partial class Utility
@@ -425,7 +439,7 @@ namespace Convention
public static _T convert_xvalue<_T>([In] string str) public static _T convert_xvalue<_T>([In] string str)
{ {
Type type = typeof(_T); Type type = typeof(_T);
var parse_method = type.GetMethod("Parse"); var parse_method = type.GetMethod(nameof(int.Parse), BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
if (parse_method != null && if (parse_method != null &&
(parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) && (parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) &&
parse_method.GetParameters().Length == 1 && parse_method.GetParameters().Length == 1 &&
@@ -2192,3 +2206,29 @@ namespace Convention
} }
} }
namespace Convention
{
public static partial class ConventionUtility
{
/// <summary>
/// Unity engine function to disable the GC
/// </summary>
public static void GC_disable()
{
#if !UNITY_EDITOR
GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;
#endif
}
/// <summary>
/// Unity engine function to enable the GC
/// </summary>
public static void GC_enable()
{
#if !UNITY_EDITOR
GarbageCollector.GCMode = GarbageCollector.Mode.Enabled;
#endif
}
}
}

View File

@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
@@ -58,24 +57,28 @@ namespace Convention
}; };
} }
private string FullPath; private string OriginPath;
private FileSystemInfo OriginInfo; private FileSystemInfo OriginInfo;
public ToolFile(string path) public ToolFile(string path)
{ {
FullPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(path)); OriginPath = Environment.ExpandEnvironmentVariables(path).Replace('\\', '/');
Refresh(); Refresh();
} }
public override string ToString() public override string ToString()
{ {
return this.FullPath; return this.OriginPath;
} }
#region Path #region Path
public static implicit operator string(ToolFile data) => data.FullPath; public static implicit operator string(ToolFile data) => data.OriginPath;
public string GetAbsPath()
{
return Path.GetFullPath(OriginPath);
}
public string GetFullPath() public string GetFullPath()
{ {
return this.FullPath; return this.OriginPath;
} }
public string GetName(bool is_ignore_extension = false) public string GetName(bool is_ignore_extension = false)
{ {
@@ -86,13 +89,13 @@ namespace Convention
else if (OriginInfo is DirectoryInfo dinfo) else if (OriginInfo is DirectoryInfo dinfo)
return dinfo.Name; return dinfo.Name;
} }
var result = this.FullPath[..( var result = this.OriginPath[..(
(this.FullPath.Contains('.') && is_ignore_extension) (this.OriginPath.Contains('.') && is_ignore_extension)
? this.FullPath.LastIndexOf('.') ? this.OriginPath.LastIndexOf('.')
: ^0 : ^0
)] )]
[..( [..(
(this.FullPath[^1] == '/' || this.FullPath[^1] == '\\') (this.OriginPath[^1] == '/' || this.OriginPath[^1] == '\\')
? ^1 ? ^1
: ^0 : ^0
)]; )];
@@ -103,32 +106,32 @@ namespace Convention
{ {
if (IsDir()) if (IsDir())
return ""; return "";
return this.FullPath[( return this.OriginPath[(
(this.FullPath.Contains('.')) (this.OriginPath.Contains('.'))
? this.FullPath.LastIndexOf('.') ? this.OriginPath.LastIndexOf('.')
: ^0 : ^0
)..]; )..];
} }
public string GetFilename(bool is_without_extension = false) public string GetFilename(bool is_without_extension = false)
{ {
if (is_without_extension && Path.HasExtension(FullPath)) if (is_without_extension && Path.HasExtension(OriginPath))
{ {
return Path.GetFileNameWithoutExtension(FullPath); return Path.GetFileNameWithoutExtension(OriginPath);
} }
else if (FullPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || FullPath.EndsWith(Path.AltDirectorySeparatorChar.ToString())) else if (OriginPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || OriginPath.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
{ {
return Path.GetFileName(FullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); return Path.GetFileName(OriginPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
} }
else else
{ {
return Path.GetFileName(FullPath); return Path.GetFileName(OriginPath);
} }
} }
public string GetDir() public string GetDir()
{ {
return Path.GetDirectoryName(FullPath); return Path.GetDirectoryName(OriginPath);
} }
public ToolFile GetDirToolFile() public ToolFile GetDirToolFile()
@@ -138,7 +141,7 @@ namespace Convention
public string GetCurrentDirName() public string GetCurrentDirName()
{ {
return Path.GetDirectoryName(FullPath); return Path.GetDirectoryName(OriginPath);
} }
public ToolFile GetParentDir() public ToolFile GetParentDir()
@@ -150,7 +153,7 @@ namespace Convention
#region Exists #region Exists
public bool Exists() => File.Exists(FullPath) || Directory.Exists(FullPath); public bool Exists() => File.Exists(OriginPath) || Directory.Exists(OriginPath);
public static implicit operator bool(ToolFile file) => file.Exists(); public static implicit operator bool(ToolFile file) => file.Exists();
@@ -162,13 +165,13 @@ namespace Convention
OriginInfo = null; OriginInfo = null;
else if (IsDir()) else if (IsDir())
{ {
OriginInfo = new DirectoryInfo(FullPath); OriginInfo = new DirectoryInfo(OriginPath);
FullPath = Path.GetFullPath(FullPath); OriginPath = Path.GetFullPath(OriginPath);
} }
else else
{ {
OriginInfo = new FileInfo(FullPath); OriginInfo = new FileInfo(OriginPath);
FullPath = Path.GetFullPath(FullPath); OriginPath = Path.GetFullPath(OriginPath);
} }
return this; return this;
} }
@@ -181,7 +184,7 @@ namespace Convention
} }
public T LoadAsJson<T>(string key = "data") public T LoadAsJson<T>(string key = "data")
{ {
return ES3.Load<T>(key, FullPath); return ES3.Load<T>(key, OriginPath);
} }
public string LoadAsText() public string LoadAsText()
{ {
@@ -217,7 +220,7 @@ namespace Convention
if (IsFile() == false) if (IsFile() == false)
throw new InvalidOperationException("Target is not a file"); throw new InvalidOperationException("Target is not a file");
var lines = File.ReadAllLines(FullPath); var lines = File.ReadAllLines(OriginPath);
var result = new List<string[]>(); var result = new List<string[]>();
foreach (var line in lines) foreach (var line in lines)
@@ -244,11 +247,11 @@ namespace Convention
public Texture2D LoadAsImage() public Texture2D LoadAsImage()
{ {
return ES3Plugin.LoadImage(FullPath); return ES3Plugin.LoadImage(OriginPath);
} }
public IEnumerator LoadAsImage([In] Action<Texture2D> callback) public IEnumerator LoadAsImage([In] Action<Texture2D> callback)
{ {
UnityWebRequest request = UnityWebRequestTexture.GetTexture(FullPath); UnityWebRequest request = UnityWebRequestTexture.GetTexture(OriginPath);
yield return request.SendWebRequest(); yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success) if (request.result == UnityWebRequest.Result.Success)
@@ -259,11 +262,11 @@ namespace Convention
} }
public AudioClip LoadAsAudio() public AudioClip LoadAsAudio()
{ {
return ES3Plugin.LoadAudio(FullPath, GetAudioType(FullPath)); return ES3Plugin.LoadAudio(OriginPath, GetAudioType(OriginPath));
} }
public IEnumerator LoadAsAudio([In] Action<AudioClip> callback) public IEnumerator LoadAsAudio([In] Action<AudioClip> callback)
{ {
UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(FullPath, GetAudioType(FullPath)); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(OriginPath, GetAudioType(OriginPath));
yield return request.SendWebRequest(); yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success) if (request.result == UnityWebRequest.Result.Success)
@@ -274,18 +277,18 @@ namespace Convention
} }
public AssetBundle LoadAsAssetBundle() public AssetBundle LoadAsAssetBundle()
{ {
return AssetBundle.LoadFromFile(FullPath); return AssetBundle.LoadFromFile(OriginPath);
} }
public IEnumerator LoadAsAssetBundle([In] Action<AssetBundle> callback) public IEnumerator LoadAsAssetBundle([In] Action<AssetBundle> callback)
{ {
AssetBundleCreateRequest result = AssetBundle.LoadFromFileAsync(FullPath); AssetBundleCreateRequest result = AssetBundle.LoadFromFileAsync(OriginPath);
yield return result; yield return result;
callback(result.assetBundle); callback(result.assetBundle);
yield return null; yield return null;
} }
public IEnumerator LoadAsAssetBundle([In]Action<float> progress, [In] Action<AssetBundle> callback) public IEnumerator LoadAsAssetBundle([In]Action<float> progress, [In] Action<AssetBundle> callback)
{ {
AssetBundleCreateRequest result = AssetBundle.LoadFromFileAsync(FullPath); AssetBundleCreateRequest result = AssetBundle.LoadFromFileAsync(OriginPath);
while (result.isDone == false) while (result.isDone == false)
{ {
progress(result.progress); progress(result.progress);
@@ -311,11 +314,11 @@ namespace Convention
} }
public void SaveAsJson<T>(T data, string key = "data") public void SaveAsJson<T>(T data, string key = "data")
{ {
ES3.Save(key, data,FullPath); ES3.Save(key, data,OriginPath);
} }
public void SaveAsText(string data) public void SaveAsText(string data)
{ {
using var fs = new FileStream(FullPath, FileMode.CreateNew, FileAccess.Write); using var fs = new FileStream(OriginPath, FileMode.CreateNew, FileAccess.Write);
using var sw = new StreamWriter(fs); using var sw = new StreamWriter(fs);
sw.Write(data); sw.Write(data);
sw.Flush(); sw.Flush();
@@ -337,7 +340,7 @@ namespace Convention
public void SaveAsBinary(byte[] data) public void SaveAsBinary(byte[] data)
{ {
SaveDataAsBinary(FullPath, data, (OriginInfo as FileInfo).OpenWrite()); SaveDataAsBinary(OriginPath, data, (OriginInfo as FileInfo).OpenWrite());
} }
public void SaveAsCsv(List<string[]> csvData) public void SaveAsCsv(List<string[]> csvData)
@@ -346,7 +349,7 @@ namespace Convention
throw new InvalidOperationException("Target is not a file"); throw new InvalidOperationException("Target is not a file");
var lines = csvData.Select(row => string.Join(",", row)); var lines = csvData.Select(row => string.Join(",", row));
File.WriteAllLines(FullPath, lines); File.WriteAllLines(OriginPath, lines);
} }
public void SaveAsXml(string xmlData) public void SaveAsXml(string xmlData)
@@ -380,9 +383,9 @@ namespace Convention
{ {
if (Exists()) if (Exists())
{ {
return Directory.Exists(this.FullPath); return Directory.Exists(this.OriginPath);
} }
return this.FullPath[^1] == '\\' || this.FullPath[^1] == '/'; return this.OriginPath[^1] == '\\' || this.OriginPath[^1] == '/';
} }
public bool IsFile() public bool IsFile()
@@ -420,7 +423,7 @@ namespace Convention
{ {
if (IsDir()) if (IsDir())
{ {
return GetDirectorySize(FullPath); return GetDirectorySize(OriginPath);
} }
else else
{ {
@@ -458,6 +461,20 @@ namespace Convention
#region Operator #region Operator
public static ToolFile operator |(ToolFile left, string rightPath) public static ToolFile operator |(ToolFile left, string rightPath)
{
if (rightPath == null)
{
return new ToolFile(left.IsDir() ? left.GetFullPath() : $"{left.GetFullPath().Replace('\\', '/')}/");
}
string first = left.GetFullPath().Replace('\\','/');
string second = rightPath.Replace('\\', '/');
if (first == "./")
return new ToolFile(second);
else if (first == "../")
return new ToolFile($"{new ToolFile(left.GetAbsPath()).GetParentDir()}/{second}");
return new ToolFile($"{first}/{second}");
}
public static ToolFile operator |(ToolFile left, ToolFile rightPath)
{ {
string lp = left.GetFullPath(); string lp = left.GetFullPath();
return new ToolFile(Path.Combine(lp, rightPath)); return new ToolFile(Path.Combine(lp, rightPath));
@@ -467,19 +484,19 @@ namespace Convention
{ {
if (obj is ToolFile other) if (obj is ToolFile other)
{ {
return Path.GetFullPath(FullPath).Equals(Path.GetFullPath(other.FullPath), StringComparison.OrdinalIgnoreCase); return Path.GetFullPath(OriginPath).Equals(Path.GetFullPath(other.OriginPath), StringComparison.OrdinalIgnoreCase);
} }
return false; return false;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return Path.GetFullPath(FullPath).GetHashCode(); return Path.GetFullPath(OriginPath).GetHashCode();
} }
public ToolFile Open(string path) public ToolFile Open(string path)
{ {
this.FullPath = path; this.OriginPath = path;
Refresh(); Refresh();
return this; return this;
} }
@@ -492,9 +509,9 @@ namespace Convention
if (Exists() == false) if (Exists() == false)
{ {
if (IsDir()) if (IsDir())
Directory.CreateDirectory(this.FullPath); Directory.CreateDirectory(this.OriginPath);
else else
File.Create(this.FullPath).Close(); File.Create(this.OriginPath).Close();
Refresh(); Refresh();
} }
return this; return this;
@@ -511,7 +528,7 @@ namespace Convention
var file = OriginInfo as FileInfo; var file = OriginInfo as FileInfo;
file.MoveTo(newPath); file.MoveTo(newPath);
} }
FullPath = newPath; OriginPath = newPath;
return this; return this;
} }
public ToolFile Move(string path) public ToolFile Move(string path)
@@ -537,7 +554,7 @@ namespace Convention
public ToolFile Copy(string targetPath = null) public ToolFile Copy(string targetPath = null)
{ {
if (targetPath == null) if (targetPath == null)
return new ToolFile(FullPath); return new ToolFile(OriginPath);
if (!Exists()) if (!Exists())
throw new FileNotFoundException("File not found"); throw new FileNotFoundException("File not found");
@@ -547,9 +564,9 @@ namespace Convention
targetFile = targetFile | GetFilename(); targetFile = targetFile | GetFilename();
if (IsDir()) if (IsDir())
CopyDirectory(FullPath, targetFile.GetFullPath()); CopyDirectory(OriginPath, targetFile.GetFullPath());
else else
File.Copy(FullPath, targetFile.GetFullPath()); File.Copy(OriginPath, targetFile.GetFullPath());
return targetFile; return targetFile;
} }
@@ -575,9 +592,9 @@ namespace Convention
public ToolFile Delete() public ToolFile Delete()
{ {
if (IsDir()) if (IsDir())
Directory.Delete(FullPath); Directory.Delete(OriginPath);
else else
File.Delete(FullPath); File.Delete(OriginPath);
return this; return this;
} }
@@ -598,7 +615,7 @@ namespace Convention
} }
public ToolFile TryCreateParentPath() public ToolFile TryCreateParentPath()
{ {
string dirPath = Path.GetDirectoryName(FullPath); string dirPath = Path.GetDirectoryName(OriginPath);
if (!string.IsNullOrEmpty(dirPath) && !Directory.Exists(dirPath)) if (!string.IsNullOrEmpty(dirPath) && !Directory.Exists(dirPath))
{ {
Directory.CreateDirectory(dirPath); Directory.CreateDirectory(dirPath);
@@ -609,14 +626,14 @@ namespace Convention
{ {
if (!IsDir()) if (!IsDir())
throw new InvalidOperationException("Target is not a directory"); throw new InvalidOperationException("Target is not a directory");
return Directory.GetFileSystemEntries(FullPath).ToList(); return Directory.GetFileSystemEntries(OriginPath).ToList();
} }
public List<ToolFile> DirToolFileIter() public List<ToolFile> DirToolFileIter()
{ {
if (!IsDir()) if (!IsDir())
throw new InvalidOperationException("Target is not a directory"); throw new InvalidOperationException("Target is not a directory");
var result = new List<ToolFile>(); var result = new List<ToolFile>();
foreach (var entry in Directory.GetFileSystemEntries(FullPath)) foreach (var entry in Directory.GetFileSystemEntries(OriginPath))
{ {
result.Add(new ToolFile(entry)); result.Add(new ToolFile(entry));
} }
@@ -624,7 +641,7 @@ namespace Convention
} }
public ToolFile BackToParentDir() public ToolFile BackToParentDir()
{ {
FullPath = GetDir(); OriginPath = GetDir();
Refresh(); Refresh();
return this; return this;
} }
@@ -633,7 +650,7 @@ namespace Convention
if (!IsDir()) if (!IsDir())
throw new InvalidOperationException("Target is not a directory"); throw new InvalidOperationException("Target is not a directory");
var entries = Directory.GetFileSystemEntries(FullPath); var entries = Directory.GetFileSystemEntries(OriginPath);
if (ignore_folder) if (ignore_folder)
{ {
return entries.Count(entry => File.Exists(entry)); return entries.Count(entry => File.Exists(entry));
@@ -740,13 +757,13 @@ namespace Convention
{ {
if (IsDir()) if (IsDir())
{ {
ZipFile.CreateFromDirectory(FullPath, outputPath); ZipFile.CreateFromDirectory(OriginPath, outputPath);
} }
else else
{ {
using (var archive = ZipFile.Open(outputPath, ZipArchiveMode.Create)) using (var archive = ZipFile.Open(outputPath, ZipArchiveMode.Create))
{ {
archive.CreateEntryFromFile(FullPath, GetFilename()); archive.CreateEntryFromFile(OriginPath, GetFilename());
} }
} }
} }
@@ -778,7 +795,7 @@ namespace Convention
try try
{ {
ZipFile.ExtractToDirectory(FullPath, outputPath); ZipFile.ExtractToDirectory(OriginPath, outputPath);
return new ToolFile(outputPath); return new ToolFile(outputPath);
} }
catch (Exception ex) catch (Exception ex)
@@ -897,7 +914,7 @@ namespace Convention
try try
{ {
using (var hashAlgorithm = GetHashAlgorithm(algorithm)) using (var hashAlgorithm = GetHashAlgorithm(algorithm))
using (var stream = File.OpenRead(FullPath)) using (var stream = File.OpenRead(OriginPath))
{ {
byte[] hash = hashAlgorithm.ComputeHash(stream); byte[] hash = hashAlgorithm.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLower(); return BitConverter.ToString(hash).Replace("-", "").ToLower();
@@ -955,7 +972,7 @@ namespace Convention
// 注意:这是一个简化实现,实际的文件监控需要更复杂的实现 // 注意:这是一个简化实现,实际的文件监控需要更复杂的实现
// 可以使用 FileSystemWatcher 来实现完整功能 // 可以使用 FileSystemWatcher 来实现完整功能
var watcher = new FileSystemWatcher(FullPath) var watcher = new FileSystemWatcher(OriginPath)
{ {
IncludeSubdirectories = recursive, IncludeSubdirectories = recursive,
EnableRaisingEvents = true EnableRaisingEvents = true
@@ -1087,7 +1104,7 @@ namespace Convention
try try
{ {
var fileInfo = new FileInfo(FullPath); var fileInfo = new FileInfo(OriginPath);
var attributes = fileInfo.Attributes; var attributes = fileInfo.Attributes;
permissions["read"] = true; // 如果能获取到文件信息,说明可读 permissions["read"] = true; // 如果能获取到文件信息,说明可读
@@ -1114,7 +1131,7 @@ namespace Convention
try try
{ {
var fileInfo = new FileInfo(FullPath); var fileInfo = new FileInfo(OriginPath);
var attributes = fileInfo.Attributes; var attributes = fileInfo.Attributes;
if (write.HasValue) if (write.HasValue)

View File

@@ -0,0 +1,679 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Convention
{
/// <summary>
/// 统一的文件交互类支持本地文件、网络文件和localhost路径的自适应处理
/// 使用UnityWebRequest实现跨平台兼容支持WebGL和IL2CPP
///
/// <list type="bullet"><see cref="ToolFile"/>是依赖项</list>
/// </summary>
[Serializable]
public sealed class Interaction
{
#region Fields and Properties
private string originalPath;
private string processedPath;
private PathType pathType;
private object cachedData;
public string OriginalPath => originalPath;
public string ProcessedPath => processedPath;
public PathType Type => pathType;
public enum PathType
{
LocalFile, // 本地文件 (file://)
NetworkHTTP, // 网络HTTP (http://)
NetworkHTTPS, // 网络HTTPS (https://)
LocalServer, // 本地服务器 (localhost)
StreamingAssets // StreamingAssets目录
}
#endregion
public Interaction(string path)
{
SetPath(path);
}
#region Setup
/// <summary>
/// 设置并处理路径
/// </summary>
public Interaction SetPath(string path)
{
originalPath = path;
processedPath = ProcessPath(path);
pathType = DeterminePathType(path);
return this;
}
/// <summary>
/// 处理路径转换为UnityWebRequest可识别的格式
/// </summary>
private string ProcessPath(string path)
{
if (string.IsNullOrEmpty(path))
return path;
// 网络路径直接返回
if (path.StartsWith("http://") || path.StartsWith("https://"))
{
return path;
}
// localhost处理
if (path.StartsWith("localhost"))
{
return path.StartsWith("localhost/") ? "http://" + path : "http://localhost/" + path;
}
// StreamingAssets路径处理
if (path.StartsWith("StreamingAssets/") || path.StartsWith("StreamingAssets\\"))
{
return Path.Combine(Application.streamingAssetsPath, path.Substring(16)).Replace("\\", "/");
}
// 本地文件路径处理
string fullPath = Path.IsPathRooted(path) ? path : Path.GetFullPath(path);
// WebGL平台特殊处理
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
// WebGL只能访问StreamingAssets尝试构建StreamingAssets路径
return Application.streamingAssetsPath + "/" + Path.GetFileName(fullPath);
}
// 其他平台使用file://协议
return "file:///" + fullPath.Replace("\\", "/");
}
/// <summary>
/// 确定路径类型
/// </summary>
private PathType DeterminePathType(string path)
{
if (string.IsNullOrEmpty(path))
return PathType.LocalFile;
if (path.StartsWith("https://"))
return PathType.NetworkHTTPS;
if (path.StartsWith("http://"))
return PathType.NetworkHTTP;
if (path.StartsWith("localhost"))
return PathType.LocalServer;
if (path.StartsWith("StreamingAssets"))
return PathType.StreamingAssets;
return PathType.LocalFile;
}
#endregion
#region Load
#region LoadAsync
public IEnumerator LoadAsTextAsync(Action<string> onSuccess, Action<string> onError = null)
{
using UnityWebRequest request = UnityWebRequest.Get(processedPath);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
cachedData = request.downloadHandler.text;
onSuccess?.Invoke((string)cachedData);
}
else
{
string errorMsg = $"Failed to load text from {originalPath}: {request.error}";
onError?.Invoke(errorMsg);
}
}
public IEnumerator LoadAsBinaryAsync(Action<byte[]> onSuccess, Action<string> onError = null)
{
using UnityWebRequest request = UnityWebRequest.Get(processedPath);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
cachedData = request.downloadHandler.data;
onSuccess?.Invoke((byte[])cachedData);
}
else
{
string errorMsg = $"Failed to load binary from {originalPath}: {request.error}";
onError?.Invoke(errorMsg);
}
}
public IEnumerator LoadAsBinaryAsync(Action<float> progress, Action<byte[]> onSuccess, Action<string> onError = null)
{
using UnityWebRequest request = UnityWebRequest.Get(processedPath);
var result = request.SendWebRequest();
while (result.isDone == false)
{
progress(result.progress);
yield return null;
}
if (request.result == UnityWebRequest.Result.Success)
{
cachedData = request.downloadHandler.data;
onSuccess?.Invoke((byte[])cachedData);
}
else
{
string errorMsg = $"Failed to load binary from {originalPath}: {request.error}";
onError?.Invoke(errorMsg);
}
}
public IEnumerator LoadAsRawJsonAsync<T>(Action<T> onSuccess, Action<string> onError = null)
{
yield return LoadAsTextAsync(
text =>
{
try
{
T result = JsonUtility.FromJson<T>(text);
cachedData = result;
onSuccess?.Invoke(result);
}
catch (Exception e)
{
onError?.Invoke($"Failed to parse JSON: {e.Message}");
}
},
onError
);
}
public IEnumerator LoadAsJsonAsync<T>(Action<T> onSuccess, Action<string> onError = null)
{
yield return LoadAsTextAsync(
text =>
{
try
{
using StreamReader reader = new(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(text)));
var jsonReader = new ES3Internal.ES3JSONReader(reader.BaseStream, new(originalPath));
onSuccess?.Invoke(jsonReader.Read<T>());
}
catch (Exception e)
{
onError?.Invoke($"Failed to parse JSON: {e.Message}");
}
},
onError
);
}
public IEnumerator LoadAsImageAsync(Action<Texture2D> onSuccess, Action<string> onError = null)
{
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(processedPath))
{
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
Texture2D texture = DownloadHandlerTexture.GetContent(request);
cachedData = texture;
onSuccess?.Invoke(texture);
}
else
{
string errorMsg = $"Failed to load image from {originalPath}: {request.error}";
onError?.Invoke(errorMsg);
}
}
}
public IEnumerator LoadAsAudioAsync(Action<AudioClip> onSuccess, Action<string> onError = null, AudioType audioType = AudioType.UNKNOWN)
{
if (audioType == AudioType.UNKNOWN)
audioType = GetAudioType(originalPath);
using (UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(processedPath, audioType))
{
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
AudioClip audioClip = DownloadHandlerAudioClip.GetContent(request);
cachedData = audioClip;
onSuccess?.Invoke(audioClip);
}
else
{
string errorMsg = $"Failed to load audio from {originalPath}: {request.error}";
onError?.Invoke(errorMsg);
}
}
}
public IEnumerator LoadAsAssetBundle(Action<float> progress, Action<AssetBundle> onSuccess, Action<string> onError = null)
{
AssetBundleCreateRequest request = null;
yield return LoadAsBinaryAsync(x => progress(x * 0.5f), data => request = AssetBundle.LoadFromMemoryAsync(data), onError);
while (request.isDone == false)
{
progress(0.5f + request.progress * 0.5f);
yield return null;
}
try
{
AssetBundle bundle = request.assetBundle;
if (bundle != null)
{
cachedData = bundle;
onSuccess?.Invoke(bundle);
}
else
{
onError?.Invoke($"Failed to load AssetBundle from data.");
}
}
catch (Exception e)
{
onError?.Invoke($"Failed to load AssetBundle: {e.Message}");
}
}
#endregion
#region Load Sync
public string LoadAsText()
{
string buffer = null;
bool isEnd = false;
bool IsError = false;
var it = LoadAsTextAsync(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
});
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if(IsError)
{
throw;
}
}
return buffer;
}
public byte[] LoadAsBinary()
{
byte[] buffer = null;
bool isEnd = false;
bool IsError = false;
var it = LoadAsBinaryAsync(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
});
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if (IsError)
{
throw;
}
}
return buffer;
}
public T LoadAsRawJson<T>()
{
T buffer = default;
bool isEnd = false;
bool IsError = false;
var it = LoadAsRawJsonAsync<T>(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
});
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if (IsError)
{
throw;
}
}
return buffer;
}
public T LoadAsJson<T>()
{
T buffer = default;
bool isEnd = false;
bool IsError = false;
var it = LoadAsJsonAsync<T>(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
});
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if (IsError)
{
throw;
}
}
return buffer;
}
public Texture2D LoadAsImage()
{
Texture2D buffer = null;
bool isEnd = false;
bool IsError = false;
var it = LoadAsImageAsync(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
});
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if (IsError)
{
throw;
}
}
return buffer;
}
public AudioClip LoadAsAudio(AudioType audioType = AudioType.UNKNOWN)
{
AudioClip buffer = null;
bool isEnd = false;
bool IsError = false;
var it = LoadAsAudioAsync(x =>
{
buffer = x;
isEnd = true;
}, e =>
{
IsError = true;
throw new Exception(e);
}, audioType);
try
{
while (!isEnd)
{
it.MoveNext();
}
}
catch
{
if (IsError)
{
throw;
}
}
return buffer;
}
#endregion
#endregion
#region Save
public Interaction SaveAsText(string content)
{
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
throw new NotSupportedException("WebGL平台不支持文件保存。");
}
if (pathType != PathType.LocalFile)
{
throw new NotSupportedException("仅支持保存到本地文件路径。");
}
new ToolFile(originalPath).SaveAsText(content);
return this;
}
public Interaction SaveAsBinary(byte[] data)
{
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
throw new NotSupportedException("WebGL平台不支持文件保存。");
}
if (pathType != PathType.LocalFile)
{
throw new NotSupportedException("仅支持保存到本地文件路径。");
}
new ToolFile(originalPath).SaveAsBinary(data);
return this;
}
public Interaction SaveAsRawJson<T>(T obj)
{
new ToolFile(originalPath).SaveAsRawJson(obj);
return this;
}
public Interaction SaveAsJson<T>(T obj)
{
new ToolFile(originalPath).SaveAsJson(obj);
return this;
}
#endregion
#region Tools
/// <summary>
/// 获取本地文件路径
/// </summary>
private string GetLocalPath()
{
if (processedPath.StartsWith("file:///"))
{
return processedPath.Substring(8);
}
return originalPath;
}
/// <summary>
/// 根据文件扩展名获取音频类型
/// </summary>
private AudioType GetAudioType(string path)
{
return BasicAudioSystem.GetAudioType(path);
}
/// <summary>
/// 检查文件是否存在
/// </summary>
public bool Exists()
{
if (pathType == PathType.LocalFile && Application.platform != RuntimePlatform.WebGLPlayer)
{
return File.Exists(GetLocalPath());
}
else
{
// TODO : 网络文件和WebGL平台需要通过请求检查
// 当前默认存在
return true;
}
}
/// <summary>
/// 异步检查文件是否存在
/// </summary>
public IEnumerator ExistsAsync(Action<bool> callback)
{
using UnityWebRequest request = UnityWebRequest.Head(processedPath);
yield return request.SendWebRequest();
callback?.Invoke(request.result == UnityWebRequest.Result.Success);
}
#endregion
#region Operator
public static implicit operator string(Interaction interaction) => interaction?.originalPath;
public override string ToString() => originalPath;
/// <summary>
/// 路径连接操作符
/// </summary>
public static Interaction operator |(Interaction left, string rightPath)
{
if (left.pathType == PathType.NetworkHTTP || left.pathType == PathType.NetworkHTTPS || left.pathType == PathType.LocalServer)
{
string baseUrl = left.originalPath;
string newUrl = baseUrl.EndsWith("/") ? baseUrl + rightPath : baseUrl + "/" + rightPath;
return new Interaction(newUrl);
}
else
{
string newPath = Path.Combine(left.originalPath, rightPath);
return new Interaction(newPath);
}
}
#endregion
#region Tools
/// <summary>
/// 获取文件名
/// </summary>
public string GetFilename()
{
if (pathType == PathType.NetworkHTTP || pathType == PathType.NetworkHTTPS || pathType == PathType.LocalServer)
{
var uri = new Uri(processedPath);
return Path.GetFileName(uri.AbsolutePath);
}
return Path.GetFileName(originalPath);
}
/// <summary>
/// 获取文件扩展名
/// </summary>
public string GetExtension()
{
return Path.GetExtension(GetFilename());
}
/// <summary>
/// 检查扩展名
/// </summary>
public bool ExtensionIs(params string[] extensions)
{
string ext = GetExtension().ToLower();
string extWithoutDot = ext.Length > 1 ? ext[1..] : "";
foreach (string extension in extensions)
{
string checkExt = extension.ToLower();
if (ext == checkExt || extWithoutDot == checkExt || ext == "." + checkExt)
return true;
}
return false;
}
#endregion
#region Tools
/// <summary>
/// 创建一个新的Interaction实例
/// </summary>
public static Interaction Create(string path) => new(path);
/// <summary>
/// 从StreamingAssets创建
/// </summary>
public static Interaction FromStreamingAssets(string relativePath)
{
return new Interaction("StreamingAssets/" + relativePath);
}
/// <summary>
/// 从本地服务器创建
/// </summary>
public static Interaction FromLocalhost(string path, int port = 80)
{
string url = port == 80 ? $"localhost/{path}" : $"localhost:{port}/{path}";
return new Interaction(url);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 731a0091a68afe9458917e4dd0267656
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -18,7 +18,7 @@ namespace Convention.WindowsUI
[Content, OnlyPlayMode, Ignore] public RectTransformInfo BeforeMaximizeWindow = null; [Content, OnlyPlayMode, Ignore] public RectTransformInfo BeforeMaximizeWindow = null;
[Content, OnlyPlayMode, Ignore] public float BeforeMaximizeWindowBackgroundColorA = 1f; [Content, OnlyPlayMode, Ignore] public float BeforeMaximizeWindowBackgroundColorA = 1f;
private bool IsMaximizeWindowMode = false; [SerializeField] private bool IsMaximizeWindowMode = false;
[Content, OnlyPlayMode] [Content, OnlyPlayMode]
public void MaximizeWindow() public void MaximizeWindow()
{ {

View File

@@ -118,6 +118,14 @@ namespace Convention.WindowsUI.Variant
dropdownImage.eulerAngles = new(0, 0, 0); dropdownImage.eulerAngles = new(0, 0, 0);
} }
public virtual void RefreshChilds()
{
ConventionUtility.CreateSteps()
.Next(() => Switch())
.Next(() => Switch())
.Invoke();
}
public List<ItemEntry> CreateSubPropertyItem([In] PropertiesWindow propertyWindow, int count) public List<ItemEntry> CreateSubPropertyItem([In] PropertiesWindow propertyWindow, int count)
{ {
List<ItemEntry> result = new(); List<ItemEntry> result = new();