StarBound
我的《星界边境那么大,我却懒得动》这篇博客已经坑了老久了,感觉得再体验一下 Starbound 来获取一些灵感,于是我拉上朋友联机 Starbound。
以前和朋友用 Steam 连 Starbound 的时候,如果我用我的台式电脑来作为主机,朋友跟我在一个局域网,那样联机体验还不错。但是如果不在同一个屋子里联机的时候,体验就好不起来了,而且换电脑之后星球上的建筑等数据是不会被 Steam 云同步的,只有角色数据会云同步,要是能自己搭一个服务器,就可以当网游玩了,不用担心备份和 Steam 联机的延迟啦。
国外服务器用不得
于是我先拿自己的 vultr 服务器试了一下,感觉国外 ping 值 200ms 以上的话,联机体验还是不行,有时候会看到朋友的角色定住不动,或者朋友反映怪打不死之类的。后来换到阿里云就好多了,基本和在一个屋子里玩一样了。
我 18 年使用了阿里云学生版,进入副本会卡,但是便宜。20 年我采用华为云按需计费。
注意要在网络权限组里开启 21025 端口。
使用 LinuxGSM
最开始我参考了一个讲如何手工用 steam 命令行工具下载游戏然后用 screen
这个 Linux 程序来维护服务器的教程,还有 Guide:LinuxServerSetup。
不过后来我发现这实在是不好维护,还是使用自动化的工具 LinuxGSM 来得好,这是一个守护 starbound 游戏服务器的程序,还能自动安装游戏,反正能省去一些手工劳动吧。
LinuxGSM 教程中没提及的部分
我们的安装流程是:
- 安装 LinuxGSM (看官方教程即可)
- 配置用户名密码和 Hosts
- 配置 Steam Guard
- 安装 Mod
- 启动游戏
注意官网教程中的依赖少了以下几项,需要自己额外安装上:
apt install libsdl2-2.0-0 libsdl2-2.0-0:i386 # 解决报错 SDL not found and Failed to set thread priority: per-thread setup failed
apt install jq netcat lib32stdc++6 steamcmd # steamcmd 的依赖
由于众所周知的原因,我们需要用 root 账号输入 vim /etc/hosts
添加以下的域名解析(若 LinuxGSM 下载脚本失败说明第一个失效,若 steam 无法更新说明第二个失效,需自行更换为有效的):
# gfw
199.232.4.133 raw.githubusercontent.com
104.78.74.220 steamcommunity.com
接着需要按 LinuxGSM 教程中的方法将自己的用户名密码填写到指定位置。
steamuser="linonetwo012"
steampass='pswd'
安装过程中要注意自己的邮箱,如果收到了 Steam 发来的邮件,而且安装过程卡住如下,则需要黏贴上刚收到的验证码,然后按回车:
sbserver@starbound-game:~$ ./sbserver update
[ ERROR ] Updating sbserver: No appmanifest_211820.acf found
[ INFO ] Updating sbserver: Forcing update to correct issue
[ START ] Updating sbserver: SteamCMD
RFXJR
Redirecting stderr to '/home/sbserver/.local/share/Steam/logs/stderr.txt'
[ 0%] Checking for available updates...
我是在第一次 ./sbserver install
的时候安装失败了,因为没有输入验证码就无法下载游戏,所以我用 ./sbserver update
再次安装。
然后再开始安装 ./sbserver install
,装的时候注意输入验证码。
安装 Mod
先进入 steamcmd,用如下命令找到 Steam 安装目录,steam 从创意工坊下载的 mod 都会放在里面,我们需要配置 Starbound 服务器找到这些 mod。
Steam>install_folder_list
Index 0 = "/home/sbserver/.local/share/Steam" 27.92 GB free disk space
这也就是我们要在下面的 serverfiles/linux/sbinit.config
里用 ../../.local/share/Steam/steamapps/workshop/content/211820
的原因。
安装 mod 得一个个输入 mod 在创意工坊的代码,然后用 steam 的命令行工具来安装,mod 的创意工坊代码就是其网址 https://steamcommunity.com/sharedfiles/filedetails/?id=807695810
中 id=
后面的那串数字。
好在我早就把自用的 mod 放进了一个合集里,以前是方便了联机的朋友一键订阅,现在就是可以从它直接生成自动操作 steam 命令行工具的脚本,不过因为我不是很熟悉 sed 和 tee 的用法,有时候脚本内容会被追加到 moddownload.sh
里面,有时候却不会,不是很稳定:
echo login 账号填这 密码填这 > moddownload.sh && curl -s --data "collectioncount=1&publishedfileids[0]=合集的ID填这" https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/ \
| jq '.response.collectiondetails[] | .children[] | .publishedfileid' \
| sed 's/^/workshop_download_item 211820 /' | tee -a moddownload.sh && echo quit >> moddownload.sh && steamcmd +runscript /home/sbserver/moddownload.sh
接着要用另一个脚本生成 serverfiles/linux/sbinit.config
,它就相当于你想启动的 mod 的列表,里面放了一大列 steam 命令行工具下载了的 mod 的存放路径:
echo -e "{\n \"assetDirectories\": [\n \"../assets/\",\n \"../mods/\",\n " && \
curl -s --data "collectioncount=1&publishedfileids[0]=合集的ID填这" https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/ \
| jq '.response.collectiondetails[] | .children[] | .publishedfileid' \
| sed 's#^#"../../.local/share/Steam/steamapps/workshop/content/211820/#' | sed 's#/"#/#' | tr -t '\n' ',' && \
echo -e "\b\b\n ],\n \"storageDirectory\": \"../storage/\"\n}\n"
这些脚本都参考自 LinuxGSM/issues/1623。
这里我踩过的坑有:
- 没有检查生成的
sbinit.config
是不是一个合法的 JSON,有一次数组的最后一项后面多带了一个逗号,结果 starbound 服务器就无法启动了。 - 里面的 mod 路径是不是指向 steam 下载的 mod 们,因为 LinuxGSM 也会维护一个 mod 文件夹,结果我的
sbinit.config
一直指向的是这个文件夹,当我用 steam 命令行工具更新了 mod 之后,LinuxGSM 维护的 mod 文件夹是不变的,于是我就一直很奇怪为什么 mod 没更新。 - 有的 mod 会冲突,导致
Exception caught in client main loop, eof reached
等报错,可能是因为 Enhanced Storage 等 mod 与其他某个 mod 冲突了,而互相影响的 mod 太多了形成了复杂的网络无法找到是其他哪几个 mod 影响了 Enhanced Storage,所以只好把 Enhanced Storage 卸掉。 - 有时候服务端的 mod 比客户端的少,例如我在
sbinit.config
里面指向了错误的 mod 文件夹,导致客户端有 70+个 mod,而服务端只有 ~50 个 mod,这就会在登录服务器时导致服务端报错(可以在 log 里看到具体错误信息),而客户端报的错就会是incoming client packet has caused an exception
。
即使服务端和客户端开启的 mod 列表是完全相同的,在登录服务器时还是会提示 Assets mismatch between client and server, and the override option is not set
。那解决办法当然就是让所有玩家都在客户端的设置里打开 Allow Assets Mismatch
,只要两边 mod 真的是对应的,打开之后就能正常登录服务器了。但是打开之后总感觉就失去了一个测试点,像之前客户端服务端 mod 数量真的不对应的时候,就没人来帮我们做检查了。
之前在 vultr 上试着开服的时候,服务器总是卡在这三步中的某一步上:
[22:35:02.148] [Info] Root: Loaded RadioMessageDatabase in 0.106177 seconds
[22:35:10.984] [Info] Root: Loaded ItemDatabase in 14.4328 seconds
[22:35:12.441] [Info] Root: Loaded CollectionDatabase in 10.2919 seconds
我就猜想可能是可怜的破服务器仅有的 1G 内存用光了,于是我就照着 ArchLinux 的维基创建了 5G 的交换文件 wiki.archlinux.org/Swap_(简体中文),不得不赞这个维基之清晰,从上面复制黏贴命令一般不会踩到坑。
然而在载入巨大大副本的时候服务器还是会爆卡,客户端上就是一直卡在 beam down 的过程中了,用 top
一看原来是因为服务器的物理内存不足,然后 kswapd0 进程就会占用大量 CPU 算力来搬运内存中的游戏数据到 swapfile 里。这么勤劳干嘛呢,这些游戏数据是一个只用不到 20 分钟的副本的。而因为它的勤劳,我在副本里行动都一卡一卡的。
于是我在 /etc/sysctl.conf
里设置了 vm.swappiness = 5
,果然显著降低了 kswapd0 搬运内存的意愿,现在只有内存仅剩 5% 的时候它才开始搬运内容啦~再进入副本果然就是一点也不卡了。
然后未雨绸缪,我打算把 sbserver 用户下的所有进程的优先级都默认提高,遂创建 /etc/security/limits.d/sbserver-priority.conf
文件,写入 sbserver hard priority -20
,把默认的 nice 值提到和 kswapd0 一样高。重启后再用 ps -efl | grep starbound
一看,果然 nice 都变成和系统进程一个级别的 -20 了,而优先级会比系统进程的 80 低一点,只有 60,这样游戏流畅的同时系统应该也不会崩吧。
开机启动
我选用了linux设置开机自启动脚本的最佳方式里 contab 那种,新建了一个 sh 文件来执行 @reboot /home/sbserver/start_sb_at_boot.sh
,sh 的内容为 cd /home/sbserver && ./sbserver start
,要记得 chmod +x ./start_sb_at_boot.sh
查看 Log
可以用 ./sbserver console
来看 log。
退出是 ctrl+b
再按 d
,而不是 ctrl+c
。
最后要赞美 zeit 公司,他们开发的 serve 在我查看 mod 列表、阅读 log、翻阅 mod 路径的过程中起了很大的作用。在装好 nodejs 之后,再装上 serve,然后 serve /home/sbserver
,就可以用这个简洁易用的 web 界面来查看服务器上的数据了。(当然要事先在服务器的安全组里设置 5000 端口仅能从我自己的电脑的 IP 可访问,以保证安全)
Factorio
Starbound 坏就坏在它是手工小作坊,制造物品和整理物品都很繁琐。于是我又和朋友开始玩 Factorio。有了 Starbound 的经验,我立即选择使用 LinuxGSM 安装 Factorio。
Mod
Factorio 的 Mod 不是通过 Steam 管理的,所以需要手动上传到服务器上。我使用 www.npmjs.com/package/upload-server-1 来上传我在用的 Mod 和 Mod 配置。
在 Mac 上,Mod 位于 ~/Library/Application\ Support/factorio/mods
,用「访达」的「前往文件夹…」功能进去之后,我在服务器上运行 upload-server -p 5000 -f ./mods
启动一个上传服务器(先在服务器管理系统的防火墙设置里把 5000 端口的 TCP 出入打开),在上传文件里把刚刚打开的 Mac 上的 Mod 文件夹拖进去。
上传的时候好像出了点问题,不过接着我用 serve .
启动一个静态文件服务器(www.npmjs.com/package/serve),看到所有文件都成功放到 ~/serverfiles/mods
里面了。
配置
一开始服务端没有报错,但是客户端说无法连接,查了老半天没用的资料我才意识到要先同步服务端和客户端的 Mod。
等 Mod 列表完全一致之后,服务器启动后很快就退出了,用 serve
起一个服务器看 log ,发现最后的报错是 Error ServerMultiplayerManager.cpp:96: MultiplayerManager failed: "Error while running on_init: Given seed value (34186445404) is too big, the data type allows values from 0 to 4294967295
,看来这个复杂的 Mod 没有适配多人模式。在 ./serverfiles/mods/mod-list.json
里将它关掉就好了。
这时我还是连不上,用 ./fctrserver console
查看运行情况,发现出现了 factorio Error ServerMultiplayerManager.cpp:633: Matching server connection failed: Error when creating server game: Missing token.
,经过一番调查,才知道原来要把 config 里面的 username
和 password
填上论坛的账号密码才行。不过填了还是没法解决这个问题,我才发现原来用 LinuxGSM 创建的游戏,会读取另一个配置文件 ./serverfiles/data/fctrserver.json
,在这个文件里修改就解决了这个问题。
但还是出现 Refusing connection for address (IP ADDR:({xxx:7018})), username (linonetwo012). UserVerificationMissing
,这是因为配置文件里开了 "require_user_verification": true,
,校验不成功可能是因为我没有公网 IP,将这一项改为 false
就能连上了。