From e4b0c7f50efbe0c42aa933cb58a86a44367c1140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Sat, 15 Aug 2015 14:50:43 +0200 Subject: Implemented module version of mcwrapper mcwrapper functionality split to modules. This is basic implementation of modules handling. Two module types are recognized. For server and commands for mcwrapper cli interface. This way can be implemented different command and server features simply without modifying main script. Interface between main script and modules is defined using service lists. Service list informs main script what function should be called in module. More detailed description should be written to README.md file. Or even separated file describing module interface. In this commit are implemented five different modules. Players and status are server modules. They are used only if mcwrapper is running instance of Minecraft server. Modules say and list-modules are implementing mcwrapper actions. And last module argmodules is implementing mcwrapper argument. For modules usage also added utils.py. This contains shared usable code that is used even by main mcwrapper script. --- modules/utils.py | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 modules/utils.py (limited to 'modules/utils.py') 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 = () -- cgit v1.2.3