# <<*>> -^v- SNOWARE APPLICATION # usage: MINESENTINEL mainloop. # author: S.A. # time: 2026-06-07 # Copyright (c) [2026] [S.A. SNOWARE-SCU] # [MineSentinel] is licensed under Mulan PubL v2. # You can use this software according to the terms and conditions of the Mulan PubL v2. # You may obtain a copy of Mulan PubL v2 at: # http://license.coscl.org.cn/MulanPubL-2.0 # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, # EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. # See the Mulan PubL v2 for more details. # frozen_string_literal: true # rubocop:disable Metrics/MethodLength, Metrics/AbcSize def main puts ' ___ __ ___ ___ ___ ' puts ' |\/| | |\ | |__ /__` |__ |\ | | | |\ | |__ | ' puts ' | | | | \| |___ .__/ |___ | \| | | | \| |___ |___ ' puts ' MineSentinel dev-rolling version by <*> S.A. [@SNOWARE] and owned by SCU team.' puts ' Copyleft (c) 2026 S.A. SNOWARE-SCU. This program is licensed under Mulan PubL v2.' puts ' E-mail: SALflake@qq.com | QQ: 758522192 | Website: https://swe-iss.rth1.xyz/softwares/minesentinel.' puts require_relative 'method_executor' # 日志系统在此时初始化 MethodExecutor.record('debug', 'init', 'Initialized MethodExecutor, which implements logging system and intervening functions.') require_relative 'server_status_detector' require_relative 'systemd_notifier' # 心跳系统在此时初始化 require_relative 'config' MethodExecutor.record('debug', 'init', 'Initialized SystemdNotifier, which implements systemd watchdog feeder.') # 确认是否具有展开干预操作的权限 如果因场景不同不需要请注释这部分内容 unless Process.euid.zero? MethodExecutor.record('error', 'init', 'Permission denied: root required to intervene Systemd Services.') raise Errno::EPERM end MethodExecutor.record('info', 'mainloop', 'MineSentinel started, entering mainloop.') require_relative 'updates' begin upd_result = UpdateManager.check if upd_result[:status] == :NewerAvailable MethodExecutor.record('info', 'updates', "Update #{upd_result[:version]} available, please check the git repo for details.") end rescue StandardError => e MethodExecutor.record('error', 'updates', e.to_s) end # 初次启动:MineSentinel 先于 minecraft 启动(systemd Before=), # 等待服务器端口就绪,避免误判为崩溃 MethodExecutor.record('info', 'mainloop', "Initial startup grace period #{INITIAL_GRACE}s — waiting for server to come up...") grace_deadline = Time.now + INITIAL_GRACE while Time.now < grace_deadline sleep 5 next unless ServerStatusDetector::MinecraftServer.self_diag[:type] MethodExecutor.record('info', 'mainloop', 'Server port is open during grace period, resuming normal monitoring.') break end MethodExecutor.record('info', 'mainloop', 'Grace period ended.') main_loop end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize # Functional mainloop. # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def main_once chkrslts = [] (0...TRY_TIMES).each do chkrslt = ServerStatusDetector::MinecraftServer.self_diag chkrslts.append(chkrslt) end if chkrslts.all? { |chkrslt_in| chkrslt_in[:type] } MethodExecutor.record('debug', 'mainloop', 'Server health check passed.') else chkrslts.each do |result| MethodExecutor.record('warn', 'mainloop', "Server health check FAILED: #{result[:message]}") end MethodExecutor.record('info', 'mainloop', "Checked #{TRY_TIMES} time.") if chkrslts.any? { |chkrslt_in| chkrslt_in[:operation].include?('Record') } MethodExecutor.record('warn', 'mainloop', 'Action: Record -> Server died!') if chkrslts.any? { |chkrslt_in| chkrslt_in[:message] == 'Server died for unknown reason, and will be restarted.' } MethodExecutor.record('info', 'mainloop', 'Action: ForceResetServerProcess triggered.') MethodExecutor.reset_minecraft_server # 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟) cooldown_beg = Time.now MethodExecutor.record('info', 'mainloop', "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...") # rubocop:disable Metrics/BlockNesting while Time.now < cooldown_beg + RST_COOLDOWN_DURATION sleep 1 SystemdNotifier.notify('WATCHDOG=1') end # rubocop:enable Metrics/BlockNesting MethodExecutor.record('info', 'mainloop', 'Cooldown finished, resuming monitoring.') end end end end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def main_loop loop do sleep CHECK_INTERVAL SystemdNotifier.notify('WATCHDOG=1') main_once end end begin main rescue StandardError => e # 强制输出到stderr warn "[CRITICAL] Unhandled exception: #{e.message}" warn e.backtrace&.join("\n") exit 1 end