# 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)