aboutsummaryrefslogtreecommitdiff
path: root/mcwrapper/wrapper.py
diff options
context:
space:
mode:
Diffstat (limited to 'mcwrapper/wrapper.py')
-rw-r--r--mcwrapper/wrapper.py175
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)