mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-11-11 08:38:45 +00:00
提交
This commit is contained in:
3
JNFrame2/Assets/Plugins/SHFrame/Editor.meta
Normal file
3
JNFrame2/Assets/Plugins/SHFrame/Editor.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52ffd32d9bb44ec088d1d095ec79e968
|
||||
timeCreated: 1728700704
|
||||
115
JNFrame2/Assets/Plugins/SHFrame/Editor/CleanMaterial.cs
Normal file
115
JNFrame2/Assets/Plugins/SHFrame/Editor/CleanMaterial.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
// -----------------------------------------------
|
||||
// Copyright © Sirius. All rights reserved.
|
||||
// CreateTime: 2021/12/30 18:0:55
|
||||
// -----------------------------------------------
|
||||
/******************************************************************************
|
||||
* DESCRIPTION: 清理材质未使用的纹理贴图的引用
|
||||
*
|
||||
* Copyright (c) 2020, 谭伟俊 (TanWeijun)
|
||||
* All rights reserved
|
||||
*
|
||||
* COMPANY:
|
||||
* CREATED: 2020.03.01, 15:20, CST
|
||||
*******************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class CleanMaterial
|
||||
{
|
||||
[MenuItem("Assets/Tools/Clean Material")]
|
||||
public static void Clean()
|
||||
{
|
||||
Material[] materials = Selection.GetFiltered<Material>(SelectionMode.Assets | SelectionMode.DeepAssets);
|
||||
foreach (var material in materials)
|
||||
{
|
||||
CleanOneMaterial(material);
|
||||
}
|
||||
|
||||
Debug.Log("清理材质全部完成");
|
||||
}
|
||||
|
||||
private static bool CleanOneMaterial(Material _material)
|
||||
{
|
||||
// 收集材质使用到的所有纹理贴图
|
||||
HashSet<string> textureGUIDs = CollectTextureGUIDs(_material);
|
||||
|
||||
string materialPathName = Path.GetFullPath(AssetDatabase.GetAssetPath(_material));
|
||||
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
using (StreamReader reader = new StreamReader(materialPathName))
|
||||
{
|
||||
Regex regex = new Regex(@"\s+guid:\s+(\w+),");
|
||||
string line = reader.ReadLine();
|
||||
while (null != line)
|
||||
{
|
||||
if (line.Contains("m_Texture:"))
|
||||
{
|
||||
// 包含纹理贴图引用的行,使用正则表达式获取纹理贴图的guid
|
||||
Match match = regex.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
string textureGUID = match.Groups[1].Value;
|
||||
if (textureGUIDs.Contains(textureGUID))
|
||||
{
|
||||
strBuilder.AppendLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 材质没有用到纹理贴图,guid赋值为0来清除引用关系
|
||||
strBuilder.AppendLine(line.Substring(0, line.IndexOf("fileID:") + 7) + " 0}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strBuilder.AppendLine(line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strBuilder.AppendLine(line);
|
||||
}
|
||||
|
||||
line = reader.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(materialPathName))
|
||||
{
|
||||
writer.Write(strBuilder.ToString());
|
||||
}
|
||||
Debug.Log($"清理材质--{materialPathName}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static HashSet<string> CollectTextureGUIDs(Material _material)
|
||||
{
|
||||
HashSet<string> textureGUIDs = new HashSet<string>();
|
||||
for (int i = 0; i < ShaderUtil.GetPropertyCount(_material.shader); ++i)
|
||||
{
|
||||
if (ShaderUtil.ShaderPropertyType.TexEnv == ShaderUtil.GetPropertyType(_material.shader, i))
|
||||
{
|
||||
Texture texture = _material.GetTexture(ShaderUtil.GetPropertyName(_material.shader, i));
|
||||
if (null == texture)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string textureGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(texture));
|
||||
if (!textureGUIDs.Contains(textureGUID))
|
||||
{
|
||||
textureGUIDs.Add(textureGUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return textureGUIDs;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
JNFrame2/Assets/Plugins/SHFrame/Editor/CleanMaterial.cs.meta
Normal file
11
JNFrame2/Assets/Plugins/SHFrame/Editor/CleanMaterial.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 706f69076a7589b4397cd353fa8eae8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
126
JNFrame2/Assets/Plugins/SHFrame/Editor/DependAnalysis.cs
Normal file
126
JNFrame2/Assets/Plugins/SHFrame/Editor/DependAnalysis.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public class DependAnalysis : EditorWindow
|
||||
{
|
||||
private static Object[] targetObjects;
|
||||
private bool[] foldoutArr;
|
||||
private Object[][] beDependArr;
|
||||
private static int targetCount;
|
||||
private Vector2 scrollPos;
|
||||
string[] withoutExtensions = new string[] { ".prefab", ".unity", ".mat", ".asset", ".controller" };
|
||||
|
||||
[MenuItem("Assets/Tools/查找被引用", false, 19)]
|
||||
static void FindReferences()
|
||||
{
|
||||
targetObjects = Selection.GetFiltered<Object>(SelectionMode.Assets);
|
||||
targetCount = targetObjects == null ? 0 : targetObjects.Length;
|
||||
if (targetCount == 0) return;
|
||||
DependAnalysis window = GetWindow<DependAnalysis>("依赖分析");
|
||||
window.Init();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
beDependArr = new Object[targetCount][];
|
||||
foldoutArr = new bool[targetCount];
|
||||
EditorStyles.foldout.richText = true;
|
||||
for (int i = 0; i < targetCount; i++) beDependArr[i] = GetBeDepend(targetObjects[i]);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (beDependArr.Length != targetCount) return;
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
Object[] objArr;
|
||||
int count;
|
||||
string objName;
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
objArr = beDependArr[i];
|
||||
count = objArr == null ? 0 : objArr.Length;
|
||||
objName = Path.GetFileName(AssetDatabase.GetAssetPath(targetObjects[i]));
|
||||
string info = count == 0
|
||||
? $"<color=yellow>{objName}【{count}】</color>"
|
||||
: $"{objName}【{count}】";
|
||||
foldoutArr[i] = EditorGUILayout.Foldout(foldoutArr[i], info);
|
||||
if (foldoutArr[i])
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
foreach (var obj in objArr)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15);
|
||||
EditorGUILayout.ObjectField(obj, typeof(Object),true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15);
|
||||
EditorGUILayout.LabelField("【Null】");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找所有引用目标资源的物体
|
||||
/// </summary>
|
||||
/// <param name="target">目标资源</param>
|
||||
/// <returns></returns>
|
||||
private Object[] GetBeDepend(Object target)
|
||||
{
|
||||
if (target == null) return null;
|
||||
string path = AssetDatabase.GetAssetPath(target);
|
||||
if (string.IsNullOrEmpty(path)) return null;
|
||||
string guid = AssetDatabase.AssetPathToGUID(path);
|
||||
string[] files = Directory.GetFiles(Application.dataPath, "*",
|
||||
SearchOption.AllDirectories).Where(s => withoutExtensions.Contains(Path.GetExtension(s).ToLower()))
|
||||
.ToArray();
|
||||
List<Object> objects = new List<Object>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
string assetPath = file.Replace(Application.dataPath, "");
|
||||
assetPath = "Assets" + assetPath;
|
||||
string readText = File.ReadAllText(file);
|
||||
|
||||
if (!readText.StartsWith("%YAML"))
|
||||
{
|
||||
var depends = AssetDatabase.GetDependencies(assetPath, false);
|
||||
if (depends != null)
|
||||
{
|
||||
foreach (var dep in depends)
|
||||
{
|
||||
if (dep == path)
|
||||
{
|
||||
objects.Add(AssetDatabase.LoadAssetAtPath<Object>(assetPath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Regex.IsMatch(readText, guid)) objects.Add(AssetDatabase.LoadAssetAtPath<Object>(assetPath));
|
||||
}
|
||||
|
||||
return objects.ToArray();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
targetObjects = null;
|
||||
beDependArr = null;
|
||||
foldoutArr = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c15d3a97f5b069f469dd60573a7fd842
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
JNFrame2/Assets/Plugins/SHFrame/Editor/HybridCLR.meta
Normal file
8
JNFrame2/Assets/Plugins/SHFrame/Editor/HybridCLR.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92cecf72550b15c48b34487614f5fc16
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.IO;
|
||||
using HybridCLR.Editor;
|
||||
using HybridCLR.Editor.Commands;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SHFrame.Editor
|
||||
{
|
||||
public static class BuildAssetsCommand
|
||||
{
|
||||
public static string HybridCLRBuildCacheDir => Application.dataPath + "/HybridCLRBuildCache";
|
||||
|
||||
public static string AssetBundleOutputDir => $"{HybridCLRBuildCacheDir}/AssetBundleOutput";
|
||||
|
||||
public static string DllBytesDir => $"{Application.dataPath}/HotAssets/HotDlls";
|
||||
|
||||
public static string GetAssetBundleOutputDirByTarget(BuildTarget target)
|
||||
{
|
||||
return $"{AssetBundleOutputDir}/{target}";
|
||||
}
|
||||
|
||||
public static string ToRelativeAssetPath(string s)
|
||||
{
|
||||
return s.Substring(s.IndexOf("Assets/"));
|
||||
}
|
||||
|
||||
[MenuItem("HybridCLR/Build/泛型补充dll与热更新dll导出")]
|
||||
public static void BuildAndCopyABAOTHotUpdateDlls()
|
||||
{
|
||||
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
|
||||
CompileDllCommand.CompileDll(target);
|
||||
CopyABAOTHotUpdateDlls(target);
|
||||
}
|
||||
|
||||
public static void CopyABAOTHotUpdateDlls(BuildTarget target)
|
||||
{
|
||||
CopyAssetBundlesToStreamingAssets(target);
|
||||
|
||||
ClearEmptyFolder(DllBytesDir);
|
||||
|
||||
CopyAOTAssembliesToStreamingAssets();
|
||||
CopyHotUpdateAssembliesToStreamingAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
public static void CopyAOTAssembliesToStreamingAssets()
|
||||
{
|
||||
var target = EditorUserBuildSettings.activeBuildTarget;
|
||||
string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
|
||||
//string aotAssembliesDstDir = "D:\\User\\Dou\\Unitys\\2023fars\\Assets\\HotAsset\\HotDlls";
|
||||
string aotAssembliesDstDir = Application.streamingAssetsPath;
|
||||
|
||||
foreach (var dll in SettingsUtil.AOTAssemblyNames)
|
||||
{
|
||||
string srcDllPath = $"{aotAssembliesSrcDir}/{dll}.dll";
|
||||
if (!File.Exists(srcDllPath))
|
||||
{
|
||||
Debug.LogError($"ab中添加AOT补充元数据dll:{srcDllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。");
|
||||
continue;
|
||||
}
|
||||
string dllBytesPath = $"{DllBytesDir}/{dll}.dll.bytes";
|
||||
File.Copy(srcDllPath, dllBytesPath, true);
|
||||
Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyHotUpdateAssembliesToStreamingAssets()
|
||||
{
|
||||
var target = EditorUserBuildSettings.activeBuildTarget;
|
||||
|
||||
string hotfixDllSrcDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target);
|
||||
//string hotfixAssembliesDstDir = "D:\\User\\Dou\\Unitys\\2023fars\\Assets\\HotAsset\\HotDlls";
|
||||
string hotfixAssembliesDstDir = Application.streamingAssetsPath;
|
||||
|
||||
foreach (var dll in SettingsUtil.HotUpdateAssemblyFilesExcludePreserved)
|
||||
{
|
||||
string dllPath = $"{hotfixDllSrcDir}/{dll}";
|
||||
string dllBytesPath = $"{DllBytesDir}/{dll}.bytes";
|
||||
File.Copy(dllPath, dllBytesPath, true);
|
||||
Debug.Log($"[CopyHotUpdateAssembliesToStreamingAssets] copy hotfix dll {dllPath} -> {dllBytesPath}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyAssetBundlesToStreamingAssets(BuildTarget target)
|
||||
{
|
||||
string streamingAssetPathDst = Application.streamingAssetsPath;
|
||||
Directory.CreateDirectory(streamingAssetPathDst);
|
||||
string outputDir = GetAssetBundleOutputDirByTarget(target);
|
||||
var abs = new string[] { "prefabs" };
|
||||
foreach (var ab in abs)
|
||||
{
|
||||
string srcAb = ToRelativeAssetPath($"{outputDir}/{ab}");
|
||||
string dstAb = ToRelativeAssetPath($"{streamingAssetPathDst}/{ab}");
|
||||
Debug.Log($"[CopyAssetBundlesToStreamingAssets] copy assetbundle {srcAb} -> {dstAb}");
|
||||
AssetDatabase.CopyAsset(srcAb, dstAb);
|
||||
}
|
||||
}
|
||||
private static void ClearEmptyFolder(string folderPath)
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(folderPath);
|
||||
DirectoryInfo[] subDirs = dir.GetDirectories("*.*", SearchOption.AllDirectories);
|
||||
foreach (DirectoryInfo subDir in subDirs)
|
||||
{
|
||||
FileSystemInfo[] subFiles = subDir.GetFileSystemInfos();
|
||||
if (subFiles.Length == 0)
|
||||
{
|
||||
File.Delete(subDir.FullName + ".meta");
|
||||
subDir.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
FileInfo[] files = dir.GetFiles();
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
File.Delete(file.FullName);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a8357532218e6f49a9bb9401382d6d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.IO;
|
||||
using HybridCLR.Editor;
|
||||
using HybridCLR.Editor.Commands;
|
||||
using HybridCLR.Editor.Installer;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SHFrame.Editor
|
||||
{
|
||||
public class BuildPlayerCommand
|
||||
{
|
||||
public static void CopyAssets(string outputDir)
|
||||
{
|
||||
Directory.CreateDirectory(outputDir);
|
||||
|
||||
foreach (var srcFile in Directory.GetFiles(Application.streamingAssetsPath))
|
||||
{
|
||||
string dstFile = $"{outputDir}/{Path.GetFileName(srcFile)}";
|
||||
File.Copy(srcFile, dstFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InstallFromRepo()
|
||||
{
|
||||
var ic = new InstallerController();
|
||||
ic.InstallDefaultHybridCLR();
|
||||
}
|
||||
|
||||
public static void InstallBuildWin64()
|
||||
{
|
||||
InstallFromRepo();
|
||||
Build_Win64(true);
|
||||
}
|
||||
|
||||
[MenuItem("HybridCLR/Build/Win64")]
|
||||
public static void Build_Win64()
|
||||
{
|
||||
Build_Win64(false);
|
||||
}
|
||||
|
||||
public static void Build_Win64(bool existWhenCompleted)
|
||||
{
|
||||
BuildTarget target = BuildTarget.StandaloneWindows64;
|
||||
BuildTarget activeTarget = EditorUserBuildSettings.activeBuildTarget;
|
||||
if (activeTarget != BuildTarget.StandaloneWindows64 && activeTarget != BuildTarget.StandaloneWindows)
|
||||
{
|
||||
Debug.LogError("请先切到Win平台再打包");
|
||||
return;
|
||||
}
|
||||
// Get filename.
|
||||
string outputPath = $"{SettingsUtil.ProjectDir}/Release-Win64";
|
||||
|
||||
var buildOptions = BuildOptions.CompressWithLz4;
|
||||
|
||||
string location = $"{outputPath}/HybridCLRTrial.exe";
|
||||
|
||||
PrebuildCommand.GenerateAll();
|
||||
Debug.Log("====> Build App");
|
||||
BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions()
|
||||
{
|
||||
scenes = new string[] { "Assets/Scenes/main.unity" },
|
||||
locationPathName = location,
|
||||
options = buildOptions,
|
||||
target = target,
|
||||
targetGroup = BuildTargetGroup.Standalone,
|
||||
};
|
||||
|
||||
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
|
||||
if (report.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
|
||||
{
|
||||
Debug.LogError("打包失败");
|
||||
if (existWhenCompleted)
|
||||
{
|
||||
EditorApplication.Exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("====> 复制热更新资源和代码");
|
||||
BuildAssetsCommand.BuildAndCopyABAOTHotUpdateDlls();
|
||||
BashUtil.CopyDir(Application.streamingAssetsPath, $"{outputPath}/HybridCLRTrial_Data/StreamingAssets", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7796c8df856ac4a4a9d644329c1f0958
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
JNFrame2/Assets/Plugins/SHFrame/Editor/SHFrame.Editor.asmdef
Normal file
21
JNFrame2/Assets/Plugins/SHFrame/Editor/SHFrame.Editor.asmdef
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "SHFrame.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:e34a5702dd353724aa315fb8011f08c3",
|
||||
"GUID:4d1926c9df5b052469a1c63448b7609a",
|
||||
"GUID:2373f786d14518f44b0f475db77ba4de",
|
||||
"GUID:ac484fe5e443f4146a129450c27f000f"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7d50a21d47842f4b81d103bfc07e719
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
JNFrame2/Assets/Plugins/SHFrame/Editor/TextureSetting.cs
Normal file
87
JNFrame2/Assets/Plugins/SHFrame/Editor/TextureSetting.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class AutoSetTextureUISprite : AssetPostprocessor
|
||||
{
|
||||
void OnPreprocessTexture()
|
||||
{
|
||||
//自动设置类型
|
||||
if (assetPath.Contains("FGui"))
|
||||
{
|
||||
//fgui的图片资源需要设置为Sprite和不生成Mip Maps
|
||||
TextureImporter textureImporter = (TextureImporter)assetImporter;
|
||||
textureImporter.textureType = TextureImporterType.Default;
|
||||
textureImporter.textureShape = TextureImporterShape.Texture2D;
|
||||
textureImporter.mipmapEnabled = false;
|
||||
textureImporter.filterMode = FilterMode.Bilinear;
|
||||
}
|
||||
}
|
||||
|
||||
// private void OnPostprocessMaterial(Material material)
|
||||
// {
|
||||
// if (assetPath.Contains("FairyRes"))
|
||||
// {
|
||||
// material.SetInt("_StraightAlphaInput", 1);
|
||||
// AssetDatabase.SaveAssets();
|
||||
// }
|
||||
// }
|
||||
|
||||
// [MenuItem("Tools/ConvertGammaToLinearTex")]
|
||||
public static void ConvertGammaToLinearTex()
|
||||
{
|
||||
var assetGUIDs = Selection.assetGUIDs;
|
||||
foreach (var assetGUID in assetGUIDs)
|
||||
{
|
||||
var assetPath = AssetDatabase.GUIDToAssetPath(assetGUID);
|
||||
Debug.Log(assetPath);
|
||||
if (!assetPath.Contains(".png")) continue;
|
||||
|
||||
Texture2D srcTex = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
||||
if (srcTex != null)
|
||||
{
|
||||
Texture2D temp = new Texture2D(srcTex.width, srcTex.height, TextureFormat.RGBA32, true);
|
||||
Color[] pixels = srcTex.GetPixels();
|
||||
for (int j = 0; j < pixels.Length; j++)
|
||||
{
|
||||
Color pixel = pixels[j];
|
||||
pixel.r = Mathf.Pow(pixel.r, 2.2f);
|
||||
pixel.g = Mathf.Pow(pixel.g, 2.2f);
|
||||
pixel.b = Mathf.Pow(pixel.b, 2.2f);
|
||||
pixels[j] = pixel;
|
||||
}
|
||||
|
||||
temp.SetPixels(pixels);
|
||||
temp.Apply();
|
||||
var bytes = temp.EncodeToPNG();
|
||||
File.WriteAllBytes(assetPath, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
// List<string> selectedAsset = FindSelectionObject("*. png");
|
||||
// int count = selectedAsset.Count;
|
||||
// for (int i = 0; i < count; i++)
|
||||
// {
|
||||
// Texture2D srcTex = AssetDatabase.LoadAssetAtPath<Texture2D>(selectedAsset[i]);
|
||||
// if (srcTex != null)
|
||||
// {
|
||||
// Texture2D temp = new Texture2D(srcTex.width, srcTex.height, TextureFormat.RGBA32, true);
|
||||
// Color[] pixels = srcTex.GetPixels();
|
||||
// for (int j = 0; j < pixels.Length; j++)
|
||||
// {
|
||||
// Color pixel = pixels[i];
|
||||
// pixel.r = Mathf.Pow(pixel.r, 2.2f);
|
||||
// pixel.g = Mathf.Pow(pixel.g, 2.2f);
|
||||
// pixel.b = Mathf.Pow(pixel.b, 2.2f);
|
||||
// pixels[i] = pixel;
|
||||
// }
|
||||
//
|
||||
// temp.SetPixels(pixels);
|
||||
// temp.Apply();
|
||||
// var bytes = temp.EncodeToPNG();
|
||||
// File.WriteAllBytes(selectedAsset[i], bytes);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74bbc7b5200382a44b37699f1687e3c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80372f1a333653748a8443a51f860e63
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9855eda2b13a0084ab0def6f7ca74141
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityToolbarExtender;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public class EditorResourceMode
|
||||
{
|
||||
static class ToolbarStyles
|
||||
{
|
||||
public static readonly GUIStyle ToolBarExtenderBtnStyle;
|
||||
|
||||
public static readonly GUIStyle ToolBarTextStyle;
|
||||
|
||||
public static readonly GUIStyle ToolBarButtonGuiStyle;
|
||||
|
||||
static ToolbarStyles()
|
||||
{
|
||||
ToolBarExtenderBtnStyle = new GUIStyle("Command")
|
||||
{
|
||||
fontSize = 12,
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
imagePosition = ImagePosition.ImageAbove,
|
||||
fontStyle = FontStyle.Normal,
|
||||
fixedWidth = 60
|
||||
};
|
||||
|
||||
ToolBarTextStyle = new GUIStyle(ButtonStyleName)
|
||||
{
|
||||
padding = new RectOffset(2, 8, 2, 2),
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontStyle = FontStyle.Bold
|
||||
};
|
||||
|
||||
ToolBarButtonGuiStyle = new GUIStyle(ButtonStyleName)
|
||||
{
|
||||
padding = new RectOffset(2, 8, 2, 2),
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontStyle = FontStyle.Bold
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static EditorResourceMode()
|
||||
{
|
||||
ToolbarExtender.RightToolbarGUI.Add(OnToolbarGUI);
|
||||
_resourceModeIndex = EditorPrefs.GetInt("EditorResourceMode", 0);
|
||||
}
|
||||
|
||||
private const string ButtonStyleName = "Tab middle";
|
||||
static GUIStyle _buttonGuiStyle;
|
||||
|
||||
private static readonly string[] _resourceModeNames =
|
||||
{ "EditorMode (编辑器下的模拟模式)",
|
||||
"OfflinePlayMode (单机模式)",
|
||||
"HostPlayMode (联机运行模式)",
|
||||
"WebPlayMode (WebGL运行模式)"
|
||||
};
|
||||
|
||||
private static int _resourceModeIndex = 0;
|
||||
public static int ResourceModeIndex => _resourceModeIndex;
|
||||
|
||||
static void OnToolbarGUI()
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
|
||||
{
|
||||
// GUILayout.Label("资源加载模式:",ToolbarStyles.ToolBarTextStyle);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// 资源模式
|
||||
int selectedIndex = EditorGUILayout.Popup("", _resourceModeIndex, _resourceModeNames, ToolbarStyles.ToolBarButtonGuiStyle);
|
||||
// ReSharper disable once RedundantCheckBeforeAssignment
|
||||
if (selectedIndex != _resourceModeIndex)
|
||||
{
|
||||
Debug.Log($"更改编辑器资源运行模式 : {_resourceModeNames[selectedIndex]}");
|
||||
_resourceModeIndex = selectedIndex;
|
||||
EditorPrefs.SetInt("EditorResourceMode", selectedIndex);
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUILayout.Space(400);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce74293562bfebc45856ef1b22eb41b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityToolbarExtender;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public class SceneSwitchLeftButton
|
||||
{
|
||||
private static readonly string SceneMain = "Boot";
|
||||
|
||||
static SceneSwitchLeftButton()
|
||||
{
|
||||
ToolbarExtender.LeftToolbarGUI.Add(OnToolbarGUI);
|
||||
}
|
||||
|
||||
static readonly string ButtonStyleName = "Tab middle";
|
||||
static GUIStyle _buttonGuiStyle;
|
||||
|
||||
static void OnToolbarGUI()
|
||||
{
|
||||
_buttonGuiStyle ??= new GUIStyle(ButtonStyleName)
|
||||
{
|
||||
padding = new RectOffset(2, 8, 2, 2),
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontStyle = FontStyle.Bold,
|
||||
};
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(
|
||||
new GUIContent("Launcher", EditorGUIUtility.FindTexture("PlayButton"), $"Start Scene Launcher"),
|
||||
_buttonGuiStyle))
|
||||
{
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
EditorApplication.isPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneHelper.StartScene(SceneMain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class SceneHelper
|
||||
{
|
||||
static string _sceneToOpen;
|
||||
|
||||
public static void StartScene(string sceneName)
|
||||
{
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
EditorApplication.isPlaying = false;
|
||||
}
|
||||
|
||||
_sceneToOpen = sceneName;
|
||||
EditorApplication.update += OnUpdate;
|
||||
}
|
||||
|
||||
static void OnUpdate()
|
||||
{
|
||||
if (_sceneToOpen == null ||
|
||||
EditorApplication.isPlaying || EditorApplication.isPaused ||
|
||||
EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.update -= OnUpdate;
|
||||
|
||||
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:scene " + _sceneToOpen, null);
|
||||
if (guids.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("Couldn't find scene file");
|
||||
}
|
||||
else
|
||||
{
|
||||
string scenePath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
EditorSceneManager.OpenScene(scenePath);
|
||||
EditorApplication.isPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
_sceneToOpen = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f015ac6f30edc48409abd5eaeb7de23b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
using UnityEngine.UIElements;
|
||||
#else
|
||||
using UnityEngine.Experimental.UIElements;
|
||||
#endif
|
||||
|
||||
namespace UnityToolbarExtender
|
||||
{
|
||||
public static class ToolbarCallback
|
||||
{
|
||||
static Type m_toolbarType = typeof(Editor).Assembly.GetType("UnityEditor.Toolbar");
|
||||
static Type m_guiViewType = typeof(Editor).Assembly.GetType("UnityEditor.GUIView");
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
static Type m_iWindowBackendType = typeof(Editor).Assembly.GetType("UnityEditor.IWindowBackend");
|
||||
static PropertyInfo m_windowBackend = m_guiViewType.GetProperty("windowBackend",
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static PropertyInfo m_viewVisualTree = m_iWindowBackendType.GetProperty("visualTree",
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
#else
|
||||
static PropertyInfo m_viewVisualTree = m_guiViewType.GetProperty("visualTree",
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
#endif
|
||||
static FieldInfo m_imguiContainerOnGui = typeof(IMGUIContainer).GetField("m_OnGUIHandler",
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static ScriptableObject m_currentToolbar;
|
||||
|
||||
/// <summary>
|
||||
/// Callback for toolbar OnGUI method.
|
||||
/// </summary>
|
||||
public static Action OnToolbarGUI;
|
||||
public static Action OnToolbarGUILeft;
|
||||
public static Action OnToolbarGUIRight;
|
||||
|
||||
static ToolbarCallback()
|
||||
{
|
||||
EditorApplication.update -= OnUpdate;
|
||||
EditorApplication.update += OnUpdate;
|
||||
}
|
||||
|
||||
static void OnUpdate()
|
||||
{
|
||||
// Relying on the fact that toolbar is ScriptableObject and gets deleted when layout changes
|
||||
if (m_currentToolbar == null)
|
||||
{
|
||||
// Find toolbar
|
||||
var toolbars = Resources.FindObjectsOfTypeAll(m_toolbarType);
|
||||
m_currentToolbar = toolbars.Length > 0 ? (ScriptableObject) toolbars[0] : null;
|
||||
if (m_currentToolbar != null)
|
||||
{
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
var root = m_currentToolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var rawRoot = root.GetValue(m_currentToolbar);
|
||||
var mRoot = rawRoot as VisualElement;
|
||||
RegisterCallback("ToolbarZoneLeftAlign", OnToolbarGUILeft);
|
||||
RegisterCallback("ToolbarZoneRightAlign", OnToolbarGUIRight);
|
||||
|
||||
void RegisterCallback(string root, Action cb) {
|
||||
var toolbarZone = mRoot.Q(root);
|
||||
|
||||
var parent = new VisualElement()
|
||||
{
|
||||
style = {
|
||||
flexGrow = 1,
|
||||
flexDirection = FlexDirection.Row,
|
||||
}
|
||||
};
|
||||
var container = new IMGUIContainer();
|
||||
container.style.flexGrow = 1;
|
||||
container.onGUIHandler += () => {
|
||||
cb?.Invoke();
|
||||
};
|
||||
parent.Add(container);
|
||||
toolbarZone.Add(parent);
|
||||
}
|
||||
#else
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
var windowBackend = m_windowBackend.GetValue(m_currentToolbar);
|
||||
|
||||
// Get it's visual tree
|
||||
var visualTree = (VisualElement) m_viewVisualTree.GetValue(windowBackend, null);
|
||||
#else
|
||||
// Get it's visual tree
|
||||
var visualTree = (VisualElement) m_viewVisualTree.GetValue(m_currentToolbar, null);
|
||||
#endif
|
||||
|
||||
// Get first child which 'happens' to be toolbar IMGUIContainer
|
||||
var container = (IMGUIContainer) visualTree[0];
|
||||
|
||||
// (Re)attach handler
|
||||
var handler = (Action) m_imguiContainerOnGui.GetValue(container);
|
||||
handler -= OnGUI;
|
||||
handler += OnGUI;
|
||||
m_imguiContainerOnGui.SetValue(container, handler);
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnGUI()
|
||||
{
|
||||
var handler = OnToolbarGUI;
|
||||
if (handler != null) handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4fbfa229ee894b478fb21c046afa6d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityToolbarExtender
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class ToolbarExtender
|
||||
{
|
||||
static int m_toolCount;
|
||||
static GUIStyle m_commandStyle = null;
|
||||
|
||||
public static readonly List<Action> LeftToolbarGUI = new List<Action>();
|
||||
public static readonly List<Action> RightToolbarGUI = new List<Action>();
|
||||
|
||||
static ToolbarExtender()
|
||||
{
|
||||
Type toolbarType = typeof(Editor).Assembly.GetType("UnityEditor.Toolbar");
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
string fieldName = "k_ToolCount";
|
||||
#else
|
||||
string fieldName = "s_ShownToolIcons";
|
||||
#endif
|
||||
|
||||
FieldInfo toolIcons = toolbarType.GetField(fieldName,
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
m_toolCount = toolIcons != null ? ((int) toolIcons.GetValue(null)) : 8;
|
||||
#elif UNITY_2019_1_OR_NEWER
|
||||
m_toolCount = toolIcons != null ? ((int) toolIcons.GetValue(null)) : 7;
|
||||
#elif UNITY_2018_1_OR_NEWER
|
||||
m_toolCount = toolIcons != null ? ((Array) toolIcons.GetValue(null)).Length : 6;
|
||||
#else
|
||||
m_toolCount = toolIcons != null ? ((Array) toolIcons.GetValue(null)).Length : 5;
|
||||
#endif
|
||||
|
||||
ToolbarCallback.OnToolbarGUI = OnGUI;
|
||||
ToolbarCallback.OnToolbarGUILeft = GUILeft;
|
||||
ToolbarCallback.OnToolbarGUIRight = GUIRight;
|
||||
}
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
public const float space = 8;
|
||||
#else
|
||||
public const float space = 10;
|
||||
#endif
|
||||
public const float largeSpace = 20;
|
||||
public const float buttonWidth = 32;
|
||||
public const float dropdownWidth = 80;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
public const float playPauseStopWidth = 140;
|
||||
#else
|
||||
public const float playPauseStopWidth = 100;
|
||||
#endif
|
||||
|
||||
static void OnGUI()
|
||||
{
|
||||
// Create two containers, left and right
|
||||
// Screen is whole toolbar
|
||||
|
||||
if (m_commandStyle == null)
|
||||
{
|
||||
m_commandStyle = new GUIStyle("CommandLeft");
|
||||
}
|
||||
|
||||
var screenWidth = EditorGUIUtility.currentViewWidth;
|
||||
|
||||
// Following calculations match code reflected from Toolbar.OldOnGUI()
|
||||
float playButtonsPosition = Mathf.RoundToInt ((screenWidth - playPauseStopWidth) / 2);
|
||||
|
||||
Rect leftRect = new Rect(0, 0, screenWidth, Screen.height);
|
||||
leftRect.xMin += space; // Spacing left
|
||||
leftRect.xMin += buttonWidth * m_toolCount; // Tool buttons
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
leftRect.xMin += space; // Spacing between tools and pivot
|
||||
#else
|
||||
leftRect.xMin += largeSpace; // Spacing between tools and pivot
|
||||
#endif
|
||||
leftRect.xMin += 64 * 2; // Pivot buttons
|
||||
leftRect.xMax = playButtonsPosition;
|
||||
|
||||
Rect rightRect = new Rect(0, 0, screenWidth, Screen.height);
|
||||
rightRect.xMin = playButtonsPosition;
|
||||
rightRect.xMin += m_commandStyle.fixedWidth * 3; // Play buttons
|
||||
rightRect.xMax = screenWidth;
|
||||
rightRect.xMax -= space; // Spacing right
|
||||
rightRect.xMax -= dropdownWidth; // Layout
|
||||
rightRect.xMax -= space; // Spacing between layout and layers
|
||||
rightRect.xMax -= dropdownWidth; // Layers
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
rightRect.xMax -= space; // Spacing between layers and account
|
||||
#else
|
||||
rightRect.xMax -= largeSpace; // Spacing between layers and account
|
||||
#endif
|
||||
rightRect.xMax -= dropdownWidth; // Account
|
||||
rightRect.xMax -= space; // Spacing between account and cloud
|
||||
rightRect.xMax -= buttonWidth; // Cloud
|
||||
rightRect.xMax -= space; // Spacing between cloud and collab
|
||||
rightRect.xMax -= 78; // Colab
|
||||
|
||||
// Add spacing around existing controls
|
||||
leftRect.xMin += space;
|
||||
leftRect.xMax -= space;
|
||||
rightRect.xMin += space;
|
||||
rightRect.xMax -= space;
|
||||
|
||||
// Add top and bottom margins
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
leftRect.y = 4;
|
||||
leftRect.height = 22;
|
||||
rightRect.y = 4;
|
||||
rightRect.height = 22;
|
||||
#else
|
||||
leftRect.y = 5;
|
||||
leftRect.height = 24;
|
||||
rightRect.y = 5;
|
||||
rightRect.height = 24;
|
||||
#endif
|
||||
|
||||
if (leftRect.width > 0)
|
||||
{
|
||||
GUILayout.BeginArea(leftRect);
|
||||
GUILayout.BeginHorizontal();
|
||||
foreach (var handler in LeftToolbarGUI)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
if (rightRect.width > 0)
|
||||
{
|
||||
GUILayout.BeginArea(rightRect);
|
||||
GUILayout.BeginHorizontal();
|
||||
foreach (var handler in RightToolbarGUI)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
|
||||
public static void GUILeft() {
|
||||
GUILayout.BeginHorizontal();
|
||||
foreach (var handler in LeftToolbarGUI)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void GUIRight() {
|
||||
GUILayout.BeginHorizontal();
|
||||
foreach (var handler in RightToolbarGUI)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8e582c227e3bb4498b2703b86ca00d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 548460d28fdf29144a5f942b778caa3c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace UnityWebSocket.Editor
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
public const string GITHUB = "https://github.com/psygames/UnityWebSocket";
|
||||
public const string QQ_GROUP = "1126457634";
|
||||
public const string QQ_GROUP_LINK = "https://qm.qq.com/cgi-bin/qm/qr?k=KcexYJ9aYwogFXbj2aN0XHH5b2G7ICmd";
|
||||
public const string EMAIL = "799329256@qq.com";
|
||||
public const string AUHTOR = "psygames";
|
||||
public const string VERSION = "2.7.0";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8d0ca4e93274b74b864a13d989326c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace UnityWebSocket.Editor
|
||||
{
|
||||
internal class SettingsWindow : EditorWindow
|
||||
{
|
||||
static SettingsWindow window = null;
|
||||
[MenuItem("Tools/UnityWebSocket", priority = 100)]
|
||||
internal static void Open()
|
||||
{
|
||||
if (window != null)
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
|
||||
window = GetWindow<SettingsWindow>(true, "UnityWebSocket");
|
||||
window.minSize = window.maxSize = new Vector2(600, 310);
|
||||
window.Show();
|
||||
window.BeginCheck();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawLogo();
|
||||
DrawVersion();
|
||||
DrawSeparator(80);
|
||||
DrawSeparator(186);
|
||||
DrawHelper();
|
||||
DrawFooter();
|
||||
}
|
||||
|
||||
Texture2D logoTex = null;
|
||||
private void DrawLogo()
|
||||
{
|
||||
if (logoTex == null)
|
||||
{
|
||||
logoTex = new Texture2D(66, 66);
|
||||
logoTex.LoadImage(Convert.FromBase64String(LOGO_BASE64.VALUE));
|
||||
for (int i = 0; i < 66; i++) for (int j = 0; j < 15; j++) logoTex.SetPixel(i, j, Color.clear);
|
||||
logoTex.Apply();
|
||||
}
|
||||
|
||||
var logoPos = new Rect(10, 10, 66, 66);
|
||||
GUI.DrawTexture(logoPos, logoTex);
|
||||
var title = "<color=#3A9AD8><b>UnityWebSocket</b></color>";
|
||||
var titlePos = new Rect(80, 20, 500, 50);
|
||||
GUI.Label(titlePos, title, TextStyle(24));
|
||||
}
|
||||
|
||||
private void DrawSeparator(int y)
|
||||
{
|
||||
EditorGUI.DrawRect(new Rect(10, y, 580, 1), Color.white * 0.5f);
|
||||
}
|
||||
|
||||
private GUIStyle TextStyle(int fontSize = 10, TextAnchor alignment = TextAnchor.UpperLeft, float alpha = 0.85f)
|
||||
{
|
||||
var style = new GUIStyle();
|
||||
style.fontSize = fontSize;
|
||||
style.normal.textColor = (EditorGUIUtility.isProSkin ? Color.white : Color.black) * alpha;
|
||||
style.alignment = alignment;
|
||||
style.richText = true;
|
||||
return style;
|
||||
}
|
||||
|
||||
private void DrawVersion()
|
||||
{
|
||||
GUI.Label(new Rect(440, 10, 150, 10), "Current Version: " + Settings.VERSION, TextStyle(alignment: TextAnchor.MiddleLeft));
|
||||
if (string.IsNullOrEmpty(latestVersion))
|
||||
{
|
||||
GUI.Label(new Rect(440, 30, 150, 10), "Checking for Updates...", TextStyle(alignment: TextAnchor.MiddleLeft));
|
||||
}
|
||||
else if (latestVersion == "unknown")
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.Label(new Rect(440, 30, 150, 10), "Latest Version: " + latestVersion, TextStyle(alignment: TextAnchor.MiddleLeft));
|
||||
if (Settings.VERSION == latestVersion)
|
||||
{
|
||||
if (GUI.Button(new Rect(440, 50, 150, 18), "Check Update"))
|
||||
{
|
||||
latestVersion = "";
|
||||
changeLog = "";
|
||||
BeginCheck();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUI.Button(new Rect(440, 50, 150, 18), "Update to | " + latestVersion))
|
||||
{
|
||||
ShowUpdateDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowUpdateDialog()
|
||||
{
|
||||
var isOK = EditorUtility.DisplayDialog("UnityWebSocket",
|
||||
"Update UnityWebSocket now?\n" + changeLog,
|
||||
"Update Now", "Cancel");
|
||||
|
||||
if (isOK)
|
||||
{
|
||||
UpdateVersion();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVersion()
|
||||
{
|
||||
Application.OpenURL(Settings.GITHUB + "/releases");
|
||||
}
|
||||
|
||||
private void DrawHelper()
|
||||
{
|
||||
GUI.Label(new Rect(330, 200, 100, 18), "GitHub:", TextStyle(10, TextAnchor.MiddleRight));
|
||||
if (GUI.Button(new Rect(440, 200, 150, 18), "UnityWebSocket"))
|
||||
{
|
||||
Application.OpenURL(Settings.GITHUB);
|
||||
}
|
||||
|
||||
GUI.Label(new Rect(330, 225, 100, 18), "Report:", TextStyle(10, TextAnchor.MiddleRight));
|
||||
if (GUI.Button(new Rect(440, 225, 150, 18), "Report an Issue"))
|
||||
{
|
||||
Application.OpenURL(Settings.GITHUB + "/issues/new");
|
||||
}
|
||||
|
||||
GUI.Label(new Rect(330, 250, 100, 18), "Email:", TextStyle(10, TextAnchor.MiddleRight));
|
||||
if (GUI.Button(new Rect(440, 250, 150, 18), Settings.EMAIL))
|
||||
{
|
||||
var uri = new Uri(string.Format("mailto:{0}?subject={1}", Settings.EMAIL, "UnityWebSocket Feedback"));
|
||||
Application.OpenURL(uri.AbsoluteUri);
|
||||
}
|
||||
|
||||
GUI.Label(new Rect(330, 275, 100, 18), "QQ群:", TextStyle(10, TextAnchor.MiddleRight));
|
||||
if (GUI.Button(new Rect(440, 275, 150, 18), Settings.QQ_GROUP))
|
||||
{
|
||||
Application.OpenURL(Settings.QQ_GROUP_LINK);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFooter()
|
||||
{
|
||||
EditorGUI.DropShadowLabel(new Rect(10, 230, 400, 20), "Developed by " + Settings.AUHTOR);
|
||||
EditorGUI.DropShadowLabel(new Rect(10, 250, 400, 20), "All rights reserved");
|
||||
}
|
||||
|
||||
UnityWebRequest req;
|
||||
string changeLog = "";
|
||||
string latestVersion = "";
|
||||
void BeginCheck()
|
||||
{
|
||||
EditorApplication.update -= VersionCheckUpdate;
|
||||
EditorApplication.update += VersionCheckUpdate;
|
||||
|
||||
req = UnityWebRequest.Get(Settings.GITHUB + "/releases/latest");
|
||||
req.SendWebRequest();
|
||||
}
|
||||
|
||||
private void VersionCheckUpdate()
|
||||
{
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
if (req == null
|
||||
|| req.result == UnityWebRequest.Result.ConnectionError
|
||||
|| req.result == UnityWebRequest.Result.DataProcessingError
|
||||
|| req.result == UnityWebRequest.Result.ProtocolError)
|
||||
#elif UNITY_2018_1_OR_NEWER
|
||||
if (req == null || req.isNetworkError || req.isHttpError)
|
||||
#else
|
||||
if (req == null || req.isError)
|
||||
#endif
|
||||
{
|
||||
EditorApplication.update -= VersionCheckUpdate;
|
||||
latestVersion = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.isDone)
|
||||
{
|
||||
EditorApplication.update -= VersionCheckUpdate;
|
||||
latestVersion = req.url.Substring(req.url.LastIndexOf("/") + 1).TrimStart('v');
|
||||
|
||||
if (Settings.VERSION != latestVersion)
|
||||
{
|
||||
var text = req.downloadHandler.text;
|
||||
var st = text.IndexOf("content=\"" + latestVersion);
|
||||
st = st > 0 ? text.IndexOf("\n", st) : -1;
|
||||
var end = st > 0 ? text.IndexOf("\" />", st) : -1;
|
||||
if (st > 0 && end > st)
|
||||
{
|
||||
changeLog = text.Substring(st + 1, end - st - 1).Trim();
|
||||
changeLog = changeLog.Replace("\r", "");
|
||||
changeLog = changeLog.Replace("\n", "\n- ");
|
||||
changeLog = "\nCHANGE LOG: \n- " + changeLog + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class LOGO_BASE64
|
||||
{
|
||||
internal const string VALUE = "iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAMAAADUivDaAAAAq1BMVEUAAABKmtcvjtYzl" +
|
||||
"9szmNszl9syl9k0mNs0mNwzmNs0mNszl9szl9s0mNs0mNwzmNw0mNwyltk0mNw0mNwzl9s0mNsymNs0mNszmNwzmNwzm" +
|
||||
"NszmNs0mNwzl9w0mNwzmNw0mNs0mNs0mNwzl9wzmNs0mNwzmNs0mNwzl90zmNszmNszl9szmNsxmNszmNszmNw0mNwzm" +
|
||||
"Nw0mNs2neM4pe41mt43ouo2oOY5qfM+UHlaAAAAMnRSTlMAAwXN3sgI+/069MSCK6M/MA74h9qfFHB8STWMJ9OSdmNcI" +
|
||||
"8qya1IeF+/U0EIa57mqmFTYJe4AAAN3SURBVFjD7ZbpkppAFEa/bgVBREF2kEVGFNeZsM77P1kadURnJkr8k1Qlx1Khu" +
|
||||
"/pw7+2lwH/+YcgfMBBLG7VocwDamzH+wJBB8Qhjve2f0TdrGwjei6o4Ub/nM/APw5Z7vvSB/qrCrqbD6fBEVtigeMxks" +
|
||||
"fX9zWbj+z1jhqgSBplQ50eGo4614WXlRAzgrRhmtSfvxAn7pB0N5ObaKKZZuU5/d37IBcBgUQwqDuf7Z2gUmVAl4NGNr" +
|
||||
"/UeHxV5n39ulbaKLI86h6HilmM5M1aN126lpNhtl59yeTsp8nUMvpNC1J3bh5FtfVRk+bJrJunn5d4U4piJ/Vw9eXgsj" +
|
||||
"4ZpZaCjg9waZkIpnBWLJ44OwoNu60F2UnSaEkKv4XnAlCpm6B4F/aKMDiyGi2L8SEEAVdxNLuzmgV7nFwObEe2xQVuX+" +
|
||||
"RV1lWetga3w+cN1sXgvm4cJH8OEgZC1DPKhfF/BIymmQrMjq/x65FUeEkDup8GxoexZmznHCvANtXU/CAq13yimhQGtm" +
|
||||
"H4VCPnBBL1fTKo3CqEcvq7Lb/OwHxWTYlyw+JmjKoVvDLVOQB4pVsM8K8smgvLCxZDlIijwyOEc+nr/msMwK0+GQWGBd" +
|
||||
"tmhjv8icTds1s2ammaFh04QLLe69NK7guP6mTDMaw3o6nAX/Z7EXUskPSvWEWg4srVlp5NTDXv9Lce9HGN5eeG4nj5Yz" +
|
||||
"ACteU2wQLo4MBtJfd1nw5nG1/s9zwUQ6pykL1TQjqdeuvQW0naz2XKLYL4Cwzr4vj+OQdD96CSp7Lrynp4aeFF0xdm5q" +
|
||||
"6OFtFfPv7URxpWJNjd/N+3+I9+1klMav12Qtgbt9R2JaIopjkzaPtOFq4KxUpqfUMSFnQrySWjLoQzRZS4HMH84ME1ej" +
|
||||
"S1YJpQZ3B+sR1uCQJSBdGdCk1eAEgORR88KK05W8dh2MA+A/SKCYu3mCJ0Ek7HBx4HHeuwYy5G3x8hSMTJcOMFbinCsn" +
|
||||
"hO1V1aszGULvA0g4UFsb4VA0hAFcyo6cgLsAoT7uUtGAH5wQKQle0wuLyxLTaNyJEYwxw4wSljLK1TP8CAaOyhBMMEsj" +
|
||||
"OBoXgo7VGElFkSWL+vef1RF2YNXeRWYzQBTpkhC8KaZHhuIogArkQLKClBZjU26B2IZgGz+cpZkHl8g3fYUaW/YP2kb2" +
|
||||
"M/V97JY/vZN859n+QmO7XtC9Bf2jAAAAABJRU5ErkJggg==";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c42d421cc4c34f3eae1fbd67f0dced0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
JNFrame2/Assets/Plugins/SHFrame/Runtime.meta
Normal file
8
JNFrame2/Assets/Plugins/SHFrame/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f8f1e0db70b81f47a0f19fa43c4aab5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core.meta
Normal file
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 432675d70b0a07649b59b082792671e3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 940971d20d6d4082860bf85d1846f422
|
||||
timeCreated: 1708673958
|
||||
@@ -0,0 +1,453 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架链表类。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">指定链表的元素类型。</typeparam>
|
||||
public sealed class GameFrameworkLinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable
|
||||
{
|
||||
private readonly LinkedList<T> m_LinkedList;
|
||||
private readonly Queue<LinkedListNode<T>> m_CachedNodes;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化游戏框架链表类的新实例。
|
||||
/// </summary>
|
||||
public GameFrameworkLinkedList()
|
||||
{
|
||||
m_LinkedList = new LinkedList<T>();
|
||||
m_CachedNodes = new Queue<LinkedListNode<T>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表中实际包含的结点数量。
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_LinkedList.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表结点缓存数量。
|
||||
/// </summary>
|
||||
public int CachedNodeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CachedNodes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表的第一个结点。
|
||||
/// </summary>
|
||||
public LinkedListNode<T> First
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_LinkedList.First;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表的最后一个结点。
|
||||
/// </summary>
|
||||
public LinkedListNode<T> Last
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_LinkedList.Last;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示 ICollection`1 是否为只读。
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((ICollection<T>)m_LinkedList).IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取可用于同步对 ICollection 的访问的对象。
|
||||
/// </summary>
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((ICollection)m_LinkedList).SyncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否同步对 ICollection 的访问(线程安全)。
|
||||
/// </summary>
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((ICollection)m_LinkedList).IsSynchronized;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表中指定的现有结点后添加包含指定值的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的现有结点。</param>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>包含指定值的新结点。</returns>
|
||||
public LinkedListNode<T> AddAfter(LinkedListNode<T> node, T value)
|
||||
{
|
||||
LinkedListNode<T> newNode = AcquireNode(value);
|
||||
m_LinkedList.AddAfter(node, newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表中指定的现有结点后添加指定的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的现有结点。</param>
|
||||
/// <param name="newNode">指定的新结点。</param>
|
||||
public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
|
||||
{
|
||||
m_LinkedList.AddAfter(node, newNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表中指定的现有结点前添加包含指定值的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的现有结点。</param>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>包含指定值的新结点。</returns>
|
||||
public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value)
|
||||
{
|
||||
LinkedListNode<T> newNode = AcquireNode(value);
|
||||
m_LinkedList.AddBefore(node, newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表中指定的现有结点前添加指定的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的现有结点。</param>
|
||||
/// <param name="newNode">指定的新结点。</param>
|
||||
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
|
||||
{
|
||||
m_LinkedList.AddBefore(node, newNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表的开头处添加包含指定值的新结点。
|
||||
/// </summary>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>包含指定值的新结点。</returns>
|
||||
public LinkedListNode<T> AddFirst(T value)
|
||||
{
|
||||
LinkedListNode<T> node = AcquireNode(value);
|
||||
m_LinkedList.AddFirst(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表的开头处添加指定的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的新结点。</param>
|
||||
public void AddFirst(LinkedListNode<T> node)
|
||||
{
|
||||
m_LinkedList.AddFirst(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表的结尾处添加包含指定值的新结点。
|
||||
/// </summary>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>包含指定值的新结点。</returns>
|
||||
public LinkedListNode<T> AddLast(T value)
|
||||
{
|
||||
LinkedListNode<T> node = AcquireNode(value);
|
||||
m_LinkedList.AddLast(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表的结尾处添加指定的新结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的新结点。</param>
|
||||
public void AddLast(LinkedListNode<T> node)
|
||||
{
|
||||
m_LinkedList.AddLast(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从链表中移除所有结点。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
LinkedListNode<T> current = m_LinkedList.First;
|
||||
while (current != null)
|
||||
{
|
||||
ReleaseNode(current);
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
m_LinkedList.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除链表结点缓存。
|
||||
/// </summary>
|
||||
public void ClearCachedNodes()
|
||||
{
|
||||
m_CachedNodes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确定某值是否在链表中。
|
||||
/// </summary>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>某值是否在链表中。</returns>
|
||||
public bool Contains(T value)
|
||||
{
|
||||
return m_LinkedList.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从目标数组的指定索引处开始将整个链表复制到兼容的一维数组。
|
||||
/// </summary>
|
||||
/// <param name="array">一维数组,它是从链表复制的元素的目标。数组必须具有从零开始的索引。</param>
|
||||
/// <param name="index">array 中从零开始的索引,从此处开始复制。</param>
|
||||
public void CopyTo(T[] array, int index)
|
||||
{
|
||||
m_LinkedList.CopyTo(array, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从特定的 ICollection 索引开始,将数组的元素复制到一个数组中。
|
||||
/// </summary>
|
||||
/// <param name="array">一维数组,它是从 ICollection 复制的元素的目标。数组必须具有从零开始的索引。</param>
|
||||
/// <param name="index">array 中从零开始的索引,从此处开始复制。</param>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
((ICollection)m_LinkedList).CopyTo(array, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找包含指定值的第一个结点。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找的指定值。</param>
|
||||
/// <returns>包含指定值的第一个结点。</returns>
|
||||
public LinkedListNode<T> Find(T value)
|
||||
{
|
||||
return m_LinkedList.Find(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找包含指定值的最后一个结点。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找的指定值。</param>
|
||||
/// <returns>包含指定值的最后一个结点。</returns>
|
||||
public LinkedListNode<T> FindLast(T value)
|
||||
{
|
||||
return m_LinkedList.FindLast(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从链表中移除指定值的第一个匹配项。
|
||||
/// </summary>
|
||||
/// <param name="value">指定值。</param>
|
||||
/// <returns>是否移除成功。</returns>
|
||||
public bool Remove(T value)
|
||||
{
|
||||
LinkedListNode<T> node = m_LinkedList.Find(value);
|
||||
if (node != null)
|
||||
{
|
||||
m_LinkedList.Remove(node);
|
||||
ReleaseNode(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从链表中移除指定的结点。
|
||||
/// </summary>
|
||||
/// <param name="node">指定的结点。</param>
|
||||
public void Remove(LinkedListNode<T> node)
|
||||
{
|
||||
m_LinkedList.Remove(node);
|
||||
ReleaseNode(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除位于链表开头处的结点。
|
||||
/// </summary>
|
||||
public void RemoveFirst()
|
||||
{
|
||||
LinkedListNode<T> first = m_LinkedList.First;
|
||||
if (first == null)
|
||||
{
|
||||
throw new GameFrameworkException("First is invalid.");
|
||||
}
|
||||
|
||||
m_LinkedList.RemoveFirst();
|
||||
ReleaseNode(first);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除位于链表结尾处的结点。
|
||||
/// </summary>
|
||||
public void RemoveLast()
|
||||
{
|
||||
LinkedListNode<T> last = m_LinkedList.Last;
|
||||
if (last == null)
|
||||
{
|
||||
throw new GameFrameworkException("Last is invalid.");
|
||||
}
|
||||
|
||||
m_LinkedList.RemoveLast();
|
||||
ReleaseNode(last);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(m_LinkedList);
|
||||
}
|
||||
|
||||
private LinkedListNode<T> AcquireNode(T value)
|
||||
{
|
||||
LinkedListNode<T> node = null;
|
||||
if (m_CachedNodes.Count > 0)
|
||||
{
|
||||
node = m_CachedNodes.Dequeue();
|
||||
node.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = new LinkedListNode<T>(value);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private void ReleaseNode(LinkedListNode<T> node)
|
||||
{
|
||||
node.Value = default(T);
|
||||
m_CachedNodes.Enqueue(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将值添加到 ICollection`1 的结尾处。
|
||||
/// </summary>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
void ICollection<T>.Add(T value)
|
||||
{
|
||||
AddLast(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct Enumerator : IEnumerator<T>, IEnumerator
|
||||
{
|
||||
private LinkedList<T>.Enumerator m_Enumerator;
|
||||
|
||||
internal Enumerator(LinkedList<T> linkedList)
|
||||
{
|
||||
if (linkedList == null)
|
||||
{
|
||||
throw new GameFrameworkException("Linked list is invalid.");
|
||||
}
|
||||
|
||||
m_Enumerator = linkedList.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前结点。
|
||||
/// </summary>
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前的枚举数。
|
||||
/// </summary>
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理枚举数。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Enumerator.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一个结点。
|
||||
/// </summary>
|
||||
/// <returns>返回下一个结点。</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
return m_Enumerator.MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置枚举数。
|
||||
/// </summary>
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
((IEnumerator<T>)m_Enumerator).Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bf55be1c3044db1a097fe21337a8378
|
||||
timeCreated: 1708674214
|
||||
@@ -0,0 +1,217 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架链表范围。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">指定链表范围的元素类型。</typeparam>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct GameFrameworkLinkedListRange<T> : IEnumerable<T>, IEnumerable
|
||||
{
|
||||
private readonly LinkedListNode<T> m_First;
|
||||
private readonly LinkedListNode<T> m_Terminal;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化游戏框架链表范围的新实例。
|
||||
/// </summary>
|
||||
/// <param name="first">链表范围的开始结点。</param>
|
||||
/// <param name="terminal">链表范围的终结标记结点。</param>
|
||||
public GameFrameworkLinkedListRange(LinkedListNode<T> first, LinkedListNode<T> terminal)
|
||||
{
|
||||
if (first == null || terminal == null || first == terminal)
|
||||
{
|
||||
throw new GameFrameworkException("Range is invalid.");
|
||||
}
|
||||
|
||||
m_First = first;
|
||||
m_Terminal = terminal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表范围是否有效。
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_First != null && m_Terminal != null && m_First != m_Terminal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表范围的开始结点。
|
||||
/// </summary>
|
||||
public LinkedListNode<T> First
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_First;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表范围的终结标记结点。
|
||||
/// </summary>
|
||||
public LinkedListNode<T> Terminal
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Terminal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取链表范围的结点数量。
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>是否包含指定值。</returns>
|
||||
public bool Contains(T value)
|
||||
{
|
||||
for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next)
|
||||
{
|
||||
if (current.Value.Equals(value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct Enumerator : IEnumerator<T>, IEnumerator
|
||||
{
|
||||
private readonly GameFrameworkLinkedListRange<T> m_GameFrameworkLinkedListRange;
|
||||
private LinkedListNode<T> m_Current;
|
||||
private T m_CurrentValue;
|
||||
|
||||
internal Enumerator(GameFrameworkLinkedListRange<T> range)
|
||||
{
|
||||
if (!range.IsValid)
|
||||
{
|
||||
throw new GameFrameworkException("Range is invalid.");
|
||||
}
|
||||
|
||||
m_GameFrameworkLinkedListRange = range;
|
||||
m_Current = m_GameFrameworkLinkedListRange.m_First;
|
||||
m_CurrentValue = default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前结点。
|
||||
/// </summary>
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CurrentValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前的枚举数。
|
||||
/// </summary>
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CurrentValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理枚举数。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一个结点。
|
||||
/// </summary>
|
||||
/// <returns>返回下一个结点。</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (m_Current == null || m_Current == m_GameFrameworkLinkedListRange.m_Terminal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_CurrentValue = m_Current.Value;
|
||||
m_Current = m_Current.Next;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置枚举数。
|
||||
/// </summary>
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
m_Current = m_GameFrameworkLinkedListRange.m_First;
|
||||
m_CurrentValue = default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37e57e731a2147e1b9850b6e604a8748
|
||||
timeCreated: 1708674214
|
||||
@@ -0,0 +1,283 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架多值字典类。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">指定多值字典的主键类型。</typeparam>
|
||||
/// <typeparam name="TValue">指定多值字典的值类型。</typeparam>
|
||||
public sealed class GameFrameworkMultiDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>, IEnumerable
|
||||
{
|
||||
private readonly GameFrameworkLinkedList<TValue> m_LinkedList;
|
||||
private readonly Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> m_Dictionary;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化游戏框架多值字典类的新实例。
|
||||
/// </summary>
|
||||
public GameFrameworkMultiDictionary()
|
||||
{
|
||||
m_LinkedList = new GameFrameworkLinkedList<TValue>();
|
||||
m_Dictionary = new Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取多值字典中实际包含的主键数量。
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Dictionary.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取多值字典中指定主键的范围。
|
||||
/// </summary>
|
||||
/// <param name="key">指定的主键。</param>
|
||||
/// <returns>指定主键的范围。</returns>
|
||||
public GameFrameworkLinkedListRange<TValue> this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
|
||||
m_Dictionary.TryGetValue(key, out range);
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理多值字典。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_Dictionary.Clear();
|
||||
m_LinkedList.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查多值字典中是否包含指定主键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的主键。</param>
|
||||
/// <returns>多值字典中是否包含指定主键。</returns>
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查多值字典中是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的主键。</param>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>多值字典中是否包含指定值。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
|
||||
if (m_Dictionary.TryGetValue(key, out range))
|
||||
{
|
||||
return range.Contains(value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取多值字典中指定主键的范围。
|
||||
/// </summary>
|
||||
/// <param name="key">指定的主键。</param>
|
||||
/// <param name="range">指定主键的范围。</param>
|
||||
/// <returns>是否获取成功。</returns>
|
||||
public bool TryGetValue(TKey key, out GameFrameworkLinkedListRange<TValue> range)
|
||||
{
|
||||
return m_Dictionary.TryGetValue(key, out range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定的主键增加指定的值。
|
||||
/// </summary>
|
||||
/// <param name="key">指定的主键。</param>
|
||||
/// <param name="value">指定的值。</param>
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
|
||||
if (m_Dictionary.TryGetValue(key, out range))
|
||||
{
|
||||
m_LinkedList.AddBefore(range.Terminal, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
LinkedListNode<TValue> first = m_LinkedList.AddLast(value);
|
||||
LinkedListNode<TValue> terminal = m_LinkedList.AddLast(default(TValue));
|
||||
m_Dictionary.Add(key, new GameFrameworkLinkedListRange<TValue>(first, terminal));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的主键中移除指定的值。
|
||||
/// </summary>
|
||||
/// <param name="key">指定的主键。</param>
|
||||
/// <param name="value">指定的值。</param>
|
||||
/// <returns>是否移除成功。</returns>
|
||||
public bool Remove(TKey key, TValue value)
|
||||
{
|
||||
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
|
||||
if (m_Dictionary.TryGetValue(key, out range))
|
||||
{
|
||||
for (LinkedListNode<TValue> current = range.First; current != null && current != range.Terminal; current = current.Next)
|
||||
{
|
||||
if (current.Value.Equals(value))
|
||||
{
|
||||
if (current == range.First)
|
||||
{
|
||||
LinkedListNode<TValue> next = current.Next;
|
||||
if (next == range.Terminal)
|
||||
{
|
||||
m_LinkedList.Remove(next);
|
||||
m_Dictionary.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Dictionary[key] = new GameFrameworkLinkedListRange<TValue>(next, range.Terminal);
|
||||
}
|
||||
}
|
||||
|
||||
m_LinkedList.Remove(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的主键中移除所有的值。
|
||||
/// </summary>
|
||||
/// <param name="key">指定的主键。</param>
|
||||
/// <returns>是否移除成功。</returns>
|
||||
public bool RemoveAll(TKey key)
|
||||
{
|
||||
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
|
||||
if (m_Dictionary.TryGetValue(key, out range))
|
||||
{
|
||||
m_Dictionary.Remove(key);
|
||||
|
||||
LinkedListNode<TValue> current = range.First;
|
||||
while (current != null)
|
||||
{
|
||||
LinkedListNode<TValue> next = current != range.Terminal ? current.Next : null;
|
||||
m_LinkedList.Remove(current);
|
||||
current = next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(m_Dictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>> IEnumerable<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
/// <returns>循环访问集合的枚举数。</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环访问集合的枚举数。
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct Enumerator : IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>, IEnumerator
|
||||
{
|
||||
private Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>.Enumerator m_Enumerator;
|
||||
|
||||
internal Enumerator(Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
throw new GameFrameworkException("Dictionary is invalid.");
|
||||
}
|
||||
|
||||
m_Enumerator = dictionary.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前结点。
|
||||
/// </summary>
|
||||
public KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>> Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前的枚举数。
|
||||
/// </summary>
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理枚举数。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Enumerator.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一个结点。
|
||||
/// </summary>
|
||||
/// <returns>返回下一个结点。</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
return m_Enumerator.MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置枚举数。
|
||||
/// </summary>
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
((IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>)m_Enumerator).Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97d9f0c9d701483bbab0e0390d75ab6f
|
||||
timeCreated: 1708674203
|
||||
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 类型和名称的组合值。
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TypeNamePair : IEquatable<TypeNamePair>
|
||||
{
|
||||
private readonly Type m_Type;
|
||||
private readonly string m_Name;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化类型和名称的组合值的新实例。
|
||||
/// </summary>
|
||||
/// <param name="type">类型。</param>
|
||||
public TypeNamePair(Type type)
|
||||
: this(type, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化类型和名称的组合值的新实例。
|
||||
/// </summary>
|
||||
/// <param name="type">类型。</param>
|
||||
/// <param name="name">名称。</param>
|
||||
public TypeNamePair(Type type, string name)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
throw new GameFrameworkException("Type is invalid.");
|
||||
}
|
||||
|
||||
m_Type = type;
|
||||
m_Name = name ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取类型。
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get { return m_Type; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取名称。
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取类型和名称的组合值字符串。
|
||||
/// </summary>
|
||||
/// <returns>类型和名称的组合值字符串。</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (m_Type == null)
|
||||
{
|
||||
throw new GameFrameworkException("Type is invalid.");
|
||||
}
|
||||
|
||||
string typeName = m_Type.FullName;
|
||||
return string.IsNullOrEmpty(m_Name) ? typeName : Utility.Text.Format("{0}.{1}", typeName, m_Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象的哈希值。
|
||||
/// </summary>
|
||||
/// <returns>对象的哈希值。</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_Type.GetHashCode() ^ m_Name.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较对象是否与自身相等。
|
||||
/// </summary>
|
||||
/// <param name="obj">要比较的对象。</param>
|
||||
/// <returns>被比较的对象是否与自身相等。</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TypeNamePair && Equals((TypeNamePair)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较对象是否与自身相等。
|
||||
/// </summary>
|
||||
/// <param name="value">要比较的对象。</param>
|
||||
/// <returns>被比较的对象是否与自身相等。</returns>
|
||||
public bool Equals(TypeNamePair value)
|
||||
{
|
||||
return m_Type == value.m_Type && m_Name == value.m_Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个对象是否相等。
|
||||
/// </summary>
|
||||
/// <param name="a">值 a。</param>
|
||||
/// <param name="b">值 b。</param>
|
||||
/// <returns>两个对象是否相等。</returns>
|
||||
public static bool operator ==(TypeNamePair a, TypeNamePair b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个对象是否不相等。
|
||||
/// </summary>
|
||||
/// <param name="a">值 a。</param>
|
||||
/// <param name="b">值 b。</param>
|
||||
/// <returns>两个对象是否不相等。</returns>
|
||||
public static bool operator !=(TypeNamePair a, TypeNamePair b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6392d2762b514ba59d1c2313dbd7946e
|
||||
timeCreated: 1708673974
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb832def3641bc0429ba1ed88e6864ef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架异常类。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class GameFrameworkException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化游戏框架异常类的新实例。
|
||||
/// </summary>
|
||||
public GameFrameworkException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定错误消息初始化游戏框架异常类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">描述错误的消息。</param>
|
||||
public GameFrameworkException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定错误消息和对作为此异常原因的内部异常的引用来初始化游戏框架异常类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">解释异常原因的错误消息。</param>
|
||||
/// <param name="innerException">导致当前异常的异常。如果 innerException 参数不为空引用,则在处理内部异常的 catch 块中引发当前异常。</param>
|
||||
public GameFrameworkException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用序列化数据初始化游戏框架异常类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="info">存有有关所引发异常的序列化的对象数据。</param>
|
||||
/// <param name="context">包含有关源或目标的上下文信息。</param>
|
||||
protected GameFrameworkException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64a0d4c7981db7540b553dafd57d4766
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using SHFrame;
|
||||
using EventArgs = System.EventArgs;
|
||||
|
||||
namespace GameFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架中包含事件数据的类的基类。
|
||||
/// </summary>
|
||||
public abstract class GameFrameworkEventArgs : EventArgs, IReference
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化游戏框架中包含事件数据的类的新实例。
|
||||
/// </summary>
|
||||
public GameFrameworkEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理引用。
|
||||
/// </summary>
|
||||
public abstract void Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c22c08df08643ef8665d53a2ccf977c
|
||||
timeCreated: 1721617064
|
||||
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log.meta
Normal file
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db204a57722c74d44be1b3abd4ced8a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace SHFrame
|
||||
{
|
||||
public static partial class GameFrameworkLog
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架日志辅助器接口。
|
||||
/// </summary>
|
||||
public interface ILogHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录日志。
|
||||
/// </summary>
|
||||
/// <param name="level">游戏框架日志等级。</param>
|
||||
/// <param name="message">日志内容。</param>
|
||||
void Log(GameFrameworkLogLevel level, object message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d65b13152a7569458b4da6d6d896d18
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
2639
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/GameFrameworkLog.cs
Normal file
2639
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/GameFrameworkLog.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba33a75fffd3342489eb9f65b4dd5374
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏框架日志等级。
|
||||
/// </summary>
|
||||
public enum GameFrameworkLogLevel : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 调试。
|
||||
/// </summary>
|
||||
Debug = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 信息。
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// 警告。
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// 错误。
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// 严重错误。
|
||||
/// </summary>
|
||||
Fatal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd8dd7c832422a746ac6a2f1571fbe61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
2890
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/Log.cs
Normal file
2890
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/Log.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/Log.cs.meta
Normal file
11
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Log/Log.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02f3ffa8fd9ef5547b732e521ddb6343
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Net.meta
Normal file
8
JNFrame2/Assets/Plugins/SHFrame/Runtime/Core/Net.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bfe3877976172c40a676de42ea7e0b0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64aaa103f0a1fbd4a96e026e7fe7b3a0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7af9750e0bda15429621c4bd7d0bada
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b3a2a8f55d4a47f599b1fa3ed612389
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the event data for the <see cref="IWebSocket.OnClose"/> event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// That event occurs when the WebSocket connection has been closed.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If you would like to get the reason for the close, you should access
|
||||
/// the <see cref="Code"/> or <see cref="Reason"/> property.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CloseEventArgs : EventArgs
|
||||
{
|
||||
#region Internal Constructors
|
||||
|
||||
internal CloseEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
internal CloseEventArgs(ushort code)
|
||||
: this(code, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal CloseEventArgs(CloseStatusCode code)
|
||||
: this((ushort)code, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal CloseEventArgs(CloseStatusCode code, string reason)
|
||||
: this((ushort)code, reason)
|
||||
{
|
||||
}
|
||||
|
||||
internal CloseEventArgs(ushort code, string reason)
|
||||
{
|
||||
Code = code;
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status code for the close.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="ushort"/> that represents the status code for the close if any.
|
||||
/// </value>
|
||||
public ushort Code { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason for the close.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the reason for the close if any.
|
||||
/// </value>
|
||||
public string Reason { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connection has been closed cleanly.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the connection has been closed cleanly; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool WasClean { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enum value same as Code
|
||||
/// </summary>
|
||||
public CloseStatusCode StatusCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Enum.IsDefined(typeof(CloseStatusCode), Code))
|
||||
return (CloseStatusCode)Code;
|
||||
return CloseStatusCode.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29b987d07ba15434cb1744135a7a5416
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the status code for the WebSocket connection close.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The values of this enumeration are defined in
|
||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-7.4">
|
||||
/// Section 7.4</see> of RFC 6455.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// "Reserved value" cannot be sent as a status code in
|
||||
/// closing handshake by an endpoint.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum CloseStatusCode : ushort
|
||||
{
|
||||
Unknown = 65534,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1000. Indicates normal close.
|
||||
/// </summary>
|
||||
Normal = 1000,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1001. Indicates that an endpoint is
|
||||
/// going away.
|
||||
/// </summary>
|
||||
Away = 1001,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1002. Indicates that an endpoint is
|
||||
/// terminating the connection due to a protocol error.
|
||||
/// </summary>
|
||||
ProtocolError = 1002,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1003. Indicates that an endpoint is
|
||||
/// terminating the connection because it has received a type of
|
||||
/// data that it cannot accept.
|
||||
/// </summary>
|
||||
UnsupportedData = 1003,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1004. Still undefined. A Reserved value.
|
||||
/// </summary>
|
||||
Undefined = 1004,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1005. Indicates that no status code was
|
||||
/// actually present. A Reserved value.
|
||||
/// </summary>
|
||||
NoStatus = 1005,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1006. Indicates that the connection was
|
||||
/// closed abnormally. A Reserved value.
|
||||
/// </summary>
|
||||
Abnormal = 1006,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1007. Indicates that an endpoint is
|
||||
/// terminating the connection because it has received a message that
|
||||
/// contains data that is not consistent with the type of the message.
|
||||
/// </summary>
|
||||
InvalidData = 1007,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1008. Indicates that an endpoint is
|
||||
/// terminating the connection because it has received a message that
|
||||
/// violates its policy.
|
||||
/// </summary>
|
||||
PolicyViolation = 1008,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1009. Indicates that an endpoint is
|
||||
/// terminating the connection because it has received a message that
|
||||
/// is too big to process.
|
||||
/// </summary>
|
||||
TooBig = 1009,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1010. Indicates that a client is
|
||||
/// terminating the connection because it has expected the server to
|
||||
/// negotiate one or more extension, but the server did not return
|
||||
/// them in the handshake response.
|
||||
/// </summary>
|
||||
MandatoryExtension = 1010,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1011. Indicates that a server is
|
||||
/// terminating the connection because it has encountered an unexpected
|
||||
/// condition that prevented it from fulfilling the request.
|
||||
/// </summary>
|
||||
ServerError = 1011,
|
||||
/// <summary>
|
||||
/// Equivalent to close status 1015. Indicates that the connection was
|
||||
/// closed due to a failure to perform a TLS handshake. A Reserved value.
|
||||
/// </summary>
|
||||
TlsHandshakeFailure = 1015,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e34ee317292e4225a10427cc35f85ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the event data for the <see cref="IWebSocket.OnError"/> event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// That event occurs when the <see cref="IWebSocket"/> gets an error.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If you would like to get the error message, you should access
|
||||
/// the <see cref="Message"/> property.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// And if the error is due to an exception, you can get it by accessing
|
||||
/// the <see cref="Exception"/> property.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ErrorEventArgs : EventArgs
|
||||
{
|
||||
#region Internal Constructors
|
||||
|
||||
internal ErrorEventArgs(string message)
|
||||
: this(message, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal ErrorEventArgs(string message, Exception exception)
|
||||
{
|
||||
this.Message = message;
|
||||
this.Exception = exception;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception that caused the error.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An <see cref="System.Exception"/> instance that represents the cause of
|
||||
/// the error if it is due to an exception; otherwise, <see langword="null"/>.
|
||||
/// </value>
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the error message.
|
||||
/// </value>
|
||||
public string Message { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 884e7db60b6444154b7200e0e436f2de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>IWebSocket indicate a network connection.</para>
|
||||
/// <para>It can be connecting, connected, closing or closed state. </para>
|
||||
/// <para>You can send and receive messages by using it.</para>
|
||||
/// <para>Register callbacks for handling messages.</para>
|
||||
/// <para> ----------------------------------------------------------- </para>
|
||||
/// <para>IWebSocket 表示一个网络连接,</para>
|
||||
/// <para>它可以是 connecting connected closing closed 状态,</para>
|
||||
/// <para>可以发送和接收消息,</para>
|
||||
/// <para>通过注册消息回调,来处理接收到的消息。</para>
|
||||
/// </summary>
|
||||
public interface IWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Establishes a connection asynchronously.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method does not wait for the connect process to be complete.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method does nothing if the connection has already been
|
||||
/// established.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <para>
|
||||
/// This instance is not a client.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// -or-
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The close process is in progress.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// -or-
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// A series of reconnecting has failed.
|
||||
/// </para>
|
||||
/// </exception>
|
||||
void ConnectAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Closes the connection asynchronously.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method does not wait for the close to be complete.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method does nothing if the current state of the connection is
|
||||
/// Closing or Closed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
void CloseAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Sends the specified data asynchronously using the WebSocket connection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method does not wait for the send to be complete.
|
||||
/// </remarks>
|
||||
/// <param name="data">
|
||||
/// An array of <see cref="byte"/> that represents the binary data to send.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The current state of the connection is not Open.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="data"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
void SendAsync(byte[] data);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the specified data using the WebSocket connection.
|
||||
/// </summary>
|
||||
/// <param name="text">
|
||||
/// A <see cref="string"/> that represents the text data to send.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The current state of the connection is not Open.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="text"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="text"/> could not be UTF-8 encoded.
|
||||
/// </exception>
|
||||
void SendAsync(string text);
|
||||
|
||||
/// <summary>
|
||||
/// get the address which to connect.
|
||||
/// </summary>
|
||||
string Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// get sub protocols .
|
||||
/// </summary>
|
||||
string[] SubProtocols { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of the connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <para>
|
||||
/// One of the <see cref="WebSocketState"/> enum values.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It indicates the current state of the connection.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The default value is <see cref="WebSocketState.Closed"/>.
|
||||
/// </para>
|
||||
/// </value>
|
||||
WebSocketState ReadyState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current binaryType of the connection, supported on WEBGL platform only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <para>
|
||||
/// It indicates the current binaryType of the connection.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The default value is "arraybuffer", options: "blob" or "arraybuffer".
|
||||
/// </para>
|
||||
/// </value>
|
||||
string BinaryType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the WebSocket connection has been established.
|
||||
/// </summary>
|
||||
event EventHandler<OpenEventArgs> OnOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the WebSocket connection has been closed.
|
||||
/// </summary>
|
||||
event EventHandler<CloseEventArgs> OnClose;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="IWebSocket"/> gets an error.
|
||||
/// </summary>
|
||||
event EventHandler<ErrorEventArgs> OnError;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="IWebSocket"/> receives a message.
|
||||
/// </summary>
|
||||
event EventHandler<MessageEventArgs> OnMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37ee2146eb8c34ffab8b081a632b05cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
public class MessageEventArgs : EventArgs
|
||||
{
|
||||
private byte[] _rawData;
|
||||
private string _data;
|
||||
|
||||
internal MessageEventArgs(Opcode opcode, byte[] rawData)
|
||||
{
|
||||
Opcode = opcode;
|
||||
_rawData = rawData;
|
||||
}
|
||||
|
||||
internal MessageEventArgs(Opcode opcode, string data)
|
||||
{
|
||||
Opcode = opcode;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opcode for the message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see cref="Opcode.Text"/>, <see cref="Opcode.Binary"/>.
|
||||
/// </value>
|
||||
internal Opcode Opcode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message data as a <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the message data if its type is
|
||||
/// text and if decoding it to a string has successfully done;
|
||||
/// otherwise, <see langword="null"/>.
|
||||
/// </value>
|
||||
public string Data
|
||||
{
|
||||
get
|
||||
{
|
||||
SetData();
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message data as an array of <see cref="byte"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An array of <see cref="byte"/> that represents the message data.
|
||||
/// </value>
|
||||
public byte[] RawData
|
||||
{
|
||||
get
|
||||
{
|
||||
SetRawData();
|
||||
return _rawData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the message type is binary.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the message type is binary; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsBinary
|
||||
{
|
||||
get
|
||||
{
|
||||
return Opcode == Opcode.Binary;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the message type is text.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the message type is text; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsText
|
||||
{
|
||||
get
|
||||
{
|
||||
return Opcode == Opcode.Text;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetData()
|
||||
{
|
||||
if (_data != null) return;
|
||||
|
||||
if (RawData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_data = Encoding.UTF8.GetString(RawData);
|
||||
}
|
||||
|
||||
private void SetRawData()
|
||||
{
|
||||
if (_rawData != null) return;
|
||||
|
||||
if (_data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rawData = Encoding.UTF8.GetBytes(_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b44eda173b4924081bab76ae9d1b0a9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the WebSocket frame type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The values of this enumeration are defined in
|
||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">
|
||||
/// Section 5.2</see> of RFC 6455.
|
||||
/// </remarks>
|
||||
public enum Opcode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 1. Indicates text frame.
|
||||
/// </summary>
|
||||
Text = 0x1,
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 2. Indicates binary frame.
|
||||
/// </summary>
|
||||
Binary = 0x2,
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 8. Indicates connection close frame.
|
||||
/// </summary>
|
||||
Close = 0x8,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeac0ef90273544ebbae046672caf362
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
public class OpenEventArgs : EventArgs
|
||||
{
|
||||
internal OpenEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fb6fd704bd4e4b8ba63cd0b28712955
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
public const string GITHUB = "https://github.com/psygames/UnityWebSocket";
|
||||
public const string QQ_GROUP = "1126457634";
|
||||
public const string QQ_GROUP_LINK = "https://qm.qq.com/cgi-bin/qm/qr?k=KcexYJ9aYwogFXbj2aN0XHH5b2G7ICmd";
|
||||
public const string EMAIL = "799329256@qq.com";
|
||||
public const string AUHTOR = "psygames";
|
||||
public const string VERSION = "2.7.0";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e268303c7a605e343b1b132e5559f01f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference html5 WebSocket ReadyState Properties
|
||||
/// Indicates the state of a WebSocket connection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The values of this enumeration are defined in
|
||||
/// <see href="http://www.w3.org/TR/websockets/#dom-websocket-readystate">
|
||||
/// The WebSocket API</see>.
|
||||
/// </remarks>
|
||||
public enum WebSocketState : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 0. Indicates that the connection has not
|
||||
/// yet been established.
|
||||
/// </summary>
|
||||
Connecting = 0,
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 1. Indicates that the connection has
|
||||
/// been established, and the communication is possible.
|
||||
/// </summary>
|
||||
Open = 1,
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 2. Indicates that the connection is
|
||||
/// going through the closing handshake, or the close method has
|
||||
/// been invoked.
|
||||
/// </summary>
|
||||
Closing = 2,
|
||||
/// <summary>
|
||||
/// Equivalent to numeric value 3. Indicates that the connection has
|
||||
/// been closed or could not be established.
|
||||
/// </summary>
|
||||
Closed = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f6567ad13cb147a59f8af784f1c5f60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 396c66b333d624d539153070900bb73b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c110a898ae8b0b41bcf4da49c2b0425
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,363 @@
|
||||
#if !NET_LEGACY && (UNITY_EDITOR || !UNITY_WEBGL)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
public class WebSocket : IWebSocket
|
||||
{
|
||||
public string Address { get; private set; }
|
||||
public string[] SubProtocols { get; private set; }
|
||||
|
||||
public WebSocketState ReadyState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (socket == null)
|
||||
return WebSocketState.Closed;
|
||||
switch (socket.State)
|
||||
{
|
||||
case System.Net.WebSockets.WebSocketState.Closed:
|
||||
case System.Net.WebSockets.WebSocketState.None:
|
||||
return WebSocketState.Closed;
|
||||
case System.Net.WebSockets.WebSocketState.CloseReceived:
|
||||
case System.Net.WebSockets.WebSocketState.CloseSent:
|
||||
return WebSocketState.Closing;
|
||||
case System.Net.WebSockets.WebSocketState.Connecting:
|
||||
return WebSocketState.Connecting;
|
||||
case System.Net.WebSockets.WebSocketState.Open:
|
||||
return WebSocketState.Open;
|
||||
}
|
||||
return WebSocketState.Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public string BinaryType { get; set; } = "arraybuffer";
|
||||
|
||||
public event EventHandler<OpenEventArgs> OnOpen;
|
||||
public event EventHandler<CloseEventArgs> OnClose;
|
||||
public event EventHandler<ErrorEventArgs> OnError;
|
||||
public event EventHandler<MessageEventArgs> OnMessage;
|
||||
|
||||
private ClientWebSocket socket;
|
||||
private bool isOpening => socket != null && socket.State == System.Net.WebSockets.WebSocketState.Open;
|
||||
|
||||
#region APIs
|
||||
public WebSocket(string address)
|
||||
{
|
||||
this.Address = address;
|
||||
}
|
||||
|
||||
public WebSocket(string address, string subProtocol)
|
||||
{
|
||||
this.Address = address;
|
||||
this.SubProtocols = new string[] { subProtocol };
|
||||
}
|
||||
|
||||
public WebSocket(string address, string[] subProtocols)
|
||||
{
|
||||
this.Address = address;
|
||||
this.SubProtocols = subProtocols;
|
||||
}
|
||||
|
||||
public void ConnectAsync()
|
||||
{
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
WebSocketManager.Instance.Add(this);
|
||||
#endif
|
||||
if (socket != null)
|
||||
{
|
||||
HandleError(new Exception("Socket is busy."));
|
||||
return;
|
||||
}
|
||||
socket = new ClientWebSocket();
|
||||
if (this.SubProtocols != null)
|
||||
{
|
||||
foreach (var protocol in this.SubProtocols)
|
||||
{
|
||||
if (string.IsNullOrEmpty(protocol)) continue;
|
||||
Log($"Add Sub Protocol {protocol}");
|
||||
socket.Options.AddSubProtocol(protocol);
|
||||
}
|
||||
}
|
||||
Task.Run(ConnectTask);
|
||||
}
|
||||
|
||||
public void CloseAsync()
|
||||
{
|
||||
if (!isOpening) return;
|
||||
SendBufferAsync(new SendBuffer(null, WebSocketMessageType.Close));
|
||||
}
|
||||
|
||||
public void SendAsync(byte[] data)
|
||||
{
|
||||
if (!isOpening) return;
|
||||
var buffer = new SendBuffer(data, WebSocketMessageType.Binary);
|
||||
SendBufferAsync(buffer);
|
||||
}
|
||||
|
||||
public void SendAsync(string text)
|
||||
{
|
||||
if (!isOpening) return;
|
||||
var data = Encoding.UTF8.GetBytes(text);
|
||||
var buffer = new SendBuffer(data, WebSocketMessageType.Text);
|
||||
SendBufferAsync(buffer);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private async Task ConnectTask()
|
||||
{
|
||||
Log("Connect Task Begin ...");
|
||||
|
||||
try
|
||||
{
|
||||
var uri = new Uri(Address);
|
||||
await socket.ConnectAsync(uri, CancellationToken.None);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HandleError(e);
|
||||
HandleClose((ushort)CloseStatusCode.Abnormal, e.Message);
|
||||
SocketDispose();
|
||||
return;
|
||||
}
|
||||
|
||||
HandleOpen();
|
||||
|
||||
Log("Connect Task End !");
|
||||
|
||||
await ReceiveTask();
|
||||
}
|
||||
|
||||
class SendBuffer
|
||||
{
|
||||
public byte[] data;
|
||||
public WebSocketMessageType type;
|
||||
public SendBuffer(byte[] data, WebSocketMessageType type)
|
||||
{
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private object sendQueueLock = new object();
|
||||
private Queue<SendBuffer> sendQueue = new Queue<SendBuffer>();
|
||||
private bool isSendTaskRunning;
|
||||
|
||||
private void SendBufferAsync(SendBuffer buffer)
|
||||
{
|
||||
if (isSendTaskRunning)
|
||||
{
|
||||
lock (sendQueueLock)
|
||||
{
|
||||
if (buffer.type == WebSocketMessageType.Close)
|
||||
{
|
||||
sendQueue.Clear();
|
||||
}
|
||||
sendQueue.Enqueue(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isSendTaskRunning = true;
|
||||
sendQueue.Enqueue(buffer);
|
||||
Task.Run(SendTask);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendTask()
|
||||
{
|
||||
Log("Send Task Begin ...");
|
||||
|
||||
try
|
||||
{
|
||||
SendBuffer buffer = null;
|
||||
while (sendQueue.Count > 0 && isOpening)
|
||||
{
|
||||
lock (sendQueueLock)
|
||||
{
|
||||
buffer = sendQueue.Dequeue();
|
||||
}
|
||||
if (buffer.type == WebSocketMessageType.Close)
|
||||
{
|
||||
Log($"Close Send Begin ...");
|
||||
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Normal Closure", CancellationToken.None);
|
||||
Log($"Close Send End !");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"Send, type: {buffer.type}, size: {buffer.data.Length}, queue left: {sendQueue.Count}");
|
||||
await socket.SendAsync(new ArraySegment<byte>(buffer.data), buffer.type, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HandleError(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isSendTaskRunning = false;
|
||||
}
|
||||
|
||||
Log("Send Task End !");
|
||||
}
|
||||
|
||||
private async Task ReceiveTask()
|
||||
{
|
||||
Log("Receive Task Begin ...");
|
||||
|
||||
string closeReason = "";
|
||||
ushort closeCode = 0;
|
||||
bool isClosed = false;
|
||||
var segment = new ArraySegment<byte>(new byte[8192]);
|
||||
var ms = new MemoryStream();
|
||||
|
||||
try
|
||||
{
|
||||
while (!isClosed)
|
||||
{
|
||||
var result = await socket.ReceiveAsync(segment, CancellationToken.None);
|
||||
ms.Write(segment.Array, 0, result.Count);
|
||||
if (!result.EndOfMessage) continue;
|
||||
var data = ms.ToArray();
|
||||
ms.SetLength(0);
|
||||
switch (result.MessageType)
|
||||
{
|
||||
case WebSocketMessageType.Binary:
|
||||
HandleMessage(Opcode.Binary, data);
|
||||
break;
|
||||
case WebSocketMessageType.Text:
|
||||
HandleMessage(Opcode.Text, data);
|
||||
break;
|
||||
case WebSocketMessageType.Close:
|
||||
isClosed = true;
|
||||
closeCode = (ushort)result.CloseStatus;
|
||||
closeReason = result.CloseStatusDescription;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HandleError(e);
|
||||
closeCode = (ushort)CloseStatusCode.Abnormal;
|
||||
closeReason = e.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ms.Close();
|
||||
}
|
||||
|
||||
HandleClose(closeCode, closeReason);
|
||||
SocketDispose();
|
||||
|
||||
Log("Receive Task End !");
|
||||
}
|
||||
|
||||
private void SocketDispose()
|
||||
{
|
||||
sendQueue.Clear();
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
|
||||
private void HandleOpen()
|
||||
{
|
||||
Log("OnOpen");
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
HandleEventSync(new OpenEventArgs());
|
||||
#else
|
||||
OnOpen?.Invoke(this, new OpenEventArgs());
|
||||
#endif
|
||||
}
|
||||
|
||||
private void HandleMessage(Opcode opcode, byte[] rawData)
|
||||
{
|
||||
Log($"OnMessage, type: {opcode}, size: {rawData.Length}\n{BitConverter.ToString(rawData)}");
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
HandleEventSync(new MessageEventArgs(opcode, rawData));
|
||||
#else
|
||||
OnMessage?.Invoke(this, new MessageEventArgs(opcode, rawData));
|
||||
#endif
|
||||
}
|
||||
|
||||
private void HandleClose(ushort code, string reason)
|
||||
{
|
||||
Log($"OnClose, code: {code}, reason: {reason}");
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
HandleEventSync(new CloseEventArgs(code, reason));
|
||||
#else
|
||||
OnClose?.Invoke(this, new CloseEventArgs(code, reason));
|
||||
#endif
|
||||
}
|
||||
|
||||
private void HandleError(Exception exception)
|
||||
{
|
||||
Log("OnError, error: " + exception.Message);
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
HandleEventSync(new ErrorEventArgs(exception.Message));
|
||||
#else
|
||||
OnError?.Invoke(this, new ErrorEventArgs(exception.Message));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
private readonly Queue<EventArgs> eventQueue = new Queue<EventArgs>();
|
||||
private readonly object eventQueueLock = new object();
|
||||
private void HandleEventSync(EventArgs eventArgs)
|
||||
{
|
||||
lock (eventQueueLock)
|
||||
{
|
||||
eventQueue.Enqueue(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
EventArgs e;
|
||||
while (eventQueue.Count > 0)
|
||||
{
|
||||
lock (eventQueueLock)
|
||||
{
|
||||
e = eventQueue.Dequeue();
|
||||
}
|
||||
|
||||
if (e is CloseEventArgs)
|
||||
{
|
||||
OnClose?.Invoke(this, e as CloseEventArgs);
|
||||
WebSocketManager.Instance.Remove(this);
|
||||
}
|
||||
else if (e is OpenEventArgs)
|
||||
{
|
||||
OnOpen?.Invoke(this, e as OpenEventArgs);
|
||||
}
|
||||
else if (e is MessageEventArgs)
|
||||
{
|
||||
OnMessage?.Invoke(this, e as MessageEventArgs);
|
||||
}
|
||||
else if (e is ErrorEventArgs)
|
||||
{
|
||||
OnError?.Invoke(this, e as ErrorEventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[System.Diagnostics.Conditional("UNITY_WEB_SOCKET_LOG")]
|
||||
static void Log(string msg)
|
||||
{
|
||||
UnityEngine.Debug.Log($"<color=yellow>[UnityWebSocket]</color>" +
|
||||
$"<color=green>[T-{Thread.CurrentThread.ManagedThreadId:D3}]</color>" +
|
||||
$"<color=red>[{DateTime.Now.TimeOfDay}]</color>" +
|
||||
$" {msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d10f88a23641b4beb8df74460fb7f705
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
#if !NET_LEGACY && (UNITY_EDITOR || !UNITY_WEBGL) && !UNITY_WEB_SOCKET_ENABLE_ASYNC
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
[DefaultExecutionOrder(-10000)]
|
||||
internal class WebSocketManager : MonoBehaviour
|
||||
{
|
||||
private const string rootName = "[UnityWebSocket]";
|
||||
private static WebSocketManager _instance;
|
||||
public static WebSocketManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_instance) CreateInstance();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
public static void CreateInstance()
|
||||
{
|
||||
GameObject go = GameObject.Find("/" + rootName);
|
||||
if (!go) go = new GameObject(rootName);
|
||||
_instance = go.GetComponent<WebSocketManager>();
|
||||
if (!_instance) _instance = go.AddComponent<WebSocketManager>();
|
||||
}
|
||||
|
||||
private readonly List<WebSocket> sockets = new List<WebSocket>();
|
||||
|
||||
public void Add(WebSocket socket)
|
||||
{
|
||||
if (!sockets.Contains(socket))
|
||||
sockets.Add(socket);
|
||||
}
|
||||
|
||||
public void Remove(WebSocket socket)
|
||||
{
|
||||
if (sockets.Contains(socket))
|
||||
sockets.Remove(socket);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (sockets.Count <= 0) return;
|
||||
for (int i = sockets.Count - 1; i >= 0; i--)
|
||||
{
|
||||
sockets[i].Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99157fb5def394c83a9e5342036c92b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fb37927ec1ce4def9c5e7cff883f9f5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,148 @@
|
||||
#if !UNITY_EDITOR && UNITY_WEBGL
|
||||
using System;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
public class WebSocket : IWebSocket
|
||||
{
|
||||
public string Address { get; private set; }
|
||||
public string[] SubProtocols { get; private set; }
|
||||
public WebSocketState ReadyState { get { return (WebSocketState)WebSocketManager.WebSocketGetState(instanceId); } }
|
||||
public string BinaryType { get; set; } = "arraybuffer";
|
||||
|
||||
public event EventHandler<OpenEventArgs> OnOpen;
|
||||
public event EventHandler<CloseEventArgs> OnClose;
|
||||
public event EventHandler<ErrorEventArgs> OnError;
|
||||
public event EventHandler<MessageEventArgs> OnMessage;
|
||||
|
||||
internal int instanceId = 0;
|
||||
|
||||
public WebSocket(string address)
|
||||
{
|
||||
this.Address = address;
|
||||
AllocateInstance();
|
||||
}
|
||||
|
||||
public WebSocket(string address, string subProtocol)
|
||||
{
|
||||
this.Address = address;
|
||||
this.SubProtocols = new string[] { subProtocol };
|
||||
AllocateInstance();
|
||||
}
|
||||
|
||||
public WebSocket(string address, string[] subProtocols)
|
||||
{
|
||||
this.Address = address;
|
||||
this.SubProtocols = subProtocols;
|
||||
AllocateInstance();
|
||||
}
|
||||
|
||||
internal void AllocateInstance()
|
||||
{
|
||||
instanceId = WebSocketManager.AllocateInstance(this.Address, this.BinaryType);
|
||||
Log($"Allocate socket with instanceId: {instanceId}");
|
||||
if (this.SubProtocols == null) return;
|
||||
foreach (var protocol in this.SubProtocols)
|
||||
{
|
||||
if (string.IsNullOrEmpty(protocol)) continue;
|
||||
Log($"Add Sub Protocol {protocol}, with instanceId: {instanceId}");
|
||||
int code = WebSocketManager.WebSocketAddSubProtocol(instanceId, protocol);
|
||||
if (code < 0)
|
||||
{
|
||||
HandleOnError(GetErrorMessageFromCode(code));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WebSocket()
|
||||
{
|
||||
Log($"Free socket with instanceId: {instanceId}");
|
||||
WebSocketManager.WebSocketFree(instanceId);
|
||||
}
|
||||
|
||||
public void ConnectAsync()
|
||||
{
|
||||
Log($"Connect with instanceId: {instanceId}");
|
||||
WebSocketManager.Add(this);
|
||||
int code = WebSocketManager.WebSocketConnect(instanceId);
|
||||
if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
|
||||
}
|
||||
|
||||
public void CloseAsync()
|
||||
{
|
||||
Log($"Close with instanceId: {instanceId}");
|
||||
int code = WebSocketManager.WebSocketClose(instanceId, (int)CloseStatusCode.Normal, "Normal Closure");
|
||||
if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
|
||||
}
|
||||
|
||||
public void SendAsync(string text)
|
||||
{
|
||||
Log($"Send, type: {Opcode.Text}, size: {text.Length}");
|
||||
int code = WebSocketManager.WebSocketSendStr(instanceId, text);
|
||||
if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
|
||||
}
|
||||
|
||||
public void SendAsync(byte[] data)
|
||||
{
|
||||
Log($"Send, type: {Opcode.Binary}, size: {data.Length}");
|
||||
int code = WebSocketManager.WebSocketSend(instanceId, data, data.Length);
|
||||
if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
|
||||
}
|
||||
|
||||
internal void HandleOnOpen()
|
||||
{
|
||||
Log("OnOpen");
|
||||
OnOpen?.Invoke(this, new OpenEventArgs());
|
||||
}
|
||||
|
||||
internal void HandleOnMessage(byte[] rawData)
|
||||
{
|
||||
Log($"OnMessage, type: {Opcode.Binary}, size: {rawData.Length}");
|
||||
OnMessage?.Invoke(this, new MessageEventArgs(Opcode.Binary, rawData));
|
||||
}
|
||||
|
||||
internal void HandleOnMessageStr(string data)
|
||||
{
|
||||
Log($"OnMessage, type: {Opcode.Text}, size: {data.Length}");
|
||||
OnMessage?.Invoke(this, new MessageEventArgs(Opcode.Text, data));
|
||||
}
|
||||
|
||||
internal void HandleOnClose(ushort code, string reason)
|
||||
{
|
||||
Log($"OnClose, code: {code}, reason: {reason}");
|
||||
OnClose?.Invoke(this, new CloseEventArgs(code, reason));
|
||||
WebSocketManager.Remove(instanceId);
|
||||
}
|
||||
|
||||
internal void HandleOnError(string msg)
|
||||
{
|
||||
Log("OnError, error: " + msg);
|
||||
OnError?.Invoke(this, new ErrorEventArgs(msg));
|
||||
}
|
||||
|
||||
internal static string GetErrorMessageFromCode(int errorCode)
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case -1: return "WebSocket instance not found.";
|
||||
case -2: return "WebSocket is already connected or in connecting state.";
|
||||
case -3: return "WebSocket is not connected.";
|
||||
case -4: return "WebSocket is already closing.";
|
||||
case -5: return "WebSocket is already closed.";
|
||||
case -6: return "WebSocket is not in open state.";
|
||||
case -7: return "Cannot close WebSocket. An invalid code was specified or reason is too long.";
|
||||
default: return $"Unknown error code {errorCode}.";
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("UNITY_WEB_SOCKET_LOG")]
|
||||
static void Log(string msg)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[UnityWebSocket]" +
|
||||
$"[{DateTime.Now.TimeOfDay}]" +
|
||||
$" {msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74a5b3c22251243d2a2f33e74741559d
|
||||
timeCreated: 1466578513
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,153 @@
|
||||
#if !UNITY_EDITOR && UNITY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using AOT;
|
||||
|
||||
namespace UnityWebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// Class providing static access methods to work with JSLIB WebSocket
|
||||
/// </summary>
|
||||
internal static class WebSocketManager
|
||||
{
|
||||
/* Map of websocket instances */
|
||||
private static Dictionary<int, WebSocket> sockets = new Dictionary<int, WebSocket>();
|
||||
|
||||
/* Delegates */
|
||||
public delegate void OnOpenCallback(int instanceId);
|
||||
public delegate void OnMessageCallback(int instanceId, IntPtr msgPtr, int msgSize);
|
||||
public delegate void OnMessageStrCallback(int instanceId, IntPtr msgStrPtr);
|
||||
public delegate void OnErrorCallback(int instanceId, IntPtr errorPtr);
|
||||
public delegate void OnCloseCallback(int instanceId, int closeCode, IntPtr reasonPtr);
|
||||
|
||||
/* WebSocket JSLIB functions */
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketConnect(int instanceId);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketClose(int instanceId, int code, string reason);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketSend(int instanceId, byte[] dataPtr, int dataLength);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketSendStr(int instanceId, string data);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketGetState(int instanceId);
|
||||
|
||||
/* WebSocket JSLIB callback setters and other functions */
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketAllocate(string url, string binaryType);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern int WebSocketAddSubProtocol(int instanceId, string protocol);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketFree(int instanceId);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketSetOnOpen(OnOpenCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketSetOnMessage(OnMessageCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketSetOnMessageStr(OnMessageStrCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketSetOnError(OnErrorCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
public static extern void WebSocketSetOnClose(OnCloseCallback callback);
|
||||
|
||||
/* If callbacks was initialized and set */
|
||||
private static bool isInitialized = false;
|
||||
|
||||
/* Initialize WebSocket callbacks to JSLIB */
|
||||
private static void Initialize()
|
||||
{
|
||||
WebSocketSetOnOpen(DelegateOnOpenEvent);
|
||||
WebSocketSetOnMessage(DelegateOnMessageEvent);
|
||||
WebSocketSetOnMessageStr(DelegateOnMessageStrEvent);
|
||||
WebSocketSetOnError(DelegateOnErrorEvent);
|
||||
WebSocketSetOnClose(DelegateOnCloseEvent);
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OnOpenCallback))]
|
||||
public static void DelegateOnOpenEvent(int instanceId)
|
||||
{
|
||||
if (sockets.TryGetValue(instanceId, out var socket))
|
||||
{
|
||||
socket.HandleOnOpen();
|
||||
}
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OnMessageCallback))]
|
||||
public static void DelegateOnMessageEvent(int instanceId, IntPtr msgPtr, int msgSize)
|
||||
{
|
||||
if (sockets.TryGetValue(instanceId, out var socket))
|
||||
{
|
||||
var bytes = new byte[msgSize];
|
||||
Marshal.Copy(msgPtr, bytes, 0, msgSize);
|
||||
socket.HandleOnMessage(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OnMessageCallback))]
|
||||
public static void DelegateOnMessageStrEvent(int instanceId, IntPtr msgStrPtr)
|
||||
{
|
||||
if (sockets.TryGetValue(instanceId, out var socket))
|
||||
{
|
||||
string msgStr = Marshal.PtrToStringAuto(msgStrPtr);
|
||||
socket.HandleOnMessageStr(msgStr);
|
||||
}
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OnErrorCallback))]
|
||||
public static void DelegateOnErrorEvent(int instanceId, IntPtr errorPtr)
|
||||
{
|
||||
if (sockets.TryGetValue(instanceId, out var socket))
|
||||
{
|
||||
string errorMsg = Marshal.PtrToStringAuto(errorPtr);
|
||||
socket.HandleOnError(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OnCloseCallback))]
|
||||
public static void DelegateOnCloseEvent(int instanceId, int closeCode, IntPtr reasonPtr)
|
||||
{
|
||||
if (sockets.TryGetValue(instanceId, out var socket))
|
||||
{
|
||||
string reason = Marshal.PtrToStringAuto(reasonPtr);
|
||||
socket.HandleOnClose((ushort)closeCode, reason);
|
||||
}
|
||||
}
|
||||
|
||||
internal static int AllocateInstance(string address, string binaryType)
|
||||
{
|
||||
if (!isInitialized) Initialize();
|
||||
return WebSocketAllocate(address, binaryType);
|
||||
}
|
||||
|
||||
internal static void Add(WebSocket socket)
|
||||
{
|
||||
if (!sockets.ContainsKey(socket.instanceId))
|
||||
{
|
||||
sockets.Add(socket.instanceId, socket);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Remove(int instanceId)
|
||||
{
|
||||
if (sockets.ContainsKey(instanceId))
|
||||
{
|
||||
sockets.Remove(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 246cdc66a1e2047148371a8e56e17d3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c8ae854864e4cbd983fc58be762c79f
|
||||
timeCreated: 1708674128
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 引用接口。
|
||||
/// </summary>
|
||||
public interface IReference
|
||||
{
|
||||
/// <summary>
|
||||
/// 清理引用。
|
||||
/// </summary>
|
||||
void Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cba846464f634fbb834dc447a0a38a9a
|
||||
timeCreated: 1708674128
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 内存池对象基类。
|
||||
/// </summary>
|
||||
public abstract class MemoryObject : IReference
|
||||
{
|
||||
/// <summary>
|
||||
/// 清理内存对象回收入池。
|
||||
/// </summary>
|
||||
public virtual void Clear()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从内存池中初始化。
|
||||
/// </summary>
|
||||
public abstract void InitFromPool();
|
||||
|
||||
/// <summary>
|
||||
/// 回收到内存池。
|
||||
/// </summary>
|
||||
public abstract void RecycleToPool();
|
||||
}
|
||||
|
||||
public static partial class ReferencePool
|
||||
{
|
||||
/// <summary>
|
||||
/// 从内存池获取内存对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">内存对象类型。</typeparam>
|
||||
/// <returns>内存对象。</returns>
|
||||
public static T Alloc<T>() where T : MemoryObject, new()
|
||||
{
|
||||
T memory = Acquire<T>();
|
||||
memory.InitFromPool();
|
||||
return memory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将内存对象归还内存池。
|
||||
/// </summary>
|
||||
/// <param name="memory">内存对象。</param>
|
||||
public static void Dealloc(MemoryObject memory)
|
||||
{
|
||||
if (memory == null)
|
||||
{
|
||||
throw new Exception("Memory is invalid.");
|
||||
}
|
||||
|
||||
memory.RecycleToPool();
|
||||
Release(memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfed1dd6cf8149568d3c3d50bf75106e
|
||||
timeCreated: 1709627386
|
||||
@@ -0,0 +1,202 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
public static partial class ReferencePool
|
||||
{
|
||||
private sealed class ReferenceCollection
|
||||
{
|
||||
private readonly Queue<IReference> m_References;
|
||||
private readonly Type m_ReferenceType;
|
||||
private int m_UsingReferenceCount;
|
||||
private int m_AcquireReferenceCount;
|
||||
private int m_ReleaseReferenceCount;
|
||||
private int m_AddReferenceCount;
|
||||
private int m_RemoveReferenceCount;
|
||||
|
||||
public ReferenceCollection(Type referenceType)
|
||||
{
|
||||
m_References = new Queue<IReference>();
|
||||
m_ReferenceType = referenceType;
|
||||
m_UsingReferenceCount = 0;
|
||||
m_AcquireReferenceCount = 0;
|
||||
m_ReleaseReferenceCount = 0;
|
||||
m_AddReferenceCount = 0;
|
||||
m_RemoveReferenceCount = 0;
|
||||
}
|
||||
|
||||
public Type ReferenceType
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_ReferenceType;
|
||||
}
|
||||
}
|
||||
|
||||
public int UnusedReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_References.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public int UsingReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_UsingReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int AcquireReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_AcquireReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int ReleaseReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_ReleaseReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int AddReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_AddReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int RemoveReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_RemoveReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public T Acquire<T>() where T : class, IReference, new()
|
||||
{
|
||||
if (typeof(T) != m_ReferenceType)
|
||||
{
|
||||
throw new GameFrameworkException("Type is invalid.");
|
||||
}
|
||||
|
||||
m_UsingReferenceCount++;
|
||||
m_AcquireReferenceCount++;
|
||||
lock (m_References)
|
||||
{
|
||||
if (m_References.Count > 0)
|
||||
{
|
||||
return (T)m_References.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
m_AddReferenceCount++;
|
||||
return new T();
|
||||
}
|
||||
|
||||
public IReference Acquire()
|
||||
{
|
||||
m_UsingReferenceCount++;
|
||||
m_AcquireReferenceCount++;
|
||||
lock (m_References)
|
||||
{
|
||||
if (m_References.Count > 0)
|
||||
{
|
||||
return m_References.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
m_AddReferenceCount++;
|
||||
return (IReference)Activator.CreateInstance(m_ReferenceType);
|
||||
}
|
||||
|
||||
public void Release(IReference reference)
|
||||
{
|
||||
reference.Clear();
|
||||
lock (m_References)
|
||||
{
|
||||
if (m_EnableStrictCheck && m_References.Contains(reference))
|
||||
{
|
||||
throw new GameFrameworkException("The reference has been released.");
|
||||
}
|
||||
|
||||
m_References.Enqueue(reference);
|
||||
}
|
||||
|
||||
m_ReleaseReferenceCount++;
|
||||
m_UsingReferenceCount--;
|
||||
}
|
||||
|
||||
public void Add<T>(int count) where T : class, IReference, new()
|
||||
{
|
||||
if (typeof(T) != m_ReferenceType)
|
||||
{
|
||||
throw new GameFrameworkException("Type is invalid.");
|
||||
}
|
||||
|
||||
lock (m_References)
|
||||
{
|
||||
m_AddReferenceCount += count;
|
||||
while (count-- > 0)
|
||||
{
|
||||
m_References.Enqueue(new T());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(int count)
|
||||
{
|
||||
lock (m_References)
|
||||
{
|
||||
m_AddReferenceCount += count;
|
||||
while (count-- > 0)
|
||||
{
|
||||
m_References.Enqueue((IReference)Activator.CreateInstance(m_ReferenceType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(int count)
|
||||
{
|
||||
lock (m_References)
|
||||
{
|
||||
if (count > m_References.Count)
|
||||
{
|
||||
count = m_References.Count;
|
||||
}
|
||||
|
||||
m_RemoveReferenceCount += count;
|
||||
while (count-- > 0)
|
||||
{
|
||||
m_References.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAll()
|
||||
{
|
||||
lock (m_References)
|
||||
{
|
||||
m_RemoveReferenceCount += m_References.Count;
|
||||
m_References.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5c27b7f50b84106ba9ac7500e5d5c36
|
||||
timeCreated: 1708674128
|
||||
@@ -0,0 +1,225 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 引用池。
|
||||
/// </summary>
|
||||
public static partial class ReferencePool
|
||||
{
|
||||
private static readonly Dictionary<Type, ReferenceCollection> s_ReferenceCollections = new Dictionary<Type, ReferenceCollection>();
|
||||
private static bool m_EnableStrictCheck = false;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置是否开启强制检查。
|
||||
/// </summary>
|
||||
public static bool EnableStrictCheck
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_EnableStrictCheck;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_EnableStrictCheck = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取引用池的数量。
|
||||
/// </summary>
|
||||
public static int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_ReferenceCollections.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有引用池的信息。
|
||||
/// </summary>
|
||||
/// <returns>所有引用池的信息。</returns>
|
||||
public static ReferencePoolInfo[] GetAllReferencePoolInfos()
|
||||
{
|
||||
int index = 0;
|
||||
ReferencePoolInfo[] results = null;
|
||||
|
||||
lock (s_ReferenceCollections)
|
||||
{
|
||||
results = new ReferencePoolInfo[s_ReferenceCollections.Count];
|
||||
foreach (KeyValuePair<Type, ReferenceCollection> referenceCollection in s_ReferenceCollections)
|
||||
{
|
||||
results[index++] = new ReferencePoolInfo(referenceCollection.Key, referenceCollection.Value.UnusedReferenceCount, referenceCollection.Value.UsingReferenceCount, referenceCollection.Value.AcquireReferenceCount, referenceCollection.Value.ReleaseReferenceCount, referenceCollection.Value.AddReferenceCount, referenceCollection.Value.RemoveReferenceCount);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有引用池。
|
||||
/// </summary>
|
||||
public static void ClearAll()
|
||||
{
|
||||
lock (s_ReferenceCollections)
|
||||
{
|
||||
foreach (KeyValuePair<Type, ReferenceCollection> referenceCollection in s_ReferenceCollections)
|
||||
{
|
||||
referenceCollection.Value.RemoveAll();
|
||||
}
|
||||
|
||||
s_ReferenceCollections.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池获取引用。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">引用类型。</typeparam>
|
||||
/// <returns>引用。</returns>
|
||||
public static T Acquire<T>() where T : class, IReference, new()
|
||||
{
|
||||
return GetReferenceCollection(typeof(T)).Acquire<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池获取引用。
|
||||
/// </summary>
|
||||
/// <param name="referenceType">引用类型。</param>
|
||||
/// <returns>引用。</returns>
|
||||
public static IReference Acquire(Type referenceType)
|
||||
{
|
||||
InternalCheckReferenceType(referenceType);
|
||||
return GetReferenceCollection(referenceType).Acquire();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将引用归还引用池。
|
||||
/// </summary>
|
||||
/// <param name="reference">引用。</param>
|
||||
public static void Release(IReference reference)
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
throw new GameFrameworkException("Reference is invalid.");
|
||||
}
|
||||
|
||||
Type referenceType = reference.GetType();
|
||||
InternalCheckReferenceType(referenceType);
|
||||
GetReferenceCollection(referenceType).Release(reference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向引用池中追加指定数量的引用。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">引用类型。</typeparam>
|
||||
/// <param name="count">追加数量。</param>
|
||||
public static void Add<T>(int count) where T : class, IReference, new()
|
||||
{
|
||||
GetReferenceCollection(typeof(T)).Add<T>(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向引用池中追加指定数量的引用。
|
||||
/// </summary>
|
||||
/// <param name="referenceType">引用类型。</param>
|
||||
/// <param name="count">追加数量。</param>
|
||||
public static void Add(Type referenceType, int count)
|
||||
{
|
||||
InternalCheckReferenceType(referenceType);
|
||||
GetReferenceCollection(referenceType).Add(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池中移除指定数量的引用。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">引用类型。</typeparam>
|
||||
/// <param name="count">移除数量。</param>
|
||||
public static void Remove<T>(int count) where T : class, IReference
|
||||
{
|
||||
GetReferenceCollection(typeof(T)).Remove(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池中移除指定数量的引用。
|
||||
/// </summary>
|
||||
/// <param name="referenceType">引用类型。</param>
|
||||
/// <param name="count">移除数量。</param>
|
||||
public static void Remove(Type referenceType, int count)
|
||||
{
|
||||
InternalCheckReferenceType(referenceType);
|
||||
GetReferenceCollection(referenceType).Remove(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池中移除所有的引用。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">引用类型。</typeparam>
|
||||
public static void RemoveAll<T>() where T : class, IReference
|
||||
{
|
||||
GetReferenceCollection(typeof(T)).RemoveAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从引用池中移除所有的引用。
|
||||
/// </summary>
|
||||
/// <param name="referenceType">引用类型。</param>
|
||||
public static void RemoveAll(Type referenceType)
|
||||
{
|
||||
InternalCheckReferenceType(referenceType);
|
||||
GetReferenceCollection(referenceType).RemoveAll();
|
||||
}
|
||||
|
||||
private static void InternalCheckReferenceType(Type referenceType)
|
||||
{
|
||||
if (!m_EnableStrictCheck)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (referenceType == null)
|
||||
{
|
||||
throw new GameFrameworkException("Reference type is invalid.");
|
||||
}
|
||||
|
||||
if (!referenceType.IsClass || referenceType.IsAbstract)
|
||||
{
|
||||
throw new GameFrameworkException("Reference type is not a non-abstract class type.");
|
||||
}
|
||||
|
||||
if (!typeof(IReference).IsAssignableFrom(referenceType))
|
||||
{
|
||||
throw new GameFrameworkException(Utility.Text.Format("Reference type '{0}' is invalid.", referenceType.FullName));
|
||||
}
|
||||
}
|
||||
|
||||
private static ReferenceCollection GetReferenceCollection(Type referenceType)
|
||||
{
|
||||
if (referenceType == null)
|
||||
{
|
||||
throw new GameFrameworkException("ReferenceType is invalid.");
|
||||
}
|
||||
|
||||
ReferenceCollection referenceCollection = null;
|
||||
lock (s_ReferenceCollections)
|
||||
{
|
||||
if (!s_ReferenceCollections.TryGetValue(referenceType, out referenceCollection))
|
||||
{
|
||||
referenceCollection = new ReferenceCollection(referenceType);
|
||||
s_ReferenceCollections.Add(referenceType, referenceCollection);
|
||||
}
|
||||
}
|
||||
|
||||
return referenceCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54c8553d02fd4c649f3b76b3a37134aa
|
||||
timeCreated: 1708674128
|
||||
@@ -0,0 +1,125 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SHFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// 引用池信息。
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct ReferencePoolInfo
|
||||
{
|
||||
private readonly Type m_Type;
|
||||
private readonly int m_UnusedReferenceCount;
|
||||
private readonly int m_UsingReferenceCount;
|
||||
private readonly int m_AcquireReferenceCount;
|
||||
private readonly int m_ReleaseReferenceCount;
|
||||
private readonly int m_AddReferenceCount;
|
||||
private readonly int m_RemoveReferenceCount;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化引用池信息的新实例。
|
||||
/// </summary>
|
||||
/// <param name="type">引用池类型。</param>
|
||||
/// <param name="unusedReferenceCount">未使用引用数量。</param>
|
||||
/// <param name="usingReferenceCount">正在使用引用数量。</param>
|
||||
/// <param name="acquireReferenceCount">获取引用数量。</param>
|
||||
/// <param name="releaseReferenceCount">归还引用数量。</param>
|
||||
/// <param name="addReferenceCount">增加引用数量。</param>
|
||||
/// <param name="removeReferenceCount">移除引用数量。</param>
|
||||
public ReferencePoolInfo(Type type, int unusedReferenceCount, int usingReferenceCount, int acquireReferenceCount, int releaseReferenceCount, int addReferenceCount, int removeReferenceCount)
|
||||
{
|
||||
m_Type = type;
|
||||
m_UnusedReferenceCount = unusedReferenceCount;
|
||||
m_UsingReferenceCount = usingReferenceCount;
|
||||
m_AcquireReferenceCount = acquireReferenceCount;
|
||||
m_ReleaseReferenceCount = releaseReferenceCount;
|
||||
m_AddReferenceCount = addReferenceCount;
|
||||
m_RemoveReferenceCount = removeReferenceCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取引用池类型。
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取未使用引用数量。
|
||||
/// </summary>
|
||||
public int UnusedReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_UnusedReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取正在使用引用数量。
|
||||
/// </summary>
|
||||
public int UsingReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_UsingReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取获取引用数量。
|
||||
/// </summary>
|
||||
public int AcquireReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_AcquireReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取归还引用数量。
|
||||
/// </summary>
|
||||
public int ReleaseReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_ReleaseReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取增加引用数量。
|
||||
/// </summary>
|
||||
public int AddReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_AddReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取移除引用数量。
|
||||
/// </summary>
|
||||
public int RemoveReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_RemoveReferenceCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d7f634c21e544808a2439c6a60bb180
|
||||
timeCreated: 1708674128
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f574775e0ef37c479b5699fcacaec63
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e189d066e92ffe4daaa48b8cf53925d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user