aboutsummaryrefslogtreecommitdiff
path: root/mcwrapper
diff options
context:
space:
mode:
Diffstat (limited to 'mcwrapper')
-rwxr-xr-xmcwrapper/__init__.py23
-rw-r--r--mcwrapper/mod.py3
-rw-r--r--mcwrapper/players.py49
-rw-r--r--mcwrapper/status.py58
-rw-r--r--mcwrapper/wrapper.py99
5 files changed, 156 insertions, 76 deletions
diff --git a/mcwrapper/__init__.py b/mcwrapper/__init__.py
index 18e73aa..f0f1df5 100755
--- a/mcwrapper/__init__.py
+++ b/mcwrapper/__init__.py
@@ -6,16 +6,27 @@ import argparse
from . import prints
from . import alarm
from .wrapper import MCWrapper
+from .status import MCStatus
+from .players import MCPlayers
from .mod import MoD
mcserver_wrapper = None
+mcserver_status = None
+mcserver_players = None
mcserver_mod = None
def __wrapper_atexit__():
"This is called when wrapper is exiting"
- if mcserver_wrapper is not None:
- mcserver_wrapper.clean()
+ toclean = (
+ mcserver_wrapper,
+ mcserver_status,
+ mcserver_players,
+ mcserver_mod
+ )
+ for c in toclean:
+ if c is not None:
+ c.clean()
def __wrapper_toexit__():
@@ -75,11 +86,13 @@ def main():
alarm.init()
- global mcserver_wrapper
- mcserver_wrapper = MCWrapper(command, pfile, sfile)
+ atexit.register(__wrapper_atexit__)
+ global mcserver_wrapper, mcserver_status, mcserver_players
+ mcserver_wrapper = MCWrapper(command)
signal.signal(signal.SIGTERM, __signal_term__)
signal.signal(signal.SIGINT, __signal_term__)
- atexit.register(__wrapper_atexit__)
+ mcserver_status = MCStatus(mcserver_wrapper, sfile)
+ mcserver_players = MCPlayers(mcserver_wrapper, pfile)
mcserver_wrapper.start()
if mod_file is not None:
diff --git a/mcwrapper/mod.py b/mcwrapper/mod.py
index 73200f1..bb580e8 100644
--- a/mcwrapper/mod.py
+++ b/mcwrapper/mod.py
@@ -12,6 +12,9 @@ class MoD:
self.file = file
alarm.set("mod-time", period, self.__handler__, repeat=True)
+ def clean(self):
+ alarm.unset("mod-time")
+
def __handler__(self):
lines = []
try:
diff --git a/mcwrapper/players.py b/mcwrapper/players.py
new file mode 100644
index 0000000..c3dabb4
--- /dev/null
+++ b/mcwrapper/players.py
@@ -0,0 +1,49 @@
+# vim: expandtab ft=python ts=4 sw=4 sts=4:
+import os
+
+from . import prints
+
+__PLAYERSFILE__ = 'players'
+
+
+class MCPlayers:
+ "Tracks online players"
+ def __init__(self, wrapper, file_export=False):
+ self.players = set()
+ self.wrapper = wrapper
+ wrapper.hook_start(self.__reset__)
+ wrapper.hook_stop(self.__reset__)
+ wrapper.hook_line('logged in with entity id', self.__user_join__)
+ wrapper.hook_line('left the game', self.__user_leave__)
+ self.file_export = file_export
+ self.__reset__()
+
+ def clean(self):
+ try:
+ os.remove(__PLAYERSFILE__)
+ except FileNotFoundError:
+ pass
+
+ def __reset__(self):
+ if self.file_export:
+ open(__PLAYERSFILE__, 'w') # Just create empty file
+
+ def __user_join__(self, line):
+ username = line[len('[00:00:00] [Server thread/INFO]: '):]
+ username = username[:username.index('[')]
+ prints.info("User '" + username + "' joined server.")
+ self.players.add(username)
+ if self.file_export:
+ with open(__PLAYERSFILE__, 'a') as file:
+ file.write(username + '\n')
+
+ def __user_leave__(self, line):
+ username = line[len('[00:00:00] [Server thread/INFO]: '):]
+ username = username[:username.index(' ')]
+ prints.info("User '" + username + "' left server.")
+ self.players.remove(username)
+ if self.file_export:
+ with open(__PLAYERSFILE__, 'w') as file:
+ file.write('\n'.join(self.players))
+ if self.players:
+ file.write('\n')
diff --git a/mcwrapper/status.py b/mcwrapper/status.py
new file mode 100644
index 0000000..42f8ed8
--- /dev/null
+++ b/mcwrapper/status.py
@@ -0,0 +1,58 @@
+# vim: expandtab ft=python ts=4 sw=4 sts=4:
+import os
+
+from . import prints
+
+__STATUSSTRINGS__ = {
+ 0: "Not running",
+ 1: "Starting",
+ 2: "Running",
+ 3: "Stopping",
+ }
+__STATUSFILE__ = 'status'
+
+
+class MCStatus:
+ "Tracks server status"
+ def __init__(self, wrapper, file_export=False):
+ self.wrapper = wrapper
+ self.status = 0
+ wrapper.hook_start(self.__server_start__)
+ wrapper.hook_stop(self.__server_stop__)
+ wrapper.hook_line(': Done', self.__server_started__)
+ wrapper.hook_line(': Stopping the server', self.__server_stopping__)
+ self.file_export = file_export
+ if file_export:
+ with open(__STATUSFILE__, 'w') as file:
+ file.write(__STATUSSTRINGS__[0] + '\n')
+
+ def clean(self):
+ try:
+ os.remove(__STATUSFILE__)
+ except FileNotFoundError:
+ pass
+
+ def __server_start__(self):
+ self.status = 1
+ if self.file_export:
+ with open(__STATUSFILE__, 'w') as file:
+ file.write(__STATUSSTRINGS__[1] + '\n')
+
+ def __server_stop__(self):
+ if self.file_export:
+ with open(__STATUSFILE__, 'w') as file:
+ file.write(__STATUSSTRINGS__[0] + '\n')
+
+ def __server_started__(self, line):
+ prints.info("Server start.")
+ self.status = 2
+ if self.file_export:
+ with open(__STATUSFILE__, 'w') as file:
+ file.write(__STATUSSTRINGS__[2] + '\n')
+
+ def __server_stopping__(self, line):
+ prints.info("Server stop.")
+ self.status = 3
+ if self.file_export:
+ with open(__STATUSFILE__, 'w') as file:
+ file.write(__STATUSSTRINGS__[3] + '\n')
diff --git a/mcwrapper/wrapper.py b/mcwrapper/wrapper.py
index 3e9a0d7..96f74d5 100644
--- a/mcwrapper/wrapper.py
+++ b/mcwrapper/wrapper.py
@@ -5,30 +5,21 @@ import subprocess
import time
from threading import Thread
-from .import prints
-
-__STATUSSTRINGS__ = {
- 0: "Not running",
- 1: "Starting",
- 2: "Running",
- 3: "Stopping",
- }
+from .import prints
__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
+ def __init__(self, command):
self.process = None
self.command = command
- self.statusfile = statusfile
- self.plaersfile = playersfile
+ self._running = False
+ self._hook_start = []
+ self._hook_stop = []
+ self._hook_line = []
prints.info("Server wrapper initializing")
if os.path.isfile(__PIDFILE__):
with open(__PIDFILE__) as file:
@@ -45,11 +36,6 @@ class MCWrapper:
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__,
@@ -66,14 +52,6 @@ class MCWrapper:
os.remove(__PIDFILE__)
except FileNotFoundError:
pass
- try:
- os.remove(__STATUSFILE__)
- except FileNotFoundError:
- pass
- try:
- os.remove(__STATUSFILE__)
- except FileNotFoundError:
- pass
def start(self):
"Start Minecraft server"
@@ -81,12 +59,11 @@ class MCWrapper:
self.command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
start_new_session=False)
+ for h in self._hook_start:
+ h()
+ self._running = True
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():
@@ -98,14 +75,15 @@ class MCWrapper:
self.process.stdin.write(bytes(
"/stop\n", sys.getdefaultencoding()))
self.process.stdin.flush()
+ self._running = False
def running(self):
"Returns True if mc server is running. Othervise False."
- return bool(self.status)
+ return True
def write_to_terminal(self, text):
"Write to server terminal. If server not running it does nothing"
- if self.status == 2:
+ if self._running:
prints.info("Input: " + text.rstrip(), 1)
self.process.stdin.write(bytes(text, sys.getdefaultencoding()))
self.process.stdin.flush()
@@ -113,57 +91,36 @@ class MCWrapper:
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 hook_start(self, handler):
+ self._hook_start.append(handler)
+
+ def hook_stop(self, handler):
+ self._hook_stop.append(handler)
- 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 hook_line(self, contains, handler):
+ n = dict()
+ n["contains"] = contains
+ n["handler"] = handler
+ self._hook_line.append(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)
+ i = 0
+ while i < len(self._hook_line):
+ if self._hook_line[i]["contains"] in line:
+ self._hook_line[i]["handler"](line)
+ i += 1
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()
+ # TODO use polling
if line:
self.write_to_terminal(line + "\n")
else: