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
## ⚙️ 当前阶段与功能支持