Beispiel #1
0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = API(wrapper, "Web", internal=True)
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.socket = False
        self.data = Storage("web")

        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []
        # if not self.config["Web"]["web-password"] == None:
        #   self.log.info("Changing web-mode password because web-password was changed in wrapper.properties")
        #  ***** change code to hashlib if this gets uncommented
        #   self.data.Data["password"] = md5.md5(self.config["Web"]["web-password"]).hexdigest()
        #   self.config["Web"]["web-password"] = None
        #   self.wrapper.configManager.save()

        self.api.registerEvent("server.consoleMessage", self.onServerConsole)
        self.api.registerEvent("player.message", self.onPlayerMessage)
        self.api.registerEvent("player.login", self.onPlayerJoin)
        self.api.registerEvent("player.logout", self.onPlayerLeave)
        self.api.registerEvent("irc.message", self.onChannelMessage)
        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.config = wrapper.config  # Remember if you need to save use 'wrapper.configManager.save()' not config.save
        self.log = logging.getLogger('Web')
        self.check_password = self.wrapper.cipher.check_pw()

        if not Flask:
            self.config["Web"]["web-enabled"] = False
            self.wrapper.configManager.save()
            self.log.critical(
                "You don't have the 'flask/flask_socketio' dashboard dependencies installed "
                "on your system. You can now restart, but Web mode is disabled."
            )
            self.wrapper.haltsig.halt = True

        self.app = Flask(__name__)
        self.app.config['SECRET_KEY'] = "".join(
            [chr(random.randrange(48, 90)) for i in range(32)])  # LOL
        self.socketio = SocketIO(self.app)

        # Flask filters
        def strftime(f):
            return datetime.datetime.fromtimestamp(
                int(f)).strftime('%Y-%m-%d @ %I:%M%p')

        self.app.jinja_env.filters["strftime"] = strftime

        # Register handlers
        self.add_decorators()

        self.data_storage = Storage("dash")
        if "keys" not in self.data_storage.Data:
            self.data_storage.Data["keys"] = []

        self.loginAttempts = 0
        self.last_attempt = 0
        self.disableLogins = 0

        # Start thread for running server
        t = threading.Thread(target=self.run, args=())
        t.daemon = True
        t.start()
Beispiel #3
0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = wrapper.api
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.pass_handler = self.wrapper.cipher
        self.socket = False
        self.storage = Storage("web", pickle=False)
        self.data = self.storage.Data
        self.xplayer = ConsolePlayer(self.wrapper, self.console_output)

        self.adminname = "Web Admin"
        self.xplayer.username = self.adminname

        self.onlyusesafe_ips = self.config["Web"]["safe-ips-use"]
        self.safe_ips = self.config["Web"]["safe-ips"]

        if "keys" not in self.data:
            self.data["keys"] = []

        # Register events
        self.api.registerEvent("server.consoleMessage", self.on_server_console)
        self.api.registerEvent("player.message", self.on_player_message)
        self.api.registerEvent("player.login", self.on_player_join)
        self.api.registerEvent("player.logout", self.on_player_leave)
        self.api.registerEvent("irc.message", self.on_channel_message)

        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
        self.props = ""
        self.propsCount = 0
Beispiel #4
0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = API(wrapper, "Web", internal=True)
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.socket = False
        self.data = Storage("web")
        self.pass_handler = self.wrapper.cipher

        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []

        self.api.registerEvent("server.consoleMessage", self.onServerConsole)
        self.api.registerEvent("player.message", self.onPlayerMessage)
        self.api.registerEvent("player.login", self.onPlayerJoin)
        self.api.registerEvent("player.logout", self.onPlayerLeave)
        self.api.registerEvent("irc.message", self.onChannelMessage)
        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.config = wrapper.config  # Remember if you need to save use 'wrapper.configManager.save()' not config.save
        self.log = logging.getLogger('Web')
        self.check_password = self.wrapper.cipher.check_pw()

        if not Flask:
            self.config["Web"]["web-enabled"] = False
            self.wrapper.configManager.save()
            self.log.critical("You don't have the 'flask/flask_socketio' dashboard dependencies installed "
                              "on your system. You can now restart, but Web mode is disabled.")
            self.wrapper.haltsig.halt = True

        self.app = Flask(__name__)
        self.app.config['SECRET_KEY'] = "".join([chr(random.randrange(48, 90)) for i in range(32)])  # LOL
        self.socketio = SocketIO(self.app)

        # Flask filters
        def strftime(f):
            return datetime.datetime.fromtimestamp(int(f)).strftime('%Y-%m-%d @ %I:%M%p')
            
        self.app.jinja_env.filters["strftime"] = strftime

        # Register handlers
        self.add_decorators()

        self.data_storage = Storage("dash")
        if "keys" not in self.data_storage.Data:
            self.data_storage.Data["keys"] = []

        self.loginAttempts = 0
        self.last_attempt = 0
        self.disableLogins = 0

        # Start thread for running server
        t = threading.Thread(target=self.run, args=())
        t.daemon = True
        t.start()
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = API(wrapper, "Web", internal=True)
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.socket = False
        self.data = Storage("web")
        self.pass_handler = self.wrapper.cipher

        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []

        self.api.registerEvent("server.consoleMessage", self.onServerConsole)
        self.api.registerEvent("player.message", self.onPlayerMessage)
        self.api.registerEvent("player.login", self.onPlayerJoin)
        self.api.registerEvent("player.logout", self.onPlayerLeave)
        self.api.registerEvent("irc.message", self.onChannelMessage)
        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
Beispiel #7
0
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = wrapper.api
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.pass_handler = self.wrapper.cipher
        self.socket = False
        self.storage = Storage("web", pickle=False)
        self.data = self.storage.Data
        self.xplayer = ConsolePlayer(self.wrapper, self.console_output)

        self.adminname = "Web Admin"
        self.xplayer.username = self.adminname

        self.onlyusesafe_ips = self.config["Web"]["safe-ips-use"]
        self.safe_ips = self.config["Web"]["safe-ips"]

        if "keys" not in self.data:
            self.data["keys"] = []

        # Register events
        self.api.registerEvent("server.consoleMessage", self.on_server_console)
        self.api.registerEvent("player.message", self.on_player_message)
        self.api.registerEvent("player.login", self.on_player_join)
        self.api.registerEvent("player.logout", self.on_player_leave)
        self.api.registerEvent("irc.message", self.on_channel_message)

        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
        self.props = ""
        self.propsCount = 0
Beispiel #8
0
    def __init__(self, username, wrapper):

        self.wrapper = wrapper
        self.javaserver = wrapper.javaserver
        self.log = wrapper.log

        self.username = username
        self.loggedIn = time.time()

        # TODO - clean this out.  let player objects GC with their client?
        # mcserver will set this to false later to close the thread.
        self.abort = False
        # meanwhile, it still needs to respect wrapper halts
        self.wrapper_signal = self.wrapper.halt

        # these are all MCUUID objects.. I have separated out various
        #  uses of uuid to clarify for later refractoring
        # ---------------
        # Mojang uuid - the bought and paid Mojand UUID.  Never
        # changes- our one constant point of reference per player.
        # offline uuid - created as a MD5 hash of "OfflinePlayer:%s" % username
        # client uuid - what the client stores as the uuid (should be
        # the same as Mojang?) The player.uuid used by old api (and
        # internally here).
        # server uuid = the local server uuid... used to reference
        # the player on the local server.  Could be same as Mojang UUID
        # if server is in online mode or same as offline if server is
        # in offline mode (proxy mode).
        # *******************

        # This can be False if cache (and requests) Fail... bad name or
        # bad Mojang service connection.
        self.mojangUuid = self.wrapper.uuids.getuuidbyusername(username)

        # IF False error carries forward, this is not a valid player,
        # for whatever reason...
        self.clientUuid = self.mojangUuid

        # These two are offline by default.
        self.offlineUuid = self.wrapper.uuids.getuuidfromname(self.username)
        # Start out as the Offline -
        # change it to Mojang if local server is Online
        self.serverUuid = self.offlineUuid

        self.ipaddress = "127.0.0.0"
        self.loginposition = [0, 0, 0]
        self._position = [0, 0, 0, 0, 0]  # internally used for non-proxy mode

        self.client = None
        self.clientgameversion = self.wrapper.servervitals.protocolVersion
        self.clientboundPackets = Packets_cb(self.clientgameversion)
        self.serverboundPackets = Packets_sb(self.clientgameversion)

        self.playereid = None

        # some player properties associated with abilities
        #
        # default is 1.  Should normally be congruent with speed.
        self.field_of_view = float(1)
        # Client set godmode is 0x01
        self.godmode = 0x00
        # Client set creative is 0x08
        self.creative = 0x00
        # default is 1
        self.fly_speed = float(1)

        if self.wrapper.proxy:
            gotclient = False
            for client in self.wrapper.servervitals.clients:
                if client.username == self.username:
                    self.client = client
                    # Both MCUUID objects
                    self.clientUuid = client.uuid
                    self.serverUuid = client.serveruuid

                    self.ipaddress = client.ip

                    # pktSB already set to self.wrapper.servervitals.protocolVersion
                    self.clientboundPackets = self.client.pktCB
                    self.clientgameversion = self.client.clientversion
                    gotclient = True
                    break
            if not gotclient:
                self.log.error("Proxy is on, but this client is not "
                               "listed in wrapper.proxy.clients!")
                self.log.error("The usual cause of this would be that"
                               " someone is connecting directly to"
                               " your server port and not the wrapper"
                               " proxy port!")

        # Process login data
        self.data = Storage(self.clientUuid.string,
                            root="wrapper-data/players")
        if "firstLoggedIn" not in self.data.Data:
            self.data.Data["firstLoggedIn"] = (time.time(), time.tzname)
        if "logins" not in self.data.Data:
            self.data.Data["logins"] = {}
        self.data.Data["lastLoggedIn"] = (self.loggedIn, time.tzname)
        self.data.save()

        # start player logged in time tracking thread
        t = threading.Thread(target=self._track, args=())
        t.daemon = True
        t.start()
Beispiel #9
0
class Web(object):
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = wrapper.api
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.pass_handler = self.wrapper.cipher
        self.socket = False
        self.storage = Storage("web", pickle=False)
        self.data = self.storage.Data
        self.xplayer = ConsolePlayer(self.wrapper, self.console_output)

        self.adminname = "Web Admin"
        self.xplayer.username = self.adminname

        self.onlyusesafe_ips = self.config["Web"]["safe-ips-use"]
        self.safe_ips = self.config["Web"]["safe-ips"]

        if "keys" not in self.data:
            self.data["keys"] = []

        # Register events
        self.api.registerEvent("server.consoleMessage", self.on_server_console)
        self.api.registerEvent("player.message", self.on_player_message)
        self.api.registerEvent("player.login", self.on_player_join)
        self.api.registerEvent("player.logout", self.on_player_leave)
        self.api.registerEvent("irc.message", self.on_channel_message)

        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
        self.props = ""
        self.propsCount = 0
        # t = threading.Thread(target=self.update_graph, args=())
        # t.daemon = True
        # t.start()

    # ================ Start  and Run code section ================
    # ordered by the time they are referenced in the code.

    # def update_graph(self):
    #     while not self.wrapper.haltsig.halt:
    #         while len(self.memoryGraph) > 200:
    #             del self.memoryGraph[0]
    #         if self.wrapper.javaserver.getmemoryusage():
    #             self.memoryGraph.append(
    #                 [time.time(), self.wrapper.javaserver.getmemoryusage()])
    #        time.sleep(1)

    def wrap(self):
        """ Wrapper starts excution here (via a thread). """
        if not pkg_resources:
            self.log.error("`pkg_resources` is not installed.  It is usually "
                           "distributed with setuptools. Check https://stackov"
                           "erflow.com/questions/7446187/no-module-named-pkg-r"
                           "esources for possible solutions")
            return 
        
        while not self.wrapper.haltsig.halt:
            try:
                if self.bind():
                    # cProfile.run("self.listen()", "cProfile-debug")
                    self.listen()
                else:
                    self.log.error(
                        "Could not bind web to %s:%d - retrying in 5"
                        " seconds" % (
                            self.config["Web"]["web-bind"],
                            self.config["Web"]["web-port"]
                        )
                    )
            except:
                for line in traceback.format_exc().split("\n"):
                    self.log.error(line)
            time.sleep(5)
        # closing also calls storage.save().
        self.storage.close()

    def bind(self):
        """ Started by self.wrap() to bind socket. """
        if self.socket is not False:
            self.socket.close()
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind((self.config["Web"]["web-bind"],
                              self.config["Web"]["web-port"]))
            self.socket.listen(5)
            return True
        except:
            return False

    def listen(self):
        """ Excuted by self.wrap() to listen for client(s). """
        self.log.info("Web Interface bound to %s:%d" % (
            self.config["Web"]["web-bind"], self.config["Web"]["web-port"]))
        while not self.wrapper.haltsig.halt:
            # noinspection PyUnresolvedReferences
            sock, addr = self.socket.accept()
            if self.onlyusesafe_ips:
                if addr[0] not in self.safe_ips:
                    sock.close()
                    self.log.info(
                        "Sorry charlie (an unathorized IP %s attempted "
                        "connection)", addr[0]
                    )
                    continue
            client = Client(self.wrapper, sock, addr, self)

            t = threading.Thread(target=client.wrap, args=())
            t.daemon = True
            t.start()
        self.storage.save()

    def console_output(self, message):
        display = str(message)
        if type(message) is dict:
            if "text" in message:
                display = message["text"]
        self.on_server_console({"message": display})

    # ========== EVENTS SECTION ==========================

    def on_server_console(self, payload):
        while len(self.consoleScrollback) > 1000:
            try:
                self.consoleScrollback.pop()
            except:
                break
        self.consoleScrollback.append((time.time(), payload["message"]))

    def on_player_message(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                self.chatScrollback.pop()
            except:
                break
        self.chatScrollback.append(
            [time.time(), {"type": "player",
                           "payload": {"player": payload["player"].username,
                                       "message": payload["message"]}}])

    def on_player_join(self, payload):
        # abrupt disconnections can cause player on-join although player is
        # not on...
        if not payload["player"]:
            return
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append([
            time.time(), {"type": "playerJoin",
                          "payload": {"player": payload["player"].username}}])

    def on_player_leave(self, payload):
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append([
            time.time(), {"type": "playerLeave",
                          "payload": {"player": payload["player"].username}}])

    def on_channel_message(self, payload):
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append([
            time.time(), {"type": "irc", "payload": payload}])

    # ========== Externally-called Methods section ==========================

    def check_login(self, password):
        """
        Returns True or False to indicate login success.
         - Called by client.run_action, action="login"
        """

        # Threshold for logins
        if time.time() - self.disableLogins < 60:
            self.loginAttempts = 0
            return None

        # check password validity
        if self.pass_handler.check_pw(password, self.config["Web"]["web-password"]):  # noqa
            return True

        # unsuccessful password attempt
        self.loginAttempts += 1
        if self.loginAttempts > 4 and time.time() - self.lastAttempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.lastAttempt = time.time()
        return False

    def make_key(self, remember_me, username):
        a = ""
        z = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
        for i in range(64):
            a += z[random.randrange(0, len(z))]
        # a += chr(random.randrange(97, 122))
        self.data["keys"].append([a, time.time(), remember_me, username])
        return a

    def validate_key(self, key):
        # day = 86400
        week = 604800
        curr_time = int(time.time())
        old_keys = []
        success = False

        for i in self.data["keys"]:
            if len(i) > 2:
                expire_time = int(i[1])
                remembered = i[2]
                user = getargs(i, 3)
                if user != "":
                    self.adminname = user

                # even "remembereds" should expire after a week after last use
                if remembered:
                    expire_time += week

                if curr_time - expire_time > week:  # or day or whatever
                    # remove expired keys
                    old_keys.append(i[0])
                else:
                    if i[0] == key:  # Validate key
                        if remembered:
                            # remembereds are reset at each successful login:
                            self.update_key(i, 1, curr_time)
                        self.loginAttempts = 0
                        success = True
            else:
                # remove bad malformed keys
                old_keys.append(i[0])

        for oldkey in old_keys:
            self.remove_key(oldkey)
        self.storage.save()
        return success

    def update_key(self, key, field_number, data):
        for i, v in enumerate(self.data["keys"]):
            if v[0] == key:
                self.data["keys"][i][field_number] = data

    def remove_key(self, key):
        for i, v in enumerate(self.data["keys"]):
            if v[0] == key:
                del self.data["keys"][i]

    def getdisk_usage(self):
        """only works on Python 3.  returns 0 for Python 2"""
        if disk_usage:
            # noinspection PyCallingNonCallable
            spaces = disk_usage(self.serverpath)
            frsp = spaces.free
            return frsp
        return int(0)
Beispiel #10
0
class Web(object):
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = API(wrapper, "Web", internal=True)
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.socket = False
        self.data = Storage("web")
        self.pass_handler = self.wrapper.cipher

        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []

        self.api.registerEvent("server.consoleMessage", self.onServerConsole)
        self.api.registerEvent("player.message", self.onPlayerMessage)
        self.api.registerEvent("player.login", self.onPlayerJoin)
        self.api.registerEvent("player.logout", self.onPlayerLeave)
        self.api.registerEvent("irc.message", self.onChannelMessage)
        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0

        # t = threading.Thread(target=self.updateGraph, args=())
        # t.daemon = True
        # t.start()

    def __del__(self):
        self.data.close()

    def onServerConsole(self, payload):
        while len(self.consoleScrollback) > 1000:
            try:
                del self.consoleScrollback[0]
            except Exception as e:
                break
        self.consoleScrollback.append((time.time(), payload["message"]))

    def onPlayerMessage(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "player",
            "payload": {
                "player": payload["player"].username,
                "message": payload["message"]
            }
        }))

    def onPlayerJoin(self, payload):
        # print(payload)
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "playerJoin",
            "payload": {
                "player": payload["player"].username
            }
        }))

    def onPlayerLeave(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "playerLeave",
            "payload": {
                "player": payload["player"]
            }
        }))

    def onChannelMessage(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "irc",
            "payload": payload
        }))

    def updateGraph(self):
        while not self.wrapper.halt.halt:
            while len(self.memoryGraph) > 200:
                del self.memoryGraph[0]
            if self.wrapper.javaserver.getmemoryusage():
                self.memoryGraph.append(
                    [time.time(),
                     self.wrapper.javaserver.getmemoryusage()])
            time.sleep(1)

    def checkLogin(self, password):
        if time.time() - self.disableLogins < 60:
            return False  # Threshold for logins
        if self.pass_handler.check_pw(password,
                                      self.config["Web"]["web-password"]):
            return True
        self.loginAttempts += 1
        if self.loginAttempts > 10 and time.time() - self.lastAttempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.lastAttempt = time.time()

    def makeKey(self, rememberme):
        a = ""
        z = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@-_"
        for i in range(64):  # not enough performance issue to justify xrange
            a += z[random.randrange(0, len(z))]
            # a += chr(random.randrange(97, 122))
        if rememberme:
            print("Will remember!")
        self.data.Data["keys"].append([a, time.time(), rememberme])
        return a

    def validateKey(self, key):
        for i in self.data.Data["keys"]:
            expiretime = 2592000
            if len(i) > 2:
                if not i[2]:
                    expiretime = 21600
            # Validate key and ensure it's under a week old
            if i[0] == key and time.time() - i[1] < expiretime:
                self.loginAttempts = 0
                return True
        return False

    def removeKey(self, key):
        # we dont want to do things like this.  Never delete or insert while iterating over a dictionary
        #  because dictionaries change order as the hashtables are changed during insert and delete operations...
        for i, v in enumerate(self.data.Data["keys"]):
            if v[0] == key:
                del self.data.Data["keys"][i]

    def wrap(self):
        while not self.wrapper.halt.halt:
            try:
                if self.bind():
                    self.listen()
                else:
                    self.log.error(
                        "Could not bind web to %s:%d - retrying in 5 seconds",
                        self.config["Web"]["web-bind"],
                        self.config["Web"]["web-port"])
            except Exception as e:
                self.log.exception(e)
            time.sleep(5)

    def bind(self):
        if self.socket is not False:
            self.socket.close()
        try:
            self.socket = socket.socket()
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind((self.config["Web"]["web-bind"],
                              self.config["Web"]["web-port"]))
            self.socket.listen(5)
            return True
        except Exception as e:
            return False

    def listen(self):
        self.log.info("Web Interface bound to %s:%d",
                      self.config["Web"]["web-bind"],
                      self.config["Web"]["web-port"])
        while not self.wrapper.halt.halt:
            # noinspection PyUnresolvedReferences
            sock, addr = self.socket.accept()
            # self.log.debug("(WEB) Connection %s started", str(addr))
            client = WebClient(sock, addr, self)
            t = threading.Thread(target=client.wrap, args=())
            t.daemon = True
            t.start()
