报错修改
This commit is contained in:
3
Assets/Main/HotfixUpdateScript/Editor.meta
Normal file
3
Assets/Main/HotfixUpdateScript/Editor.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f50e24669aba44ba98efe5c74695fed5
|
||||
timeCreated: 1744077396
|
||||
@@ -0,0 +1,51 @@
|
||||
using Stary.Evo.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
[CustomEditor(typeof(HybridClREntrance))]
|
||||
public class HybridClREntranceEditor : UnityEditor.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化属性,在OnEnable中获取
|
||||
/// </summary>
|
||||
[HideInInspector]
|
||||
private SerializedProperty domain;
|
||||
|
||||
private string[] domainNames;
|
||||
private void OnEnable()
|
||||
{
|
||||
domain = serializedObject.FindProperty("domain");
|
||||
domainNames = CreatAssetWindow.GetCreatDomainAllName();
|
||||
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// 获取当前选中的索引
|
||||
int selectedIndex = System.Array.IndexOf(domainNames, domain.stringValue);
|
||||
if (selectedIndex < 0) selectedIndex = 0; // 默认选中第一个
|
||||
|
||||
// 绘制下拉选择框
|
||||
selectedIndex = EditorGUILayout.Popup("Domain", selectedIndex, domainNames);
|
||||
|
||||
// 更新选择的域名
|
||||
domain.stringValue = domainNames[selectedIndex];
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
HybridClREntrance hybridClREntrance = target as HybridClREntrance;
|
||||
if (GUILayout.Button("打开Domain"))
|
||||
{
|
||||
hybridClREntrance.OpenDomain();
|
||||
}
|
||||
if (GUILayout.Button("关闭Domain"))
|
||||
{
|
||||
hybridClREntrance.CloseDomain();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0996e24f28249ccb2bfb2a14e1358c1
|
||||
timeCreated: 1744706140
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "stary.main.editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:4492e37c9663479418f9522cc4796b57",
|
||||
"GUID:d1a793c2b6959e04ea45b972eaa369c8",
|
||||
"GUID:044184040b21c434b8aee6f2a3424c06"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c6af7aee383dac46b510d588d1b015d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/Main/HotfixUpdateScript/Runtime.meta
Normal file
3
Assets/Main/HotfixUpdateScript/Runtime.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea087baad4524017b420384544bfae06
|
||||
timeCreated: 1744077212
|
||||
8
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate.meta
Normal file
8
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45d07e1bb40ffcf4c8d9dc0cc5c31331
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73f0ed03caf557d42ab55a0979f6c452
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a15f3bb70d00bcd4b8c01eb4ace17c3b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class AppConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// package name
|
||||
/// </summary>
|
||||
public static string PackageDomainName { get; set; }
|
||||
|
||||
|
||||
public static string IpConfig { get; set; }
|
||||
public static string UserName { get; set; }
|
||||
public static string PassWord { get; set; }
|
||||
public static string ProductName { get; set; }
|
||||
public static string Platform { get; set; }
|
||||
|
||||
public static string MainDomainVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主场景main实例物体
|
||||
/// </summary>
|
||||
private static GameObject _MainBaseModel;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 赋值默认的实例
|
||||
/// </summary>
|
||||
public static void SetDefaultMainInstance(GameObject mainbase)
|
||||
{
|
||||
_MainBaseModel = mainbase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 赋值默认的实例
|
||||
/// </summary>
|
||||
public static GameObject GetDefaultMainInstance()
|
||||
{
|
||||
return _MainBaseModel;
|
||||
}
|
||||
}
|
||||
|
||||
public class GlobalConfig
|
||||
{
|
||||
public const string RikidHandLeft = "LeftHandRender/RKHandVisual/Hand_L/left_wrist/left_palm";
|
||||
public const string RikidHandRight = "RightHandRender/RKHandVisual/Hand_R/right_wrist/right_palm";
|
||||
public const string RikidHandRightIndexTip = "RightHandRender/RKHandVisual/Hand_R/right_wrist/right_index_metacarpal/right_index_proximal/right_index_intermediate/right_index_distal/right_index_tip";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43c9d32ba5934c65bf80d76e35074a1a
|
||||
timeCreated: 1741162242
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Main;
|
||||
using Stary.Evo.AudioCore;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
/// <summary>
|
||||
/// 热更基类,应该继承的基类
|
||||
/// </summary>
|
||||
public class DomainBase : MonoBehaviour
|
||||
{
|
||||
public string DomainName { protected get; set; }
|
||||
|
||||
public Transform TransformInfo;
|
||||
protected bool isExit { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 触发Domain时,调用该方法
|
||||
/// </summary>
|
||||
/// <param name="param"></param>
|
||||
public virtual void OnEnter(string param)
|
||||
{
|
||||
TransformInfo = transform.Find("TransformInfo");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Domain被关闭时,会调该方法
|
||||
/// </summary>
|
||||
/// <param name="param"></param>
|
||||
public virtual void OnExit()
|
||||
{
|
||||
isExit = true;
|
||||
AudioCoreManager.StopMusic();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual async Task OnEnterAsync(string param)
|
||||
{
|
||||
isExit = true;
|
||||
}
|
||||
|
||||
public virtual Task OnExitAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async void OnDestroy()
|
||||
{
|
||||
await ForceUnloadAllAssets();
|
||||
}
|
||||
|
||||
// 强制卸载所有资源包,该方法请在合适的时机调用。
|
||||
// 注意:Package在销毁的时候也会自动调用该方法。
|
||||
private async UniTask ForceUnloadAllAssets()
|
||||
{
|
||||
var package = YooAssets.TryGetPackage(DomainName);
|
||||
if (package != null)
|
||||
{
|
||||
var operation = package.UnloadAllAssetsAsync();
|
||||
await operation;
|
||||
await package.DestroyAsync();
|
||||
YooAssets.RemovePackage(DomainName);
|
||||
|
||||
Resources.UnloadUnusedAssets();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
Debug.Log($"UnityEvo:{DomainName} 资源包已卸载...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"UnityEvo:{DomainName} 资源包不存在,请检查是否已经卸载还是卸载异常...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8923efe3eb184e7f9283f71e2dc4ea10
|
||||
timeCreated: 1742540500
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 555a223b39dcb90489791ddba86221e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
/// <summary>
|
||||
/// 类对象池
|
||||
/// </summary>
|
||||
public interface IClassPool
|
||||
{
|
||||
/// <summary>
|
||||
/// 将类对象存进池里
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
void Creation(GameObject obj);
|
||||
/// <summary>
|
||||
/// 从未激活池里面取类对象,并放入激活池
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
GameObject Spawn(Transform parent);
|
||||
/// <summary>
|
||||
/// 从激活池中释放类对象到未激活池中
|
||||
/// </summary>
|
||||
GameObject RecycleSpawn(GameObject obj);
|
||||
/// <summary>
|
||||
/// 回收类对象
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
/// <summary>
|
||||
/// 清空类对象池
|
||||
/// </summary>
|
||||
void RecycleAll();
|
||||
|
||||
int GetPolLength();
|
||||
}
|
||||
public class ClassObjectPool:IClassPool
|
||||
{
|
||||
private AssetHandle poolObject;
|
||||
|
||||
/// <summary>
|
||||
/// 回收对象的父物体
|
||||
/// </summary>
|
||||
private Transform recycle;
|
||||
|
||||
protected List<GameObject> activepool = new List<GameObject>();
|
||||
protected Stack<GameObject> inactivepool = new Stack<GameObject>();
|
||||
//没有回收的个数
|
||||
protected int noRecycleCount;
|
||||
|
||||
public ClassObjectPool(AssetHandle poolObject,Transform recycle)
|
||||
{
|
||||
this.poolObject = poolObject;
|
||||
this.recycle = recycle;
|
||||
}
|
||||
/// <summary>
|
||||
/// 将类对象存进池里
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public void Creation(GameObject obj)
|
||||
{
|
||||
inactivepool.Push(obj);
|
||||
noRecycleCount++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从未激活池里面取类对象,并放入激活池
|
||||
/// </summary>
|
||||
/// <param name="createIfPoolEmpty">如果为空是否new出来</param>
|
||||
public GameObject Spawn(Transform parent)
|
||||
{
|
||||
GameObject obj = null;
|
||||
if(noRecycleCount>0)
|
||||
{
|
||||
obj = inactivepool.Pop();
|
||||
obj.transform.SetParent(parent);
|
||||
noRecycleCount--;
|
||||
if(obj==null)
|
||||
Debug.LogErrorFormat("对象池中不存在此对象【{0}】请排查代码", obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj= poolObject.InstantiateSync(parent);
|
||||
}
|
||||
obj.transform.localPosition = Vector3.zero;
|
||||
obj.transform.localRotation = Quaternion.identity;
|
||||
obj.transform.localScale = Vector3.one;
|
||||
obj.SetActive(true);
|
||||
activepool.Add(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从激活池中释放类对象到未激活池中
|
||||
/// </summary>
|
||||
public GameObject RecycleSpawn(GameObject obj)
|
||||
{
|
||||
if(obj!=null&&activepool.Contains(obj))
|
||||
{
|
||||
activepool.Remove(obj);
|
||||
inactivepool.Push(obj);
|
||||
obj.transform.parent = recycle;
|
||||
obj.gameObject.SetActive(false);
|
||||
noRecycleCount++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空类对象池
|
||||
/// </summary>
|
||||
public void RecycleAll()
|
||||
{
|
||||
noRecycleCount=0;
|
||||
|
||||
foreach (var pool in inactivepool)
|
||||
{
|
||||
GameObject.Destroy(pool);
|
||||
}
|
||||
inactivepool.Clear();
|
||||
foreach (var pool in activepool)
|
||||
{
|
||||
GameObject.Destroy(pool);
|
||||
}
|
||||
activepool.Clear();
|
||||
}
|
||||
public int GetPolLength()
|
||||
{
|
||||
return inactivepool.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4fc6b70fe91726439cc0d19d7e92edc
|
||||
timeCreated: 1626164240
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0346f4dcafef4fe7b87b5db6881b56e1
|
||||
timeCreated: 1741164620
|
||||
@@ -0,0 +1,19 @@
|
||||
using Main;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class FsmLoadSystem : FsmSystem , IFsmSystem
|
||||
{
|
||||
private OpenDomainType OpenDomainType { get; set; }
|
||||
|
||||
public ProgressBarPanel ProgressBarPanel { get; set; }
|
||||
public void SetOpenDomainType(OpenDomainType type)
|
||||
{
|
||||
this.OpenDomainType = type;
|
||||
}
|
||||
public OpenDomainType GetOpenDomainType()
|
||||
{
|
||||
return this.OpenDomainType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f012ae5832b467a802b8d775b1dae1e
|
||||
timeCreated: 1744710396
|
||||
@@ -0,0 +1,259 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using HybridCLR;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class HotFixState : AbstractFSMIState
|
||||
{
|
||||
public string[] PatchedAOTAssemblyList = new string[]
|
||||
{
|
||||
"System.Core.dll",
|
||||
"UnityEngine.CoreModule.dll",
|
||||
"mscorlib.dll",
|
||||
"DOTween.dll",
|
||||
"InformationSave.RunTime.dll",
|
||||
"UIFarme.RunTime.dll",
|
||||
"UniTask.dll",
|
||||
"YooAsset.dll",
|
||||
"com.stary.evo.runtime.dll"
|
||||
|
||||
};
|
||||
public HotFixState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
public override async UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
DomainConfig domainConfig = param as DomainConfig;
|
||||
Debug.Log("UnityEvo:热更脚本...");
|
||||
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
|
||||
// Editor环境下,HotUpdate.dll.bytes已经被自动加载,不需要加载,重复加载反而会出问题。
|
||||
|
||||
|
||||
// 添加类型存在性检查
|
||||
string fullClassName = $"{domainConfig.@namespace}.{domainConfig.className}";
|
||||
|
||||
#if EDITOR_SIMULATEMODE
|
||||
// Editor下无需加载,直接查找获得HotUpdate程序集
|
||||
Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies()
|
||||
.First(a => a.GetName().Name == $"HotUpdate_{AppConfig.PackageDomainName}");
|
||||
#else
|
||||
string hotUpdateAssemblyName = $"HotUpdate_{AppConfig.PackageDomainName}";
|
||||
|
||||
|
||||
Type assemblyType = IsAssetLoaded(fullClassName);
|
||||
if (assemblyType!=null)
|
||||
{
|
||||
Debug.Log($"UnityEvo:热更程序集:{hotUpdateAssemblyName} 已经加载过了");
|
||||
FsmSystem.SetCurState(nameof(LoadResState), domainConfig, assemblyType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//判断DLL是否下载成功
|
||||
foreach (var aot in PatchedAOTAssemblyList)
|
||||
{
|
||||
var aotName = $"Android_{aot}";
|
||||
var handle = package.LoadAssetAsync<TextAsset>(aotName);
|
||||
await handle;
|
||||
if (handle.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
var assetObj = handle.AssetObject as TextAsset;
|
||||
_sAssetDatas.Add(assetObj);
|
||||
Debug.Log($"UnityEvo:dll:{aotName} ,下载成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"UnityEvo:dll:{aotName} ,下载失败");
|
||||
}
|
||||
}
|
||||
|
||||
//先补元数据
|
||||
LoadMetadataForAOTAssemblies();
|
||||
|
||||
// 加载热更dll
|
||||
var hotfixDll = package.LoadAssetAsync<TextAsset>($"Android_HotUpdate_{AppConfig.PackageDomainName}.dll");
|
||||
await hotfixDll;
|
||||
var hotfixPdb = package.LoadAssetAsync<TextAsset>($"Android_HotUpdate_{AppConfig.PackageDomainName}.pdb");
|
||||
await hotfixPdb;
|
||||
Assembly hotUpdateAss = null;
|
||||
if (hotfixDll.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
var hotfixDllAsset = hotfixDll.AssetObject as TextAsset;
|
||||
if (hotfixPdb.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
Debug.Log($"UnityEvo:dll:加载成功!!");
|
||||
var hotfixPdbAsset = hotfixPdb.AssetObject as TextAsset;
|
||||
hotUpdateAss = Assembly.Load(hotfixDllAsset.bytes, hotfixPdbAsset.bytes);
|
||||
Debug.Log($"UnityEvo:dll: Assembly.Load加载成功!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hotfixDllAsset != null)
|
||||
hotUpdateAss = Assembly.Load(hotfixDllAsset.bytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:Android_HotUpdate_{AppConfig.PackageDomainName}.dll加载失败!!!!!");
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Debug.Log($"UnityEvo:dll: Assembly.Load加载后是否为空:{hotUpdateAss == null}");
|
||||
if (hotUpdateAss != null)
|
||||
{
|
||||
|
||||
Type type = hotUpdateAss.GetType(fullClassName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:热更类不存在!程序集: {hotUpdateAss.FullName}\n" +
|
||||
$"期望类名: {fullClassName}\n" +
|
||||
$"可用类型列表:{string.Join("\n", hotUpdateAss.GetTypes().Select(t => t.FullName))}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加方法存在性检查
|
||||
Debug.Log($"UnityEvo:dll:开始MethodInfo检查!!");
|
||||
MethodInfo onEnterMethod = type.GetMethod("OnEnter");
|
||||
MethodInfo onEnterAsyncMethod = type.GetMethod("OnEnterAsync");
|
||||
if (onEnterMethod == null || onEnterAsyncMethod == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:热更类进入方法缺失!\n" +
|
||||
$"存在方法:{string.Join(", ", type.GetMethods().Select(m => m.Name))}");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"UnityEvo:dll:OnEnter检查成功!!");
|
||||
MethodInfo onExitMethod = type.GetMethod("OnExit");
|
||||
MethodInfo onExitAsyncMethod = type.GetMethod("OnExitAsync");
|
||||
if (onExitMethod == null || onExitAsyncMethod == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:热更类退出方法缺失!\n" +
|
||||
$"存在方法:{string.Join(", ", type.GetMethods().Select(m => m.Name))}");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"UnityEvo:dll:OnExit检查成功!!");
|
||||
// AppConfig.SetDefaultHotfixType(type);
|
||||
FsmSystem.SetCurState(nameof(LoadResState), domainConfig, type);
|
||||
// // 创建热更类实例
|
||||
// DomainBase hotfixInstance = AppConfig.HOTFIXBASE.AddComponent(type) as DomainBase;
|
||||
//
|
||||
// if (hotfixInstance == null)
|
||||
// {
|
||||
// Debug.LogError($"热更类{fullClassName}实例创建失败!必须继承MonoBehaviour");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // 原有调用逻辑修改为使用实例
|
||||
// onEnterMethod.Invoke(hotfixInstance, new object[] { "" }); // 第一个参数改为实例对象
|
||||
// onEnterAsyncMethod.Invoke(hotfixInstance, new object[] { "" });
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:热更类不存在!程序集: hotUpdateAss 不存在\n" +
|
||||
$"期望类名: {fullClassName}\n" +
|
||||
$"可用类型列表:{string.Join("\n", hotUpdateAss.GetTypes().Select(t => t.FullName))}");
|
||||
}
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
#region 补充元数据
|
||||
|
||||
// //补充元数据dll的列表
|
||||
// //通过RuntimeApi.LoadMetadataForAOTAssembly()函数来补充AOT泛型的原始元数据
|
||||
// private List<string> AOTMetaAssemblyFiles { get; } =new()
|
||||
// {
|
||||
// "Android_mscorlib.dll", "Android_System.dll", "Android_System.Core.dll",
|
||||
// };
|
||||
|
||||
private readonly List<TextAsset> _sAssetDatas = new List<TextAsset>();
|
||||
|
||||
|
||||
// public byte[] ReadBytesFromStreamingAssets(string dllName)
|
||||
// {
|
||||
// if (_sAssetDatas.ContainsKey(dllName))
|
||||
// {
|
||||
// return _sAssetDatas[dllName].bytes;
|
||||
// }
|
||||
//
|
||||
// return Array.Empty<byte>();
|
||||
// }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
|
||||
/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
|
||||
/// </summary>
|
||||
private void LoadMetadataForAOTAssemblies()
|
||||
{
|
||||
HomologousImageMode mode = HomologousImageMode.SuperSet;
|
||||
// foreach (var aotDllName in AOTGenericReferences.PatchedAOTAssemblyList)
|
||||
foreach (var aotDll in _sAssetDatas)
|
||||
{
|
||||
// var aotName = $"Android_{aotDllName}";
|
||||
//
|
||||
// byte[] dllBytes = ReadBytesFromStreamingAssets(aotName);
|
||||
if (aotDll != null)
|
||||
{
|
||||
byte[] dllBytes = aotDll.bytes;
|
||||
// 添加元数据加载校验
|
||||
if (dllBytes == null || dllBytes.Length == 0)
|
||||
{
|
||||
Debug.LogError($"AOT元数据加载失败:{aotDll.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
LoadImageErrorCode err = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
|
||||
|
||||
Debug.Log($"UnityEvo:【补元】{aotDll.name} 加载结果: {err} 字节数: {dllBytes.Length}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Type IsAssetLoaded(string assemblyName)
|
||||
{
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
Type assemblyType = assembly.GetType(assemblyName);
|
||||
if (assemblyType!=null)
|
||||
{
|
||||
|
||||
Debug.Log("type:" + assemblyType);
|
||||
return assemblyType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
public override UniTask OnExitAsync()
|
||||
{
|
||||
_sAssetDatas.Clear();
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 693e2ecffc1b43caa7c7818e3aba708c
|
||||
timeCreated: 1741165298
|
||||
@@ -0,0 +1,88 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Main;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class LoadResMainState : AbstractFSMIState
|
||||
{
|
||||
private string _doMain = "Main";
|
||||
|
||||
public LoadResMainState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override async UniTask OnEnterAsync()
|
||||
{
|
||||
// Debug.Log("加载资源...");
|
||||
// var package = YooAssets.GetPackage(_doMain);
|
||||
//
|
||||
// DomainConfig config = null;
|
||||
// //加载热更配置文件
|
||||
// var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
|
||||
// await loadHotfixSettingsOp;
|
||||
// if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
|
||||
// {
|
||||
// //更新成功
|
||||
// Debug.Log($"UnityEvo:加载主配置文件 loadHotfixSettings : 【成功】");
|
||||
// config = loadHotfixSettingsOp.AssetObject as DomainConfig;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.LogError($"UnityEvo:加载主配置文件 loadHotfixSettings : 【失败】");
|
||||
// }
|
||||
//
|
||||
// // 加载热更资源
|
||||
// var loadOperation = package.LoadAssetAsync<GameObject>(config.mainPrefab);
|
||||
//
|
||||
// await loadOperation;
|
||||
// if (loadOperation.Status == EOperationStatus.Succeed)
|
||||
// {
|
||||
// GameObject loadhotfix = loadOperation.InstantiateSync();
|
||||
// AppConfig.SetDefaultMainInstance(loadhotfix);
|
||||
// if (loadhotfix != null)
|
||||
// {
|
||||
// DomainBase hotfixInstance = loadhotfix.GetComponent<MainDomain>();
|
||||
// if (hotfixInstance == null)
|
||||
// {
|
||||
// hotfixInstance = loadhotfix.AddComponent<MainDomain>();
|
||||
// }
|
||||
//
|
||||
// if (hotfixInstance == null)
|
||||
// {
|
||||
// Debug.LogError($"热更类{loadhotfix.name}实例创建失败!必须继承MonoBehaviour");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // 原有调用逻辑修改为使用实例
|
||||
// hotfixInstance.OnEnter("");
|
||||
// hotfixInstance.OnEnterAsync("");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override UniTask OnExitAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0fed0de80c04e7fb0f513ce21fed7b9
|
||||
timeCreated: 1744361114
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Immersal.AR;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.InformationSave;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class LoadResState : AbstractFSMIState
|
||||
{
|
||||
private DomainConfig.LoadResType loadResType;
|
||||
public GameObject mainPrefab;
|
||||
private DomainConfig domainConfig;
|
||||
public LoadResState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override UniTask OnEnterAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override async UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
Debug.Log("加载资源...");
|
||||
domainConfig = param1 as DomainConfig;
|
||||
loadResType = domainConfig.loadResType;
|
||||
Type type = param2 as Type;
|
||||
var package = YooAssets.GetPackage(domainConfig.domain);
|
||||
if (loadResType == DomainConfig.LoadResType.Prefab)
|
||||
{
|
||||
// 加载热更资源
|
||||
var loadOperation = package.LoadAssetAsync<GameObject>(domainConfig.mainPrefab);
|
||||
|
||||
await loadOperation;
|
||||
if (loadOperation.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
ARSpace arSpace = GameObject.FindObjectOfType<ARSpace>();
|
||||
if (arSpace != null)
|
||||
{
|
||||
Debug.Log("UnityEvo:找到ARSpace,开始加载点云运行环境...");
|
||||
mainPrefab = loadOperation.InstantiateSync(arSpace.transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("UnityEvo:未找到ARSpace,开始加载普通运行环境,通过语音唤醒...");
|
||||
mainPrefab = loadOperation.InstantiateSync();
|
||||
}
|
||||
if(domainConfig.domain =="Main")
|
||||
AppConfig.SetDefaultMainInstance(mainPrefab);
|
||||
}
|
||||
}
|
||||
else if (loadResType == DomainConfig.LoadResType.Scene)
|
||||
{
|
||||
var sceneMode = domainConfig.loadSceneMode;
|
||||
var physicsMode = LocalPhysicsMode.None;
|
||||
SceneHandle handle = package.LoadSceneAsync(domainConfig.mainScene, sceneMode, physicsMode);
|
||||
await handle;
|
||||
mainPrefab = GameObject.Find(domainConfig.mainPrefab);
|
||||
}
|
||||
|
||||
LocalTransformInfo info = mainPrefab.GetComponentInChildren<LocalTransformInfo>();
|
||||
FsmLoadSystem fsmLoadSystem = FsmSystem as FsmLoadSystem;
|
||||
|
||||
if (info._list.Count >= 2)
|
||||
{
|
||||
if (fsmLoadSystem.GetOpenDomainType() == OpenDomainType.DEFAULT)
|
||||
{
|
||||
info.Switch(0);
|
||||
}
|
||||
else if (fsmLoadSystem.GetOpenDomainType() == OpenDomainType.VIOICE)
|
||||
{
|
||||
info.Switch(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:{mainPrefab.name}的TransformInfo长度小于2,无法继续运行,请排查");
|
||||
}
|
||||
|
||||
if (mainPrefab != null)
|
||||
{
|
||||
DomainBase hotfixInstance = mainPrefab.GetComponent(type) as DomainBase;
|
||||
if (hotfixInstance == null)
|
||||
{
|
||||
hotfixInstance = mainPrefab.AddComponent(type) as DomainBase;
|
||||
}
|
||||
|
||||
hotfixInstance.DomainName = domainConfig.domain;
|
||||
if (hotfixInstance == null)
|
||||
{
|
||||
Debug.LogError($"热更类{type.Name}实例创建失败!必须继承MonoBehaviour");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 原有调用逻辑修改为使用实例
|
||||
hotfixInstance.OnEnter("");
|
||||
hotfixInstance.OnEnterAsync("");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
|
||||
public override async UniTask OnExitAsync()
|
||||
{
|
||||
Debug.Log("UnityEvo:Domain退出...");
|
||||
if (domainConfig.domain != "Main")
|
||||
{
|
||||
DomainBase domainBase = mainPrefab.GetComponent<DomainBase>();
|
||||
if (domainBase == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:{mainPrefab.name}的DomainBase为空,无法退出,请排查");
|
||||
}
|
||||
else
|
||||
{
|
||||
domainBase.OnExit();
|
||||
await domainBase.OnExitAsync();
|
||||
}
|
||||
|
||||
if (loadResType == DomainConfig.LoadResType.Scene)
|
||||
{
|
||||
await SceneManager.UnloadSceneAsync("server");
|
||||
}
|
||||
|
||||
if (domainBase != null)
|
||||
GameObject.Destroy(domainBase.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f689e40cd4654793a8f1d3ce69ba3532
|
||||
timeCreated: 1741165763
|
||||
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Main;
|
||||
using Newtonsoft.Json;
|
||||
using Stary.Evo.UIFarme;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Networking;
|
||||
using YooAsset;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class ResStartState : AbstractFSMIState
|
||||
{
|
||||
public ResStartState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override async UniTask OnEnterAsync()
|
||||
{
|
||||
Debug.Log("UnityEvo:启动开始资源初始化...");
|
||||
|
||||
|
||||
//初始化读取资源配置表
|
||||
HotfixMainResDomain hotfixMainResDomain = Resources.Load<HotfixMainResDomain>("HotfixMainResDomain");
|
||||
if (hotfixMainResDomain != null)
|
||||
{
|
||||
AppConfig.IpConfig = hotfixMainResDomain.hotfixMainResDomainEntity.ipconfig;
|
||||
AppConfig.UserName = hotfixMainResDomain.hotfixMainResDomainEntity.username;
|
||||
AppConfig.PassWord = hotfixMainResDomain.hotfixMainResDomainEntity.password;
|
||||
AppConfig.ProductName =Application.identifier;
|
||||
AppConfig.MainDomainVersion = hotfixMainResDomain.hotfixMainResDomainEntity.mainDomainVersion;
|
||||
}
|
||||
|
||||
Debug.Log($"UnityEvo:读取资源配置表成功...{AppConfig.IpConfig}{AppConfig.UserName}{AppConfig.PassWord}");
|
||||
// 初始化资源系统
|
||||
YooAssets.Initialize();
|
||||
|
||||
|
||||
//自定义网络请求器
|
||||
// 设置自定义请求委托
|
||||
//YooAssets.SetDownloadSystemUnityWebRequest(NasWebRequester);
|
||||
|
||||
|
||||
//初始化资源加载模块
|
||||
// 增加包存在性检查
|
||||
var package = YooAssets.TryGetPackage(AppConfig.PackageDomainName);
|
||||
if (package == null)
|
||||
{
|
||||
Debug.LogWarning($"UnityEvo:资源包 {AppConfig.PackageDomainName} 不存在,正在尝试创建...");
|
||||
package = YooAssets.CreatePackage(AppConfig.PackageDomainName);
|
||||
}
|
||||
|
||||
YooAssets.SetDefaultPackage(package);
|
||||
|
||||
// 初始化资源包
|
||||
#if EDITOR_SIMULATEMODE
|
||||
await EDITOR_SIMULATEMODE(package);
|
||||
FsmSystem.SetCurState(nameof(ResUpdateServerState));
|
||||
#elif OFFLINE_PLAYMODE
|
||||
await OFFLINE_PLAYMODE(package);
|
||||
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
|
||||
#elif HOST_PLAYMODE
|
||||
// if (package.PackageName.Equals("Main"))
|
||||
// {
|
||||
// await OFFLINE_PLAYMODE(package);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
//登录
|
||||
string url = AppConfig.IpConfig + "/Authentication/login";
|
||||
bool isLogin = await WebRequestSystem.Login(url, AppConfig.UserName, AppConfig.PassWord);
|
||||
if (isLogin)
|
||||
await HOST_PLAYMODE(package);
|
||||
else
|
||||
await Chche_PLAYMODE(package);
|
||||
// }
|
||||
|
||||
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
|
||||
|
||||
#elif WEB_PLAYMODE
|
||||
// IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
|
||||
// var webServerFileSystemParams = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
|
||||
// var webRemoteFileSystemParams =
|
||||
// FileSystemParameters.CreateDefaultWebRemoteFileSystemParameters(remoteServices); //支持跨域下载
|
||||
//
|
||||
// var initParameters = new WebPlayModeParameters();
|
||||
// initParameters.WebServerFileSystemParameters = webServerFileSystemParams;
|
||||
// initParameters.WebRemoteFileSystemParameters = webRemoteFileSystemParams;
|
||||
//
|
||||
// var initOperation = package.InitializeAsync(initParameters);
|
||||
// await initOperation;
|
||||
//
|
||||
// if (initOperation.Status == EOperationStatus.Succeed)
|
||||
// Debug.Log("UnityEvo:资源包初始化成功!");
|
||||
// else
|
||||
// Debug.LogError($"UnityEvo:资源包初始化失败:{initOperation.Error}");
|
||||
|
||||
Debug.LogError($"UnityEvo:暂不支持");
|
||||
#endif
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
|
||||
public override UniTask OnExitAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
private async UniTask EDITOR_SIMULATEMODE(ResourcePackage package)
|
||||
{
|
||||
var initParams=YooAssetFileSystem.EditorSimulateInitializeParameter();
|
||||
var initialization = package.InitializeAsync(initParams);
|
||||
await initialization;
|
||||
if (initialization.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
Assert.AreEqual(EOperationStatus.Succeed, initialization.Status);
|
||||
Debug.Log("UnityEvo:资源包初始化成功!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:资源包初始化失败:{initialization.Error}");
|
||||
}
|
||||
}
|
||||
private async UniTask Chche_PLAYMODE(ResourcePackage package)
|
||||
{
|
||||
Debug.Log("UnityEvo:网络连接不通畅,切换缓存加载!");
|
||||
var initParams=YooAssetFileSystem.hostInitializeParameter();
|
||||
var initOperation = package.InitializeAsync(initParams);
|
||||
await initOperation;
|
||||
|
||||
var operation = package.RequestPackageVersionAsync();
|
||||
await operation;
|
||||
if (operation.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", operation.PackageVersion);
|
||||
Debug.Log("UnityEvo:从本地缓存中加载资源包,初始化获取版本号成功!");
|
||||
}else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:从本地缓存中加载资源包,初始化获取版本号失败!");
|
||||
}
|
||||
|
||||
if (initOperation.Status == EOperationStatus.Succeed)
|
||||
Debug.Log("UnityEvo:从本地缓存中资源包,初始化成功!");
|
||||
else
|
||||
Debug.LogError($"UnityEvo:从本地缓存中资源包,初始化失败:{initOperation.Error}");
|
||||
}
|
||||
private async UniTask OFFLINE_PLAYMODE(ResourcePackage package)
|
||||
{
|
||||
var initParams=YooAssetFileSystem.OfflineInitializeParameter();
|
||||
var initOperation = package.InitializeAsync(initParams);
|
||||
await initOperation;
|
||||
|
||||
var operation = package.RequestPackageVersionAsync();
|
||||
await operation;
|
||||
if (operation.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", operation.PackageVersion);
|
||||
Debug.Log("UnityEvo:从本地加载资源包,初始化获取版本号成功!");
|
||||
}else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:从本地加载资源包,初始化获取版本号失败!");
|
||||
}
|
||||
|
||||
if (initOperation.Status == EOperationStatus.Succeed)
|
||||
Debug.Log("UnityEvo:从本地加载资源包,初始化成功!");
|
||||
else
|
||||
Debug.LogError($"UnityEvo:从本地加载资源包,初始化失败:{initOperation.Error}");
|
||||
}
|
||||
|
||||
public async UniTask HOST_PLAYMODE(ResourcePackage package)
|
||||
{
|
||||
// 新增平台判断代码
|
||||
#if UNITY_EDITOR
|
||||
BuildTarget buildTarget = UnityEditor.EditorUserBuildSettings.activeBuildTarget;
|
||||
AppConfig.Platform = buildTarget.ToString();
|
||||
#else
|
||||
AppConfig.Platform = Application.platform.ToString();
|
||||
#endif
|
||||
Debug.Log($"目标平台标识: {AppConfig.Platform}");
|
||||
|
||||
// 请求资源版本
|
||||
string url = $"{AppConfig.IpConfig}/ResDomain/GetResDomainByDomain";
|
||||
var resDmainRequst = new ResDmainRequst()
|
||||
{
|
||||
ProductName = AppConfig.ProductName,
|
||||
DomainName = AppConfig.PackageDomainName,
|
||||
Platform = AppConfig.Platform,
|
||||
};
|
||||
//获取服务器版本
|
||||
var resDmainMessageEntity = await WebRequestSystem.Post(url, JsonConvert.SerializeObject(resDmainRequst));
|
||||
if (resDmainMessageEntity.code == 200)
|
||||
{
|
||||
ResDmainResponse resDmainResponse =
|
||||
JsonConvert.DeserializeObject<ResDmainResponse>(resDmainMessageEntity.data.ToString());
|
||||
//获取当前版本
|
||||
var oldVersion = PlayerPrefs.GetString($"{AppConfig.PackageDomainName}_GAME_VERSION");
|
||||
//版本不一致,开始下载
|
||||
if (resDmainResponse.PackageVersion != oldVersion)
|
||||
{
|
||||
await Download(resDmainResponse.DocumentFileId);
|
||||
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION",
|
||||
resDmainResponse.PackageVersion);
|
||||
}
|
||||
else //版本一致,加载缓存资源
|
||||
{
|
||||
Debug.Log($"UnityEvo:资源版本一致,自动跳过更新...");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:获取资源版本失败: 【{resDmainMessageEntity.message}】");
|
||||
}
|
||||
|
||||
var initParams=YooAssetFileSystem.hostInitializeParameter();
|
||||
// initParameters.CacheFileSystemParameters = cacheFileSystemParams;
|
||||
|
||||
var initOperation = package.InitializeAsync(initParams);
|
||||
|
||||
await initOperation;
|
||||
|
||||
if (initOperation.Status == EOperationStatus.Succeed)
|
||||
Debug.Log("UnityEvo:从远程加载资源包,初始化成功!");
|
||||
else
|
||||
Debug.LogError($"UnityEvo:从远程加载资源包,初始化失败:{initOperation.Error}");
|
||||
}
|
||||
|
||||
public async UniTask Download(string fileId)
|
||||
{
|
||||
// 在任意MonoBehaviour或DomainBase派生类中
|
||||
string loadPath = $"{Application.persistentDataPath}/DownloadedContent/{AppConfig.PackageDomainName}";
|
||||
if (Directory.Exists(loadPath))
|
||||
{
|
||||
Directory.Delete(loadPath, true);
|
||||
}
|
||||
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
|
||||
if (loadSystem.ProgressBarPanel == null)
|
||||
{
|
||||
loadSystem.ProgressBarPanel =Object.Instantiate(Resources.Load<GameObject>("ProgressBarPanel"),
|
||||
Camera.main.transform).GetOrAddComponent<ProgressBarPanel>();
|
||||
}
|
||||
await ZipTool.DownloadAndUnzipAsync(fileId, loadPath, DownLoadProgress, UnzipProgress);
|
||||
}
|
||||
|
||||
private void DownLoadProgress(float progress)
|
||||
{
|
||||
Debug.Log($"下载进度:{progress:P0}");
|
||||
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
|
||||
loadSystem.ProgressBarPanel.SetProgressBarValue("下载中", progress);
|
||||
}
|
||||
|
||||
private void UnzipProgress(float progress)
|
||||
{
|
||||
Debug.Log($"解压进度:{progress:P0}");
|
||||
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
|
||||
loadSystem.ProgressBarPanel.SetProgressBarValue("解压中", progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2462c737fc9c4b53b35557f8a6aac453
|
||||
timeCreated: 1741165298
|
||||
@@ -0,0 +1,144 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class ResUpdateLocalState : AbstractFSMIState
|
||||
{
|
||||
public ResUpdateLocalState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override async UniTask OnEnterAsync()
|
||||
{
|
||||
//初始化读取资源配置表
|
||||
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
|
||||
|
||||
|
||||
//更新失败
|
||||
|
||||
Debug.Log($"UnityEvo:切换为加载本地缓存资源...");
|
||||
string packageVersion = "";
|
||||
if (package.PackageName.Equals("Main"))
|
||||
{
|
||||
var operation = package.RequestPackageVersionAsync();
|
||||
await operation;
|
||||
if (operation.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
packageVersion = operation.PackageVersion;
|
||||
Debug.Log($"Unity:【Main】Request package Version : {packageVersion}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//更新失败
|
||||
Debug.LogError("Unity:【Main】" + operation.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 获取上次成功记录的版本
|
||||
packageVersion = PlayerPrefs.GetString($"{AppConfig.PackageDomainName}_GAME_VERSION", string.Empty);
|
||||
if (string.IsNullOrEmpty(packageVersion))
|
||||
{
|
||||
Debug.Log($"UnityEvo:没有找到本地版本记录,需要更新资源!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Debug.Log($"UnityEvo:获取资源版本 Version : 【{packageVersion}】");
|
||||
Debug.Log($"UnityEvo:开始加载本地资源...");
|
||||
// Assert.AreEqual(EOperationStatus.Succeed, requetVersionOp.Status);
|
||||
|
||||
|
||||
// 加载本地缓存的资源清单文件
|
||||
var updateManifestOp = package.UpdatePackageManifestAsync(packageVersion);
|
||||
await updateManifestOp;
|
||||
if (updateManifestOp.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
Debug.Log($"UnityEvo:更新本地资源清单 updateManifest : 【成功】");
|
||||
}
|
||||
else
|
||||
{
|
||||
//更新失败
|
||||
Debug.LogError($"UnityEvo:加载本地资源清单文件失败,需要更新资源!: 【{updateManifestOp.Error}】");
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.AreEqual(EOperationStatus.Succeed, updateManifestOp.Status);
|
||||
|
||||
|
||||
//4.下载补丁包
|
||||
await Download();
|
||||
|
||||
|
||||
//加载热更配置文件
|
||||
var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
|
||||
await loadHotfixSettingsOp;
|
||||
DomainConfig domainConfig = null;
|
||||
if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
Debug.Log($"UnityEvo:加载热更配置文件 DomainConfig : 【成功】");
|
||||
domainConfig = loadHotfixSettingsOp.AssetObject as DomainConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:加载热更配置文件 DomainConfig : 【失败】");
|
||||
}
|
||||
|
||||
|
||||
if (domainConfig == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:【{package.PackageName}】加载DomainConfig为空,无法继续执行后续流程,请检查!!!");
|
||||
}
|
||||
|
||||
FsmSystem.SetCurState(nameof(HotFixState), domainConfig);
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
|
||||
public override UniTask OnExitAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
#region 下载热更资源
|
||||
|
||||
public async UniTask Download()
|
||||
{
|
||||
int downloadingMaxNum = 1;
|
||||
int failedTryAgain = 1;
|
||||
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
|
||||
var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
|
||||
|
||||
// 在正常开始游戏之前,还需要验证本地清单内容的完整性。
|
||||
if (downloader.TotalDownloadCount > 0)
|
||||
{
|
||||
Debug.Log("UnityEvo:资源内容本地并不完整,需要更新资源!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eca158039896455dba3ded1eb703a5da
|
||||
timeCreated: 1742291100
|
||||
@@ -0,0 +1,193 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class ResUpdateServerState : AbstractFSMIState
|
||||
{
|
||||
public ResUpdateServerState(IFsmSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
public override async UniTask OnEnterAsync()
|
||||
{
|
||||
Debug.Log("UnityEvo:开始资源更新...");
|
||||
|
||||
|
||||
// //初始化读取资源配置表
|
||||
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
|
||||
// 请求资源版本
|
||||
var requetVersionOp = package.RequestPackageVersionAsync();
|
||||
await requetVersionOp;
|
||||
string packageVersion = "";
|
||||
if (requetVersionOp.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
packageVersion = requetVersionOp.PackageVersion;
|
||||
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", packageVersion);
|
||||
Debug.Log($"UnityEvo:获取资源版本 Version : 【{packageVersion}】");
|
||||
Debug.Log($"UnityEvo:开始加载服务器资源...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:获取资源版本失败: 【{requetVersionOp.Error}】");
|
||||
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert.AreEqual(EOperationStatus.Succeed, requetVersionOp.Status);
|
||||
|
||||
|
||||
// 更新资源清单
|
||||
var updateManifestOp = package.UpdatePackageManifestAsync(packageVersion);
|
||||
await updateManifestOp;
|
||||
if (updateManifestOp.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
Debug.Log($"UnityEvo:更新资源清单 updateManifest : 【成功】");
|
||||
}
|
||||
else
|
||||
{
|
||||
//更新失败
|
||||
Debug.LogError($"UnityEvo:更新资源清单失败: 【{updateManifestOp.Error}】");
|
||||
}
|
||||
|
||||
Assert.AreEqual(EOperationStatus.Succeed, updateManifestOp.Status);
|
||||
|
||||
|
||||
//4.下载补丁包
|
||||
await Download();
|
||||
|
||||
|
||||
//加载热更配置文件
|
||||
var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
|
||||
await loadHotfixSettingsOp;
|
||||
DomainConfig domainConfig = null;
|
||||
if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//更新成功
|
||||
Debug.Log($"UnityEvo:加载热更配置文件 loadHotfixSettings : 【成功】");
|
||||
domainConfig = loadHotfixSettingsOp.AssetObject as DomainConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"UnityEvo:加载热更配置文件 loadHotfixSettings : 【失败】");
|
||||
}
|
||||
|
||||
|
||||
if (domainConfig == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:【{package.PackageName}】加载DomainConfig为空,无法继续执行后续流程,请检查!!!");
|
||||
}
|
||||
|
||||
FsmSystem.SetCurState(nameof(HotFixState), domainConfig);
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T>(T param)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
}
|
||||
|
||||
|
||||
public override UniTask OnExitAsync()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
#region 下载热更资源
|
||||
|
||||
public async UniTask Download()
|
||||
{
|
||||
int downloadingMaxNum = 10;
|
||||
int failedTryAgain = 3;
|
||||
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
|
||||
var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
|
||||
|
||||
//没有需要下载的资源
|
||||
if (downloader.TotalDownloadCount == 0)
|
||||
{
|
||||
Debug.Log("UnityEvo:没有需要下载的资源,跳过更新");
|
||||
return;
|
||||
}
|
||||
|
||||
//需要下载的文件总数和总大小
|
||||
int totalDownloadCount = downloader.TotalDownloadCount;
|
||||
long totalDownloadBytes = downloader.TotalDownloadBytes;
|
||||
Debug.Log($"UnityEvo:需要下载的资源的个数【{totalDownloadCount}】,需要下载的资源的总大小{totalDownloadBytes / 1024} MB");
|
||||
//===================================适应新版本YooAsset插件的修改===================================
|
||||
//注册回调方法
|
||||
downloader.DownloadErrorCallback = OnDownloadErrorFunction;
|
||||
downloader.DownloadUpdateCallback = OnDownloadProgressUpdateFunction;
|
||||
downloader.DownloadFinishCallback = OnDownloadOverFunction;
|
||||
downloader.DownloadFileBeginCallback = OnStartDownloadFileFunction;
|
||||
//===================================适应新版本YooAsset插件的修改===================================
|
||||
|
||||
//开启下载
|
||||
downloader.BeginDownload();
|
||||
await downloader;
|
||||
|
||||
//检测下载结果
|
||||
if (downloader.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
//下载成功
|
||||
Debug.Log("UnityEvo:资源更新完成");
|
||||
}
|
||||
else
|
||||
{
|
||||
//下载失败
|
||||
Debug.Log("UnityEvo:资源更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
//===================================适应新版本YooAsset插件的修改===================================
|
||||
/// <summary>
|
||||
/// 开始下载
|
||||
/// </summary>
|
||||
private void OnStartDownloadFileFunction(DownloadFileData downloadFileData)
|
||||
{
|
||||
Debug.Log(
|
||||
$"UnityEvo:开始下载:文件名:{downloadFileData.FileName},文件大小:{downloadFileData.FileSize / 1024f / 1024f} MB");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载完成
|
||||
/// </summary>
|
||||
private void OnDownloadOverFunction(DownloaderFinishData downloaderFinishData)
|
||||
{
|
||||
Debug.Log("UnityEvo:下载" + (downloaderFinishData.Succeed ? "成功" : "失败"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新中
|
||||
/// </summary>
|
||||
private void OnDownloadProgressUpdateFunction(DownloadUpdateData downloadUpdateData)
|
||||
{
|
||||
Debug.Log(
|
||||
$"UnityEvo:文件总数:{downloadUpdateData.TotalDownloadCount},已下载文件数:{downloadUpdateData.CurrentDownloadCount},下载总大小:{downloadUpdateData.TotalDownloadBytes / 1024f / 1024f} MB,已下载大小{downloadUpdateData.CurrentDownloadBytes / 1024f / 1024f} MB");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载出错
|
||||
/// </summary>
|
||||
/// <param name="errorData"></param>
|
||||
private void OnDownloadErrorFunction(DownloadErrorData errorData)
|
||||
{
|
||||
Debug.Log($"UnityEvo:下载出错:包名:{errorData.PackageName} 文件名:{errorData.FileName},错误信息:{errorData.ErrorInfo}");
|
||||
}
|
||||
//===================================适应新版本YooAsset插件的修改===================================
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf15fa0d3ca74b0991fd60bef616d007
|
||||
timeCreated: 1741248693
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class HybridClREntrance : MonoBehaviour
|
||||
{
|
||||
private FsmLoadSystem _fsmSystem;
|
||||
public string domain;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_fsmSystem = new FsmLoadSystem();
|
||||
// 初始化日志
|
||||
_fsmSystem.AddState(new ResStartState(_fsmSystem));
|
||||
_fsmSystem.AddState(new ResUpdateServerState(_fsmSystem));
|
||||
_fsmSystem.AddState(new ResUpdateLocalState(_fsmSystem));
|
||||
_fsmSystem.AddState(new HotFixState(_fsmSystem));
|
||||
_fsmSystem.AddState(new LoadResState(_fsmSystem));
|
||||
//_fsmSystem.AddState(new LoadResMainState(_fsmSystem));
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
domain = "Main";
|
||||
#endif
|
||||
_fsmSystem.SetOpenDomainType(OpenDomainType.DEFAULT);
|
||||
AppConfig.PackageDomainName = domain;
|
||||
_fsmSystem.SetCurState(nameof(ResStartState));
|
||||
}
|
||||
|
||||
public void OpenDomain()
|
||||
{
|
||||
AppConfig.PackageDomainName = domain;
|
||||
_fsmSystem.SetCurState(nameof(ResStartState));
|
||||
}
|
||||
|
||||
public void OpenDomain(string domain, OpenDomainType openDomainType)
|
||||
{
|
||||
_fsmSystem.SetOpenDomainType(openDomainType);
|
||||
AppConfig.PackageDomainName = domain;
|
||||
_fsmSystem.SetCurState(nameof(ResStartState));
|
||||
}
|
||||
|
||||
|
||||
public void CloseDomain()
|
||||
{
|
||||
_fsmSystem.SetCurState(nameof(DefaultState));
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_fsmSystem.CurState.OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开的项目类型
|
||||
/// DEFAULT:默认打开方式
|
||||
/// VIOICE:语音打开方式
|
||||
/// </summary>
|
||||
public enum OpenDomainType
|
||||
{
|
||||
DEFAULT,
|
||||
VIOICE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 467bc10b56c272043a89b12cbf23b3ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40e68a50876f42e3ad8ecec2e4d36aa3
|
||||
timeCreated: 1745896633
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
public interface IAudioTableData : IData
|
||||
{
|
||||
UniTask LoadData(string audiotabledata_asset, string uitabledata_asset);
|
||||
Stary.Evo.TableTextConversion.AudioTableData.MessageInfo GetAudioInfo(string auid);
|
||||
UniTask<AudioClip> GetAudioClip(string auid);
|
||||
AudioTableData.AudioToUITableData GetAudioToUIInfo(string auid);
|
||||
UniTask<AudioTableData.AudioToUIData> GetAudioClipToUISprite(string auid);
|
||||
Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid);
|
||||
UniTask<Sprite> GetUISprite(string uiid);
|
||||
}
|
||||
|
||||
|
||||
public class AudioTableData : AbstractData, IAudioTableData
|
||||
{
|
||||
private Stary.Evo.TableTextConversion.AudioTableData audioTableData;
|
||||
|
||||
private Stary.Evo.TableTextConversion.UITableData uiTableDatas;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
protected override async void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
public async UniTask LoadData(string audiotabledata_asset, string uitabledata_asset)
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var audioHandle =
|
||||
YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.AudioTableData>(audiotabledata_asset);
|
||||
await audioHandle.WithCancellation(_cancellationTokenSource.Token);
|
||||
audioTableData = audioHandle.GetAssetObject<Stary.Evo.TableTextConversion.AudioTableData>();
|
||||
|
||||
var UIHandle =
|
||||
YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.UITableData>(uitabledata_asset);
|
||||
await UIHandle.WithCancellation(_cancellationTokenSource.Token);
|
||||
uiTableDatas = UIHandle.GetAssetObject<Stary.Evo.TableTextConversion.UITableData>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取音频数据
|
||||
/// </summary>
|
||||
/// <param name="auid"></param>
|
||||
/// <returns></returns>
|
||||
public Stary.Evo.TableTextConversion.AudioTableData.MessageInfo GetAudioInfo(string auid)
|
||||
{
|
||||
var info = audioTableData.infos.Find(x => x.auid == auid);
|
||||
if (info != null && !info.filename.Contains("Audios"))
|
||||
{
|
||||
info.filename = "Audios_" + info.filename;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取音频
|
||||
/// </summary>
|
||||
/// <param name="auid"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask<AudioClip> GetAudioClip(string auid)
|
||||
{
|
||||
var info=GetAudioInfo(auid);
|
||||
var handle = YooAssets.LoadAssetAsync<AudioClip>(info.filename);
|
||||
await handle.WithCancellation(_cancellationTokenSource.Token);
|
||||
if (handle.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
return handle.GetAssetObject<AudioClip>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
$"加载音频失败,错误的id为:{auid},错误的音频名称为:{info.filename},错误的错误信息为:{handle.LastError}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取音频数据获取UI数据
|
||||
/// </summary>
|
||||
/// <param name="auid"></param>
|
||||
/// <returns></returns>
|
||||
public AudioToUITableData GetAudioToUIInfo(string auid)
|
||||
{
|
||||
var info=GetAudioInfo(auid);
|
||||
|
||||
Stary.Evo.TableTextConversion.UITableData.MessageInfo messageInfo = GetUIInfo(info.uirelated);
|
||||
if (messageInfo != null)
|
||||
{
|
||||
return new AudioToUITableData()
|
||||
{
|
||||
audioFileName = info.filename,
|
||||
UIMessageInfo = messageInfo
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"没有找到对应的uiid,错误的id为:{info.uirelated}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取音频数据获取UI数据
|
||||
/// </summary>
|
||||
/// <param name="auid"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask<AudioToUIData> GetAudioClipToUISprite(string auid)
|
||||
{
|
||||
var info=GetAudioToUIInfo(auid);
|
||||
AudioClip audioClip = await GetAudioClip(auid);
|
||||
var audioTableData = new AudioToUIData();
|
||||
if (audioClip != null)
|
||||
{
|
||||
audioTableData.audioClip = audioClip;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"没有找到对应的auid,错误的id为:{auid}");
|
||||
}
|
||||
|
||||
if (info.UIMessageInfo != null)
|
||||
{
|
||||
Sprite sprite = await GetUISprite(info.UIMessageInfo.uiid);
|
||||
if ( sprite != null)
|
||||
{
|
||||
audioTableData.sprite = sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"没有找到对应的uiid,错误的id为:{info.UIMessageInfo.uiid}");
|
||||
}
|
||||
}
|
||||
return audioTableData;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取UI数据
|
||||
/// </summary>
|
||||
/// <param name="uiid"></param>
|
||||
/// <returns></returns>
|
||||
public Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid)
|
||||
{
|
||||
var info = uiTableDatas.infos.Find(x => x.uiid == uiid);
|
||||
if (info != null && !info.filename.Contains("Sprites_"))
|
||||
{
|
||||
info.filename = "Sprites_" + info.filename;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取UI
|
||||
/// </summary>
|
||||
/// <param name="auid"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask<Sprite> GetUISprite(string uiid)
|
||||
{
|
||||
var info = GetUIInfo(uiid);
|
||||
|
||||
var handle = YooAssets.LoadAssetAsync<Sprite>(info.filename);
|
||||
await handle.WithCancellation(_cancellationTokenSource.Token);
|
||||
if (handle.Status == EOperationStatus.Succeed)
|
||||
{
|
||||
return handle.GetAssetObject<Sprite>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
$"加载ui失败,错误的id为:{uiid},错误的ui名称为:{info.filename},错误的错误信息为:{handle.LastError}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException e)
|
||||
{
|
||||
Debug.Log("异步取消操作");
|
||||
}
|
||||
}
|
||||
|
||||
public struct AudioToUITableData
|
||||
{
|
||||
public string audioFileName;
|
||||
public Stary.Evo.TableTextConversion.UITableData.MessageInfo UIMessageInfo;
|
||||
}
|
||||
public struct AudioToUIData
|
||||
{
|
||||
public AudioClip audioClip;
|
||||
public Sprite sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 336c695a4299453bb325ceeaaa9bb4ed
|
||||
timeCreated: 1745736803
|
||||
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using DG.Tweening;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.RKTools;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
public interface IClickSystem : ISystem
|
||||
{
|
||||
void AddClickIntervalEvent(Action callback);
|
||||
void StartTime();
|
||||
void EndClick();
|
||||
|
||||
void BindClickEvent(List<ClickElementChildren> clickElementChildrens, Action<GameObject> callback);
|
||||
void ClickBreatheTween(List<ClickElementChildren> tweenList, Transform tweenTarget);
|
||||
|
||||
bool IsElementChildrenClick();
|
||||
}
|
||||
|
||||
public class ClickSystem : AbstractSystem, IClickSystem
|
||||
{
|
||||
// public Sequence loopTargetTween;
|
||||
|
||||
/// <summary>
|
||||
/// 点击计时器
|
||||
/// </summary>
|
||||
public float time;
|
||||
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
|
||||
private List<ClickElementChildren> clickElementChildrens;
|
||||
|
||||
|
||||
private Action ClickIntervalEvent;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
public void StartTime()
|
||||
{
|
||||
time = 0;
|
||||
Update();
|
||||
}
|
||||
|
||||
public void BindClickEvent(List<ClickElementChildren> clickElementChildrens, Action<GameObject> callback)
|
||||
{
|
||||
StartTime();
|
||||
this.clickElementChildrens = clickElementChildrens;
|
||||
//绑定点击事件
|
||||
foreach (var child in clickElementChildrens)
|
||||
{
|
||||
child.transform.gameObject.ObjectAddTouchEvent(callback);
|
||||
}
|
||||
}
|
||||
public void ClickBreatheTween(List<ClickElementChildren> tweenList, Transform tweenTarget)
|
||||
{
|
||||
Debug.Log("UnityEvo: 点击呼吸");
|
||||
time = 0;
|
||||
foreach (var tween in tweenList)
|
||||
{
|
||||
tween.transform.gameObject.ObjectResumeTouchEvent();
|
||||
// if (tween.transform == tweenTarget)
|
||||
// {
|
||||
// tween.isClick = true;
|
||||
// }
|
||||
|
||||
if (tween.transform == tweenTarget)
|
||||
{
|
||||
tween.isClick = true;
|
||||
tween.transform.GetComponent<Animator>().CrossFade("dianji_idle", 0.2f);
|
||||
this.SendEvent<ModeType, ClickElementChildren>(ModeType.PLayAudioOrVideo,tween);
|
||||
tweenTarget.gameObject.ObjectPauseTouchEvent();
|
||||
// if (loopTargetTween != null)
|
||||
// {
|
||||
// loopTargetTween.Kill();
|
||||
// loopTargetTween = null;
|
||||
// }
|
||||
//
|
||||
// loopTargetTween = DOTween.Sequence();
|
||||
// loopTargetTween.Append(tweenTarget.DOScale(tweenTarget.lossyScale * 1.3f, 1f));
|
||||
//
|
||||
// loopTargetTween.SetLoops(6, LoopType.Yoyo);
|
||||
// loopTargetTween.OnKill(() =>
|
||||
// {
|
||||
// if (tweenTarget != null)
|
||||
// tweenTarget.DOScale(Vector3.one, 0.3f);
|
||||
// });
|
||||
// loopTargetTween.Play();
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// tween.transform.DOKill();
|
||||
// tween.transform.DOScale(Vector3.one * 0.9f, 0.5f);
|
||||
tween.transform.GetComponent<Animator>().CrossFade("dianji_nood", 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddClickIntervalEvent(Action callback)
|
||||
{
|
||||
this.ClickIntervalEvent = callback;
|
||||
}
|
||||
public bool IsElementChildrenClick()
|
||||
{
|
||||
//是否全部已经点击
|
||||
for (int i = 0; i < clickElementChildrens.Count; i++)
|
||||
{
|
||||
if (clickElementChildrens[i].isClick == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EndClick()
|
||||
{
|
||||
// if (loopTargetTween != null)
|
||||
// {
|
||||
// loopTargetTween.Kill();
|
||||
// loopTargetTween = null;
|
||||
// }
|
||||
|
||||
time = 0;
|
||||
if (tokenSource != null)
|
||||
{
|
||||
tokenSource?.Cancel();
|
||||
tokenSource?.Dispose();
|
||||
tokenSource = null;
|
||||
}
|
||||
|
||||
foreach (var children in clickElementChildrens)
|
||||
{
|
||||
children.transform.gameObject.ObjectRemoveTouchEvent();
|
||||
children.transform.GetComponent<Animator>().CrossFade("dianji_disappear", 0.2f);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async void Update()
|
||||
{
|
||||
//TODO暂时禁用
|
||||
// if (tokenSource != null)
|
||||
// {
|
||||
// tokenSource?.Cancel();
|
||||
// }
|
||||
//
|
||||
// tokenSource = new CancellationTokenSource();
|
||||
// try
|
||||
// {
|
||||
// while (time <= 11 && !tokenSource.IsCancellationRequested)
|
||||
// {
|
||||
// time += Time.deltaTime;
|
||||
//
|
||||
// if (time >= 10)
|
||||
// {
|
||||
// Debug.Log("UnityEvo:执行未点击间隔事件");
|
||||
// time = 0;
|
||||
// ClickIntervalEvent?.Invoke();
|
||||
// }
|
||||
//
|
||||
// await UniTask.Yield(tokenSource.Token);
|
||||
// }
|
||||
// }
|
||||
// catch (OperationCanceledException e)
|
||||
// {
|
||||
// Debug.Log("UnityEvo: 取消任务:" + e);
|
||||
// }
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (tokenSource != null)
|
||||
{
|
||||
tokenSource?.Cancel();
|
||||
tokenSource?.Dispose();
|
||||
tokenSource = null;
|
||||
}
|
||||
|
||||
// if (loopTargetTween != null)
|
||||
// {
|
||||
// loopTargetTween.Kill();
|
||||
// loopTargetTween = null;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public class ClickElementChildren
|
||||
{
|
||||
public bool isClick = false;
|
||||
public string[] auid;
|
||||
public string vid;
|
||||
public Transform transform;
|
||||
|
||||
public Transform targetTransform;
|
||||
|
||||
public ClickElementChildren(Transform child)
|
||||
{
|
||||
|
||||
this. transform = child;
|
||||
}
|
||||
public ClickElementChildren(Transform child, string[] auid, string vid)
|
||||
{
|
||||
this.auid = auid;
|
||||
this.vid = vid;
|
||||
this. transform = child;
|
||||
}
|
||||
public ClickElementChildren(Transform child,Transform targetTransform , string[] auid, string vid)
|
||||
{
|
||||
this.auid = auid;
|
||||
this.vid = vid;
|
||||
this. transform = child;
|
||||
this.targetTransform= targetTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 206bc7f4b09d456898aed456017e6529
|
||||
timeCreated: 1745915168
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.AudioCore;
|
||||
using Stary.Evo.InformationSave;
|
||||
using Stary.Evo.UIFarme;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using YooAsset;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
public class IntroPanel : BasePanel
|
||||
{
|
||||
static readonly string path = "IntroPanel";
|
||||
|
||||
private Image intro;
|
||||
|
||||
private LocalTransformInfo transformInfo;
|
||||
|
||||
public IntroPanel()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize(GameObject panelGo)
|
||||
{
|
||||
base.Initialize(panelGo);
|
||||
|
||||
intro = activePanel.transform.Find("Intro").GetComponent<Image>();
|
||||
transformInfo = activePanel.transform.GetComponent<LocalTransformInfo>();
|
||||
intro.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
base.OnEnter();
|
||||
|
||||
this.RegisterEvent<IntroType, string, string>(IntroType.IntroSprite, OnClickPreviousIntro);
|
||||
this.RegisterEvent<IntroType, RuntimeAnimatorController, string>(IntroType.IntroAnimation, OnAddAnimation);
|
||||
this.RegisterEvent<IntroType, string, string>(IntroType.IntroAudio, OnClickPreviousIntroAudio);
|
||||
}
|
||||
|
||||
public override void OnExit(float delay = 0)
|
||||
{
|
||||
base.OnExit(delay);
|
||||
intro.sprite = null;
|
||||
intro.gameObject.SetActive(false);
|
||||
this.UnRegisterEvent<IntroType, string, string>(IntroType.IntroSprite, OnClickPreviousIntro);
|
||||
this.UnRegisterEvent<IntroType, string, string>(IntroType.IntroAudio, OnClickPreviousIntroAudio);
|
||||
this.UnRegisterEvent<IntroType, RuntimeAnimatorController, string>(IntroType.IntroAnimation, OnAddAnimation);
|
||||
var animator = intro.transform.GetComponent<Animator>();
|
||||
if (animator != null)
|
||||
{
|
||||
GameObject.Destroy(animator);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OnAddAnimation(RuntimeAnimatorController animatorController, string desc)
|
||||
{
|
||||
var animator = intro.transform.GetOrAddComponent<Animator>();
|
||||
animator.runtimeAnimatorController = animatorController;
|
||||
|
||||
intro.SetNativeSize();
|
||||
intro.gameObject.SetActive(true);
|
||||
transformInfo.Set(desc);
|
||||
}
|
||||
|
||||
public async void OnClickPreviousIntro(string spriteName, string desc)
|
||||
{
|
||||
var handle= YooAssets.LoadAssetAsync<Sprite>(spriteName);
|
||||
await handle.Task;
|
||||
intro.sprite = handle.GetAssetObject<Sprite>();
|
||||
intro.SetNativeSize();
|
||||
intro.gameObject.SetActive(true);
|
||||
transformInfo.Set(desc);
|
||||
}
|
||||
|
||||
public async void OnClickPreviousIntroAudio(string auid, string desc)
|
||||
{
|
||||
var info = await this.GetData<IAudioTableData>().GetAudioClipToUISprite(auid);
|
||||
AudioCoreManager.PlayVoice(new AudioData()
|
||||
{
|
||||
clip = info.audioClip,
|
||||
});
|
||||
SetIntroSprite(info.sprite, desc);
|
||||
}
|
||||
private void SetIntroSprite(Sprite sprite, string desc)
|
||||
{
|
||||
|
||||
intro.sprite = sprite;
|
||||
intro.SetNativeSize();
|
||||
intro.gameObject.SetActive(true);
|
||||
transformInfo.Set(desc);
|
||||
}
|
||||
public enum IntroType
|
||||
{
|
||||
IntroSprite,
|
||||
IntroAudio,
|
||||
IntroAnimation
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fced65e1b5fa8041a7f8474992e2a5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.AudioCore;
|
||||
using Stary.Evo.InformationSave;
|
||||
using Stary.Evo.UIFarme;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using YooAsset;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
public class ProgressBarPanel : MonoBehaviour
|
||||
{
|
||||
private Image _fill;
|
||||
private Text _fillText;
|
||||
private Text _fillMessage;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_fill = this.transform.Find("Fill").GetComponent<Image>();
|
||||
_fillText = _fill.transform.Find("FillText").GetComponent<Text>();
|
||||
_fillMessage = _fill.transform.Find("FillMessage").GetComponent<Text>();
|
||||
_fill.fillAmount = 0;
|
||||
_fillText.text = "0%";
|
||||
}
|
||||
|
||||
|
||||
public async void SetProgressBarValue(string message, float value)
|
||||
{
|
||||
this.gameObject.SetActive(true);
|
||||
_fill.fillAmount = value;
|
||||
_fillMessage.text = message;
|
||||
_fillText.text = $"{value:P0}";
|
||||
if (value >= 1)
|
||||
{
|
||||
this.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1795353bee11314b950ef806ab8458f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo;
|
||||
using YooAsset;
|
||||
|
||||
namespace X_04_04
|
||||
{
|
||||
public interface IUITableData : IData
|
||||
{
|
||||
UniTask LoadData(string audiotabledata_asset);
|
||||
Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid);
|
||||
}
|
||||
|
||||
|
||||
public class UITableData : AbstractData, IUITableData
|
||||
{
|
||||
private Stary.Evo.TableTextConversion.UITableData uiTableDatas;
|
||||
protected override async void OnInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async UniTask LoadData(string audiotabledata_asset)
|
||||
{
|
||||
var handle = YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.UITableData>(audiotabledata_asset);
|
||||
await handle.Task;
|
||||
uiTableDatas = handle.GetAssetObject<Stary.Evo.TableTextConversion.UITableData>();
|
||||
}
|
||||
|
||||
public Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid)
|
||||
{
|
||||
var info = uiTableDatas.infos.Find(x => x.uiid == uiid);
|
||||
if (info != null && !info.filename.Contains("Sprites_"))
|
||||
{
|
||||
info.filename="Sprites_"+info.filename;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31eb4845a899e544dab03b7aba620e01
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Main
|
||||
{
|
||||
public interface IVideoTableData : IData
|
||||
{
|
||||
UniTask LoadData(string videotabledata_asset);
|
||||
Stary.Evo.TableTextConversion.VideoTableData.MessageInfo PlayVideoName(string vidid);
|
||||
}
|
||||
|
||||
|
||||
public class VideoTableData : AbstractData, IVideoTableData
|
||||
{
|
||||
private Stary.Evo.TableTextConversion.VideoTableData videoTableDatas;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
protected override async void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
public async UniTask LoadData(string videotabledata_asset)
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var handle = YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.VideoTableData>(videotabledata_asset);
|
||||
await handle.WithCancellation(this._cancellationTokenSource.Token);
|
||||
videoTableDatas = handle.GetAssetObject<Stary.Evo.TableTextConversion.VideoTableData>();
|
||||
}
|
||||
|
||||
public Stary.Evo.TableTextConversion.VideoTableData.MessageInfo PlayVideoName(string vidid)
|
||||
{
|
||||
var info = videoTableDatas.infos.Find(x => x.vidid == vidid);
|
||||
if (info != null && !info.filename.Contains("Video"))
|
||||
{
|
||||
info.filename = "Video_" + info.filename;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException e)
|
||||
{
|
||||
Debug.Log("异步取消操作");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c1c8c62f563482ea249c39b08cab4fa
|
||||
timeCreated: 1745311080
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3f64633ec28465bb032f798d31269cf
|
||||
timeCreated: 1745723917
|
||||
@@ -0,0 +1,6 @@
|
||||
public enum ModeType
|
||||
{
|
||||
VideoStart,
|
||||
VideoEnd,
|
||||
PLayAudioOrVideo
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83e20a216f634924a360a92115ffe0cd
|
||||
timeCreated: 1745723981
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using DG.Tweening;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.AudioCore;
|
||||
using Stary.Evo.UIFarme;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Video;
|
||||
using YooAsset;
|
||||
|
||||
|
||||
public class VideoPanel : BasePanel
|
||||
{
|
||||
private RawImage intro;
|
||||
|
||||
private GameObject prospect;
|
||||
private GameObject bg;
|
||||
|
||||
private Animator _animator;
|
||||
|
||||
private VideoPlayer videoPlayer;
|
||||
|
||||
private AudioSource audioSource;
|
||||
|
||||
private RenderTexture renderTexture;
|
||||
|
||||
public VideoPanel()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize(GameObject panelGo)
|
||||
{
|
||||
base.Initialize(panelGo);
|
||||
|
||||
intro = activePanel.transform.Find("Intro").GetComponent<RawImage>();
|
||||
prospect = activePanel.transform.Find("prospect").gameObject;
|
||||
bg = activePanel.transform.Find("bg").gameObject;
|
||||
videoPlayer = activePanel.GetComponentInChildren<VideoPlayer>();
|
||||
audioSource = activePanel.GetComponentInChildren<AudioSource>();
|
||||
_animator = activePanel.GetComponentInChildren<Animator>();
|
||||
}
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
base.OnEnter();
|
||||
|
||||
this.RegisterEvent<ModeType, VideoInfo>(ModeType.VideoStart, OnStartMove);
|
||||
this.RegisterEvent<ModeType>(ModeType.VideoEnd, OnStopMove);
|
||||
}
|
||||
|
||||
public override void OnExit(float delay = 0)
|
||||
{
|
||||
base.OnExit(delay);
|
||||
this.UnRegisterEvent<ModeType, VideoInfo>(ModeType.VideoStart, OnStartMove);
|
||||
this.UnRegisterEvent<ModeType>(ModeType.VideoEnd, OnStopMove);
|
||||
}
|
||||
|
||||
|
||||
private void OnStartMove(VideoInfo info)
|
||||
{
|
||||
if (info.filename.Equals(""))
|
||||
{
|
||||
Debug.LogError("UnityEvo:视频文件名为空,必须传文件名才能执行");
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.position != null)
|
||||
{
|
||||
activePanel.transform.position = info.position.SetVector3Ctor();
|
||||
}
|
||||
|
||||
if (info.rotation != null)
|
||||
{
|
||||
activePanel.transform.rotation = Quaternion.Euler(info.rotation.SetVector3Ctor());
|
||||
}
|
||||
|
||||
if (info.scale != null)
|
||||
{
|
||||
activePanel.transform.DOScale(info.scale.SetVector3Ctor(), 0.5f);
|
||||
}
|
||||
|
||||
OnStartMove(info.filename, info.callback, info.isFixedSize);
|
||||
}
|
||||
|
||||
|
||||
private async void OnStartMove(string fileName, Action callback, bool isFixedSize)
|
||||
{
|
||||
if (isFixedSize)
|
||||
{
|
||||
_animator.gameObject.SetActive(false);
|
||||
bg.SetActive(false);
|
||||
prospect.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_animator.gameObject.SetActive(true);
|
||||
bg.SetActive(true);
|
||||
prospect.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
//新增renderTexture创建
|
||||
if (videoPlayer.isPlaying)
|
||||
{
|
||||
videoPlayer.Stop();
|
||||
//释放renderTexture
|
||||
if (renderTexture != null)
|
||||
{
|
||||
renderTexture.Release();
|
||||
renderTexture = null;
|
||||
}
|
||||
}
|
||||
|
||||
var videHandle = YooAssets.LoadAssetAsync<VideoClip>(fileName);
|
||||
await videHandle.Task;
|
||||
Debug.Log("UnityEvo:开始播放视频:" + fileName);
|
||||
VideoClip videoClip = videHandle.GetAssetObject<VideoClip>();
|
||||
|
||||
renderTexture = new RenderTexture((int)videoClip.width, (int)videoClip.height, 24);
|
||||
videoPlayer.targetTexture = renderTexture;
|
||||
intro.texture = renderTexture;
|
||||
intro.SetNativeSize();
|
||||
var musicHandle = YooAssets.LoadAssetAsync<AudioClip>(fileName + "_mp3");
|
||||
await musicHandle.Task;
|
||||
|
||||
videoPlayer.source = VideoSource.VideoClip;
|
||||
videoPlayer.clip = videoClip;
|
||||
|
||||
Debug.Log("UnityEvo:开始播放视频的音频:" + fileName + "_mp3");
|
||||
AudioClip audioClip = musicHandle.GetAssetObject<AudioClip>();
|
||||
audioSource.clip = audioClip;
|
||||
|
||||
//等待视频加载完成
|
||||
videoPlayer.Prepare();
|
||||
videoPlayer.prepareCompleted += (source) =>
|
||||
{
|
||||
_animator.Play("vid_maskAni", 0, 0);
|
||||
|
||||
//预加载
|
||||
if (videoPlayer.isPrepared)
|
||||
{
|
||||
videoPlayer.Play();
|
||||
audioSource.Play();
|
||||
}
|
||||
};
|
||||
|
||||
videoPlayer.loopPointReached += (source) =>
|
||||
{
|
||||
OnStopMove();
|
||||
callback?.Invoke();
|
||||
};
|
||||
videoPlayer.errorReceived += (source, error) => { Debug.LogError($"视频播放失败:{error}"); };
|
||||
}
|
||||
|
||||
private void OnStopMove()
|
||||
{
|
||||
Debug.Log("UnityEvo:视频播放完成");
|
||||
AudioCoreManager.SetMusicVolume(new AudioData()
|
||||
{
|
||||
fadeDuration = 2f,
|
||||
volume = 1f,
|
||||
});
|
||||
_animator.Play("vid_maskDefault", 0, 0);
|
||||
if (renderTexture != null)
|
||||
{
|
||||
renderTexture.Release();
|
||||
renderTexture = null;
|
||||
}
|
||||
|
||||
if (audioSource.isPlaying)
|
||||
{
|
||||
audioSource.Stop();
|
||||
}
|
||||
|
||||
PanelSystem.PopQueue<VideoPanel>();
|
||||
}
|
||||
|
||||
|
||||
public struct VideoInfo
|
||||
{
|
||||
public string filename;
|
||||
public Vector3Ctor position;
|
||||
public Vector3Ctor rotation;
|
||||
public Vector3Ctor scale;
|
||||
public Action callback;
|
||||
public bool isFixedSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb1f78aabccc4287a8df3dfdc5158c7b
|
||||
timeCreated: 1745723921
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.AudioCore;
|
||||
using Stary.Evo.UIFarme;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IVideoSystem : ISystem
|
||||
{
|
||||
void PlayVideo(VideoPanel.VideoInfo info);
|
||||
void StopVideo();
|
||||
}
|
||||
|
||||
public class VideoSystem : AbstractSystem, IVideoSystem
|
||||
{
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
protected override void OnInit()
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public async void PlayVideo(VideoPanel.VideoInfo info)
|
||||
{
|
||||
AudioCoreManager.SetMusicVolume(new AudioData()
|
||||
{
|
||||
fadeDuration = 2f,
|
||||
volume = 0f,
|
||||
});
|
||||
await this.GetSystem<IPanelSystem>().PushQueue<VideoPanel>(AppConfig.GetDefaultMainInstance().transform,"Main");
|
||||
this.GetSystem<IPanelSystem>().SendPanelEvent(ModeType.VideoStart,info);
|
||||
}
|
||||
|
||||
public void StopVideo()
|
||||
{
|
||||
this.GetSystem<IPanelSystem>().SendPanelEvent(ModeType.VideoEnd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8c49084e4704cb99a6caae60466e889
|
||||
timeCreated: 1745724754
|
||||
141
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate/Res.cs
Normal file
141
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate/Res.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
namespace R
|
||||
{
|
||||
public class Res
|
||||
{
|
||||
public class Main
|
||||
{
|
||||
public static class anim
|
||||
{
|
||||
public const string vid_maskani_anim = "Anim_vid_maskAni";
|
||||
public const string vid_maskcontroller_controller = "Anim_vid_maskController";
|
||||
public const string vid_maskdefault_anim = "Anim_vid_maskDefault";
|
||||
}
|
||||
public static class audios
|
||||
{
|
||||
public const string au_effect_element_pop_mp3 = "Audios_au_effect_element_pop";
|
||||
public const string au_x_07_ending1_wav = "Audios_au_X_07_ending1";
|
||||
public const string au_x_07_ending2_wav = "Audios_au_X_07_ending2";
|
||||
public const string au_x_07_ending3_wav = "Audios_au_X_07_ending3";
|
||||
public const string au_x_07_ending4_wav = "Audios_au_X_07_ending4";
|
||||
}
|
||||
public static class config
|
||||
{
|
||||
public const string domainconfig_asset = "Config_DomainConfig";
|
||||
}
|
||||
public static class dll
|
||||
{
|
||||
public static class android
|
||||
{
|
||||
public const string com_stary_evo_runtime_dll_bytes = "Android_com.stary.evo.runtime.dll";
|
||||
public const string dotween_dll_bytes = "Android_DOTween.dll";
|
||||
public const string informationsave_runtime_dll_bytes = "Android_InformationSave.RunTime.dll";
|
||||
public const string mscorlib_dll_bytes = "Android_mscorlib.dll";
|
||||
public const string system_core_dll_bytes = "Android_System.Core.dll";
|
||||
public const string uifarme_runtime_dll_bytes = "Android_UIFarme.RunTime.dll";
|
||||
public const string unitask_dll_bytes = "Android_UniTask.dll";
|
||||
public const string unityengine_coremodule_dll_bytes = "Android_UnityEngine.CoreModule.dll";
|
||||
public const string yooasset_dll_bytes = "Android_YooAsset.dll";
|
||||
}
|
||||
}
|
||||
public static class prefabs
|
||||
{
|
||||
public const string guideball_point_prefab = "Prefabs_GuideBall_Point";
|
||||
public const string guideball_zone_prefab = "Prefabs_GuideBall_Zone";
|
||||
public const string kkcontroller_prefab = "Prefabs_KKController";
|
||||
public const string main_prefab = "Prefabs_Main";
|
||||
public const string progressbarpanel_prefab = "Prefabs_ProgressBarPanel";
|
||||
public const string videopanel_prefab = "Prefabs_VideoPanel";
|
||||
public const string watermark_prefab = "Prefabs_Watermark";
|
||||
}
|
||||
public static class scenes
|
||||
{
|
||||
}
|
||||
public static class spriteatlas
|
||||
{
|
||||
public const string video_spriteatlas = "";
|
||||
public const string vid_video_mask_spriteatlas = "";
|
||||
}
|
||||
public static class sprites
|
||||
{
|
||||
public const string ui_video_kepu_png = "Sprites_ui_video_kepu";
|
||||
public const string ui_x_07_ending1_png = "Sprites_ui_X_07_ending1";
|
||||
public const string ui_x_07_ending2_png = "Sprites_ui_X_07_ending2";
|
||||
public const string ui_x_07_ending3_png = "Sprites_ui_X_07_ending3";
|
||||
public const string ui_zone1_shengmingjiankangqu_png = "Sprites_ui_zone1_shengmingjiankangqu";
|
||||
public const string ui_zone2_jiankangyingxiangyinsuqu_png = "Sprites_ui_zone2_jiankangyingxiangyinsuqu";
|
||||
public const string ui_zone3_shengmingfanghuqu_png = "Sprites_ui_zone3_shengmingfanghuqu";
|
||||
public const string ui_zone4_jiankangsuyangqu_png = "Sprites_ui_zone4_jiankangsuyangqu";
|
||||
public const string ui_zone5_jiankangshenghuofangshiqu_png = "Sprites_ui_zone5_jiankangshenghuofangshiqu";
|
||||
public static class video
|
||||
{
|
||||
}
|
||||
public static class vid_video_mask
|
||||
{
|
||||
public const string vid_mask_001_png = "vid_video_mask_vid_mask_001";
|
||||
public const string vid_mask_002_png = "vid_video_mask_vid_mask_002";
|
||||
public const string vid_mask_003_png = "vid_video_mask_vid_mask_003";
|
||||
public const string vid_mask_004_png = "vid_video_mask_vid_mask_004";
|
||||
public const string vid_mask_005_png = "vid_video_mask_vid_mask_005";
|
||||
public const string vid_mask_006_png = "vid_video_mask_vid_mask_006";
|
||||
public const string vid_mask_007_png = "vid_video_mask_vid_mask_007";
|
||||
public const string vid_mask_008_png = "vid_video_mask_vid_mask_008";
|
||||
public const string vid_mask_009_png = "vid_video_mask_vid_mask_009";
|
||||
public const string vid_mask_010_png = "vid_video_mask_vid_mask_010";
|
||||
public const string vid_mask_011_png = "vid_video_mask_vid_mask_011";
|
||||
public const string vid_mask_012_png = "vid_video_mask_vid_mask_012";
|
||||
public const string vid_mask_013_png = "vid_video_mask_vid_mask_013";
|
||||
public const string vid_mask_014_png = "vid_video_mask_vid_mask_014";
|
||||
public const string vid_mask_015_png = "vid_video_mask_vid_mask_015";
|
||||
public const string vid_mask_016_png = "vid_video_mask_vid_mask_016";
|
||||
public const string vid_mask_017_png = "vid_video_mask_vid_mask_017";
|
||||
public const string vid_mask_018_png = "vid_video_mask_vid_mask_018";
|
||||
public const string vid_mask_019_png = "vid_video_mask_vid_mask_019";
|
||||
public const string vid_mask_020_png = "vid_video_mask_vid_mask_020";
|
||||
public const string vid_mask_021_png = "vid_video_mask_vid_mask_021";
|
||||
public const string vid_mask_022_png = "vid_video_mask_vid_mask_022";
|
||||
public const string vid_mask_023_png = "vid_video_mask_vid_mask_023";
|
||||
public const string vid_mask_024_png = "vid_video_mask_vid_mask_024";
|
||||
public const string vid_mask_025_png = "vid_video_mask_vid_mask_025";
|
||||
public const string vid_mask_026_png = "vid_video_mask_vid_mask_026";
|
||||
public const string vid_mask_027_png = "vid_video_mask_vid_mask_027";
|
||||
public const string vid_mask_028_png = "vid_video_mask_vid_mask_028";
|
||||
public const string vid_mask_029_png = "vid_video_mask_vid_mask_029";
|
||||
public const string vid_mask_030_png = "vid_video_mask_vid_mask_030";
|
||||
public const string vid_mask_031_png = "vid_video_mask_vid_mask_031";
|
||||
public const string vid_mask_032_png = "vid_video_mask_vid_mask_032";
|
||||
public const string vid_mask_033_png = "vid_video_mask_vid_mask_033";
|
||||
public const string vid_mask_034_png = "vid_video_mask_vid_mask_034";
|
||||
public const string vid_mask_035_png = "vid_video_mask_vid_mask_035";
|
||||
public const string vid_mask_036_png = "vid_video_mask_vid_mask_036";
|
||||
public const string vid_mask_037_png = "vid_video_mask_vid_mask_037";
|
||||
public const string vid_mask_038_png = "vid_video_mask_vid_mask_038";
|
||||
public const string vid_mask_039_png = "vid_video_mask_vid_mask_039";
|
||||
public const string vid_mask_040_png = "vid_video_mask_vid_mask_040";
|
||||
public const string vid_mask_041_png = "vid_video_mask_vid_mask_041";
|
||||
public const string vid_mask_042_png = "vid_video_mask_vid_mask_042";
|
||||
public const string vid_mask_043_png = "vid_video_mask_vid_mask_043";
|
||||
public const string vid_mask_044_png = "vid_video_mask_vid_mask_044";
|
||||
public const string vid_mask_045_png = "vid_video_mask_vid_mask_045";
|
||||
public const string vid_mask_046_png = "vid_video_mask_vid_mask_046";
|
||||
public const string vid_mask_047_png = "vid_video_mask_vid_mask_047";
|
||||
public const string vid_mask_048_png = "vid_video_mask_vid_mask_048";
|
||||
public const string vid_mask_049_png = "vid_video_mask_vid_mask_049";
|
||||
public const string vid_mask_050_png = "vid_video_mask_vid_mask_050";
|
||||
public const string vid_mask_051_png = "vid_video_mask_vid_mask_051";
|
||||
public const string vid_mask_052_png = "vid_video_mask_vid_mask_052";
|
||||
public const string vid_mask_053_png = "vid_video_mask_vid_mask_053";
|
||||
public const string vid_mask_054_png = "vid_video_mask_vid_mask_054";
|
||||
public const string vid_mask_055_png = "vid_video_mask_vid_mask_055";
|
||||
public const string vid_mask_056_png = "vid_video_mask_vid_mask_056";
|
||||
public const string vid_mask_057_png = "vid_video_mask_vid_mask_057";
|
||||
public const string vid_mask_058_png = "vid_video_mask_vid_mask_058";
|
||||
public const string vid_mask_059_png = "vid_video_mask_vid_mask_059";
|
||||
public const string vid_mask_060_png = "vid_video_mask_vid_mask_060";
|
||||
}
|
||||
}
|
||||
public static class video
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate/Res.cs.meta
Normal file
11
Assets/Main/HotfixUpdateScript/Runtime/HotUpdate/Res.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c2846e6784c72e4186e83c127058adb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dca552217fdddaa4098bbd152e410548
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public static class BezierCurve
|
||||
{
|
||||
// 新增路径生成方法
|
||||
public static Vector3[] GenerateBezierPath(Vector3[] controlPoints, int segments = 20)
|
||||
{
|
||||
Vector3[] path = new Vector3[segments + 1];
|
||||
|
||||
for (int i = 0; i <= segments; i++)
|
||||
{
|
||||
float t = i / (float)segments;
|
||||
switch (controlPoints.Length)
|
||||
{
|
||||
case 2:
|
||||
path[i] = LineBezier(controlPoints[0], controlPoints[1], t);
|
||||
break;
|
||||
case 3:
|
||||
path[i] = QuadraticBezier(controlPoints, t);
|
||||
break;
|
||||
case 4:
|
||||
path[i] = CubicBezier(controlPoints, t);
|
||||
break;
|
||||
default: // 处理n阶情况
|
||||
path[i] = BezierInterpolation(controlPoints, t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// 一阶贝塞尔(线性插值)
|
||||
public static Vector3 LineBezier(Vector3 p0, Vector3 p1, float t)
|
||||
{
|
||||
return Vector3.Lerp(p0, p1, t);
|
||||
}
|
||||
|
||||
// 二阶贝塞尔曲线
|
||||
public static Vector3 QuadraticBezier(Vector3[] points, float t)
|
||||
{
|
||||
if (points.Length != 3) Debug.LogError("需要3个控制点");
|
||||
|
||||
float u = 1 - t;
|
||||
return
|
||||
u * u * points[0] +
|
||||
2 * u * t * points[1] +
|
||||
t * t * points[2];
|
||||
}
|
||||
|
||||
// 三阶贝塞尔曲线
|
||||
public static Vector3 CubicBezier(Vector3[] points, float t)
|
||||
{
|
||||
if (points.Length != 4) Debug.LogError("需要4个控制点");
|
||||
|
||||
float u = 1 - t;
|
||||
return
|
||||
u * u * u * points[0] +
|
||||
3 * u * u * t * points[1] +
|
||||
3 * u * t * t * points[2] +
|
||||
t * t * t * points[3];
|
||||
}
|
||||
|
||||
// n阶贝塞尔曲线(通用实现)
|
||||
public static Vector3 BezierInterpolation(Vector3[] points, float t)
|
||||
{
|
||||
if (points.Length < 2)
|
||||
{
|
||||
Debug.LogError("至少需要2个控制点");
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
Vector3[] temp = new Vector3[points.Length];
|
||||
points.CopyTo(temp, 0);
|
||||
|
||||
// 德卡斯特里奥算法递推
|
||||
for (int k = 1; k < points.Length; k++)
|
||||
{
|
||||
for (int i = 0; i < points.Length - k; i++)
|
||||
{
|
||||
temp[i] = Vector3.Lerp(temp[i], temp[i + 1], t);
|
||||
}
|
||||
}
|
||||
return temp[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0f6bf90b6c476e4eb75f7a6dae5c45d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public static class VectorExtension {
|
||||
|
||||
|
||||
public static Vector3Ctor GetVector3Ctor(this Vector3 vector)
|
||||
{
|
||||
return new Vector3Ctor(vector);
|
||||
}
|
||||
|
||||
public static Vector3 SetVector3Ctor(this Vector3Ctor vector3Ctor)
|
||||
{
|
||||
return new Vector3(vector3Ctor.x, vector3Ctor.y, vector3Ctor.z);
|
||||
}
|
||||
|
||||
public static Vector2Ctor GetVector2Ctor( this Vector2 vector)
|
||||
{
|
||||
return new Vector2Ctor(vector);
|
||||
}
|
||||
|
||||
public static Vector2 SetVector2Data(this Vector2Ctor vector3Ctor)
|
||||
{
|
||||
return new Vector2(vector3Ctor.x, vector3Ctor.y);
|
||||
}
|
||||
|
||||
}
|
||||
[Serializable]
|
||||
public class Vector3Ctor
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
|
||||
public Vector3Ctor()
|
||||
{
|
||||
|
||||
}
|
||||
public Vector3Ctor(Vector3 vector)
|
||||
{
|
||||
x = vector.x;
|
||||
y = vector.y;
|
||||
z = vector.z;
|
||||
}
|
||||
public Vector3Ctor(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
[Serializable]
|
||||
public class Vector2Ctor
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
|
||||
public Vector2Ctor(Vector2 vector)
|
||||
{
|
||||
x = vector.x;
|
||||
y = vector.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1077acf7c628ea24586d2c243ab718b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using Rokid.UXR.Module;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Android;
|
||||
using ModuleManager = Rokid.UXR.Module.ModuleManager;
|
||||
|
||||
public class VoiceSwitchingScene : MonoBehaviour
|
||||
{
|
||||
private String[] _sceneNames = new string[]
|
||||
{
|
||||
"打开第一个场景",
|
||||
"打开第二个场景",
|
||||
"打开第三个场景",
|
||||
"打开第四个场景",
|
||||
"打开第五个场景",
|
||||
"打开第六个场景",
|
||||
"打开第七个场景",
|
||||
"打开第八个场景",
|
||||
"打开第九个场景",
|
||||
"打开第十个场景",
|
||||
"打开第十一个场景",
|
||||
"打开第十二个场景",
|
||||
"打开第十三个场景",
|
||||
"打开第十四个场景",
|
||||
"打开第十五个场景",
|
||||
"打开第十六个场景",
|
||||
"打开第十七个场景",
|
||||
"打开第十八个场景",
|
||||
"打开第十九个场景",
|
||||
"打开第二十个场景",
|
||||
|
||||
};
|
||||
|
||||
private String[] _sceneNamesSpell = new string[]
|
||||
{
|
||||
"da kai di yi ge chang jing",
|
||||
"da kai di er ge chang jing",
|
||||
"da kai di san ge chang jing",
|
||||
"da kai di si ge chang jing",
|
||||
"da kai di wu ge chang jing",
|
||||
"da kai di liu ge chang jing",
|
||||
"da kai di qi ge chang jing",
|
||||
"da kai di ba ge chang jing",
|
||||
"da kai di jiu ge chang jing",
|
||||
"da kai di shi ge chang jing",
|
||||
"da kai di shi yi ge chang jing",
|
||||
"da kai di shi er ge chang jing",
|
||||
"da kai di shi san ge chang jing",
|
||||
"da kai di shi si ge chang jing",
|
||||
"da kai di shi wu ge chang jing",
|
||||
"da kai di shi liu ge chang jing",
|
||||
"da kai di shi qi ge chang jing",
|
||||
"da kai di shi ba ge chang jing",
|
||||
"da kai di shi jiu ge chang jing",
|
||||
"da kai di shi er shi ge chang jing",
|
||||
};
|
||||
private HybridClREntrance hybridClREntrance;
|
||||
|
||||
private MainDomainAll mainDomainAll;
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
hybridClREntrance = GetComponent<HybridClREntrance>();
|
||||
if (!Permission.HasUserAuthorizedPermission("android.permission.RECORD_AUDIO"))
|
||||
{
|
||||
Permission.RequestUserPermission("android.permission.RECORD_AUDIO");
|
||||
}
|
||||
ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
|
||||
OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(LANGUAGE.CHINESE);
|
||||
|
||||
mainDomainAll = Resources.Load<MainDomainAll>("MainDomainAll");
|
||||
for (int i = 0; i < mainDomainAll.domainAll.Length; i++)
|
||||
{
|
||||
if(mainDomainAll.domainAll[i].isVideo)
|
||||
OfflineVoiceModule.Instance.AddInstruct(LANGUAGE.CHINESE, _sceneNames[i], _sceneNamesSpell[i], this.gameObject.name, "OnReceive");
|
||||
}
|
||||
OfflineVoiceModule.Instance.Commit();
|
||||
}
|
||||
|
||||
|
||||
void OnReceive(string msg)
|
||||
{
|
||||
int index = Array.IndexOf(_sceneNames, msg);
|
||||
if (index != -1)
|
||||
{
|
||||
hybridClREntrance.OpenDomain(mainDomainAll.domainAll[index].domainName , OpenDomainType.VIOICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97f6e15196934254c94bd3de1f798b77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class WebRequestTools
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48091c7012fc4cd88a54c096febdcdd0
|
||||
timeCreated: 1748318581
|
||||
@@ -0,0 +1,35 @@
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class YooAssetFileSystem
|
||||
{
|
||||
public static InitializeParameters OfflineInitializeParameter()
|
||||
{
|
||||
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
|
||||
var initParameters = new OfflinePlayModeParameters();
|
||||
initParameters.BuildinFileSystemParameters = buildinFileSystemParams;
|
||||
return initParameters;
|
||||
}
|
||||
public static InitializeParameters hostInitializeParameter()
|
||||
{
|
||||
var initParameters = new OfflinePlayModeParameters();
|
||||
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(null,
|
||||
$"{Application.persistentDataPath}/DownloadedContent/{AppConfig.PackageDomainName}");
|
||||
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.APPEND_FILE_EXTENSION, true);
|
||||
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, true);
|
||||
initParameters.BuildinFileSystemParameters = buildinFileSystemParams;
|
||||
return initParameters;
|
||||
}
|
||||
public static InitializeParameters EditorSimulateInitializeParameter()
|
||||
{
|
||||
var buildResult = EditorSimulateModeHelper.SimulateBuild(AppConfig.PackageDomainName);
|
||||
var packageRoot = buildResult.PackageRootDirectory;
|
||||
var editorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);
|
||||
var initParams = new EditorSimulateModeParameters();
|
||||
initParams.EditorFileSystemParameters = editorFileSystemParameters;
|
||||
return initParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6f288f9aa65416baf25a50a4056fcae
|
||||
timeCreated: 1749695458
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class ZipTool
|
||||
{
|
||||
// 新增:带进度回调的下载解压方法
|
||||
public static async UniTask DownloadAndUnzipAsync(string fildId, string extractPath,
|
||||
Action<float> downloadProgress = null, Action<float> unzipProgress = null)
|
||||
{
|
||||
string tempPath = Path.Combine(Application.persistentDataPath, "temp.zip");
|
||||
|
||||
try
|
||||
{
|
||||
string url = $"{AppConfig.IpConfig}/FileLoad/Download/{fildId}";
|
||||
await WebRequestSystem.GetFile(url, tempPath,downloadProgress);
|
||||
|
||||
// 解压下载的文件
|
||||
await UnzipAsync(tempPath, extractPath, unzipProgress);
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理临时文件
|
||||
if (File.Exists(tempPath))
|
||||
{
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改:添加进度回调参数
|
||||
public static async UniTask UnzipAsync(string zipPath, string extractPath,
|
||||
Action<float> progressCallback = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
|
||||
{
|
||||
float totalEntries = archive.Entries.Count;
|
||||
float processed = 0;
|
||||
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
string filePath = Path.Combine(extractPath, entry.FullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.Name))
|
||||
{
|
||||
using (Stream inputStream = entry.Open())
|
||||
using (FileStream outputStream = File.Create(filePath))
|
||||
{
|
||||
await inputStream.CopyToAsync(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
processed++;
|
||||
float progress = processed / totalEntries;
|
||||
progressCallback?.Invoke(progress);
|
||||
}
|
||||
}
|
||||
Debug.Log($"解压完成: {extractPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"解压失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义网络请求器需要登录
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private static UnityWebRequest WebRequester(string url)
|
||||
{
|
||||
var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET);
|
||||
var authorization = GetAuthorization(AppConfig.UserName, AppConfig.PassWord);
|
||||
request.SetRequestHeader("AUTHORIZATION", authorization);
|
||||
return request;
|
||||
}
|
||||
|
||||
private static string GetAuthorization(string userName, string password)
|
||||
{
|
||||
string auth = userName + ":" + password;
|
||||
var bytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth);
|
||||
return "Basic " + System.Convert.ToBase64String(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f5ebd73225744188b38cf307e565ff2
|
||||
timeCreated: 1747988495
|
||||
3
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket.meta
Normal file
3
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90f5ed74142c4ae695e3e0c175e59b06
|
||||
timeCreated: 1750651156
|
||||
14
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket/.gitignore
vendored
Normal file
14
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
*.sln
|
||||
*.csproj
|
||||
SocketClient/.vscode/
|
||||
SocketClient/Packages/
|
||||
SocketClient/Logs/
|
||||
SocketClient/Library/
|
||||
SocketServer/Packages/
|
||||
SocketServer/obj/
|
||||
SocketServer/Logs/
|
||||
SocketServer/Library/
|
||||
SocketServer/.vscode/
|
||||
SocketClient/obj/
|
||||
SocketClient/Temp/
|
||||
SocketServer/Temp/
|
||||
71
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket/README.md
Normal file
71
Assets/Main/HotfixUpdateScript/Runtime/UnitySocket/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# UnitySocket
|
||||
该项目是基于Unity平台 纯C#编写的Socket网络模块,包括客户端与服务端的实现
|
||||
|
||||
## 环境依赖
|
||||
|
||||
- 项目运行平台: Unity2022.3.22f1
|
||||
|
||||
该项目仅包含网络底层的几个C#脚本与一个测试脚本,可以随意选择Unity版本测试。
|
||||
|
||||
## 特点
|
||||
1. 回调事件完善,包含服务器主动踢出、客户端主动断开、心跳包等底层功能逻辑。
|
||||
2. 对象形式,非单例模式,一个项目可new多个连接
|
||||
3. 代码量少,纯C#编写,未使用插件、DLL及工具类,便于二次封装,利于移植
|
||||
## 报文组成
|
||||
- 通用
|
||||
|
||||
报文总长度(4byte) + 报文类型(2byte) + 数据(N byte)
|
||||
|
||||
- 心跳包
|
||||
|
||||
报文总长度(4byte) + 报文类型-心跳包(2byte)
|
||||
|
||||
- 客户端断开
|
||||
|
||||
报文总长度(4byte) + 报文类型-客户端断开(2byte)
|
||||
|
||||
- 服务端踢出
|
||||
|
||||
报文总长度(4byte) + 报文类型-服务端踢出(2byte)
|
||||
|
||||
## 工作流
|
||||
- 建立连接
|
||||
1. 服务端通过 new SocketServer(string ip,int port) 建立连接
|
||||
2. 客户端通过 new SocketClient(string ip,int port) 建立实例,通过Connect函数尝试连接。
|
||||
3. 客户端连接成功后,开始定时发送心跳包报文;服务端接收后记录时间,定时检测判断是否超时。
|
||||
4. 如存在以下情况,则断开连接:
|
||||
- 服务端主动踢出连接,即时响应,回调DisConnect
|
||||
- 客户端主动断开连接,即时响应,回调DisConnect
|
||||
- 客户端关闭连接,即时响应,回调DisConnect
|
||||
- 服务端检测到客户端太久没有发送心跳包,此处需要响应时间,根据心跳包超时间隔、心跳包发送间隔及服务端心跳包超时检测间隔来决定,发现超时则回调DisConnect
|
||||
- 通信逻辑过程中报错,即时响应,自动重连,默认重连上限为三次,达到重连上限并未成功,则回调DisConnect
|
||||
- 接收数据
|
||||
1. 接收线程收到数据,存放入DataBuff数据缓存区。
|
||||
2. DataBuff中不断通过SocketDataPack尝试解包,解包成功后将报文发送到接收线程。
|
||||
3. 接收线程收到报文后,触发OnReceive回调
|
||||
4. 业务层通过OnReceive回调接收到报文后,反序列化报文体得到数据
|
||||
- 发送数据
|
||||
1. 业务层序列化数据得到报文体字节集,通过Send函数发送数据。
|
||||
2. Send函数中,通过SocketDataPack装包后,以字节集的形式发送出去。
|
||||
|
||||
## 代码说明
|
||||
- DataBuffer:Socket传输数据缓存区,此处主要处理Socket传输时粘包、分包的情况
|
||||
- SocketEvent:Socket报文类型枚举,此处只枚举了网络底层需要发送的报文类型,业务逻辑层所使用的报文类型,建议封装至报文体序列化类中
|
||||
- SocketDataPack:Socket报文类,处理具体的拆包、装包逻辑
|
||||
- SocketServer:Socket服务端
|
||||
- SocketClient:Socket客户端
|
||||
|
||||
## 回调事件
|
||||
- SocketServer 服务端
|
||||
- public event Action<Socket> OnConnect; //客户端建立连接回调
|
||||
- public event Action<Socket> OnDisconnect; // 客户端断开连接回调
|
||||
- public event Action<Socket, SocketDataPack> OnReceive; // 接收报文回调
|
||||
- SocketClient 客户端
|
||||
- public event Action OnConnectSuccess; // 连接成功回调
|
||||
- public event Action OnConnectError; // 连接失败回调
|
||||
- public event Action OnDisconnect; // 断开回调
|
||||
- public event Action<SocketDataPack> OnReceive; // 接收报文回调
|
||||
- public event Action<SocketException> OnError; // 异常捕获回调
|
||||
- public event Action<int> OnReConnectSuccess; // 重连成功回调
|
||||
- public event Action<int> OnReConnectError; // 单次重连失败回调
|
||||
- public event Action<int> OnReconnecting; // 单次重连中回调
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f0c65b3aa2616440978bf5d9f804e7c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b43e2f4bdef74642999b4d5530a9b3d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e04a0e82198abe4fb1796dc44bc9e9d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3cdc884284e12f48a350017446975ca
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
/// <summary>
|
||||
/// Socket传输过程的缓冲区,尝试拆包获得数据
|
||||
/// </summary>
|
||||
public class DataBuffer
|
||||
{
|
||||
// 缓存区长度
|
||||
private const int MIN_BUFF_LEN = 1024;
|
||||
|
||||
private byte[] _buff;
|
||||
private int _buffLength = 0;
|
||||
|
||||
public DataBuffer(int minBuffLen = MIN_BUFF_LEN)
|
||||
{
|
||||
if (minBuffLen <= 0)
|
||||
{
|
||||
minBuffLen = MIN_BUFF_LEN;
|
||||
}
|
||||
_buff = new byte[minBuffLen];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加缓存数据
|
||||
/// </summary>
|
||||
public void AddBuffer(byte[] data, int len)
|
||||
{
|
||||
byte[] buff = new byte[len];
|
||||
Array.Copy(data, buff, len);
|
||||
if (len > _buff.Length - _buffLength) //超过当前缓存
|
||||
{
|
||||
byte[] temp = new byte[_buffLength + len];
|
||||
Array.Copy(_buff, 0, temp, 0, _buffLength);
|
||||
Array.Copy(buff, 0, temp, _buffLength, len);
|
||||
_buff = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(data, 0, _buff, _buffLength, len);
|
||||
}
|
||||
_buffLength += len;//修改当前数据标记
|
||||
}
|
||||
|
||||
public bool TryUnpack(out SocketDataPack dataPack)
|
||||
{
|
||||
dataPack = SocketDataPack.Unpack(_buff);
|
||||
if (dataPack == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 清理旧缓存
|
||||
_buffLength -= dataPack.BuffLength;
|
||||
byte[] temp = new byte[_buffLength < MIN_BUFF_LEN ? MIN_BUFF_LEN : _buffLength];
|
||||
Array.Copy(_buff, dataPack.BuffLength, temp, 0, _buffLength);
|
||||
_buff = temp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e879d0a89aac39f4a8320fd3857d0012
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,332 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Socket客户端
|
||||
/// </summary>
|
||||
public class SocketClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 主线程
|
||||
/// </summary>
|
||||
private readonly SynchronizationContext _mainThread;
|
||||
|
||||
|
||||
private readonly string _ip;
|
||||
private readonly int _port;
|
||||
|
||||
private const int TIMEOUT_CONNECT = 3000; // 连接超时时间 毫秒
|
||||
private const int TIMEOUT_SEND = 3000; // 发送超时时间 毫秒
|
||||
private const int TIMEOUT_RECEIVE = 3000; //接收超时时间 毫秒
|
||||
|
||||
private const int HEAD_OFFSET = 2000; //心跳包发送间隔 毫秒
|
||||
private const int RECONN_MAX_SUM = 3; //最大重连次数
|
||||
|
||||
private Socket _client;
|
||||
private Thread _receiveThread;
|
||||
private System.Timers.Timer _connTimeoutTimer;
|
||||
private System.Timers.Timer _headTimer;
|
||||
private readonly DataBuffer _dataBuffer = new DataBuffer();
|
||||
|
||||
public event Action OnConnectSuccess; // 连接成功回调
|
||||
public event Action OnConnectError; // 连接失败回调
|
||||
public event Action OnDisconnect; // 断开回调
|
||||
public event Action<SocketDataPack> OnReceive; // 接收报文回调
|
||||
public event Action<SocketDataPack> OnSend; // 发送报文回调
|
||||
public event Action<SocketException> OnError; // 异常捕获回调
|
||||
public event Action<int> OnReConnectSuccess; // 重连成功回调
|
||||
public event Action<int> OnReConnectError; // 单次重连失败回调
|
||||
public event Action<int> OnReconnecting; // 单次重连中回调
|
||||
|
||||
private bool _isConnect = false;
|
||||
private bool _isReconnect = false;
|
||||
|
||||
|
||||
public SocketClient(string ip, int port)
|
||||
{
|
||||
_mainThread = SynchronizationContext.Current;
|
||||
|
||||
_ip = ip;
|
||||
_port = port;
|
||||
}
|
||||
public void Connect(Action success = null, Action error = null)
|
||||
{
|
||||
OnDisconnect += () => { UnityEngine.Debug.Log("UnityEvo:断开连接"); };
|
||||
|
||||
OnReceive += (dataPack) => { UnityEngine.Debug.LogFormat("UnityEvo:接收数据>>>{0}", (SocketEvent)dataPack.Type); };
|
||||
OnSend += (dataPack) => { UnityEngine.Debug.LogFormat("UnityEvo:发送数据>>>{0}", (SocketEvent)dataPack.Type); };
|
||||
OnError += (ex) => { UnityEngine.Debug.LogFormat("UnityEvo:出现异常>>>{0}", ex); };
|
||||
OnReConnectSuccess += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:第{0}次重连成功", num); };
|
||||
OnReConnectError += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:第{0}次重连失败", num); };
|
||||
OnReconnecting += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:正在进行第{0}次重连", num); };
|
||||
|
||||
void OnTrigger(bool flag)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
PostMainThreadAction(success);
|
||||
PostMainThreadAction(OnConnectSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
PostMainThreadAction(error);
|
||||
PostMainThreadAction(OnConnectError);
|
||||
}
|
||||
|
||||
if (_connTimeoutTimer != null)
|
||||
{
|
||||
_connTimeoutTimer.Stop();
|
||||
_connTimeoutTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建套接字
|
||||
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, TIMEOUT_SEND);
|
||||
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, TIMEOUT_RECEIVE);
|
||||
IPAddress ipAddress = IPAddress.Parse(_ip);//解析IP地址
|
||||
IPEndPoint ipEndpoint = new IPEndPoint(ipAddress, _port);
|
||||
IAsyncResult result = _client.BeginConnect(ipEndpoint, new AsyncCallback((iar) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket client = (Socket)iar.AsyncState;
|
||||
client.EndConnect(iar);
|
||||
|
||||
_isConnect = true;
|
||||
// 开始发送心跳包
|
||||
_headTimer = new System.Timers.Timer(HEAD_OFFSET);
|
||||
_headTimer.AutoReset = true;
|
||||
_headTimer.Elapsed += delegate (object sender, ElapsedEventArgs args)
|
||||
{
|
||||
Send((UInt16)SocketEvent.ClientHead);
|
||||
};
|
||||
_headTimer.Start();
|
||||
|
||||
// 开始接收数据
|
||||
_receiveThread = new Thread(new ThreadStart(ReceiveEvent));
|
||||
_receiveThread.IsBackground = true;
|
||||
_receiveThread.Start();
|
||||
|
||||
OnTrigger(true);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
OnTrigger(false);
|
||||
}
|
||||
}), _client);//异步连接
|
||||
|
||||
_connTimeoutTimer = new System.Timers.Timer(TIMEOUT_CONNECT);
|
||||
_connTimeoutTimer.AutoReset = false;
|
||||
_connTimeoutTimer.Elapsed += delegate (object sender, ElapsedEventArgs args)
|
||||
{
|
||||
OnTrigger(false);
|
||||
};
|
||||
_connTimeoutTimer.Start();
|
||||
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
OnTrigger(false);
|
||||
// throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断线重连
|
||||
/// </summary>
|
||||
/// <param name="num"></param>
|
||||
/// <param name="index"></param>
|
||||
public void ReConnect(int num = RECONN_MAX_SUM, int index = 0)
|
||||
{
|
||||
_isReconnect = true;
|
||||
|
||||
num--;
|
||||
index++;
|
||||
if (num < 0)
|
||||
{
|
||||
onDisconnect();
|
||||
_isReconnect = false;
|
||||
return;
|
||||
}
|
||||
PostMainThreadAction<int>(OnReconnecting, index);
|
||||
Connect(() =>
|
||||
{
|
||||
PostMainThreadAction<int>(OnReConnectSuccess, index);
|
||||
_isReconnect = false;
|
||||
}, () =>
|
||||
{
|
||||
PostMainThreadAction<int>(OnReConnectError, index);
|
||||
ReConnect(num, index);
|
||||
});
|
||||
|
||||
}
|
||||
public void Send(UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
|
||||
{
|
||||
buff = buff ?? new byte[] { };
|
||||
var dataPack = new SocketDataPack(e, buff);
|
||||
var data = dataPack.Buff;
|
||||
try
|
||||
{
|
||||
_client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback((asyncSend) =>
|
||||
{
|
||||
Socket c = (Socket)asyncSend.AsyncState;
|
||||
c.EndSend(asyncSend);
|
||||
PostMainThreadAction<SocketDataPack>(onTrigger, dataPack);
|
||||
PostMainThreadAction<SocketDataPack>(OnSend, dataPack);
|
||||
}), _client);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
onError(ex);
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 线程内接收数据的函数
|
||||
/// </summary>
|
||||
private void ReceiveEvent()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_isConnect) break;
|
||||
if (_client.Available <= 0) continue;
|
||||
byte[] rbytes = new byte[8 * 1024];
|
||||
int len = _client.Receive(rbytes);
|
||||
if (len > 0)
|
||||
{
|
||||
_dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中
|
||||
if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包
|
||||
{
|
||||
if (dataPack.Type == (UInt16)SocketEvent.ServerKickout)
|
||||
{
|
||||
// 服务端踢出
|
||||
onDisconnect();
|
||||
}
|
||||
else if (dataPack.Type == (UInt16)SocketEvent.ServerMessage)
|
||||
{
|
||||
// 收到消息
|
||||
PostMainThreadAction<SocketDataPack>(OnReceive, dataPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
onError(ex);
|
||||
// throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 业务逻辑 - 客户端主动断开
|
||||
/// </summary>
|
||||
public void DisConnect()
|
||||
{
|
||||
Send((UInt16)SocketEvent.ClientDisconn);
|
||||
onDisconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存数据清理
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (!_isConnect) return;
|
||||
_isConnect = false;
|
||||
if (_headTimer != null)
|
||||
{
|
||||
_headTimer.Stop();
|
||||
_headTimer = null;
|
||||
}
|
||||
// if (_receiveThread != null)
|
||||
// {
|
||||
// _receiveThread.Abort();
|
||||
// _receiveThread = null;
|
||||
// }
|
||||
if (_connTimeoutTimer != null)
|
||||
{
|
||||
_connTimeoutTimer.Stop();
|
||||
_connTimeoutTimer = null;
|
||||
}
|
||||
if (_client != null)
|
||||
{
|
||||
_client.Close();
|
||||
_client = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 错误回调
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
private void onError(SocketException ex)
|
||||
{
|
||||
Close();
|
||||
|
||||
PostMainThreadAction<SocketException>(OnError, ex);
|
||||
|
||||
if (!_isReconnect)
|
||||
{
|
||||
ReConnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 断开回调
|
||||
/// </summary>
|
||||
private void onDisconnect()
|
||||
{
|
||||
|
||||
Close();
|
||||
|
||||
PostMainThreadAction(OnDisconnect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知主线程回调
|
||||
/// </summary>
|
||||
private void PostMainThreadAction(Action action)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action e = (Action)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
if (e != null) e();
|
||||
}), new { action = action });
|
||||
}
|
||||
private void PostMainThreadAction<T>(Action<T> action, T arg1)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action<T> e = (Action<T>)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
T t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o);
|
||||
if (e != null) e(t1);
|
||||
}), new { action = action, arg1 = arg1 });
|
||||
}
|
||||
public void PostMainThreadAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action<T1, T2> e = (Action<T1, T2>)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
T1 t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o);
|
||||
T2 t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o);
|
||||
if (e != null) e(t1, t2);
|
||||
}), new { action = action, arg1 = arg1, arg2 = arg2 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e91098b3165d6b478e1a1ab72aeb94c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
/// <summary>
|
||||
/// Socket通信过程中的数据包 处理具体拆包装包逻辑
|
||||
/// </summary>
|
||||
public class SocketDataPack
|
||||
{
|
||||
// 消息:数据总长度(4byte) + 数据类型(2byte) + 数据(N byte)
|
||||
public const int HEAD_DATA_LEN = 4;
|
||||
public const int HEAD_TYPE_LEN = 2;
|
||||
|
||||
public static int HeadLen
|
||||
{
|
||||
get { return HEAD_DATA_LEN + HEAD_TYPE_LEN; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据包类型
|
||||
/// </summary>
|
||||
public UInt16 Type;
|
||||
/// <summary>
|
||||
/// 数据包数据
|
||||
/// </summary>
|
||||
public byte[] Data;
|
||||
public byte[] Buff;
|
||||
|
||||
public int BuffLength
|
||||
{
|
||||
get { return Buff.Length; }
|
||||
}
|
||||
public int DataLength
|
||||
{
|
||||
get { return Data.Length; }
|
||||
}
|
||||
|
||||
public SocketDataPack()
|
||||
{
|
||||
|
||||
}
|
||||
public SocketDataPack(UInt16 type, byte[] data)
|
||||
{
|
||||
Type = type;
|
||||
Data = data;
|
||||
|
||||
Buff = GetBuff(Type, Data);
|
||||
}
|
||||
|
||||
public static byte[] GetBuff(UInt16 type, byte[] data)
|
||||
{
|
||||
byte[] buff = new byte[data.Length + HeadLen];
|
||||
byte[] temp;
|
||||
temp = BitConverter.GetBytes(buff.Length);
|
||||
Array.Copy(temp, 0, buff, 0, HEAD_DATA_LEN);
|
||||
temp = BitConverter.GetBytes(type);
|
||||
Array.Copy(temp, 0, buff, HEAD_DATA_LEN, HEAD_TYPE_LEN);
|
||||
|
||||
Array.Copy(data, 0, buff, HeadLen, data.Length);
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
public static SocketDataPack Unpack(byte[] buff)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (buff.Length < HeadLen)
|
||||
{
|
||||
// 头部没取完则返回
|
||||
return null;
|
||||
}
|
||||
byte[] temp;
|
||||
// 取数据长度
|
||||
temp = new byte[HEAD_DATA_LEN];
|
||||
Array.Copy(buff, 0, temp, 0, HEAD_DATA_LEN);
|
||||
int buffLength = BitConverter.ToInt32(temp, 0);
|
||||
if (buffLength <= 0) return null;
|
||||
if (buffLength > buff.Length)
|
||||
{
|
||||
// 数据没取完
|
||||
return null;
|
||||
}
|
||||
int dataLength = buffLength - HeadLen;
|
||||
// 取数据类型
|
||||
temp = new byte[HEAD_TYPE_LEN];
|
||||
Array.Copy(buff, HEAD_DATA_LEN, temp, 0, HEAD_TYPE_LEN);
|
||||
UInt16 dataType = BitConverter.ToUInt16(temp, 0);
|
||||
// 取数据
|
||||
byte[] data = new byte[dataLength];
|
||||
Array.Copy(buff, HeadLen, data, 0, dataLength);
|
||||
|
||||
var dataPack = new SocketDataPack(dataType, data);
|
||||
// UnityEngine.Debug.LogFormat("buffLen:{0} type:{1} dataLength:{2}", buffLength, dataType, data.Length);
|
||||
|
||||
return dataPack;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 存在不完整数据解包 则返回null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e0c4c6bf95d3324a975f6ce0cdee4c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
/// <summary>
|
||||
/// 网络事件协议ID枚举
|
||||
/// </summary>
|
||||
public enum SocketEvent
|
||||
{
|
||||
ClientHead = 0x0001, //心跳包
|
||||
ClientDisconn = 0x0002, //客户端主动断开
|
||||
ServerKickout = 0x0003, //服务端踢出
|
||||
|
||||
ServerMessage = 0x0004, //服务端发送消息
|
||||
ClientMessage = 0x0005, //客户端发送消息
|
||||
|
||||
ScTest = 0x1001, //测试用
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1c8d60cbbc2c7547827955a371fda00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,349 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using UnityEngine;
|
||||
|
||||
public class SocketInfo
|
||||
{
|
||||
public string IP;
|
||||
public Thread ReceiveThread;
|
||||
public long HeadTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Socket服务端
|
||||
/// </summary>
|
||||
public class SocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 主线程
|
||||
/// </summary>
|
||||
private readonly SynchronizationContext _mainThread;
|
||||
|
||||
|
||||
private const int HEAD_TIMEOUT = 5000; // 心跳超时 毫秒
|
||||
private const int HEAD_CHECKTIME = 5000; // 心跳包超时检测 毫秒
|
||||
|
||||
public readonly Dictionary<Socket, SocketInfo> ClientInfoDic = new Dictionary<Socket, SocketInfo>();
|
||||
|
||||
private readonly Socket _server;
|
||||
private System.Timers.Timer _headCheckTimer;
|
||||
private readonly DataBuffer _dataBuffer = new DataBuffer();
|
||||
|
||||
public event Action<Socket> OnConnect; //客户端建立连接回调
|
||||
public event Action<Socket> OnDisconnect; // 客户端断开连接回调
|
||||
public event Action<Socket, SocketDataPack> OnReceive; // 接收报文回调
|
||||
public event Action<Socket, SocketDataPack> OnSend; // 发送报文回调
|
||||
|
||||
// 目前捕获异常将触发OnDisconnect回调 暂不单独处理
|
||||
// public event Action<SocketException> OnError; // 异常捕获回调
|
||||
|
||||
private bool _isValid = true;
|
||||
|
||||
public SocketServer(string ip, int port)
|
||||
{
|
||||
OnConnect += (client) =>
|
||||
{
|
||||
UnityEngine.Debug.LogFormat("UnityEvo:连接成功 >> IP:{0}", client.LocalEndPoint.ToString());
|
||||
};
|
||||
OnDisconnect += (client) =>
|
||||
{
|
||||
if(ClientInfoDic.TryGetValue(client, out var value))
|
||||
Debug.LogFormat("UnityEvo:连接断开 >> IP:{0}", value.IP);
|
||||
};
|
||||
|
||||
_mainThread = SynchronizationContext.Current;
|
||||
|
||||
_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
IPAddress ipAddress = IPAddress.Parse(ip); //解析IP地址
|
||||
_server.Bind(new IPEndPoint(ipAddress, port)); //绑定IP地址:端口
|
||||
|
||||
_server.Listen(10); //设定最多10个排队连接请求
|
||||
|
||||
// 启动线程监听连接
|
||||
var connectThread = new Thread(ListenClientConnect);
|
||||
connectThread.Start();
|
||||
|
||||
// 心跳包定时检测
|
||||
_headCheckTimer = new System.Timers.Timer(HEAD_CHECKTIME);
|
||||
_headCheckTimer.AutoReset = true;
|
||||
_headCheckTimer.Elapsed += delegate(object sender, ElapsedEventArgs args) { CheckHeadTimeOut(); };
|
||||
_headCheckTimer.Start();
|
||||
Debug.Log($"SocketServer Start: {ip}:{port}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 监听客户端连接
|
||||
/// </summary>
|
||||
private void ListenClientConnect()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_isValid) break;
|
||||
Socket client = _server.Accept();
|
||||
IPEndPoint ipEndPoint = (IPEndPoint)client.RemoteEndPoint;
|
||||
Thread receiveThread = new Thread(ReceiveEvent);
|
||||
ClientInfoDic.Add(client,
|
||||
new SocketInfo() { IP = ipEndPoint.Address.ToString(), ReceiveThread = receiveThread, HeadTime = GetNowTime() });
|
||||
receiveThread.Start(client);
|
||||
|
||||
PostMainThreadAction<Socket>(OnConnect, client);
|
||||
}
|
||||
catch
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前时间戳
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private long GetNowTime()
|
||||
{
|
||||
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
return Convert.ToInt64(ts.TotalMilliseconds);
|
||||
}
|
||||
|
||||
public void Send(Socket client, UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
|
||||
{
|
||||
SendMessage(client, e, buff, onTrigger);
|
||||
}
|
||||
|
||||
|
||||
private void SendMessage(Socket client, UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
|
||||
{
|
||||
buff = buff ?? new byte[] { };
|
||||
var dataPack = new SocketDataPack(e, buff);
|
||||
var data = dataPack.Buff;
|
||||
try
|
||||
{
|
||||
client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback((asyncSend) =>
|
||||
{
|
||||
Socket c = (Socket)asyncSend.AsyncState;
|
||||
c.EndSend(asyncSend);
|
||||
PostMainThreadAction<SocketDataPack>(onTrigger, dataPack);
|
||||
PostMainThreadAction<Socket, SocketDataPack>(OnSend, client, dataPack);
|
||||
}), client);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
CloseClient(client);
|
||||
// onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线程内接收数据的函数
|
||||
/// </summary>
|
||||
private void ReceiveEvent(object client)
|
||||
{
|
||||
Socket tsocket = (Socket)client;
|
||||
while (true)
|
||||
{
|
||||
if (!_isValid) return;
|
||||
if (!ClientInfoDic.ContainsKey(tsocket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
byte[] rbytes = new byte[8 * 1024];
|
||||
int len = tsocket.Receive(rbytes);
|
||||
if (len > 0)
|
||||
{
|
||||
_dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中
|
||||
if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包
|
||||
{
|
||||
if (dataPack.Type == (UInt16)SocketEvent.ClientHead)
|
||||
{
|
||||
// 接收到心跳包
|
||||
ReceiveHead(tsocket);
|
||||
}
|
||||
else if (dataPack.Type == (UInt16)SocketEvent.ClientDisconn)
|
||||
{
|
||||
// 客户端断开连接
|
||||
CloseClient(tsocket);
|
||||
}
|
||||
else if (dataPack.Type == (UInt16)SocketEvent.ClientMessage)
|
||||
{
|
||||
// 收到消息
|
||||
PostMainThreadAction<Socket, SocketDataPack>(OnReceive, tsocket, dataPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tsocket.Poll(-1, SelectMode.SelectRead))
|
||||
{
|
||||
CloseClient(tsocket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
CloseClient(tsocket);
|
||||
// onError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 接收到心跳包
|
||||
/// </summary>
|
||||
private void ReceiveHead(Socket client)
|
||||
{
|
||||
SocketInfo info;
|
||||
if (ClientInfoDic.TryGetValue(client, out info))
|
||||
{
|
||||
long now = GetNowTime();
|
||||
long offset = now - info.HeadTime;
|
||||
UnityEngine.Debug.Log("更新心跳时间戳 >>>" + now + " 间隔>>>" + offset);
|
||||
if (offset > HEAD_TIMEOUT)
|
||||
{
|
||||
// 心跳包收到但超时逻辑
|
||||
}
|
||||
|
||||
info.HeadTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测心跳包超时
|
||||
/// </summary>
|
||||
private void CheckHeadTimeOut()
|
||||
{
|
||||
var tempList = new List<Socket>();
|
||||
foreach (var socket in ClientInfoDic.Keys)
|
||||
{
|
||||
tempList.Add(socket);
|
||||
}
|
||||
|
||||
foreach (var socket in tempList)
|
||||
{
|
||||
var info = ClientInfoDic[socket];
|
||||
long now = GetNowTime();
|
||||
long offset = now - info.HeadTime;
|
||||
if (offset > HEAD_TIMEOUT)
|
||||
{
|
||||
// 心跳包超时
|
||||
KickOut(socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void KickOut(Socket client)
|
||||
{
|
||||
// 踢出连接
|
||||
Send(client, (UInt16)SocketEvent.ServerKickout, null, (dataPack) => { CloseClient(client); });
|
||||
}
|
||||
|
||||
public void KickOutAll()
|
||||
{
|
||||
var tempList = new List<Socket>();
|
||||
foreach (var socket in ClientInfoDic.Keys)
|
||||
{
|
||||
tempList.Add(socket);
|
||||
}
|
||||
|
||||
foreach (var socket in tempList)
|
||||
{
|
||||
KickOut(socket);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理客户端连接
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
private void CloseClient(Socket client)
|
||||
{
|
||||
PostMainThreadAction<Socket>((socket) =>
|
||||
{
|
||||
if (OnDisconnect != null) OnDisconnect(socket);
|
||||
ClientInfoDic.Remove(socket);
|
||||
socket.Close();
|
||||
}, client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (!_isValid) return;
|
||||
_isValid = false;
|
||||
// if (_connectThread != null) _connectThread.Abort();
|
||||
var tempList = new List<Socket>();
|
||||
foreach (var socket in ClientInfoDic.Keys)
|
||||
{
|
||||
tempList.Add(socket);
|
||||
}
|
||||
|
||||
foreach (var socket in tempList)
|
||||
{
|
||||
CloseClient(socket);
|
||||
}
|
||||
|
||||
if (_headCheckTimer != null)
|
||||
{
|
||||
_headCheckTimer.Stop();
|
||||
_headCheckTimer = null;
|
||||
}
|
||||
|
||||
_server.Close();
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 错误回调
|
||||
// /// </summary>
|
||||
// /// <param name="e"></param>
|
||||
// private void onError(SocketException ex)
|
||||
// {
|
||||
// PostMainThreadAction<SocketException>(OnError, ex);
|
||||
// }
|
||||
|
||||
|
||||
// <summary>
|
||||
/// 通知主线程回调
|
||||
/// </summary>
|
||||
private void PostMainThreadAction(Action action)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action e = (Action)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
if (e != null) e();
|
||||
}), new { action = action });
|
||||
}
|
||||
|
||||
private void PostMainThreadAction<T>(Action<T> action, T arg1)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action<T> e = (Action<T>)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
T t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o);
|
||||
if (e != null) e(t1);
|
||||
}), new { action = action, arg1 = arg1 });
|
||||
}
|
||||
|
||||
public void PostMainThreadAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
|
||||
{
|
||||
_mainThread.Post(new SendOrPostCallback((o) =>
|
||||
{
|
||||
Action<T1, T2> e = (Action<T1, T2>)o.GetType().GetProperty("action")?.GetValue(o);
|
||||
T1 t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o);
|
||||
T2 t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o);
|
||||
if (e != null) e(t1, t2);
|
||||
}), new { action = action, arg1 = arg1, arg2 = arg2 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2c6448e77af258458f5959d7e15c022
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SocketClientMain : MonoBehaviour
|
||||
{
|
||||
private string ip = "127.0.0.1";
|
||||
private int port = 8080;
|
||||
private SocketClient _client;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_client = new SocketClient("192.168.31.67", 6854);
|
||||
|
||||
|
||||
|
||||
_client.Connect(() =>
|
||||
{
|
||||
UnityEngine.Debug.Log("连接成功");
|
||||
|
||||
// _client.DisConnect();
|
||||
}, () => { UnityEngine.Debug.Log("连接失败"); });
|
||||
}
|
||||
|
||||
public void StartConnect(string ip = "", int port = 0)
|
||||
{
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.A))
|
||||
{
|
||||
var bytes = System.Text.Encoding.UTF8.GetBytes("我是测试数据");
|
||||
_client.Send((System.UInt16)SocketEvent.ScTest, bytes);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.D))
|
||||
{
|
||||
_client.DisConnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 注意由于Unity编译器环境下,游戏开启/关闭只影响主线程的开关,游戏关闭回调时需要通过Close函数来关闭服务端/客户端的线程。
|
||||
if (_client != null)
|
||||
{
|
||||
_client.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 052b8595da3036e4a94eabc9a5a2b95b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SocketServerMain : MonoBehaviour
|
||||
{
|
||||
SocketServer _server;
|
||||
private void Awake()
|
||||
{
|
||||
_server = new SocketServer("192.168.31.67", 6854);
|
||||
|
||||
_server.OnReceive += (client, data) =>
|
||||
{
|
||||
UnityEngine.Debug.LogFormat("[{0}]接收到数据>>>{1} {2}", client.LocalEndPoint.ToString(), (SocketEvent)data.Type, data.Buff.Length);
|
||||
|
||||
switch ((SocketEvent)data.Type)
|
||||
{
|
||||
case SocketEvent.ScTest:
|
||||
UnityEngine.Debug.LogFormat("接收到测试数据 >>> {0}", System.Text.Encoding.UTF8.GetString(data.Data));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.A))
|
||||
{
|
||||
// 踢出连接
|
||||
foreach (var item in _server.ClientInfoDic.Keys)
|
||||
{
|
||||
_server.KickOutAll();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 注意由于Unity编译器环境下,游戏开启/关闭只影响主线程的开关,游戏关闭回调时需要通过Close函数来关闭服务端/客户端的线程。
|
||||
if (_server != null)
|
||||
{
|
||||
_server.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b72333f2bc06a7428fb3c0eadeed0d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "stary.hotfixupdate.main",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:e34a5702dd353724aa315fb8011f08c3",
|
||||
"GUID:d1a793c2b6959e04ea45b972eaa369c8",
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||
"GUID:4d1926c9df5b052469a1c63448b7609a",
|
||||
"GUID:006be2ba6588a8b4989d3724bb01d8b7",
|
||||
"GUID:fe14bc9dd681249d19cf4ef377c7e29e",
|
||||
"GUID:6447e10b87dc140ab924878c1ecef665",
|
||||
"GUID:ec45849e30ba03e4dab386099d8c697b",
|
||||
"GUID:aa8669166ad4e984bb229dc22e7b1e02",
|
||||
"GUID:66fdc96a271fc954e9da30c76a486d9c",
|
||||
"GUID:6a5d7223300a2ef48aa366288a446472",
|
||||
"GUID:10c9b58b77ad42b4193e2a393b1a9899",
|
||||
"GUID:fad681b9bfe621d4fa07f4f69c311443",
|
||||
"GUID:ff70fce64a3314305977bdf5610ed86b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4492e37c9663479418f9522cc4796b57
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user