aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md30
-rwxr-xr-xmcwrapper/__init__.py40
-rw-r--r--mcwrapper/alarm.py66
-rw-r--r--mcwrapper/mod.py24
-rw-r--r--mcwrapper/wrapper.py15
5 files changed, 145 insertions, 30 deletions
diff --git a/README.md b/README.md
index 7d3ee28..52ea73e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
MCSERVER-WRAPPER
================
Minecraft server wrapper written in Python3 that extracts server status and list
-of online players.
+of online players and more.
Requires:
-----------------
@@ -34,11 +34,16 @@ positional arguments:
command Command to be executed to start Minecraft server.
optional arguments:
- -h, --help show this help message and exit
- --verbose, -v Increase verbose level of output
- --quiet, -q Decrease verbose level of output
- --status-file, -s Outputs server status to file "status"
- --players-file, -p Outputs list of online players to file "players"
+ -h, --help show this help message and exit
+ --verbose, -v Increase verbose level of output
+ --quiet, -q Decrease verbose level of output
+ --status-file, -s Outputs server status to file "status"
+ --players-file, -p Outputs list of online players to file "players"
+ --mod-file MOD_FILE, -m MOD_FILE
+ Prints periodically random line from given file as
+ message of the day.
+ --mod-time MOD_TIME Period used for message of the day in seconds. In
+ default 900 (15 minutes).
```
### How it works
@@ -74,8 +79,11 @@ This file in in status directory and is named as `players`. If server is running
it contains online players. Player name per line. If server isn't running, it
content don't have to be valid.
-MCWRAPPER-TERMINAL
-------------------
-This application is going to be used for interactive terminal access to minecraft
-server console. It should use latest minecraft server log as input and output will
-be pushed to input pipe of mcwrapper. This app is currently in development.
+### Message of the day
+This prints to players various short messages in given interval. Message is from
+file passed as --mod-file and it's randomly selected line. This file is read on
+wrapper start, so if you edit it while it's running, no change will happen unless
+you send USR1 signal to mcwrapper server. You can use this simple script:
+```
+[ -f server.pid ] && kill -USR1 $(cat server.pid)
+```
diff --git a/mcwrapper/__init__.py b/mcwrapper/__init__.py
index 2ae3c2a..dec7194 100755
--- a/mcwrapper/__init__.py
+++ b/mcwrapper/__init__.py
@@ -1,28 +1,27 @@
# vim: expandtab ft=python ts=4 sw=4 sts=4:
-import os
-import sys
-import subprocess
import signal
-import time
import atexit
import argparse
-from threading import Thread
from . import prints
-from . import wrapper
+from . import alarm
from .wrapper import MCWrapper
+from .mod import MoD
mcserver_wrapper = None
+mcserver_mod = None
def __wrapper_atexit__():
"This is called when wrapper is exiting"
- mcserver_wrapper.clean()
+ if mcserver_wrapper is not None:
+ mcserver_wrapper.clean()
def __wrapper_toexit__():
"This function is called when system signalizes that mcwrapper should exit"
- mcserver_wrapper.stop()
+ if mcserver_wrapper is not None:
+ mcserver_wrapper.stop()
def __signal_term__(_signo, _stack_frame):
@@ -36,6 +35,12 @@ __HELP_DESC__ = """
"""
+def reload():
+ "Reloads input files. Currently applicable only on mod."
+ if mcserver_mod is not None:
+ mcserver_mod.load_mods()
+
+
def main():
"Main function"
global verbose_level
@@ -49,6 +54,12 @@ def main():
parser.add_argument('--players-file', '-p', action='store_true',
help="""Outputs list of online players to file
\"players\" """)
+ parser.add_argument('--mod-file', '-m', type=str,
+ help="""Prints periodically random line from
+ given file as message of the day.""")
+ parser.add_argument('--mod-time', type=int,
+ help="""Period used for message of the day in
+ seconds. In default 900 (15 minutes).""")
parser.add_argument('command', nargs=argparse.REMAINDER,
help="""Command to be executed to start Minecraft
server.""")
@@ -58,20 +69,31 @@ def main():
command = args.command
sfile = args.status_file
pfile = args.players_file
+ mod_file = args.mod_file
+ mod_time = args.mod_time
if not command:
parser.print_help()
return
+ # Just small hack to not open minecraft server gui
if 'nogui' not in command:
command.append('nogui')
+ alarm.init()
+ signal.signal(signal.SIGUSR1, reload)
+ signal.signal(signal.SIGUSR2, reload) # probably can be used for something else in future
+
global mcserver_wrapper
mcserver_wrapper = MCWrapper(command, pfile, sfile)
signal.signal(signal.SIGTERM, __signal_term__)
signal.signal(signal.SIGINT, __signal_term__)
atexit.register(__wrapper_atexit__)
- mcserver_wrapper.execstart()
+ mcserver_wrapper.start()
+ if mod_file is not None:
+ global mcserver_mod
+ mcserver_mod = MoD(mcserver_wrapper, mod_file, mod_time or 900)
+ mcserver_wrapper.process.wait()
if __name__ == '__main__':
main()
diff --git a/mcwrapper/alarm.py b/mcwrapper/alarm.py
new file mode 100644
index 0000000..de61741
--- /dev/null
+++ b/mcwrapper/alarm.py
@@ -0,0 +1,66 @@
+# vim: expandtab ft=python ts=4 sw=4 sts=4:
+import signal
+import time
+
+# Dict with alarms. Alarm is dictionary with initial time, alarm time and
+# handler
+__alarms__ = dict()
+__alarm_wait__ = None
+
+
+def __handler__(signum, frame):
+ if __alarm_wait__["arg"] is not None:
+ __alarm_wait__["handler"](__alarm_wait__["arg"])
+ else:
+ __alarm_wait__["handler"]()
+ if not __alarm_wait__["repeat"]:
+ __alarms__.pop(__alarm_wait__["name"])
+ else:
+ __alarm_wait__["time"] = __alarm_wait__["time"] + \
+ __alarm_wait__["timeout"]
+ __update__()
+
+
+def __update__():
+ lowest = None
+ lowest_time = None
+ now = time.time()
+ for name, al in __alarms__.items():
+ t = al["time"] + al["timeout"] - now
+ if lowest_time is None or lowest_time > t:
+ lowest_time = t
+ lowest = al
+ global __alarm_wait__
+ if lowest is not None:
+ if lowest_time < 1:
+ # Less then second is missed alarm. Fire handler.
+ __alarm_wait__ = lowest
+ __handler__(None, None)
+ return
+ __alarm_wait__ = lowest
+ signal.alarm(int(lowest_time))
+ elif __alarm_wait__ is not None:
+ signal.alarm(0) # close any alarm
+ __alarm_wait__ = None
+
+
+def init():
+ signal.signal(signal.SIGALRM, __handler__) # prepare alarm
+
+
+def set(name, t, handler, repeat=False, arg=None):
+ al = dict()
+ al["time"] = time.time()
+ al["handler"] = handler
+ al["timeout"] = t
+ al["repeat"] = repeat
+ al["arg"] = arg
+ al["name"] = name
+ __alarms__[name] = al
+ __update__()
+
+
+def unset(name):
+ if name in __update__:
+ __alarms__.pop(name)
+ __update__()
diff --git a/mcwrapper/mod.py b/mcwrapper/mod.py
new file mode 100644
index 0000000..78ffb8e
--- /dev/null
+++ b/mcwrapper/mod.py
@@ -0,0 +1,24 @@
+# vim: expandtab ft=python ts=4 sw=4 sts=4:
+import random
+
+from . import alarm
+
+
+class MoD:
+ "Message of the day handler"
+ def __init__(self, mcwrapper, file, period=900):
+ self.mcwrapper = mcwrapper
+ self.file = file
+ self.load_mods()
+ alarm.set("mod-time", period, self.__handler__, repeat=True)
+
+ def load_mods(self):
+ "Loads messages from self.file"
+ with open(self.file, "r") as f:
+ self.lines = f.readlines()
+
+ def __handler__(self):
+ if len(self.lines) > 0:
+ i = random.randint(0, len(self.lines) - 1)
+ self.mcwrapper.write_to_terminal("/say " + self.lines[i].rstrip()
+ + "\n")
diff --git a/mcwrapper/wrapper.py b/mcwrapper/wrapper.py
index 1896266..3e9a0d7 100644
--- a/mcwrapper/wrapper.py
+++ b/mcwrapper/wrapper.py
@@ -36,11 +36,11 @@ class MCWrapper:
try:
os.kill(lpid, 0)
except OSError:
- prints.warning("Detected forced termination of previous server "
- "wrapper instance.")
+ prints.warning("Detected forced termination of previous server"
+ " wrapper instance.")
else:
- prints.error("Another wrapper is running with given identifier.",
- -1, 1)
+ prints.error("Another wrapper is running with given "
+ "identifier.", -1, 1)
try:
os.mkfifo(__INPUTPIPE__, 0o640)
except FileExistsError:
@@ -75,11 +75,6 @@ class MCWrapper:
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(
@@ -111,7 +106,7 @@ class MCWrapper:
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)
+ prints.info("Input: " + text.rstrip(), 1)
self.process.stdin.write(bytes(text, sys.getdefaultencoding()))
self.process.stdin.flush()
return True