diff options
Diffstat (limited to 'mcwrapper/wrapper.py')
-rw-r--r-- | mcwrapper/wrapper.py | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/mcwrapper/wrapper.py b/mcwrapper/wrapper.py new file mode 100644 index 0000000..1896266 --- /dev/null +++ b/mcwrapper/wrapper.py @@ -0,0 +1,175 @@ +# vim: expandtab ft=python ts=4 sw=4 sts=4: +import os +import sys +import subprocess +import time +from threading import Thread + +from .import prints + +__STATUSSTRINGS__ = { + 0: "Not running", + 1: "Starting", + 2: "Running", + 3: "Stopping", + } + +__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 + self.process = None + self.command = command + self.statusfile = statusfile + self.plaersfile = playersfile + prints.info("Server wrapper initializing") + if os.path.isfile(__PIDFILE__): + with open(__PIDFILE__) as file: + lpid = int(file.readline()) + try: + os.kill(lpid, 0) + except OSError: + prints.warning("Detected forced termination of previous server " + "wrapper instance.") + else: + prints.error("Another wrapper is running with given identifier.", + -1, 1) + try: + 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__, + daemon=True) + + def clean(self): + "Cleans files generated by wrapper" + prints.info("Server wrapper clean.") + try: + os.remove(__INPUTPIPE__) + except FileNotFoundError: + pass + try: + os.remove(__PIDFILE__) + except FileNotFoundError: + pass + try: + os.remove(__STATUSFILE__) + except FileNotFoundError: + pass + try: + os.remove(__STATUSFILE__) + except FileNotFoundError: + pass + + def execstart(self): + "Start execution of Minecraft server and hold until its exits" + self.start() + self.process.wait() + + def start(self): + "Start Minecraft server" + self.process = subprocess.Popen( + self.command, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + start_new_session=False) + 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(): + self.outputhread.start() + + def stop(self): + "Sends /stop command to Minecraft server" + if self.running(): + self.process.stdin.write(bytes( + "/stop\n", sys.getdefaultencoding())) + self.process.stdin.flush() + + def running(self): + "Returns True if mc server is running. Othervise False." + return bool(self.status) + + def write_to_terminal(self, text): + "Write to server terminal. If server not running it does nothing" + if self.status == 2: + prints.info("Input: " + text, 1) + self.process.stdin.write(bytes(text, sys.getdefaultencoding())) + self.process.stdin.flush() + return True + 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 __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 __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) + + 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() + if line: + self.write_to_terminal(line + "\n") + else: + time.sleep(3) |