Beispiel #11
0
class Web(object):
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = API(wrapper, "Web", internal=True)
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.socket = False
        self.data = Storage("web")
        self.pass_handler = self.wrapper.cipher

        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []

        self.api.registerEvent("server.consoleMessage", self.onServerConsole)
        self.api.registerEvent("player.message", self.onPlayerMessage)
        self.api.registerEvent("player.login", self.onPlayerJoin)
        self.api.registerEvent("player.logout", self.onPlayerLeave)
        self.api.registerEvent("irc.message", self.onChannelMessage)
        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0

        # t = threading.Thread(target=self.updateGraph, args=())
        # t.daemon = True
        # t.start()

    def __del__(self):
            self.data.close()

    def onServerConsole(self, payload):
        while len(self.consoleScrollback) > 1000:
            try:
                del self.consoleScrollback[0]
            except Exception as e:
                break
        self.consoleScrollback.append((time.time(), payload["message"]))

    def onPlayerMessage(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "player", 
            "payload": {
                "player": payload["player"].username, 
                "message": payload["message"]
            }
        }))

    def onPlayerJoin(self, payload):
        # print(payload)
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "playerJoin", 
            "payload": {
                "player": payload["player"].username
            }
        }))

    def onPlayerLeave(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {
            "type": "playerLeave", 
            "payload": {
                "player": payload["player"]
            }
        }))

    def onChannelMessage(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                del self.chatScrollback[0]
            except Exception as e:
                break
        self.chatScrollback.append((time.time(), {"type": "irc", "payload": payload}))

    def updateGraph(self):
        while not self.wrapper.halt.halt:
            while len(self.memoryGraph) > 200:
                del self.memoryGraph[0]
            if self.wrapper.javaserver.getmemoryusage():
                self.memoryGraph.append([time.time(), self.wrapper.javaserver.getmemoryusage()])
            time.sleep(1)

    def checkLogin(self, password):
        if time.time() - self.disableLogins < 60:
            return False  # Threshold for logins
        if self.pass_handler.check_pw(password, self.config["Web"]["web-password"]):
            return True
        self.loginAttempts += 1
        if self.loginAttempts > 10 and time.time() - self.lastAttempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.lastAttempt = time.time()

    def makeKey(self, rememberme):
        a = ""
        z = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@-_"
        for i in range(64):  # not enough performance issue to justify xrange
            a += z[random.randrange(0, len(z))]
            # a += chr(random.randrange(97, 122))
        if rememberme:
            print("Will remember!")
        self.data.Data["keys"].append([a, time.time(), rememberme])
        return a

    def validateKey(self, key):
        for i in self.data.Data["keys"]:
            expiretime = 2592000
            if len(i) > 2:
                if not i[2]:
                    expiretime = 21600
            # Validate key and ensure it's under a week old
            if i[0] == key and time.time() - i[1] < expiretime:
                self.loginAttempts = 0
                return True
        return False

    def removeKey(self, key):
        # we dont want to do things like this.  Never delete or insert while iterating over a dictionary
        #  because dictionaries change order as the hashtables are changed during insert and delete operations...
        for i, v in enumerate(self.data.Data["keys"]):
            if v[0] == key:
                del self.data.Data["keys"][i]

    def wrap(self):
        while not self.wrapper.halt.halt:
            try:
                if self.bind():
                    self.listen()
                else:
                    self.log.error("Could not bind web to %s:%d - retrying in 5 seconds",
                                   self.config["Web"]["web-bind"], self.config["Web"]["web-port"])
            except Exception as e:
                self.log.exception(e)
            time.sleep(5)

    def bind(self):
        if self.socket is not False:
            self.socket.close()
        try:
            self.socket = socket.socket()
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind((self.config["Web"]["web-bind"], self.config["Web"]["web-port"]))
            self.socket.listen(5)
            return True
        except Exception as e:
            return False

    def listen(self):
        self.log.info("Web Interface bound to %s:%d",
                      self.config["Web"]["web-bind"], self.config["Web"]["web-port"])
        while not self.wrapper.halt.halt:
            # noinspection PyUnresolvedReferences
            sock, addr = self.socket.accept()
            # self.log.debug("(WEB) Connection %s started", str(addr))
            client = WebClient(sock, addr, self)
            t = threading.Thread(target=client.wrap, args=())
            t.daemon = True
            t.start()
Beispiel #12
0
 def on_start(self):
     import sys
     storage = Storage.load_molecules(sys.argv[1])
     atoms = storage.get_atoms()
     self.layout.init_game(storage, atoms)
Beispiel #13
0
    def __init__(self, secret_passphrase):
        # setup log and config
        # needs a false setting on first in case config does not
        # load (like after changes).
        self.storage = False
        self.log = logging.getLogger('Wrapper.py')
        self.configManager = Config()
        self.configManager.loadconfig()
        self.config = self.configManager.config

        # Read Config items
        # hard coded cursor for non-readline mode
        self.cursor = ">"
        # This was to allow alternate encodings
        self.encoding = self.config["General"]["encoding"]
        self.serverpath = self.config["General"]["server-directory"]
        self.proxymode = self.config["Proxy"]["proxy-enabled"]
        self.wrapper_onlinemode = self.config["Proxy"]["online-mode"]
        self.halt_message = self.config["Misc"]["halt-message"]

        # encryption items (for passwords and sensitive user data)
        # salt is generated and stored in wrapper.properties.json
        config_changes = False
        salt = self.config["General"]["salt"]
        if not salt:
            salt = gensalt(self.encoding)
            self.config["General"]["salt"] = salt
            config_changes = True
        # passphrase is provided at startup by the wrapper operator or script (not stored)
        passphrase = phrase_to_url_safebytes(secret_passphrase, self.encoding,
                                             salt)
        self.cipher = Crypt(passphrase, self.encoding)

        # Update passwords (hash any plaintext passwords)
        for groups in self.config:
            for cfg_items in self.config[groups]:
                if cfg_items[-10:] == "-plaintext":
                    # i.e., cfg_items ===> like ["web-password-plaintext"]
                    hash_item = cfg_items[:-10]
                    # hash_item ===> i.e., ["web-password"]
                    if hash_item in self.config[groups] and self.config[
                            groups][cfg_items]:
                        # encrypt contents of (i.e.) ["web-password-plaintext"]
                        hashed_item = self.cipher.encrypt(
                            self.config[groups][cfg_items])
                        # store in "" ["Web"]["web-password"]
                        self.config[groups][hash_item] = hashed_item
                        # set plaintext item to false (successful digest)
                        self.config[groups][cfg_items] = False
                        config_changes = True

        # Patch any old update paths "..wrapper/development/build/version.json"
        # new paths are: "..wrapper/development"
        for entries in self.config["Updates"]:
            if "/build/version.json" in str(self.config["Updates"][entries]):
                oldentry = copy.copy(self.config["Updates"][entries])
                self.config["Updates"][entries] = oldentry.split(
                    "/build/version.json")[0]
                config_changes = True

        # save changes made to config file
        if config_changes:
            self.configManager.save()

        # reload branch update info.
        self.auto_update_wrapper = self.config["Updates"][
            "auto-update-wrapper"]
        self.auto_update_branch = self.config["Updates"]["auto-update-branch"]
        if not self.auto_update_branch:
            self.update_url = "https://raw.githubusercontent.com/benbaptist/minecraft-wrapper/development"
        else:
            self.update_url = self.config["Updates"][self.auto_update_branch]

        self.use_timer_tick_event = self.config["Gameplay"][
            "use-timer-tick-event"]
        self.use_readline = not (self.config["Misc"]["use-betterconsole"])

        # Storages
        self.wrapper_storage = Storage("wrapper", encoding=self.encoding)
        self.wrapper_permissions = Storage("permissions",
                                           encoding=self.encoding,
                                           pickle=False)
        self.wrapper_usercache = Storage("usercache",
                                         encoding=self.encoding,
                                         pickle=False)

        # storage Data objects
        self.storage = self.wrapper_storage.Data
        self.usercache = self.wrapper_usercache.Data
        # self.wrapper_permissions accessed only by permissions module

        # core functions and datasets
        self.perms = Permissions(self)
        self.uuids = UUIDS(self.log, self.usercache)
        self.plugins = Plugins(self)
        self.commands = Commands(self)
        self.events = Events(self)
        self.players = {}
        self.registered_permissions = {}
        self.help = {}
        self.input_buff = ""
        self.sig_int = False
        self.command_hist = ['/help', 'help']
        self.command_index = 1

        # init items that are set up later (or opted out of/ not set up.)
        self.javaserver = None
        self.api = None
        self.irc = None
        self.scripts = None
        self.web = None
        self.proxy = None
        self.backups = None

        #  HaltSig - Why? ... because if self.halt was just `False`, passing
        #  a self.halt would simply be passing `False` (immutable).  Changing
        # the value of self.halt would not necessarily change the value of the
        # passed parameter (unless it was specifically referenced back as
        # `wrapper.halt`). Since the halt signal needs to be passed, possibly
        # several layers deep, and into modules that it may be desireable to
        # not have direct access to wrapper, using a HaltSig object is
        # more desireable and reliable in behavior.
        self.halt = HaltSig()

        self.updated = False
        # future plan to expose this to api
        self.xplayer = ConsolePlayer(self)

        # Error messages for non-standard import failures.
        if not readline and self.use_readline:
            self.log.warning(
                "'readline' not imported.  This is needed for proper"
                " console functioning. Press <Enter> to acknowledge...")
            sys.stdin.readline()

        # requests is just being used in too many places to try
        # and track its usages piece-meal.
        if not requests:
            self.log.error(
                "You must have the requests module installed to use wrapper!"
                " console functioning. Press <Enter> to Exit...")
            sys.stdin.readline()
            self._halt()

        # create server/proxy vitals and config objects
        self.servervitals = ServerVitals(self.players)

        # LETS TAKE A SECOND TO DISCUSS PLAYER OBJECTS:
        # The ServerVitals class gets passed the player object list now, but
        # player objects are now housed in wrapper.  This is how we are
        # passing information between proxy and wrapper.

        self.servervitals.serverpath = self.config["General"][
            "server-directory"]
        self.servervitals.state = OFF
        self.servervitals.command_prefix = self.config["Misc"][
            "command-prefix"]

        self.proxyconfig = ProxyConfig()
        self.proxyconfig.proxy = self.config["Proxy"]
        self.proxyconfig.entity = self.config["Entities"]
Beispiel #14
0
    def __init__(self):
        # setup log and config
        # needs a false setting on first in case config does not
        # load (like after changes).
        self.storage = False
        self.log = logging.getLogger('Wrapper.py')
        self.configManager = Config()
        self.configManager.loadconfig()
        # set up config
        self.config = self.configManager.config

        # Read Config items
        # hard coded cursor for non-readline mode
        self.cursor = ">"
        self.wrapper_ban_system = False
        # This was to allow alternate encodings
        self.encoding = self.config["General"]["encoding"]
        self.proxymode = self.config["Proxy"]["proxy-enabled"]
        self.wrapper_onlinemode = self.config["Proxy"]["online-mode"]
        self.wrapper_ban_system = self.proxymode and self.wrapper_ban_system
        self.auto_update_wrapper = self.config["Updates"][
            "auto-update-wrapper"]
        self.auto_update_branch = self.config["Updates"]["auto-update-branch"]
        self.use_timer_tick_event = self.config["Gameplay"][
            "use-timer-tick-event"]
        self.command_prefix = self.config["Misc"]["command-prefix"]
        self.use_readline = self.config["Misc"]["use-readline"]

        # Storages
        self.wrapper_storage = Storage("wrapper", encoding=self.encoding)

        self.wrapper_permissions = Storage("permissions",
                                           encoding=self.encoding,
                                           pickle=False)

        self.wrapper_usercache = Storage("usercache",
                                         encoding=self.encoding,
                                         pickle=False)

        # storage Data objects
        self.storage = self.wrapper_storage.Data
        self.permissions = self.wrapper_permissions.Data
        self.usercache = self.wrapper_usercache.Data

        # core functions and datasets
        self.perms = Permissions(self)
        self.uuids = UUIDS(self)
        self.plugins = Plugins(self)
        self.commands = Commands(self)
        self.events = Events(self)
        self.registered_permissions = {}
        self.help = {}
        self.input_buff = ""
        self.sig_int = False
        self.command_hist = ['/help', 'help']
        self.command_index = 1

        # init items that are set up later (or opted out of/ not set up.)
        self.javaserver = None
        self.api = None
        self.irc = None
        self.scripts = None
        self.web = None
        self.proxy = None
        self.backups = None
        self.halt = False
        self.updated = False
        # future plan to expose this to api
        self.xplayer = ConsolePlayer(self)
        # define the slot once here and not at each clients Instantiation:
        self.inv_slots = range(46)

        # Error messages for non-standard import failures.
        if not readline and self.use_readline:
            self.log.warning(
                "'readline' not imported.  This is needed for proper"
                " console functioning. Press <Enter> to acknowledge...")
            sys.stdin.readline()

        # requests is just being used in too many places to try
        # and track its usages piece-meal.
        if not requests:
            self.log.error(
                "You must have the requests module installed to use wrapper!"
                " console functioning. Press <Enter> to Exit...")
            sys.stdin.readline()
            self._halt()
Beispiel #15
0
class Web(object):
    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.api = wrapper.api
        self.log = logging.getLogger('Web')
        self.config = wrapper.config
        self.serverpath = self.config["General"]["server-directory"]
        self.pass_handler = self.wrapper.cipher
        self.socket = False
        self.storage = Storage("web", pickle=False)
        self.data = self.storage.Data
        self.xplayer = ConsolePlayer(self.wrapper, self.console_output)

        self.adminname = "Web Admin"
        self.xplayer.username = self.adminname

        self.onlyusesafe_ips = self.config["Web"]["safe-ips-use"]
        self.safe_ips = self.config["Web"]["safe-ips"]

        if "keys" not in self.data:
            self.data["keys"] = []

        # Register events
        self.api.registerEvent("server.consoleMessage", self.on_server_console)
        self.api.registerEvent("player.message", self.on_player_message)
        self.api.registerEvent("player.login", self.on_player_join)
        self.api.registerEvent("player.logout", self.on_player_leave)
        self.api.registerEvent("irc.message", self.on_channel_message)

        self.consoleScrollback = []
        self.chatScrollback = []
        self.memoryGraph = []
        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0
        self.props = ""
        self.propsCount = 0
        # t = threading.Thread(target=self.update_graph, args=())
        # t.daemon = True
        # t.start()

    # ================ Start  and Run code section ================
    # ordered by the time they are referenced in the code.

    # def update_graph(self):
    #     while not self.wrapper.haltsig.halt:
    #         while len(self.memoryGraph) > 200:
    #             del self.memoryGraph[0]
    #         if self.wrapper.javaserver.getmemoryusage():
    #             self.memoryGraph.append(
    #                 [time.time(), self.wrapper.javaserver.getmemoryusage()])
    #        time.sleep(1)

    def wrap(self):
        """ Wrapper starts excution here (via a thread). """
        if not pkg_resources:
            self.log.error("`pkg_resources` is not installed.  It is usually "
                           "distributed with setuptools. Check https://stackov"
                           "erflow.com/questions/7446187/no-module-named-pkg-r"
                           "esources for possible solutions")
            return

        while not self.wrapper.haltsig.halt:
            try:
                if self.bind():
                    # cProfile.run("self.listen()", "cProfile-debug")
                    self.listen()
                else:
                    self.log.error(
                        "Could not bind web to %s:%d - retrying in 5"
                        " seconds" % (self.config["Web"]["web-bind"],
                                      self.config["Web"]["web-port"]))
            except:
                for line in traceback.format_exc().split("\n"):
                    self.log.error(line)
            time.sleep(5)
        # closing also calls storage.save().
        self.storage.close()

    def bind(self):
        """ Started by self.wrap() to bind socket. """
        if self.socket is not False:
            self.socket.close()
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind((self.config["Web"]["web-bind"],
                              self.config["Web"]["web-port"]))
            self.socket.listen(5)
            return True
        except:
            return False

    def listen(self):
        """ Excuted by self.wrap() to listen for client(s). """
        self.log.info(
            "Web Interface bound to %s:%d" %
            (self.config["Web"]["web-bind"], self.config["Web"]["web-port"]))
        while not self.wrapper.haltsig.halt:
            # noinspection PyUnresolvedReferences
            sock, addr = self.socket.accept()
            if self.onlyusesafe_ips:
                if addr[0] not in self.safe_ips:
                    sock.close()
                    self.log.info(
                        "Sorry charlie (an unathorized IP %s attempted "
                        "connection)", addr[0])
                    continue
            client = Client(self.wrapper, sock, addr, self)

            t = threading.Thread(target=client.wrap, args=())
            t.daemon = True
            t.start()
        self.storage.save()

    def console_output(self, message):
        display = str(message)
        if type(message) is dict:
            if "text" in message:
                display = message["text"]
        self.on_server_console({"message": display})

    # ========== EVENTS SECTION ==========================

    def on_server_console(self, payload):
        while len(self.consoleScrollback) > 1000:
            try:
                self.consoleScrollback.pop()
            except:
                break
        self.consoleScrollback.append((time.time(), payload["message"]))

    def on_player_message(self, payload):
        while len(self.chatScrollback) > 200:
            try:
                self.chatScrollback.pop()
            except:
                break
        self.chatScrollback.append([
            time.time(), {
                "type": "player",
                "payload": {
                    "player": payload["player"].username,
                    "message": payload["message"]
                }
            }
        ])

    def on_player_join(self, payload):
        # abrupt disconnections can cause player on-join although player is
        # not on...
        if not payload["player"]:
            return
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append([
            time.time(), {
                "type": "playerJoin",
                "payload": {
                    "player": payload["player"].username
                }
            }
        ])

    def on_player_leave(self, payload):
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append([
            time.time(), {
                "type": "playerLeave",
                "payload": {
                    "player": payload["player"].username
                }
            }
        ])

    def on_channel_message(self, payload):
        while len(self.chatScrollback) > 200:
            self.chatScrollback.pop()
        self.chatScrollback.append(
            [time.time(), {
                "type": "irc",
                "payload": payload
            }])

    # ========== Externally-called Methods section ==========================

    def check_login(self, password):
        """
        Returns True or False to indicate login success.
         - Called by client.run_action, action="login"
        """

        # Threshold for logins
        if time.time() - self.disableLogins < 60:
            self.loginAttempts = 0
            return None

        # check password validity
        if self.pass_handler.check_pw(
                password, self.config["Web"]["web-password"]):  # noqa
            return True

        # unsuccessful password attempt
        self.loginAttempts += 1
        if self.loginAttempts > 4 and time.time() - self.lastAttempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.lastAttempt = time.time()
        return False

    def make_key(self, remember_me, username):
        a = ""
        z = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
        for i in range(64):
            a += z[random.randrange(0, len(z))]
        # a += chr(random.randrange(97, 122))
        self.data["keys"].append([a, time.time(), remember_me, username])
        return a

    def validate_key(self, key):
        # day = 86400
        week = 604800
        curr_time = int(time.time())
        old_keys = []
        success = False

        for i in self.data["keys"]:
            if len(i) > 2:
                expire_time = int(i[1])
                remembered = i[2]
                user = getargs(i, 3)
                if user != "":
                    self.adminname = user

                # even "remembereds" should expire after a week after last use
                if remembered:
                    expire_time += week

                if curr_time - expire_time > week:  # or day or whatever
                    # remove expired keys
                    old_keys.append(i[0])
                else:
                    if i[0] == key:  # Validate key
                        if remembered:
                            # remembereds are reset at each successful login:
                            self.update_key(i, 1, curr_time)
                        self.loginAttempts = 0
                        success = True
            else:
                # remove bad malformed keys
                old_keys.append(i[0])

        for oldkey in old_keys:
            self.remove_key(oldkey)
        self.storage.save()
        return success

    def update_key(self, key, field_number, data):
        for i, v in enumerate(self.data["keys"]):
            if v[0] == key:
                self.data["keys"][i][field_number] = data

    def remove_key(self, key):
        for i, v in enumerate(self.data["keys"]):
            if v[0] == key:
                del self.data["keys"][i]

    def getdisk_usage(self):
        """only works on Python 3.  returns 0 for Python 2"""
        if disk_usage:
            # noinspection PyCallingNonCallable
            spaces = disk_usage(self.serverpath)
            frsp = spaces.free
            return frsp
        return int(0)
Beispiel #16
0
class Player(object):
    """
    .. code:: python

        def __init__(self, username, wrapper)

    ..

    This class is normally passed as an argument to an event
    callback, but can be also be called using getPlayer(username):

    .. code:: python

        player = self.api.getPlayer(<username>)

    ..

    Player objects contains methods and data of a currently
    logged-in player. This object is destroyed
    upon logging off.  Most features are tied heavily to
    proxy mode implementations and the proxy client instance.

    The player object has a self.__str___ representation that returns the
    player.username.  Therefore, plugins do not need to attempt string
    conversion or do explicit references to player.username in their code
    (str(player) or player.username in plugin code).

    When using events, events in the "proxy" (Group 'Proxy') section are only
    available in proxy mode.  "server" events (Group 'core/mcserver.py')
    are available even without proxy mode, as long as the server is running.


    Supported properties of the player:
    
    .. code:: python

        self.username
        self.loggedIn
        self.mojangUuid
        self.offlineUuid
        self.loginposition
        self.playereid
        self.ipaddress

        # proxy only
        self.serverUuid (proxy only)
        self.clientUuid (proxy only)
        self.clientgameversion
        self.clientboundPackets = Packets_cb(self.clientgameversion)
        self.serverboundPackets = Packets_sb(self.clientgameversion)

        # some player properties associated with abilities (proxy)
        # default is 1.  Should normally be congruent with speed.
        self.field_of_view = float(1)
        # Client set godmode is 0x01
        self.godmode = 0x00
        # Client set creative is 0x08
        self.creative = 0x00
        # default is 1
        self.fly_speed = float(1)

    ..

    """
    def __init__(self, username, wrapper):

        self.wrapper = wrapper
        self.javaserver = wrapper.javaserver
        self.log = wrapper.log

        self.username = username
        self.loggedIn = time.time()

        # TODO - clean this out.  let player objects GC with their client?
        # mcserver will set this to false later to close the thread.
        self.abort = False
        # meanwhile, it still needs to respect wrapper halts
        self.wrapper_signal = self.wrapper.halt

        # these are all MCUUID objects.. I have separated out various
        #  uses of uuid to clarify for later refractoring
        # ---------------
        # Mojang uuid - the bought and paid Mojand UUID.  Never
        # changes- our one constant point of reference per player.
        # offline uuid - created as a MD5 hash of "OfflinePlayer:%s" % username
        # client uuid - what the client stores as the uuid (should be
        # the same as Mojang?) The player.uuid used by old api (and
        # internally here).
        # server uuid = the local server uuid... used to reference
        # the player on the local server.  Could be same as Mojang UUID
        # if server is in online mode or same as offline if server is
        # in offline mode (proxy mode).
        # *******************

        # This can be False if cache (and requests) Fail... bad name or
        # bad Mojang service connection.
        self.mojangUuid = self.wrapper.uuids.getuuidbyusername(username)

        # IF False error carries forward, this is not a valid player,
        # for whatever reason...
        self.clientUuid = self.mojangUuid

        # These two are offline by default.
        self.offlineUuid = self.wrapper.uuids.getuuidfromname(self.username)
        # Start out as the Offline -
        # change it to Mojang if local server is Online
        self.serverUuid = self.offlineUuid

        self.ipaddress = "127.0.0.0"
        self.loginposition = [0, 0, 0]
        self._position = [0, 0, 0, 0, 0]  # internally used for non-proxy mode

        self.client = None
        self.clientgameversion = self.wrapper.servervitals.protocolVersion
        self.clientboundPackets = Packets_cb(self.clientgameversion)
        self.serverboundPackets = Packets_sb(self.clientgameversion)

        self.playereid = None

        # some player properties associated with abilities
        #
        # default is 1.  Should normally be congruent with speed.
        self.field_of_view = float(1)
        # Client set godmode is 0x01
        self.godmode = 0x00
        # Client set creative is 0x08
        self.creative = 0x00
        # default is 1
        self.fly_speed = float(1)

        if self.wrapper.proxy:
            gotclient = False
            for client in self.wrapper.servervitals.clients:
                if client.username == self.username:
                    self.client = client
                    # Both MCUUID objects
                    self.clientUuid = client.uuid
                    self.serverUuid = client.serveruuid

                    self.ipaddress = client.ip

                    # pktSB already set to self.wrapper.servervitals.protocolVersion
                    self.clientboundPackets = self.client.pktCB
                    self.clientgameversion = self.client.clientversion
                    gotclient = True
                    break
            if not gotclient:
                self.log.error("Proxy is on, but this client is not "
                               "listed in wrapper.proxy.clients!")
                self.log.error("The usual cause of this would be that"
                               " someone is connecting directly to"
                               " your server port and not the wrapper"
                               " proxy port!")

        # Process login data
        self.data = Storage(self.clientUuid.string,
                            root="wrapper-data/players")
        if "firstLoggedIn" not in self.data.Data:
            self.data.Data["firstLoggedIn"] = (time.time(), time.tzname)
        if "logins" not in self.data.Data:
            self.data.Data["logins"] = {}
        self.data.Data["lastLoggedIn"] = (self.loggedIn, time.tzname)
        self.data.save()

        # start player logged in time tracking thread
        t = threading.Thread(target=self._track, args=())
        t.daemon = True
        t.start()

    def __str__(self):
        return self.username

    def __del__(self):
        self.data.close()

    @property
    def name(self):
        return self.username

    @property
    def uuid(self):
        return self.mojangUuid

    def _track(self):
        """
        internal tracking that updates a player's server play time.
        Not a part of the public player object API.

        Sample ReST formattings -

        # emphasized notes
        Note: *You do not need to run this function unless you want*
         *certain permission nodes to be granted by default.*
         *i.e., 'essentials.list' should be on by default, so players*
         *can run /list without having any permissions*

        # code samples
            :sample usage:

                .. code:: python

                    < code here >

                ..

        """
        self.data.Data["logins"][int(self.loggedIn)] = time.time()
        while not (self.abort or self.wrapper_signal.halt):
            timeupdate = time.time()
            if timeupdate % 60:  # Just update every 60 seconds
                self.data.Data["logins"][int(self.loggedIn)] = int(time.time())
            # this needs a fast response to ensure the storage closes
            # immediately on player logoff
            time.sleep(.5)
        self.data.close()

    def execute(self, string):
        """
        Run a command as this player. If proxy mode is not enabled,
        it simply falls back to using the 1.8 'execute' command. To 
        be clear, this does NOT work with any Wrapper.py or plugin 
        commands.  The command does not pass through the wrapper.  
        It is only sent to the server console (or the actual server in
        proxy mode).

        :arg string: full command string send on player's behalf to server.

        :returns: Nothing; passes the server or the console as an
         "execute" command.

        """
        try:
            self.client.chat_to_server("/%s" % string)
        except AttributeError:
            if self.wrapper.servervitals.protocolVersion > PROTOCOL_1_7_9:
                self.wrapper.javaserver.console("execute %s ~ ~ ~ %s" %
                                                (self.username, string))
            else:
                self.log.warning("could not run player.execute - wrapper not"
                                 " in proxy mode and minecraft version is less"
                                 " than 1.8 (when /execute was implemented).")

    def sendCommand(self, command, args):
        """
        Sends a command to the wrapper interface as the player instance.
        This would find a nice application with a '\sudo' plugin command.

        :sample usage:

            .. code:: python

                player=getPlayer("username")
                player.sendCommand("perms", ("users", "SurestTexas00", "info"))

            ..

        :Args:
            :command: The wrapper (or plugin) command to execute; no
             slash prefix
            :args: list of arguments (I think it is a list, not a
             tuple or dict!)

        :returns: Nothing; passes command through commands.py function
         'playercommand()'

        """
        pay = {"player": self, "command": command, "args": args}
        self.wrapper.api.callEvent("player.runCommand", pay)

    def say(self, string):
        """
        Send a message as a player.

        :arg string: message/command sent to the server as the player.

        Beware: *in proxy mode, the message string is sent directly to*
        *the server without wrapper filtering,so it could be used to*
        *execute minecraft commands as the player if the string is*
        *prefixed with a slash.*

        """
        try:
            self.client.chat_to_server(string)
        except AttributeError:
            # pre-1.8
            self.wrapper.javaserver.console("say @a <%s> %s" %
                                            (self.username, string))

    def getClient(self):
        """
        Returns the player client context.  Use at your own risk - items
        in client are generally private or subject to change (you are
        working with an undefined API!)... what works in this wrapper
        version may not work in the next.

        :returns: player client object (and possibly sets self.client
         to the matching client).

        """
        if self.client is None:
            for client in self.wrapper.servervitals.clients:
                if client.username == self.username:
                    self.client = client
                    return client
            self.log.warning(
                "getClient could not return a client for:%s"
                " \nThe usual cause of this condition"
                " is that no client instance exists because"
                " proxy is not enabled.", self.username)
            return None
        else:
            return self.client

    def getPosition(self):
        """
        Get the players position
        
        :Note:  The player's position is obtained by parsing client
         packets, which are not sent until the client logs in to 
         the server.  Allow some time after server login to verify 
         the wrapper has had the oppportunity to parse a suitable 
         packet to get the information!
        
        :returns: a tuple of the player's current position x, y, z, 
         and yaw, pitch of head.
        
        """
        if self.wrapper.proxy:
            return self.client.position + self.client.head
        else:
            # Non-proxy mode:
            return self._position

    def getGamemode(self):
        """
        Get the player's current gamemode.
        
        :Note:  The player's Gamemode is obtained by parsing client
         packets, which are not sent until the client logs in to 
         the server.  Allow some time after server login to verify 
         the wrapper has had the oppportunity to parse a suitable 
         packet to get the information!
         
        :returns:  An Integer of the the player's current gamemode.

        """
        try:
            return self.client.gamemode
        except AttributeError:
            # Non-proxy mode:
            return 0

    def getDimension(self):
        """
        Get the player's current dimension.

        :Note:  The player's Dimension is obtained by parsing client
         packets, which are not sent until the client logs in to 
         the server.  Allow some time after server login to verify 
         the wrapper has had the oppportunity to parse a suitable 
         packet to get the information!
         
         :returns: the player's current dimension.

             :Nether: -1
             :Overworld: 0
             :End: 1

        """
        try:
            return self.client.dimension
        except AttributeError:
            # Non-proxy mode:
            return 0

    def setGamemode(self, gamemode=0):
        """
        Sets the user's gamemode.

        :arg gamemode: desired gamemode, as a value 0-3

        """
        if gamemode in (0, 1, 2, 3):
            try:
                self.client.gamemode = gamemode
            except AttributeError:
                # Non-proxy mode:
                pass
            self.wrapper.javaserver.console("gamemode %d %s" %
                                            (gamemode, self.username))

    def setResourcePack(self, url, hashrp=""):
        """
        Sets the player's resource pack to a different URL. If the
        user hasn't already allowed resource packs, the user will
        be prompted to change to the specified resource pack.
        Probably broken right now.

        :Args:
            :url: URL of resource pack
            :hashrp: resource pack hash
        :return: False if not in proxy mode.
        
        """
        try:
            version = self.wrapper.proxy.srv_data.protocolVersion
        except AttributeError:
            # Non proxy mode
            return False
        if version < PROTOCOL_1_8START:
            self.client.packet.sendpkt(self.clientboundPackets.PLUGIN_MESSAGE,
                                       [_STRING, _BYTEARRAY],
                                       ("MC|RPack", url))
        else:
            self.client.packet.sendpkt(
                self.clientboundPackets.RESOURCE_PACK_SEND, [_STRING, _STRING],
                (url, hashrp))

    def isOp(self, strict=False):
        """
        Check if player has Operator status. Accepts player as OP
        based on either the username OR server UUID (unless 'strict'
        is set).

        Note: *If a player has been opped since the last server start,*
        *make sure that you run refreshOpsList() to ensure that*
        *wrapper will acknowlege them as OP.*

        :arg strict: True - use ONLY the UUID as verification

        :returns:  A 1-10 (or more?) op level if the player is currently
         a server operator.

        Can be treated, as before, like a
        boolean - 'if player.isOp():', but now also adds ability
        to granularize with the OP level.  Levels above 4 are
        reserved for wrapper.  10 indicates owner. 5-9 are
        reserved for future minecraft or wrapper levels.  pre-1.8
        servers return 1.  levels above 4 are based on name only
        from the file "superops.txt" in the wrapper folder.
        To assign levels, change the lines of <PlayerName>=<oplevel>
        to your desired names.  Player must be an actual OP before
        the superops.txt will have any effect.  Op level of 10 is
        be required to operate permissions commands.

        """

        if self.wrapper.servervitals.operator_list in (False, None):
            return False  # no ops in file
        # each op item is a dictionary
        for ops in self.wrapper.servervitals.operator_list:
            if ops["uuid"] == self.serverUuid.string:
                return ops["level"]
            if ops["name"] == self.username and not strict:
                return ops["level"]
        return False

    def message(self, message="", position=0):
        """
        Sends a message to the player.

        :Args:
            :message: Can be text, colorcoded text, or json chat
            :position:  an integer 0-2.  2 will place it above XP bar.
             1 or 0 will place it in the chat. Using position 2 will
             only display any text component (or can be used to display
             standard minecraft translates, such as
             "{'translate': 'commands.generic.notFound', 'color': 'red'}" and
             "{'translate': 'tile.bed.noSleep'}"


        :returns: Nothing


        """

        if self.wrapper.proxy:
            self.client.chat_to_client(message, position)
        else:
            self.javaserver.broadcast(message, who=self.username)

    def actionMessage(self, message=""):
        try:
            version = self.wrapper.proxy.srv_data.protocolVersion
        except AttributeError:
            # Non proxy mode
            return False

        if version < PROTOCOL_1_8START:
            parsing = [_STRING, _NULL]
            data = [message]
        else:
            parsing = [_STRING, _BYTE]
            data = (json.dumps({"text": processoldcolorcodes(message)}), 2)

        self.client.packet.sendpkt(
            self.clientboundPackets.CHAT_MESSAGE,
            parsing,  # "string|byte"
            data)

    def setVisualXP(self, progress, level, total):
        """
         Change the XP bar on the client's side only. Does not
         affect actual XP levels.

        :Args:
            :progress:  Float between Between 0 and 1
            :level:  Integer (short in older versions) of EXP level
            :total: Total EXP.

        :returns: Nothing

        """
        try:
            version = self.wrapper.proxy.srv_data.protocolVersion
        except AttributeError:
            # Non proxy mode
            return False

        if version > PROTOCOL_1_8START:
            parsing = [_FLOAT, _VARINT, _VARINT]
        else:
            parsing = [_FLOAT, _SHORT, _SHORT]

        self.client.packet.sendpkt(self.clientboundPackets.SET_EXPERIENCE,
                                   parsing, (progress, level, total))

    def openWindow(self, windowtype, title, slots):
        """
        Opens an inventory window on the client side.  EntityHorse
        is not supported due to further EID requirement.  *1.8*
        *experimental only.*

        :Args:
            :windowtype:  Window Type (text string). See below
             or applicable wiki entry (for version specific info)
            :title: Window title - wiki says chat object (could
             be string too?)
            :slots:

        :returns: None (False if client is less than 1.8 version)


        Valid window names (1.9)

        :minecraft\:chest: Chest, large chest, or minecart with chest

        :minecraft\:crafting_table: Crafting table

        :minecraft\:furnace: Furnace

        :minecraft\:dispenser: Dispenser

        :minecraft\:enchanting_table: Enchantment table

        :minecraft\:brewing_stand: Brewing stand

        :minecraft\:villager: Villager

        :minecraft\:beacon: Beacon

        :minecraft\:anvil: Anvil

        :minecraft\:hopper: Hopper or minecart with hopper

        :minecraft\:dropper: Dropper

        :EntityHorse: Horse, donkey, or mule

        """
        try:
            version = self.wrapper.proxy.srv_data.protocolVersion
        except AttributeError:
            # Non proxy mode
            return False
        client = self.client
        client.windowCounter += 1
        if client.windowCounter > 200:
            client.windowCounter = 2

        # TODO Test what kind of field title is (json or text)

        if not version > PROTOCOL_1_8START:
            return False

        client.packet.sendpkt(self.clientboundPackets.OPEN_WINDOW,
                              [_UBYTE, _STRING, _JSON, _UBYTE],
                              (client.windowCounter, windowtype, {
                                  "text": title
                              }, slots))

        return None  # return a Window object soon

    def setPlayerAbilities(self, fly):
        """
        *based on old playerSetFly (which was an unfinished function)*

        NOTE - You are implementing these abilities on the client
         side only.. if the player is in survival mode, the server
         may think the client is hacking!

        this will set 'is flying' and 'can fly' to true for the player.
        these flags/settings will be set according to the players
        properties, which you can set just prior to calling this
        method:

            :getPlayer().godmode:  Hex or integer (see chart below)

            :getPlayer().creative: Hex or integer (see chart below)

            :getPlayer().field_of_view: Float - default is 1.0

            :getPlayer().fly_speed: Float - default is 1.0

        :arg fly: Boolean

            :True: set fly mode.
            :False: to unset fly mode

        :Bitflags used (for all versions): These can be added to
         produce combination effects.   This function sets
         0x02 and 0x04 together (0x06).

            :Invulnerable: 0x01
            :Flying: 0x02
            :Allow Flying: 0x04
            :Creative Mode: 0x08

        :returns: Nothing

        """
        try:
            sendclient = self.client.packet.sendpkt
            sendserver = self.client.server.packet.sendpkt
        except AttributeError:
            # Non proxy mode
            return False

        # TODO later add and keep track of godmode and creative- code
        # will currently unset them.

        if fly:
            setfly = 0x06  # for set fly
        else:
            setfly = 0x00

        bitfield = self.godmode | self.creative | setfly

        # Note in versions before 1.8, field of view is the
        # walking speed for client (still a float) Server
        # field of view is still walking speed
        sendclient(self.clientboundPackets.PLAYER_ABILITIES,
                   [_BYTE, _FLOAT, _FLOAT],
                   (bitfield, self.fly_speed, self.field_of_view))

        sendserver(self.serverboundPackets.PLAYER_ABILITIES,
                   [_BYTE, _FLOAT, _FLOAT],
                   (bitfield, self.fly_speed, self.field_of_view))

    def sendBlock(self,
                  position,
                  blockid,
                  blockdata,
                  sendblock=True,
                  numparticles=1,
                  partdata=1):
        """
        Used to make phantom blocks visible ONLY to the client.  Sends
        either a particle or a block to the minecraft player's client.
        For blocks iddata is just block id - No need to bitwise the
        blockdata; just pass the additional block data.  The particle
        sender is only a basic version and is not intended to do
        anything more than send something like a barrier particle to
        temporarily highlight something for the player.  Fancy particle
        operations should be custom done by the plugin or someone can
        write a nicer particle-renderer.

        :Args:

            :position: players position as tuple.  The coordinates must
             be in the player's render distance or the block will appear
             at odd places.

            :blockid: usually block id, but could be particle id too.  If
             sending pre-1.8 particles this is a string not a number...
             the valid values are found here

            :blockdata: additional block meta (a number specifying a subtype).

            :sendblock: True for sending a block.

            :numparticles: if particles, their numeric count.

            :partdata: if particles; particle data.  Particles with
             additional ID cannot be used ("Ironcrack").

        :Valid 'blockid' values:
         http://wayback.archive.org/web/20151023030926/https://gist.github.com/thinkofdeath/5110835

        """
        try:
            sendclient = self.client.packet.sendpkt
        except AttributeError:
            # Non proxy
            return False

        posx = position
        x = (position[0])
        y = (position[1])
        z = (position[2])
        if self.clientgameversion > PROTOCOL_1_7_9:
            # 1.8 +
            iddata = blockid << 4 | blockdata

            # [1.8pos/1.7x | 1.7y | 1.7z | 1.7BlockID/1.8iddata | 1.7blockdata]
            # these are whitespaced this way to line up visually
            blockparser = [_POSITION, _NULL, _NULL, _VARINT, _NULL]

            particleparser = [
                _INT, _BOOL, _FLOAT, _FLOAT, _FLOAT, _FLOAT, _FLOAT, _FLOAT,
                _FLOAT, _INT
            ]
        else:
            # 1.7
            posx = x
            iddata = blockid

            blockparser = [_INT, _UBYTE, _INT, _VARINT, _UBYTE]

            particleparser = [
                _STRING, _NULL, _FLOAT, _FLOAT, _FLOAT, _FLOAT, _FLOAT, _FLOAT,
                _FLOAT, _INT
            ]

        if sendblock:
            sendclient(self.clientboundPackets.BLOCK_CHANGE, blockparser,
                       (posx, y, x, iddata, blockdata))
        else:
            sendclient(self.clientboundPackets.PARTICLE, particleparser,
                       (blockid, True, x + .5, y + .5, z + .5, 0, 0, 0,
                        partdata, numparticles))

    # Inventory-related actions.
    def getItemInSlot(self, slot):
        """
        Returns the item object of an item currently being held.

        """
        try:
            return self.client.inventory[slot]
        except AttributeError:
            # Non proxy
            return False

    def getHeldItem(self):
        """
        Returns the item object of an item currently being held.

        """
        try:
            return self.client.inventory[36 + self.client.slot]
        except AttributeError:
            # Non proxy
            return False

    # Permissions-related
    def hasPermission(self,
                      node,
                      another_player=False,
                      group_match=True,
                      find_child_groups=True):
        """
        If the player has the specified permission node (either
        directly, or inherited from a group that the player is in),
        it will return the value (usually True) of the node.
        Otherwise, it returns False.  Using group_match and
        find_child_groups are enabled by default.  Permissions
        can be sped up by disabling child inheritance or even
        group matching entirely (for high speed loops, for
        instance).  Normally, permissions are related to
        commands the player typed, so the 'cost' of child
        inheritance is not a concern.

        :Args:
            :node: Permission node (string)
            :another_player: sending a string name of another player
             will check THAT PLAYER's permission instead! Useful for
             checking a player's permission for someone who is not
             logged in and has no player object.
            :group_match: return a permission for any group the player
             is a member of.  If False, will only return permissions
             player has directly.
            :find_child_groups: If group matching, this will
             additionally locate matches when a group contains
             a permission that is another group's name.  So if group
             'admin' contains a permission called 'moderator', anyone
             with group admin will also have group moderator's
             permissions as well.

        :returns:  Boolean indicating whether player has permission or not.

        """
        uuid_to_check = self.mojangUuid.string
        if another_player:
            # get other player mojang uuid
            uuid_to_check = str(
                self.wrapper.uuids.getuuidbyusername(another_player))
            if not uuid_to_check:
                # probably a bad name provided.. No further check needed.
                return False

        return self.wrapper.perms.has_permission(uuid_to_check, node,
                                                 group_match,
                                                 find_child_groups)

    def setPermission(self, node, value=True):
        """
        Adds the specified permission node and optionally a value
        to the player.

        :Args:
            :node: Permission node (string)
            :value: defaults to True, but can be set to False to
             explicitly revoke a particular permission from the
             player, or to any arbitrary value.

        :returns: Nothing

        """
        self.wrapper.perms.set_permission(self.mojangUuid.string, node, value)

    def removePermission(self, node):
        """
        Completely removes a permission node from the player. They
        will inherit this permission from their groups or from
        plugin defaults.

        If the player does not have the specific permission, an
        IndexError is raised. Note that this method has no effect
        on nodes inherited from groups or plugin defaults.

        :arg node: Permission node (string)

        :returns:  Boolean; True if operation succeeds, False if
         it fails (set debug mode to see/log error).

        """
        return self.wrapper.perms.remove_permission(self.mojangUuid.string,
                                                    node)

    def resetPerms(self, uuid):
        """

        resets all user data (removes all permissions).

        :arg uuid: The online/mojang uuid (string)

        :returns:  nothing

        """
        return self.wrapper.perms.fill_user(uuid)

    def hasGroup(self, group):
        """
        Returns a boolean of whether or not the player is in
        the specified permission group.

        :arg group: Group node (string)

        :returns:  Boolean of whether player has permission or not.

        """
        return self.wrapper.perms.has_group(self.mojangUuid.string, group)

    def getGroups(self):
        """
        Returns a list of permission groups that the player is in.

        :returns:  list of groups

        """
        return self.wrapper.perms.get_groups(self.mojangUuid.string)

    def setGroup(self, group, creategroup=True):
        """
        Adds the player to a specified group.  Returns False if
        the command fails (set debiug to see error).  Failure
        is only normally expected if the group does not exist
        and creategroup is False.

        :Args:
            :group: Group node (string)
            :creategroup: If True (by default), will create the
             group if it does not exist already.  This WILL
             generate a warning log since it is not an expected
             condition.

        :returns:  Boolean; True if operation succeeds, False
         if it fails (set debug mode to see/log error).

        """
        return self.wrapper.perms.set_group(self.mojangUuid.string, group,
                                            creategroup)

    def removeGroup(self, group):
        """
        Removes the player to a specified group.

        :arg group: Group node (string)

        :returns:  (use debug logging to see any errors)

            :True: Group was found and .remove operation performed
             (assume success if no exception raised).
            :None: User not in group
            :False: player uuid not found!

        """
        return self.wrapper.perms.remove_group(self.mojangUuid.string, group)

    # Player Information
    def getFirstLogin(self):
        """
        Returns a tuple containing the timestamp of when the user
        first logged in for the first time, and the timezone (same
        as time.tzname).

        """
        return self.data.Data["firstLoggedIn"]

    # Cross-server commands
    def connect(self, address, port):
        # TODO - WORK IN PROGRESS
        """
        Upon calling, the player object will become defunct and
        the client will be transferred to another server or wrapper
        instance (provided it has online-mode turned off).

        :Args:
            :address: server address (local address)
            :port: server port (local port)

        :returns: Nothing

        """
        self.client.change_servers(address, port)
    def __init__(self, secret_passphrase):
        # setup log and config
        # needs a false setting on first in case config does not
        # load (like after changes).
        self.storage = False
        self.log = logging.getLogger('Wrapper.py')
        self.configManager = Config()
        self.configManager.loadconfig()
        self.config = self.configManager.config

        # Read Config items
        # hard coded cursor for non-readline mode
        self.cursor = ">"
        # This was to allow alternate encodings
        self.encoding = self.config["General"]["encoding"]
        self.serverpath = self.config["General"]["server-directory"]
        self.proxymode = self.config["Proxy"]["proxy-enabled"]
        self.wrapper_onlinemode = self.config["Proxy"]["online-mode"]
        self.halt_message = self.config["Misc"]["halt-message"]

        # encryption items (for passwords and sensitive user data)
        # salt is generated and stored in wrapper.properties.json
        config_changes = False
        salt = self.config["General"]["salt"]
        if not salt:
            salt = gensalt(self.encoding)
            self.config["General"]["salt"] = salt
            config_changes = True
        # passphrase is provided at startup by the wrapper operator or script (not stored)
        passphrase = phrase_to_url_safebytes(secret_passphrase, self.encoding, salt)
        self.cipher = Crypt(passphrase, self.encoding)

        # Update passwords (hash any plaintext passwords)
        for groups in self.config:
            for cfg_items in self.config[groups]:
                if cfg_items[-10:] == "-plaintext":
                    # i.e., cfg_items ===> like ["web-password-plaintext"]
                    hash_item = cfg_items[:-10]
                    # hash_item ===> i.e., ["web-password"]
                    if hash_item in self.config[groups] and self.config[groups][cfg_items]:
                        # encrypt contents of (i.e.) ["web-password-plaintext"]
                        hashed_item = self.cipher.encrypt(
                            self.config[groups][cfg_items])
                        # store in "" ["Web"]["web-password"]
                        self.config[groups][hash_item] = hashed_item
                        # set plaintext item to false (successful digest)
                        self.config[groups][cfg_items] = False
                        config_changes = True

        # Patch any old update paths "..wrapper/development/build/version.json"
        # new paths are: "..wrapper/development"
        for entries in self.config["Updates"]:
            if "/build/version.json" in str(self.config["Updates"][entries]):
                oldentry = copy.copy(self.config["Updates"][entries])
                self.config["Updates"][entries] = oldentry.split("/build/version.json")[0]
                config_changes = True

        # save changes made to config file
        if config_changes:
            self.configManager.save()

        # reload branch update info.
        self.auto_update_wrapper = self.config["Updates"]["auto-update-wrapper"]
        self.auto_update_branch = self.config["Updates"]["auto-update-branch"]
        if not self.auto_update_branch:
            self.update_url = "https://raw.githubusercontent.com/benbaptist/minecraft-wrapper/development"
        else:
            self.update_url = self.config["Updates"][self.auto_update_branch]

        self.use_timer_tick_event = self.config[
            "Gameplay"]["use-timer-tick-event"]
        self.use_readline = not(self.config["Misc"]["use-betterconsole"])

        # Storages
        self.wrapper_storage = Storage(
            "wrapper", encoding=self.encoding)
        self.wrapper_permissions = Storage(
            "permissions", encoding=self.encoding, pickle=False)
        self.wrapper_usercache = Storage(
            "usercache", encoding=self.encoding, pickle=False)

        # storage Data objects
        self.storage = self.wrapper_storage.Data
        self.usercache = self.wrapper_usercache.Data
        # self.wrapper_permissions accessed only by permissions module

        # core functions and datasets
        self.perms = Permissions(self)
        self.uuids = UUIDS(self.log, self.usercache)
        self.plugins = Plugins(self)
        self.commands = Commands(self)
        self.events = Events(self)
        self.players = {}
        self.registered_permissions = {}
        self.help = {}
        self.input_buff = ""
        self.sig_int = False
        self.command_hist = ['/help', 'help']
        self.command_index = 1

        # init items that are set up later (or opted out of/ not set up.)
        self.javaserver = None
        self.api = None
        self.irc = None
        self.scripts = None
        self.web = None
        self.proxy = None
        self.backups = None

        #  HaltSig - Why? ... because if self.halt was just `False`, passing
        #  a self.halt would simply be passing `False` (immutable).  Changing
        # the value of self.halt would not necessarily change the value of the
        # passed parameter (unless it was specifically referenced back as
        # `wrapper.halt`). Since the halt signal needs to be passed, possibly
        # several layers deep, and into modules that it may be desireable to
        # not have direct access to wrapper, using a HaltSig object is
        # more desireable and reliable in behavior.
        self.halt = HaltSig()

        self.updated = False
        # future plan to expose this to api
        self.xplayer = ConsolePlayer(self)

        # Error messages for non-standard import failures.
        if not readline and self.use_readline:
            self.log.warning(
                "'readline' not imported.  This is needed for proper"
                " console functioning. Press <Enter> to acknowledge...")
            sys.stdin.readline()

        # requests is just being used in too many places to try
        # and track its usages piece-meal.
        if not requests:
            self.log.error(
                "You must have the requests module installed to use wrapper!"
                " console functioning. Press <Enter> to Exit...")
            sys.stdin.readline()
            self._halt()

        # create server/proxy vitals and config objects
        self.servervitals = ServerVitals(self.players)

        # LETS TAKE A SECOND TO DISCUSS PLAYER OBJECTS:
        # The ServerVitals class gets passed the player object list now, but
        # player objects are now housed in wrapper.  This is how we are
        # passing information between proxy and wrapper.

        self.servervitals.serverpath = self.config[
            "General"]["server-directory"]
        self.servervitals.state = OFF
        self.servervitals.command_prefix = self.config[
            "Misc"]["command-prefix"]

        self.proxyconfig = ProxyConfig()
        self.proxyconfig.proxy = self.config["Proxy"]
        self.proxyconfig.entity = self.config["Entities"]
class Wrapper(object):
    def __init__(self, secret_passphrase):
        # setup log and config
        # needs a false setting on first in case config does not
        # load (like after changes).
        self.storage = False
        self.log = logging.getLogger('Wrapper.py')
        self.configManager = Config()
        self.configManager.loadconfig()
        self.config = self.configManager.config

        # Read Config items
        # hard coded cursor for non-readline mode
        self.cursor = ">"
        # This was to allow alternate encodings
        self.encoding = self.config["General"]["encoding"]
        self.serverpath = self.config["General"]["server-directory"]
        self.proxymode = self.config["Proxy"]["proxy-enabled"]
        self.wrapper_onlinemode = self.config["Proxy"]["online-mode"]
        self.halt_message = self.config["Misc"]["halt-message"]

        # encryption items (for passwords and sensitive user data)
        # salt is generated and stored in wrapper.properties.json
        config_changes = False
        salt = self.config["General"]["salt"]
        if not salt:
            salt = gensalt(self.encoding)
            self.config["General"]["salt"] = salt
            config_changes = True
        # passphrase is provided at startup by the wrapper operator or script (not stored)
        passphrase = phrase_to_url_safebytes(secret_passphrase, self.encoding, salt)
        self.cipher = Crypt(passphrase, self.encoding)

        # Update passwords (hash any plaintext passwords)
        for groups in self.config:
            for cfg_items in self.config[groups]:
                if cfg_items[-10:] == "-plaintext":
                    # i.e., cfg_items ===> like ["web-password-plaintext"]
                    hash_item = cfg_items[:-10]
                    # hash_item ===> i.e., ["web-password"]
                    if hash_item in self.config[groups] and self.config[groups][cfg_items]:
                        # encrypt contents of (i.e.) ["web-password-plaintext"]
                        hashed_item = self.cipher.encrypt(
                            self.config[groups][cfg_items])
                        # store in "" ["Web"]["web-password"]
                        self.config[groups][hash_item] = hashed_item
                        # set plaintext item to false (successful digest)
                        self.config[groups][cfg_items] = False
                        config_changes = True

        # Patch any old update paths "..wrapper/development/build/version.json"
        # new paths are: "..wrapper/development"
        for entries in self.config["Updates"]:
            if "/build/version.json" in str(self.config["Updates"][entries]):
                oldentry = copy.copy(self.config["Updates"][entries])
                self.config["Updates"][entries] = oldentry.split("/build/version.json")[0]
                config_changes = True

        # save changes made to config file
        if config_changes:
            self.configManager.save()

        # reload branch update info.
        self.auto_update_wrapper = self.config["Updates"]["auto-update-wrapper"]
        self.auto_update_branch = self.config["Updates"]["auto-update-branch"]
        if not self.auto_update_branch:
            self.update_url = "https://raw.githubusercontent.com/benbaptist/minecraft-wrapper/development"
        else:
            self.update_url = self.config["Updates"][self.auto_update_branch]

        self.use_timer_tick_event = self.config[
            "Gameplay"]["use-timer-tick-event"]
        self.use_readline = not(self.config["Misc"]["use-betterconsole"])

        # Storages
        self.wrapper_storage = Storage(
            "wrapper", encoding=self.encoding)
        self.wrapper_permissions = Storage(
            "permissions", encoding=self.encoding, pickle=False)
        self.wrapper_usercache = Storage(
            "usercache", encoding=self.encoding, pickle=False)

        # storage Data objects
        self.storage = self.wrapper_storage.Data
        self.usercache = self.wrapper_usercache.Data
        # self.wrapper_permissions accessed only by permissions module

        # core functions and datasets
        self.perms = Permissions(self)
        self.uuids = UUIDS(self.log, self.usercache)
        self.plugins = Plugins(self)
        self.commands = Commands(self)
        self.events = Events(self)
        self.players = {}
        self.registered_permissions = {}
        self.help = {}
        self.input_buff = ""
        self.sig_int = False
        self.command_hist = ['/help', 'help']
        self.command_index = 1

        # init items that are set up later (or opted out of/ not set up.)
        self.javaserver = None
        self.api = None
        self.irc = None
        self.scripts = None
        self.web = None
        self.proxy = None
        self.backups = None

        #  HaltSig - Why? ... because if self.halt was just `False`, passing
        #  a self.halt would simply be passing `False` (immutable).  Changing
        # the value of self.halt would not necessarily change the value of the
        # passed parameter (unless it was specifically referenced back as
        # `wrapper.halt`). Since the halt signal needs to be passed, possibly
        # several layers deep, and into modules that it may be desireable to
        # not have direct access to wrapper, using a HaltSig object is
        # more desireable and reliable in behavior.
        self.halt = HaltSig()

        self.updated = False
        # future plan to expose this to api
        self.xplayer = ConsolePlayer(self)

        # Error messages for non-standard import failures.
        if not readline and self.use_readline:
            self.log.warning(
                "'readline' not imported.  This is needed for proper"
                " console functioning. Press <Enter> to acknowledge...")
            sys.stdin.readline()

        # requests is just being used in too many places to try
        # and track its usages piece-meal.
        if not requests:
            self.log.error(
                "You must have the requests module installed to use wrapper!"
                " console functioning. Press <Enter> to Exit...")
            sys.stdin.readline()
            self._halt()

        # create server/proxy vitals and config objects
        self.servervitals = ServerVitals(self.players)

        # LETS TAKE A SECOND TO DISCUSS PLAYER OBJECTS:
        # The ServerVitals class gets passed the player object list now, but
        # player objects are now housed in wrapper.  This is how we are
        # passing information between proxy and wrapper.

        self.servervitals.serverpath = self.config[
            "General"]["server-directory"]
        self.servervitals.state = OFF
        self.servervitals.command_prefix = self.config[
            "Misc"]["command-prefix"]

        self.proxyconfig = ProxyConfig()
        self.proxyconfig.proxy = self.config["Proxy"]
        self.proxyconfig.entity = self.config["Entities"]

    def __del__(self):
        """prevent error message on very first wrapper starts when
        wrapper exits after creating new wrapper.properties file.
        """
        if self.storage:
            self.wrapper_storage.close()
            self.wrapper_permissions.close()
            self.wrapper_usercache.close()

    def start(self):
        """wrapper execution starts here"""

        self.signals()

        self.backups = Backups(self)

        self.api = API(self, "Wrapper.py")
        self._registerwrappershelp()

        # This is not the actual server... the MCServer
        # class is a console wherein the server is started
        self.javaserver = MCServer(self, self.servervitals)
        self.javaserver.init()

        # load plugins
        self.plugins.loadplugins()

        if self.config["IRC"]["irc-enabled"]:  # this should be a plugin
            self.irc = IRC(self.javaserver, self.log, self)
            t = threading.Thread(target=self.irc.init, args=())
            t.daemon = True
            t.start()

        if self.config["Web"]["web-enabled"]:  # this should be a plugin
            if manageweb.pkg_resources and manageweb.requests:
                self.log.warning(
                    "Our apologies!  Web mode is currently broken.  Wrapper"
                    " will start web mode anyway, but it will not likely "
                    "function well (or at all).  For now, you should turn "
                    "off web mode in wrapper.properties.json.")
                self.web = manageweb.Web(self)
                t = threading.Thread(target=self.web.wrap, args=())
                t.daemon = True
                t.start()
            else:
                self.log.error(
                    "Web remote could not be started because you do not have"
                    " the required modules installed: pkg_resources\n"
                    "Hint: http://stackoverflow.com/questions/7446187")

        # Console Daemon runs while not wrapper.halt.halt
        consoledaemon = threading.Thread(
            target=self.parseconsoleinput, args=())
        consoledaemon.daemon = True
        consoledaemon.start()

        # Timer runs while not wrapper.halt.halt
        ts = threading.Thread(target=self.event_timer_second, args=())
        ts.daemon = True
        ts.start()

        if self.use_timer_tick_event:
            # Timer runs while not wrapper.halt.halt
            tt = threading.Thread(target=self.event_timer_tick, args=())
            tt.daemon = True
            tt.start()

        if self.config["General"]["shell-scripts"]:
            if os.name in ("posix", "mac"):
                self.scripts = Scripts(self)
            else:
                self.log.error(
                    "Sorry, but shell scripts only work on *NIX-based systems!"
                    " If you are using a  *NIX-based system, please file a "
                    "bug report.")

        if self.proxymode:
            t = threading.Thread(target=self._startproxy, args=())
            t.daemon = True
            t.start()

        if self.auto_update_wrapper:
            t = threading.Thread(target=self._auto_update_process, args=())
            t.daemon = True
            t.start()

        self.javaserver.handle_server()
        # handle_server always runs, even if the actual server is not started

        self.plugins.disableplugins()
        self.log.info("Plugins disabled")
        self.wrapper_storage.close()
        self.wrapper_permissions.close()
        self.wrapper_usercache.close()
        self.log.info("Wrapper Storages closed and saved.")

        # wrapper execution ends here.  handle_server ends when
        # wrapper.halt.halt is True.
        if self.sig_int:
            self.log.info("Ending threads, please wait...")
            time.sleep(5)
            os.system("reset")

    def signals(self):
        signal.signal(signal.SIGINT, self.sigint)
        signal.signal(signal.SIGTERM, self.sigterm)
        # noinspection PyBroadException
        try:
            # lacking in Windows
            signal.signal(signal.SIGTSTP, self.sigtstp)
        except:
            pass

    def sigint(*args):
        self = args[0]  # We are only interested in the self component
        self.log.info("Wrapper.py received SIGINT; halting...\n")
        self.sig_int = True
        self._halt()

    def sigterm(*args):
        self = args[0]  # We are only interested in the self component
        self.log.info("Wrapper.py received SIGTERM; halting...\n")
        self._halt()

    def sigtstp(*args):
        # We are only interested in the 'self' component
        self = args[0]
        self.log.info("Wrapper.py received SIGTSTP; NO sleep support!"
                      " Wrapper halting...\n")
        os.system("kill -CONT %d" % self.javaserver.proc.pid)
        self._halt()

    def _halt(self):
        self.javaserver.stop(self.halt_message, restart_the_server=False)
        self.halt.halt = True

    def shutdown(self):
        self._halt()

    def write_stdout(self, message="", source="print"):
        """
        :param message: desired output line.  Default is wrapper.
        :param source: "server", "wrapper", "print" or "log".  Default is
         print.

        """
        cursor = self.cursor

        if self.use_readline:
            print(message)
            return

        def _wrapper(msg):
            """_wrapper is normally displaying a live typing buffer.
            Therefore, there is no cr/lf at end because it is 
            constantly being re-printed in the same spot as the
            user types."""
            if msg != "":
                # re-print what the console user was typing right below that.
                # /wrapper commands receive special magenta coloring
                if msg[0:1] == '/':
                    print("{0}{1}{2}{3}{4}{5}".format(
                        UP_LINE, cursor, FG_YELLOW,
                        msg, RESET, CLEAR_EOL))
                else:
                    print("{0}{1}{2}{3}".format(
                        BACKSPACE, cursor,
                        msg, CLEAR_EOL))

        def _server(msg):
            # print server lines
            print("{0}{1}{2}\r\n".format(UP_LINE, CLEAR_LINE, msg, CLEAR_EOL))

        def _print(msg):
            _server(msg)

        parse = {
            "server": _server,
            "wrapper": _wrapper,
            "print": _print,
        }

        # if this fails due to key error, we WANT that raised, as it is
        #  a program code error, not a run-time error.
        parse[source](message)

    def getconsoleinput(self):
        """If wrapper is NOT using readline (self.use_readline == False),
        then getconsoleinput manually implements our own character 
        reading, parsing, arrow keys, command history, etc.  This 
        is desireable because it allows the user to continue to see
        their input and modify it, even if the server is producing
        console line messages that would normally "carry away" the 
        user's typing.

        Implemented in response to issue 326:
        'Command being typed gets carried off by console every time
         server generates output #326' by @Darkness3840:
        https://github.com/benbaptist/minecraft-wrapper/issues/326
        """
        if self.use_readline:
            # Obtain a line of console input
            try:
                consoleinput = sys.stdin.readline().strip()
            except Exception as e:
                self.log.error(
                    "[continue] variable 'consoleinput' in 'console()' did"
                    " not evaluate \n%s" % e)
                consoleinput = ""

        else:
            arrow_index = 0
            # working buffer allows arrow use to restore what they
            # were typing but did not enter as a command yet
            working_buff = ''
            while not self.halt.halt:
                keypress = readkey.getcharacter()
                keycode = readkey.convertchar(keypress)
                length = len(self.input_buff)

                if keycode == "right":
                    arrow_index += 1
                    if arrow_index > length:
                        arrow_index = length

                if keycode == "left":
                    arrow_index -= 1
                    if arrow_index < 1:
                        arrow_index = 0

                if keycode == "up":
                    # goes 'back' in command history time
                    self.command_index -= 1
                    if self.command_index < 1:
                        self.command_index = 0
                    self.input_buff = self.command_hist[self.command_index]
                    arrow_index = len(self.input_buff)

                if keycode == "down":
                    # goes forward in command history time
                    self.command_index += 1

                    if self.command_index + 1 > len(self.command_hist):
                        # These actions happen when at most recent typing
                        self.command_index = len(self.command_hist)
                        self.input_buff = '%s' % working_buff
                        self.write_stdout(
                            "%s " % self.input_buff, source="wrapper")
                        arrow_index = len(self.input_buff)
                        continue

                    self.input_buff = self.command_hist[self.command_index]
                    arrow_index = len(self.input_buff)

                buff_left = "%s" % self.input_buff[:arrow_index]
                buff_right = "%s" % self.input_buff[arrow_index:]

                if keycode == "backspace":
                    if len(buff_left) > 0:
                        buff_left = buff_left[:-1]
                        self.input_buff = "%s%s" % (buff_left, buff_right)
                        working_buff = "%s" % self.input_buff
                        arrow_index -= 1

                if keycode == "delete":
                    if len(buff_right) > 0:
                        buff_right = buff_right[1:]
                    self.input_buff = "%s%s" % (buff_left, buff_right)
                    working_buff = "%s" % self.input_buff

                if keycode in ("enter", "cr", "lf"):
                    # scroll up (because cr is not added to buffer)
                    # print("")
                    break

                if keycode in ("ctrl-c", "ctrl-x"):
                    self.sigterm()
                    break

                # hide special key codes like PAGE_UP, etc if not used
                if not keycode:
                    buff_left = "%s%s" % (buff_left, keypress)
                    self.input_buff = "%s%s" % (buff_left, buff_right)
                    working_buff = "%s" % self.input_buff
                    arrow_index += 1

                # with open('readout.txt', "w") as f:
                #     f.write("left: '%s'\nright: '%s'\nbuff: '%s'" % (
                #         buff_left, buff_right, self.input_buff))

                if len(buff_right) > 0:
                    self.write_stdout("{0}{1}{2}{3}".format(
                        REVERSED, buff_left, RESET, buff_right),
                        "wrapper")
                else:
                    self.write_stdout(
                        "%s " % self.input_buff, source="wrapper")

            consoleinput = "%s" % self.input_buff
            self.input_buff = ""

            if consoleinput in self.command_hist:
                # if the command is already in the history somewhere,
                # remove it and re-append to the end (most recent)
                self.command_hist.remove(consoleinput)
                self.command_hist.append(consoleinput)
            else:
                # or just add it.
                self.command_hist.append(consoleinput)
            self.command_index = len(self.command_hist)

            # print the finished command to console
            self.write_stdout(
                "%s\r\n" % self.input_buff, source="wrapper")

        return consoleinput

    def parseconsoleinput(self):
        while not self.halt.halt:
            consoleinput = self.getconsoleinput()
            # No command (perhaps just a line feed or spaces?)
            if len(consoleinput) < 1:
                continue

            # for use with runwrapperconsolecommand() command
            wholecommandline = consoleinput[0:].split(" ")
            command = str(getargs(wholecommandline, 0)).lower()

            # this can be passed to runwrapperconsolecommand() command for args
            allargs = wholecommandline[1:]

            # Console only commands (not accessible in-game)
            if command in ("/halt", "halt"):
                self._halt()
            elif command in ("/stop", "stop"):
                self.javaserver.stop_server_command()
            # "kill" (with no slash) is a server command.
            elif command == "/kill":
                self.javaserver.kill("Server killed at Console...")
            elif command in ("/start", "start"):
                self.javaserver.start()
            elif command in ("/restart", "restart"):
                self.javaserver.restart()
            elif command in ("/update-wrapper", "update-wrapper"):
                self._checkforupdate(True)
            # "plugins" command (with no slash) reserved for server commands
            elif command == "/plugins":
                self.listplugins()
            elif command in ("/mem", "/memory", "mem", "memory"):
                self._memory()
            elif command in ("/raw", "raw"):
                self._raw(consoleinput)
            elif command in ("/freeze", "freeze"):
                self._freeze()
            elif command in ("/unfreeze", "unfreeze"):
                self._unfreeze()
            elif command == "/version":
                readout("/version", self.getbuildstring(),
                        usereadline=self.use_readline)
            elif command in ("/mute", "/pause", "/cm", "/m", "/p"):
                self._mute_console(allargs)

            # Commands that share the commands.py in-game interface

            # "reload" (with no slash) may be used by bukkit servers
            elif command == "/reload":
                self.runwrapperconsolecommand("reload", [])

            # proxy mode ban system
            elif self.proxymode and command == "/ban":
                self.runwrapperconsolecommand("ban", allargs)

            elif self.proxymode and command == "/ban-ip":
                self.runwrapperconsolecommand("ban-ip", allargs)

            elif self.proxymode and command == "/pardon-ip":
                self.runwrapperconsolecommand("pardon-ip", allargs)

            elif self.proxymode and command == "/pardon":
                self.runwrapperconsolecommand("pardon", allargs)

            elif command in ("/perm", "/perms", "/super", "/permissions",
                             "perm", "perms", "super", "permissions"):
                self.runwrapperconsolecommand("perms", allargs)

            elif command in ("/playerstats", "/stats", "playerstats", "stats"):
                self.runwrapperconsolecommand("playerstats", allargs)

            elif command in ("/ent", "/entity", "/entities", "ent",
                             "entity", "entities"):
                self.runwrapperconsolecommand("ent", allargs)

            elif command in ("/config", "/con", "/prop",
                             "/property", "/properties"):
                self.runwrapperconsolecommand("config", allargs)

            elif command in ("op", "/op"):
                self.runwrapperconsolecommand("op", allargs)

            elif command in ("deop", "/deop"):
                self.runwrapperconsolecommand("deop", allargs)

            elif command in ("pass", "/pass", "pw", "/pw", "password", "/password"):
                self.runwrapperconsolecommand("password", allargs)

            # TODO Add more commands below here, below the original items:
            # TODO __________________

            # more commands here...

            # TODO __________________
            # TODO add more commands above here, above the help-related items:

            # minecraft help command
            elif command == "help":
                readout("/help", "Get wrapper.py help.",
                        separator=" (with a slash) - ",
                        usereadline=self.use_readline)
                self.javaserver.console(consoleinput)

            # wrapper's help (console version)
            elif command == "/help":
                self._show_help_console()

            # wrapper ban help
            elif command == "/bans":
                self._show_help_bans()

            # Commmand not recognized by wrapper
            else:
                try:
                    self.javaserver.console(consoleinput)
                except Exception as e:
                    self.log.error("[BREAK] Console input exception"
                                   " (nothing passed to server) \n%s" % e)
                    break
                continue

    def _registerwrappershelp(self):
        # All commands listed herein are accessible in-game
        # Also require player.isOp()
        new_usage = "<player> [-s SUPER-OP] [-o OFFLINE] [-l <level>]"
        self.api.registerHelp(
            "Wrapper", "Internal Wrapper.py commands ",
            [
                ("/wrapper [update/memory/halt]",
                 "If no subcommand is provided, it will"
                 " show the Wrapper version.", None),
                ("/playerstats [all]",
                 "Show the most active players. If no subcommand"
                 " is provided, it'll show the top 10 players.",
                 None),
                ("/plugins",
                 "Show a list of the installed plugins", None),
                ("/reload", "Reload all plugins.", None),
                ("/op %s" % new_usage, "This and deop are Wrapper commands.",
                 None),
                ("/permissions <groups/users/RESET>",
                 "Command used to manage permission groups and"
                 " users, add permission nodes, etc.",
                 None),
                ("/entity <count/kill> [eid] [count]",
                 "/entity help/? for more help.. ", None),
                ("/config", "Change wrapper.properties (type"
                            " /config help for more..)", None),
                ("/password", "Sample usage: /pw IRC control-irc-pass <new"
                              "password>", None),

                # Minimum server version for commands to appear is
                # 1.7.6 (registers perm later in serverconnection.py)
                # These won't appear if proxy mode is not on (since
                # serverconnection is part of proxy).
                ("/ban <name> [reason..] [d:<days>/h:<hours>]",
                 "Ban a player. Specifying h:<hours> or d:<days>"
                 " creates a temp ban.", "mc1.7.6"),
                ("/ban-ip <ip> [<reason..> <d:<number of days>]",
                 "- Ban an IP address. Reason and days"
                 " (d:) are optional.", "mc1.7.6"),
                ("/pardon <player> [False]",
                 " - pardon a player. Default is byuuidonly."
                 "  To unban a specific "
                 "name (without checking uuid), use `pardon"
                 " <player> False`", "mc1.7.6"),
                ("/pardon-ip <address>", "Pardon an IP address.", "mc1.7.6"),
                ("/banlist [players|ips] [searchtext]",
                 "search and display the banlist (warning -"
                 " displays on single page!)", "mc1.7.6")
            ])

    def runwrapperconsolecommand(self, wrappercommand, argslist):
        xpayload = {
            'player': self.xplayer,
            'command': wrappercommand,
            'args': argslist
        }
        self.commands.playercommand(xpayload)

    def isonlinemode(self):
        """
        :returns: Whether the server OR (for proxy mode) wrapper
        is in online mode.  This should normally 'always' render
        True. Under rare circumstances it could be false, such
        as when this wrapper and its server are the target for
        a wrapper lobby with player.connect().
        """
        if self.proxymode:
            # if wrapper is using proxy mode (which should be set to online)
            return self.config["Proxy"]["online-mode"]
        if self.javaserver is not None:
            if self.servervitals.onlineMode:
                # if local server is online-mode
                return True
        return False

    def listplugins(self):
        readout("",
                "List of Wrapper.py plugins installed:", separator="", pad=4,
                usereadline=self.use_readline)
        for plid in self.plugins:
            plugin = self.plugins[plid]
            if plugin["good"]:
                name = plugin["name"]
                summary = plugin["summary"]
                if summary is None:
                    summary = "No description available for this plugin"

                version = plugin["version"]
                readout(name, summary,
                        separator=(
                            " - v%s - " % ".".join([str(_) for _ in version])),
                        usereadline=self.use_readline)
            else:
                readout("failed to load plugin", plugin, pad=25,
                        usereadline=self.use_readline)

    def _start_emailer(self):
        alerts = self.config["Alerts"]["enabled"]
        if alerts:
            self.config["Alerts"] = "alerts true"

    def _startproxy(self):

        # error will raise if requests or cryptography is missing.
        self.proxy = Proxy(self.halt, self.proxyconfig,
                           self.servervitals, self.log,
                           self.usercache, self.events)

        # wait for server to start
        timer = 0
        while self.servervitals.state < STARTED:
            timer += 1
            time.sleep(.1)
            if timer > 1200:
                self.log.warning(
                    "Proxy mode did not detect a started server within 2"
                    " minutes.  Disabling proxy mode because something is"
                    " wrong.")
                self.disable_proxymode()
                return

        if self.proxy.proxy_port == self.servervitals.server_port:
            self.log.warning("Proxy mode cannot start because the wrapper"
                             " port is identical to the server port.")
            self.disable_proxymode()
            return

        proxythread = threading.Thread(target=self.proxy.host, args=())
        proxythread.daemon = True
        proxythread.start()

    def disable_proxymode(self):
        self.proxymode = False
        self.config["Proxy"]["proxy-enabled"] = False
        self.configManager.save()
        self.log.warning(
            "\nProxy mode is now turned off in wrapper.properties.json.\n")

    @staticmethod
    def getbuildstring():
        if core_buildinfo_version.__branch__ == "dev":
            return "%s (development build #%d)" % (
                core_buildinfo_version.__version__,
                core_buildinfo_version.__build__)

        elif core_buildinfo_version.__branch__ == "stable":
            return "%s (stable)" % core_buildinfo_version.__version__
        else:
            return "Version: %s (%s build #%d)" % (
                core_buildinfo_version.__version__,
                core_buildinfo_version.__branch__,
                core_buildinfo_version.__build__)

    def _auto_update_process(self):
        while not self.halt.halt:
            time.sleep(3600)
            if self.updated:
                self.log.info(
                    "An update for wrapper has been loaded,"
                    " Please restart wrapper.")
            else:
                self._checkforupdate()

    def _checkforupdate(self, update_now=False):
        """ checks for update """
        self.log.info("Checking for new builds...")
        update = self.get_wrapper_update_info()
        if update:
            version, build, repotype, reponame = update
            self.log.info(
                "New Wrapper.py %s build #%d is available!"
                " (current build is #%d)",
                repotype, build, core_buildinfo_version.__build__)

            if self.auto_update_wrapper or update_now:
                self.log.info("Updating...")
                self.performupdate(version, build)
            else:
                self.log.info(
                    "Because you have 'auto-update-wrapper' set to False,"
                    " you must manually update Wrapper.py. To update"
                    " Wrapper.py manually, please type /update-wrapper.")
        else:
            self.log.info("No new versions available.")

    def get_wrapper_update_info(self, repotype=None):
        """get the applicable branch wrapper update"""
        # read the installed branch info
        if repotype is None:
            repotype = core_buildinfo_version.__branch__
        if self.auto_update_branch:
            branch_key = self.auto_update_branch
        else:
            branch_key = "%s-branch" % repotype
        r = requests.get("%s/build/version.json" % self.config["Updates"][branch_key])
        if r.status_code == 200:
            data = r.json()
            if data["__build__"] > core_buildinfo_version.__build__:
                if repotype == "dev":
                    reponame = "development"
                elif repotype == "stable":
                    reponame = "master"
                else:
                    reponame = data["__branch__"]
                if "__version__" not in data:
                    data["__version__"] = data["version"]
                return data["__version__"], data["__build__"], data["__branch__"], reponame

        else:
            self.log.warning(
                "Failed to check for updates - are you connected to the"
                " internet? (Status Code %d)", r.status_code)
            return False

    def performupdate(self, version, build):
        """
        Perform update; returns True if update succeeds.  User must
        still restart wrapper manually.

        :param version: first argument from get_wrapper_update_info()
        :param build: second argument from get_wrapper_update_info()
        :return: True if update succeeds
        """

        wraphash = requests.get("%s/build/Wrapper.py.md5" % self.update_url)
        wrapperfile = requests.get("%s/Wrapper.py" % self.update_url)

        if wraphash.status_code == 200 and wrapperfile.status_code == 200:
            self.log.info("Verifying Wrapper.py...")
            if hashlib.md5(wrapperfile.content).hexdigest() == wraphash.text:
                self.log.info(
                    "Update file successfully verified. Installing...")
                with open(sys.argv[0], "wb") as f:
                    f.write(wrapperfile.content)
                self.log.info(
                    "Wrapper.py %s (#%d) installed. Please reboot Wrapper.py.",
                    ".".join([str(_) for _ in version]), build)
                self.updated = True
                return True
            else:
                return False
        else:
            self.log.error(
                "Failed to update due to an internal error (%d, %d)",
                wraphash.status_code,
                wrapperfile.status_code, exc_info=True)
            return False

    def event_timer_second(self):
        while not self.halt.halt:
            time.sleep(1)
            self.events.callevent("timer.second", None)
            """ eventdoc
                <group> wrapper <group>

                <description> a timer that is called each second.
                <description>

                <abortable> No <abortable>

            """

    def event_timer_tick(self):
        while not self.halt.halt:
            self.events.callevent("timer.tick", None)
            time.sleep(0.05)
            """ eventdoc
                <group> wrapper <group>

                <description> a timer that is called each 1/20th
                <sp> of a second, like a minecraft tick.
                <description>

                <abortable> No <abortable>

                <comments>
                Use of this timer is not suggested and is turned off
                <sp> by default in the wrapper.config.json file
                <comments>

            """

    def _pause_console(self, pause_time):
        if not self.javaserver:
            readout("ERROR - ",
                    "There is no running server instance to mute.",
                    separator="", pad=10, usereadline=self.use_readline)
            return
        self.javaserver.server_muted = True
        readout("Server is now muted for %d seconds." % pause_time, "",
                separator="", command_text_fg="yellow",
                usereadline=self.use_readline)
        time.sleep(pause_time)
        readout("Server now unmuted.", "", separator="",
                usereadline=self.use_readline)
        self.javaserver.server_muted = False
        for lines in self.javaserver.queued_lines:
            readout("Q\\", "", lines, pad=3, usereadline=self.use_readline)
            time.sleep(.1)
        self.javaserver.queued_lines = []

    def _mute_console(self, all_args):
        pausetime = 30
        if len(all_args) > 0:
            pausetime = get_int(all_args[0])
        # spur off a pause thread
        cm = threading.Thread(target=self._pause_console, args=(pausetime,))
        cm.daemon = True
        cm.start()

    def _freeze(self):
        try:
            self.javaserver.freeze()
        except OSError as ex:
            self.log.error(ex)
        except EnvironmentError as e:
            self.log.warning(e)
        except Exception as exc:
            self.log.exception(
                "Something went wrong when trying to freeze the"
                " server! (%s)", exc)

    def _memory(self):
        try:
            get_bytes = self.javaserver.getmemoryusage()
        except OSError as e:
            self.log.error(e)
        except Exception as ex:
            self.log.exception(
                "Something went wrong when trying to fetch"
                " memory usage! (%s)", ex)
        else:
            amount, units = format_bytes(get_bytes)
            self.log.info(
                "Server Memory Usage: %s %s (%s bytes)" % (
                    amount, units, get_bytes))

    def _raw(self, console_input):
        try:
            if len(getargsafter(console_input[1:].split(" "), 1)) > 0:
                self.javaserver.console(
                    getargsafter(console_input[1:].split(" "), 1))
            else:
                self.log.info("Usage: /raw [command]")
        except EnvironmentError as e:
            self.log.warning(e)

    def _unfreeze(self):
        try:
            self.javaserver.unfreeze()
        except OSError as ex:
            self.log.error(ex)
        except EnvironmentError as e:
            self.log.warning(e)
        except Exception as exc:
            self.log.exception(
                "Something went wrong when trying to unfreeze"
                " the server! (%s)", exc)

    def _show_help_console(self):
        # This is the console help command display.
        readout("", "Get Minecraft help.",
                separator="help (no slash) - ", pad=0,
                usereadline=self.use_readline)
        readout("/reload", "Reload Wrapper.py plugins.",
                usereadline=self.use_readline)
        readout("/plugins", "Lists Wrapper.py plugins.",
                usereadline=self.use_readline)
        readout("/update-wrapper",
                "Checks for new Wrapper.py updates, and will install\n"
                "them automatically if one is available.",
                usereadline=self.use_readline)
        readout("/stop",
                "Stop the minecraft server without"
                " auto-restarting and without\n"
                "                  shuttingdown Wrapper.py.",
                usereadline=self.use_readline)
        readout("/start", "Start the minecraft server.",
                usereadline=self.use_readline)
        readout("/restart", "Restarts the minecraft server.",
                usereadline=self.use_readline)
        readout("/halt", "Shutdown Wrapper.py completely.",
                usereadline=self.use_readline)
        readout("/cm [seconds]",
                "Mute server output (Wrapper console"
                " logging still happens)",
                usereadline=self.use_readline)
        readout("/kill", "Force kill the server without saving.",
                usereadline=self.use_readline)
        readout("/freeze",
                "Temporarily locks the server up"
                " until /unfreeze is executed\n"
                "                  (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/unfreeze", "Unlocks a frozen state server"
                             " (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/mem", "Get memory usage of the server"
                        " (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/raw [command]",
                "Send command to the Minecraft"
                " Server. Useful for Forge\n"
                "                  commands like '/fml confirm'.",
                usereadline=self.use_readline)
        readout("/password",
                "run `/password help` for more...)",
                usereadline=self.use_readline)
        readout("/perms", "/perms for more...)",
                usereadline=self.use_readline)
        readout("/config", "Change wrapper.properties (type"
                           " /config help for more..)",
                usereadline=self.use_readline)
        readout("/version", self.getbuildstring(),
                usereadline=self.use_readline)
        readout("/entity",
                "Work with entities (run /entity for more...)",
                usereadline=self.use_readline)
        readout("/bans", "Display the ban help page.",
                usereadline=self.use_readline)

    def _show_help_bans(self):
        # ban commands help.
        if not self.proxymode:
            readout(
                "ERROR - ",
                "Wrapper proxy-mode bans are not enabled "
                "(proxy mode is not on).", separator="",
                pad=10,
                usereadline=self.use_readline)
            return

        readout(
            "",
            "Bans - To use the server's versions, do not type a slash.",
            separator="", pad=5,
            usereadline=self.use_readline)
        readout(
            "", "", separator="-----1.7.6 and later ban commands-----",
            pad=10,
            usereadline=self.use_readline)
        readout(
            "/ban",
            " - Ban a player. Specifying h:<hours> or d:<days>"
            " creates a temp ban.",
            separator="<name> [reason..] [d:<days>/h:<hours>] ",
            pad=12,
            usereadline=self.use_readline)
        readout(
            "/ban-ip",
            " - Ban an IP address. Reason and days (d:) are optional.",
            separator="<ip> [<reason..> <d:<number of days>] ", pad=12,
            usereadline=self.use_readline)
        readout(
            "/pardon",
            " - pardon a player. Default is byuuidonly.  To unban a"
            "specific name (without checking uuid), use"
            " `pardon <player> False`",
            separator="<player> [byuuidonly(true/false)]", pad=12,
            usereadline=self.use_readline)
        readout(
            "/pardon-ip", " - Pardon an IP address.",
            separator="<address> ", pad=12,
            usereadline=self.use_readline)
        readout(
            "/banlist",
            " - search and display the banlist (warning -"
            " displays on single page!)",
            separator="[players|ips] [searchtext] ", pad=12,
            usereadline=self.use_readline)
Beispiel #19
0
    def getStorage(self, name, world=False, pickle=True):
        """
        Returns a storage object manager for saving data between reboots.

        :Args:
            :name:  The name of the storage (on disk).
            :world:  THe location of the storage on disk -
                :False: '/wrapper-data/plugins'.
                :True: '<serverpath>/<worldname>/plugins'.
            :Pickle:  Whether wrapper should pickle or save as json.

            Pickle formatting is the default. pickling is
             less strict than json formats and leverages binary storage.
             Use of json can result in errors if your keys or data do not
             conform to json standards (like use of string keys).  However,
             pickle is not generally human-readable, whereas json is human
             readable.

        :Returns: A storage object manager.  The manager contains a
         storage dictionary called 'Data'. 'Data' contains the
         data your plugin will remember across reboots.
        ___

        :NOTE: This method is somewhat different from previous Wrapper
         versions prior to 0.10.1 (build 182).  The storage object is
         no longer a data object itself; It is a manager used for
         controlling the saving of the object data.  The actual data
         is contained in the property/dictionary variable 'Data'

        ___

        :sample methods:

            The new method:

            .. code:: python

                # to start a storage:
                self.homes = self.api.getStorage("homes", True)

                # access the data:
                for player in self.homes.Data:  # note upper case `D`
                    print("player %s has a home at: %s" % (
                        player, self.homes.Data[player]))

                # to save (storages also do periodic saves every minute):
                self.homes.save()

                # to close (and save):
                def onDisable(self):
                    self.homes.close()

                # to load a storage from disk:
                self.homes.load()
            ..

            the key difference is here (under the old Storage API):

            .. code:: python

                # This used to work under the former API
                # however, this will produce an exception
                # because "self.homes" is no longer an
                # iterable data set:
                for player in self.homes:  <= Exception!
                    print("player %s has a home at: %s" % (
                        player, self.homes[player]))
            ..

            **tip**
            *to make the transition easier for existing code, redefine
            your the storage statements above like this to re-write as
            few lines as possible (and avoid problems with other
            plugins that might link to your plugin's data)*:

            .. code:: python

                # change your storage setup from:
                self.homes = self.api.getStorage("homes", True)

                # to:
                self.homestorage = self.api.getStorage("homes", True)
                self.homes = homestorage.Data

                # Now the only other change you need to make is to any
                # .save() or .close() statements:
                def onDisable(self):
                    # self.homes.close()  # change to -
                    self.homestorage.close()
            ..

        """
        if world:
            return Storage(
                name,
                root="%s/%s/plugins/%s" %
                (self.serverpath, self.minecraft.getWorldName(), self.id),
                pickle=pickle)
        else:
            return Storage(name,
                           root="wrapper-data/plugins/%s" % self.id,
                           pickle=pickle)
    def __init__(self, username, wrapper):
        """
        :UUIDS:
            All uuids are wrapper's MCUUID objects.  If being used in a string
            context, they must be used with the *.string property (or str()
            explicitly):
                player.mojangUuid.string
                player.mojangUuis.__str__
                str(player.mojangUuid)

            The only exception to this is the `uuid` property, which is always
             a string.

            :uuid (property, string): This will pull the best uuid
             available in this order-
            :1) mojangUuid: The bought and paid Mojand UUID.  Never changes and
             is the prefered way to ID player keys.
            :2) offlineUuid: A MD5 hash of "OfflinePlayer:%s" % username
            :3) clientUuid: What the client believes is the uuid.  If
             Wrapper is online, this should be the same as mojangUuid.
            :4) serverUuid: The player's local uuid on the server,
             usually the same as offline uuid.

        :param username:
        :param wrapper:
        """

        self.wrapper = wrapper
        self.javaserver = wrapper.javaserver
        self.log = wrapper.log
        self.username = username
        self.loggedIn = time.time()

        # mcserver will set this to false later to close the thread.
        self.abort = False
        self.data = None
        # meanwhile, it still needs to respect wrapper halts
        self.wrapper_signal = self.wrapper.haltsig
        self.kick_nonproxy_connects = self.wrapper.config["Proxy"][
            "disconnect-nonproxy-connections"]

        self.mojangUuid = False
        self.clientUuid = False
        # These two are offline by default.
        self.offlineUuid = self.wrapper.uuids.getuuidfromname(self.username)
        self.serverUuid = self.offlineUuid

        self.ipaddress = "127.0.0.0"
        self.loginposition = [0, 0, 0]
        self._position = [0, 0, 0, 0, 0]  # internally used for non-proxy mode

        self.client = None
        self.clientgameversion = self.wrapper.javaserver.protocolVersion
        self.cbpkt = Packets_cb(self.clientgameversion)
        self.sbpkt = Packets_sb(self.clientgameversion)

        self.playereid = None

        # some player properties associated with abilities
        #
        # default is 1.  Should normally be congruent with speed.
        self.field_of_view = float(1)
        # Client set godmode is 0x01
        self.godmode = 0x00
        # Client set creative is 0x08
        self.creative = 0x00
        # default is 1
        self.fly_speed = float(1)

        if self.wrapper.proxy:
            gotclient = False
            for client in self.wrapper.proxy.clients:
                if client.username == self.username:
                    self.client = client
                    self.clientUuid = client.wrapper_uuid
                    self.serverUuid = client.local_uuid
                    self.mojangUuid = client.mojanguuid
                    self.ipaddress = client.ip

                    # pktSB already set to self.wrapper.javaserver.protocolVersion  # noqa
                    self.clientboundPackets = self.client.pktCB
                    self.clientgameversion = self.client.clientversion
                    gotclient = True
                    break
            if not gotclient:
                pprint.pprint(self.wrapper.proxy.clients)
                self.log.error("Proxy is on, but this client is not "
                               "listed in proxy.clients!")
                self.log.error("The usual cause of this would be that"
                               " someone attempted to connect directly to"
                               " your server port and not the wrapper"
                               " proxy port, but can also be the result of"
                               " a player that has abruptly disconnected.")
                if self.kick_nonproxy_connects:
                    port = self.wrapper.proxy.proxy_port
                    self.log.info("API.player Kicked %s" % self.name)
                    self.abort = True
                    self.wrapper.javaserver.console(
                        "kick %s %s" % (
                            self.name,
                            "Access Denied!  Use port %s instead!" % port
                        )
                    )

                    return
        if not self.mojangUuid:
            # poll cache/mojang for proper uuid
            self.mojangUuid = self.wrapper.uuids.getuuidbyusername(username)

        # Process login data
        self.data = Storage(
            self.mojangUuid.string, root="wrapper-data/players")
        if "firstLoggedIn" not in self.data.Data:
            self.data.Data["firstLoggedIn"] = (time.time(), time.tzname)
        if "logins" not in self.data.Data:
            self.data.Data["logins"] = {}
        self.data.Data["lastLoggedIn"] = (self.loggedIn, time.tzname)
        self.data.save()

        # start player logged in time tracking thread
        t = threading.Thread(target=self._track, args=())
        t.daemon = True
        t.start()
class Web(object):

    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.config = wrapper.config  # Remember if you need to save use 'wrapper.configManager.save()' not config.save
        self.log = logging.getLogger('Web')
        self.check_password = self.wrapper.cipher.check_pw()

        if not Flask:
            self.config["Web"]["web-enabled"] = False
            self.wrapper.configManager.save()
            self.log.critical("You don't have the 'flask/flask_socketio' dashboard dependencies installed "
                              "on your system. You can now restart, but Web mode is disabled.")
            self.wrapper.haltsig.halt = True

        self.app = Flask(__name__)
        self.app.config['SECRET_KEY'] = "".join([chr(random.randrange(48, 90)) for i in range(32)])  # LOL
        self.socketio = SocketIO(self.app)

        # Flask filters
        def strftime(f):
            return datetime.datetime.fromtimestamp(int(f)).strftime('%Y-%m-%d @ %I:%M%p')
            
        self.app.jinja_env.filters["strftime"] = strftime

        # Register handlers
        self.add_decorators()

        self.data_storage = Storage("dash")
        if "keys" not in self.data_storage.Data:
            self.data_storage.Data["keys"] = []

        self.loginAttempts = 0
        self.last_attempt = 0
        self.disableLogins = 0

        # Start thread for running server
        t = threading.Thread(target=self.run, args=())
        t.daemon = True
        t.start()

    def __del__(self):
        self.data_storage.close()

    # Authorization methods
    def check_login(self, password):
        if time.time() - self.disableLogins < 60:
            return False  # Threshold for logins
        if self.check_password(password, self.config["Web"]["web-password"]):
            return True
        self.loginAttempts += 1
        if self.loginAttempts > 10 and time.time() - self.last_attempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.last_attempt = time.time()

    def make_key(self, rememberme):
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@-_"
        a = "".join([random.choice(chars) for _i in range(64)])

        self.data_storage.Data["keys"].append([a, time.time(), rememberme])
        return a

    def validate_key(self):
        if "__wrapper_cookie" not in request.cookie:
            return False

        key = request.cookie["__wrapper_cookie"]
        for i in self.data_storage.Data["keys"]:
            expiretime = 7884000  # Three weeks old
            if len(i) > 2:
                if not i[2]:
                    expiretime = 21600
            # Validate key and ensure it's under the expiretime
            if i[0] == key and time.time() - i[1] < expiretime:
                self.loginAttempts = 0
                return True
        return False

    def remove_key(self, key):
        for i, v in enumerate(self.data_storage.Data["keys"]):
            if v[0] == key:
                del self.data_storage.Data["keys"][i]

    # Dectorators and misc.
    def add_decorators(self):
        @self.app.before_request
        def handle():
            print("I'm a freakin' sandwich dude!")

        @self.app.route("/")
        def index():
            return render_template("dashboard.html")

        @self.app.route("/login", methods=["GET", "POST"])
        def login():
            badpass = False
            if request.method == "POST":
                password = request.form["password"]
                rememberme = "remember" in request.form

                if self.check_login(password):
                    key = self.make_key(rememberme)
                    return redirect("/")
                    # self.log.warning("%s logged in to web mode (remember me: %s)", request.addr, rememberme)
                else:
                    badpass = True

            return render_template("login.html", badPass=badpass)

        @self.socketio.on('connect')
        def handle_connect():
            pass

        @self.socketio.on('disconnect')
        def handle_disconnect():
            pass

    def run(self, halt):
        while not self.wrapper.haltsig.halt:
            self.socketio.run(self.app, host=self.config["Web"]["web-bind"],
                          port=self.config["Web"]["web-port"])

        self.data_storage.close()
Beispiel #22
0
def _update_db(index, term, docid):
    if _db.get(term, False):
        if _db[term].get(docid, False):
            _db[term][docid].update([index])
        else:
            _db[term][docid] = set([index])
    else:
        _db[term][docid] = set([index])


if __name__ == '__main__':

    processing = Preprocessing()
    print('Hurray !!! Program Started')
    data = Doc()._registerInstances()._getInstance('XML').parse('data.xml')
    glData = Storage()._transformData(data)._get_transformed_version()

    glData['tokens'] = glData['TEXT'].apply(
        lambda row: processing._action(row))
    print 'tokens', glData['tokens']

    result = zip(glData['DOCID'], glData['tokens'])

    print 'result', result

    for docid, etW in result:
        map(lambda term: _update_db(term[0], term[1], docid),
            list(enumerate(etW)))

    p_in_idx = Storage()._set_data_positional_data_frame(
        _db.keys(), _db.values())._get_positional_data_frame()
class Web(object):

    def __init__(self, wrapper):
        self.wrapper = wrapper
        self.config = wrapper.config  # Remember if you need to save use 'wrapper.configManager.save()' not config.save
        self.log = logging.getLogger('Web')

        if not Flask:
            self.config["Web"]["web-enabled"] = False
            self.wrapper.configManager.save()
            self.log.critical("You don't have the 'flask/flask_socketio' dashboard dependencies installed "
                              "on your system. You can now restart, but Web mode is disabled.")
            self.wrapper.halt = True

        self.app = Flask(__name__)
        self.app.config['SECRET_KEY'] = "".join([chr(random.randrange(48, 90)) for i in range(32)])  # LOL
        self.socketio = SocketIO(self.app)

        # Flask filters
        def strftime(f):
            return datetime.datetime.fromtimestamp(int(f)).strftime('%Y-%m-%d @ %I:%M%p')
            
        self.app.jinja_env.filters["strftime"] = strftime

        # Register handlers
        self.add_decorators()

        self.data = Storage("dash")
        if "keys" not in self.data.Data:
            self.data.Data["keys"] = []

        self.loginAttempts = 0
        self.lastAttempt = 0
        self.disableLogins = 0

        # Start thread for running server
        t = threading.Thread(target=self.run, args=())
        t.daemon = True
        t.start()

    def __del__(self):
        self.data.close()

    # Authorization methods
    def checkLogin(self, password):
        if time.time() - self.disableLogins < 60:
            return False  # Threshold for logins
        if password == self.config["Web"]["web-password"]:
            return True
        self.loginAttempts += 1
        if self.loginAttempts > 10 and time.time() - self.lastAttempt < 60:
            self.disableLogins = time.time()
            self.log.warning("Disabled login attempts for one minute")
        self.lastAttempt = time.time()

    def makeKey(self, rememberme):
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@-_"
        a = "".join([random.choice(chars) for i in range(64)])

        self.data.Data["keys"].append([a, time.time(), rememberme])
        return a

    def validateKey(self):
        if "__wrapper_cookie" not in request.cookie:
            return False

        key = request.cookie["__wrapper_cookie"]
        for i in self.data.Data["keys"]:
            expiretime = 7884000  # Three weeks old
            if len(i) > 2:
                if not i[2]:
                    expiretime = 21600
            # Validate key and ensure it's under the expiretime
            if i[0] == key and time.time() - i[1] < expiretime:
                self.loginAttempts = 0
                return True
        return False

    def removeKey(self, key):
        for i, v in enumerate(self.data.Data["keys"]):
            if v[0] == key:
                del self.data.Data["keys"][i]

    # Dectorators and misc.
    def add_decorators(self):
        @self.app.before_request
        def handle():
            print("I'm a freakin' sandwich dude!")

        @self.app.route("/")
        def index():
            return render_template("dashboard.html")

        @self.app.route("/login", methods=["GET", "POST"])
        def login():
            badpass = False
            if request.method == "POST":
                password = request.form["password"]
                rememberme = "remember" in request.form

                if self.checkLogin(password):
                    key = self.makeKey(rememberme)
                    return redirect("/")
                    # self.log.warning("%s logged in to web mode (remember me: %s)", request.addr, rememberme)
                else:
                    badpass = True

            return render_template("login.html", badPass=badpass)

        @self.socketio.on('connect')
        def handle_connect():
            pass

        @self.socketio.on('disconnect')
        def handle_disconnect():
            pass

    def run(self):
        # Need a method to end this Thread!
        # the ending code needs a self.data.close() to close the storage object
        self.socketio.run(self.app, host=self.config["Web"]["web-bind"],
                          port=self.config["Web"]["web-port"])
Beispiel #24
0
    def getStorage(self, name, world=False, formatting="pickle"):
        """
        Returns a storage object manager.  The manager contains the
        storage object, 'Data' (a dictionary). 'Data' contains the
        data your plugin will remember across reboots.

        :NOTE: This method is somewhat different from previous Wrapper
         versions prior to 0.10.1 (build 182).  The storage object is
         no longer a data object itself; It is a manager used for
         controlling the saving of the object data.  The actual data
         is contained in Dictionary subitem 'Data'

        ___

        :Args:
            :name:  The name of the storage (on disk).
            :world:
                :False: set the storage's location to
                 '/wrapper-data/plugins'.
                :True: set the storage path to
                 '<serverpath>/<worldname>/plugins'.

            :formatting="pickle":  Pickle formatting is the default. pickling is
             less strict than json formats and leverages binary storage.
             Use of json (or future implemented formats) can result in
             errors if your keys or data do not conform to json standards
             (like use of string keys).  However, pickle is not generally
             human-readable, whereas json is human readable. If you need
             a human-readable copy (for debugging), consider using
             self.api.helpers.putjsonfile(<yourDictionary>) to write a
             copy to disk in Json.  if you do so, check the return status
             of `putjsonfile` to make sure it was written.

        ___

        :sample methods:

            The new method:

            .. code:: python

                # to start a storage:
                self.homes = self.api.getStorage("homes", True)

                # access the data:
                for player in self.homes.Data:  # note upper case `D`
                    print("player %s has a home at: %s" % (
                        player, self.homes.Data[player]))

                # to save (storages also do periodic saves every minute):
                self.homes.save()

                # to close (and save):
                def onDisable(self):
                    self.homes.close()
            ..

            the key difference is here (under the old Storage API):

            .. code:: python

                # This used to work under the former API
                # however, this will produce an exception
                # because "self.homes" is no longer an
                # iterable data set:
                for player in self.homes:  <= Exception!
                    print("player %s has a home at: %s" % (
                        player, self.homes[player]))
            ..

            **tip**
            *to make the transition easier for existing code, redefine
            your the storage statements above like this to re-write as
            few lines as possible (and avoid problems with other
            plugins that might link to your plugin's data)*:

            .. code:: python

                # change your storage setup from:
                self.homes = self.api.getStorage("homes", True)

                # to:
                self.homestorage = self.api.getStorage("homes", True)
                self.homes = homestorage.Data

                # Now the only other change you need to make is to any
                # .save() or .close() statements:
                def onDisable(self):
                    # self.homes.close()  # change to -
                    self.homestorage.close()
            ..

        """
        pickle = False
        if formatting == "pickle":
            pickle = True
        if world:
            return Storage(
                name,
                root="%s/%s/plugins/%s" %
                (self.serverpath, self.minecraft.getWorldName(), self.id),
                pickle=pickle)
        else:
            return Storage(name,
                           root="wrapper-data/plugins/%s" % self.id,
                           pickle=pickle)
Beispiel #25
0
class Wrapper(object):
    def __init__(self, secret_passphrase):
        # setup log and config
        # needs a false setting on first in case config does not
        # load (like after changes).
        self.storage = False
        self.log = logging.getLogger('Wrapper.py')
        self.configManager = Config()
        self.configManager.loadconfig()
        self.config = self.configManager.config

        # Read Config items
        # hard coded cursor for non-readline mode
        self.cursor = ">"
        # This was to allow alternate encodings
        self.encoding = self.config["General"]["encoding"]
        self.serverpath = self.config["General"]["server-directory"]
        self.proxymode = self.config["Proxy"]["proxy-enabled"]
        self.wrapper_onlinemode = self.config["Proxy"]["online-mode"]
        self.halt_message = self.config["Misc"]["halt-message"]

        # encryption items (for passwords and sensitive user data)
        # salt is generated and stored in wrapper.properties.json
        config_changes = False
        salt = self.config["General"]["salt"]
        if not salt:
            salt = gensalt(self.encoding)
            self.config["General"]["salt"] = salt
            config_changes = True
        # passphrase is provided at startup by the wrapper operator or script (not stored)
        passphrase = phrase_to_url_safebytes(secret_passphrase, self.encoding,
                                             salt)
        self.cipher = Crypt(passphrase, self.encoding)

        # Update passwords (hash any plaintext passwords)
        for groups in self.config:
            for cfg_items in self.config[groups]:
                if cfg_items[-10:] == "-plaintext":
                    # i.e., cfg_items ===> like ["web-password-plaintext"]
                    hash_item = cfg_items[:-10]
                    # hash_item ===> i.e., ["web-password"]
                    if hash_item in self.config[groups] and self.config[
                            groups][cfg_items]:
                        # encrypt contents of (i.e.) ["web-password-plaintext"]
                        hashed_item = self.cipher.encrypt(
                            self.config[groups][cfg_items])
                        # store in "" ["Web"]["web-password"]
                        self.config[groups][hash_item] = hashed_item
                        # set plaintext item to false (successful digest)
                        self.config[groups][cfg_items] = False
                        config_changes = True

        # Patch any old update paths "..wrapper/development/build/version.json"
        # new paths are: "..wrapper/development"
        for entries in self.config["Updates"]:
            if "/build/version.json" in str(self.config["Updates"][entries]):
                oldentry = copy.copy(self.config["Updates"][entries])
                self.config["Updates"][entries] = oldentry.split(
                    "/build/version.json")[0]
                config_changes = True

        # save changes made to config file
        if config_changes:
            self.configManager.save()

        # reload branch update info.
        self.auto_update_wrapper = self.config["Updates"][
            "auto-update-wrapper"]
        self.auto_update_branch = self.config["Updates"]["auto-update-branch"]
        if not self.auto_update_branch:
            self.update_url = "https://raw.githubusercontent.com/benbaptist/minecraft-wrapper/development"
        else:
            self.update_url = self.config["Updates"][self.auto_update_branch]

        self.use_timer_tick_event = self.config["Gameplay"][
            "use-timer-tick-event"]
        self.use_readline = not (self.config["Misc"]["use-betterconsole"])

        # Storages
        self.wrapper_storage = Storage("wrapper", encoding=self.encoding)
        self.wrapper_permissions = Storage("permissions",
                                           encoding=self.encoding,
                                           pickle=False)
        self.wrapper_usercache = Storage("usercache",
                                         encoding=self.encoding,
                                         pickle=False)

        # storage Data objects
        self.storage = self.wrapper_storage.Data
        self.usercache = self.wrapper_usercache.Data
        # self.wrapper_permissions accessed only by permissions module

        # core functions and datasets
        self.perms = Permissions(self)
        self.uuids = UUIDS(self.log, self.usercache)
        self.plugins = Plugins(self)
        self.commands = Commands(self)
        self.events = Events(self)
        self.players = {}
        self.registered_permissions = {}
        self.help = {}
        self.input_buff = ""
        self.sig_int = False
        self.command_hist = ['/help', 'help']
        self.command_index = 1

        # init items that are set up later (or opted out of/ not set up.)
        self.javaserver = None
        self.api = None
        self.irc = None
        self.scripts = None
        self.web = None
        self.proxy = None
        self.backups = None

        #  HaltSig - Why? ... because if self.halt was just `False`, passing
        #  a self.halt would simply be passing `False` (immutable).  Changing
        # the value of self.halt would not necessarily change the value of the
        # passed parameter (unless it was specifically referenced back as
        # `wrapper.halt`). Since the halt signal needs to be passed, possibly
        # several layers deep, and into modules that it may be desireable to
        # not have direct access to wrapper, using a HaltSig object is
        # more desireable and reliable in behavior.
        self.halt = HaltSig()

        self.updated = False
        # future plan to expose this to api
        self.xplayer = ConsolePlayer(self)

        # Error messages for non-standard import failures.
        if not readline and self.use_readline:
            self.log.warning(
                "'readline' not imported.  This is needed for proper"
                " console functioning. Press <Enter> to acknowledge...")
            sys.stdin.readline()

        # requests is just being used in too many places to try
        # and track its usages piece-meal.
        if not requests:
            self.log.error(
                "You must have the requests module installed to use wrapper!"
                " console functioning. Press <Enter> to Exit...")
            sys.stdin.readline()
            self._halt()

        # create server/proxy vitals and config objects
        self.servervitals = ServerVitals(self.players)

        # LETS TAKE A SECOND TO DISCUSS PLAYER OBJECTS:
        # The ServerVitals class gets passed the player object list now, but
        # player objects are now housed in wrapper.  This is how we are
        # passing information between proxy and wrapper.

        self.servervitals.serverpath = self.config["General"][
            "server-directory"]
        self.servervitals.state = OFF
        self.servervitals.command_prefix = self.config["Misc"][
            "command-prefix"]

        self.proxyconfig = ProxyConfig()
        self.proxyconfig.proxy = self.config["Proxy"]
        self.proxyconfig.entity = self.config["Entities"]

    def __del__(self):
        """prevent error message on very first wrapper starts when
        wrapper exits after creating new wrapper.properties file.
        """
        if self.storage:
            self.wrapper_storage.close()
            self.wrapper_permissions.close()
            self.wrapper_usercache.close()

    def start(self):
        """wrapper execution starts here"""

        self.signals()

        self.backups = Backups(self)

        self.api = API(self, "Wrapper.py")
        self._registerwrappershelp()

        # This is not the actual server... the MCServer
        # class is a console wherein the server is started
        self.javaserver = MCServer(self, self.servervitals)
        self.javaserver.init()

        # load plugins
        self.plugins.loadplugins()

        if self.config["IRC"]["irc-enabled"]:  # this should be a plugin
            self.irc = IRC(self.javaserver, self.log, self)
            t = threading.Thread(target=self.irc.init, args=())
            t.daemon = True
            t.start()

        if self.config["Web"]["web-enabled"]:  # this should be a plugin
            if manageweb.pkg_resources and manageweb.requests:
                self.log.warning(
                    "Our apologies!  Web mode is currently broken.  Wrapper"
                    " will start web mode anyway, but it will not likely "
                    "function well (or at all).  For now, you should turn "
                    "off web mode in wrapper.properties.json.")
                self.web = manageweb.Web(self)
                t = threading.Thread(target=self.web.wrap, args=())
                t.daemon = True
                t.start()
            else:
                self.log.error(
                    "Web remote could not be started because you do not have"
                    " the required modules installed: pkg_resources\n"
                    "Hint: http://stackoverflow.com/questions/7446187")

        # Console Daemon runs while not wrapper.halt.halt
        consoledaemon = threading.Thread(target=self.parseconsoleinput,
                                         args=())
        consoledaemon.daemon = True
        consoledaemon.start()

        # Timer runs while not wrapper.halt.halt
        ts = threading.Thread(target=self.event_timer_second, args=())
        ts.daemon = True
        ts.start()

        if self.use_timer_tick_event:
            # Timer runs while not wrapper.halt.halt
            tt = threading.Thread(target=self.event_timer_tick, args=())
            tt.daemon = True
            tt.start()

        if self.config["General"]["shell-scripts"]:
            if os.name in ("posix", "mac"):
                self.scripts = Scripts(self)
            else:
                self.log.error(
                    "Sorry, but shell scripts only work on *NIX-based systems!"
                    " If you are using a  *NIX-based system, please file a "
                    "bug report.")

        if self.proxymode:
            t = threading.Thread(target=self._startproxy, args=())
            t.daemon = True
            t.start()

        if self.auto_update_wrapper:
            t = threading.Thread(target=self._auto_update_process, args=())
            t.daemon = True
            t.start()

        self.javaserver.handle_server()
        # handle_server always runs, even if the actual server is not started

        self.plugins.disableplugins()
        self.log.info("Plugins disabled")
        self.wrapper_storage.close()
        self.wrapper_permissions.close()
        self.wrapper_usercache.close()
        self.log.info("Wrapper Storages closed and saved.")

        # wrapper execution ends here.  handle_server ends when
        # wrapper.halt.halt is True.
        if self.sig_int:
            self.log.info("Ending threads, please wait...")
            time.sleep(5)
            os.system("reset")

    def signals(self):
        signal.signal(signal.SIGINT, self.sigint)
        signal.signal(signal.SIGTERM, self.sigterm)
        # noinspection PyBroadException
        try:
            # lacking in Windows
            signal.signal(signal.SIGTSTP, self.sigtstp)
        except:
            pass

    def sigint(*args):
        self = args[0]  # We are only interested in the self component
        self.log.info("Wrapper.py received SIGINT; halting...\n")
        self.sig_int = True
        self._halt()

    def sigterm(*args):
        self = args[0]  # We are only interested in the self component
        self.log.info("Wrapper.py received SIGTERM; halting...\n")
        self._halt()

    def sigtstp(*args):
        # We are only interested in the 'self' component
        self = args[0]
        self.log.info("Wrapper.py received SIGTSTP; NO sleep support!"
                      " Wrapper halting...\n")
        os.system("kill -CONT %d" % self.javaserver.proc.pid)
        self._halt()

    def _halt(self):
        self.javaserver.stop(self.halt_message, restart_the_server=False)
        self.halt.halt = True

    def shutdown(self):
        self._halt()

    def write_stdout(self, message="", source="print"):
        """
        :param message: desired output line.  Default is wrapper.
        :param source: "server", "wrapper", "print" or "log".  Default is
         print.

        """
        cursor = self.cursor

        if self.use_readline:
            print(message)
            return

        def _wrapper(msg):
            """_wrapper is normally displaying a live typing buffer.
            Therefore, there is no cr/lf at end because it is 
            constantly being re-printed in the same spot as the
            user types."""
            if msg != "":
                # re-print what the console user was typing right below that.
                # /wrapper commands receive special magenta coloring
                if msg[0:1] == '/':
                    print("{0}{1}{2}{3}{4}{5}".format(UP_LINE, cursor,
                                                      FG_YELLOW, msg, RESET,
                                                      CLEAR_EOL))
                else:
                    print("{0}{1}{2}{3}".format(BACKSPACE, cursor, msg,
                                                CLEAR_EOL))

        def _server(msg):
            # print server lines
            print("{0}{1}{2}\r\n".format(UP_LINE, CLEAR_LINE, msg, CLEAR_EOL))

        def _print(msg):
            _server(msg)

        parse = {
            "server": _server,
            "wrapper": _wrapper,
            "print": _print,
        }

        # if this fails due to key error, we WANT that raised, as it is
        #  a program code error, not a run-time error.
        parse[source](message)

    def getconsoleinput(self):
        """If wrapper is NOT using readline (self.use_readline == False),
        then getconsoleinput manually implements our own character 
        reading, parsing, arrow keys, command history, etc.  This 
        is desireable because it allows the user to continue to see
        their input and modify it, even if the server is producing
        console line messages that would normally "carry away" the 
        user's typing.

        Implemented in response to issue 326:
        'Command being typed gets carried off by console every time
         server generates output #326' by @Darkness3840:
        https://github.com/benbaptist/minecraft-wrapper/issues/326
        """
        if self.use_readline:
            # Obtain a line of console input
            try:
                consoleinput = sys.stdin.readline().strip()
            except Exception as e:
                self.log.error(
                    "[continue] variable 'consoleinput' in 'console()' did"
                    " not evaluate \n%s" % e)
                consoleinput = ""

        else:
            arrow_index = 0
            # working buffer allows arrow use to restore what they
            # were typing but did not enter as a command yet
            working_buff = ''
            while not self.halt.halt:
                keypress = readkey.getcharacter()
                keycode = readkey.convertchar(keypress)
                length = len(self.input_buff)

                if keycode == "right":
                    arrow_index += 1
                    if arrow_index > length:
                        arrow_index = length

                if keycode == "left":
                    arrow_index -= 1
                    if arrow_index < 1:
                        arrow_index = 0

                if keycode == "up":
                    # goes 'back' in command history time
                    self.command_index -= 1
                    if self.command_index < 1:
                        self.command_index = 0
                    self.input_buff = self.command_hist[self.command_index]
                    arrow_index = len(self.input_buff)

                if keycode == "down":
                    # goes forward in command history time
                    self.command_index += 1

                    if self.command_index + 1 > len(self.command_hist):
                        # These actions happen when at most recent typing
                        self.command_index = len(self.command_hist)
                        self.input_buff = '%s' % working_buff
                        self.write_stdout("%s " % self.input_buff,
                                          source="wrapper")
                        arrow_index = len(self.input_buff)
                        continue

                    self.input_buff = self.command_hist[self.command_index]
                    arrow_index = len(self.input_buff)

                buff_left = "%s" % self.input_buff[:arrow_index]
                buff_right = "%s" % self.input_buff[arrow_index:]

                if keycode == "backspace":
                    if len(buff_left) > 0:
                        buff_left = buff_left[:-1]
                        self.input_buff = "%s%s" % (buff_left, buff_right)
                        working_buff = "%s" % self.input_buff
                        arrow_index -= 1

                if keycode == "delete":
                    if len(buff_right) > 0:
                        buff_right = buff_right[1:]
                    self.input_buff = "%s%s" % (buff_left, buff_right)
                    working_buff = "%s" % self.input_buff

                if keycode in ("enter", "cr", "lf"):
                    # scroll up (because cr is not added to buffer)
                    # print("")
                    break

                if keycode in ("ctrl-c", "ctrl-x"):
                    self.sigterm()
                    break

                # hide special key codes like PAGE_UP, etc if not used
                if not keycode:
                    buff_left = "%s%s" % (buff_left, keypress)
                    self.input_buff = "%s%s" % (buff_left, buff_right)
                    working_buff = "%s" % self.input_buff
                    arrow_index += 1

                # with open('readout.txt', "w") as f:
                #     f.write("left: '%s'\nright: '%s'\nbuff: '%s'" % (
                #         buff_left, buff_right, self.input_buff))

                if len(buff_right) > 0:
                    self.write_stdout(
                        "{0}{1}{2}{3}".format(REVERSED, buff_left, RESET,
                                              buff_right), "wrapper")
                else:
                    self.write_stdout("%s " % self.input_buff,
                                      source="wrapper")

            consoleinput = "%s" % self.input_buff
            self.input_buff = ""

            if consoleinput in self.command_hist:
                # if the command is already in the history somewhere,
                # remove it and re-append to the end (most recent)
                self.command_hist.remove(consoleinput)
                self.command_hist.append(consoleinput)
            else:
                # or just add it.
                self.command_hist.append(consoleinput)
            self.command_index = len(self.command_hist)

            # print the finished command to console
            self.write_stdout("%s\r\n" % self.input_buff, source="wrapper")

        return consoleinput

    def parseconsoleinput(self):
        while not self.halt.halt:
            consoleinput = self.getconsoleinput()
            # No command (perhaps just a line feed or spaces?)
            if len(consoleinput) < 1:
                continue

            # for use with runwrapperconsolecommand() command
            wholecommandline = consoleinput[0:].split(" ")
            command = str(getargs(wholecommandline, 0)).lower()

            # this can be passed to runwrapperconsolecommand() command for args
            allargs = wholecommandline[1:]

            # Console only commands (not accessible in-game)
            if command in ("/halt", "halt"):
                self._halt()
            elif command in ("/stop", "stop"):
                self.javaserver.stop_server_command()
            # "kill" (with no slash) is a server command.
            elif command == "/kill":
                self.javaserver.kill("Server killed at Console...")
            elif command in ("/start", "start"):
                self.javaserver.start()
            elif command in ("/restart", "restart"):
                self.javaserver.restart()
            elif command in ("/update-wrapper", "update-wrapper"):
                self._checkforupdate(True)
            # "plugins" command (with no slash) reserved for server commands
            elif command == "/plugins":
                self.listplugins()
            elif command in ("/mem", "/memory", "mem", "memory"):
                self._memory()
            elif command in ("/raw", "raw"):
                self._raw(consoleinput)
            elif command in ("/freeze", "freeze"):
                self._freeze()
            elif command in ("/unfreeze", "unfreeze"):
                self._unfreeze()
            elif command == "/version":
                readout("/version",
                        self.getbuildstring(),
                        usereadline=self.use_readline)
            elif command in ("/mute", "/pause", "/cm", "/m", "/p"):
                self._mute_console(allargs)

            # Commands that share the commands.py in-game interface

            # "reload" (with no slash) may be used by bukkit servers
            elif command == "/reload":
                self.runwrapperconsolecommand("reload", [])

            # proxy mode ban system
            elif self.proxymode and command == "/ban":
                self.runwrapperconsolecommand("ban", allargs)

            elif self.proxymode and command == "/ban-ip":
                self.runwrapperconsolecommand("ban-ip", allargs)

            elif self.proxymode and command == "/pardon-ip":
                self.runwrapperconsolecommand("pardon-ip", allargs)

            elif self.proxymode and command == "/pardon":
                self.runwrapperconsolecommand("pardon", allargs)

            elif command in ("/perm", "/perms", "/super", "/permissions",
                             "perm", "perms", "super", "permissions"):
                self.runwrapperconsolecommand("perms", allargs)

            elif command in ("/playerstats", "/stats", "playerstats", "stats"):
                self.runwrapperconsolecommand("playerstats", allargs)

            elif command in ("/ent", "/entity", "/entities", "ent", "entity",
                             "entities"):
                self.runwrapperconsolecommand("ent", allargs)

            elif command in ("/config", "/con", "/prop", "/property",
                             "/properties"):
                self.runwrapperconsolecommand("config", allargs)

            elif command in ("op", "/op"):
                self.runwrapperconsolecommand("op", allargs)

            elif command in ("deop", "/deop"):
                self.runwrapperconsolecommand("deop", allargs)

            elif command in ("pass", "/pass", "pw", "/pw", "password",
                             "/password"):
                self.runwrapperconsolecommand("password", allargs)

            # TODO Add more commands below here, below the original items:
            # TODO __________________

            # more commands here...

            # TODO __________________
            # TODO add more commands above here, above the help-related items:

            # minecraft help command
            elif command == "help":
                readout("/help",
                        "Get wrapper.py help.",
                        separator=" (with a slash) - ",
                        usereadline=self.use_readline)
                self.javaserver.console(consoleinput)

            # wrapper's help (console version)
            elif command == "/help":
                self._show_help_console()

            # wrapper ban help
            elif command == "/bans":
                self._show_help_bans()

            # Commmand not recognized by wrapper
            else:
                try:
                    self.javaserver.console(consoleinput)
                except Exception as e:
                    self.log.error("[BREAK] Console input exception"
                                   " (nothing passed to server) \n%s" % e)
                    break
                continue

    def _registerwrappershelp(self):
        # All commands listed herein are accessible in-game
        # Also require player.isOp()
        new_usage = "<player> [-s SUPER-OP] [-o OFFLINE] [-l <level>]"
        self.api.registerHelp(
            "Wrapper",
            "Internal Wrapper.py commands ",
            [
                ("/wrapper [update/memory/halt]",
                 "If no subcommand is provided, it will"
                 " show the Wrapper version.", None),
                ("/playerstats [all]",
                 "Show the most active players. If no subcommand"
                 " is provided, it'll show the top 10 players.", None),
                ("/plugins", "Show a list of the installed plugins", None),
                ("/reload", "Reload all plugins.", None),
                ("/op %s" % new_usage, "This and deop are Wrapper commands.",
                 None),
                ("/permissions <groups/users/RESET>",
                 "Command used to manage permission groups and"
                 " users, add permission nodes, etc.", None),
                ("/entity <count/kill> [eid] [count]",
                 "/entity help/? for more help.. ", None),
                ("/config", "Change wrapper.properties (type"
                 " /config help for more..)", None),
                ("/password", "Sample usage: /pw IRC control-irc-pass <new"
                 "password>", None),

                # Minimum server version for commands to appear is
                # 1.7.6 (registers perm later in serverconnection.py)
                # These won't appear if proxy mode is not on (since
                # serverconnection is part of proxy).
                ("/ban <name> [reason..] [d:<days>/h:<hours>]",
                 "Ban a player. Specifying h:<hours> or d:<days>"
                 " creates a temp ban.", "mc1.7.6"),
                ("/ban-ip <ip> [<reason..> <d:<number of days>]",
                 "- Ban an IP address. Reason and days"
                 " (d:) are optional.", "mc1.7.6"),
                ("/pardon <player> [False]",
                 " - pardon a player. Default is byuuidonly."
                 "  To unban a specific "
                 "name (without checking uuid), use `pardon"
                 " <player> False`", "mc1.7.6"),
                ("/pardon-ip <address>", "Pardon an IP address.", "mc1.7.6"),
                ("/banlist [players|ips] [searchtext]",
                 "search and display the banlist (warning -"
                 " displays on single page!)", "mc1.7.6")
            ])

    def runwrapperconsolecommand(self, wrappercommand, argslist):
        xpayload = {
            'player': self.xplayer,
            'command': wrappercommand,
            'args': argslist
        }
        self.commands.playercommand(xpayload)

    def isonlinemode(self):
        """
        :returns: Whether the server OR (for proxy mode) wrapper
        is in online mode.  This should normally 'always' render
        True. Under rare circumstances it could be false, such
        as when this wrapper and its server are the target for
        a wrapper lobby with player.connect().
        """
        if self.proxymode:
            # if wrapper is using proxy mode (which should be set to online)
            return self.config["Proxy"]["online-mode"]
        if self.javaserver is not None:
            if self.servervitals.onlineMode:
                # if local server is online-mode
                return True
        return False

    def listplugins(self):
        readout("",
                "List of Wrapper.py plugins installed:",
                separator="",
                pad=4,
                usereadline=self.use_readline)
        for plid in self.plugins:
            plugin = self.plugins[plid]
            if plugin["good"]:
                name = plugin["name"]
                summary = plugin["summary"]
                if summary is None:
                    summary = "No description available for this plugin"

                version = plugin["version"]
                readout(name,
                        summary,
                        separator=(" - v%s - " %
                                   ".".join([str(_) for _ in version])),
                        usereadline=self.use_readline)
            else:
                readout("failed to load plugin",
                        plugin,
                        pad=25,
                        usereadline=self.use_readline)

    def _start_emailer(self):
        alerts = self.config["Alerts"]["enabled"]
        if alerts:
            self.config["Alerts"] = "alerts true"

    def _startproxy(self):

        # error will raise if requests or cryptography is missing.
        self.proxy = Proxy(self.halt, self.proxyconfig, self.servervitals,
                           self.log, self.usercache, self.events)

        # wait for server to start
        timer = 0
        while self.servervitals.state < STARTED:
            timer += 1
            time.sleep(.1)
            if timer > 1200:
                self.log.warning(
                    "Proxy mode did not detect a started server within 2"
                    " minutes.  Disabling proxy mode because something is"
                    " wrong.")
                self.disable_proxymode()
                return

        if self.proxy.proxy_port == self.servervitals.server_port:
            self.log.warning("Proxy mode cannot start because the wrapper"
                             " port is identical to the server port.")
            self.disable_proxymode()
            return

        proxythread = threading.Thread(target=self.proxy.host, args=())
        proxythread.daemon = True
        proxythread.start()

    def disable_proxymode(self):
        self.proxymode = False
        self.config["Proxy"]["proxy-enabled"] = False
        self.configManager.save()
        self.log.warning(
            "\nProxy mode is now turned off in wrapper.properties.json.\n")

    @staticmethod
    def getbuildstring():
        if core_buildinfo_version.__branch__ == "dev":
            return "%s (development build #%d)" % (
                core_buildinfo_version.__version__,
                core_buildinfo_version.__build__)

        elif core_buildinfo_version.__branch__ == "stable":
            return "%s (stable)" % core_buildinfo_version.__version__
        else:
            return "Version: %s (%s build #%d)" % (
                core_buildinfo_version.__version__,
                core_buildinfo_version.__branch__,
                core_buildinfo_version.__build__)

    def _auto_update_process(self):
        while not self.halt.halt:
            time.sleep(3600)
            if self.updated:
                self.log.info("An update for wrapper has been loaded,"
                              " Please restart wrapper.")
            else:
                self._checkforupdate()

    def _checkforupdate(self, update_now=False):
        """ checks for update """
        self.log.info("Checking for new builds...")
        update = self.get_wrapper_update_info()
        if update:
            version, build, repotype, reponame = update
            self.log.info(
                "New Wrapper.py %s build #%d is available!"
                " (current build is #%d)", repotype, build,
                core_buildinfo_version.__build__)

            if self.auto_update_wrapper or update_now:
                self.log.info("Updating...")
                self.performupdate(version, build)
            else:
                self.log.info(
                    "Because you have 'auto-update-wrapper' set to False,"
                    " you must manually update Wrapper.py. To update"
                    " Wrapper.py manually, please type /update-wrapper.")
        else:
            self.log.info("No new versions available.")

    def get_wrapper_update_info(self, repotype=None):
        """get the applicable branch wrapper update"""
        # read the installed branch info
        if repotype is None:
            repotype = core_buildinfo_version.__branch__
        if self.auto_update_branch:
            branch_key = self.auto_update_branch
        else:
            branch_key = "%s-branch" % repotype
        r = requests.get("%s/build/version.json" %
                         self.config["Updates"][branch_key])
        if r.status_code == 200:
            data = r.json()
            if data["__build__"] > core_buildinfo_version.__build__:
                if repotype == "dev":
                    reponame = "development"
                elif repotype == "stable":
                    reponame = "master"
                else:
                    reponame = data["__branch__"]
                if "__version__" not in data:
                    data["__version__"] = data["version"]
                return data["__version__"], data["__build__"], data[
                    "__branch__"], reponame

        else:
            self.log.warning(
                "Failed to check for updates - are you connected to the"
                " internet? (Status Code %d)", r.status_code)
            return False

    def performupdate(self, version, build):
        """
        Perform update; returns True if update succeeds.  User must
        still restart wrapper manually.

        :param version: first argument from get_wrapper_update_info()
        :param build: second argument from get_wrapper_update_info()
        :return: True if update succeeds
        """

        wraphash = requests.get("%s/build/Wrapper.py.md5" % self.update_url)
        wrapperfile = requests.get("%s/Wrapper.py" % self.update_url)

        if wraphash.status_code == 200 and wrapperfile.status_code == 200:
            self.log.info("Verifying Wrapper.py...")
            if hashlib.md5(wrapperfile.content).hexdigest() == wraphash.text:
                self.log.info(
                    "Update file successfully verified. Installing...")
                with open(sys.argv[0], "wb") as f:
                    f.write(wrapperfile.content)
                self.log.info(
                    "Wrapper.py %s (#%d) installed. Please reboot Wrapper.py.",
                    ".".join([str(_) for _ in version]), build)
                self.updated = True
                return True
            else:
                return False
        else:
            self.log.error(
                "Failed to update due to an internal error (%d, %d)",
                wraphash.status_code,
                wrapperfile.status_code,
                exc_info=True)
            return False

    def event_timer_second(self):
        while not self.halt.halt:
            time.sleep(1)
            self.events.callevent("timer.second", None)
            """ eventdoc
                <group> wrapper <group>

                <description> a timer that is called each second.
                <description>

                <abortable> No <abortable>

            """

    def event_timer_tick(self):
        while not self.halt.halt:
            self.events.callevent("timer.tick", None)
            time.sleep(0.05)
            """ eventdoc
                <group> wrapper <group>

                <description> a timer that is called each 1/20th
                <sp> of a second, like a minecraft tick.
                <description>

                <abortable> No <abortable>

                <comments>
                Use of this timer is not suggested and is turned off
                <sp> by default in the wrapper.config.json file
                <comments>

            """

    def _pause_console(self, pause_time):
        if not self.javaserver:
            readout("ERROR - ",
                    "There is no running server instance to mute.",
                    separator="",
                    pad=10,
                    usereadline=self.use_readline)
            return
        self.javaserver.server_muted = True
        readout("Server is now muted for %d seconds." % pause_time,
                "",
                separator="",
                command_text_fg="yellow",
                usereadline=self.use_readline)
        time.sleep(pause_time)
        readout("Server now unmuted.",
                "",
                separator="",
                usereadline=self.use_readline)
        self.javaserver.server_muted = False
        for lines in self.javaserver.queued_lines:
            readout("Q\\", "", lines, pad=3, usereadline=self.use_readline)
            time.sleep(.1)
        self.javaserver.queued_lines = []

    def _mute_console(self, all_args):
        pausetime = 30
        if len(all_args) > 0:
            pausetime = get_int(all_args[0])
        # spur off a pause thread
        cm = threading.Thread(target=self._pause_console, args=(pausetime, ))
        cm.daemon = True
        cm.start()

    def _freeze(self):
        try:
            self.javaserver.freeze()
        except OSError as ex:
            self.log.error(ex)
        except EnvironmentError as e:
            self.log.warning(e)
        except Exception as exc:
            self.log.exception(
                "Something went wrong when trying to freeze the"
                " server! (%s)", exc)

    def _memory(self):
        try:
            get_bytes = self.javaserver.getmemoryusage()
        except OSError as e:
            self.log.error(e)
        except Exception as ex:
            self.log.exception(
                "Something went wrong when trying to fetch"
                " memory usage! (%s)", ex)
        else:
            amount, units = format_bytes(get_bytes)
            self.log.info("Server Memory Usage: %s %s (%s bytes)" %
                          (amount, units, get_bytes))

    def _raw(self, console_input):
        try:
            if len(getargsafter(console_input[1:].split(" "), 1)) > 0:
                self.javaserver.console(
                    getargsafter(console_input[1:].split(" "), 1))
            else:
                self.log.info("Usage: /raw [command]")
        except EnvironmentError as e:
            self.log.warning(e)

    def _unfreeze(self):
        try:
            self.javaserver.unfreeze()
        except OSError as ex:
            self.log.error(ex)
        except EnvironmentError as e:
            self.log.warning(e)
        except Exception as exc:
            self.log.exception(
                "Something went wrong when trying to unfreeze"
                " the server! (%s)", exc)

    def _show_help_console(self):
        # This is the console help command display.
        readout("",
                "Get Minecraft help.",
                separator="help (no slash) - ",
                pad=0,
                usereadline=self.use_readline)
        readout("/reload",
                "Reload Wrapper.py plugins.",
                usereadline=self.use_readline)
        readout("/plugins",
                "Lists Wrapper.py plugins.",
                usereadline=self.use_readline)
        readout("/update-wrapper",
                "Checks for new Wrapper.py updates, and will install\n"
                "them automatically if one is available.",
                usereadline=self.use_readline)
        readout("/stop", "Stop the minecraft server without"
                " auto-restarting and without\n"
                "                  shuttingdown Wrapper.py.",
                usereadline=self.use_readline)
        readout("/start",
                "Start the minecraft server.",
                usereadline=self.use_readline)
        readout("/restart",
                "Restarts the minecraft server.",
                usereadline=self.use_readline)
        readout("/halt",
                "Shutdown Wrapper.py completely.",
                usereadline=self.use_readline)
        readout("/cm [seconds]", "Mute server output (Wrapper console"
                " logging still happens)",
                usereadline=self.use_readline)
        readout("/kill",
                "Force kill the server without saving.",
                usereadline=self.use_readline)
        readout("/freeze", "Temporarily locks the server up"
                " until /unfreeze is executed\n"
                "                  (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/unfreeze", "Unlocks a frozen state server"
                " (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/mem", "Get memory usage of the server"
                " (Only works on *NIX servers).",
                usereadline=self.use_readline)
        readout("/raw [command]", "Send command to the Minecraft"
                " Server. Useful for Forge\n"
                "                  commands like '/fml confirm'.",
                usereadline=self.use_readline)
        readout("/password",
                "run `/password help` for more...)",
                usereadline=self.use_readline)
        readout("/perms", "/perms for more...)", usereadline=self.use_readline)
        readout("/config", "Change wrapper.properties (type"
                " /config help for more..)",
                usereadline=self.use_readline)
        readout("/version",
                self.getbuildstring(),
                usereadline=self.use_readline)
        readout("/entity",
                "Work with entities (run /entity for more...)",
                usereadline=self.use_readline)
        readout("/bans",
                "Display the ban help page.",
                usereadline=self.use_readline)

    def _show_help_bans(self):
        # ban commands help.
        if not self.proxymode:
            readout("ERROR - ", "Wrapper proxy-mode bans are not enabled "
                    "(proxy mode is not on).",
                    separator="",
                    pad=10,
                    usereadline=self.use_readline)
            return

        readout("",
                "Bans - To use the server's versions, do not type a slash.",
                separator="",
                pad=5,
                usereadline=self.use_readline)
        readout("",
                "",
                separator="-----1.7.6 and later ban commands-----",
                pad=10,
                usereadline=self.use_readline)
        readout("/ban", " - Ban a player. Specifying h:<hours> or d:<days>"
                " creates a temp ban.",
                separator="<name> [reason..] [d:<days>/h:<hours>] ",
                pad=12,
                usereadline=self.use_readline)
        readout("/ban-ip",
                " - Ban an IP address. Reason and days (d:) are optional.",
                separator="<ip> [<reason..> <d:<number of days>] ",
                pad=12,
                usereadline=self.use_readline)
        readout("/pardon",
                " - pardon a player. Default is byuuidonly.  To unban a"
                "specific name (without checking uuid), use"
                " `pardon <player> False`",
                separator="<player> [byuuidonly(true/false)]",
                pad=12,
                usereadline=self.use_readline)
        readout("/pardon-ip",
                " - Pardon an IP address.",
                separator="<address> ",
                pad=12,
                usereadline=self.use_readline)
        readout("/banlist", " - search and display the banlist (warning -"
                " displays on single page!)",
                separator="[players|ips] [searchtext] ",
                pad=12,
                usereadline=self.use_readline)
Beispiel #26
0
 def __init__(self, molecules_file, board_size):
     self.storage = Storage.load_molecules(molecules_file)
     atoms = self.storage.get_atoms()
     self.board = Board([], 0)
     self.board.generate(board_size, atoms)