chore: initialize standalone UPM package
Publish UPM package / publish (push) Failing after 1s

This commit is contained in:
2026-08-26 02:50:31 +08:00
commit 78ccae3e07
142 changed files with 6572 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
using UnityEngine;
namespace ShrinkModFramework
{
[DefaultExecutionOrder(-2100)]
public sealed class ShrinkModBootstrap : MonoBehaviour
{
[Header("Override (留空则只使用组件上的选项)")]
[SerializeField] private ShrinkModFrameworkSettings settingsOverride;
[Header("Bootstrap")]
[SerializeField] private bool autoLoadOnAwake = true;
private static bool _bootstrapped;
private void Awake()
{
if (_bootstrapped)
{
Destroy(gameObject);
return;
}
_bootstrapped = true;
DontDestroyOnLoad(gameObject);
var shouldAutoLoad = settingsOverride ? settingsOverride.autoLoadOnStartup : autoLoadOnAwake;
if (shouldAutoLoad)
ShrinkModLoader.LoadAll(settingsOverride);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23fd762abde4cfe48bf93f9ff86ffdd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
using UnityEngine;
namespace ShrinkModFramework
{
[CreateAssetMenu(fileName = "ShrinkModFrameworkSettings", menuName = "ShrinkModFramework/Settings")]
public class ShrinkModFrameworkSettings : ScriptableObject
{
private static ShrinkModFrameworkSettings _instance;
public static ShrinkModFrameworkSettings Instance
{
get
{
if (_instance) return _instance;
_instance = Resources.Load<ShrinkModFrameworkSettings>("ShrinkModFrameworkSettings");
#if UNITY_EDITOR
if (!_instance)
{
var guids = UnityEditor.AssetDatabase.FindAssets("t:ShrinkModFrameworkSettings");
if (guids.Length > 0)
{
var path = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[0]);
_instance = UnityEditor.AssetDatabase.LoadAssetAtPath<ShrinkModFrameworkSettings>(path);
}
}
#endif
if (!_instance)
{
_instance = CreateInstance<ShrinkModFrameworkSettings>();
Debug.LogWarning(
"[ShrinkModFramework] 未找到 ShrinkModFrameworkSettings 配置文件,当前使用默认配置。");
}
return _instance;
}
internal set => _instance = value;
}
[Header("Bootstrap")]
public bool autoLoadOnStartup = true;
[Tooltip("使用 ShrinkContextHost 协调模组组件;关闭时回退到旧的只增不减 ShrinkModLoader。")]
public bool useContextHost = true;
[Header("Logging")]
public bool verboseLogging = true;
[Header("Discovery")]
public string[] assemblyNamePrefixes = new string[0];
[Header("External Mods")]
public bool enableExternalDllMods = true;
public bool autoCreateExternalModsDirectory = true;
public string externalModsFolderName = "Mods";
public bool watchExternalModsDirectory = true;
public float externalModsReloadDelaySeconds = 0.5f;
[Min(1)]
[Tooltip("Mono 下外部程序集 revision 会常驻;历史数量达到该软阈值后提示 Domain Reload/重启。")]
public int externalAssemblyRevisionSoftLimit = 16;
[Header("Harmony")]
public bool enableHarmonyPatching = true;
[Header("Networking")]
public bool enableNetworkSync = true;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f9516fe9a338fb448308a9205c3d448
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using System;
namespace ShrinkModFramework
{
/// <summary>把文件系统的 burst 事件折叠为一次主线程 reload。</summary>
internal sealed class ShrinkModReloadDebouncer
{
private bool _pending;
private float _reloadAt;
public bool IsPending => _pending;
public void Schedule(float now, float delaySeconds)
{
_pending = true;
_reloadAt = now + Math.Max(0.05f, delaySeconds);
}
public bool TryConsume(float now)
{
if (!_pending || now < _reloadAt)
return false;
_pending = false;
return true;
}
public void Reset()
{
_pending = false;
_reloadAt = 0f;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec113876ec861b64cb8391eed726108b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using UnityEngine;
namespace ShrinkModFramework
{
public static class ShrinkModRuntimeBootstrap
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticStateForPlayMode()
{
ShrinkModLoader.ResetForDomainReload();
ShrinkModNetworkManager.ResetForDomainReload();
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void AutoLoad()
{
var settings = ShrinkModFrameworkSettings.Instance;
ShrinkModRuntimeDriver.EnsureCreated(settings);
if (settings && !settings.autoLoadOnStartup)
return;
try
{
ShrinkModLoader.LoadAll(settings);
}
catch (System.Exception ex)
{
Debug.LogException(ex);
Debug.LogError($"[ShrinkModFramework] 自动启动失败:{ex.Message}");
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 329c6f80f39fd4840b987c16a6124b1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+173
View File
@@ -0,0 +1,173 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using UnityEngine;
namespace ShrinkModFramework
{
[DefaultExecutionOrder(-2099)]
internal sealed class ShrinkModRuntimeDriver : MonoBehaviour
{
private static ShrinkModRuntimeDriver _instance;
private readonly ConcurrentQueue<Action> _mainThreadActions = new();
private readonly ShrinkModReloadDebouncer _externalReloadDebouncer = new();
private FileSystemWatcher _watcher;
private ShrinkModFrameworkSettings _settings;
public static void EnsureCreated(ShrinkModFrameworkSettings settings)
{
if (_instance)
{
_instance.ApplySettings(settings);
return;
}
var go = new GameObject("ShrinkModRuntimeDriver");
if (Application.isPlaying)
DontDestroyOnLoad(go);
_instance = go.AddComponent<ShrinkModRuntimeDriver>();
_instance.ApplySettings(settings);
}
public static void Enqueue(Action action)
{
if (action == null)
return;
if (_instance == null)
throw new InvalidOperationException("ShrinkModRuntimeDriver 尚未创建。");
_instance._mainThreadActions.Enqueue(action);
}
internal static ShrinkModRuntimeDriver InstanceForTesting => _instance;
internal bool HasWatcherForTesting => _watcher != null;
internal void PumpForTesting() => Update();
private void Update()
{
while (_mainThreadActions.TryDequeue(out var action))
{
try
{
action();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
if (!_externalReloadDebouncer.TryConsume(Time.realtimeSinceStartup))
return;
try
{
ShrinkModLoader.LoadNewExternalMods(_settings);
}
catch (Exception ex)
{
Debug.LogException(ex);
Debug.LogError($"[ShrinkModFramework] 自动热加载外部模组失败:{ex.Message}");
}
}
private void OnDestroy()
{
DisposeWatcher();
if (_instance == this)
_instance = null;
}
private void ApplySettings(ShrinkModFrameworkSettings settings)
{
_settings = settings ?? ShrinkModFrameworkSettings.Instance;
if (_settings == null || !_settings.enableExternalDllMods || !_settings.watchExternalModsDirectory)
{
DisposeWatcher();
return;
}
TrySetupWatcher();
}
private void TrySetupWatcher()
{
#if ENABLE_IL2CPP && !UNITY_EDITOR
return;
#else
var directory = ShrinkExternalModAssemblyLoader.GetExternalModsDirectory(_settings);
if (_settings.autoCreateExternalModsDirectory)
Directory.CreateDirectory(directory);
if (!Directory.Exists(directory))
return;
if (_watcher != null && string.Equals(_watcher.Path, directory, StringComparison.OrdinalIgnoreCase))
return;
DisposeWatcher();
try
{
_watcher = new FileSystemWatcher(directory, "*.dll")
{
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime
};
_watcher.Created += OnExternalModsChanged;
_watcher.Changed += OnExternalModsChanged;
_watcher.Deleted += OnExternalModsChanged;
_watcher.Renamed += OnExternalModsRenamed;
_watcher.EnableRaisingEvents = true;
}
catch (Exception ex)
{
Debug.LogWarning($"[ShrinkModFramework] 外部模组目录监听启动失败:{ex.Message}");
DisposeWatcher();
}
#endif
}
private void OnExternalModsChanged(object sender, FileSystemEventArgs args)
{
ScheduleExternalReload(args.FullPath);
}
private void OnExternalModsRenamed(object sender, RenamedEventArgs args)
{
ScheduleExternalReload(args.OldFullPath);
ScheduleExternalReload(args.FullPath);
}
private void ScheduleExternalReload(string path)
{
if (string.IsNullOrWhiteSpace(path))
return;
_mainThreadActions.Enqueue(() =>
{
var delay = _settings ? Mathf.Max(0.05f, _settings.externalModsReloadDelaySeconds) : 0.5f;
_externalReloadDebouncer.Schedule(Time.realtimeSinceStartup, delay);
});
}
private void DisposeWatcher()
{
if (_watcher == null)
return;
_watcher.EnableRaisingEvents = false;
_watcher.Created -= OnExternalModsChanged;
_watcher.Changed -= OnExternalModsChanged;
_watcher.Deleted -= OnExternalModsChanged;
_watcher.Renamed -= OnExternalModsRenamed;
_watcher.Dispose();
_watcher = null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a850cdc47a8b5b41a7559c3d1fd9efc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: