From 35d2ae9a115a17df2267cf0673d59419f4fd2a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Sun, 30 Oct 2016 22:44:34 +0100 Subject: Split status and players to separate classes --- mcwrapper/__init__.py | 23 +++++++++--- mcwrapper/mod.py | 3 ++ mcwrapper/players.py | 49 +++++++++++++++++++++++++ mcwrapper/status.py | 58 ++++++++++++++++++++++++++++++ mcwrapper/wrapper.py | 99 +++++++++++++++------------------------------------ setup.py | 2 ++ 6 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 mcwrapper/players.py create mode 100644 mcwrapper/status.py diff --git a/mcwrapper/__init__.py b/mcwrapper/__init__.py index 18e73aa..f0f1df5 100755 --- a/mcwrapper/__init__.py +++ b/mcwrapper/__init__.py @@ -6,16 +6,27 @@ import argparse from . import prints from . import alarm from .wrapper import MCWrapper +from .status import MCStatus +from .players import MCPlayers from .mod import MoD mcserver_wrapper = None +mcserver_status = None +mcserver_players = None mcserver_mod = None def __wrapper_atexit__(): "This is called when wrapper is exiting" - if mcserver_wrapper is not None: - mcserver_wrapper.clean() + toclean = ( + mcserver_wrapper, + mcserver_status, + mcserver_players, + mcserver_mod + ) + for c in toclean: + if c is not None: + c.clean() def __wrapper_toexit__(): @@ -75,11 +86,13 @@ def main(): alarm.init() - global mcserver_wrapper - mcserver_wrapper = MCWrapper(command, pfile, sfile) + atexit.register(__wrapper_atexit__) + global mcserver_wrapper, mcserver_status, mcserver_players + mcserver_wrapper = MCWrapper(command) signal.signal(signal.SIGTERM, __signal_term__) signal.signal(signal.SIGINT, __signal_term__) - atexit.register(__wrapper_atexit__) + mcserver_status = MCStatus(mcserver_wrapper, sfile) + mcserver_players = MCPlayers(mcserver_wrapper, pfile) mcserver_wrapper.start() if mod_file is not None: diff --git a/mcwrapper/mod.py b/mcwrapper/mod.py index 73200f1..bb580e8 100644 --- a/mcwrapper/mod.py +++ b/mcwrapper/mod.py @@ -12,6 +12,9 @@ class MoD: self.file = file alarm.set("mod-time", period, self.__handler__, repeat=True) + def clean(self): + alarm.unset("mod-time") + def __handler__(self): lines = [] try: diff --git a/mcwrapper/players.py b/mcwrapper/players.py new file mode 100644 index 0000000..c3dabb4 --- /dev/null +++ b/mcwrapper/players.py @@ -0,0 +1,49 @@ +# vim: expandtab ft=python ts=4 sw=4 sts=4: +import os + +from . import prints + +__PLAYERSFILE__ = 'players' + + +class MCPlayers: + "Tracks online players" + def __init__(self, wrapper, file_export=False): + self.players = set() + self.wrapper = wrapper + wrapper.hook_start(self.__reset__) + wrapper.hook_stop(self.__reset__) + wrapper.hook_line('logged in with entity id', self.__user_join__) + wrapper.hook_line('left the game', self.__user_leave__) + self.file_export = file_export + self.__reset__() + + def clean(self): + try: + os.remove(__PLAYERSFILE__) + except FileNotFoundError: + pass + + def __reset__(self): + if self.file_export: + open(__PLAYERSFILE__, 'w') # Just create empty file + + def __user_join__(self, line): + username = line[len('[00:00:00] [Server thread/INFO]: '):] + username = username[:username.index('[')] + prints.info("User '" + username + "' joined server.") + self.players.add(username) + if self.file_export: + with open(__PLAYERSFILE__, 'a') as file: + file.write(username + '\n') + + def __user_leave__(self, line): + username = line[len('[00:00:00] [Server thread/INFO]: '):] + username = username[:username.index(' ')] + prints.info("User '" + username + "' left server.") + self.players.remove(username) + if self.file_export: + with open(__PLAYERSFILE__, 'w') as file: + file.write('\n'.join(self.players)) + if self.players: + file.write('\n') diff --git a/mcwrapper/status.py b/mcwrapper/status.py new file mode 100644 index 0000000..42f8ed8 --- /dev/null +++ b/mcwrapper/status.py @@ -0,0 +1,58 @@ +# vim: expandtab ft=python ts=4 sw=4 sts=4: +import os + +from . import prints + +__STATUSSTRINGS__ = { + 0: "Not running", + 1: "Starting", + 2: "Running", + 3: "Stopping", + } +__STATUSFILE__ = 'status' + + +class MCStatus: + "Tracks server status" + def __init__(self, wrapper, file_export=False): + self.wrapper = wrapper + self.status = 0 + wrapper.hook_start(self.__server_start__) + wrapper.hook_stop(self.__server_stop__) + wrapper.hook_line(': Done', self.__server_started__) + wrapper.hook_line(': Stopping the server', self.__server_stopping__) + self.file_export = file_export + if file_export: + with open(__STATUSFILE__, 'w') as file: + file.write(__STATUSSTRINGS__[0] + '\n') + + def clean(self): + try: + os.remove(__STATUSFILE__) + except FileNotFoundError: + pass + + def __server_start__(self): + self.status = 1 + if self.file_export: + with open(__STATUSFILE__, 'w') as file: + file.write(__STATUSSTRINGS__[1] + '\n') + + def __server_stop__(self): + if self.file_export: + with open(__STATUSFILE__, 'w') as file: + file.write(__STATUSSTRINGS__[0] + '\n') + + def __server_started__(self, line): + prints.info("Server start.") + self.status = 2 + if self.file_export: + with open(__STATUSFILE__, 'w') as file: + file.write(__STATUSSTRINGS__[2] + '\n') + + def __server_stopping__(self, line): + prints.info("Server stop.") + self.status = 3 + if self.file_export: + with open(__STATUSFILE__, 'w') as file: + file.write(__STATUSSTRINGS__[3] + '\n') diff --git a/mcwrapper/wrapper.py b/mcwrapper/wrapper.py index 3e9a0d7..96f74d5 100644 --- a/mcwrapper/wrapper.py +++ b/mcwrapper/wrapper.py @@ -5,30 +5,21 @@ import subprocess import time from threading import Thread -from .import prints - -__STATUSSTRINGS__ = { - 0: "Not running", - 1: "Starting", - 2: "Running", - 3: "Stopping", - } +from .import prints __INPUTPIPE__ = 'input_pipe' -__STATUSFILE__ = 'status' -__PLAYERSFILE__ = 'players' __PIDFILE__ = 'server.pid' class MCWrapper: "Minecraft server wrapper class" - def __init__(self, command, statusfile=False, playersfile=False): - self.players = set() - self.status = 0 + def __init__(self, command): self.process = None self.command = command - self.statusfile = statusfile - self.plaersfile = playersfile + self._running = False + self._hook_start = [] + self._hook_stop = [] + self._hook_line = [] prints.info("Server wrapper initializing") if os.path.isfile(__PIDFILE__): with open(__PIDFILE__) as file: @@ -45,11 +36,6 @@ class MCWrapper: os.mkfifo(__INPUTPIPE__, 0o640) except FileExistsError: pass - if statusfile: - with open(__STATUSFILE__, 'w') as file: - file.write(__STATUSSTRINGS__[0] + '\n') - if playersfile: - open(__PLAYERSFILE__, 'w') self.inputthread = Thread(target=self.__input_thread__, daemon=True) self.outputhread = Thread(target=self.__output_thread__, @@ -66,14 +52,6 @@ class MCWrapper: os.remove(__PIDFILE__) except FileNotFoundError: pass - try: - os.remove(__STATUSFILE__) - except FileNotFoundError: - pass - try: - os.remove(__STATUSFILE__) - except FileNotFoundError: - pass def start(self): "Start Minecraft server" @@ -81,12 +59,11 @@ class MCWrapper: self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, start_new_session=False) + for h in self._hook_start: + h() + self._running = True with open(__PIDFILE__, "w") as file: file.write(str(self.process.pid)) - self.status = 1 - if self.statusfile: - with open(__STATUSFILE__, 'w') as file: - file.write(__STATUSSTRINGS__[1] + '\n') if not self.inputthread.is_alive(): self.inputthread.start() if not self.outputhread.is_alive(): @@ -98,14 +75,15 @@ class MCWrapper: self.process.stdin.write(bytes( "/stop\n", sys.getdefaultencoding())) self.process.stdin.flush() + self._running = False def running(self): "Returns True if mc server is running. Othervise False." - return bool(self.status) + return True def write_to_terminal(self, text): "Write to server terminal. If server not running it does nothing" - if self.status == 2: + if self._running: prints.info("Input: " + text.rstrip(), 1) self.process.stdin.write(bytes(text, sys.getdefaultencoding())) self.process.stdin.flush() @@ -113,57 +91,36 @@ class MCWrapper: else: return False - def __user_join__(self, username): - prints.info("User '" + username + "' joined server.") - self.players.add(username) - if self.plaersfile: - with open(__PLAYERSFILE__, 'a') as file: - file.write(username + '\n') + def hook_start(self, handler): + self._hook_start.append(handler) + + def hook_stop(self, handler): + self._hook_stop.append(handler) - def __user_leave__(self, username): - prints.info("User '" + username + "' left server.") - self.players.remove(username) - if self.plaersfile: - with open(__PLAYERSFILE__, 'w') as file: - file.writelines(self.players) - if self.players: - file.write('\n') + def hook_line(self, contains, handler): + n = dict() + n["contains"] = contains + n["handler"] = handler + self._hook_line.append(n) def __parse_line__(self, line): - if ': Done' in line: - prints.info("Server start.") - self.status = 2 - if self.statusfile: - with open(__STATUSFILE__, 'w') as file: - file.write(__STATUSSTRINGS__[2] + '\n') - elif ': Stopping the server' in line: - prints.info("Server stop.") - self.status = 3 - if self.statusfile: - with open(__STATUSFILE__, 'w') as file: - file.write(__STATUSSTRINGS__[3] + '\n') - elif 'logged in with entity id' in line: - name = line[len('[00:00:00] [Server thread/INFO]: '):] - name = name[:name.index('[')] - self.__user_join__(name) - elif 'left the game' in line: - name = line[len('[00:00:00] [Server thread/INFO]: '):] - name = name[:name.index(' ')] - self.__user_leave__(name) + i = 0 + while i < len(self._hook_line): + if self._hook_line[i]["contains"] in line: + self._hook_line[i]["handler"](line) + i += 1 def __output_thread__(self): for linen in self.process.stdout: line = linen.decode(sys.getdefaultencoding()) prints.info(line.rstrip(), 2, notime=True) self.__parse_line__(line.rstrip()) - if self.statusfile: - with open(__STATUSFILE__, 'w') as file: - file.write(__STATUSSTRINGS__[0] + '\n') def __input_thread__(self): with open(__INPUTPIPE__, 'r') as pipe: while True: line = pipe.readline().rstrip() + # TODO use polling if line: self.write_to_terminal(line + "\n") else: diff --git a/setup.py b/setup.py index 499bab5..ce6d681 100755 --- a/setup.py +++ b/setup.py @@ -10,6 +10,8 @@ try: long_description = pypandoc.convert(readme_file, 'rst', format='md') except (IOError, ImportError): print("Pandoc not found. Long_description conversion failure.") +except RuntimeError as e: + print("Pandoc conversion failed: " + str(e)) setup( name='mcserver-wrapper', -- cgit v1.2.3