This commit is contained in:
2025-10-31 15:31:34 +08:00
parent cf47a388ca
commit ad94eae693
316 changed files with 40772 additions and 89 deletions

View File

@@ -0,0 +1,94 @@
using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
public class Boot : SingletonBehaviour<Boot>
{
public AssetReferenceGameObject patchWindowRef;
// DLL<4C><4C><EFBFBD>ã<EFBFBD>0=<3D><><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA><EFBFBD><EFBFBD>, 1=<3D>ȸ<EFBFBD><C8B8>³<EFBFBD><C2B3><EFBFBD><EFBFBD><EFBFBD>
public Dictionary<string, int> dllMap = new Dictionary<string, int>()
{
{ "mscorlib", 0 },
{ "System", 0 },
{ "System.Core", 0 },
{ "UnityScripts.HotUpdate", 1 },
};
void Awake()
{
Application.targetFrameRate = 60;
Application.runInBackground = true;
DontDestroyOnLoad(gameObject);
StartCoroutine(Initialize());
}
IEnumerator Initialize()
{
// <20><>ʼ<EFBFBD><CABC>Addressables
var initHandle = Addressables.InitializeAsync();
yield return initHandle;
// <20><><EFBFBD>ظ<EFBFBD><D8B8>´<EFBFBD><C2B4><EFBFBD>
var windowHandle = patchWindowRef.LoadAssetAsync<GameObject>();
yield return windowHandle;
var windowObj = Instantiate(windowHandle.Result, GameManager.Inst.MainCanvasUI.transform);
var patchWindow = windowObj.GetComponent<PatchWindow>();
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>£<EFBFBD><C2A3><EFBFBD><EFBFBD>ɺ<EFBFBD><C9BA>ص<EFBFBD>LoadDll
patchWindow.StartCheckUpdate(() => StartCoroutine(LoadDllAndStartGame()));
}
IEnumerator LoadDllAndStartGame()
{
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DLL
foreach (var dll in dllMap)
{
yield return LoadSingleDll(dll.Key, dll.Value);
}
// <20><><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ
yield return LoadGameStart();
}
IEnumerator LoadSingleDll(string dllName, int dllType)
{
var handle = Addressables.LoadAssetAsync<TextAsset>(KeyManager.LoadDllKey(dllName));
yield return handle;
if (dllType == 0)
{
// <20><><EFBFBD>ز<EFBFBD><D8B2><EFBFBD>Ԫ<EFBFBD><D4AA><EFBFBD><EFBFBD>
RuntimeApi.LoadMetadataForAOTAssembly(handle.Result.bytes, HomologousImageMode.SuperSet);
}
else
{
#if UNITY_EDITOR
// <20><EFBFBD><E0BCAD>ģʽ<C4A3><CABD>ֱ<EFBFBD>ӻ<EFBFBD>ȡ<EFBFBD>Ѽ<EFBFBD><D1BC>صij<D8B5><C4B3><EFBFBD><EFBFBD><EFBFBD>
var hotUpdateAss = AppDomain.CurrentDomain.GetAssemblies()
.First(a => a.GetName().Name == "UnityScripts.HotUpdate");
#else
// <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD>ȸ<EFBFBD><C8B8>³<EFBFBD><C2B3><EFBFBD><EFBFBD><EFBFBD>
var hotUpdateAss = Assembly.Load(handle.Result.bytes);
#endif
Debug.Log($"<22>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD> {dllName}");
}
Addressables.Release(handle);
}
IEnumerator LoadGameStart()
{
var handle = Addressables.LoadAssetAsync<GameObject>(KeyManager.LoadPrefabKey("GameStart"));
yield return handle;
Instantiate(handle.Result);
Addressables.Release(handle);
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ConsoleToScreen : MonoBehaviour
{
const int maxLines = 50;
const int maxLineLength = 120;
private string _logStr = "";
private readonly List<string> _lines = new List<string>();
public int fontSize = 15;
void OnEnable() { Application.logMessageReceived += Log; }
void OnDisable() { Application.logMessageReceived -= Log; }
public void Log(string logString, string stackTrace, LogType type)
{
foreach (var line in logString.Split('\n'))
{
if (line.Length <= maxLineLength)
{
_lines.Add(line);
continue;
}
var lineCount = line.Length / maxLineLength + 1;
for (int i = 0; i < lineCount; i++)
{
if ((i + 1) * maxLineLength <= line.Length)
{
_lines.Add(line.Substring(i * maxLineLength, maxLineLength));
}
else
{
_lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength));
}
}
}
if (_lines.Count > maxLines)
{
_lines.RemoveRange(0, _lines.Count - maxLines);
}
_logStr = string.Join("\n", _lines);
}
void OnGUI()
{
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity,
new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f));
GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) });
}
}

