diff options
-rw-r--r-- | example.conf | 12 | ||||
-rwxr-xr-x | mcwrapper | 488 | ||||
-rw-r--r-- | modules/argmodules.py | 35 | ||||
-rw-r--r-- | modules/list-modules.py | 51 | ||||
-rw-r--r-- | modules/players.py | 52 | ||||
-rw-r--r-- | modules/say.py | 49 | ||||
-rw-r--r-- | modules/status.py | 71 | ||||
-rw-r--r-- | modules/utils.py | 196 |
8 files changed, 801 insertions, 153 deletions
diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..d494f93 --- /dev/null +++ b/example.conf @@ -0,0 +1,12 @@ +# This is exaple configuration for mcwrapper +# Use Python3 syntax to specify configuration. +# For full list of configuration options refer to documentation. + +modules = {'say', 'argmodules', 'list-modules'} + +server = dict() +server["exampleserver"] = { + "modules": {'status', 'players'}, + "folder": '/dev/shm/mcwrapper-exampleserver', + "logOutput": False, + } @@ -3,111 +3,176 @@ import os import sys import re import subprocess -import datetime import signal import time +import datetime +import traceback from threading import Thread +import importlib.machinery as imp +################################################################################# +# Search for data folder -__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') +__all_data_folders__ = ( + os.path.dirname(__file__), + '.', + '~/mcwrapper', + '/usr/share/mcwrapper', + ) +def __is_data_folder__(path): + if os.path.isdir(path + '/modules') and \ + os.path.isfile(path + '/modules/utils.py'): + return True + else: + return False -def __server_init__(identifier): - __setfiles__(identifier) - __server_clean__() # Clean before execute - print("Wrapper initializing with identifier: " + identifier) +def __data_folder_missing__(path): + if not os.path.isdir(path + '/modules'): + print('Folder ' + path + 'modules not found.') + if not os.path.isfile(path + '/modules/utils.py'): + print('File ' + path + '/modules/utils.py not found.') + +__data_folder__ = None +try: + __data_folder__ = os.environ['DATAF'] + if not __is_data_folder__(__data_folder__): + print("Error: " + __data_folder__ + " doesn't seems to be mcwrapper data folder.", file=sys.stderr) + sys.exit(2) +except KeyError: + for df in __all_data_folders__: + if os.path.isdir(df): + __data_folder__ = df + break + if __data_folder__ == None: + print("Error: No mcwrapper data folder found.", file=sys.stderr) + sys.exit(1) + +################################################################################# +# Load and set utils + +utils = imp.SourceFileLoader("utils", + __data_folder__ + '/modules/utils.py').load_module() + +################################################################################# +# Load configuration + +__all_config_files__ = ( + 'mcwrapper.conf', + '~/.mcwrapper.conf', + '~/.config/mcwrapper.conf', + '/etc/mcwrapper.conf', + ) + +def __set_empty_config__(): + global conf + print('Warning: User configuration not loaded. Using default.', file=sys.stderr) + conf = type('defconf', (object,), {}) + +__config_file__ = None +try: + __config_file__ = os.environ['CONFIG'] +except KeyError: + for cf in __all_config_files__: + if os.path.isfile(cf): + __config_file__ = cf + break +if __config_file__ == None: + __set_empty_config__() +else: try: - os.mkdir(__folder__) + conf = imp.SourceFileLoader("conf", __config_file__).load_module() 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 + traceback.print_exc() + __set_empty_config__() -def __server_clean__(): - print("Wrapper clean.") +# Set conf to utils +utils.conf = conf + +utils.configSet(utils.__default_config__) +# Set additional runtime configuration variables +conf.verbose_level = 0 +conf.action = None +conf.action_module = None +conf.command = [] +conf.__modules_action__ = set() +conf.__modules_argument__ = set() +conf.modulesFolder = __data_folder__ + '/modules' + +################################################################################# +# Modules management + +def __module_load__(modname): try: - os.remove(__players__) - except Exception: - pass + module = imp.SourceFileLoader(modname, + conf.modulesFolder + '/' + modname + '.py').load_module() + return module + except FileNotFoundError: + if conf.verbose_level >= -2: + print("Error: Unknown module " + mod, file=sys.stderr) + sys.exit(3) + +def __module_unload__(mod): + for name, value in vars(conf): + if re.search('^__modules', name): + try: + value.remote(mod) + except KeyError: + pass + del mod + +# Load global modules +for mod in conf.modules: + module = __module_load__(mod) + if utils.Service.action in module.services: + conf.__modules_action__.add(module) + if utils.Service.argument in module.services: + conf.__modules_argument__.add(module) + +def server_modules_load(): + conf.__modules_config__ = set() + conf.__modules_init__ = set() + conf.__modules_clean__ = set() + conf.__modules_parse__ = set() + for mod in conf.modules: + if conf.verbose_level >= 1: + print('Loading module: ' + mod) + module = __module_load__(mod) + if utils.Service.config in module.services: + conf.__modules_config__.add(module) + if utils.Service.init in module.services: + conf.__modules_init__.add(module) + if utils.Service.clean in module.services: + conf.__modules_clean__.add(module) + if utils.Service.parse in module.services: + conf.__modules_parse__.add(module) + +################################################################################# + +def __server_init__(identifier): + if conf.verbose_level >= 0: + print("Wrapper initializing with identifier: " + identifier) try: - os.remove(__status__) - except Exception: + os.mkdir(conf.folder) + except FileExistsError: pass + if os.path.isfile(conf.inputPipe): + if conf.verbose_level >= 2: + print("Error: Server input pipe already exists. Is another wrapper running?") + sys.exit(4) + os.mkfifo(conf.inputPipe, 0o640) + utils.serviceCall('init', 'init') + +def __server_clean__(): + if conf.verbose_level >= 0: + print("Wrapper clean.") + utils.serviceCall('clean', 'clean') try: - os.remove(__pipe__) - except Exception: + os.remove(conf.inputPipe) + except FileNotFoundError: 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) + utils.serviceCall('parse', 'parse', [line], 2) ################################################################################# @@ -120,22 +185,23 @@ class __InputThread__(Thread): def stopexec(self): self.stopread = True def wake(self): - with open(__pipe__, 'w') as f: + with open(self.pipein, '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="") + if ln.rstrip(): + if conf.verbose_level >= 1: + print("Input: " + ln, end="") self.pipeprocess.write(bytes(ln, sys.getdefaultencoding())) self.pipeprocess.flush() else: time.sleep(1) try: - os.remove(__pipe__) - except Exception: + os.remove(conf.inputPipe) + except FileNotFoundError: pass def __server_send_stop__(): @@ -145,83 +211,199 @@ def __server_send_stop__(): 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 + __server_init__(conf.identifier) + if conf.logOutput: + try: + os.makedirs(os.path.dirname(conf.logFile)) + except FileExistsError: + pass + if type(cmd) != str: + cmd = ' '.join(cmd) + if conf.verbose_level >= 1: + print("Start command: " + cmd) prc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - inputThread = __InputThread__(__pipe__, prc.stdin) + stderr=subprocess.STDOUT, shell=True) + inputThread = __InputThread__(conf.inputPipe, prc.stdin) inputThread.start() - inputThread.wake() # Input thread is stack in waiting for first line + inputThread.wake() # Input thread is stuck in waiting for first line for linen in prc.stdout: line = linen.decode(sys.getdefaultencoding()) - with open(__log__, 'a') as flg: - flg.write(line) + if conf.verbose_level >= 2: + print(line.rstrip()) + if conf.logOutput: + with open(conf.logFile, '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: +def __signal_term__(_signo, _stack_frame): + __server_send_stop__() + +def action_start_exec(): + if not conf.identifier: + argument_help_exec() + return + server_modules_load() + utils.serviceCall('config', 'config', [conf]) + if not conf.command: + argument_help_exec() + return + signal.signal(signal.SIGTERM, __signal_term__) + signal.signal(signal.SIGINT, __signal_term__) + mcexec(conf.identifier, conf.command) + + +def action_stop_exec(): + if not conf.identifier: + argument_help_exec() + return + if not os.path.exists(conf.inputPipe): 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: + with open(conf.inputPipe, 'w') as f: f.write("/stop\n") f.flush() - while os.path.isfile(__status__): + while os.path.exists(conf.inputPipe): 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 argument_help_exec(): + if conf.action_module == None: + if conf.action == 'start': + print('mcwrapper [arguments...] start IDENTIFIER {command...}') + print(' Start server under "IDENTIFIER" with command "command"') + print('') + print(' arguments') + utils.printArgumentsHelp() + print(' IDENTIFIER') + print(' Identifier for new server instance. This allows multiple server') + print(' instances running with this wrapper.') + print(' Identifier is word without spaces and preferably without special') + print(' characters.') + print(' command') + print(' Command to execute Minecraft server.') + elif conf.action == 'stop': + print('mcwrapper [arguments...] stop IDENTIFIER') + # TODO + else: + print('mcwrapper [arguments...] ACTION ...') + print(' This script is executing Minecraft server and reads its output.') + print('') + print(' arguments') + utils.printArgumentsHelp() + print('') + print(' ACTION') + print(' start') + print(' Starts server.') + print(' stop') + print(' Sends stop command to Minecraft server.') + for mod in conf.__modules_action__: + mod.action_help() + print('') + print('For more informations abou specific actions enter --help') + print('with ACTION argument.') + else: + conf.action_module.action_full_help() ################################################################################# -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) + print_help = False + arguments = set() + i = 1 + while i < len(sys.argv): + arg = sys.argv[i] + if arg[0] == '-': + if len(arg) > 2 and arg[1] == '-': + cnt, mod = utils.serviceCall('argument', 'argument', [sys.argv[i:]], 2) + if cnt: + arguments.add(mod) + i += cnt + continue + if arg == '--help': + print_help = True + i += 1 + continue + if arg == '--verbose': + conf.verbose_level += 1 + i += 1 + continue + if arg == '--quiet': + conf.verbose_level += 1 + i += 1 + continue + else: + docontinue = False + i += 1 + for l in arg[1:]: + cnt, mod = utils.serviceCall('argument', 'argument_short', + [l, sys.argv[i:]], 2) + if cnt: + arguments.add(mod) + i += cnt + docontinue = True + elif l == 'h': + print_help = True + docontinue = True + elif l == 'v': + conf.verbose_level += 1 + docontinue = True + elif l == 'q': + conf.verbose_level -= 1 + docontinue = True + if docontinue: + continue + if conf.action == None: + rtn, mod = utils.serviceCall('action', 'action', [sys.argv[i:]], 2) + if rtn: + i += rtn + continue + if arg.lower() == 'start': + conf.action = 'start' + for arg in sys.argv[i+1:]: + if conf.identifier == None: + conf.identifier = arg + else: + if type(conf.command) == str: + conf.command += arg + else: + conf.command.append(arg) + break + if arg.lower() == 'stop': + conf.action = 'stop' + i += 1 + continue + else: + if conf.action_module == None: + if conf.action == 'stop': + if not conf.identifier: + conf.identifier = args[0] + i += 1 + continue + else: + cnt = conf.action_module.action(sys.argv[i:]) + if cnt: + i += cnt + continue + sys.exit("Unknown argument: " + arg) + + + if conf.identifier: + utils.setServerConf(conf.identifier) + for mod in arguments: + mod.argument_exec() + if print_help: + argument_help_exec() + sys.exit() + if conf.action_module != None: + conf.action_module.action_exec() else: - print("unknown action: " + todo) + if conf.action == 'start': + action_start_exec() + elif conf.action == 'stop': + action_stop_exec() + else: + pass # This shouldn't happen diff --git a/modules/argmodules.py b/modules/argmodules.py new file mode 100644 index 0000000..1b004f1 --- /dev/null +++ b/modules/argmodules.py @@ -0,0 +1,35 @@ +import re +import utils +from utils import conf + +services = ( + utils.Service.argument, + ) + +__add_modules__ = set() + +def argument(args): + global __add_modules__ + if not re.search('^--modules=', args[0]): + return 0 + __add_modules__ = args[0][10:].split(',') + return 1 + +def argument_short(l, args): + global __add_modules__ + if l == 'm': + if len(args) < 1: + return 0 + __add_modules__ = args[0].split(',') + return 1 + return 0 + +def argument_exec(): + for mod in __add_modules__: + conf.modules.add(mod) + +def argument_help(): + if conf.action == 'start' or conf.action == 'list-modules': + print(' -m MODULE,... --module=MODULE,...') + print(' Load additional server modules. Multiple modules can be') + print(' specified. Separate them using commas.') diff --git a/modules/list-modules.py b/modules/list-modules.py new file mode 100644 index 0000000..ca93dc3 --- /dev/null +++ b/modules/list-modules.py @@ -0,0 +1,51 @@ +import sys +import re +import utils +from utils import conf +import importlib.machinery as imp + +services = ( + utils.Service.action, + ) + +def action(args): + if conf.action == None: + if args[0].lower() != 'list-modules': + return False + conf.action = 'list-modules' + conf.action_module = sys.modules[__name__] + return 1 + elif conf.identifier == None: + conf.identifier = args[0] + return 1 + return 0 + +def action_exec(): + if conf.verbose_level >= 1: + for mod in conf.modules: + try: + module = imp.SourceFileLoader(mod, + conf.modulesFolder + '/' + mod + '.py').load_module() + print(module) + for service in module.services: + print(' ' + utils.Service.toStr(service)) + except FileNotFoundError: + sys.exit('Unknown module: ' + mod) + else: + # TODO add check if module exists + for mod in conf.modules: + print(mod) + +def action_help(): + print(' list-modules') + print(' List all modules that will be used.') + +def action_full_help(): + print('mcwrapper [arguments...] list-modules [IDENTIFIER]') + print(' List all modules that will be used.') + print('') + print(' arguments') + utils.printArgumentsHelp() + print(' IDENTIFIER') + print(' Identifier of Minecraft server instance.') + print(' If specified, server modules are printed.') diff --git a/modules/players.py b/modules/players.py new file mode 100644 index 0000000..91e50b6 --- /dev/null +++ b/modules/players.py @@ -0,0 +1,52 @@ +import os +import sys +import re +import utils +from utils import conf + +services = ( + utils.Service.config, + utils.Service.init, + utils.Service.clean, + utils.Service.parse + ) + +players = set() + +def config(conf): + conf.playersFile = conf.folder + '/players' + +def init(): + with open(conf.playersFile, 'w') as f: + pass + +def clean(): + os.remove(conf.playersFile) + +def parse(line): + if '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) + else: + return False + return True + + +def __user_join__(username): + print("User '" + username + "' joined server.") + with open(conf.playersFile, 'a') as f: + players.add(username) + f.write(username + '\n') + +def __user_leave__(username): + print("User '" + username + "' left server.") + players.remove(username) + with open(conf.playersFile, 'w') as f: + f.writelines(players) + if players: + f.write('\n') diff --git a/modules/say.py b/modules/say.py new file mode 100644 index 0000000..64810e0 --- /dev/null +++ b/modules/say.py @@ -0,0 +1,49 @@ +import sys +import re +import utils +from utils import conf + +services = ( + utils.Service.action, + ) + +def action(args): + if conf.action == None: + if args[0].lower() != 'say': + return False + conf.action = 'say' + conf.action_module = sys.modules[__name__] + conf.sayMessage = [] + for arg in args[1:]: + if conf.identifier == None: + conf.identifier = arg + else: + conf.sayMessage.append(arg) + return len(args) + else: + return 0 + +def action_exec(): + if not conf.sayMessage or not conf.identifier: + action_full_help() + return + if not utils.isServerRunning(): + sys.exit("Server is not running or wrong identifier.") + with open(sconf.inputPipe, 'w') as f: + f.write("/say " + ' '.join(map(str, sconf.saymessage)) + '\n') + f.flush() + +def action_help(): + print(' say') + print(' Sends message to Minecraft server chat.') + +def action_full_help(): + print('mcwrapper [arguments...] say IDENTIFIER {message...}') + print(' Sends message to Minecraft server chat.') + print('') + print(' arguments') + utils.printArgumentsHelp() + print(' IDENTIFIER') + print(' Identifier of running server instance.') + print(' message') + print(' Message to be send to Minecraft server chat.') diff --git a/modules/status.py b/modules/status.py new file mode 100644 index 0000000..906c1ec --- /dev/null +++ b/modules/status.py @@ -0,0 +1,71 @@ +import os +import sys +import re +import utils +from utils import conf + +services = ( + utils.Service.config, + utils.Service.init, + utils.Service.clean, + utils.Service.parse, + ) + +__STATUSSTRINGS__ = { + 0: "Not running", + 1: "Starting", + 2: "Running", + 3: "Stopping", + } + +def config(conf): + conf.statusFile = conf.folder + '/status' + +def init(): + with open(conf.statusFile, 'w') as f: + f.write(__STATUSSTRINGS__[1]) + +def clean(): + os.remove(conf.statusFile) + +def parse(line): + if ': Done' in line: + __server_start__() + elif ': Stopping the server' in line: + __server_stop__() + else: + return False + return True + +def __server_start__(): + print("Server start.") + with open(conf.statusFile, 'w') as f: + f.write(__STATUSSTRINGS__[2] + '\n') + pass + +def __server_stop__(): + print("Server stop.") + with open(conf.statusFile, 'w') as f: + f.write(__STATUSSTRINGS__[3] + '\n') + pass + +#### For other modules #### +def get_status(conf): + """Returns server status as number. + Requires conf (server configuration) set with identifier using utils.confset(). + Returns: + 0 - Not running + 1 - Starting + 2 - Running + 3 - Stopping + -1 - Unknown status + """ + conf.statusFile = conf.folder + '/status' + if not os.path.exists(conf.statusFile): + return 0 + with open(conf.statusFile, 'r') as f: + status = f.readline().rstrip() + for i in range(len(__STATUSSTRINGS__)): + if __STATUSSTRINGS__[i] == status: + return i + return -1 diff --git a/modules/utils.py b/modules/utils.py new file mode 100644 index 0000000..c8406ce --- /dev/null +++ b/modules/utils.py @@ -0,0 +1,196 @@ +# This is python file with usable utilities for modules +# This can't be used as mcwrapper module. Although it's not a problem if loaded. +import os +import datetime +import re +import struct +import traceback +from enum import Enum + +# Dummy variable to be used before it is set by mcwrapper +conf = type('defconf', (object,), {}) + +class Service(Enum): + # Request service of conf function + # This function is called right after argument parsing. + # Configuration is loaded before almost anything is done. + # Prototype: config(conf) + # Where conf is class containing configuration variables. + config = 1 + # Request service of init function + # This function is called right before Minecraft server is started. + # Prototype: init() + init = 2 + # Request service of clean function + # This function is called before mcwrapper exits. + # Prototype: clean() + clean = 3 + # Request service of parse function + # Prototype: parse(line) + # Where line is line from Minecraft server standard and error output. + parse = 4 + # Signalize that exceptions shouldn't be ignored. + # Otherwise exception is printed and module is removed. + exceptionThrow = 101 + # Requests service of action and action_help function. + # This flag can't be denied by serviceServer or by configuration. + # Prototype: action(act, args) + # Where act is string specifying action and args are rest of command line + # arguments. + # Prototype: action_help() + action = 201 + # Requests service of argument function. + # This flag can't be denied by serviceServer or by configuration. + # Prototype: argument(arg, args) + # Where arg is parser argument and args are rest of command line arguments. + argument = 202 + def toStr(service): + if service == Service.config: + return 'S-Config' + elif service == Service.init: + return 'S-Init' + elif service == Service.clean: + return 'S-Clean' + elif service == Service.parse: + return 'S-Parse' + elif service == Service.exceptionThrow: + return 'F-ExceptionThrow' + elif service == Service.action: + return 'P-Action' + elif service == Service.argument: + return 'P-Argument' + +def __module_disable__(module): + """Disable specified module""" + if verbose_level >= 0: + print('Disabling module: ' + str(module)) + if Service.clean in module.services: + try: + module.clean() + except Exception: + traceback.print_exc() + for name, value in vars(conf).items(): + if re.search('^__modules', name): + try: + value.remove(module) + except KeyError: + pass + del module + +def printArgumentsHelp(): + """Prints help for all arguments from loaded modules""" + print(' -h, --help') + print(' Prints this help text.') + print(' -v, --verbose') + print(' Increase verbose level of output.') + print(' -q, --quiet') + print(' Decrease verbose level of output.') + for mod in conf.__modules_argument__: + mod.argument_help() + +def serviceCall(servicename, func, argv=[], mode=0): + """Calls func in all/n-th modules with specified service. + + servicename - String name of service + func - String name of functions to be called + argv - List of arguments passed to functions + mode - Mode of execution + 0 - called for every module without result returning + 1 - called for every module and return result + 2 - called until True is returned from function called + """ + def execmod(servicename, func, argv, mod): + cmd = 'mod.' + func + '( ' + for i in range(0, len(argv)): + cmd += 'argv[' + str(i) + '],' + cmd = cmd[0:len(cmd)-1] + ')' + try: + return eval(cmd) + except Exception as e: + if Service.exceptionThrow in mod.services: + raise e + else: + traceback.print_exc() + __module_disable__(mod) + return None + if mode == 0: + for mod in vars(conf)['__modules_' + servicename + '__'].copy(): + execmod(servicename, func, argv, mod) + return + elif mode == 1: + ret = dict() + for mod in vars(conf)['__modules_' + servicename + '__'].copy(): + ret[mod] = execmod(servicename, func, argv, mod) + return ret + elif mode == 2: + for mod in vars(conf)['__modules_' + servicename + '__'].copy(): + rtn = execmod(servicename, func, argv, mod) + if rtn: + return rtn, mod + return None, None + +def isServerRunning(): + """Check if server is running. It checks if input_pipe exists. + Returns: + True - Running in any state or residue pipe exists. + False - Not running + """ + return os.path.exists(conf.inputPipe) + +__default_config__ = { + "modules": {'say', 'argmodules', 'list-modules', 'printconf'}, + "identifier": None, + } +__default_server_config__ = { + "modules": {'status', 'players'}, + "folder": '/dev/shm/mcwrapper-exampleserver', + "logOutput": False, + "logFile": datetime.datetime.now().strftime('%y-%m-%d-%H-%M-%S') + '.log', + "command": [], + } + +def setServerConf(identifier): + """Sets server configuration.""" + conf.identifier = identifier + try: + conf.server[identifier] + vars(conf).update(conf.server[identifier]) + except AttributeError: + if conf.verbose_level >= 0: + print('W: No configuration associated with identifier: "' + conf.identifier) + configSet(__default_server_config__) + # Set additional runtime configuration variables + conf.inputPipe = conf.folder + '/input_pipe' + +def configSet(confs): + """This is for setting default configurations. If configuration for module is + not set in conf file, then it must be set while module initialization. + + confs - dictionary of configuration options and default values. + """ + for name, val in confs.items(): + try: + dir(conf).index(name) + except ValueError: + exec('conf.' + name + '=val') + +def varint_unpack(data): + "Returns varint value from beginning of data and number of bytes used." + i = 0 + nextbt = True + newdata = 0 + while nextbt: + bt = data[i] + if not bt & (1 << 7): + nextbt = False + bt = bt & ~(1 << 7) + newdata = newdata | (bt << (i * 7)) + print(newdata) + return newdata, i + +def varint_pack(integer): + pass + +################################################################################# +## dummy module +services = () |