ConsoleStdinHeeler

ConsoleStdinHeeler

Valheim专用服务器的非阻塞STDIN控制台输入和原生服务器管理命令。

[size=6]ConsoleStdinHeeler[/size]


[url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/releases][img]https://img.shields.io/github/v/release/dreamwraith/Valheim-ConsoleStdinHeeler?logo=github&color=1081c2[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/releases][img]https://img.shields.io/github/downloads/dreamwraith/Valheim-ConsoleStdinHeeler/total?logo=github&color=1081c2[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/commits/main][img]https://img.shields.io/github/last-commit/dreamwraith/Valheim-ConsoleStdinHeeler?logo=git[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/actions][img]https://img.shields.io/github/actions/workflow/status/dreamwraith/Valheim-ConsoleStdinHeeler/publish.yml?label=Publish%20Portals&logo=githubactions[/img][/url] [url=https://thunderstore.io/c/valheim/p/DreamWraith/ConsoleStdinHeeler/][img]https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fthunderstore.io%2Fapi%2Fexperimental%2Fpackage%2FDreamWraith%2FConsoleStdinHeeler%2F&query=%24.latest.version_number&label=Thunderstore&logo=thunderstore&color=2980b9[/img][/url] [url=https://valheim.hexium.gg/mods/DreamWraith/ConsoleStdinHeeler][img]https://img.shields.io/badge/Hexium-ConsoleStdinHeeler-6c5ce7[/img][/url] [url=https://www.nexusmods.com/valheim/mods/4329][img]https://img.shields.io/badge/Nexus_Mods-4329-da8e35?logo=nexusmods&logoColor=white[/img][/url] [url=https://store.steampowered.com/app/892970/Valheim/][img]https://img.shields.io/badge/Valheim-Deep_North_%2F_1.x-1b2838?logo=steam&logoColor=white[/img][/url] [url=https://valheim.thunderstore.io/package/denikson/BepInExPack_Valheim/][img]https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fthunderstore.io%2Fapi%2Fexperimental%2Fpackage%2Fdenikson%2FBepInExPack_Valheim%2F&query=%24.latest.version_number&label=BepInEx&color=5B57E7[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/ConsoleStdinHeeler.csproj][img]https://img.shields.io/badge/.NET%20Framework-4.8-512BD4?logo=dotnet[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler#the-solution][img]https://img.shields.io/badge/Type-Dedicated%20Server-orange[/img][/url] [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/LICENSE.md][img]https://img.shields.io/github/license/dreamwraith/Valheim-ConsoleStdinHeeler?color=blue[/img][/url] [url=https://gist.github.com/dreamwraith/77c91d656c842611bf8c40febf8056f2][img]https://img.shields.io/badge/AI%20Philosophy-Software%20Craft-2ea44f?logo=github[/img][/url]



为 Valheim 专用服务器提供非阻塞 STDIN 控制台输入与原生服务器管理命令。



[quote]


[!NOTE]


[b]面向专用服务器:[/b] 本模组专为在交互式终端、多路复用器([b]screen[/b]、[b]tmux[/b])、systemd 服务或 Docker 容器中运行的无头 Valheim 服务器(Linux 或 Windows)设计。若在标准游戏客户端中加载,它会自动保持休眠状态。


[/quote]


[line]



[size=5]问题所在[/size]


在原版 Valheim 中,以无头模式运行的专用服务器(Windows 上的 [b]valheim_server.exe[/b] 或 Linux 上的 [b]valheim_server.x86_64[/b])不会读取标准输入([b]STDIN[/b])。虽然日志和状态消息会输出到标准输出,但服务器管理员无法在终端中输入命令,也无法将命令通过管道传入服务器进程。



此外:


[list]


[*] [b]无原生服务器控制台输入[/b]:专用服务器不处理交互式终端输入或管道流。服务器管理员若要在本地运行命令,必须安装外部 RCON 模组、通过游戏客户端管理员连接或使用远程管理工具。


