Beispiel #1
0
 def __init__(self, blocks_path, sx, sy, sz):
     Thread.__init__(self)
     self.x, self.y, self.z = sx, sy, sz
     self.blocks_path = blocks_path
     self.world_name = os.path.basename(os.path.dirname(blocks_path))
     self.in_queue = Queue()
     self.out_queue = Queue()
     self.logger = ColouredLogger()
Beispiel #2
0
 def connectionMade(self):
     self.logger = ColouredLogger(debug)
     self.ops = []
     self.nickname = self.factory.main_factory.irc_nick
     self.password = self.factory.main_factory.irc_pass
     irc.IRCClient.connectionMade(self)
     self.factory.instance = self
     self.factory, self.controller_factory = self.factory.main_factory, self.factory
     self.world = None
     self.sendLine('NAMES ' + self.factory.irc_channel)
Beispiel #3
0
 def __init__(self, blockstore):
     Thread.__init__(self)
     self.blockstore = blockstore
     #self.setDaemon(True) # means that python can terminate even if still active
     #self.work = Event()    # TODO use event to start and stop
     self.last_lag = 0
     self.running = True
     self.was_physics = False
     self.was_unflooding = False
     self.changed = set()
     self.working = set() # could be a list or a sorted list but why bother (world updates may appear in random order but most of the time so many get updated it should be unnoticable)
     self.sponge_locations = set()
     self.logger = ColouredLogger(debug)
Beispiel #4
0
class ControllerProtocol(LineReceiver):
    """
    Protocol for dealing with controller requests.
    """

    def connectionMade(self):
        self.logger = ColouredLogger(debug)
        peer = self.transport.getPeer()
        self.logger.debug("Control connection made from %s:%s" % (peer.host, peer.port))
        self.factory, self.controller_factory = self.factory.main_factory, self.factory

    def connectionLost(self, reason):
        peer = self.transport.getPeer()
        self.logger.debug("Control connection lost from %s:%s" % (peer.host, peer.port))

    def sendJson(self, data):
        self.sendLine(simplejson.dumps(data))

    def lineReceived(self, line):
        data = simplejson.loads(line)
        peer = self.transport.getPeer()
        if data['password'] != self.factory.controller_password:
            self.sendJson({"error": "invalid password"})
            self.logger.info("Control: Invalid password %s (%s:%s)" % (data, peer.host, peer.port))
        else:
            command = data['command'].lower()
            try:
                func = getattr(self, "command%s" % command.title())
            except AttributeError:
                self.sendJson({"error": "unknown command %s" % command})
            else:
                self.logger.debug("Control: %s %s (%s:%s)" % (command.upper(), data, peer.host, peer.port))
                try:
                    func(data)
                except Exception, e:
                    self.sendJson({"error": "%s" % e})
                    traceback.print_exc()
Beispiel #5
0
 def connectionMade(self):
     self.logger = ColouredLogger(debug)
     peer = self.transport.getPeer()
     self.logger.debug("Control connection made from %s:%s" % (peer.host, peer.port))
     self.factory, self.controller_factory = self.factory.main_factory, self.factory
