下载

下载

我正在为某人编写一个程序,该程序需要能够判断 Minecraft 服务器是否在线。如果它能够判断有多少玩家或哪些玩家在线,那就太好了。我不确定 Minecraft 服务器是如何工作的,所以我不确定如何去做。如果你想知道它的具体 IP,你可以给我发消息,尽管我不确定我是否被允许在这里公开发布它。

答案1

对于监控,我会检查是否已经有一个纳吉奥斯可以做你想做的事情的脚本。

事实证明,Minecraft Server Nagios 插件已经存在,check_minecraft.py

下载

wget 'https://raw.githubusercontent.com/vertecx/nagios-plugins/256f81f28c5c79d569bd2265f3ba38c222726c39/check_minecraft.py'

笔记:上面的 Nagios 插件是为 Python 2 编写的。为了使其与 Python 3 兼容,请应用此补丁(也在此答案的底部重现):

curl -sL https://pastebin.com/raw/G2bnsrCw | dos2unix | patch check_minecraft.py

使用帮助

deltik@node51 [~]$ python check_minecraft.py -h
usage: check_minecraft.py [-h] -H ADDRESS [-p INTEGER] [-n INTEGER]
                          [-m STRING] [-f] [-w DOUBLE] [-c DOUBLE] [-t DOUBLE]
                          [-v]

This plugin will try to connect to a Minecraft server.

optional arguments:
  -h, --help            show this help message and exit
  -H ADDRESS, --hostname ADDRESS
                        host name or IP address
  -p INTEGER, --port INTEGER
                        port number (default: 25565)
  -n INTEGER, --number-of-checks INTEGER
                        number of checks to get stable average response time
                        (default: 5)
  -m STRING, --motd STRING
                        expected motd in server response (default: A Minecraft
                        Server)
  -f, --warn-on-full    generate warning if server is full
  -w DOUBLE, --warning DOUBLE
                        response time to result in warning status (seconds)
  -c DOUBLE, --critical DOUBLE
                        response time to result in critical status (seconds)
  -t DOUBLE, --timeout DOUBLE
                        seconds before connection times out (default: 10)
  -v, --verbose         show details for command-line debugging (Nagios may
                        truncate output)
MINECRAFT UNKNOWN: Invalid arguments

使用示例

检查是否mc.deltik.org返回预期的 MOTD(每日消息)Deltik Minecraft Server

deltik@node51 [~] python check_minecraft.py -H mc.deltik.org -m "Deltik Minecraft Server"
MINECRAFT OK: 0/20 players online - 300 bytes in 0.0345 second response time|time=0.0345476s;0.0;0.0;0.0;10.0

资源

check_minecraft.py全文转载如下:

#!/usr/bin/env python
# coding=utf8

from datetime import datetime, timedelta
import sys, string, socket, time, argparse

# Exit statuses recognized by Nagios.
STATE_OK = 0
STATE_WARNING = 1
STATE_CRITICAL = 2
STATE_UNKNOWN = 3

# Output formatting string.
OUTPUT_OK = "MINECRAFT OK: {0} - {1} bytes in {2:.3} second response time|time={2}s;{3};{4};0.0;{5}"
OUTPUT_WARNING = "MINECRAFT WARNING: {0} - {1} bytes in {2:.3} second response time|time={2}s;{3};{4};0.0;{5}"
OUTPUT_CRITICAL = "MINECRAFT CRITICAL: {0} - {1} bytes in {2:.3} second response time|time={2}s;{3};{4};0.0;{5}"
OUTPUT_EXCEPTION = "MINECRAFT CRITICAL: {0}"
OUTPUT_UNKNOWN = "MINECRAFT UNKNOWN: Invalid arguments"

# Minecraft packet ID:s, delimiters and encoding.
MC_SERVER_LIST_PING = "\xfe"
MC_DISCONNECT = "\xff"
MC_DELIMITER = u"\xa7"
MC_ENCODING = "utf-16be"

def log(start, message):
    print("{0}: {1}".format(datetime.now() - start, message))

def get_server_info(host, port, num_checks, timeout, verbose):
    start_time = datetime.now()
    total_delta = timedelta()
    byte_count = len(MC_SERVER_LIST_PING) * num_checks

    # Contact the server multiple times to get a stable average response time.
    for i in range(0, num_checks):
        if (verbose): iteration = "Iteration {0}/{1}: ".format(i + 1, num_checks)

        # Save start time and connect to server.
        if (verbose): log(start_time, "{0}Connecting to {1} on port {2}.".format(iteration, host, port))
        net_start_time = datetime.now()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        s.connect((host, port))

        # Send Minecraft Server List Ping packet.
        if (verbose): log(start_time, "{0}Sending Server List Ping.".format(iteration))
        s.send(MC_SERVER_LIST_PING)

        # Receive answer from server. The largest answer returned by the server that also works with the Minecraft client
        # seems to be around 520 bytes (259 unicode character at 2 bytes each plus one start byte and one length byte).
        if (verbose): log(start_time, "{0}Receiving data...".format(iteration))
        data = s.recv(550)
        data_len = len(data)
        byte_count += data_len
        if (verbose): log(start_time, "{0}Received {1} bytes".format(iteration, data_len))

        s.close()

        # Check if returned data seems valid. If not, throw AssertionError exception.
        if (verbose):
            if (data[0] == MC_DISCONNECT):
                log(start_time, "Returned data seems valid.")
            else:
                log(start_time, "Returned data is invalid. First byte is {0:#x}.".format(ord(data[0])))

        assert data[0] == MC_DISCONNECT

        # Save response time for later average calculation.
        delta = datetime.now() - net_start_time
        total_delta += delta

        time.sleep(0.1)

    # Calculate the average response time in seconds
    total_response = total_delta.seconds + total_delta.microseconds / 1000000.0
    average_response = total_response / num_checks

    # Decode and split returned skipping the first two bytes.
    info = data[3:].decode(MC_ENCODING).split(MC_DELIMITER)
    motd = info[:]
    del motd[-1] # removing max_players
    del motd[-1] # removing players
    motd = ''.join(motd).replace("\n","") # removing newlines

    return {'motd': motd,
            'players': int(info[-2]),
            'max_players': int(info[-1]),
            'byte_count': byte_count,
            'response_time': average_response}