[*] [b]缺少运维命令[/b]:专用服务器二进制文件缺少标准运维命令,例如 [b]shutdown[/b]、[b]stop[/b]、[b]kickall[/b] 以及倒计时计时器。停止服务器通常需要发送 [b]SIGINT[/b](Ctrl+C)或终止进程,这可能导致存档不完整。


[*] [b]主线程阻塞与 EOF 空转风险[/b]:在主线程上同步读取会阻塞 Unity 游戏循环。反之,若后台读取器未处理流关闭,则在脱离或非交互环境(例如 systemd 服务或分离的容器)下运行且 STDIN 到达 EOF 时,可能迅速进入空转循环并导致 CPU 占用飙升。


[/list]


[line]



[size=5]解决方案[/size]


[b]ConsoleStdinHeeler[/b] 实现了非阻塞后台 STDIN 监听器,配合主线程执行队列与专用服务器命令套件:


[list]


[*] [b]非阻塞后台读取器[/b]:运行专用后台线程([b]ConsoleStdinHeeler_Reader[/b])读取 [b]Console.ReadLine()[/b],不会阻塞 Unity 主线程。


[*] [b]EOF 流关闭保护[/b]:检测连续的 EOF 读取尝试,并在输入流关闭时终止读取线程,防止在脱离环境中出现 CPU 空转。


[*] [b]主线程队列调度[/b]:后台读取器的命令被加入线程安全的 [b]ConcurrentQueue<string>[/b],并在 Unity [b]Update()[/b] 周期中按顺序执行。


[*] [b]专用服务器管理套件[/b]:提供必要的服务器管理命令,包括干净保存、带倒计时计时器的优雅关闭、玩家名单诊断、管理、传送、广播、袭击和时间控制。


[*] [b]模组命令路由[/b]:将无法识别的命令路由到 Valheim 中央的 [b]Terminal.commands[/b] 字典,并赋予提升的权限([b]Terminal.m_cheat[/b]),使来自 [b]WorldEditCommands[/b]、[b]ServerDevcommands[/b] 和 [b]InfinityHammer[/b] 等模组的命令可直接从终端执行。


[*] [b]ANSI 终端转换[/b]:将 Unity 的 [b]<color=...>[/b] 富文本标记转换为终端 ANSI 颜色代码,以便控制台日志可读且格式化。


[/list]


[line]



[size=5]功能特性[/size]



[size=4]非阻塞 STDIN 架构[/size]


[list]


[*] [b]后台工作线程[/b]:在独立线程上读取 [b]Console.ReadLine()[/b]([b]Thread.IsBackground = true[/b]),保持主游戏循环不被打断。


[*] [b]EOF 流检测[/b]:受监控的读取循环会跟踪连续的 null 读取。如果 STDIN 到达流关闭(例如无交互 TTY 的 systemd,或未使用 [b]-i[/b] 的 Docker),监听器会记录该情况并停止读取线程。


[*] [b]主线程执行[/b]:命令被推送到 [b]ConcurrentQueue<string>[/b],并在 [b]Update()[/b] 期间于 Unity 主线程出队,确保与引擎和模组 API 的线程安全。


[*] [b]异常处理[/b]:单条命令的执行封装在异常处理器中,防止格式错误或失败的命令中断服务器 tick。


[/list]


[size=4]专用服务器管理[/size]


[list]


[*] [b]优雅关闭([b]shutdown[/b] / [b]stop[/b] / [b]exit[/b] / [b]quit[/b])[/b]:触发世界和玩家档案保存([b]ZNet.instance.Save[/b]),随后干净地终止服务器。支持可选的以分钟为单位的倒计时延迟及广播原因(例如 [b]stop 5 Maintenance[/b])与取消([b]stop cancel[/b])。


[*] [b]立即保存世界([b]save[/b])[/b]:立即触发世界数据和已连接玩家档案的同步保存([b]ZNet.instance.Save(saveOtherPlayerProfiles: true)[/b])。


