import argparse import logging import re import subprocess import threading from typing import List import message_queue LOG = logging.getLogger(__name__) class ProcessWrapper: """Iterator that spawns a process and yields lines from its stdout.""" def __init__(self, command: List[str]): self.proc = subprocess.Popen( " ".join(command), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True, ) def __iter__(self): return iter(self.proc.stdout.readline, "") def send(self, msg): self.proc.stdin.write(msg) self.proc.stdin.flush() def wait(self): return self.proc.wait() @property def closed(self) -> bool: return self.proc.returncode is not None def send_process_output( process: ProcessWrapper, msg_queue: message_queue.MessageQueue ): # "[07:36:28] [Server thread/INFO] [minecraft/DedicatedServer]: test" log = LOG.getChild("process_output") prog = re.compile(r"(\[.*\] )?\[(.*)\] \[(.*)\]: <(.*)> (.*)") for line in process: log.info(line.rstrip("\n")) result = prog.search(line) if result: log.info( "user: {} msg: {}".format( result.group(4), result.group(5).rstrip("\n"), ) ) msg_queue.add( {"user": result.group(4), "msg": result.group(5).rstrip("\n")}, ) def relay_queue_input( process: ProcessWrapper, msg_queue: message_queue.MessageQueue ): log = LOG.getChild("relay_input") while not process.closed: try: for message in msg_queue: log.debug(message) if "command" in message: command = message["command"] + "\n" log.debug("forwarding to process: {!r}".format(command)) process.send(command) else: log.debug(message) except Exception as e: log.exception(e) def main(): logging.basicConfig(level=logging.DEBUG) parser = argparse.ArgumentParser() parser.add_argument("--matrix_server", required=True) parser.add_argument("--matrix_server_port", type=int, default=5001) parser.add_argument("command", nargs=argparse.REMAINDER) args = parser.parse_args() LOG.info("Running Minecraft Server Wrapper") wrapper = ProcessWrapper(args.command) queue = message_queue.MessageQueue( host=args.matrix_server, port=args.matrix_server_port, side=message_queue.Side.CLIENT, ) send_worker = threading.Thread( target=send_process_output, args=(wrapper, queue), daemon=True, ) receive_worker = threading.Thread( target=relay_queue_input, args=(wrapper, queue), daemon=True, ) send_worker.start() receive_worker.start() LOG.info("All threads created") send_worker.join() receive_worker.join() queue.close() LOG.info("All threads terminated") return wrapper.wait() if __name__ == "__main__": main()