mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2025-12-09 14:13:31 +08:00
UXAssist v1.0.9
This commit is contained in:
@@ -714,6 +714,7 @@ public static class FactoryPatch
|
|||||||
{
|
{
|
||||||
_lastKey = KeyCode.None;
|
_lastKey = KeyCode.None;
|
||||||
}
|
}
|
||||||
|
if (!VFInput.noModifier) return;
|
||||||
int delta;
|
int delta;
|
||||||
if (UpdateKeyPressed(KeyCode.LeftArrow))
|
if (UpdateKeyPressed(KeyCode.LeftArrow))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UXAssist.Common;
|
using UXAssist.Common;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace UXAssist;
|
namespace UXAssist;
|
||||||
|
|
||||||
@@ -15,6 +18,7 @@ public static class GamePatch
|
|||||||
|
|
||||||
public static ConfigEntry<bool> EnableWindowResizeEnabled;
|
public static ConfigEntry<bool> EnableWindowResizeEnabled;
|
||||||
public static ConfigEntry<bool> LoadLastWindowRectEnabled;
|
public static ConfigEntry<bool> LoadLastWindowRectEnabled;
|
||||||
|
public static ConfigEntry<bool> AutoSaveOptEnabled;
|
||||||
public static ConfigEntry<bool> ConvertSavesFromPeaceEnabled;
|
public static ConfigEntry<bool> ConvertSavesFromPeaceEnabled;
|
||||||
public static ConfigEntry<Vector4> LastWindowRect;
|
public static ConfigEntry<Vector4> LastWindowRect;
|
||||||
private static Harmony _gamePatch;
|
private static Harmony _gamePatch;
|
||||||
@@ -23,9 +27,11 @@ public static class GamePatch
|
|||||||
{
|
{
|
||||||
EnableWindowResizeEnabled.SettingChanged += (_, _) => EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
|
EnableWindowResizeEnabled.SettingChanged += (_, _) => EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
|
||||||
LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
|
LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
|
||||||
|
AutoSaveOptEnabled.SettingChanged += (_, _) => AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
|
||||||
ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
|
ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
|
||||||
EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
|
EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
|
||||||
LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
|
LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
|
||||||
|
AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
|
||||||
ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
|
ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
|
||||||
_gamePatch ??= Harmony.CreateAndPatchAll(typeof(GamePatch));
|
_gamePatch ??= Harmony.CreateAndPatchAll(typeof(GamePatch));
|
||||||
}
|
}
|
||||||
@@ -34,6 +40,7 @@ public static class GamePatch
|
|||||||
{
|
{
|
||||||
LoadLastWindowRect.Enable(false);
|
LoadLastWindowRect.Enable(false);
|
||||||
EnableWindowResize.Enable(false);
|
EnableWindowResize.Enable(false);
|
||||||
|
AutoSaveOpt.Enable(false);
|
||||||
ConvertSavesFromPeace.Enable(false);
|
ConvertSavesFromPeace.Enable(false);
|
||||||
_gamePatch?.UnpatchSelf();
|
_gamePatch?.UnpatchSelf();
|
||||||
_gamePatch = null;
|
_gamePatch = null;
|
||||||
@@ -178,6 +185,148 @@ public static class GamePatch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class AutoSaveOpt
|
||||||
|
{
|
||||||
|
private static Harmony _patch;
|
||||||
|
|
||||||
|
public static void Enable(bool on)
|
||||||
|
{
|
||||||
|
if (on)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(GameConfig.gameSaveFolder + "AutoSaves/");
|
||||||
|
_patch ??= Harmony.CreateAndPatchAll(typeof(AutoSaveOpt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_patch?.UnpatchSelf();
|
||||||
|
_patch = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSave))]
|
||||||
|
private static bool GameSave_AutoSave_Prefix(ref bool __result)
|
||||||
|
{
|
||||||
|
if (!GameSave.SaveCurrentGame(GameSave.AutoSaveTmp))
|
||||||
|
{
|
||||||
|
GlobalObject.SaveOpCounter();
|
||||||
|
__result = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpFilename = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + GameSave.saveExt;
|
||||||
|
var targetFilename = $"{GameConfig.gameSaveFolder}AutoSaves/[{GameMain.data.gameDesc.clusterString}] {DateTime.Now:yyyy-MM-dd_hh-mm-ss}{GameSave.saveExt}";
|
||||||
|
File.Move(tmpFilename, targetFilename);
|
||||||
|
__result = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.RefreshList))]
|
||||||
|
public static void UILoadGameWindow_RefreshList_Postfix(UILoadGameWindow __instance)
|
||||||
|
{
|
||||||
|
var baseDir = GameConfig.gameSaveFolder + "AutoSaves/";
|
||||||
|
var files = Directory.GetFiles(baseDir, "*" + GameSave.saveExt, SearchOption.TopDirectoryOnly);
|
||||||
|
var entries = __instance.entries;
|
||||||
|
var entries2 = new List<UIGameSaveEntry>();
|
||||||
|
var entryPrefab = __instance.entryPrefab;
|
||||||
|
var entryPrefabParent = entryPrefab.transform.parent;
|
||||||
|
foreach (var f in files)
|
||||||
|
{
|
||||||
|
var fileInfo = new FileInfo(f);
|
||||||
|
var entry = Object.Instantiate(entryPrefab, entryPrefabParent);
|
||||||
|
entry.fileInfo = fileInfo;
|
||||||
|
entries2.Add(entry);
|
||||||
|
}
|
||||||
|
entries2.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
|
||||||
|
if (entries2.Count > 10)
|
||||||
|
entries2.RemoveRange(10, entries2.Count - 10);
|
||||||
|
var autoSaveText = ">> " + "自动存档条目".Translate();
|
||||||
|
foreach (var entry in entries2)
|
||||||
|
{
|
||||||
|
entry.indexText.text = "";
|
||||||
|
var saveName = entry.saveName;
|
||||||
|
entry._saveName = $"AutoSaves/{saveName}";
|
||||||
|
var quoteIndex = saveName.IndexOf('[');
|
||||||
|
if (quoteIndex >= 0)
|
||||||
|
{
|
||||||
|
var quoteIndex2 = saveName.IndexOf(']', quoteIndex + 1);
|
||||||
|
if (quoteIndex2 > 0) saveName = saveName.Substring(quoteIndex, quoteIndex2 + 1 - quoteIndex);
|
||||||
|
}
|
||||||
|
entry.nameText.text = $"{autoSaveText} {saveName}";
|
||||||
|
entry.nameText.fontStyle = FontStyle.Italic;
|
||||||
|
entry.nameText.color = new Color(1f, 1f, 1f, 0.7f);
|
||||||
|
entry.timeText.text = $"{entry.fileDate:yyyy-MM-dd HH:mm:ss}";
|
||||||
|
GameSave.ReadModes(entry.fileInfo.FullName, out var isSandbox, out var isPeace);
|
||||||
|
if (entry.sandboxIcon != null)
|
||||||
|
{
|
||||||
|
entry.sandboxIcon.gameObject.SetActive(isSandbox);
|
||||||
|
}
|
||||||
|
if (entry.combatIcon != null)
|
||||||
|
{
|
||||||
|
entry.combatIcon.gameObject.SetActive(!isPeace);
|
||||||
|
}
|
||||||
|
entry.selected = false;
|
||||||
|
entry.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
entries.AddRange(entries2);
|
||||||
|
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
|
||||||
|
var displayIndex = 1;
|
||||||
|
for (var i = 0; i < entries.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = entries[i];
|
||||||
|
entry.index = i + 1;
|
||||||
|
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
|
||||||
|
if (string.IsNullOrEmpty(entry.indexText.text)) continue;
|
||||||
|
entry.indexText.text = displayIndex.ToString();
|
||||||
|
displayIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.RefreshList))]
|
||||||
|
public static void UISaveGameWindow_RefreshList_Postfix(UISaveGameWindow __instance)
|
||||||
|
{
|
||||||
|
var entries = __instance.entries;
|
||||||
|
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
|
||||||
|
for (var i = 0; i < entries.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = entries[i];
|
||||||
|
entry.index = i + 1;
|
||||||
|
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
|
||||||
|
entry.indexText.text = (i + 1).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.DoLoadSelectedGame))]
|
||||||
|
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.OnSelectedChange))]
|
||||||
|
private static IEnumerable<CodeInstruction> UILoadGameWindow_ReplaceSaveName_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.Start().MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry.saveName)))
|
||||||
|
);
|
||||||
|
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldfld, AccessTools.Field(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry._saveName))));
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadCurrentGame))]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadGameDesc))]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeader))]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeaderAndDescAndProperty))]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveExist))]
|
||||||
|
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SavePath))]
|
||||||
|
private static IEnumerable<CodeInstruction> GameSave_RemoveValidateOnLoad_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.Start().MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(CommonUtils), nameof(CommonUtils.ValidFileName)))
|
||||||
|
);
|
||||||
|
matcher.RemoveInstruction();
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ConvertSavesFromPeace
|
private static class ConvertSavesFromPeace
|
||||||
{
|
{
|
||||||
private static Harmony _patch;
|
private static Harmony _patch;
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#### 一些提升用户体验的功能和补丁
|
#### 一些提升用户体验的功能和补丁
|
||||||
|
|
||||||
## Changlog
|
## Changlog
|
||||||
|
* 1.0.9
|
||||||
|
+ New function: `Better auto-save mechanism`
|
||||||
|
- Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time
|
||||||
|
- Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore
|
||||||
* 1.0.8
|
* 1.0.8
|
||||||
+ New function: `Enhanced control for logistic storage limits`
|
+ New function: `Enhanced control for logistic storage limits`
|
||||||
* 1.0.7
|
* 1.0.7
|
||||||
@@ -47,6 +51,9 @@
|
|||||||
+ General
|
+ General
|
||||||
- Enable game window resize
|
- Enable game window resize
|
||||||
- Remember window position and size on last exit
|
- Remember window position and size on last exit
|
||||||
|
- Better auto-save mechanism
|
||||||
|
- Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time
|
||||||
|
- Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore
|
||||||
- Convert Peace-Mode saves to Combat-Mode on loading
|
- Convert Peace-Mode saves to Combat-Mode on loading
|
||||||
+ Planet/Factory
|
+ Planet/Factory
|
||||||
- Unlimited interactive range
|
- Unlimited interactive range
|
||||||
@@ -81,6 +88,10 @@
|
|||||||
* [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): Off-grid building & stepped rotation implementations
|
* [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): Off-grid building & stepped rotation implementations
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
* 1.0.9
|
||||||
|
+ 新功能:`更好的自动保存机制`
|
||||||
|
- 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中
|
||||||
|
- 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了
|
||||||
* 1.0.8
|
* 1.0.8
|
||||||
+ 新功能:`物流塔存储数量限制控制改进`
|
+ 新功能:`物流塔存储数量限制控制改进`
|
||||||
* 1.0.7
|
* 1.0.7
|
||||||
@@ -124,6 +135,9 @@
|
|||||||
+ 通用
|
+ 通用
|
||||||
- 可调整游戏窗口大小(可最大化和拖动边框)
|
- 可调整游戏窗口大小(可最大化和拖动边框)
|
||||||
- 记住上次退出时的窗口位置和大小
|
- 记住上次退出时的窗口位置和大小
|
||||||
|
- 更好的自动保存机制
|
||||||
|
- 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中
|
||||||
|
- 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了
|
||||||
- 在加载和平模式存档时将其转换为战斗模式
|
- 在加载和平模式存档时将其转换为战斗模式
|
||||||
+ 行星/工厂
|
+ 行星/工厂
|
||||||
- 无限交互距离
|
- 无限交互距离
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ public static class UIConfigWindow
|
|||||||
I18N.Add("Dyson Sphere", "Dyson Sphere", "戴森球");
|
I18N.Add("Dyson Sphere", "Dyson Sphere", "戴森球");
|
||||||
I18N.Add("Enable game window resize", "Enable game window resize (maximum box and thick frame)", "可调整游戏窗口大小(可最大化和拖动边框)");
|
I18N.Add("Enable game window resize", "Enable game window resize (maximum box and thick frame)", "可调整游戏窗口大小(可最大化和拖动边框)");
|
||||||
I18N.Add("Remeber window position and size on last exit", "Remeber window position and size on last exit", "记住上次退出时的窗口位置和大小");
|
I18N.Add("Remeber window position and size on last exit", "Remeber window position and size on last exit", "记住上次退出时的窗口位置和大小");
|
||||||
|
I18N.Add("Better auto-save mechanism", "Better auto-save mechanism", "更好的自动存档机制");
|
||||||
|
I18N.Add("Better auto-save mechanism tips", "Auto saves are stored in 'Save\\AutoSaves' folder, filenames are combined with cluster address and date-time", "自动存档会以星区地址和日期时间组合为文件名存储在'Save\\AutoSaves'文件夹中");
|
||||||
I18N.Add("Convert old saves to Combat Mode on loading", "Convert old saves to Combat Mode on loading (Use settings in new game panel)", "读取旧档时转为战斗模式(使用新游戏面板的战斗难度设置)");
|
I18N.Add("Convert old saves to Combat Mode on loading", "Convert old saves to Combat Mode on loading (Use settings in new game panel)", "读取旧档时转为战斗模式(使用新游戏面板的战斗难度设置)");
|
||||||
I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离");
|
I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离");
|
||||||
I18N.Add("Night Light", "Sunlight at night", "夜间日光灯");
|
I18N.Add("Night Light", "Sunlight at night", "夜间日光灯");
|
||||||
@@ -61,6 +63,12 @@ public static class UIConfigWindow
|
|||||||
y += 36f;
|
y += 36f;
|
||||||
MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.LoadLastWindowRectEnabled, "Remeber window position and size on last exit");
|
MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.LoadLastWindowRectEnabled, "Remeber window position and size on last exit");
|
||||||
y += 36f;
|
y += 36f;
|
||||||
|
MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.AutoSaveOptEnabled, "Better auto-save mechanism");
|
||||||
|
x = 200f;
|
||||||
|
y += 6f;
|
||||||
|
MyWindow.AddTipsButton(x, y, tab1, "Better auto-save mechanism", "Better auto-save mechanism tips", "auto-save-opt-tips");
|
||||||
|
x = 0f;
|
||||||
|
y += 30f;
|
||||||
MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.ConvertSavesFromPeaceEnabled, "Convert old saves to Combat Mode on loading");
|
MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.ConvertSavesFromPeaceEnabled, "Convert old saves to Combat Mode on loading");
|
||||||
x += 10f;
|
x += 10f;
|
||||||
y = 278f;
|
y = 278f;
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public class UXAssist : BaseUnityPlugin
|
|||||||
"Load last window position and size when game starts");
|
"Load last window position and size when game starts");
|
||||||
GamePatch.LastWindowRect = Config.Bind("Game", "LastWindowRect", new Vector4(0f, 0f, 0f, 0f),
|
GamePatch.LastWindowRect = Config.Bind("Game", "LastWindowRect", new Vector4(0f, 0f, 0f, 0f),
|
||||||
"Last window position and size");
|
"Last window position and size");
|
||||||
|
GamePatch.AutoSaveOptEnabled = Config.Bind("Game", "AutoSaveOpt", false,
|
||||||
|
"Better auto-save mechanism");
|
||||||
GamePatch.ConvertSavesFromPeaceEnabled = Config.Bind("Game", "ConvertSavesFromPeace", false,
|
GamePatch.ConvertSavesFromPeaceEnabled = Config.Bind("Game", "ConvertSavesFromPeace", false,
|
||||||
"Convert saves from Peace mode to Combat mode on save loading");
|
"Convert saves from Peace mode to Combat mode on save loading");
|
||||||
FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Factory", "UnlimitInteractive", false,
|
FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Factory", "UnlimitInteractive", false,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net472</TargetFramework>
|
<TargetFramework>net472</TargetFramework>
|
||||||
<BepInExPluginGuid>org.soardev.uxassist</BepInExPluginGuid>
|
<BepInExPluginGuid>org.soardev.uxassist</BepInExPluginGuid>
|
||||||
<Description>DSP MOD - UXAssist</Description>
|
<Description>DSP MOD - UXAssist</Description>
|
||||||
<Version>1.0.8</Version>
|
<Version>1.0.9</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<PackageId>UXAssist</PackageId>
|
<PackageId>UXAssist</PackageId>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "UXAssist",
|
"name": "UXAssist",
|
||||||
"version_number": "1.0.8",
|
"version_number": "1.0.9",
|
||||||
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/UXAssist",
|
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/UXAssist",
|
||||||
"description": "Some functions and patches for better user experience / 一些提升用户体验的功能和补丁",
|
"description": "Some functions and patches for better user experience / 一些提升用户体验的功能和补丁",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|||||||
Reference in New Issue
Block a user