[*] [b]玩家名单([b]players[/b] / [b]list[/b])[/b]:显示已连接玩家、角色名、IP 端点、往返延迟(ping)和内部玩家 ID。


[*] [b]玩家管理([b]kick[/b]、[b]ban[/b]、[b]unban[/b]、[b]banned[/b])[/b]:按角色名、IP、Steam ID 或 PlayFab ID 管理玩家。


[*] [b]批量断开([b]kickall[/b])[/b]:保存世界和玩家数据,显示公告横幅,并在可配置延迟(默认:3 秒)后以自定义踢出原因断开所有已连接玩家。


[/list]


[size=4]远程游戏内通知与控制[/size]


[list]


[] [b]横幅通知([b]say[/b])[/b]:通过 [b]ZRoutedRpc[/b] 向所有玩家([b]@all[/b] 或 [b][/b])或指定玩家发送左上角通知横幅。


[*] [b]居中公告([b]announce[/b] / [b]broadcast[/b] / [b]alert[/b])[/b]:向所有玩家或指定玩家显示屏幕中央公告横幅。支持通过 [b]-t <seconds>[/b] 设置可选的持续时间(最长 60 秒),每秒刷新一次且不淡出,并支持取消([b]announce cancel[/b])。


[*] [b]传送([b]tp[/b] / [b]teleport[/b])[/b]:将玩家传送到另一名玩家的位置或坐标([b]x,y,z[/b])。支持对含空格的玩家名使用引号。


[*] [b]玩家目标选择与 [b]@random[/b][/b]:支持将 [b]@random[/b] 作为玩家参数([b]tp[/b]、[b]say[/b]、[b]announce[/b]、[b]spawn[/b]、[b]event[/b])来随机选择一名已连接玩家。


[*] [b]预制体生成([b]spawn[/b])[/b]:在玩家附近或坐标处生成预制体,可选数量、星级和静默标志([b]-s[/b]),将实体安全地放置在表面。


[*] [b]袭击与事件控制([b]event[/b]、[b]stopevent[/b]、[b]randomevent[/b])[/b]:在玩家或坐标附近启动指定或随机袭击,或停止当前活动事件。


[*] [b]世界时间控制([b]tod[/b]、[b]skiptime[/b]、[b]sleep[/b])[/b]:调整一天中的时间([b]0.0[/b] 至 [b]1.0[/b])、按秒向前跳过,或推进到早晨并与客户端同步。


[/list]


[size=4]终端与模组兼容性[/size]


[list]


[*] [b]模组命令透传[/b]:无法识别的命令会被传递给 [b]Terminal.commands[/b],允许已安装的开发者和管理模组通过控制台执行。


[*] [b]作弊权限提升[/b]:在命令分发期间设置 [b]Terminal.m_cheat = true[/b],以便作弊标记命令和管理命令能在专用服务器上执行。


[*] [b]客户端命令过滤[/b]:拦截需要本地玩家角色或渲染摄像机的命令(例如 [b]god[/b]、[b]fly[/b]、[b]ghost[/b]、[b]pos[/b]、[b]beard[/b]、[b]hair[/b]、[b]fov[/b]),并给出明确警告,而不是抛出 [b]NullReferenceException[/b] 错误。


[*] [b]ANSI 颜色格式化[/b]:将 Unity 的 [b]<color=...>[/b] 标签转换为终端 ANSI 颜色,并支持可配置的显示模式([b]Ansi[/b]、[b]Strip[/b]、[b]Raw[/b])。


[*] [b]命令参考([b]help[/b])[/b]:显示专用服务器命令和自动发现的模组命令,[b]help all[/b] 可展示完整的原生命令目录。


[/list]


[line]



[size=5]可用命令[/size]



[b]命令 | 参数 | 描述[/b]


[b]save[/b] | [i](无)[/i] | 触发世界和玩家档案保存。


