1
0
mirror of https://github.com/soarqin/DSP_Mods.git synced 2025-12-09 02:53:29 +08:00

UXAssist 1.2.4

This commit is contained in:
2024-09-24 00:33:41 +08:00
parent daaf7b4901
commit 237830fc91
13 changed files with 149 additions and 73 deletions

View File

@@ -1,5 +1,13 @@
## Changlog
* 1.2.4
+ `Sunlight at night`:
- Fix flickering issue while mecha is sailing.
- Can configure the light angles now.
+ `Scale up mouse cursor`: Fix known issues.
+ `Buy out techs with their prerequisites`: Fix a bug that warning popup from invalid data.
+ Does not patch `BulletTime`'s speed control now, as `BulletTime` has been updated to support configurable maximum speed.
+ Some minor fixes and tweaks.
* 1.2.3
+ `Real-time logistic stations info panel`: Fix bar length not match with item amount when item amount is more than capacity.
+ `Sunlight at night`: Fix not working.
@@ -190,6 +198,14 @@
## 更新日志
* 1.2.4
+ `夜间日光灯`
- 修复了航行时闪烁的问题
- 现在可以配置入射光线角度了
+ `放大鼠标指针`:修复已知问题
+ `买断科技也同时买断所有前置科技`:修复了数据错误警告弹窗的问题
+ 不再对`BulletTime`的速度控制打补丁,因为`BulletTime`已更新支持可配置最大速度
+ 一些小修复和调整
* 1.2.3
+ `物流运输站实时信息面板`:修复了物品数量超过容量限制时条长度不匹配的问题
+ `夜间日光灯`:修复了不起作用的问题

View File

@@ -1,15 +1,30 @@
using System;
using System.Linq;
using System.Reflection;
using HarmonyLib;
namespace UXAssist.Common;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class PatchImplGuidAttribute(string guid) : Attribute
public class PatchGuidAttribute(string guid) : Attribute
{
public string Guid { get; } = guid;
}
public enum PatchCallbackFlag
{
// OnEnable() is called After patch is applied by default, set this flag to call it before patch is applied
CallOnEnableBeforePatch,
// OnDisable() is called Before patch is removed by default, set this flag to call it after patch is removed
CallOnDisableAfterUnpatch,
}
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class PatchSetCallbackFlagAttribute(PatchCallbackFlag flag) : Attribute
{
public PatchCallbackFlag Flag { get; } = flag;
}
public class PatchImpl<T> where T : PatchImpl<T>, new()
{
private static T Instance { get; } = new();
@@ -21,14 +36,18 @@ public class PatchImpl<T> where T : PatchImpl<T>, new()
var thisInstance = Instance;
if (enable)
{
var guid = typeof(T).GetCustomAttribute<PatchImplGuidAttribute>()?.Guid ?? $"PatchImpl.{typeof(T).FullName ?? typeof(T).ToString()}";
var guid = typeof(T).GetCustomAttribute<PatchGuidAttribute>()?.Guid ?? $"PatchImpl.{typeof(T).FullName ?? typeof(T).ToString()}";
var callOnEnableBefore = typeof(T).GetCustomAttributes<PatchSetCallbackFlagAttribute>().Any(n => n.Flag == PatchCallbackFlag.CallOnEnableBeforePatch);
if (callOnEnableBefore) thisInstance.OnEnable();
thisInstance._patch ??= Harmony.CreateAndPatchAll(typeof(T), guid);
thisInstance.OnEnable();
if (!callOnEnableBefore) thisInstance.OnEnable();
return;
}
thisInstance.OnDisable();
var callOnDisableAfter = typeof(T).GetCustomAttributes<PatchSetCallbackFlagAttribute>().Any(n => n.Flag == PatchCallbackFlag.CallOnDisableAfterUnpatch);
if (!callOnDisableAfter) thisInstance.OnDisable();
thisInstance._patch?.UnpatchSelf();
thisInstance._patch = null;
if (callOnDisableAfter) thisInstance.OnDisable();
}
public static Harmony GetHarmony() => Instance._patch;

