# <<*>> -^v- SNOWARE APPLICATION # usage: MINECRAFT Server monitoring. # 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 module ServerStatusDetector require 'socket' require 'timeout' class TcpServer # rubocop:disable Metrics/MethodLength, Metrics/AbcSize # @param host [String] # @param port [Integer] # @param timeout [Integer] # @return [Hash{Symbol=>Integer, Symbol=>Symbol, Symbol=>Float,nil}] def self.test_port(host = '127.0.0.1', port = 25_565, timeout = 10) result = { port: port, status: :unknown, response_time: nil } begin_time = Time.now # 带错误处理的代码需要使用 begin...end begin Timeout.timeout(timeout) do # 此方法接收整数和代码块作为参数,并在超时时引发错误 socket = TCPSocket.new(host, port) result[:status] = :open result[:response_time] = (Time.now - begin_time).round(4) socket.close end rescue Timeout::Error result[:status] = :timeout rescue Errno::ECONNREFUSED result[:status] = :closed rescue Errno::ECONNRESET result[:status] = :interrupted rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH result[:status] = :unreachable rescue StandardError => e result[:status] = :error result[:error] = e.message end result end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize end # 继承为TcpServer编写的属性和方法 class MinecraftServer < TcpServer # rubocop:disable Metrics/MethodLength # @return [Hash{Symbol=>Boolean, Symbol=>Array, Symbol=>String}] def self_diag res = test_port('127.0.0.1', 25_565) unless res[:error].nil? message = "Unexpected ERROR when testing SMP by TCP: #{res[:status]},INFO: #{res[:error]}" puts message return { type: false, operation: %w[Record], message: message } # 立刻错误处理是好习惯 end case res[:status] when :open MethodExecutor.record('debug', 'self_diag', "TCP check OK — #{res[:response_time]}s") { type: true } when :timeout MethodExecutor.record('warn', 'self_diag', 'TCP check timed out.') { type: false, operation: %w[Record], message: 'We sent packet without response.' } when :closed MethodExecutor.record('warn', 'self_diag', 'TCP port closed — server is down.') { type: false, operation: %w[Record ForceResetServerProcess], # 重启时注意要设置最大尝试次数 message: 'Server died for unknown reason, and will be restarted.' } else MethodExecutor.record('warn', 'self_diag', "TCP check returned unexpected status: #{res[:status]}.") { type: false, operation: %w[Record], message: "Unexpected status: #{res[:status]}." } end end # rubocop:enable Metrics/MethodLength end end