[b]shutdown[/b] / [b]stop[/b] / [b]exit[/b] / [b]quit[/b] | [b][min] [reason][/b] / [b]cancel[/b] | 优雅关闭(可选倒计时/取消),终止前保存世界数据。


[b]players[/b] / [b]list[/b] | [i](无)[/i] | 列出已连接玩家、角色名、IP 端点和 ping 延迟。


[b]kick[/b] | [b]<name/ip/userID>[/b] | 将玩家踢出服务器。


[b]kickall[/b] | [b][seconds] [reason][/b] | 保存世界/玩家档案,并在宽限延迟后断开所有已连接玩家。


[b]ban[/b] | [b]<name/ip/userID>[/b] | 按名称、IP 或平台用户 ID 封禁玩家。


[b]unban[/b] | [b]<name/ip/userID>[/b] | 将玩家从封禁列表中移除。


[b]banned[/b] | [i](无)[/i] | 显示当前封禁列表。


[b]say[/b] | [b]<@all/*\|player> <message>[/b] | 向所有客户端或指定玩家显示左上角通知横幅。


[b]announce[/b] / [b]broadcast[/b] / [b]alert[/b] | [b][-t sec] <@all/*\|player> <message>[/b] / [b]cancel[/b] | 显示屏幕中央公告(可选 [b]-t[/b] 持续时间最长 60 秒,或 [b]cancel[/b])。


[b]tp[/b] / [b]teleport[/b] | [b]<player> <target/x,y,z>[/b] | 将玩家传送到另一名玩家或坐标处。


[b]spawn[/b] | [b]<prefab> <player/coords> [amt] [lvl] [-s][/b] | 在玩家或坐标附近生成预制体,可指定数量、星级和可选静默模式([b]-s[/b])。


[b]event[/b] | [b]<name> <player/coords>[/b] | 在玩家或坐标附近启动袭击/事件。


[b]stopevent[/b] | [i](无)[/i] | 停止当前任何活动的袭击/事件。


[b]randomevent[/b] | [i](无)[/i] | 在玩家附近启动随机袭击/事件。


[b]tod[/b] | [b]<0-1>[/b] | 为所有已连接客户端设置一天中的时间(例如 [b]0.5[/b] 表示正午)。


[b]skiptime[/b] | [b][gameseconds][/b] | 为所有已连接客户端按秒向前跳过(默认:240)。


[b]sleep[/b] | [i](无)[/i] | 快进到次日早晨。


[b]time[/b] | [i](无)[/i] | 显示当前世界时间、天数与睡眠状态。


[b]timescale[/b] | [b][scale][/b] | 调整游戏模拟速度(默认:1.0)。


[b]ping[/b] | [i](无)[/i] | 输出服务器 ping。


[b]info[/b] | [i](无)[/i] | 输出服务器内存和系统规格。


[b]help[/b] | [i](可选:[b]all[/b])[/i] | 显示专用服务器和模组命令。使用 [b]help all[/b] 查看完整原生命令目录。


[i]模组命令[/i] | [i](视情况而定)[/i] | 已安装 BepInEx 模组注册的命令会直接执行。


[quote]


[!TIP]


[b]玩家目标选择与 [b]@random[/b]:[/b] 任何接受玩家目标的命令([b]tp[/b]、[b]say[/b]、[b]announce[/b]、[b]spawn[/b]、[b]event[/b])都支持 [b]@random[/b] 来随机选择一名已连接玩家。[b]仅客户端命令保护:[/b] 需要活动本地玩家角色的命令(例如 [b]god[/b]、[b]fly[/b]、[b]goto[/b]、[b]pos[/b]、[b]beard[/b])会被拦截并给出警告,而不会在服务器日志中抛出 [b]NullReferenceException[/b] 错误。


[/quote]


[line]



[size=5]配置[/size]


可在 [b]BepInEx/config/dreamwraith.ConsoleStdinHeeler.cfg[/b] 中自定义设置:




[b]节 | 键 | 类型 | 默认值 | 描述[/b]


[b]1 - General[/b] | [b]EnableMod[/b] | [b]bool[/b] | [b]true[/b] | 启用或禁用 STDIN 控制台监听器的主开关。


[b]1 - General[/b] | [b]EnableCheatsByDefault[/b] | [b]bool[/b] | [b]true[/b] | 允许模组和管理作弊命令在专用服务器上执行。


[b]2 - Display[/b] | [b]ConsolePrompt[/b] | [b]string[/b] | [b]> [/b] | 收到 STDIN 命令时在日志中显示的前缀。


[b]2 - Display[/b] | [b]ColorFormatting[/b] | [b]enum[/b] | [b]Ansi[/b] | 如何处理控制台输出中的 Unity 富文本颜色标签:Ansi(以终端颜色渲染)、Strip(清理为纯文本)或 Raw(保留标签不变)。


[b]9 - Debug[/b] | [b]EnableDebugLogs[/b] | [b]bool[/b] | [b]false[/b] | 启用详细诊断日志。


[line]



[size=5]服务器部署说明[/size]



[size=4]Linux [b]screen[/b] 或 [b]tmux[/b][/size]


在 [b]screen[/b] 或 [b]tmux[/b] 会话中启动 Valheim 服务器,以便交互式输入命令:


[code]screen -S valheim ./start_server_bepinex.sh


# 直接在控制台中输入 'save'、'players' 或 'shutdown'[/code]



[size=4]systemd 服务[/size]


在无交互终端([b]StandardInput=null[/b])的 systemd 下运行时,ConsoleStdinHeeler 会检测到流关闭(EOF)并干净地停止读取线程,而不会空转 CPU。要将命令传入 systemd 服务,请配置 FIFO 管道或交互式 TTY。



[size=4]Docker[/size]


在 Docker 内运行时,请分配标准输入:


[code]docker run -i -t ...


# 或附加到现有容器:


docker attach <container_id>[/code]


如果在未使用 [b]-i[/b] 的情况下以分离模式运行,读取器会检测到 EOF 并终止线程。



[line]



[size=5]兼容性[/size]


[list]


[*] [b]专用服务器[/b]:专为无头专用服务器(Windows 上的 [b]valheim_server.exe[/b] 或 Linux 上的 [b]valheim_server.x86_64[/b])构建。如果在标准游戏客户端中启动,会安全地自行禁用。


[*] [b]服务器管理与开发模组[/b]:兼容服务器管理和开发者模组,包括 [b]WorldEditCommands[/b]、[b]ServerDevcommands[/b] 和 [b]InfinityHammer[/b]。


[*] [b]原版游戏终端[/b]:透传 Valheim 原生终端和服务器实用工具([b]save[/b]、[b]ping[/b]、[b]info[/b]、[b]genloc[/b]、[b]optterrain[/b]、[b]listkeys[/b] 等)。


[*] [b]交互式多路复用器与容器[/b]:兼容 [b]screen[/b]、[b]tmux[/b]、systemd 服务单元、Windows 命令提示符/PowerShell 以及交互式 Docker 容器。


[*] [b]模组管理器[/b]:完全兼容 [b]Gale[/b]、[b]Thunderstore Mod Manager[/b] 和 [b]r2modman[/b]。


[/list]


[line]



[size=5]安装[/size]



[size=4]手动安装[/size]


[list=1]


[*] 确保你的专用服务器上已安装 [b]BepInExPack Valheim[/b]。


[*] 下载并解压最新发布归档。


[*] 将 [b]ConsoleStdinHeeler.dll[/b] 放入服务器的 [b]BepInEx/plugins/[/b] 目录。


[*] 启动专用服务器。


[/list]


[size=4]模组管理器[/size]


[list=1]


[*] 通过 [b]Gale[/b]、[b]Thunderstore Mod Manager[/b] 或 [b]r2modman[/b] 安装。


[*] 启动专用服务器。


[/list]


[line]



[size=5]从源码构建[/size]


项目源代码可在 [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler]GitHub[/url] 上获取,使用可移植的 MSBuild 配置,可自动检测标准 Steam 路径。



[code]dotnet build -c Release[/code]



编译后的程序集将放置在 [b]bin/Release/net48/ConsoleStdinHeeler.dll[/b]。以 [b]Release[/b] 配置构建还会自动将分发包打包到 [b]bin/Publish/ConsoleStdinHeeler-<Version>.zip[/b]。



[size=4]自定义与 CI 路径[/size]


对于非标准 Steam 库位置或模组管理器配置文件,请将 [b]ConsoleStdinHeeler.csproj.user.example[/b] 复制到项目根目录下的 [b]ConsoleStdinHeeler.csproj.user[/b](此文件被 git 忽略,并由 MSBuild 自动加载):



[code]<?xml version="1.0" encoding="utf-8"?>


<Project>


<PropertyGroup>


<GamePath>D:\SteamLibrary\steamapps\common\Valheim</GamePath>


<BepInExCorePath>$(UserProfile)\AppData\Roaming\com.kesomannen.gale\valheim\profiles\<ProfileName>\BepInEx\core</BepInExCorePath>


</PropertyGroup>


</Project>[/code]



[line]



[size=5]打包、发布与发行[/size]


用于发行管理、打包以及发布到 [b]Thunderstore[/b]、[b]Hexium[/b] 和 [b]Nexus Mods[/b] 的所有开发者自动化工具都整合到单一 PowerShell 7 入口点 [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/.scripts/modtools.ps1].scripts/modtools.ps1[/url],由共享的 [url=https://github.com/DreamWraith/DW-ValheimModTools][b]DW.ValheimModTools[/b][/url] 模块提供支持:


[list]


[*] [b]发行管理[/b]:pwsh ./.scripts/modtools.ps1 release 以 Release 模式编译,创建模组与源码归档,提取变更日志注释,并使用 gh CLI 发布 GitHub Releases(默认草稿,或用 -Publish 发布)。


[*] [b]打包与版本递增[/b]:pwsh ./.scripts/modtools.ps1 package -Bump Patch 递增 ConsoleStdinHeeler.csproj 中的 SemVer,更新 manifest.json,并打包分发包。


[*] [b]门户发布[/b]:pwsh ./.scripts/modtools.ps1 publish -Target All 直接上传到 Thunderstore、Hexium 和 Nexus Mods API。


[*] [b]CI/CD 工作流[/b]:[url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/.github/workflows/publish.yml].github/workflows/publish.yml[/url] 提供自动化 GitHub Actions 工作流,在发布 GitHub Release 时发布到 Thunderstore、Hexium 和 Nexus Mods。


[/list]


有关标志、工作流和密钥配置的详细文档,请参阅 [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/.scripts/README.md].scripts/README.md[/url]。



[line]



[size=5]许可证与作者说明[/size]


[list]


[*] 本项目采用 GNU General Public License v3.0 许可 - 详情请参阅 [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler/blob/main/LICENSE.md]LICENSE.md[/url] 文件。源代码和问题追踪可在 [url=https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler]https://github.com/dreamwraith/Valheim-ConsoleStdinHeeler[/url] 获取。


[*] [url=https://gist.github.com/dreamwraith/98564f8441dc234bfadd7e2b605c694c][b]关于我针对 Valheim 模组开发的一点说明[/b][/url] - 关于开源模组开发、社区包容性和反守门主义的思考。


[*] [url=https://gist.github.com/dreamwraith/77c91d656c842611bf8c40febf8056f2][b]关于 AI、软件工艺以及此代码为何存在的说明[/b][/url] - 关于软件工艺、人类能动性和工程责任的个人随笔。


[/list]