aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmcwrapper227
1 files changed, 227 insertions, 0 deletions
diff --git a/mcwrapper b/mcwrapper
new file mode 100755
index 0000000..b3cf223
--- /dev/null
+++ b/mcwrapper
@@ -0,0 +1,227 @@
+#!/usr/bin/env python3
+import os
+import sys
+import re
+import subprocess
+import datetime
+import signal
+import time
+from threading import Thread
+
+__log_prefix__ = '/home/minecraft/log/'
+__folderprefix__ = '/dev/shm/'
+__folder__ = __folderprefix__
+__players__ = 'players'
+__status__ = 'status'
+__pipe__ = 'input_pipe'
+__log__ = 'none.log'
+
+__STATUSSTRINGS__ = { 0: "Not running",
+ 1: "Starting",
+ 2: "Running",
+ 3: "Stopping"
+ }
+
+def __setfiles__(identifier):
+ global __folder__
+ global __players__
+ global __status__
+ global __pipe__
+ global __log__
+ __folder__ = __folderprefix__ + 'mcwrapper_' + identifier + '/'
+ __players__ = __folder__ + 'players'
+ __status__ = __folder__ + 'status'
+ __pipe__ = __folder__ + 'input_pipe'
+ __log__ = __log_prefix__ + identifier + '/' + \
+ datetime.datetime.now().strftime('%y-%m-%d-%H-%M-%S')
+
+def __user_join__(username):
+ print("User '" + username + "' joined server.")
+ with open(__players__, 'a') as f:
+ f.write(username + '\n')
+
+def __user_leave__(username):
+ print("User '" + username + "' left server.")
+ players = set()
+ with open(__players__) as f:
+ for line in f:
+ pl = line.rstrip()
+ if pl != username:
+ players.add(line.rstrip())
+ with open(__players__, 'w') as f:
+ f.writelines(players)
+ if players:
+ f.write('\n')
+
+
+def __server_init__(identifier):
+ __setfiles__(identifier)
+ __server_clean__() # Clean before execute
+ print("Wrapper initializing with identifier: " + identifier)
+ try:
+ os.mkdir(__folder__)
+ except Exception:
+ pass
+ if os.path.isfile(__status__):
+ sys.exit("Server status file already exists. Is another wrapper running?")
+ with open(__status__, 'w') as f:
+ f.write(__STATUSSTRINGS__[1])
+ os.mkfifo(__pipe__, 0o640)
+
+def __server_start__():
+ print("Server start.")
+ with open(__status__, 'w') as f:
+ f.write(__STATUSSTRINGS__[2])
+ pass
+
+def __server_stop__():
+ print("Server stop.")
+ with open(__status__, 'w') as f:
+ f.write(__STATUSSTRINGS__[3])
+ pass
+
+def __server_clean__():
+ print("Wrapper clean.")
+ try:
+ os.remove(__players__)
+ except Exception:
+ pass
+ try:
+ os.remove(__status__)
+ except Exception:
+ pass
+ try:
+ os.remove(__pipe__)
+ except Exception:
+ pass
+
+def __parse_line__(line):
+ if ': Done' in line:
+ __server_start__()
+ elif ': Stopping the server' in line:
+ __server_stop__()
+ elif 'logged in with entity id' in line:
+ name = line[len('[00:00:00] [Server thread/INFO]: '):]
+ name = name[:name.index('[')]
+ __user_join__(name)
+ elif 'left the game' in line:
+ name = line[len('[00:00:00] [Server thread/INFO]: '):]
+ name = name[:name.index(' ')]
+ __user_leave__(name)
+
+#################################################################################
+
+class __InputThread__(Thread):
+ def __init__(self, pipein, pipeprocess):
+ Thread.__init__(self, name='InputThread')
+ self.pipein = pipein
+ self.pipeprocess = pipeprocess
+ self.stopread = False
+ def stopexec(self):
+ self.stopread = True
+ def wake(self):
+ with open(__pipe__, 'w') as f:
+ f.write("\n")
+ f.flush()
+ def run(self):
+ with open(self.pipein, 'r') as p:
+ while not self.stopread:
+ ln = p.readline()
+ if ln:
+ print("Input: " + ln, end="")
+ self.pipeprocess.write(bytes(ln, sys.getdefaultencoding()))
+ self.pipeprocess.flush()
+ else:
+ time.sleep(1)
+ try:
+ os.remove(__pipe__)
+ except Exception:
+ pass
+
+def __server_send_stop__():
+ global prc
+ prc.stdin.write(bytes("/stop\n", sys.getdefaultencoding()))
+ prc.stdin.flush()
+
+def mcexec(identifier, cmd):
+ """Executes cmd and parses output for server status changes.
+
+ cmd - List of program and arguments to be executed.
+ logfile - Path in string. To entered file will be printed out full process output.
+ """
+ global prc
+ __server_init__(identifier)
+ try:
+ os.makedirs(__log_prefix__ + identifier)
+ except Exception:
+ pass
+ prc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ inputThread = __InputThread__(__pipe__, prc.stdin)
+ inputThread.start()
+ inputThread.wake() # Input thread is stack in waiting for first line
+ for linen in prc.stdout:
+ line = linen.decode(sys.getdefaultencoding())
+ with open(__log__, 'a') as flg:
+ flg.write(line)
+ __parse_line__(line.rstrip())
+ inputThread.stopexec()
+ __server_clean__()
+
+#################################################################################
+
+def __get_status__(identifier):
+ if not os.path.isfile(__status__):
+ return 0
+ else:
+ with open(__status__) as f:
+ status = f.readline().rstrip()
+ for key, val in __STATUSSTRINGS__.items():
+ if val == status:
+ return key
+
+def server_stop(identifier):
+ __setfiles__(identifier)
+ status = __get_status__(identifier)
+ if status == 0:
+ sys.exit("Such server is not running")
+ elif status == 1:
+ print("Server is starting. Waiting...")
+ while __get_status__(identifier) != 2:
+ time.sleep(1)
+ elif status == 3:
+ sys.exit("Server already stops")
+ with open(__pipe__, 'w') as f:
+ f.write("/stop\n")
+ f.flush()
+ while os.path.isfile(__status__):
+ pass
+
+def server_say(identifier, message):
+ __setfiles__(identifier)
+ if __get_status__(identifier) != 2:
+ sys.exit("Server is not running")
+ with open(__pipe__, 'w') as f:
+ f.write("/say " + ' '.join(map(str, message)))
+ f.flush()
+
+#################################################################################
+
+def __signal_term__(_signo, _stack_frame):
+ __server_send_stop__()
+
+if __name__ == '__main__':
+ todo = sys.argv[1]
+ identifier = sys.argv[2]
+ if todo == 'start':
+ signal.signal(signal.SIGTERM, __signal_term__)
+ cmd = sys.argv[3:]
+ mcexec(identifier, cmd)
+ __server_clean__()
+ elif todo == 'stop':
+ server_stop(identifier)
+ elif todo == 'say':
+ message = sys.argv[3:]
+ server_say(identifier, message)
+ else:
+ print("unknown action: " + todo)