From c6830a35dbe234fe91bee57e3f517d3c62a71972 Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Mon, 6 Oct 2014 21:40:09 +0200 Subject: [PATCH 1/2] Added RCon Observer It does the rcons! And the observings! --- overviewer_core/observer.py | 41 ++++++++++++++++ overviewer_core/rcon.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 overviewer_core/rcon.py diff --git a/overviewer_core/observer.py b/overviewer_core/observer.py index e17c8f5..e068acb 100644 --- a/overviewer_core/observer.py +++ b/overviewer_core/observer.py @@ -19,6 +19,7 @@ import progressbar import sys import os import json +import rcon class Observer(object): """Base class that defines the observer interface. @@ -392,3 +393,43 @@ class ServerAnnounceObserver(Observer): self.target_handle.write('say %s\n' % output) self.target_handle.flush() + +# Fair amount of code duplication incoming +# Perhaps both ServerAnnounceObserver and RConObserver +# could share the percentage interval code. + +class RConObserver(Observer): + """Send the output to a Minecraft server via rcon""" + + def __init__(self, target, password, port=25575, pct_interval=10): + self.pct_interval = pct_interval + self.conn = rcon.RConConnection(target, port) + self.conn.login(password) + self.last_update = 0 + super(RConObserver, self).__init__() + + def start(self, max_value): + self._send_output("Starting render of %d total tiles" % max_value) + super(RConObserver, self).start(max_value) + + def finish(self): + self._send_output("Render complete!") + super(RConObserver, self).finish() + self.conn.close() + + def update(self, current_value): + super(RConObserver, self).update(current_value) + if self._need_update(): + self._send_output('Rendered %d of %d tiles, %d%% complete' % + (self.get_current_value(), self.get_max_value(), + self.get_percentage())) + self.last_update = current_value + + def _need_update(self): + return self.get_percentage() - \ + (self.last_update * 100.0 / self.get_max_value()) >= self.pct_interval + + def _send_output(self, output): + self.conn.command("say", output) + + diff --git a/overviewer_core/rcon.py b/overviewer_core/rcon.py new file mode 100644 index 0000000..52a1434 --- /dev/null +++ b/overviewer_core/rcon.py @@ -0,0 +1,95 @@ +# This file is part of the Minecraft Overviewer. +# +# Minecraft Overviewer is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# Minecraft Overviewer is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the Overviewer. If not, see . + +import socket +#from enum import Enum +import struct +import select + +class RConException(Exception): + def __init__(self, request_id, reason): + self.request_id = request_id + self.reason = reason + + def __str__(self): + return ("Failed RCon request with request ID %d, reason %s" % + (self.request_id, self.reason)) + +# In D, enums are just that, enums. They're a group of named constants, +# sometimes with a tag, sometimes anonymous. +# In Python, Enums use the same syntax as class objects that derive from +# the "Enum" base class, even though they are not normal python classes +# and work as singletons anyway, but instead of using a different syntax, +# Python instead decided to have a chapter in their docs about how Enums +# are different from regular classes while looking exactly the same. +# You can look at said document of failure right here: +# https://docs.python.org/3/library/enum.html#how-are-enums-different +# +# "D has too much shit going on for me" -- agrif, 2014 +# +# Fortunately, we're not allowed to use Enums in Python 2. + +#class RConType(Enum): +# command = 2 +# login = 3 + + +class RConConnection(): + rid = 0 + + def __init__(self, target, port): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((target, port)) + + def send(self, t, payload): + self.rid = self.rid + 1 + header = struct.pack(" Date: Mon, 6 Oct 2014 21:52:21 +0200 Subject: [PATCH 2/2] Add documentation for RConObserver --- docs/config.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index c617f52..c8892b0 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -376,6 +376,31 @@ Observers once for every 1% of progress. **Required** + + ``RConObserver(target, password[, port][, pct_interval])`` + This Observer will announce render progress with the server's ``say`` + command through RCon. + + * ``target=
`` + Address of the target Minecraft server. + + **Required** + + * ``password=`` + The server's rcon password. + + **Required** + + * ``port=`` + Port on which the Minecraft server listens for incoming RCon connections. + + **Default:** ``25575`` + + * ``pct_interval=`` + Percentage interval in which the progress should be announced, the same as + for ``ServerAnnounceObserver``. + + **Default:** ``10``