Beispiel #6
0
class ChatBot(irc.IRCClient):
    """An IRC-server chat integration bot."""

    ocommands = ["help", "cmdlist", "banreason", "banned", "kick", "ban", "shutdown", "rank", "derank", "spec", "boot"]
    ncommands = ["who", "worlds", "staff", "credits", "help", "rules", "cmdlist", "about", "lastseen", "roll"]

    def connectionMade(self):
        self.logger = ColouredLogger(debug)
        self.ops = []
        self.nickname = self.factory.main_factory.irc_nick
        self.password = self.factory.main_factory.irc_pass
        irc.IRCClient.connectionMade(self)
        self.factory.instance = self
        self.factory, self.controller_factory = self.factory.main_factory, self.factory
        self.world = None
        self.sendLine('NAMES ' + self.factory.irc_channel)

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)
        self.logger.info("IRC client disconnected. (%s)" % reason)

    # callbacks for events

    def ctcpQuery_VERSION(self, user, channel, data):
        """Called when received a CTCP VERSION request."""
        nick = user.split("!")[0]
        self.ctcpMakeReply(nick, [('VERSION', 'The Archives %s - a Minecraft server written in Python.' % VERSION)])

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.logger.info("IRC client connected.")
        self.msg("NickServ", "IDENTIFY %s %s" % (self.nickname, self.password))
        self.msg("ChanServ", "INVITE %s" % self.factory.irc_channel)
        self.join(self.factory.irc_channel)

    def joined(self, channel):
        """This will get called when the bot joins the channel."""
        self.logger.info("IRC client joined %s." % channel)

    def sendError(self, error):
        self.logger.error("Sending error: %s" % error)
        self.sendPacked(TYPE_ERROR, error)
        reactor.callLater(0.2, self.transport.loseConnection)

    def lineReceived(self, line): # use instead of query
        line = irc.lowDequote(line)
        try:
            prefix, command, params = irc.parsemsg(line)
            if irc.numeric_to_symbolic.has_key(command):
                command = irc.numeric_to_symbolic[command]
            self.handleCommand(command, prefix, params)
        except irc.IRCBadMessage:
            self.badMessage(line, *sys.exc_info())
        try:
            if command == "RPL_NAMREPLY":
                names = params[3].split()
                for name in names:
                    if name.startswith("@"):
                        self.ops.append(name[1:])
        except:
            self.logger.error(traceback.format_exc())

    def AdminCommand(self, command):
        try:
            user = command[0]
            if user in self.ops:
                if len(command) > 1:
                    if command[1].startswith("#"):
                        if self.factory.staffchat:
                            # It's an staff-only message.
                            if len(command[1]) < 1:
                                self.msg(user, "07Please include a message to send.")
                            else:
                                text = " ".join(command[1:])[1:]
                                self.factory.sendMessageToAll(text, "staff", user=(COLOUR_PURPLE + user))
                    elif command[1].lower() in self.ocommands and len(command) > 1:
                        theCommand = command[1].lower()
                        if theCommand == ("help"):
                            self.msg(user, "07Admin Help")
                            self.msg(user, "07Commands: Use 'cmdlist'")
                            if self.factory.staffchat:
                                self.msg(user, "07StaffChat: Use '#message'")
                        elif theCommand == ("cmdlist"):
                            self.msg(user, "07Here are your Admin Commands:")
                            self.msg(user, "07ban banned banreason boot derank kick rank shutdown spec")
                            self.msg(user, "07Use 'command arguments' to do it.")
                        elif theCommand == ("banreason"):
                            if len(command) == 3:
                                username = command[2]
                                if not self.factory.isBanned(username):
                                    self.msg(user, "07%s is not Banned." % username)
                                else:
                                    self.msg(user, "07Reason: %s" % self.factory.banReason(username))
                            else:
                                self.msg(user, "07You must provide a name.")
                        elif theCommand == ("banned"):
                            self.msg(user, ", ".join(self.factory.banned))
                        elif theCommand == ("kick"):
                            theUser = command[2]
                            if user.lower() in self.factory.usernames:
                                if len(command) > 2:
                                    self.factory.usernames[theUser.lower()].sendError(
                                        "You were kicked by %s: %s" % (user, " ".join(command[3:])))
                                else:
                                    self.factory.usernames[theUser.lower()].sendError("You were kicked by %s!" % user)
                                self.msg(user, "%s has been kicked from the server%s." % (
                                str(command[2], (" for %s" % " ".join(command[3:]) if len(command) > 2 else ""))))
                                return
                            self.msg(user, "%s is not online." % command[2])
                        elif theCommand == ("ban"):
                            if command > 3:
                                if self.factory.isBanned(command[2]):
                                    self.msg(user, "07%s is already Banned." % command[2])
                                else:
                                    self.factory.addBan(command[2], " ".join(command[3:]))
                                    if command[2] in self.factory.usernames:
                                        self.factory.usernames[command[2]].sendError("You got banned!")
                                    self.msg(user,
                                        "07%s has been Banned for %s." % (command[2], " ".join(command[3:])))
                            else:
                                self.msg(user, "07Please give a username and reason.")
                        elif theCommand == ("shutdown"):
                            world = str(command[2]).lower()
                            if world in self.factory.worlds:
                                self.factory.unloadWorld(world)
                                self.msg(user, "07World '" + world + "' shutdown.")
                            else:
                                self.msg(user, "07World '" + world + "' is not loaded.")
                        elif theCommand == ("rank"):
                            if not len(command) > 2:
                                self.msg(user, "07You must provide a username.")
                            else:
                                self.msg(user, Rank(self, command[1:] + [user], "irc", True, self.factory))
                        elif theCommand == ("derank"):
                            if not len(command) > 2:
                                self.msg(user, "07You must provide a username.")
                            else:
                                self.msg(user, DeRank(self, command[1:] + [user], "irc", True, self.factory))
                        elif theCommand == ("spec"):
                            if not len(command) > 2:
                                self.msg(user, "07You must provide a username.")
                            else:
                                self.msg(user, Spec(self, command[1], "irc", True, self.factory))
                        elif theCommand == "despec":
                            if not len(command) > 2:
                                self.msg(user, "07You must provide a username.")
                            else:
                                self.msg(user, DeSpec(self, command[1], "irc", True, self.factory))
                        elif theCommand == ("boot"):
                            world = str(command[2]).lower()
                            returned = self.factory.loadWorld("worlds/" + world, world)
                            if returned == False:
                                self.msg(user, "07 World %s loading failed." % world)
                            else:
                                self.msg(user, "07World %s booted." % world)
                        else:
                            self.msg(user, "07Sorry, %s is not a command!" % theCommand)
                    else:
                        self.msg(user, "07%s is not a command!" % command[1].lower())
                else:
                    self.msg(user, "07You must provide a valid command to use the IRC bot.")
            else:
                if command[1].startswith("#"):
                    if self.factory.staffchat:
                        self.msg(user, "07You must be an op to use StaffChat.")
                elif command[1].lower() in self.ocommands:
                    self.msg(user, "07You must be an op to use %s." % command[1])
                else:
                    self.msg(user, "07%s is not a command!" % command[1])
            if not command[1].startswith("#"):
                self.logger.info("%s just used: %s" % (user, " ".join(command[1:])))
        except:
            self.logger.error(traceback.format_exc())
            self.msg(user, "Internal Server Error (See the Console for more details)")

    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        try:
            user = user.split('!', 1)[0]
            msg = self.factory.messagestrip(msg)
            if channel == self.nickname:
                if not (self.nickname == user or "Serv" in user):
                    msg_command = msg.split()
                    self.AdminCommand([user] + msg_command)
            elif channel.lower() == self.factory.irc_channel.lower():
                if msg.lower().lstrip(self.nickname.lower()).startswith("$" + self.nickname.lower()):
                    msg_command = msg.split()
                    msg_command[1] = msg_command[1].lower()
                    if len(msg_command) > 1:
                        if msg_command[1] in self.ncommands and len(msg_command) > 1:
                            if msg_command[1] == "who":
                                self.msg(self.factory.irc_channel, "07Who's Online?")
                                none = True
                                for key in self.factory.worlds:
                                    users = ", ".join(str(c.username) for c in self.factory.worlds[key].clients)
                                    if users:
                                        whois = ("07%s: %s" % (key, users))
                                        self.msg(self.factory.irc_channel, whois)
                                        users = None
                                        none = False
                                if none:
                                    self.msg(self.factory.irc_channel, "07No users are online.")
                            elif msg_command[1] == "worlds":
                                self.msg(self.factory.irc_channel, "07Worlds Booted:")
                                worlds = ", ".join([id for id, world in self.factory.worlds.items()])
                                self.msg(self.factory.irc_channel, "07Online Worlds: " + worlds)
                            elif msg_command[1] == "staff":
                                self.msg(self.factory.irc_channel, "07Please see your PM for the Staff List.")
                                self.msg(user, "The Server Staff")
                                list = Staff(self, self.factory)
                                for each in list:
                                    self.msg(user, " ".join(each))
                            elif msg_command[1] == "credits":
                                self.msg(self.factory.irc_channel, "07Please see your PM for the Credits.")
                                self.msg(user, "The Credits")
                                list = Credits()
                                for each in list:
                                    self.msg(user, "".join(each))
                            elif msg_command[1] == "help":
                                self.msg(self.factory.irc_channel, "07Help Center")
                                self.msg(self.factory.irc_channel, "07Commands: Use '$" + self.nickname + " cmdlist'")
                                self.msg(self.factory.irc_channel, "07WorldChat: Use '!world message'")
                                self.msg(self.factory.irc_channel, "07IRCChat: Use '$message'")
                                self.msg(self.factory.irc_channel, "07About: Use '$" + self.nickname + " about'")
                                self.msg(self.factory.irc_channel, "07Credits: Use '$" + self.nickname + " credits'")
                            elif msg_command[1] == "rules":
                                self.msg(self.factory.irc_channel, "07Please see your PM for the Rules.")
                                self.msg(user, "The Rules")
                                try:
                                    r = open('config/rules.txt', 'r')
                                except IOError:
                                    self.msg(user, "07There are no rules for this server.")
                                    return
                                for line in r:
                                    line = line.replace("\n", "")
                                    self.msg(user, line)
                            elif msg_command[1] == "cmdlist":
                                self.msg(self.factory.irc_channel, "07Command List")
                                self.msg(self.factory.irc_channel,
                                    "07about cmdlist credits help rules staff who worlds")
                                self.msg(self.factory.irc_channel,
                                    "07Use '$" + self.nickname + " command arguments' to do it.")
                                self.msg(self.factory.irc_channel,
                                    "07NOTE: Admin Commands are by PMing " + self.nickname + " - only for ops.")
                            elif msg_command[1] == "about":
                                self.msg(self.factory.irc_channel,
                                    "07About the Server, powered by The Archives %s | Credits: Use '$%s credits'" % (
                                    VERSION, self.nickname))
                                self.msg(self.factory.irc_channel, "07Name: %s; Owners: %s" % (
                                self.factory.server_name, ", ".join(self.factory.owners)))
                                try:
                                    self.msg(self.factory.irc_channel, "07URL: " + self.factory.heartbeat.url)
                                except:
                                    self.msg(self.factory.irc_channel, "07URL: N/A (minecraft.net is offline)")
                                self.msg(self.factory.irc_channel, "07Site: " + self.factory.info_url)
                            elif msg_command[1] == "lastseen":
                                if len(msg_command) < 2:
                                    self.msg(self.factory.irc_channel, "07Please enter a username to look for.")
                                else:
                                    if msg_command[2].lower() not in self.factory.lastseen:
                                        self.msg(self.factory.irc_channel,
                                            "07There are no records of %s." % msg_command[2])
                                    else:
                                        t = time.time() - self.factory.lastseen[msg_command[2].lower()]
                                        days = t // 86400
                                        hours = (t % 86400) // 3600
                                        mins = (t % 3600) // 60
                                        desc = "%id, %ih, %im" % (days, hours, mins)
                                        self.msg(self.factory.irc_channel,
                                            "07%s was last seen %s ago." % (msg_command[2], desc))
                            else:
                                self.msg(self.factory.irc_channel, "07Sorry, %s is not a command!" % msg_command[1])
                            self.logger.info("%s just used: %s" % (user, " ".join(msg_command[1:])))
                        elif msg_command[1] in self.ocommands and len(msg_command) > 1:
                            if user in self.ops:
                                self.msg(self.factory.irc_channel,
                                    "07Please do not use %s in the channel; use a query instead!" % msg_command[1])
                            else:
                                self.msg(self.factory.irc_channel, "07You must be an op to use %s." % msg_command[1])
                        else:
                            self.msg(self.factory.irc_channel,
                                "07You must provide a valid command to use the IRC bot - try the help command.")
                    else:
                        self.msg(self.factory.irc_channel,
                            "07You must provide a valid command to use the IRC bot - try the help command.")
                elif msg.startswith("$"):
                    self.logger.info("<$%s> %s" % (user, msg))
                elif msg.startswith("!"):
                    # It's a world message.
                    message = msg.split(" ")
                    if len(message) == 1:
                        self.msg(self.factory.irc_channel, "07Please include a message to send.")
                    else:
                        try:
                            world = message[0][1:len(message[0])]
                            out = "\n ".join(message[1:])
                        except ValueError:
                            self.msg(self.factory.irc_channel, "07Please include a message to send.")
                        else:
                            if world in self.factory.worlds:
                                self.factory.sendMessageToAll(out, "world", user=user, world=world)
                            else:
                                self.msg(self.factory.irc_channel, "07That world does not exist. Try !world message")
                else:
                    for character in msg:
                        if not character.lower() in PRINTABLE:
                            msg = msg.replace("&0", "&0")
                            msg = msg.replace("&1", "&1")
                            msg = msg.replace("&2", "&2")
                            msg = msg.replace("&3", "&3")
                            msg = msg.replace("&4", "&4")
                            msg = msg.replace("&5", "&5")
                            msg = msg.replace("&6", "&6")
                            msg = msg.replace("&7", "&7")
                            msg = msg.replace("&8", "&8")
                            msg = msg.replace("&9", "&9")
                            msg = msg.replace("&a", "&a")
                            msg = msg.replace("&b", "&b")
                            msg = msg.replace("&c", "&c")
                            msg = msg.replace("&d", "&d")
                            msg = msg.replace("&e", "&e")
                            msg = msg.replace("&f", "&f")
                            msg = msg.replace("0", "&f")
                            msg = msg.replace("00", "&f")
                            msg = msg.replace("1", "&0")
                            msg = msg.replace("01", "&0")
                            msg = msg.replace("2", "&1")
                            msg = msg.replace("02", "&1")
                            msg = msg.replace("3", "&2")
                            msg = msg.replace("03", "&2")
                            msg = msg.replace("4", "&c")
                            msg = msg.replace("04", "&c")
                            msg = msg.replace("5", "&4")
                            msg = msg.replace("05", "&4")
                            msg = msg.replace("6", "&5")
                            msg = msg.replace("06", "&5")
                            msg = msg.replace("7", "&6")
                            msg = msg.replace("07", "&6")
                            msg = msg.replace("8", "&e")
                            msg = msg.replace("08", "&e")
                            msg = msg.replace("9", "&a")
                            msg = msg.replace("09", "&a")
                            msg = msg.replace("10", "&3")
                            msg = msg.replace("11", "&b")
                            msg = msg.replace("12", "&9")
                            msg = msg.replace("13", "&d")
                            msg = msg.replace("14", "&8")
                            msg = msg.replace("15", "&7")
                            msg = msg.replace(character, "*")
                    msg = msg.replace("%0", "&0")
                    msg = msg.replace("%1", "&1")
                    msg = msg.replace("%2", "&2")
                    msg = msg.replace("%3", "&3")
                    msg = msg.replace("%4", "&4")
                    msg = msg.replace("%5", "&5")
                    msg = msg.replace("%6", "&6")
                    msg = msg.replace("%7", "&7")
                    msg = msg.replace("%8", "&8")
                    msg = msg.replace("%9", "&9")
                    msg = msg.replace("%a", "&a")
                    msg = msg.replace("%b", "&b")
                    msg = msg.replace("%c", "&c")
                    msg = msg.replace("%d", "&d")
                    msg = msg.replace("%e", "&e")
                    msg = msg.replace("%f", "&f")
                    msg = msg.replace("./", " /")
                    msg = msg.replace(".!", " !")
                    if msg[len(msg) - 2] == "&":
                        self.msg(self.factory.irc_channel, "07You cannot use a color at the end of a message.")
                        return
                    if len(msg) > 51:
                        moddedmsg = msg[:51].replace(" ", "")
                        if moddedmsg[len(moddedmsg) - 2] == "&":
                            msg = msg.replace("&", "*")
                    self.factory.sendMessageToAll(msg, "irc", user=user)
        except:
            self.logger.error(traceback.format_exc())
            self.msg(self.factory.irc_channel, "Internal Server Error (See the Console for more details)")

    def action(self, user, channel, msg):
        """This will get called when the bot sees someone do an action."""
        msg = msg.replace("./", " /")
        msg = msg.replace(".!", " !")
        user = user.split('!', 1)[0]
        msg = "".join([char for char in msg if ord(char) < 128 and char != "" or "0"])
        self.factory.sendMessageToAll("%s* %s %s" % (COLOUR_YELLOW, COLOUR_WHITE + user, msg), "irc", user="")
        self.logger.info("* %s %s" % (user, msg))
        self.factory.chatlog.write(
            "[%s] * %s %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), user, msg))
        self.factory.chatlog.flush()

    def sendMessage(self, username, message):
        message = message.replace("&0", "01")
        message = message.replace("&1", "02")
        message = message.replace("&2", "03")
        message = message.replace("&3", "10")
        message = message.replace("&4", "05")
        message = message.replace("&5", "06")
        message = message.replace("&6", "07")
        message = message.replace("&7", "15")
        message = message.replace("&8", "14")
        message = message.replace("&9", "12")
        message = message.replace("&a", "09")
        message = message.replace("&b", "11")
        message = message.replace("&c", "04")
        message = message.replace("&d", "13")
        message = message.replace("&e", "08")
        message = message.replace("&f", "14")
        username = username.replace("&0", "01")
        username = username.replace("&1", "02")
        username = username.replace("&2", "03")
        username = username.replace("&3", "10")
        username = username.replace("&4", "05")
        username = username.replace("&5", "06")
        username = username.replace("&6", "07")
        username = username.replace("&7", "15")
        username = username.replace("&8", "14")
        username = username.replace("&9", "12")
        username = username.replace("&a", "09")
        username = username.replace("&b", "11")
        username = username.replace("&c", "04")
        username = username.replace("&d", "13")
        username = username.replace("&e", "08")
        username = username.replace("&f", "14")
        self.msg(self.factory.irc_channel, "%s: %s" % (username, message))

    def sendServerMessage(self, message, admin=False, user=""):
        message = message.replace("./", " /")
        message = message.replace(".!", " !")
        message = message.replace("&0", "01")
        message = message.replace("&1", "02")
        message = message.replace("&2", "03")
        message = message.replace("&3", "10")
        message = message.replace("&4", "05")
        message = message.replace("&5", "06")
        message = message.replace("&6", "07")
        message = message.replace("&7", "15")
        message = message.replace("&8", "14")
        message = message.replace("&9", "12")
        message = message.replace("&a", "09")
        message = message.replace("&b", "11")
        message = message.replace("&c", "04")
        message = message.replace("&d", "13")
        message = message.replace("&e", "08")
        message = message.replace("&f", "14")
        if admin:
            for op in self.ops:
                if not op == user or not IRC:
                    self.msg(op, "%s" % message)
        else:
            self.msg(self.factory.irc_channel, "%s" % message)

    def sendAction(self, username, message):
        message = message.replace("&0", "01")
        message = message.replace("&1", "02")
        message = message.replace("&2", "03")
        message = message.replace("&3", "10")
        message = message.replace("&4", "05")
        message = message.replace("&5", "06")
        message = message.replace("&6", "07")
        message = message.replace("&7", "15")
        message = message.replace("&8", "14")
        message = message.replace("&9", "12")
        message = message.replace("&a", "09")
        message = message.replace("&b", "11")
        message = message.replace("&c", "04")
        message = message.replace("&d", "13")
        message = message.replace("&e", "08")
        message = message.replace("&f", "14")
        self.msg(self.factory.irc_channel, "* %s %s" % (username, message))

    # irc callbacks

    def irc_NICK(self, prefix, params):
        "Called when an IRC user changes their nickname."
        old_nick = prefix.split('!')[0]
        new_nick = params[0]
        if old_nick in self.ops:
            self.ops.remove(old_nick)
            self.ops.append(new_nick)
        msg = "%s%s is now known as %s" % (COLOUR_YELLOW, old_nick, new_nick)
        self.factory.sendMessageToAll(msg, "irc", user="")

    def userKicked(self, kickee, channel, kicker, message):
        "Called when I observe someone else being kicked from a channel."
        if kickee in self.ops:
            self.ops.remove(kickee)
        msg = "%s%s was kicked from %s by %s" % (COLOUR_YELLOW, kickee, channel, kicker)
        self.factory.sendMessageToAll(msg, "irc", user="")
        if not kickee == message:
            msg = "%sReason: %s" % (COLOUR_YELLOW, message)
            self.factory.sendMessageToAll(msg, "irc", user="")

    def userLeft(self, user, channel):
        "Called when I see another user leaving a channel."
        if user in self.ops:
            self.ops.remove(user)
        msg = "%s%s has left %s" % (COLOUR_YELLOW, user.split("!")[0], channel)
        self.factory.sendMessageToAll(msg, "irc", user="")

    def userJoined(self, user, channel):
        "Called when I see another user joining a channel."
        if user in self.ops:
            self.ops.remove(user)
        msg = "%s%s has joined %s" % (COLOUR_YELLOW, user.split("!")[0], channel)
        self.factory.sendMessageToAll(msg, "irc", user="")

    def modeChanged(self, user, channel, set, modes, args):
        "Called when someone changes a mode."
        setUser = user.split("!")[0]
        arguments = []
        for element in args:
            if element:
                arguments.append(element.split("!")[0])
        if set and modes.startswith("o"):
            if len(arguments) < 2:
                msg = "%s%s was opped on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers opped on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="")
            for name in args:
                if not name in self.ops:
                    self.ops.append(name)
        elif not set and modes.startswith("o"):
            done = []
            for name in args:
                done.append(name.split("!")[0])
            if len(arguments) < 2:
                msg = "%s%s was deopped on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers deopped on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="")
            for name in args:
                if name in self.ops:
                    self.ops.remove(name)
        elif set and modes.startswith("v"):
            if len(arguments) < 2:
                msg = "%s%s was voiced on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers voiced on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="")
            for name in args:
                if not name in self.ops:
                    self.ops.append(name)
        elif not set and modes.startswith("v"):
            done = []
            for name in args:
                done.append(name.split("!")[0])
            if len(arguments) < 2:
                msg = "%s%s was devoiced on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers devoiced on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="")
            for name in args:
                if name in self.ops:
                    self.ops.remove(name)
        elif set and modes.startswith("b"):
            msg = "%sBan set in %s by %s" % (COLOUR_YELLOW, channel, setUser)
            self.factory.sendMessageToAll(msg, "irc", user="")
            msg = "%s(%s)" % (COLOUR_YELLOW, " ".join(args))
            self.factory.sendMessageToAll(msg, "irc", user="")
        elif not set and modes.startswith("b"):
            msg = "%sBan lifted in %s by %s" % (COLOUR_YELLOW, channel, setUser)
            self.factory.sendMessageToAll(msg, "irc", user="")
            msg = "%s(%s)" % (COLOUR_YELLOW, " ".join(args))
            self.factory.sendMessageToAll(msg, "irc", user="")

    def irc_QUIT(self, user, params):
        userhost = user
        user = user.split('!')[0]
        quitMessage = params[0]
        if userhost in self.ops:
            self.ops.remove(userhost)
        msg = "%s%s has quit IRC." % (COLOUR_YELLOW, user)
        self.factory.sendMessageToAll(msg, "irc", user=user)
        for character in msg:
            if not character.lower() in PRINTABLE:
                msg = msg.replace("&0", "&0")
                msg = msg.replace("&1", "&1")
                msg = msg.replace("&2", "&2")
                msg = msg.replace("&3", "&3")
                msg = msg.replace("&4", "&4")
                msg = msg.replace("&5", "&5")
                msg = msg.replace("&6", "&6")
                msg = msg.replace("&7", "&7")
                msg = msg.replace("&8", "&8")
                msg = msg.replace("&9", "&9")
                msg = msg.replace("&a", "&a")
                msg = msg.replace("&b", "&b")
                msg = msg.replace("&c", "&c")
                msg = msg.replace("&d", "&d")
                msg = msg.replace("&e", "&e")
                msg = msg.replace("&f", "&f")
                msg = msg.replace("0", "&f")
                msg = msg.replace("00", "&f")
                msg = msg.replace("1", "&0")
                msg = msg.replace("01", "&0")
                msg = msg.replace("2", "&1")
                msg = msg.replace("02", "&1")
                msg = msg.replace("3", "&2")
                msg = msg.replace("03", "&2")
                msg = msg.replace("4", "&c")
                msg = msg.replace("04", "&c")
                msg = msg.replace("5", "&4")
                msg = msg.replace("05", "&4")
                msg = msg.replace("6", "&5")
                msg = msg.replace("06", "&5")
                msg = msg.replace("7", "&6")
                msg = msg.replace("07", "&6")
                msg = msg.replace("8", "&e")
                msg = msg.replace("08", "&e")
                msg = msg.replace("9", "&a")
                msg = msg.replace("09", "&a")
                msg = msg.replace("10", "&3")
                msg = msg.replace("11", "&b")
                msg = msg.replace("12", "&9")
                msg = msg.replace("13", "&d")
                msg = msg.replace("14", "&8")
                msg = msg.replace("15", "&7")
                msg = msg.replace(character, "*")
        msg = msg.replace("%0", "&0")
        msg = msg.replace("%1", "&1")
        msg = msg.replace("%2", "&2")
        msg = msg.replace("%3", "&3")
        msg = msg.replace("%4", "&4")
        msg = msg.replace("%5", "&5")
        msg = msg.replace("%6", "&6")
        msg = msg.replace("%7", "&7")
        msg = msg.replace("%8", "&8")
        msg = msg.replace("%9", "&9")
        msg = msg.replace("%a", "&a")
        msg = msg.replace("%b", "&b")
        msg = msg.replace("%c", "&c")
        msg = msg.replace("%d", "&d")
        msg = msg.replace("%e", "&e")
        msg = msg.replace("%f", "&f")
        msg = msg.replace("./", " /")
        msg = msg.replace(".!", " !")
        if msg[len(msg) - 2] == "&":
            return
        if len(msg) > 51:
            moddedmsg = msg[:51].replace(" ", "")
            if moddedmsg[len(moddedmsg) - 2] == "&":
                msg = msg.replace("&", "*")
        msg = "%s(%s%s)" % (COLOUR_YELLOW, quitMessage, COLOUR_YELLOW)
        self.factory.sendMessageToAll(msg, "irc", user="")
Beispiel #7
0
class BlockStore(Thread):
    """
    A class which deals with storing the block worlds, flushing them, etc.
    """

    def __init__(self, blocks_path, sx, sy, sz):
        Thread.__init__(self)
        self.x, self.y, self.z = sx, sy, sz
        self.blocks_path = blocks_path
        self.world_name = os.path.basename(os.path.dirname(blocks_path))
        self.in_queue = Queue()
        self.out_queue = Queue()
        self.logger = ColouredLogger()

    def run(self):
        # Initialise variables
        self.physics = False
        self.physics_engine = Physics(self)
        self.raw_blocks = None # Read this from any thread but please update through TASK_BLOCKSET so queued_blocks is updated
        self.running = True
        self.unflooding = False
        self.finite_water = False
        self.queued_blocks = {} # Blocks which need to be flushed into the file.
        self.create_raw_blocks()
        # Start physics engine
        self.physics_engine.start()
        # Main eval loop
        while self.running:
            try:
                # Pop something off the queue
                task = self.in_queue.get()
                # If we've been asked to flush, do so, and say we did.
                if task[0] is TASK_FLUSH:
                    self.flush()
                    self.out_queue.put([TASK_FLUSH])
                # New block?
                elif task[0] is TASK_BLOCKSET:
                    try:
                        self[task[1]] = task[2]
                        if len(task) == 4 and task[3] == True:
                            # Tells the server to update the given block for clients.
                            self.out_queue.put([TASK_BLOCKSET, (task[1][0], task[1][1], task[1][2], task[2])])
                    except AssertionError:
                        self.logger.warning("Tried to set a block at %s in %s!" % (task[1], self.world_name))
                # Asking for a block?
                elif task[0] is TASK_BLOCKGET:
                    self.out_queue.put([TASK_BLOCKGET, task[1], self[task[1]]])
                # Perhaps physics was enabled?
                elif task[0] is TASK_PHYSICSOFF:
                    self.logger.debug("Disabling physics on '%s'..." % self.world_name)
                    self.disable_physics()
                # Or disabled?
                elif task[0] is TASK_PHYSICSON:
                    self.logger.debug("Enabling physics on '%s'..." % self.world_name)
                    self.enable_physics()
                # I can haz finite water tiem?
                elif task[0] is TASK_FWATERON:
                    self.logger.debug("Enabling finite water on '%s'..." % self.world_name)
                    self.finite_water = True
                # Noes, no more finite water.
                elif task[0] is TASK_FWATEROFF:
                    self.logger.debug("Disabling finite water on '%s'..." % self.world_name)
                    self.finite_water = False
                # Do they need to do a Moses?
                elif task[0] is TASK_UNFLOOD:
                    self.logger.debug("Unflood started on '%s'..." % self.world_name)
                    self.unflooding = True
                # Perhaps that's it, and we need to stop?
                elif task[0] is TASK_STOP:
                    self.logger.debug("Stopping block store '%s'..." % self.world_name)
                    self.physics_engine.stop()
                    self.flush()
                    self.logger.debug("Stopped block store '%s'." % self.world_name)
                    return
                # ???
                else:
                    raise ValueError("Unknown BlockStore task: %s" % task)
            except (KeyboardInterrupt, IOError):
                pass

    def enable_physics(self):
        "Turns on physics"
        self.physics = True

    def disable_physics(self):
        "Disables physics, and clears the in-memory store."
        self.physics = False

    def create_raw_blocks(self):
        "Reads in the gzipped data into a raw array"
        # Open the blocks file
        fh = gzip.GzipFile(self.blocks_path)
        self.raw_blocks = array('c')
        # Read off the size header
        fh.read(4)
        # Copy into the array in chunks
        chunk = fh.read(2048)
        while chunk:
            self.raw_blocks.extend(chunk)
            chunk = fh.read(2048)
        fh.close()

    def get_offset(self, x, y, z):
        "Turns block coordinates into a data offset"
        assert 0 <= x < self.x
        assert 0 <= y < self.y
        assert 0 <= z < self.z
        return y * (self.x * self.z) + z * (self.x) + x

    def get_coords(self, offset):
        "Turns a data offset into coordinates"
        x = offset % self.x
        z = (offset // self.x) % self.z
        y = offset // (self.x * self.z)
        return x, y, z

    def message(self, message):
        "Sends a message out to users about this World."
        self.out_queue.put([TASK_MESSAGE, message])

    def __setitem__(self, (x, y, z), block):
        "Set a block in this level to the given value."
        assert isinstance(block, str) and len(block) == 1
        # Save to queued blocks
        offset = self.get_offset(x, y, z)
        self.queued_blocks[offset] = block
        # And directly to raw blocks, if we must
        if self.raw_blocks:
            self.raw_blocks[offset] = block
            # Ask the physics engine if they'd like a look at that
        self.physics_engine.handle_change(offset, block)
Beispiel #8
0
# Arc is copyright 2009-2011 the Arc team and other contributors.
# Arc is licensed under the BSD 2-Clause modified License.
# To view more details, please see the "LICENSING" file in the "docs" folder of the Arc Package.

import sys
from arc.logger import ColouredLogger

debug = (True if "--debug" in sys.argv else False)
logger = ColouredLogger()
logger.debug("Imported arc/ folder.")
del logger
del ColouredLogger
del sys
Beispiel #9
0
class Physics(Thread):
    """
    Given a BlockStore, works out what needs doing (water, grass etc.)
    and send the changes back to the BlockStore.
    """
    # This thread:
    # * Receives changes by ATOMIC updates to 'changed' eg. self.changed.add(offset)
    # * Sends changes by queueing updates in the blockstore
    # * Reads but doesn't modify self.blockstore.raw_blocks[]
    # Optimised by only checking blocks near changes, advantages are a small work set and very up-to-date

    def __init__(self, blockstore):
        Thread.__init__(self)
        self.blockstore = blockstore
        #self.setDaemon(True) # means that python can terminate even if still active
        #self.work = Event()    # TODO use event to start and stop
        self.last_lag = 0
        self.running = True
        self.was_physics = False
        self.was_unflooding = False
        self.changed = set()
        self.working = set() # could be a list or a sorted list but why bother (world updates may appear in random order but most of the time so many get updated it should be unnoticable)
        self.sponge_locations = set()
        self.logger = ColouredLogger(debug)

    def stop(self):
        self.running = False
        self.join()

    def run(self):
        while self.running:
            if self.blockstore.physics:
                if self.blockstore.unflooding:
                    # Do n fluid removals
                    updates = 0
                    for offset, block in enumerate(self.blockstore.raw_blocks):
                        if block == CHR_LAVA or block == CHR_WATER or block == CHR_SPOUT or block == CHR_LAVA_SPOUT:
                            x, y, z = self.blockstore.get_coords(offset)
                            self.set_block((x, y, z), BLOCK_AIR)
                            updates += 1
                            if updates >= LIMIT_UNFLOOD:
                                break
                    else:
                        # Unflooding complete.
                        self.blockstore.unflooding = False
                        self.blockstore.message(COLOUR_YELLOW + "Unflooding complete.")
                        self.changed.clear()
                        self.working = set()
                else:
                    # If this is the first of a physics run, redo the queues from scratch
                    if not self.was_physics or self.was_unflooding:
                        self.logger.debug("Queue everything for '%s'." % self.blockstore.world_name)
                        self.changed.clear()
                        self.working = Popxrange(0, (self.blockstore.x * self.blockstore.y * self.blockstore.z))
                    # if working list is empty then copy changed set to working set
                    # otherwise keep using the working set till empty
                    elif len(self.working) == 0:
                        self.logger.debug("Performing expand checks for '%s' with %d changes." % (
                        self.blockstore.world_name, len(self.changed)))
                        changedfixed = self.changed # 'changedfixed' is 'changed' so gets all updates
                        self.changed = set()        # until 'changed' is a new set. This and the above statment are ATOMIC
                        self.working = set()         # changes from a Popxrange to a set
                        while len(changedfixed) > 0:
                            self.expand_checks(changedfixed.pop())
                    self.logger.debug("Starting physics run for '%s' with %d checks." % (
                    self.blockstore.world_name, len(self.working)))
                    updates = 0
                    try:
                        for x in xrange(LIMIT_CHECKS):
                            offset = self.working.pop()
                            updates += self.handle(offset)
                    except KeyError:
                        pass
                        #if overflow and (time.time() - self.last_lag > self.LAG_INTERVAL):
                        #self.blockstore.message("Physics is currently lagging in %(id)s.")
                        #self.last_lag = time.time()
                    self.logger.debug("Ended physics run for '%s' with %d updates and %d checks remaining." % (
                    self.blockstore.world_name, updates, len(self.working)))
            else:
                if self.was_physics:
                    self.blockstore.unflooding = False
                    self.changed.clear()
                    self.working = set()
            self.was_physics = self.blockstore.physics
            self.was_unflooding = self.blockstore.unflooding
            # Wait till next iter
            time.sleep(0.7) # TODO change this so takes into account run time

    def handle_change(self, offset, block): # must be ATOMIC
        "Gets called when a block is changed, with its offset and type."
        self.changed.add(offset)

    def set_block(self, (x, y, z), block): # only place blockstore is updated
        "Call to queue a block change, with its position and type."
        self.blockstore.in_queue.put([TASK_BLOCKSET, (x, y, z), chr(block), True])
Beispiel #10
0
from arc.constants import *
from arc.globals import *
from arc.logger import ColouredLogger
from arc.server import ArcFactory

FILES_TO_MAKE = ["logs/", "logs/console/", "logs/levels/", # Log folders
                "arc/archives/", # Archives
                "config/data/"] # Config data
makefiles(FILES_TO_MAKE)
makedatfile("config/data/balances.dat")
makedatfile("config/data/inbox.dat")
makedatfile("config/data/jail.dat")
makedatfile("config/data/titles.dat")

debug = (True if "--debug" in sys.argv else False)
logger = ColouredLogger(debug)

try:
    from colorama import init
except ImportError:
    logger.warn("Colorama is not installed - console colours DISABLED.")
except Exception as e:
    logger.warn("Unable to import colorama: %s" % e)
    logger.warn("Console colours DISABLED.")
else:
    init()
    logger.stdout("&f")
    logger.debug("&fIf you see this, debug mode is &eon&f!")
    logger.info("&fColorama &ainstalled&f - Console colours &cENABLED&f.")

def doExit():
Beispiel #11
0
class ChatBot(irc.IRCClient):
    """An IRC-server chat integration bot."""

    def connectionMade(self):
        self.logger = ColouredLogger(debug)
        self.ops = []
        self.nickname = self.factory.main_factory.irc_nick
        self.password = self.factory.main_factory.irc_pass
        irc.IRCClient.connectionMade(self)
        self.factory.instance = self
        self.factory, self.irc_factory = self.factory.main_factory, self.factory
        self.world = None
        self.sendLine('NAMES ' + self.factory.irc_channel)

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)
        self.logger.info("IRC client disconnected. (%s)" % reason)

    # callbacks for events

    def ctcpQuery_VERSION(self, user, channel, data):
        """Called when received a CTCP VERSION request."""
        nick = user.split("!")[0]
        self.ctcpMakeReply(nick, [('VERSION', 'The Archives %s - a Minecraft server written in Python.' % VERSION)])

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.logger.info("IRC client connected.")
        self.msg("NickServ", "IDENTIFY %s %s" % (self.nickname, self.password))
        self.msg("ChanServ", "INVITE %s" % self.factory.irc_channel)
        self.join(self.factory.irc_channel)

    def joined(self, channel):
        """This will get called when the bot joins the channel."""
        self.logger.info("IRC client joined %s." % channel)

    def sendError(self, error):
        self.logger.error("Sending error: %s" % error)
        self.sendPacked(TYPE_ERROR, error)
        reactor.callLater(0.2, self.transport.loseConnection)

    def lineReceived(self, line): # use instead of query
        line = irc.lowDequote(line)
        try:
            prefix, command, params = irc.parsemsg(line)
            if irc.numeric_to_symbolic.has_key(command):
                command = irc.numeric_to_symbolic[command]
            self.handleCommand(command, prefix, params)
        except irc.IRCBadMessage:
            self.badMessage(line, *sys.exc_info())
        try:
            if command == "RPL_NAMREPLY":
                names = params[3].split()
                for name in names:
                    if name.startswith("@"):
                        self.ops.append(name[1:])
        except:
            self.logger.error(traceback.format_exc())

    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        try:
            user = user.split('!', 1)[0]
            if channel == self.nickname:
                if not (self.nickname == user or "Serv" in user):
                    msg_command = msg.split()
                    self.factory.runCommand(msg_command[0], msg_command, "irc_query", (True if user in self.ops else False), client=self, username=user)
            elif channel.lower() == self.factory.irc_channel.lower():
                if msg.lower().lstrip(self.nickname.lower()).startswith("$" + self.nickname.lower()):
                    msg_command = msg.split()
                    msg_command[1] = msg_command[1].lower()
                    self.factory.runCommand(msg_command[1], msg_command, "irc", (True if user in self.ops else False), client=self)
                elif msg.startswith("$"):
                    self.logger.info("<$%s> %s" % (user, msg))
                elif msg.startswith("!"):
                    # It's a world message.
                    message = msg.split(" ")
                    if len(message) == 1:
                        self.msg(self.factory.irc_channel, "Please include a message to send.")
                    else:
                        try:
                            world = message[0][1:len(message[0])]
                            out = "\n ".join(message[1:])
                        except ValueError:
                            self.msg(self.factory.irc_channel, "07Please include a message to send.")
                        else:
                            if world in self.factory.worlds:
                                self.factory.sendMessageToAll(out, "world", user=user, world=world, fromloc="irc")
                            else:
                                self.msg(self.factory.irc_channel, "07That world does not exist. Try !world message")
                else:
                    msg = sanitizeMessage(msg, [MSGREPLACE["text_colour_to_game"], MSGREPLACE["irc_colour_to_game"], MSGREPLACE["escape_commands"]])
                    for character in msg:
                        if not character.lower() in PRINTABLE:
                            msg = msg.replace(character, "*")
                    if msg[len(msg) - 2] == "&":
                        self.msg(self.factory.irc_channel, "07You cannot use a color at the end of a message.")
                        return
                    self.factory.sendMessageToAll(msg, "irc", user=user, fromloc="irc")
        except:
            self.logger.error(traceback.format_exc())
            self.msg(self.factory.irc_channel, "Internal Server Error (See the Console for more details)")

    def action(self, user, channel, msg):
        """This will get called when the bot sees someone do an action."""
        msg = msg.replace("./", " /")
        msg = msg.replace(".!", " !")
        user = user.split('!', 1)[0]
        msg = "".join([char for char in msg if ord(char) < 128 and char != "" or "0"])
        self.factory.sendMessageToAll("%s* %s %s" % (COLOUR_YELLOW, COLOUR_WHITE + user, msg), "irc", user="", fromloc="irc")
        self.logger.info("* %s %s" % (user, msg))
        self.factory.chatlog.write(
            "[%s] * %s %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), user, msg))
        self.factory.chatlog.flush()

    def sendMessage(self, username, message):
        sanitizeMessage(username, MSGREPLACE["game_colour_to_irc"])
        sanitizeMessage(message, [MSGREPLACE["game_colour_to_irc"], MSGREPLACE["escape_commands"]])
        self.msg(self.factory.irc_channel, "%s: %s" % (username, message))

    def sendServerMessage(self, message, user=None):
        sanitizeMessage(message, [MSGREPLACE["game_colour_to_irc"], MSGREPLACE["escape_commands"]])
        if user != None:
            self.msg(user, "%s" % message)
        else:
            self.msg(self.factory.irc_channel, "%s" % message)

    def sendAction(self, username, message):
        sanitizeMessage(message, MSGREPLACE["game_colour_to_irc"])
        sanitizeMessage(message, [MSGREPLACE["game_colour_to_irc"], MSGREPLACE["escape_commands"]])
        self.msg(self.factory.irc_channel, "* %s %s" % (username, message))

    sendSplitServerMessage = sendWorldMessage = sendPlainWorldMessage = sendNormalMessage = sendServerMessage

    def sendServerList(self, items, wrap_at=63, plain=False, username=None):
        "Sends the items as server messages, wrapping them correctly."
        done = " ".join(items)
        self.irc_factory.sendServerMessage(done)

    # irc callbacks

    def irc_NICK(self, prefix, params):
        "Called when an IRC user changes their nickname."
        old_nick = prefix.split('!')[0]
        new_nick = params[0]
        if old_nick in self.ops:
            self.ops.remove(old_nick)
            self.ops.append(new_nick)
        msg = "%s%s is now known as %s" % (COLOUR_YELLOW, old_nick, new_nick)
        self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")

    def userKicked(self, kickee, channel, kicker, message):
        "Called when I observe someone else being kicked from a channel."
        if kickee in self.ops:
            self.ops.remove(kickee)
        msg = "%s%s was kicked from %s by %s" % (COLOUR_YELLOW, kickee, channel, kicker)
        self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")
        if not kickee == message:
            msg = "%sReason: %s" % (COLOUR_YELLOW, message)
            self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")

    def userLeft(self, user, channel):
        "Called when I see another user leaving a channel."
        if user in self.ops:
            self.ops.remove(user)
        msg = "%s%s has left %s" % (COLOUR_YELLOW, user.split("!")[0], channel)
        self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")

    def userJoined(self, user, channel):
        "Called when I see another user joining a channel."
        if user in self.ops:
            self.ops.remove(user)
        msg = "%s%s has joined %s" % (COLOUR_YELLOW, user.split("!")[0], channel)
        self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")

    def modeChanged(self, user, channel, set, modes, args):
        "Called when someone changes a mode."
        setUser = user.split("!")[0]
        arguments = []
        for element in args:
            if element:
                arguments.append(element.split("!")[0])
        if set and modes.startswith("o"):
            if len(arguments) < 2:
                msg = "%s%s was opped on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers opped on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")
            for name in args:
                if not name in self.ops:
                    self.ops.append(name)
        elif not set and modes.startswith("o"):
            done = []
            for name in args:
                done.append(name.split("!")[0])
            if len(arguments) < 2:
                msg = "%s%s was deopped on %s by %s" % (COLOUR_YELLOW, arguments[0], channel, setUser)
            else:
                msg = "%sUsers deopped on %s by %s: %s (%s)" % (
                COLOUR_YELLOW, channel, setUser, ", ".join(arguments), len(arguments))
            self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")
            for name in args:
                if name in self.ops:
                    self.ops.remove(name)

    def irc_QUIT(self, user, params):
        userhost = user
        user = user.split('!')[0]
        quitMessage = params[0]
        if userhost in self.ops:
            self.ops.remove(userhost)
        msg = "%s%s has quit IRC." % (COLOUR_YELLOW, user)
        self.factory.sendMessageToAll(msg, "irc", user=user)
        sanitizeMessage(msg, [MSGREPLACE["irc_colour_to_game"], MSGREPLACE["escape_commands"]])
        for character in msg:
            if not character.lower() in PRINTABLE:
                msg = msg.replace(character, "*")
        if msg[len(msg) - 2] == "&":
            return
        msg = "%s(%s%s)" % (COLOUR_YELLOW, quitMessage, COLOUR_YELLOW)
        self.factory.sendMessageToAll(msg, "irc", user="", fromloc="irc")

    def isRank(self):
        return False # For whatever rank, just return False. overriderank is turned on for query so doesn't matter

    isSpectator = isBuilder = isOp = isWorldOwner = isHelper = isMod = isAdmin = isDirector = isOwner = isSilenced = alloweToBuild  = canBreakAdminBlocks = isRank

    def getBlockValue(self, value):
        # Try getting the block as a direct integer type.
        try:
            block = chr(int(value))
        except ValueError:
            # OK, try a symbolic type.
            try:
                block = chr(globals()['BLOCK_%s' % value.upper()])
            except KeyError:
                return None
            # Check the block is valid
        if ord(block) > 49:
            return None
        return block

    def send(self, *args, **kwargs):
        pass

    canBreakAdminBlocks = sendPacked = sendError = sendRankUpdate = respawn = sendBlock = sendPlayerPos = sendPlayerDir = sendNewPlayer = sendPlayerLeave = sendKeepAlive = sendOverload =\
        sendOverloadChunk = sendLevel = sendLevelStart = sendLevelChunk = endSendLevel = sendAllNew = sendWelcome = send

    def getBlbLimit(self):
        return 0