def main():
    parser = argparse.ArgumentParser(description="This plugin will try to connect to a Minecraft server.");

    parser.add_argument('-H', '--hostname', dest='hostname', metavar='ADDRESS', required=True, help="host name or IP address")
    parser.add_argument('-p', '--port', dest='port', type=int, default=25565, metavar='INTEGER', help="port number (default: 25565)")
    parser.add_argument('-n', '--number-of-checks', dest='num_checks', type=int, default=5, metavar='INTEGER', help="number of checks to get stable average response time (default: 5)")
    parser.add_argument('-m', '--motd', dest='motd', default='A Minecraft Server', metavar='STRING', help="expected motd in server response (default: A Minecraft Server)")
    parser.add_argument('-f', '--warn-on-full', dest='full', action='store_true', help="generate warning if server is full")
    parser.add_argument('-w', '--warning', dest='warning', type=float, default=0.0, metavar='DOUBLE', help="response time to result in warning status (seconds)")
    parser.add_argument('-c', '--critical', dest='critical', type=float, default=0.0, metavar='DOUBLE', help="response time to result in critical status (seconds)")
    parser.add_argument('-t', '--timeout', dest='timeout', type=float, default=10.0, metavar='DOUBLE', help="seconds before connection times out (default: 10)")
    parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help="show details for command-line debugging (Nagios may truncate output)")

    # Parse the arguments. If it failes, exit overriding exit code.
    try:
        args = parser.parse_args()
    except SystemExit:
        print(OUTPUT_UNKNOWN)
        sys.exit(STATE_UNKNOWN)

    try:
        info = get_server_info(args.hostname, args.port, args.num_checks, args.timeout, args.verbose)

        if string.find(info['motd'], args.motd) > -1:
            # Check if response time is above critical level.
            if args.critical and info['response_time'] > args.critical:
                print(OUTPUT_CRITICAL.format("{0} second response time".format(info['response_time']), info['byte_count'], info['response_time'], args.warning, args.critical, args.timeout))
                sys.exit(STATE_CRITICAL)

            # Check if response time is above warning level.
            if args.warning and info['response_time'] > args.warning:
                print(OUTPUT_WARNING.format("{0} second response time".format(info['response_time']), info['byte_count'], info['response_time'], args.warning, args.critical, args.timeout))
                sys.exit(STATE_WARNING)

            # Check if server is full.
            if args.full and info['players'] == info['max_players']:
                print(OUTPUT_WARNING.format("Server full! {0} players online".format(info['players']), info['byte_count'], info['response_time'], args.warning, args.critical, args.timeout))
                sys.exit(STATE_WARNING)

            print(OUTPUT_OK.format("{0}/{1} players online".format(info['players'], info['max_players']), info['byte_count'], info['response_time'], args.warning, args.critical, args.timeout))
            sys.exit(STATE_OK)

        else:
            print(OUTPUT_WARNING.format("Unexpected MOTD, {0}".format(info['motd']), info['byte_count'], info['response_time'], args.warning, args.critical, args.timeout))
            sys.exit(STATE_WARNING)

    except socket.error as msg:
        print(OUTPUT_EXCEPTION.format(msg))
        sys.exit(STATE_CRITICAL)

    except AssertionError:
        print(OUTPUT_EXCEPTION.format("Invalid data returned by server"))
        sys.exit(STATE_CRITICAL)

    except UnicodeDecodeError:
        print(OUTPUT_EXCEPTION.format("Unable to decode server response"))
        sys.exit(STATE_CRITICAL)

if __name__ == "__main__":
    main()

许可文本:

The MIT License

Copyright (c) 2012-2016 Petter Jönsson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Python 3 兼容性补丁(由我编写):

1c1
< #!/usr/bin/env python
---
> #!/usr/bin/env python3
21,22c21,22
< MC_SERVER_LIST_PING = "\xfe"
< MC_DISCONNECT = "\xff"
---
> MC_SERVER_LIST_PING = b"\xfe"
> MC_DISCONNECT = b"\xff"
61c61
<           if (data[0] == MC_DISCONNECT):
---
>           if (data.startswith(MC_DISCONNECT)):
64c64
<               log(start_time, "Returned data is invalid. First byte is {0:#x}.".format(ord(data[0])))
---
>               log(start_time, "Returned data is invalid. First byte is {0:#x}.".format(data[0]))
66c66
<       assert data[0] == MC_DISCONNECT
---
>       assert data.startswith(MC_DISCONNECT)
114c114
<       if string.find(info['motd'], args.motd) > -1:
---
>       if info['motd'].find(args.motd) > -1:

答案2

看起来你可能想要一个基于终端的我的世界客户端。

看看这个:https://github.com/ORelio/Minecraft-Console-Client

相关内容