View File

@@ -31,7 +31,7 @@ public static class WindowFunctions
switch (uMsg)
{
case WinApi.WM_ACTIVATE:
UXAssist.Logger.LogDebug($"Activate: {wParam.ToInt32()}, {lParam.ToInt32()}");
// UXAssist.Logger.LogDebug($"Activate: {wParam.ToInt32()}, {lParam.ToInt32()}");
// TODO: Set Priority like: WinApi.SetPriorityClass(WinApi.GetCurrentProcess(), 0x00000080);
break;
case WinApi.WM_DESTROY:
@@ -41,9 +41,9 @@ public static class WindowFunctions
}
break;
}
return WinApi.CallWindowProc(_oldWndProc, hWnd, uMsg, wParam, lParam);
}
public static void ShowCPUInfo()
{
var details = WinApi.GetLogicalProcessorDetails();
@@ -51,7 +51,7 @@ public static class WindowFunctions
var hybrid = details.HybridArchitecture;
if (hybrid)
{
msg += "\nP-Cores: {details.PerformanceCoreCount}\nE-Cores: {details.EfficiencyCoreCount}";
msg += $"\nP-Cores: {details.PerformanceCoreCount}\nE-Cores: {details.EfficiencyCoreCount}";
}
var handle = WinApi.GetCurrentProcess();
@@ -114,32 +114,6 @@ public static class WindowFunctions
}
}
public static void RefreshSavePath()
{
if (ProfileName == null) return;
if (UIRoot.instance.loadGameWindow.gameObject.activeSelf)
{
UIRoot.instance.loadGameWindow._Close();
}
if (UIRoot.instance.saveGameWindow.gameObject.activeSelf)
{
UIRoot.instance.saveGameWindow._Close();
}
string gameSavePath;
if (Patches.GamePatch.ProfileBasedSaveFolderEnabled.Value && string.Compare(Patches.GamePatch.DefaultProfileName.Value, ProfileName, StringComparison.OrdinalIgnoreCase) != 0)
gameSavePath = $"{GameConfig.overrideDocumentFolder}{GameConfig.gameName}/Save/{ProfileName}/";
else
gameSavePath = $"{GameConfig.overrideDocumentFolder}{GameConfig.gameName}/Save/";
if (string.Compare(GameConfig.gameSavePath, gameSavePath, StringComparison.OrdinalIgnoreCase) == 0) return;
GameConfig.gameSavePath = gameSavePath;
if (!Directory.Exists(GameConfig.gameSavePath))
{
Directory.CreateDirectory(GameConfig.gameSavePath);
}
}
public static IntPtr FindGameWindow()
{
if (_gameWindowHandle == IntPtr.Zero)

View File

@@ -17,6 +17,8 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
public static ConfigEntry<bool> UnlimitInteractiveEnabled;
public static ConfigEntry<bool> RemoveSomeConditionEnabled;
public static ConfigEntry<bool> NightLightEnabled;
public static ConfigEntry<float> NightLightAngleX;
public static ConfigEntry<float> NightLightAngleY;
public static ConfigEntry<bool> RemoveBuildRangeLimitEnabled;
public static ConfigEntry<bool> LargerAreaForUpgradeAndDismantleEnabled;
public static ConfigEntry<bool> LargerAreaForTerraformEnabled;
@@ -55,6 +57,8 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
UnlimitInteractiveEnabled.SettingChanged += (_, _) => UnlimitInteractive.Enable(UnlimitInteractiveEnabled.Value);
RemoveSomeConditionEnabled.SettingChanged += (_, _) => RemoveSomeConditionBuild.Enable(RemoveSomeConditionEnabled.Value);
NightLightEnabled.SettingChanged += (_, _) => NightLight.Enable(NightLightEnabled.Value);
NightLightAngleX.SettingChanged += (_, _) => NightLight.UpdateSunlightAngle();
NightLightAngleY.SettingChanged += (_, _) => NightLight.UpdateSunlightAngle();
RemoveBuildRangeLimitEnabled.SettingChanged += (_, _) => RemoveBuildRangeLimit.Enable(RemoveBuildRangeLimitEnabled.Value);
LargerAreaForUpgradeAndDismantleEnabled.SettingChanged += (_, _) => LargerAreaForUpgradeAndDismantle.Enable(LargerAreaForUpgradeAndDismantleEnabled.Value);
LargerAreaForTerraformEnabled.SettingChanged += (_, _) => LargerAreaForTerraform.Enable(LargerAreaForTerraformEnabled.Value);
@@ -175,8 +179,6 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
public class NightLight : PatchImpl<NightLight>
{
private const float NightLightAngleX = -8;
private const float NightLightAngleY = -2;
private static bool _nightlightInitialized;
private static bool _mechaOnEarth;
private static AnimationState _sail;
@@ -188,6 +190,13 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
_sunlight.transform.localEulerAngles = new Vector3(0f, 180f);
}
public static void UpdateSunlightAngle()
{
if (!_sunlight) return;
_sunlight.transform.rotation = Quaternion.LookRotation(-GameMain.mainPlayer.transform.up + GameMain.mainPlayer.transform.forward * NightLightAngleX.Value / 10f +
GameMain.mainPlayer.transform.right * NightLightAngleY.Value / 10f);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.LateUpdate))]
public static void GameMain_LateUpdate_Postfix(GameMain __instance)
@@ -217,12 +226,13 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
if (!_sail || !_sail.enabled) return;
_mechaOnEarth = false;
if (!_sunlight || !_sunlight.transform) return;
if (!_sunlight) return;
_sunlight.transform.localEulerAngles = new Vector3(0f, 180f);
_sunlight = null;
return;
}
if (_sail && _sail.enabled) return;
if (!_sunlight)
{
var simu = GameMain.universeSimulator;
@@ -231,9 +241,7 @@ public class FactoryPatch : PatchImpl<FactoryPatch>
}
_mechaOnEarth = true;
if (_sunlight)
_sunlight.transform.rotation = Quaternion.LookRotation(-GameMain.mainPlayer.transform.up + GameMain.mainPlayer.transform.forward * NightLightAngleX / 10f +
GameMain.mainPlayer.transform.right * NightLightAngleY / 10f);
UpdateSunlightAngle();
}
[HarmonyTranspiler]

View File

@@ -76,13 +76,13 @@ public class GamePatch: PatchImpl<GamePatch>
LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
MouseCursorScaleUpMultiplier.SettingChanged += (_, _) =>
{
MouseCursorScaleUp.reload = true;
MouseCursorScaleUp.NeedReloadCursors = true;
MouseCursorScaleUp.Enable(MouseCursorScaleUpMultiplier.Value > 1);
};
// AutoSaveOptEnabled.SettingChanged += (_, _) => AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
ProfileBasedSaveFolderEnabled.SettingChanged += (_, _) => Functions.WindowFunctions.RefreshSavePath();
DefaultProfileName.SettingChanged += (_, _) => Functions.WindowFunctions.RefreshSavePath();
ProfileBasedSaveFolderEnabled.SettingChanged += (_, _) => RefreshSavePath();
DefaultProfileName.SettingChanged += (_, _) => RefreshSavePath();
GameUpsFactor.SettingChanged += (_, _) =>
{
if (!EnableGameUpsFactor || GameUpsFactor.Value == 0.0) return;
@@ -99,7 +99,7 @@ public class GamePatch: PatchImpl<GamePatch>
{
EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
MouseCursorScaleUp.reload = false;
MouseCursorScaleUp.NeedReloadCursors = false;
MouseCursorScaleUp.Enable(MouseCursorScaleUpMultiplier.Value > 1);
// AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
@@ -111,7 +111,7 @@ public class GamePatch: PatchImpl<GamePatch>
Enable(false);
LoadLastWindowRect.Enable(false);
EnableWindowResize.Enable(false);
MouseCursorScaleUp.reload = false;
MouseCursorScaleUp.NeedReloadCursors = false;
MouseCursorScaleUp.Enable(false);
// AutoSaveOpt.Enable(false);
ConvertSavesFromPeace.Enable(false);
@@ -132,6 +132,33 @@ public class GamePatch: PatchImpl<GamePatch>
}
}
private static void RefreshSavePath()
{
var profileName = Functions.WindowFunctions.ProfileName;
if (profileName == null) return;
if (UIRoot.instance.loadGameWindow.gameObject.activeSelf)
{
UIRoot.instance.loadGameWindow._Close();
}
if (UIRoot.instance.saveGameWindow.gameObject.activeSelf)
{
UIRoot.instance.saveGameWindow._Close();
}
string gameSavePath;
if (ProfileBasedSaveFolderEnabled.Value && string.Compare(DefaultProfileName.Value, profileName, StringComparison.OrdinalIgnoreCase) != 0)
gameSavePath = $"{GameConfig.overrideDocumentFolder}{GameConfig.gameName}/Save/{profileName}/";
else
gameSavePath = $"{GameConfig.overrideDocumentFolder}{GameConfig.gameName}/Save/";
if (string.Compare(GameConfig.gameSavePath, gameSavePath, StringComparison.OrdinalIgnoreCase) == 0) return;
GameConfig.gameSavePath = gameSavePath;
if (!Directory.Exists(GameConfig.gameSavePath))
{
Directory.CreateDirectory(GameConfig.gameSavePath);
}
}
[HarmonyPrefix, HarmonyPatch(typeof(GameMain), nameof(GameMain.HandleApplicationQuit))]
private static void GameMain_HandleApplicationQuit_Prefix()
{
@@ -487,7 +514,7 @@ public class GamePatch: PatchImpl<GamePatch>
);
matcher.Advance(2).Opcode = OpCodes.Brfalse;
matcher.Insert(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ConvertSavesFromPeace), nameof(ConvertSavesFromPeace._needConvert)))
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ConvertSavesFromPeace), nameof(_needConvert)))
);
return matcher.InstructionEnumeration();
}
@@ -500,13 +527,14 @@ public class GamePatch: PatchImpl<GamePatch>
}
}
[PatchSetCallbackFlag(PatchCallbackFlag.CallOnDisableAfterUnpatch)]
private class MouseCursorScaleUp: PatchImpl<MouseCursorScaleUp>
{
public static bool reload;
public static bool NeedReloadCursors;
protected override void OnEnable()
{
if (!reload) return;
if (!NeedReloadCursors) return;
if (!UICursor.loaded) return;
UICursor.loaded = false;
UICursor.LoadCursors();
@@ -514,7 +542,7 @@ public class GamePatch: PatchImpl<GamePatch>
protected override void OnDisable()
{
if (!reload) return;
if (!NeedReloadCursors) return;
if (!UICursor.loaded) return;
UICursor.loaded = false;
UICursor.LoadCursors();

View File

@@ -13,8 +13,13 @@ public class MyCheckBox : MonoBehaviour
public RectTransform rectTrans;
public Text labelText;
public event Action OnChecked;
protected event Action OnFree;
private bool _checked;
private ConfigEntry<bool> _configAssigned;
protected void OnDestroy()
{
OnFree?.Invoke();
}
public bool Checked
{
@@ -29,9 +34,10 @@ public class MyCheckBox : MonoBehaviour
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, ConfigEntry<bool> config, string label = "", int fontSize = 15)
{
var cb = CreateCheckBox(x, y, parent, config.Value, label, fontSize);
cb._configAssigned = config;
cb.OnChecked += () => config.Value = !config.Value;
config.SettingChanged += (_, _) => cb.Checked = config.Value;
EventHandler func = (_, _) => cb.Checked = config.Value;
config.SettingChanged += func;
cb.OnFree += () => config.SettingChanged -= func;
return cb;
}

View File

@@ -22,6 +22,7 @@ public class MyConfigWindow : MyWindowWithTabs
public override void _OnCreate()
{
base._OnCreate();
_windowTrans = GetComponent<RectTransform>();
OnUICreated?.Invoke(this, _windowTrans);
AutoFitWindowSize();
@@ -32,26 +33,16 @@ public class MyConfigWindow : MyWindowWithTabs
public override void _OnDestroy()
{
_windowTrans = null;
base._OnDestroy();
}
public override bool _OnInit()
{
if (!base._OnInit()) return false;
_windowTrans.anchoredPosition = new Vector2(0, 0);
return true;
}
public override void _OnFree()
{
}
public override void _OnOpen()
{
}
public override void _OnClose()
{
}
public override void _OnUpdate()
{
base._OnUpdate();

View File

@@ -10,6 +10,7 @@ namespace UXAssist.UI;
public class MyKeyBinder : MonoBehaviour
{
private ConfigEntry<KeyboardShortcut> _config;
protected event Action OnFree;
[SerializeField]
public Text functionText;
@@ -42,6 +43,11 @@ public class MyKeyBinder : MonoBehaviour
public UIButton setNoneKeyUIButton;
private bool _nextNotOn;
protected void OnDestroy()
{
OnFree?.Invoke();
}
public static RectTransform CreateKeyBinder(float x, float y, RectTransform parent, ConfigEntry<KeyboardShortcut> config, string label = "", int fontSize = 17)
{
@@ -82,10 +88,10 @@ public class MyKeyBinder : MonoBehaviour
Destroy(uikeyEntry);
kb.setNoneKeyUIButton.gameObject.SetActive(false);
EventHandler func = (_, _) => kb.SettingChanged();
kb.SettingChanged();
config.SettingChanged += (_, _) => {
kb.SettingChanged();
};
config.SettingChanged += func;
kb.OnFree += () => config.SettingChanged -= func;
kb.inputUIButton.onClick += kb.OnInputUIButtonClick;
kb.setDefaultUIButton.onClick += kb.OnSetDefaultKeyClick;
//kb.setNoneKeyUIButton.onClick += kb.OnSetNoneKeyClick;

View File

@@ -22,6 +22,12 @@ public class MyWindow : ManualBehaviour
protected const float TabHeight = 27f;
protected const float Margin = 30f;
protected const float Spacing = 10f;
protected event Action OnFree;
public override void _OnFree()
{
OnFree?.Invoke();
}
public virtual void TryClose()
{
@@ -222,12 +228,14 @@ public class MyWindow : ManualBehaviour
{
var slider = MySlider.CreateSlider(x, y, parent, OnConfigValueChanged(config), valueMapper.Min, valueMapper.Max, format, width);
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
config.SettingChanged += (sender, args) =>
EventHandler func = (_, _) =>
{
var index = OnConfigValueChanged(config);
slider.Value = index;
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
};
config.SettingChanged += func;
OnFree += () => config.SettingChanged -= func;
slider.OnValueChanged += () =>
{
var index = Mathf.RoundToInt(slider.Value);

View File

@@ -36,6 +36,7 @@ public static class UIConfigWindow
I18N.Add("Reset", "Reset", "重置");
I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离");
I18N.Add("Night Light", "Sunlight at night", "夜间日光灯");
I18N.Add("Angle X:", "Angle X:", "入射角度X:");
I18N.Add("Remove some build conditions", "Remove some build conditions", "移除部分不影响游戏逻辑的建造条件");
I18N.Add("Remove build range limit", "Remove build count and range limit", "移除建造数量和距离限制");
I18N.Add("Larger area for upgrade and dismantle", "Larger area for upgrade and dismantle", "范围升级和拆除的最大区域扩大");
@@ -104,6 +105,14 @@ public static class UIConfigWindow
}
}
private class AngleMapper : MyWindow.ValueMapper<float>
{
public override int Min => 0;
public override int Max => 20;
public override float IndexToValue(int index) => index - 10f;
public override int ValueToIndex(float value) => Mathf.RoundToInt(value + 10f);
}
private class DistanceMapper : MyWindow.ValueMapper<double>
{
public override int Min => 1;
@@ -161,11 +170,11 @@ public static class UIConfigWindow
wnd.AddInputField(x, y, 200f, tab1, GamePatch.DefaultProfileName, 15, "input-profile-save-folder");
y += 18f;
}
/*
x = 400f;
y = 10f;
wnd.AddButton(x, y, tab1, "Show CPU Info", 16, "button-show-cpu-info", WindowFunctions.ShowCPUInfo);
*/
if (!ModsCompat.BulletTimeWrapper.HasBulletTime)
{
y += 36f;
@@ -183,7 +192,16 @@ public static class UIConfigWindow
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.RemoveBuildRangeLimitEnabled, "Remove build range limit");
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light");
var cb = wnd.AddCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light");
x += cb.Width + 5f + 10f;
txt = wnd.AddText2(x, y + 2, tab2, "Angle X:", 13, "text-nightlight-angle-x");
x += txt.preferredWidth + 5f;
wnd.AddSlider(x, y + 7, tab2, FactoryPatch.NightLightAngleX, new AngleMapper(), "0", 80f);
x += 90f;
txt = wnd.AddText2(x, y + 2, tab2, "Y:", 13, "text-nightlight-angle-y");
x += txt.preferredWidth + 5f;
wnd.AddSlider(x, y + 7, tab2, FactoryPatch.NightLightAngleY, new AngleMapper(), "0", 80f);
x = 0;
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.LargerAreaForUpgradeAndDismantleEnabled, "Larger area for upgrade and dismantle");
y += 36f;

View File

@@ -92,6 +92,8 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
"Remove part of build condition checks that does not affect game logic");
FactoryPatch.NightLightEnabled = Config.Bind("Factory", "NightLight", false,
"Night light");
FactoryPatch.NightLightAngleX = Config.Bind("Factory", "NightLightAngleX", -8f, "Night light angle X");
FactoryPatch.NightLightAngleY = Config.Bind("Factory", "NightLightAngleY", -2f, "Night light angle Y");
PlanetPatch.PlayerActionsInGlobeViewEnabled = Config.Bind("Planet", "PlayerActionsInGlobeView", false,
"Enable player actions in globe view");
FactoryPatch.RemoveBuildRangeLimitEnabled = Config.Bind("Factory", "RemoveBuildRangeLimit", false,
@@ -234,7 +236,7 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
if (wasActive) ToggleConfigWindow();
}
[PatchImplGuid(PluginInfo.PLUGIN_GUID)]
[PatchGuid(PluginInfo.PLUGIN_GUID)]
private class UIPatch: PatchImpl<UIPatch>
{
private static GameObject _buttonOnPlanetGlobe;

View File

@@ -4,7 +4,7 @@
<TargetFramework>net472</TargetFramework>
<BepInExPluginGuid>org.soardev.uxassist</BepInExPluginGuid>
<Description>DSP MOD - UXAssist</Description>
<Version>1.2.3</Version>
<Version>1.2.4</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<PackageId>UXAssist</PackageId>

View File

@@ -1,6 +1,6 @@
{
"name": "UXAssist",
"version_number": "1.2.3",
"version_number": "1.2.4",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/UXAssist",
"description": "Some functions and patches for better user experience / 一些提升用户体验的功能和补丁",
"dependencies": [