View File

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

View File

@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : SingletonBehaviour<GameManager>
{
public GameObject MainCanvasUI;
public Camera MainCamera;
public Camera UICamera;
private void Awake()
{
DontDestroyOnLoad(MainCanvasUI);
DontDestroyOnLoad(MainCamera);
DontDestroyOnLoad(UICamera);
}
public bool HasNetwork()
{
return Application.internetReachability != NetworkReachability.NotReachable;
}
}

View File

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

View File

@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class KeyManager
{
public static string AssetDllDir = "Assets/GameRes/HotUpdate";
public static string AssetEntityDir = "Assets/GameRes/Entity";
public static string HybridCLRDataDir = $"{Application.dataPath.Replace("/Assets", "")}/HybridCLRData";
public static string HotDllSourceDir = $"{HybridCLRDataDir}/HotUpdateDlls";
public static string TDllSourceDir = $"{HybridCLRDataDir}/AssembliesPostIl2CppStrip";
public static string LoadDllKey(string dllName)
{
return $"{AssetDllDir}/{dllName}.dll.bytes";
}
public static string LoadPrefabKey(string prefabName)
{
return $"{AssetEntityDir}/{prefabName}.prefab";
}
}

View File

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

View File

@@ -0,0 +1,152 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Video;
public class PatchWindow : MonoBehaviour
{
public Text statusText;
public Slider progressBar;
public Text downloadSizeText;
public VideoPlayer video;
public Button actionButton;
public Text buttonText;
private bool hasUpdate;
private bool updateCompleted;
private System.Action onUpdateComplete;
private void Awake()
{
video.targetCamera = GameManager.Inst.UICamera;
actionButton.onClick.AddListener(OnActionButtonClick);
}
public void StartCheckUpdate(System.Action callback)
{
this.onUpdateComplete = callback;
if (GameManager.Inst.HasNetwork())
{
StartCoroutine(CheckForCatalogUpdates());
}
else
{
StartCoroutine(ReadyStart("<22><><EFBFBD><EFBFBD><EFBFBD>ʹ<E7A3AC>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>Դ"));
}
}
IEnumerator CheckForCatalogUpdates()
{
statusText.text = "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E5B5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...";
var catalogHandle = Addressables.CheckForCatalogUpdates(false);
yield return catalogHandle;
if (catalogHandle.Status != AsyncOperationStatus.Succeeded)
{
StartCoroutine(ReadyStart("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E5B5A5><EFBFBD><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>ʹ<EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>Դ"));
yield break;
}
else
{
var catalogs = catalogHandle.Result;
if (catalogs.Count > 0)
{
StartCoroutine(UpdateCatalogs(catalogs));
}
else
{
StartCoroutine(CheckNeedDownLoad());
}
}
}
IEnumerator UpdateCatalogs(List<string> catalogs)
{
statusText.text = "<22><EFBFBD><E5B5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...";
var updateHandle = Addressables.UpdateCatalogs(catalogs, false);
yield return updateHandle;
if (updateHandle.Status != AsyncOperationStatus.Succeeded)
{
StartCoroutine(ReadyStart("<22><EFBFBD><E5B5A5><EFBFBD><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>ʹ<EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>Դ"));
Addressables.Release(updateHandle);
yield break;
}
else
{
StartCoroutine(CheckNeedDownLoad());
}
Addressables.Release(updateHandle);
}
IEnumerator CheckNeedDownLoad()
{
statusText.text = "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
var sizeHandle = Addressables.GetDownloadSizeAsync("preload");
yield return sizeHandle;
if (sizeHandle.Status != AsyncOperationStatus.Succeeded)
{
StartCoroutine(ReadyStart("<22><>Դ<EFBFBD><D4B4><EFBFBD>¼<EFBFBD><C2BC><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>ʹ<EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>Դ"));
yield break;
}
else
{
long downloadSize = sizeHandle.Result;
if(downloadSize > 0)
{
hasUpdate = true;
statusText.text = "<22>п<EFBFBD><D0BF>ø<EFBFBD><C3B8><EFBFBD>";
downloadSizeText.text = $"<22><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>: {downloadSize / 1024f / 1024f:F1}MB";
buttonText.text = "<22><><EFBFBD>ظ<EFBFBD><D8B8><EFBFBD>";
actionButton.gameObject.SetActive(true);
}
else
{
StartCoroutine(ReadyStart());
}
}
}
private IEnumerator Download()
{
statusText.text = "<22><><EFBFBD>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD><EFBFBD>...";
progressBar.gameObject.SetActive(true);
var downloadHandle = Addressables.DownloadDependenciesAsync("preload");
while (!downloadHandle.IsDone)
{
float smoothProgress = Mathf.Lerp(progressBar.value, downloadHandle.PercentComplete, Time.deltaTime * 5f);
progressBar.value = smoothProgress;
var status = downloadHandle.GetDownloadStatus();
downloadSizeText.text = $"{status.DownloadedBytes / 1024f / 1024f:F1}MB / {status.TotalBytes / 1024f / 1024f:F1}MB";
yield return null;
}
if (downloadHandle.Status != AsyncOperationStatus.Succeeded)
{
StartCoroutine(ReadyStart("<22><><EFBFBD>ظ<EFBFBD><D8B8><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>ʹ<EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>Դ"));
yield break;
}
else
{
StartCoroutine(ReadyStart());
}
Addressables.Release(downloadHandle);
}
IEnumerator ReadyStart(string text = "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!")
{
yield return null;
statusText.text = text;
progressBar.value = 1f;
updateCompleted = true;
buttonText.text = "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ";
actionButton.gameObject.SetActive(true);
}
private void OnActionButtonClick()
{
if (hasUpdate && !updateCompleted)
{
StartCoroutine(Download());
}
else
{
onUpdateComplete?.Invoke();
gameObject.SetActive(false);
}
actionButton.gameObject.SetActive(false);
}
}

View File

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

View File

@@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> where T : class, new()
{
protected Singleton() { }
~Singleton() { singleton = null; }
private static T singleton;
private static readonly object locker = new object();
public static T Inst
{
get
{
if (singleton == null)
{
lock (locker)
{
if (singleton == null)
singleton = new T();
Debug.Log($"Create {typeof(T)}");
}
}
return singleton;
}
}
}

View File

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

View File

@@ -0,0 +1,24 @@
using UnityEngine;
public class SingletonBehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
public static T Inst
{
get
{
if (_instance == null)
{
_instance = FindAnyObjectByType<T>();
if (_instance == null)
{
GameObject singletonObject = new GameObject(typeof(T).Name);
_instance = singletonObject.AddComponent<T>();
}
}
return _instance;
}
}
}

View File

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

View File

@@ -0,0 +1,18 @@
{
"name": "UnityScripts.Runtime",
"rootNamespace": "",
"references": [
"GUID:9e24947de15b9834991c9d8411ea37cf",
"GUID:84651a3751eca9349aac36a66bba901b",
"GUID:13ba8ce62aa80c74598530029cb2d649"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 231d2189f0e1fc546999766032d5c36d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: