2hours.gg/

按小时计费的游戏服务器,玩多久付多久。

如何在 Linux 上运行仅支持 Windows 的游戏服务器

有些 Steam 游戏只提供 Windows 版的专用服务器,仓库里根本没有 Linux 二进制文件,所以常见的建议是“用一台 Windows 机器来跑”。但其实不必如此。借助 Wine,你可以在普通的 Linux 机器上运行这个 Windows 服务器,它照样能连接 Steam 并接受真实玩家加入。下面就是我们亲测有效的完整方法,以 Alien Swarm: Reactive Drop(一款免费的 Source 合作游戏)为例。同样的思路应该也适用于其他仅支持 Windows 的 Source 专用服务器。

第一步:先确认真的没有 Linux 服务器

不要想当然,动用 Wine 之前先直接问 Steam。用 steamcmd 指向这个应用,读取它的平台列表:

steamcmd +login anonymous +app_info_print <appid> +quit \
  | grep -iE "oslist|name"

如果专用服务器应用的 oslist 只写着 windows,那就没有原生 Linux 版本可以退而求其次。你也可以尝试强制安装 Linux 版;如果真的没有 Linux 仓库,安装会很快失败:

steamcmd +@sSteamCmdForcePlatformType linux \
  +login anonymous +app_update <appid> validate +quit
# ERROR! Failed to install app '<appid>' (Missing configuration)

这个报错,加上没有任何 ELF 文件落地,就能确认这一点。这时候就该用 Wine 了。把服务器和 app 1007(Steamworks Common Redistributables)安装到同一个文件夹里,平台类型选择 windows。App 1007 提供了服务器需要的 Steam DLL,具体原因会在第二步说明:

steamcmd +@sSteamCmdForcePlatformType windows +login anonymous \
  +force_install_dir /path/to/server \
  +app_update <appid> validate \
  +app_update 1007 validate \
  +quit

为什么直接用 Wine 尝试会失败

直接用 Wine 运行服务器会连续撞上两堵墙:

  • 没有控制台: 服务器需要一个真正的终端。如果从 /dev/null 管道输入,会报出 CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents 错误,永远无法启动。
  • 无法加载 Steam 库: 即使启动成功,它也会打印 "Unable to load Steam support library",并且永远无法向 Steam 注册,导致没人能找到或加入这台服务器。

这两个问题都能解决,下面是完整的方法。

第一步:给它一个真正的终端(PTY)

无图形界面的机器没有终端,而 Windows 控制台服务器没有终端就拒绝运行。用 script 命令包裹启动过程来分配一个伪终端,再用 Xvfb 提供一个虚拟显示器:

Xvfb :99 -screen 0 1024x768x24 &
export DISPLAY=:99 WINEPREFIX=/wine
script -qfc "wine srcds_console.exe -console -condebug \
  -game <gamedir> +map <map> -port 27015" /tmp/server.log

光这样就能让服务器加载地图并绑定游戏端口,但它仍然无法和 Steam 通信。

第二步:两个 Steam DLL,来自两个不同来源

Source 引擎需要 steamclient.dll 和 steam.dll 这两个文件同时放在服务器 exe 旁边,而这正是让我们多花了一整轮调试时间的坑:这两个文件来自不同的地方,app 1007 只能给你其中一个。

  • steamclient.dll: 来自 app 1007(Steamworks Common Redistributables),也就是你在第一步安装的那个。是官方正版,版本匹配,由 Valve 维护。
  • steam.dll: 并不在 app 1007 里面。它是 Steam 客户端的一部分,唯一干净的获取方式是在 Wine 下运行一次 Windows 版的 steamcmd.exe,它会自我更新,并在自己的文件夹里生成 steam.dll(以及 steamclient.dll)。

所以先在 Wine 下用 steamcmd.exe 引导出 steam.dll,再把两个 DLL 都复制到服务器可执行文件旁边:

# steam.dll: run Windows steamcmd once under Wine (needs Xvfb for a display)
cd "$WINEPREFIX/drive_c/steamcmd"
curl -fsSLO https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip
unzip -o steamcmd.zip
DISPLAY=:99 wine steamcmd.exe +quit    # lays down steam.dll + steamclient.dll

# place both next to the server exe
cd /path/to/server
cp "$WINEPREFIX/drive_c/steamcmd/steam.dll" .
[ -f steamclient.dll ] || cp "$WINEPREFIX/drive_c/steamcmd/steamclient.dll" .

第三步:为什么 steam.dll 才是关键

这一点几乎没人写出来过。报错信息是“Unable to load Steam support library”,但它指的支持库并不是 steamclient.dll,而是 steam.dll。Source 引擎需要在自己的可执行文件旁边找到 steam.dll,才能启动整条 Steam 初始化流程。如果只有 steamclient.dll(也就是 app 1007 能提供的全部内容),依然会失败;而一旦把 steam.dll 放到 .exe 旁边,日志就会瞬间从报错变成成功:

Connection to Steam servers successful.
VAC secure mode is activated.

只有服务器以正规游戏服务器身份登录,VAC 才会启用,所以这行日志就是它成功注册的证明。大多数 Source 服务器在这里是匿名登录,不需要 Game Server Login Token(GSLT)。只有少数游戏(比如 CS2)才需要它。

陷阱:A2S 没有响应,但服务器其实一切正常

这是最耗费我们时间的坑,也是我们差点放弃的原因。在 Wine 环境下,服务器不会响应 A2S_INFO——也就是工具用来读取服务器名称、地图和玩家数量的那个 UDP 查询。查询它时每个端口都会超时,哪怕是在同一台机器上查询也一样。如果你用 A2S 做健康检查,服务器看起来就像是彻底挂掉了。

它并没有挂掉。在 Wine 下 A2S 不响应,并不会阻止真实玩家加入。游戏内的服务器浏览器数据来自 Steam 的主列表(服务器会自行向其推送信息),而直接连接用的是游戏协议,不是 A2S。所以真正的判断标准是客户端能不能连上,而不是查询有没有响应。当我们最终试着连接时,第一次就有真实玩家成功连上并开始游玩:

Client "player" connected (10.0.0.5:27005).
1/ 8 on map <mapname>

实践中的教训是:用 Wine 托管的服务器,不要用 A2S 做健康检查,而要检查真正能反映运行状态的信号,比如日志里的“VAC secure mode is activated”那一行、玩家数量那一行,或者干脆检查游戏的 UDP 端口有没有被绑定。

这个方法对所有 Windows 游戏都适用吗?

坦白说一下这个方法的适用范围。我们是在一台 Source 引擎服务器上验证的,同样的步骤(PTY、用 steamcmd 构建的运行环境、把 steam.dll 放在 exe 旁边)应该也能用在其他仅支持 Windows 的 Source 专用服务器上。其他引擎的游戏集成 Steam 的方式不一样,所以要把它们当成单独的实验来对待。不管是哪种情况,都要预期两件事:启动速度会比原生 Linux 慢(因为多了 Wine、steamcmd 步骤和 Steam 登录),而且上面说的 A2S 无响应现象大概率也会出现在它们身上。

或者,直接跳过这一切

这种折腾很容易耗掉一整个下午。如果你只是想要一台能用的服务器,我们可以按小时为你运行 Linux 服务器集群和背后的 Wine 环境,从第一分钟起就提供公网 IP。看看我们如何在 2hours.gg 上托管

另请参阅

← 返回所有指南