読者です 読者をやめる 読者になる 読者になる

Minecraftのサーバを監視してjoinしているか調べるハック

Perl

いちいちMinecraftを立ち上げて調べるのは面倒なので、Skypeで通知してくれるスクリプトをてきとーに書きました。結構ゴリ押ししてる感があります :)

  • Skypeの通知にはtacochanを使う
  • 1分毎に監視
  • サーバが落ちてたら通知
  • joinしているプレイヤーの数を通知
use 5.016;
use warnings FATAL => 'all';
use utf8;
use AnyEvent;
use AnyEvent::Socket;
use AnyEvent::Handle;
use AnyEvent::HTTP;
use HTTP::Request::Common;

my $TACOCHAN_URL = 'http://localhost:4969';
my @chats = (
    # chatname
    #'#t.akiym/$2ea82baabf4e06d',
);

my @servers = (
    ['localhost' => \@chats],
);

my %info;

my $cv = AE::cv();

my $t; $t = AE::timer 0, 1 * 60, sub {
    for my $server (@servers) {
        my ($host, $port) = parse_hostport($server->[0], 25565);
        tcp_connect $host, $port, sub {
            my ($fh) = @_;
            if (!$fh) {
                my $message = "[$host] 死んでるかも ;(");
                if (exists $info{$host}{downtime}) {
                    # resend
                    if (time() - $info{$host}{downtime} > 60 * 60 * 12) {
                        send_message($server->[1], $message);
                        delete $info{$host}{downtime};
                    }
                } else {
                    send_message($server->[1], $message);
                    $info{$host}{downtime} = time();
                }
                die "connect failed: $!";
            } else {
                if (exists $info{$host}{downtime}) {
                    delete $info{$host}{downtime};
                }
            }

            my $hdl; $hdl = AnyEvent::Handle->new(
                fh => $fh,
                on_error => sub {
                    $_[0]->destroy;
                },
            );
            $hdl->push_write(pack('C2', 0xfe, 0x01));
            $hdl->on_read(sub {
                my @res = split /\0/,
                    join '', map { chr } unpack 'v*', $_[0]->rbuf . "\0";

                if (exists $info{$host}{version}) { # :/
                    if ($info{$host}{player} != $res[4]) {
                        my $message = do {
                            if ($res[4] == 0) {
                                "[$host] プレイヤーがいなくなりました";
                            } else {
                                "[$host] 現在のプレイヤー: $res[4]人";
                            }
                        };
                        send_message($server->[1], $message);
                    }
                }
                $info{$host} = {
                    version     => $res[2],
                    message     => $res[3],
                    player      => $res[4],
                    max_players => $res[5],
                };

                undef $hdl;
            });
        };
    }
};

$cv->recv;

# taken from http://blog.64p.org/entry/20100325/1269535529
sub http_request_by_http_request {
    my $cb = pop @_;
    my ($req, @args) = @_;
    my %headers;
    $req->headers->scan(sub {
        $headers{$_[0]} = $_[1];
    });
    http_request($req->method, $req->uri, body => $req->content, headers => \%headers, @args, $cb);
}

sub send_message {
    my ($chats, $message) = @_;
    for my $chat (@$chats) {
        my $req = POST "$TACOCHAN_URL/send", [chat => $chat, message => $message];
        my $r; $r = http_request_by_http_request($req, sub { undef $r });
    }
}

tacochanのインストールと起動

% cpanm App::Tacochan
% tacochan

とするとhttp://127.0.0.1:4969/にtacochanが立ち上がるので、http://127.0.0.1:4969/sendにchatとmessageのパラメータを渡してあげるとメッセージを送信することができます。(chatというのは#から始まるchatnameのことで、tacochanから確認することができます)


enjoy!