From be1733e16b1ca79d593838d7adff1ef13da50413 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:00:30 +0800 Subject: [PATCH 01/16] Add AutoPause project file for mod configuration --- Mods/AutoPause/AutoPause.csproj | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Mods/AutoPause/AutoPause.csproj diff --git a/Mods/AutoPause/AutoPause.csproj b/Mods/AutoPause/AutoPause.csproj new file mode 100644 index 0000000..d58c2e0 --- /dev/null +++ b/Mods/AutoPause/AutoPause.csproj @@ -0,0 +1,12 @@ + + + AutoPause + AutoPause + AnyCPU + net6.0 latest + + + + + + From 3333f7b067afcae68e91d2d8a070977ea1fd98d7 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:03:05 +0800 Subject: [PATCH 02/16] Add manifest.json for AutoPause mod --- Mods/AutoPause/manifest.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Mods/AutoPause/manifest.json diff --git a/Mods/AutoPause/manifest.json b/Mods/AutoPause/manifest.json new file mode 100644 index 0000000..b065eb3 --- /dev/null +++ b/Mods/AutoPause/manifest.json @@ -0,0 +1,10 @@ +{ + "Name": "AutoPause", + "Author": "i-Eureka", + "Version": "1.0.0", + "Description": "当客机打开界面时自动发送暂停命令", + "UniqueID": "i-Eureka.AutoPause", + "EntryDll": "AutoPause.dll", + "MinimumApiVersion": "4.0.0", + "UpdateKeys": [] +} From ad63977469dc818941948576395a6dcc6e799a71 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:03:41 +0800 Subject: [PATCH 03/16] Implement AutoPause mod entry with menu event handling --- Mods/AutoPause/ModEntry.cs | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Mods/AutoPause/ModEntry.cs diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs new file mode 100644 index 0000000..373c446 --- /dev/null +++ b/Mods/AutoPause/ModEntry.cs @@ -0,0 +1,53 @@ +using System; +using StardewModdingAPI; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.Menus; + +namespace AutoPause +{ + public class ModEntry : Mod + { + private bool _isPausedByMod = false; + + public override void Entry(IModHelper helper) + { + // 监听菜单状态变化 + helper.Events.Display.MenuChanged += OnMenuChanged; + } + + private void OnMenuChanged(object sender, MenuChangedEventArgs e) + { + // 只有在联机模式且自己是客机 (Farmhand) 时才生效 + if (!Context.IsMultiplayer || Context.IsMainPlayer) + return; + + // e.NewMenu 是新打开的菜单,e.OldMenu 是关掉的菜单 + bool hasNewMenu = e.NewMenu != null; + + if (hasNewMenu && !_isPausedByMod) + { + // 打开了菜单(如背包、箱子、对话、商店等) + SendPauseCommand("打开界面,请求暂停"); + _isPausedByMod = true; + } + else if (!hasNewMenu && _isPausedByMod) + { + // 关闭了所有菜单回到游戏界面 + SendPauseCommand("关闭界面,恢复游戏"); + _isPausedByMod = false; + } + } + + private void SendPauseCommand(string reason) + { + // 在星露谷中,直接让角色在聊天栏输入指令 + // !cmd>alos.pause 是 ALOS 插件的专用指令 + Game1.chatBox?.activate(); + Game1.chatBox?.setText("!cmd>alos.pause"); + Game1.chatBox?.chatMessage(); + + this.Monitor.Log($"[AutoPause] {reason}: 已自动发送 !cmd>alos.pause", LogLevel.Info); + } + } +} From f003609fe1c7c7aa1cb69e5e024263e6a2c8d7ae Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:04:23 +0800 Subject: [PATCH 04/16] Fix typo in workflow path for manifest.json --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51b5e5c..57caf26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: - main paths: - - "mianfest.json" + - "manifest.json" - '**.cs' - '**.csproj' - '**.slnx' @@ -157,4 +157,4 @@ jobs: with: create_attestations: ${{github.ref == env.RELEASE_REF}} create_combined_zip: "Mods-package" - \ No newline at end of file + From fa72742965a924b91225a5a9362607ad4fea04c1 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:06:26 +0800 Subject: [PATCH 05/16] Add AutoPause project to the solution --- Lixeer.ValleyServer.slnx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lixeer.ValleyServer.slnx b/Lixeer.ValleyServer.slnx index 8e29a40..3d2ef7e 100644 --- a/Lixeer.ValleyServer.slnx +++ b/Lixeer.ValleyServer.slnx @@ -4,4 +4,5 @@ - \ No newline at end of file + + From 8feee1466b3e8520fcbf980ecbe3cc99f2d2d665 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:10:17 +0800 Subject: [PATCH 06/16] Refactor SendPauseCommand to use multiplayer API Replaced chat box command input with direct multiplayer communication for pause command. --- Mods/AutoPause/ModEntry.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index 373c446..721c35a 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -41,13 +41,12 @@ private void OnMenuChanged(object sender, MenuChangedEventArgs e) private void SendPauseCommand(string reason) { - // 在星露谷中,直接让角色在聊天栏输入指令 - // !cmd>alos.pause 是 ALOS 插件的专用指令 - Game1.chatBox?.activate(); - Game1.chatBox?.setText("!cmd>alos.pause"); - Game1.chatBox?.chatMessage(); - - this.Monitor.Log($"[AutoPause] {reason}: 已自动发送 !cmd>alos.pause", LogLevel.Info); + // 直接调用多玩家通信接口发送聊天/指令 + if (Context.IsMultiplayer) + { + Game1.multiplayer.sendChatMessage(ModData.ReadWrite.Common, "!cmd>alos.pause"); + this.Monitor.Log($"[AutoPause] {reason}: 通过网络包发送了暂停请求", LogLevel.Info); + } } } } From 8cad072ebe89e964b981658c432c11127e30a02e Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:12:35 +0800 Subject: [PATCH 07/16] Refactor pause command logic and comments --- Mods/AutoPause/ModEntry.cs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index 721c35a..5b04888 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -18,34 +18,47 @@ public override void Entry(IModHelper helper) private void OnMenuChanged(object sender, MenuChangedEventArgs e) { - // 只有在联机模式且自己是客机 (Farmhand) 时才生效 + // 只有在联机模式且自己是客机时才生效 if (!Context.IsMultiplayer || Context.IsMainPlayer) return; - // e.NewMenu 是新打开的菜单,e.OldMenu 是关掉的菜单 bool hasNewMenu = e.NewMenu != null; if (hasNewMenu && !_isPausedByMod) { - // 打开了菜单(如背包、箱子、对话、商店等) - SendPauseCommand("打开界面,请求暂停"); + SendPauseCommand("打开界面"); _isPausedByMod = true; } else if (!hasNewMenu && _isPausedByMod) { - // 关闭了所有菜单回到游戏界面 - SendPauseCommand("关闭界面,恢复游戏"); + SendPauseCommand("关闭界面"); _isPausedByMod = false; } } private void SendPauseCommand(string reason) { - // 直接调用多玩家通信接口发送聊天/指令 - if (Context.IsMultiplayer) + try { - Game1.multiplayer.sendChatMessage(ModData.ReadWrite.Common, "!cmd>alos.pause"); - this.Monitor.Log($"[AutoPause] {reason}: 通过网络包发送了暂停请求", LogLevel.Info); + if (Game1.chatBox != null) + { + // 使用 SMAPI 的反射功能强制调用私有方法/设置私有属性 + // 设置聊天文本 + this.Helper.Reflection.GetMethod(Game1.chatBox, "setText").Invoke("!cmd>alos.pause"); + + // 模拟按下回车提交聊天 + // 在 1.6 版本中,textBoxEnter 接受一个 TextBox 参数 + var textBox = this.Helper.Reflection.GetField(Game1.chatBox, "chatBox").GetValue(); + if (textBox != null) + { + this.Helper.Reflection.GetMethod(Game1.chatBox, "textBoxEnter").Invoke(textBox); + this.Monitor.Log($"[AutoPause] {reason}: 已通过反射发送 !cmd>alos.pause", LogLevel.Info); + } + } + } + catch (Exception ex) + { + this.Monitor.Log($"[AutoPause] 发送指令失败: {ex.Message}", LogLevel.Error); } } } From f7d4da342d3a6684b7e6bdbaedf8713b1cd8929e Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:24:38 +0800 Subject: [PATCH 08/16] Create ModConfig class for AutoPause settings Add ModConfig class with default configuration properties --- Mods/AutoPause/ModConfig.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Mods/AutoPause/ModConfig.cs diff --git a/Mods/AutoPause/ModConfig.cs b/Mods/AutoPause/ModConfig.cs new file mode 100644 index 0000000..210f72c --- /dev/null +++ b/Mods/AutoPause/ModConfig.cs @@ -0,0 +1,11 @@ +namespace AutoPause +{ + public class ModConfig + { + // 默认配置,第一次运行后可在生成的 config.json 中修改 + public string ServerIP { get; set; } = "127.0.0.1"; + public int ServerPort { get; set; } = 29103; + public string AccessToken { get; set; } = "在这里填入你的Token"; + public string Command { get; set; } = "alos.pause"; + } +} From 4c3e762aa6b80c2443200a9b17df2f449a916336 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:25:09 +0800 Subject: [PATCH 09/16] Enhance mod functionality with HTTP commands Refactor pause command handling and add HTTP client for web commands. --- Mods/AutoPause/ModEntry.cs | 58 +++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index 5b04888..4fbeead 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -1,64 +1,76 @@ using System; +using System.Net.Http; using StardewModdingAPI; using StardewModdingAPI.Events; using StardewValley; -using StardewValley.Menus; namespace AutoPause { public class ModEntry : Mod { + private ModConfig Config; + private static readonly HttpClient httpClient = new HttpClient(); private bool _isPausedByMod = false; public override void Entry(IModHelper helper) { - // 监听菜单状态变化 + // 加载配置 + this.Config = helper.ReadConfig(); + + // 监听菜单变化 helper.Events.Display.MenuChanged += OnMenuChanged; + + this.Monitor.Log($"AutoPause (WebUI版) 已启动。目标服务器: {Config.ServerIP}:{Config.ServerPort}", LogLevel.Info); } private void OnMenuChanged(object sender, MenuChangedEventArgs e) { - // 只有在联机模式且自己是客机时才生效 + // 仅在客机模式生效 if (!Context.IsMultiplayer || Context.IsMainPlayer) return; - bool hasNewMenu = e.NewMenu != null; + bool hasMenu = e.NewMenu != null; - if (hasNewMenu && !_isPausedByMod) + // 逻辑:打开菜单时暂停,关闭菜单时恢复(再次发送命令) + if (hasMenu && !_isPausedByMod) { - SendPauseCommand("打开界面"); + TriggerWebCommand("打开菜单,请求暂停"); _isPausedByMod = true; } - else if (!hasNewMenu && _isPausedByMod) + else if (!hasMenu && _isPausedByMod) { - SendPauseCommand("关闭界面"); + TriggerWebCommand("关闭菜单,请求恢复"); _isPausedByMod = false; } } - private void SendPauseCommand(string reason) + private async void TriggerWebCommand(string reason) { try { - if (Game1.chatBox != null) + // 构建请求 URL (根据 CommandWebUI 的通用格式) + // 格式通常为: http://IP:Port/api/execute?token=TOKEN&cmd=COMMAND + string url = $"http://{Config.ServerIP}:{Config.ServerPort}/api/execute" + + $"?token={Config.AccessToken}" + + $"&cmd={Uri.EscapeDataString(Config.Command)}"; + + this.Monitor.Log($"[AutoPause] {reason}: 正在发送请求...", LogLevel.Debug); + + // 发送异步 GET 请求 + var response = await httpClient.GetAsync(url); + + if (response.IsSuccessStatusCode) + { + this.Monitor.Log($"[AutoPause] 指令发送成功: {Config.Command}", LogLevel.Info); + } + else { - // 使用 SMAPI 的反射功能强制调用私有方法/设置私有属性 - // 设置聊天文本 - this.Helper.Reflection.GetMethod(Game1.chatBox, "setText").Invoke("!cmd>alos.pause"); - - // 模拟按下回车提交聊天 - // 在 1.6 版本中,textBoxEnter 接受一个 TextBox 参数 - var textBox = this.Helper.Reflection.GetField(Game1.chatBox, "chatBox").GetValue(); - if (textBox != null) - { - this.Helper.Reflection.GetMethod(Game1.chatBox, "textBoxEnter").Invoke(textBox); - this.Monitor.Log($"[AutoPause] {reason}: 已通过反射发送 !cmd>alos.pause", LogLevel.Info); - } + this.Monitor.Log($"[AutoPause] 发送失败! HTTP状态码: {response.StatusCode}", LogLevel.Warn); } } catch (Exception ex) { - this.Monitor.Log($"[AutoPause] 发送指令失败: {ex.Message}", LogLevel.Error); + this.Monitor.Log($"[AutoPause] 网络请求异常: {ex.Message}", LogLevel.Error); } } } From 5ee8ae2633946fafacac16892561fa186e9ba740 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:25:31 +0800 Subject: [PATCH 10/16] Fix formatting in AutoPause.csproj file --- Mods/AutoPause/AutoPause.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mods/AutoPause/AutoPause.csproj b/Mods/AutoPause/AutoPause.csproj index d58c2e0..2f60133 100644 --- a/Mods/AutoPause/AutoPause.csproj +++ b/Mods/AutoPause/AutoPause.csproj @@ -3,7 +3,8 @@ AutoPause AutoPause AnyCPU - net6.0 latest + net6.0 + latest From 431a75b01c197d50532fe421573889934fe99b62 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:37:23 +0800 Subject: [PATCH 11/16] Refactor AutoPause mod entry and command handling --- Mods/AutoPause/ModEntry.cs | 59 +++++++++++++++----------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index 4fbeead..d7dd5b4 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -14,64 +14,51 @@ public class ModEntry : Mod public override void Entry(IModHelper helper) { - // 加载配置 + // 加载配置,如果不存在则按默认创建 this.Config = helper.ReadConfig(); - - // 监听菜单变化 + helper.WriteConfig(this.Config); + helper.Events.Display.MenuChanged += OnMenuChanged; - - this.Monitor.Log($"AutoPause (WebUI版) 已启动。目标服务器: {Config.ServerIP}:{Config.ServerPort}", LogLevel.Info); + this.Monitor.Log("AutoPause WebUI版已就绪。如果还报Ambiguous match,请检查是否删除了旧版DLL!", LogLevel.Info); } private void OnMenuChanged(object sender, MenuChangedEventArgs e) { - // 仅在客机模式生效 - if (!Context.IsMultiplayer || Context.IsMainPlayer) - return; - - bool hasMenu = e.NewMenu != null; + if (!Context.IsMultiplayer || Context.IsMainPlayer) return; - // 逻辑:打开菜单时暂停,关闭菜单时恢复(再次发送命令) - if (hasMenu && !_isPausedByMod) + if (e.NewMenu != null && !_isPausedByMod) { - TriggerWebCommand("打开菜单,请求暂停"); + _ = TriggerWebCommand("暂停"); _isPausedByMod = true; } - else if (!hasMenu && _isPausedByMod) + else if (e.NewMenu == null && _isPausedByMod) { - TriggerWebCommand("关闭菜单,请求恢复"); + _ = TriggerWebCommand("恢复"); _isPausedByMod = false; } } - private async void TriggerWebCommand(string reason) + private async System.Threading.Tasks.Task TriggerWebCommand(string action) { try { - // 构建请求 URL (根据 CommandWebUI 的通用格式) - // 格式通常为: http://IP:Port/api/execute?token=TOKEN&cmd=COMMAND - string url = $"http://{Config.ServerIP}:{Config.ServerPort}/api/execute" + - $"?token={Config.AccessToken}" + - $"&cmd={Uri.EscapeDataString(Config.Command)}"; - - this.Monitor.Log($"[AutoPause] {reason}: 正在发送请求...", LogLevel.Debug); - - // 发送异步 GET 请求 - var response = await httpClient.GetAsync(url); - - if (response.IsSuccessStatusCode) - { - this.Monitor.Log($"[AutoPause] 指令发送成功: {Config.Command}", LogLevel.Info); - } - else - { - this.Monitor.Log($"[AutoPause] 发送失败! HTTP状态码: {response.StatusCode}", LogLevel.Warn); - } + // 注意:这里完全不使用反射,只发网络请求 + string url = $"http://{Config.ServerIP}:{Config.ServerPort}/api/execute?token={Config.AccessToken}&cmd={Config.Command}"; + await httpClient.GetAsync(url); + this.Monitor.Log($"[AutoPause] 触发{action}成功", LogLevel.Info); } catch (Exception ex) { - this.Monitor.Log($"[AutoPause] 网络请求异常: {ex.Message}", LogLevel.Error); + this.Monitor.Log($"[AutoPause] 网络请求失败: {ex.Message}", LogLevel.Error); } } } + + public class ModConfig + { + public string ServerIP { get; set; } = "127.0.0.1"; + public int ServerPort { get; set; } = 38080; + public string AccessToken { get; set; } = "YourTokenHere"; + public string Command { get; set; } = "alos.pause"; + } } From 68202ea0ea1b9ae13ce2f3a62ba29c3eaa98a121 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:42:34 +0800 Subject: [PATCH 12/16] Refactor AutoPause mod entry and command handling --- Mods/AutoPause/ModEntry.cs | 56 +++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index d7dd5b4..fc315ac 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Threading.Tasks; using StardewModdingAPI; using StardewModdingAPI.Events; using StardewValley; @@ -14,38 +15,63 @@ public class ModEntry : Mod public override void Entry(IModHelper helper) { - // 加载配置,如果不存在则按默认创建 + // 加载配置 this.Config = helper.ReadConfig(); + + // 确保生成 config.json helper.WriteConfig(this.Config); - helper.Events.Display.MenuChanged += OnMenuChanged; - this.Monitor.Log("AutoPause WebUI版已就绪。如果还报Ambiguous match,请检查是否删除了旧版DLL!", LogLevel.Info); + // 注册菜单变更事件 + helper.Events.Display.MenuChanged += this.OnMenuChanged; + + this.Monitor.Log("AutoPause (WebUI版) 已启动,正在监听菜单状态。", LogLevel.Info); } private void OnMenuChanged(object sender, MenuChangedEventArgs e) { - if (!Context.IsMultiplayer || Context.IsMainPlayer) return; + // 只有客机且在联机模式下才触发 + if (!Context.IsMultiplayer || Context.IsMainPlayer) + return; + // e.NewMenu 不为空表示打开了新界面 if (e.NewMenu != null && !_isPausedByMod) { - _ = TriggerWebCommand("暂停"); + _ = this.TriggerWebCommand("暂停 (Open Menu)"); _isPausedByMod = true; } + // e.NewMenu 为空表示回到了游戏主画面 else if (e.NewMenu == null && _isPausedByMod) { - _ = TriggerWebCommand("恢复"); + _ = this.TriggerWebCommand("恢复 (Close Menu)"); _isPausedByMod = false; } } - private async System.Threading.Tasks.Task TriggerWebCommand(string action) + private async Task TriggerWebCommand(string action) { + if (string.IsNullOrEmpty(this.Config.AccessToken) || this.Config.AccessToken.Contains("填入")) + { + this.Monitor.Log("未配置 Token,已跳过指令发送。", LogLevel.Warn); + return; + } + try { - // 注意:这里完全不使用反射,只发网络请求 - string url = $"http://{Config.ServerIP}:{Config.ServerPort}/api/execute?token={Config.AccessToken}&cmd={Config.Command}"; - await httpClient.GetAsync(url); - this.Monitor.Log($"[AutoPause] 触发{action}成功", LogLevel.Info); + // 构建接口 URL + string url = $"http://{this.Config.ServerIP}:{this.Config.ServerPort}/api/execute" + + $"?token={this.Config.AccessToken}" + + $"&cmd={Uri.EscapeDataString(this.Config.Command)}"; + + var response = await httpClient.GetAsync(url); + + if (response.IsSuccessStatusCode) + { + this.Monitor.Log($"[AutoPause] {action} 指令发送成功", LogLevel.Info); + } + else + { + this.Monitor.Log($"[AutoPause] 接口返回错误: {response.StatusCode}", LogLevel.Warn); + } } catch (Exception ex) { @@ -53,12 +79,4 @@ private async System.Threading.Tasks.Task TriggerWebCommand(string action) } } } - - public class ModConfig - { - public string ServerIP { get; set; } = "127.0.0.1"; - public int ServerPort { get; set; } = 38080; - public string AccessToken { get; set; } = "YourTokenHere"; - public string Command { get; set; } = "alos.pause"; - } } From 9f8dad321056e8d6896e54254f872670ee6c7a7b Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:56:02 +0800 Subject: [PATCH 13/16] =?UTF-8?q?Refactor=20AutoPause=20mod=20to=20use=20W?= =?UTF-8?q?ebSocket=20instead=20of=20HTTP=20=E2=80=9D=E4=BE=9D=E8=B5=96Com?= =?UTF-8?q?mandWebUI=E2=80=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mods/AutoPause/ModEntry.cs | 62 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/Mods/AutoPause/ModEntry.cs b/Mods/AutoPause/ModEntry.cs index fc315ac..f7f9951 100644 --- a/Mods/AutoPause/ModEntry.cs +++ b/Mods/AutoPause/ModEntry.cs @@ -1,5 +1,7 @@ using System; -using System.Net.Http; +using System.Net.WebSockets; +using System.Text; +using System.Threading; using System.Threading.Tasks; using StardewModdingAPI; using StardewModdingAPI.Events; @@ -10,73 +12,67 @@ namespace AutoPause public class ModEntry : Mod { private ModConfig Config; - private static readonly HttpClient httpClient = new HttpClient(); private bool _isPausedByMod = false; public override void Entry(IModHelper helper) { - // 加载配置 + // 加载配置并确保生成文件 this.Config = helper.ReadConfig(); - - // 确保生成 config.json helper.WriteConfig(this.Config); - // 注册菜单变更事件 + // 监听菜单变化 helper.Events.Display.MenuChanged += this.OnMenuChanged; - this.Monitor.Log("AutoPause (WebUI版) 已启动,正在监听菜单状态。", LogLevel.Info); + this.Monitor.Log($"AutoPause (WebSocket版) 已启动。目标: ws://{this.Config.ServerIP}:{this.Config.ServerPort}/ws", LogLevel.Info); } private void OnMenuChanged(object sender, MenuChangedEventArgs e) { - // 只有客机且在联机模式下才触发 if (!Context.IsMultiplayer || Context.IsMainPlayer) return; - // e.NewMenu 不为空表示打开了新界面 if (e.NewMenu != null && !_isPausedByMod) { - _ = this.TriggerWebCommand("暂停 (Open Menu)"); + _ = this.TriggerWebSocketCommand("暂停"); _isPausedByMod = true; } - // e.NewMenu 为空表示回到了游戏主画面 else if (e.NewMenu == null && _isPausedByMod) { - _ = this.TriggerWebCommand("恢复 (Close Menu)"); + _ = this.TriggerWebSocketCommand("恢复"); _isPausedByMod = false; } } - private async Task TriggerWebCommand(string action) + private async Task TriggerWebSocketCommand(string action) { - if (string.IsNullOrEmpty(this.Config.AccessToken) || this.Config.AccessToken.Contains("填入")) + // 使用 using 确保 WebSocket 用完后正确释放 + using (var ws = new ClientWebSocket()) { - this.Monitor.Log("未配置 Token,已跳过指令发送。", LogLevel.Warn); - return; - } + try + { + // CommandWebUI 的 WebSocket 地址固定为 /ws + Uri serverUri = new Uri($"ws://{this.Config.ServerIP}:{this.Config.ServerPort}/ws"); + + // 1. 建立连接 + await ws.ConnectAsync(serverUri, CancellationToken.None); - try - { - // 构建接口 URL - string url = $"http://{this.Config.ServerIP}:{this.Config.ServerPort}/api/execute" + - $"?token={this.Config.AccessToken}" + - $"&cmd={Uri.EscapeDataString(this.Config.Command)}"; + // 2. 将命令转换为字节流 + // 注意:因为是直接推送到控制台 Reader.PushInput,如果需要前缀请在 config 中配置好,比如 "!cmd>alos.pause" 或 "alos.pause" + byte[] commandBytes = Encoding.UTF8.GetBytes(this.Config.Command); + ArraySegment bytesToSend = new ArraySegment(commandBytes); - var response = await httpClient.GetAsync(url); + // 3. 发送命令 + await ws.SendAsync(bytesToSend, WebSocketMessageType.Text, true, CancellationToken.None); + this.Monitor.Log($"[AutoPause] {action} 指令 ({this.Config.Command}) 已通过 WebSocket 发送成功", LogLevel.Info); - if (response.IsSuccessStatusCode) - { - this.Monitor.Log($"[AutoPause] {action} 指令发送成功", LogLevel.Info); + // 4. 正常关闭连接 + await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Command Sent", CancellationToken.None); } - else + catch (Exception ex) { - this.Monitor.Log($"[AutoPause] 接口返回错误: {response.StatusCode}", LogLevel.Warn); + this.Monitor.Log($"[AutoPause] WebSocket 发送失败: {ex.Message}", LogLevel.Error); } } - catch (Exception ex) - { - this.Monitor.Log($"[AutoPause] 网络请求失败: {ex.Message}", LogLevel.Error); - } } } } From 819f7ee874e9086f3334c8aa3f263defc3bbd5f2 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:56:55 +0800 Subject: [PATCH 14/16] Comment out AccessToken property in ModConfig --- Mods/AutoPause/ModConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mods/AutoPause/ModConfig.cs b/Mods/AutoPause/ModConfig.cs index 210f72c..a8d44fc 100644 --- a/Mods/AutoPause/ModConfig.cs +++ b/Mods/AutoPause/ModConfig.cs @@ -5,7 +5,7 @@ public class ModConfig // 默认配置,第一次运行后可在生成的 config.json 中修改 public string ServerIP { get; set; } = "127.0.0.1"; public int ServerPort { get; set; } = 29103; - public string AccessToken { get; set; } = "在这里填入你的Token"; + // public string AccessToken { get; set; } = "在这里填入你的Token"; public string Command { get; set; } = "alos.pause"; } } From ec9d89bca340b3b3850bdaf95de257dec4c9753e Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:10:09 +0800 Subject: [PATCH 15/16] Add README for AutoPause mod Added README for AutoPause mod detailing features, installation, and configuration. --- Mods/AutoPause/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Mods/AutoPause/README.md diff --git a/Mods/AutoPause/README.md b/Mods/AutoPause/README.md new file mode 100644 index 0000000..406277b --- /dev/null +++ b/Mods/AutoPause/README.md @@ -0,0 +1,36 @@ +# AutoPause (客机自动暂停增强 Mod) + +本 Mod 是针对《星露谷物语》(Stardew Valley)多人联机模式下自动暂停功能的增强扩展。主要为**客机玩家**(非房主/非服务端)提供在特定界面下的时间暂停功能,让你在整理物品或阅读剧情时不再手忙脚乱。 + +## ⚠️ 重要警告 (使用前必读) + +1. **非必选 Mod**:本 Mod 为按需使用的增强型补丁,并非基础运行的必须组件。 +2. **严重依赖 CommandWebUI**:本 Mod 本质上是一个 WebSocket 客户端,它**完全依赖**于本仓库中的 `CommandWebUI` 模组。在使用前,请务必确保服务端的 `CommandWebUI` 已正确安装、配置,并且单独测试通过(没有任何网络阻碍)。 +3. **影响联机体验**:由于星露谷的机制,客机触发暂停会导致**整个服务器的时间停止**。这必然会打断其他正在活动的玩家,严重影响多人的连贯游戏体验。**请务必与服务器内的其他小伙伴沟通一致后,谨慎使用!** + +## ⚙️ 当前阶段与功能支持 + +本 Mod 目前处于**测试阶段**,暂未覆盖游戏内的所有 UI 界面。 +目前**仅支持**在客机打开和关闭以下界面时,自动向服务器发送暂停/恢复指令: +* **菜单界面** (Menu) +* **库存/背包界面** (Inventory / Chests) +* **对话界面** (Dialogue) + +## 📦 安装说明 + +**【本 Mod 仅需安装在客机端】** 服务端**不需要**(也不建议)安装本 Mod,服务端只需正常运行 `CommandWebUI` 即可。 + +1. 下载编译好的 `AutoPause` 压缩包。 +2. 将解压后的 `AutoPause` 文件夹放入你本地游戏目录的 `Mods/` 文件夹下。 +3. 启动一次游戏,让 Mod 自动生成 `config.json` 配置文件。 + +## 🛠️ 配置指南 (`config.json`) + +第一次运行后,打开 `Mods/AutoPause/config.json`,根据你的服务器情况进行修改: + +```json +{ + "ServerIP": "127.0.0.1", // 请修改为服务端的真实 IP (公网或局域网IP) + "ServerPort": 29103, // 必须与服务端 CommandWebUI 监听的端口一致 + "Command": "alos.pause" // 触发暂停/恢复的指令,通常保持默认即可 +} From 8272bbca8e809218fa502254ebf9dcf2b6687ba3 Mon Sep 17 00:00:00 2001 From: i-Eureka <71005669+i-Eureka@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:17:03 +0800 Subject: [PATCH 16/16] Update README with AI generation and security info Add AI generation note and security warning to README. --- Mods/AutoPause/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mods/AutoPause/README.md b/Mods/AutoPause/README.md index 406277b..a8a92c9 100644 --- a/Mods/AutoPause/README.md +++ b/Mods/AutoPause/README.md @@ -7,6 +7,8 @@ 1. **非必选 Mod**:本 Mod 为按需使用的增强型补丁,并非基础运行的必须组件。 2. **严重依赖 CommandWebUI**:本 Mod 本质上是一个 WebSocket 客户端,它**完全依赖**于本仓库中的 `CommandWebUI` 模组。在使用前,请务必确保服务端的 `CommandWebUI` 已正确安装、配置,并且单独测试通过(没有任何网络阻碍)。 3. **影响联机体验**:由于星露谷的机制,客机触发暂停会导致**整个服务器的时间停止**。这必然会打断其他正在活动的玩家,严重影响多人的连贯游戏体验。**请务必与服务器内的其他小伙伴沟通一致后,谨慎使用!** +4. **AI 生成**:本 Mod 的核心代码由 AI 辅助生成并经过基础测试,但在极其复杂的网络环境或极限操作下可能仍存在未知的边界问题,请知悉。 +5. **涉及安全问题**:详见本项目CommandWebUI Mod说明 https://github.com/Lixeer/ValleyServer/blob/main/Mods/CommandWebUI/README.md ## ⚙️ 当前阶段与功能支持