我的世界基岩版(Minecraft Bedrock)服务器并没有像java那样能够根据玩家在线情况启停,对于本地物理机搭建的服务器来说,长时间没玩家又不关闭服务器,还是有点费电的。手动启停也不健康,所以有了这次尝试。
实现思路:
我的世界基岩版服务器联机主要访问两个端口,IPv4的19132和IPv6的19133,只需要检测这两个端口是否有流量访问就可以知道游戏中是否有玩家或是否要玩家请求进入服务器。再结合脚本启动和停止服务器。
因为脚本是使用cron每分钟运行,只需要在检测时,发现过去10s内有流量访问,就运行服务器;发现过去5分钟都没有流量访问,就关闭服务器。
方案设计:
创建nftables监控规则:
使用nftables创建端口监控规则,现在的系统的网络管理基本都是nftables。
sudo nft -f - <<'NFT'
table inet mc_cron {
set hit10s {
type inet_service
flags timeout
timeout 10s
}
set hit5m {
type inet_service
flags timeout
timeout 5m
}
chain input_mc {
type filter hook input priority 0;
udp dport {19132,19133} add @hit10s { udp dport }
udp dport {19132,19133} add @hit5m { udp dport }
}
}
NFT使用以上指令可以在nftables中创建一个mc_cron的表,并使用了timeout,用于记录端口流量,并进行倒计时。如果需要修改时间,可以直接改timeout后面的数值,其他不改。
这里监控了19132和19133的udp端口,如果你的服务器用了其他端口,可以自行改一下。
修改一下计时器的精度,把时间调整为1s:
echo "net.netfilter.nf_conntrack_timestamp=1" | sudo tee /etc/sysctl.d/99-mc.conf >/dev/null
sudo sysctl -p /etc/sysctl.d/99-mc.conf >/dev/null合并做成一键部署脚本:
创建一个bash执行文件,mc-cron-setup.sh,写入下面的内容:
#!/bin/bash
# 部署 nftables 倒计时链(19132 + 19133 udp)
set -e
# 如果表已存在,先删
sudo nft delete table inet mc_cron 2>/dev/null || true
sudo nft -f - <<'NFT'
table inet mc_cron {
set hit10s {
type inet_service
flags timeout
timeout 10s
}
set hit5m {
type inet_service
flags timeout
timeout 5m
}
chain input_mc {
type filter hook input priority 0;
udp dport {19132,19133} add @hit10s { udp dport }
udp dport {19132,19133} add @hit5m { udp dport }
}
}
NFT
# 计时器精度
echo "net.netfilter.nf_conntrack_timestamp=1" | sudo tee /etc/sysctl.d/99-mc.conf >/dev/null
sudo sysctl -p /etc/sysctl.d/99-mc.conf >/dev/null对应的一键卸载脚本:
创建bash执行脚本,mc-cron-clean.sh,写入下面的内容:
#!/bin/bash
# 撤销 nftables 倒计时链
set -e
# 删除 nftables 表
sudo nft delete table inet mc_cron 2>/dev/null || true
# 删除 sysctl
sudo rm -f /etc/sysctl.d/99-mc.conf
sudo sysctl --system >/dev/null 2>&1创建定时检查脚本:
创建一个脚本用于检查nftables中的倒计时链,过去10s和5min是否有流量。
创建mc-cron.sh脚本,写入下方内容:
#!/bin/bash
SRV="你的systemctl服务名称"
# 10 秒倒计时:任一端口有元素 → 启动
for port in 19132 19133; do
if nft get element inet mc_cron hit10s { $port } >/dev/null 2>&1; then
systemctl is-active "$SRV" >/dev/null || systemctl start "$SRV"
exit 0
fi
done
# 5 分钟倒计时:两个端口都无元素 → 停止
for port in 19132 19133; do
if nft get element inet mc_cron hit5m { $port } >/dev/null 2>&1; then
exit 0 # 仍有流量,不停止
fi
done
systemctl is-active "$SRV" >/dev/null && systemctl stop "$SRV"
exit 0上面的脚本是适用于使用物理机部署,使用systemctl管理的。
下面给一个docker版的,mc-cron-docker.sh:
#!/bin/bash
SRV="你的容器名称"
# 10 秒倒计时:任一端口有元素 → 启动
for port in 19132 19133; do
if nft get element inet mc_cron hit10s { $port } >/dev/null 2>&1; then
docker inspect -f '{{.State.Running}}' "$SRV" 2>/dev/null | grep -q true || \
docker start "$SRV" >/dev/null 2>&1
exit 0
fi
done
# 5 分钟倒计时:两个端口都无元素 → 停止
for port in 19132 19133; do
if nft get element inet mc_cron hit5m { $port } >/dev/null 2>&1; then
exit 0 # 仍有流量,不停止
fi
done
docker inspect -f '{{.State.Running}}' "$SRV" 2>/dev/null | grep -q true && \
docker stop "$SRV" >/dev/null 2>&1
exit 0
docker版的服务器还可以不直接启动或关闭,直接挂起容器,暂停再启动也可以,这样子服务器不用重新运行,保留暂停时的状态了。只需要把 docker stop改成 docker pause就行了。
可以使用cron执行,也可以使用其他方法定期执行,一般是一分钟执行一次。
使用方式:
首先,赋予所有脚本可执行权限。
部署使用:
1.运行nftables规则创建脚本:
bash mc-cron-setup.sh2.定时执行检查脚本:mc-cron.sh或者 mc-cron-docker.sh
关闭自动启停方案:
1.清除nftables规则及计时器:
bash mc-cron-clean.sh2.定时执行检查脚本:mc-cron.sh或者 mc-cron-docker.sh
总结:
对于这个方案,可以做到无感启停了,因为mc客户端在启动的时候就会开始并持续对服务器发送请求,基本上2分钟内服务器就可以启动了,如果显示连接失败,可以在服务器那个页面等一下,等服务器启动了,再进入。所有用户都退出之后,5分钟,服务器就会自己关闭了。