diff --git a/install_server/install_server.sh b/install_server/install_server.sh new file mode 100644 index 0000000..6c3bfb0 --- /dev/null +++ b/install_server/install_server.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +STEAM_USER="steam" +STEAM_HOME="/home/steam" + +STEAMCMD_DIR="${STEAM_HOME}/steamcmd" +ETS2_DIR="${STEAM_HOME}/ets2_sv" +ETS2_DOC_DIR="${STEAM_HOME}/ets2_doc" + +ETS2_BIN_DIR="${ETS2_DIR}/bin/linux_x64" +SCRIPT_REPO="https://github.com/A-hxin/ets2_server.git" +TMP_SCRIPT_DIR="${ETS2_BIN_DIR}/server" + +APP_ID="1948160" + +echo "========================================" +echo " ETS2 Dedicated Server 一键部署脚本" +echo " System: Ubuntu" +echo "========================================" + +if [ "$(id -u)" -ne 0 ]; then + echo "请使用 root 权限执行:" + echo "sudo bash install_ets2_server.sh" + exit 1 +fi + +echo +echo "一、创建 steam 用户" + +if id "${STEAM_USER}" >/dev/null 2>&1; then + echo "用户 ${STEAM_USER} 已存在,跳过创建" +else + adduser \ + --system \ + --shell /bin/bash \ + --gecos 'Steam Service User' \ + --group \ + --disabled-password \ + --home "${STEAM_HOME}" \ + "${STEAM_USER}" + + echo "用户 ${STEAM_USER} 创建完成" +fi + +echo +echo "二、创建工作目录" + +mkdir -p "${STEAMCMD_DIR}" "${ETS2_DIR}" "${ETS2_DOC_DIR}" +chown -R "${STEAM_USER}:${STEAM_USER}" "${STEAM_HOME}" + +echo "目录创建完成:" +echo "${STEAMCMD_DIR}" +echo "${ETS2_DIR}" +echo "${ETS2_DOC_DIR}" + +echo +echo "三、安装运行库" + +dpkg --add-architecture i386 || true + +apt update + +apt install -y \ + curl \ + ca-certificates \ + tar \ + gzip \ + git \ + libc6:i386 \ + libstdc++6:i386 \ + libgcc-s1:i386 \ + libx11-6:i386 \ + libxext6:i386 \ + libxau6:i386 \ + libxdmcp6:i386 \ + libncurses5:i386 \ + libncursesw5:i386 \ + libcurl4:i386 \ + zlib1g:i386 \ + libbz2-1.0:i386 \ + libtinfo6:i386 + +echo +echo "四、下载 SteamCMD" + +sudo -u "${STEAM_USER}" bash -c " + cd '${STEAMCMD_DIR}' && + curl -sqL 'https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz' | tar zxvf - +" + +chmod +x "${STEAMCMD_DIR}/steamcmd.sh" || true + +echo +echo "五、安装 / 更新 ETS2 Dedicated Server" + +sudo -u "${STEAM_USER}" bash -c " + cd '${STEAMCMD_DIR}' && + '${STEAMCMD_DIR}/steamcmd.sh' \ + +force_install_dir '${ETS2_DIR}' \ + +login anonymous \ + +app_update '${APP_ID}' validate \ + +quit +" + +echo +echo "六、安装 server.sh 管理脚本" + +mkdir -p "${ETS2_BIN_DIR}" + +rm -rf "${TMP_SCRIPT_DIR}" + +sudo -u "${STEAM_USER}" git clone "${SCRIPT_REPO}" "${TMP_SCRIPT_DIR}" + +if [ -f "${TMP_SCRIPT_DIR}/server.sh" ]; then + mv "${TMP_SCRIPT_DIR}/server.sh" "${ETS2_BIN_DIR}/server.sh" + chmod +x "${ETS2_BIN_DIR}/server.sh" + rm -rf "${TMP_SCRIPT_DIR}" +else + echo "错误:没有找到 server.sh" + exit 1 +fi + +echo +echo "七、创建必要目录并设置权限" + +mkdir -p "${ETS2_BIN_DIR}/logs" +mkdir -p "${ETS2_DOC_DIR}/Euro Truck Simulator 2" + +chown -R "${STEAM_USER}:${STEAM_USER}" "${ETS2_BIN_DIR}" +chown -R "${STEAM_USER}:${STEAM_USER}" "${ETS2_DOC_DIR}" + +chmod -R 775 "${ETS2_BIN_DIR}" +chmod -R 775 "${ETS2_DOC_DIR}" + +echo +echo "八、创建快捷命令" + +ln -sfn "${ETS2_BIN_DIR}/server.sh" /usr/local/bin/ets2_sv +chmod +x /usr/local/bin/ets2_sv + +echo +echo "九、是否立即执行一次 server.sh 初始化?" + +if [ -t 0 ]; then + read -rp "是否现在执行一次?可能会进入服务器运行状态 [y/N]: " RUN_ONCE + case "${RUN_ONCE}" in + y|Y|yes|YES) + echo "正在以 steam 用户执行 server.sh..." + sudo -u "${STEAM_USER}" bash "${ETS2_BIN_DIR}/server.sh" + ;; + *) + echo "已跳过首次执行" + ;; + esac +else + echo "非交互环境,跳过首次执行" +fi + +echo +echo "========================================" +echo "部署完成" +echo +echo "服务器目录:" +echo "${ETS2_DIR}" +echo +echo "配置目录:" +echo "${ETS2_DOC_DIR}/Euro Truck Simulator 2" +echo +echo "管理脚本:" +echo "${ETS2_BIN_DIR}/server.sh" +echo +echo "快捷命令:" +echo "ets2_sv" +echo +echo "如需设置 steam 用户密码:" +echo "sudo passwd steam" +echo +echo "如需切换到 steam 用户:" +echo "su - steam" +echo "========================================" \ No newline at end of file diff --git a/server/server.sh b/server/server.sh index 2140c94..2bc95c4 100644 --- a/server/server.sh +++ b/server/server.sh @@ -1 +1,756 @@ -~~~~~111 \ No newline at end of file +#!/bin/sh +# 2026年5月6日 21点21分 +# 项目地址:https://github.com/A-hxin/ets2_server/ + +# ============================== +# ETS2 Dedicated Server 管理脚本 +# ============================== + +# 服务器目录 +SERVER_HOME="/home/steam/ets2_sv/bin/linux_x64" + +# 官方启动脚本 +SERVER_LAUNCH="$SERVER_HOME/server_launch.sh" + +# Steam 运行库目录 +STEAM_PATH="/home/steam/ets2_sv/linux64" + +# SteamCMD 目录 +STEAMCMD_HOME="/home/steam/steamcmd" + +# ETS2 服务端安装目录 +ETS2_INSTALL_DIR="/home/steam/ets2_sv" + +# ETS2 Dedicated Server AppID +ETS2_APP_ID="1948160" + +# 欧卡文档目录 +# 实际读取目录: +# /home/steam/ets2_doc/Euro Truck Simulator 2 +export XDG_DATA_HOME="/home/steam/ets2_doc" + +# 设置 LD_LIBRARY_PATH +export LD_LIBRARY_PATH="$STEAM_PATH:$SERVER_HOME:${LD_LIBRARY_PATH:-}" + +# 启动参数 +SERVER_OPTIONS="-nosingle -server server_packages.sii -server_cfg server_config.sii" + +# 实际配置目录 +ETS2_DOC_HOME="$XDG_DATA_HOME/Euro Truck Simulator 2" + +# 日志根目录 +LOG_ROOT="$SERVER_HOME/logs" + +# PID 文件 +PID_FILE="$LOG_ROOT/server.pid" + +# 后台启动器 PID 文件 +RUNNER_PID_FILE="$LOG_ROOT/runner.pid" + +# 进程组 ID 文件 +PGID_FILE="$LOG_ROOT/server.pgid" + +# 最新日志软链接 +LATEST_LOG_LINK="$LOG_ROOT/latest.log" + +# 当前日志路径记录 +CURRENT_LOG_PATH_FILE="$LOG_ROOT/current_log.path" + +# 日志保留天数 +LOG_KEEP_DAYS=15 + +# 当前运行日志文件 +SERVER_LOG="" + +show_info() { + echo "[INFO] $1" +} + +show_ok() { + echo "[OK] $1" +} + +show_warn() { + echo "[WARN] $1" +} + +show_error() { + echo "[ERROR] $1" +} + +prepare_dirs() { + mkdir -p "$LOG_ROOT" + mkdir -p "$ETS2_DOC_HOME" +} + +create_log_file() { + LOG_DAY=$(date +"%Y-%m-%d") + LOG_TIME=$(date +"%Y%m%d_%H%M%S") + + LOG_DIR="$LOG_ROOT/$LOG_DAY" + mkdir -p "$LOG_DIR" + + SERVER_LOG="$LOG_DIR/server_$LOG_TIME.log" + + touch "$SERVER_LOG" + ln -sfn "$SERVER_LOG" "$LATEST_LOG_LINK" + echo "$SERVER_LOG" > "$CURRENT_LOG_PATH_FILE" + + show_info "本次日志文件: $SERVER_LOG" +} + +cleanup_old_logs() { + if [ -d "$LOG_ROOT" ]; then + find "$LOG_ROOT" -mindepth 1 -maxdepth 1 -type d -mtime +"$LOG_KEEP_DAYS" -exec rm -rf {} \; 2>/dev/null + fi +} + +check_required_files() { + echo "========== ETS2 配置文件检查 ==========" + echo "当前用户: $(whoami)" + echo "SERVER_HOME: $SERVER_HOME" + echo "SERVER_LAUNCH: $SERVER_LAUNCH" + echo "STEAM_PATH: $STEAM_PATH" + echo "XDG_DATA_HOME: $XDG_DATA_HOME" + echo "ETS2_DOC_HOME: $ETS2_DOC_HOME" + echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH" + echo "SERVER_OPTIONS: $SERVER_OPTIONS" + echo "" + + if [ -x "$SERVER_LAUNCH" ]; then + echo "[OK] server_launch.sh 存在并可执行" + else + echo "[ERROR] server_launch.sh 不存在或不可执行" + fi + + if [ -f "$ETS2_DOC_HOME/server_config.sii" ]; then + echo "[OK] server_config.sii 存在" + else + echo "[WARN] 缺少 server_config.sii" + fi + + if [ -f "$ETS2_DOC_HOME/server_packages.sii" ]; then + echo "[OK] server_packages.sii 存在" + else + echo "[ERROR] 缺少 server_packages.sii" + fi + + if [ -f "$ETS2_DOC_HOME/server_packages.dat" ]; then + echo "[OK] server_packages.dat 存在" + else + echo "[ERROR] 缺少 server_packages.dat" + fi + + echo "" + echo "当前配置目录文件:" + ls -lah "$ETS2_DOC_HOME" 2>/dev/null + echo "=======================================" +} + +get_server_pid() { + pgrep -f "eurotrucks2_server" | head -n 1 +} + +get_launch_pid() { + pgrep -f "server_launch.sh" | head -n 1 +} + +get_config_port() { + KEY="$1" + CONFIG_FILE="$ETS2_DOC_HOME/server_config.sii" + + if [ ! -f "$CONFIG_FILE" ]; then + return + fi + + grep -E "^[[:space:]]*$KEY[[:space:]]*:" "$CONFIG_FILE" | \ + head -n 1 | \ + sed -E 's/.*:[[:space:]]*([0-9]+).*/\1/' +} + +get_connection_port() { + PORT=$(get_config_port "connection_dedicated_port") + + if [ -z "$PORT" ]; then + PORT="27015" + fi + + echo "$PORT" +} + +get_query_port() { + PORT=$(get_config_port "query_dedicated_port") + + if [ -z "$PORT" ]; then + PORT="27016" + fi + + echo "$PORT" +} + +port_in_use() { + PORT="$1" + + if [ -z "$PORT" ]; then + return 1 + fi + + ss -H -lntup 2>/dev/null | awk -v port=":$PORT" ' + $5 ~ port"$" { + found=1 + } + END { + exit !found + } + ' +} + +show_port_owner() { + PORT="$1" + ss -lntup 2>/dev/null | grep -E ":$PORT[[:space:]]|:$PORT$" || true +} + +get_port_owner_pids() { + PORT="$1" + + ss -H -lntup 2>/dev/null | awk -v port=":$PORT" ' + $0 ~ port { + line=$0 + while (match(line, /pid=[0-9]+/)) { + pid=substr(line, RSTART + 4, RLENGTH - 4) + print pid + line=substr(line, RSTART + RLENGTH) + } + } + ' | sort -u +} + +get_ets2_pid_from_ports() { + CONN_PORT=$(get_connection_port) + QUERY_PORT=$(get_query_port) + + for PORT in "$CONN_PORT" "$QUERY_PORT"; do + for P in $(get_port_owner_pids "$PORT"); do + CMD=$(ps -p "$P" -o command= 2>/dev/null) + + case "$CMD" in + *eurotrucks2_server*) + echo "$P" + return 0 + ;; + esac + done + done + + return 1 +} + +check_ports_available() { + CONN_PORT=$(get_connection_port) + QUERY_PORT=$(get_query_port) + + echo "========== ETS2 端口检查 ==========" + echo "connection_dedicated_port: $CONN_PORT" + echo "query_dedicated_port: $QUERY_PORT" + echo "" + + HAS_ERROR=0 + + if port_in_use "$CONN_PORT"; then + show_error "端口 $CONN_PORT 已被占用" + show_port_owner "$CONN_PORT" + HAS_ERROR=1 + else + show_ok "端口 $CONN_PORT 可用" + fi + + if port_in_use "$QUERY_PORT"; then + show_error "端口 $QUERY_PORT 已被占用" + show_port_owner "$QUERY_PORT" + HAS_ERROR=1 + else + show_ok "端口 $QUERY_PORT 可用" + fi + + echo "==================================" + + if [ "$HAS_ERROR" -ne 0 ]; then + echo "" + show_error "端口被占用,请先执行:" + echo "$0 stop" + echo "" + echo "如果 stop 后仍占用,执行:" + echo "$0 kill" + return 1 + fi + + return 0 +} + +show_ports_status() { + CONN_PORT=$(get_connection_port) + QUERY_PORT=$(get_query_port) + + echo "========== ETS2 端口状态 ==========" + echo "connection_dedicated_port: $CONN_PORT" + show_port_owner "$CONN_PORT" + + echo "" + echo "query_dedicated_port: $QUERY_PORT" + show_port_owner "$QUERY_PORT" + echo "==================================" +} + +pgid_has_ets2() { + TARGET_PGID="$1" + + if [ -z "$TARGET_PGID" ]; then + return 1 + fi + + ps -eo pgid=,cmd= | awk -v pgid="$TARGET_PGID" ' + $1 == pgid && ($0 ~ /eurotrucks2_server/ || $0 ~ /server_launch.sh/ || $0 ~ /script .*server_launch.sh/) { + found=1 + } + END { + exit !found + } + ' +} + +kill_ets2_port_owners() { + CONN_PORT=$(get_connection_port) + QUERY_PORT=$(get_query_port) + + for PORT in "$CONN_PORT" "$QUERY_PORT"; do + for P in $(get_port_owner_pids "$PORT"); do + CMD=$(ps -p "$P" -o command= 2>/dev/null) + + case "$CMD" in + *eurotrucks2_server*|*server_launch.sh*) + show_warn "端口 $PORT 仍被 ETS2 进程占用,强制清理 PID: $P" + kill -TERM "$P" 2>/dev/null || true + sleep 1 + kill -KILL "$P" 2>/dev/null || true + ;; + *) + show_warn "端口 $PORT 被非 ETS2 进程占用,PID: $P" + echo "$CMD" + ;; + esac + done + done +} + +wait_ports_release() { + CONN_PORT=$(get_connection_port) + QUERY_PORT=$(get_query_port) + + COUNT=0 + + while [ "$COUNT" -lt 10 ]; do + if ! port_in_use "$CONN_PORT" && ! port_in_use "$QUERY_PORT"; then + show_ok "端口 $CONN_PORT / $QUERY_PORT 已释放。" + return 0 + fi + + sleep 1 + COUNT=$((COUNT + 1)) + done + + show_warn "端口仍未完全释放,尝试按端口兜底清理..." + kill_ets2_port_owners + + sleep 2 + + if ! port_in_use "$CONN_PORT" && ! port_in_use "$QUERY_PORT"; then + show_ok "端口 $CONN_PORT / $QUERY_PORT 已释放。" + return 0 + fi + + show_error "端口仍被占用:" + show_port_owner "$CONN_PORT" + show_port_owner "$QUERY_PORT" + + return 1 +} + +force_cleanup_server() { + prepare_dirs + + show_warn "正在清理残留 ETS2 进程..." + + RUNNER_PID="" + PGID="" + + if [ -f "$RUNNER_PID_FILE" ]; then + RUNNER_PID=$(cat "$RUNNER_PID_FILE") + fi + + if [ -f "$PGID_FILE" ]; then + PGID=$(cat "$PGID_FILE") + fi + + if [ -n "$PGID" ]; then + if pgid_has_ets2 "$PGID"; then + show_warn "正在停止 ETS2 进程组 PGID: $PGID" + kill -TERM "-$PGID" 2>/dev/null || true + else + show_warn "PGID $PGID 不存在或不属于 ETS2,跳过进程组终止" + fi + fi + + if [ -n "$RUNNER_PID" ]; then + show_warn "正在停止启动器 PID: $RUNNER_PID" + kill -TERM "$RUNNER_PID" 2>/dev/null || true + fi + + pkill -TERM -f "eurotrucks2_server" 2>/dev/null || true + pkill -TERM -f "server_launch.sh" 2>/dev/null || true + pkill -TERM -f "script .*server_launch.sh" 2>/dev/null || true + + sleep 3 + + if [ -n "$PGID" ]; then + if pgid_has_ets2 "$PGID"; then + kill -KILL "-$PGID" 2>/dev/null || true + fi + fi + + if [ -n "$RUNNER_PID" ]; then + kill -KILL "$RUNNER_PID" 2>/dev/null || true + fi + + pkill -KILL -f "eurotrucks2_server" 2>/dev/null || true + pkill -KILL -f "server_launch.sh" 2>/dev/null || true + pkill -KILL -f "script .*server_launch.sh" 2>/dev/null || true + + kill_ets2_port_owners + + rm -f "$PID_FILE" + rm -f "$RUNNER_PID_FILE" + rm -f "$PGID_FILE" + + wait_ports_release || true + + show_ok "残留进程清理完成。" +} + +run_server_realtime() { + cd "$SERVER_HOME" || exit 1 + + if command -v script >/dev/null 2>&1; then + script -qefc "$SERVER_LAUNCH $SERVER_OPTIONS" /dev/null + elif command -v stdbuf >/dev/null 2>&1; then + stdbuf -oL -eL "$SERVER_LAUNCH" $SERVER_OPTIONS + else + "$SERVER_LAUNCH" $SERVER_OPTIONS + fi +} + +case "$1" in + start) + show_info "正在启动 ETS2 服务器..." + + prepare_dirs + cleanup_old_logs + + SERVER_PID=$(get_server_pid) + + if [ -z "$SERVER_PID" ]; then + SERVER_PID=$(get_ets2_pid_from_ports) + fi + + if [ -n "$SERVER_PID" ]; then + show_warn "ETS2 服务器似乎已经在运行,PID: $SERVER_PID" + echo "$SERVER_PID" > "$PID_FILE" + + if [ -f "$CURRENT_LOG_PATH_FILE" ]; then + show_info "本次日志: $(cat "$CURRENT_LOG_PATH_FILE")" + fi + + exit 0 + fi + + check_ports_available || exit 1 + + create_log_file + + cd "$SERVER_HOME" || exit 1 + + setsid sh -c ' + SERVER_HOME="$1" + SERVER_LAUNCH="$2" + SERVER_OPTIONS="$3" + SERVER_LOG="$4" + + cd "$SERVER_HOME" || exit 1 + + if command -v script >/dev/null 2>&1; then + script -qefc "$SERVER_LAUNCH $SERVER_OPTIONS" /dev/null + elif command -v stdbuf >/dev/null 2>&1; then + stdbuf -oL -eL "$SERVER_LAUNCH" $SERVER_OPTIONS + else + "$SERVER_LAUNCH" $SERVER_OPTIONS + fi 2>&1 | awk '"'"'{print strftime("%Y-%m-%d %H:%M:%S"), "-", $0; fflush();}'"'"' >> "$SERVER_LOG" + ' sh "$SERVER_HOME" "$SERVER_LAUNCH" "$SERVER_OPTIONS" "$SERVER_LOG" >/dev/null 2>&1 & + + RUNNER_PID=$! + echo "$RUNNER_PID" > "$RUNNER_PID_FILE" + echo "$RUNNER_PID" > "$PGID_FILE" + + show_info "启动器 PID: $RUNNER_PID" + show_info "进程组 PGID: $RUNNER_PID" + show_info "等待 ETS2 服务端启动,最多等待 60 秒..." + + WAIT_COUNT=0 + SERVER_PID="" + LAUNCH_PID="" + + while [ "$WAIT_COUNT" -lt 60 ]; do + SERVER_PID=$(get_server_pid) + + if [ -z "$SERVER_PID" ]; then + SERVER_PID=$(get_ets2_pid_from_ports) + fi + + LAUNCH_PID=$(get_launch_pid) + + if [ -n "$SERVER_PID" ]; then + echo "$SERVER_PID" > "$PID_FILE" + show_ok "ETS2 服务器已启动,PID: $SERVER_PID" + show_info "最新日志: $LATEST_LOG_LINK" + show_info "本次日志: $SERVER_LOG" + exit 0 + fi + + if [ -n "$LAUNCH_PID" ]; then + show_info "启动脚本仍在运行,PID: $LAUNCH_PID,继续等待..." + fi + + if grep -qiE "Failed to init|Server was terminated|couldn't find an open port|Server packages file not found|SteamAPI_Init.*failed|\*\*\* ERROR \*\*\*" "$SERVER_LOG" 2>/dev/null; then + show_error "检测到启动错误,准备清理残留进程..." + tail -n 100 "$SERVER_LOG" + force_cleanup_server + exit 1 + fi + + WAIT_COUNT=$((WAIT_COUNT + 1)) + sleep 1 + done + + SERVER_PID=$(get_server_pid) + + if [ -z "$SERVER_PID" ]; then + SERVER_PID=$(get_ets2_pid_from_ports) + fi + + if [ -n "$SERVER_PID" ]; then + echo "$SERVER_PID" > "$PID_FILE" + show_ok "ETS2 服务器已启动,PID: $SERVER_PID" + show_info "最新日志: $LATEST_LOG_LINK" + show_info "本次日志: $SERVER_LOG" + exit 0 + fi + + show_error "等待 60 秒后仍未检测到 ETS2 主进程,准备清理残留进程..." + tail -n 120 "$SERVER_LOG" + force_cleanup_server + exit 1 + ;; + + debug) + show_info "正在以前台调试模式启动 ETS2 服务器..." + show_warn "当前不会后台运行,按 Ctrl+C 可停止服务器。" + + prepare_dirs + cleanup_old_logs + create_log_file + check_required_files + + OLD_PID=$(get_server_pid) + + if [ -z "$OLD_PID" ]; then + OLD_PID=$(get_ets2_pid_from_ports) + fi + + if [ -n "$OLD_PID" ]; then + show_error "检测到已有 ETS2 服务端进程正在运行,PID: $OLD_PID" + echo "" + echo "请先执行:" + echo "$0 stop" + echo "" + echo "如果仍然无法释放端口,执行:" + echo "$0 kill" + exit 1 + fi + + check_ports_available || exit 1 + + trap ' + echo "" + echo "[WARN] 收到中断信号,正在退出 debug 模式..." + force_cleanup_server + echo "[OK] debug 残留进程已清理。" + exit 130 + ' INT TERM HUP + + echo "" + echo "========== 开始前台调试 ==========" + echo "执行命令:" + echo "$SERVER_LAUNCH $SERVER_OPTIONS" + echo "本次日志:" + echo "$SERVER_LOG" + echo "==================================" + echo "" + + run_server_realtime 2>&1 | \ + awk '{print strftime("%Y-%m-%d %H:%M:%S"), "-", $0; fflush();}' | \ + tee -a "$SERVER_LOG" + + trap - INT TERM HUP + + show_warn "debug 模式已退出,正在清理可能残留的 ETS2 进程..." + force_cleanup_server + ;; + + stop) + show_info "正在停止 ETS2 服务器..." + force_cleanup_server + ;; + + kill) + force_cleanup_server + ;; + + restart) + show_info "正在重启 ETS2 服务器..." + "$0" stop + sleep 3 + "$0" start + ;; + + update) + show_info "正在更新 ETS2 专用服务器..." + + SERVER_PID=$(get_server_pid) + + if [ -n "$SERVER_PID" ]; then + show_warn "检测到服务器正在运行,先停止服务器..." + "$0" stop + sleep 3 + fi + + mkdir -p "$STEAMCMD_HOME" "$ETS2_INSTALL_DIR" + + cd "$STEAMCMD_HOME" || exit 1 + + show_info "检查 SteamCMD..." + + curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar zxvf - + + chmod +x "$STEAMCMD_HOME/steamcmd.sh" + + show_info "开始更新 ETS2 Dedicated Server..." + + "$STEAMCMD_HOME/steamcmd.sh" \ + +force_install_dir "$ETS2_INSTALL_DIR" \ + +login anonymous \ + +app_update "$ETS2_APP_ID" validate \ + +quit + + show_ok "ETS2 专用服务器更新完成。" + ;; + + status) + prepare_dirs + + SERVER_PID=$(get_server_pid) + + if [ -z "$SERVER_PID" ]; then + SERVER_PID=$(get_ets2_pid_from_ports) + fi + + LAUNCH_PID=$(get_launch_pid) + + if [ -n "$SERVER_PID" ]; then + show_ok "ETS2 服务器正在运行,PID: $SERVER_PID" + + if [ -n "$LAUNCH_PID" ]; then + show_info "启动脚本进程 PID: $LAUNCH_PID" + fi + + if [ -f "$CURRENT_LOG_PATH_FILE" ]; then + show_info "本次日志: $(cat "$CURRENT_LOG_PATH_FILE")" + fi + + show_info "最新日志: $LATEST_LOG_LINK" + echo "" + show_ports_status + else + show_warn "ETS2 服务器未运行。" + rm -f "$PID_FILE" + fi + ;; + + log) + prepare_dirs + + if [ -L "$LATEST_LOG_LINK" ] || [ -f "$LATEST_LOG_LINK" ]; then + tail -n 100 -F "$LATEST_LOG_LINK" + else + show_warn "暂无最新日志文件。" + show_info "请先执行:$0 start 或 $0 debug" + fi + ;; + + listlog) + prepare_dirs + + echo "最近日志文件:" + find "$LOG_ROOT" -type f -name "*.log" 2>/dev/null | sort | tail -n 30 + ;; + + cleanlog) + prepare_dirs + cleanup_old_logs + show_ok "已清理超过 $LOG_KEEP_DAYS 天的日志目录。" + ;; + + check) + prepare_dirs + check_required_files + echo "" + check_ports_available || true + ;; + + ports) + prepare_dirs + check_ports_available || true + ;; + + portstatus) + prepare_dirs + show_ports_status + ;; + + *) + echo "ETS2 服务器管理命令" + echo "用法: ets2_sv {start|stop|restart|status|update|debug|log|listlog|cleanlog|check|ports|portstatus|kill}" + echo "" + echo " start - 后台启动 ETS2 服务器" + echo " stop - 停止 ETS2 服务器,并释放端口" + echo " restart - 重启 ETS2 服务器" + echo " status - 查看 ETS2 服务器状态" + echo " update - 更新 ETS2 专用服务器" + echo " debug - 前台调试启动,不后台运行,实时输出" + echo " log - 实时查看最新日志" + echo " listlog - 查看最近日志文件" + echo " cleanlog - 清理旧日志" + echo " check - 检查配置文件和端口" + echo " ports - 检查端口是否可用" + echo " portstatus - 查看端口占用详情" + echo " kill - 强制清理残留 ETS2 进程" + exit 1 + ;; +esac \ No newline at end of file