def reloadIrcBot(self): if(self.irc_relay): try: self.irc_relay.quit("Reloading the IRC Bot...") global ChatBotFactory del ChatBotFactory from core.irc_client import ChatBotFactory if self.ircbot and self.use_irc: self.irc_nick = self.irc_config.get("irc", "nick") self.irc_pass = self.irc_config.get("irc", "password") self.irc_channel = self.irc_config.get("irc", "channel") self.irc_cmdlogs = self.irc_config.getboolean("irc", "cmdlogs") self.ircbot = self.irc_config.getboolean("irc", "ircbot") self.staffchat = self.irc_config.getboolean("irc", "staffchat") self.irc_relay = ChatBotFactory(self) if self.ircbot and not (self.irc_channel == "#icraft" or self.irc_channel == "#channel") and not self.irc_nick == "botname": reactor.connectTCP(self.irc_config.get("irc", "server"), self.irc_config.getint("irc", "port"), self.irc_relay) else: logging.log(logging.ERROR, "IRC Bot failed to connect, you could modify, rename or remove irc.conf") logging.log(logging.ERROR, "You need to change your 'botname' and 'channel' fields to fix this error or turn the bot off by disabling 'ircbot'") return True except: return False return False
class CoreFactory(Factory): """ Factory that deals with the general world actions and cross-user comms. """ protocol = CoreServerProtocol def reloadIrcBot(self): if(self.irc_relay): try: self.irc_relay.quit("Reloading the IRC Bot...") global ChatBotFactory del ChatBotFactory from core.irc_client import ChatBotFactory if self.ircbot and self.use_irc: self.irc_nick = self.irc_config.get("irc", "nick") self.irc_pass = self.irc_config.get("irc", "password") self.irc_channel = self.irc_config.get("irc", "channel") self.irc_cmdlogs = self.irc_config.getboolean("irc", "cmdlogs") self.ircbot = self.irc_config.getboolean("irc", "ircbot") self.staffchat = self.irc_config.getboolean("irc", "staffchat") self.irc_relay = ChatBotFactory(self) if self.ircbot and not (self.irc_channel == "#icraft" or self.irc_channel == "#channel") and not self.irc_nick == "botname": reactor.connectTCP(self.irc_config.get("irc", "server"), self.irc_config.getint("irc", "port"), self.irc_relay) else: logging.log(logging.ERROR, "IRC Bot failed to connect, you could modify, rename or remove irc.conf") logging.log(logging.ERROR, "You need to change your 'botname' and 'channel' fields to fix this error or turn the bot off by disabling 'ircbot'") return True except: return False return False def reloadConfig(self): try: # TODO: Figure out which of these would work dynamically, otherwise delete them from this area. self.owner = self.config.get("main", "owner").lower() self.duplicate_logins = self.options_config.getboolean("options", "duplicate_logins") self.info_url = self.options_config.get("options", "info_url") self.away_kick = self.options_config.getboolean("options", "away_kick") self.away_time = self.options_config.getint("options", "away_time") self.colors = self.options_config.getboolean("options", "colors") self.physics_limit = self.options_config.getint("worlds", "physics_limit") self.default_backup = self.options_config.get("worlds", "default_backup") self.asd_delay = self.options_config.getint("worlds", "asd_delay") self.gchat = self.options_config.getboolean("worlds", "gchat") self.grief_blocks = self.ploptions_config.getint("antigrief", "blocks") self.grief_time = self.ploptions_config.getint("antigrief", "time") self.backup_freq = self.ploptions_config.getint("backups", "backup_freq") self.backup_default = self.ploptions_config.getboolean("backups", "backup_default") self.backup_max = self.ploptions_config.getint("backups", "backup_max") self.backup_auto = self.ploptions_config.getboolean("backups", "backup_auto") self.enable_archives = self.ploptions_config.getboolean("archiver", "enable_archiver") self.currency = self.ploptions_config.get("bank", "currency") self.build_director = self.ploptions_config.get("build", "director") self.build_admin = self.ploptions_config.get("build", "admin") self.build_mod = self.ploptions_config.get("build", "mod") self.build_op = self.ploptions_config.get("build", "op") self.build_other = self.ploptions_config.get("build", "other") if self.backup_auto: reactor.callLater(float(self.backup_freq * 60),self.AutoBackup) except: return False def __init__(self): self.ServerVars = dict() self.specs = ConfigParser() self.last_heartbeat = time.time() self.lastseen = ConfigParser() self.config = ConfigParser() self.options_config = ConfigParser() self.ploptions_config = ConfigParser() self.wordfilter = ConfigParser() self.save_count = 1 try: self.config.read("config/main.conf") except: logging.log(logging.ERROR, "Something is messed up with your main.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: self.options_config.read("config/options.conf") except: logging.log(logging.ERROR, "Something is messed up with your options.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: self.ploptions_config.read("config/ploptions.conf") except: logging.log(logging.ERROR, "Something is messed up with your ploptions.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.use_irc = False if (os.path.exists("config/irc.conf")): self.use_irc = True self.irc_config = ConfigParser() try: self.irc_config.read("config/irc.conf") except: logging.log(logging.ERROR, "Something is messed up with your irc.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.saving = False try: self.max_clients = self.config.getint("main", "max_clients") self.server_message = self.config.get("main", "description") self.public = self.config.getboolean("main", "public") self.controller_port = self.config.get("network", "controller_port") self.controller_password = self.config.get("network", "controller_password") self.server_name = self.config.get("main", "name") if self.server_name == "iCraft Server": logging.log(logging.ERROR, "You forgot to give your server a name.") self.owner = self.config.get("main", "owner").lower() if self.owner == "yournamehere": logging.log(logging.ERROR, "You forgot to make yourself the server owner.") except: logging.log(logging.ERROR, "You don't have a main.conf file! You need to rename main.example.conf to main.conf") sys.exit(1) try: self.duplicate_logins = self.options_config.getboolean("options", "duplicate_logins") self.info_url = self.options_config.get("options", "info_url") self.away_kick = self.options_config.getboolean("options", "away_kick") self.away_time = self.options_config.getint("options", "away_time") self.colors = self.options_config.getboolean("options", "colors") self.physics_limit = self.options_config.getint("worlds", "physics_limit") self.default_name = self.options_config.get("worlds", "default_name") self.default_backup = self.options_config.get("worlds", "default_backup") self.asd_delay = self.options_config.getint("worlds", "asd_delay") self.gchat = self.options_config.getboolean("worlds", "gchat") except: logging.log(logging.ERROR, "You don't have a options.conf file! You need to rename options.example.conf to options.conf") sys.exit(1) try: self.grief_blocks = self.ploptions_config.getint("antigrief", "blocks") self.grief_time = self.ploptions_config.getint("antigrief", "time") self.backup_freq = self.ploptions_config.getint("backups", "backup_freq") self.backup_default = self.ploptions_config.getboolean("backups", "backup_default") self.backup_max = self.ploptions_config.getint("backups", "backup_max") self.backup_auto = self.ploptions_config.getboolean("backups", "backup_auto") self.enable_archives = self.ploptions_config.getboolean("archiver", "enable_archiver") self.currency = self.ploptions_config.get("bank", "currency") self.build_director = self.ploptions_config.get("build", "director") self.build_admin = self.ploptions_config.get("build", "admin") self.build_mod = self.ploptions_config.get("build", "mod") self.build_op = self.ploptions_config.get("build", "op") self.build_other = self.ploptions_config.get("build", "other") if self.backup_auto: reactor.callLater(float(self.backup_freq * 60),self.AutoBackup) except: logging.log(logging.ERROR, "You don't have a ploptions.conf file! You need to rename ploptions.example.conf to ploptions.conf") sys.exit(1) #if not os.path.exists("config/greeting.txt"): # logging.log(logging.ERROR, "You don't have a greeting.txt file! You need to rename greeting.example.txt to greeting.txt (If this error persists, you may have used Notepad.)") # sys.exit(1) #if not os.path.exists("config/rules.txt"): # logging.log(logging.ERROR, "You don't have a rules.txt file! You need to rename rules.example.txt to rules.txt (If this error persists, you may have used Notepad.)") # sys.exit(1) if self.use_irc: self.irc_nick = self.irc_config.get("irc", "nick") self.irc_pass = self.irc_config.get("irc", "password") self.irc_channel = self.irc_config.get("irc", "channel") self.irc_cmdlogs = self.irc_config.getboolean("irc", "cmdlogs") self.ircbot = self.irc_config.getboolean("irc", "ircbot") self.staffchat = self.irc_config.getboolean("irc", "staffchat") self.irc_relay = ChatBotFactory(self) if self.ircbot and not (self.irc_channel == "#icraft" or self.irc_channel == "#channel") and not self.irc_nick == "botname": reactor.connectTCP(self.irc_config.get("irc", "server"), self.irc_config.getint("irc", "port"), self.irc_relay) else: logging.log(logging.ERROR, "IRC Bot failed to connect, you could modify, rename or remove irc.conf") logging.log(logging.ERROR, "You need to change your 'botname' and 'channel' fields to fix this error or turn the bot off by disabling 'ircbot'") else: self.irc_relay = None self.default_loaded = False # Word Filter try: self.wordfilter.read("config/wordfilter.conf") except: logging.log(logging.ERROR, "Something is messed up with your wordfilter.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.filter = [] try: number = int(self.wordfilter.get("filter","count")) except: logging.log(logging.ERROR, "You need to rename wordfilter.example.conf to wordfilter.conf") sys.exit(1); for x in range(number): self.filter = self.filter + [[self.wordfilter.get("filter","s"+str(x)),self.wordfilter.get("filter","r"+str(x))]] # Salt, for the heartbeat server/verify-names self.salt = hashlib.md5(hashlib.md5(str(random.getrandbits(128))).digest()).hexdigest()[-32:].strip("0") # Load up the plugins specified self.plugins_config = ConfigParser() try: self.plugins_config.read("config/plugins.conf") except: logging.log(logging.ERROR, "Something is messed up with your irc.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: plugins = self.plugins_config.options("plugins") except: print ("NOTICE: You need to rename plugins.example.conf to plugins.conf") sys.exit(1); logging.log(logging.INFO, "Loading plugins...") load_plugins(plugins) # Open the chat log, ready for appending self.chatlog = open("logs/server.log", "a") self.chatlog = open("logs/chat.log", "a") # Create a default world, if there isn't one. if not os.path.isdir("worlds/%s" % self.default_name): logging.log(logging.INFO, "Generating %s world..." % self.default_name) sx, sy, sz = 64, 64, 64 grass_to = (sy // 2) world = World.create( "worlds/%s" % self.default_name, sx, sy, sz, # Size sx//2,grass_to+2, sz//2, 0, # Spawn ([BLOCK_DIRT]*(grass_to-1) + [BLOCK_GRASS] + [BLOCK_AIR]*(sy-grass_to)) # Levels ) logging.log(logging.INFO, "Generated.") # Initialise internal datastructures self.worlds = {} self.directors = set() self.admins = set() self.mods = set() self.globalbuilders = set() self.members = set() self.spectators = set() self.silenced = set() self.banned = {} self.ipbanned = {} self.lastseen = {} # Load up the contents of those. self.loadMeta() # Set up a few more things. self.queue = Queue() self.clients = {} self.usernames = {} self.console = StdinPlugin(self) self.console.start() self.heartbeat = Heartbeat(self) # Boot worlds that got loaded for world in self.worlds: self.loadWorld("worlds/%s" % world, world) # Set up tasks to run during execution reactor.callLater(0.1, self.sendMessages) reactor.callLater(1, self.printInfo) # Initial startup is instant, but it updates every 10 minutes. self.world_save_stack = [] reactor.callLater(60, self.saveWorlds) if self.enable_archives: self.loadPlugin('archives') reactor.callLater(1, self.loadArchives) gc.disable() self.cleanGarbage() def cleanGarbage(self): count = gc.collect() logging.log(logging.INFO, "%i garbage objects collected, %i were uncollected." % ( count, len(gc.garbage))) reactor.callLater(60*15, self.cleanGarbage) def loadMeta(self): "Loads the 'meta' - variables that change with the server (worlds, admins, etc.)" config = ConfigParser() config.read("config/data/ranks.meta") specs = ConfigParser() specs.read("config/data/spectators.meta") lastseen = ConfigParser() lastseen.read("config/data/lastseen.meta") bans = ConfigParser() bans.read("config/data/bans.meta") worlds = ConfigParser() worlds.read("config/data/worlds.meta") # Read in the admins if config.has_section("admins"): for name in config.options("admins"): self.admins.add(name) # Read in the mods if config.has_section("mods"): for name in config.options("mods"): self.mods.add(name) if config.has_section("globalbuilders"): for name in config.options("globalbuilders"): self.globalbuilders.add(name) if config.has_section("members"): for name in config.options("members"): self.members.add(name) # Read in the directors if config.has_section("directors"): for name in config.options("directors"): self.directors.add(name) if config.has_section("silenced"): for name in config.options("silenced"): self.silenced.add(name) # Read in the spectators (experimental) if specs.has_section("spectators"): for name in specs.options("spectators"): self.spectators.add(name) bans = ConfigParser() bans.read("config/data/bans.meta") # Read in the bans if bans.has_section("banned"): for name in bans.options("banned"): self.banned[name] = bans.get("banned", name) # Read in the ipbans if bans.has_section("ipbanned"): for ip in bans.options("ipbanned"): self.ipbanned[ip] = bans.get("ipbanned", ip) # Read in the lastseen if lastseen.has_section("lastseen"): for username in lastseen.options("lastseen"): self.lastseen[username] = lastseen.getfloat("lastseen", username) # Read in the worlds if worlds.has_section("worlds"): for name in worlds.options("worlds"): if name is self.default_name: self.default_loaded = True else: self.worlds[self.default_name] = None if not self.default_loaded: self.worlds[self.default_name] = None def saveMeta(self): "Saves the server's meta back to a file." config = ConfigParser() specs = ConfigParser() lastseen = ConfigParser() bans = ConfigParser() worlds = ConfigParser() # Make the sections config.add_section("directors") config.add_section("admins") config.add_section("mods") config.add_section("globalbuilders") config.add_section("members") config.add_section("silenced") bans.add_section("banned") bans.add_section("ipbanned") specs.add_section("spectators") lastseen.add_section("lastseen") # Write out things for director in self.directors: config.set("directors", director, "true") for admin in self.admins: config.set("admins", admin, "true") for mod in self.mods: config.set("mods", mod, "true") for globalbuilder in self.globalbuilders: config.set("globalbuilders", globalbuilder, "true") for member in self.members: config.set("members", member, "true") for ban, reason in self.banned.items(): bans.set("banned", ban, reason) for spectator in self.spectators: specs.set("spectators", spectator, "true") for silence in self.silenced: config.set("silenced", silence, "true") for ipban, reason in self.ipbanned.items(): bans.set("ipbanned", ipban, reason) for username, ls in self.lastseen.items(): lastseen.set("lastseen", username, str(ls)) fp = open("config/data/ranks.meta", "w") config.write(fp) fp.close() fp = open("config/data/spectators.meta", "w") specs.write(fp) fp.close() fp = open("config/data/lastseen.meta", "w") lastseen.write(fp) fp.close() fp = open("config/data/bans.meta", "w") bans.write(fp) fp.close() fp = open("config/data/worlds.meta", "w") worlds.write(fp) fp.close() def printInfo(self): logging.log(logging.INFO, "There are %s users on the server" % len(self.clients)) for key in self.worlds: logging.log(logging.INFO, "%s: %s" % (key, ", ".join(str(c.username) for c in self.worlds[key].clients))) if (time.time() - self.last_heartbeat) > 180: self.heartbeat = None self.heartbeat = Heartbeat(self) reactor.callLater(60, self.printInfo) def loadArchive(self, filename): "Boots an archive given a filename. Returns the new world ID." # Get an unused world name i = 1 while self.world_exists("a-%i" % i): i += 1 world_id = "a-%i" % i # Copy and boot self.newWorld(world_id, "../core/archives/%s" % filename) self.loadWorld("worlds/%s" % world_id, world_id) world = self.worlds[world_id] world.is_archive = True return world_id def saveWorlds(self): "Saves the worlds, one at a time, with a 1 second delay." if not self.saving: if not self.world_save_stack: self.world_save_stack = list(self.worlds) key = self.world_save_stack.pop() self.saveWorld(key) if not self.world_save_stack: reactor.callLater(60, self.saveWorlds) self.saveMeta() else: reactor.callLater(1, self.saveWorlds) def saveWorld(self, world_id,shutdown = False): try: world = self.worlds[world_id] world.save_meta() world.flush() logging.log(logging.INFO, "World '%s' has been saved." % world_id) if self.save_count == 5: for client in list(list(self.worlds[world_id].clients))[:]: client.sendServerMessage("[%s] World '%s' has been saved." % (datetime.datetime.utcnow().strftime("%H:%M"), world_id)) self.save_count = 1 else: self.save_count += 1 if shutdown: del self.worlds[world_id] except: logging.log(logging.INFO, "Error saving %s" % world_id) def claimId(self, client): for i in range(1, self.max_clients+1): if i not in self.clients: self.clients[i] = client return i raise ServerFull def releaseId(self, id): del self.clients[id] def joinWorld(self, worldid, user): "Makes the user join the given World." new_world = self.worlds[worldid] try: logging.log(logging.INFO, "%s is joining world %s" %(user.username,new_world.basename)) except: logging.log(logging.INFO, "%s is joining world %s" %(user.transport.getPeer().host,new_world.basename)) if hasattr(user, "world") and user.world: self.leaveWorld(user.world, user) user.world = new_world new_world.clients.add(user) if not worldid == self.default_name and not new_world.ASD == None: new_world.ASD.kill() new_world.ASD = None return new_world def leaveWorld(self, world, user): world.clients.remove(user) if world.autoshutdown and len(world.clients)<1: if world.basename == ("worlds/" + self.default_name): return else: if not self.asd_delay == 0: world.ASD = ResettableTimer(self.asd_delay*60,1,world.unload) else: world.ASD = ResettableTimer(30,1,world.unload) world.ASD.start() def loadWorld(self, filename, world_id): """ Loads the given world file under the given world ID, or a random one. Returns the ID of the new world. """ world = self.worlds[world_id] = World(filename) world.source = filename world.clients = set() world.id = world_id world.factory = self world.start() logging.log(logging.INFO, "World '%s' Booted." % world_id) return world_id def unloadWorld(self, world_id,ASD=False): """ Unloads the given world ID. """ try: if ASD and len(self.worlds[world_id].clients)>0: self.worlds[world_id].ASD.kill() self.worlds[world_id].ASD = None return except KeyError: return try: assert world_id != self.default_name except: self.client.sendServerMessage("You can't shutdown "+self.default_name+".") if not self.worlds[world_id].ASD == None: self.worlds[world_id].ASD.kill() self.worlds[world_id].ASD = None for client in list(list(self.worlds[world_id].clients))[:]: client.changeToWorld(self.default_name) client.sendServerMessage("World '%s' has been Shutdown." % world_id) self.worlds[world_id].stop() self.saveWorld(world_id,True) logging.log(logging.INFO, "World '%s' Shutdown." % world_id) def rebootWorld(self, world_id): """ Reboots a world in a crash case """ for client in list(list(self.worlds[world_id].clients))[:]: if world_id == self.default_name: client.loadWorld("worlds/%s" % world_id, world_id) client.changeToWorld(self.default_backup) else: client.changeToWorld(self.default_name) client.sendServerMessage("%s has been Rebooted" % world_id) self.worlds[world_id].stop() self.worlds[world_id].flush() self.worlds[world_id].save_meta() del self.worlds[world_id] world = self.worlds[world_id] = World("worlds/%s" % world_id, world_id) world.source = "worlds/" + world_id world.clients = set() world.id = world_id world.factory = self world.start() logging.log(logging.INFO, "Rebooted %s" % world_id) def publicWorlds(self): """ Returns the IDs of all public worlds """ for world_id, world in self.worlds.items(): if not world.private: yield world_id def recordPresence(self, username): """ Records a sighting of 'username' in the lastseen dict. """ self.lastseen[username.lower()] = time.time() def unloadPlugin(self, plugin_name): "Reloads the plugin with the given module name." # Unload the plugin from everywhere for plugin in plugins_by_module_name(plugin_name): if issubclass(plugin, ProtocolPlugin): for client in self.clients.values(): client.unloadPlugin(plugin) # Unload it unload_plugin(plugin_name) def loadPlugin(self, plugin_name): # Load it load_plugin(plugin_name) # Load it back into clients etc. for plugin in plugins_by_module_name(plugin_name): if issubclass(plugin, ProtocolPlugin): for client in self.clients.values(): client.loadPlugin(plugin) def sendMessages(self): "Sends all queued messages, and lets worlds recieve theirs." try: while True: # Get the next task source_client, task, data = self.queue.get_nowait() try: if isinstance(source_client, World): world = source_client elif str(source_client).startswith("<StdinPlugin"): world = self.worlds[self.default_name] else: try: world = source_client.world except AttributeError: logging.log(logging.WARN, "Source client for message has no world. Ignoring.") continue # Someone built/deleted a block if task is TASK_BLOCKSET: # Only run it for clients who weren't the source. for client in world.clients: if client is not source_client: client.sendBlock(*data) # Someone moved elif task is TASK_PLAYERPOS: # Only run it for clients who weren't the source. for client in world.clients: if client != source_client: client.sendPlayerPos(*data) # Someone moved only their direction elif task is TASK_PLAYERDIR: # Only run it for clients who weren't the source. for client in world.clients: if client != source_client: client.sendPlayerDir(*data) # Someone spoke! elif task is TASK_MESSAGE: # More Word Filter id, colour, username, text = data text = self.messagestrip(text) data = (id,colour,username,text) for client in self.clients.values(): client.sendMessage(*data) id, colour, username, text = data logging.log(logging.INFO, "%s: %s" % (username, text)) self.chatlog.write("[%s] %s: %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), username, text)) self.chatlog.flush() if self.irc_relay and world: self.irc_relay.sendMessage(username, text) # Someone spoke! elif task is TASK_IRCMESSAGE: for client in self.clients.values(): client.sendMessage(*data) id, colour, username, text = data logging.log(logging.INFO, "<%s> %s" % (username, text)) self.chatlog.write("[%s] <%s> %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), username, text)) self.chatlog.flush() if self.irc_relay and world: self.irc_relay.sendMessage(username, text) # Someone actioned! elif task is TASK_ACTION: # More Word Filter id, colour, username, text = data text = self.messagestrip(text) data = (id,colour,username,text) for client in self.clients.values(): client.sendAction(*data) id, colour, username, text = data logging.log(logging.INFO, "* %s %s" % (username, text)) self.chatlog.write("[%s] * %s %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), username, text)) self.chatlog.flush() if self.irc_relay and world: self.irc_relay.sendAction(username, text) # Someone connected to the server elif task is TASK_PLAYERCONNECT: for client in self.usernames: self.usernames[client].sendNewPlayer(*data) if self.username.lower() in INFO_VIPLIST and not self.isMod(): self.usernames[client].sendNormalMessage(COLOUR_DARKRED+"iCraft Developer spotted;") self.usernames[client].sendServerMessage("%s has come online." % source_client.username) if self.irc_relay and world: if self.username.lower() in INFO_VIPLIST and not self.isMod(): self.irc_relay.sendServerMessage("04iCraft Developer spotted;") self.irc_relay.sendServerMessage("07%s has come online." % source_client.username) # Someone joined a world! elif task is TASK_NEWPLAYER: for client in world.clients: if client != source_client: client.sendNewPlayer(*data) client.sendServerMessage("%s has joined the world." % source_client.username) # Someone left! elif task is TASK_PLAYERLEAVE: # Only run it for clients who weren't the source. for client in self.clients.values(): client.sendPlayerLeave(*data) if not source_client.username is None: client.sendServerMessage("%s has gone offline." % source_client.username) else: source_client.log("Pinged the server.") if not source_client.username is None: if self.irc_relay and world: self.irc_relay.sendServerMessage("07%s has gone offline." % source_client.username) # Someone changed worlds! elif task is TASK_WORLDCHANGE: # Only run it for clients who weren't the source. for client in data[1].clients: client.sendPlayerLeave(data[0]) client.sendServerMessage("%s joined '%s'" % (source_client.username, world.id)) if self.irc_relay and world: self.irc_relay.sendServerMessage("07%s joined '%s'" % (source_client.username, world.id)) logging.log(logging.INFO, "%s has now joined '%s'" % (source_client.username, world.id)) elif task == TASK_STAFFMESSAGE: # Give all staff the message :D id, colour, username, text, IRC = data message = self.messagestrip(text); for user, client in self.usernames.items(): if self.isMod(user): client.sendMessage(100, COLOUR_YELLOW+"#"+colour, username, message, False, False) if self.staffchat and self.irc_relay and len(data)>3: self.irc_relay.sendServerMessage("#"+username+": "+text,True,username,IRC) logging.log(logging.INFO, "#"+username+": "+text) self.adlog = open("logs/server.log", "a") self.adlog.write("["+datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S")+"] #"+username+": "+text+"\n") self.adlog.flush() elif task == TASK_GLOBALMESSAGE: # Give all world people the message id, world, message = data message = self.messagestrip(message); for client in world.clients: client.sendNormalMessage(message) elif task == TASK_WORLDMESSAGE: # Give all world people the message id, world, message = data for client in world.clients: client.sendNormalMessage(message) elif task == TASK_SERVERMESSAGE: # Give all people the message message = data message = self.messagestrip(message); for client in self.clients.values(): client.sendNormalMessage(COLOUR_DARKBLUE + message) logging.log(logging.INFO,message) if self.irc_relay and world: self.irc_relay.sendServerMessage(message) elif task == TASK_ONMESSAGE: # Give all people the message message = data message = self.messagestrip(message); for client in self.clients.values(): client.sendNormalMessage(COLOUR_YELLOW + message) if self.irc_relay and world: self.irc_relay.sendServerMessage(message) elif task == TASK_PLAYERRESPAWN: # We need to immediately respawn the user to update their nick. for client in world.clients: if client != source_client: id, username, x, y, z, h, p = data client.sendPlayerLeave(id) client.sendNewPlayer(id, username, x, y, z, h, p) elif task == TASK_SERVERURGENTMESSAGE: # Give all people the message message = data for client in self.clients.values(): client.sendNormalMessage(COLOUR_DARKRED + message) logging.log(logging.INFO,message) if self.irc_relay and world: self.irc_relay.sendServerMessage(message) elif task == TASK_AWAYMESSAGE: # Give all world people the message message = data for client in self.clients.values(): client.sendNormalMessage(COLOUR_DARKPURPLE + message) logging.log(logging.INFO, "AWAY - %s" %message) self.chatlog.write("[%s] %s %s\n" % (datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"), "", message)) self.chatlog.flush() if self.irc_relay and world: self.irc_relay.sendAction("", message) except Exception, e: logging.log(logging.ERROR, traceback.format_exc()) except Empty: pass # OK, now, for every world, let them read their queues for world in self.worlds.values(): world.read_queue() # Come back soon! reactor.callLater(0.1, self.sendMessages) def newWorld(self, new_name, template="default"): "Creates a new world from some template." # Make the directory try: os.mkdir("worlds/%s" % new_name) except: client.sendServerMessage("Sorry, that world already exists!") # Find the template files, copy them to the new location for filename in ["blocks.gz", "world.meta"]: try: shutil.copyfile("core/templates/%s/%s" % (template, filename), "worlds/%s/%s" % (new_name, filename)) except: self.client.sendServerMessage("That template doesn't exist.") def renameWorld(self, old_worldid, new_worldid): "Renames a world." assert old_worldid not in self.worlds assert self.world_exists(old_worldid) assert not self.world_exists(new_worldid) os.rename("worlds/%s" % (old_worldid), "worlds/%s" % (new_worldid)) def numberWithPhysics(self): "Returns the number of worlds with physics enabled." return len([world for world in self.worlds.values() if world.physics]) def isSilenced(self, username): return username.lower() in self.silenced def isOwner(self, username): return username.lower()==self.owner def isDirector(self, username): return username.lower() in self.directors or self.isOwner(username) def isAdmin(self, username): return username.lower() in self.admins or self.isDirector(username) def isMod(self, username): return username.lower() in self.mods or self.isAdmin(username) def isMember(self, username): #TODO: Needs to check for builder/op level also. return username.lower() in self.members or self.isMod(username) def isSpectator(self, username): return username.lower() in self.spectators def isBanned(self, username): return username.lower() in self.banned def isIpBanned(self, ip): return ip in self.ipbanned def addBan(self, username, reason): self.banned[username.lower()] = reason def removeBan(self, username): del self.banned[username.lower()] def banReason(self, username): return self.banned[username.lower()] def addIpBan(self, ip, reason): self.ipbanned[ip] = reason def removeIpBan(self, ip): del self.ipbanned[ip] def ipBanReason(self, ip): return self.ipbanned[ip] def world_exists(self, world_id): "Says if the world exists (even if unbooted)" return os.path.isdir("worlds/%s/" % world_id) def AutoBackup(self): for world in self.worlds: self.Backup(world) if self.backup_auto: reactor.callLater(float(self.backup_freq * 60),self.AutoBackup) def Backup(self, world_id): world_dir = ("worlds/%s/" % world_id) if world_id == self.default_name and not self.backup_default: return if not os.path.exists(world_dir): logging.log(logging.INFO, "World %s does not exist." % (world.id)) else: if not os.path.exists(world_dir+"backup/"): os.mkdir(world_dir+"backup/") folders = os.listdir(world_dir+"backup/") backups = list([]) for x in folders: if x.isdigit(): backups.append(x) backups.sort(lambda x, y: int(x) - int(y)) path = os.path.join(world_dir+"backup/", "0") if backups: path = os.path.join(world_dir+"backup/", str(int(backups[-1])+1)) os.mkdir(path) shutil.copy(world_dir + "blocks.gz", path) shutil.copy(world_dir + "world.meta", path) try: logging.log(logging.INFO, "%s's backup %s is saved." % (world_id, str(int(backups[-1])+1))) except: logging.log(logging.INFO, "%s's backup 0 is saved." % (world_id)) if len(backups)+1 > self.backup_max: for i in range(0,((len(backups)+1)-self.backup_max)): shutil.rmtree(os.path.join(world_dir+"backup/", str(int(backups[i])))) def messagestrip(factory,message): strippedmessage = "" for x in message: if ord(str(x)) < 128: strippedmessage = strippedmessage + str(x) message = strippedmessage for x in factory.filter: rep = re.compile(x[0], re.IGNORECASE) message = rep.sub(x[1], message) return message def loadArchives(self): self.archives = {} for name in os.listdir("core/archives/"): if os.path.isdir(os.path.join("core/archives", name)): for subfilename in os.listdir(os.path.join("core/archives", name)): match = re.match(r'^(\d\d\d\d\-\d\d\-\d\d_\d?\d\_\d\d)$', subfilename) if match: when = match.groups()[0] try: when = datetime.datetime.strptime(when, "%Y/%m/%d %H:%M:%S") except ValueError, e: logging.log(logging.WARN, "Bad archive filename %s" % subfilename) continue if name not in self.archives: self.archives[name] = {} self.archives[name][when] = "%s/%s" % (name, subfilename) logging.log(logging.INFO, "Loaded %s discrete archives." % len(self.archives)) reactor.callLater(300, self.loadArchives)
def __init__(self): self.ServerVars = dict() self.specs = ConfigParser() self.last_heartbeat = time.time() self.lastseen = ConfigParser() self.config = ConfigParser() self.options_config = ConfigParser() self.ploptions_config = ConfigParser() self.wordfilter = ConfigParser() self.save_count = 1 try: self.config.read("config/main.conf") except: logging.log(logging.ERROR, "Something is messed up with your main.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: self.options_config.read("config/options.conf") except: logging.log(logging.ERROR, "Something is messed up with your options.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: self.ploptions_config.read("config/ploptions.conf") except: logging.log(logging.ERROR, "Something is messed up with your ploptions.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.use_irc = False if (os.path.exists("config/irc.conf")): self.use_irc = True self.irc_config = ConfigParser() try: self.irc_config.read("config/irc.conf") except: logging.log(logging.ERROR, "Something is messed up with your irc.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.saving = False try: self.max_clients = self.config.getint("main", "max_clients") self.server_message = self.config.get("main", "description") self.public = self.config.getboolean("main", "public") self.controller_port = self.config.get("network", "controller_port") self.controller_password = self.config.get("network", "controller_password") self.server_name = self.config.get("main", "name") if self.server_name == "iCraft Server": logging.log(logging.ERROR, "You forgot to give your server a name.") self.owner = self.config.get("main", "owner").lower() if self.owner == "yournamehere": logging.log(logging.ERROR, "You forgot to make yourself the server owner.") except: logging.log(logging.ERROR, "You don't have a main.conf file! You need to rename main.example.conf to main.conf") sys.exit(1) try: self.duplicate_logins = self.options_config.getboolean("options", "duplicate_logins") self.info_url = self.options_config.get("options", "info_url") self.away_kick = self.options_config.getboolean("options", "away_kick") self.away_time = self.options_config.getint("options", "away_time") self.colors = self.options_config.getboolean("options", "colors") self.physics_limit = self.options_config.getint("worlds", "physics_limit") self.default_name = self.options_config.get("worlds", "default_name") self.default_backup = self.options_config.get("worlds", "default_backup") self.asd_delay = self.options_config.getint("worlds", "asd_delay") self.gchat = self.options_config.getboolean("worlds", "gchat") except: logging.log(logging.ERROR, "You don't have a options.conf file! You need to rename options.example.conf to options.conf") sys.exit(1) try: self.grief_blocks = self.ploptions_config.getint("antigrief", "blocks") self.grief_time = self.ploptions_config.getint("antigrief", "time") self.backup_freq = self.ploptions_config.getint("backups", "backup_freq") self.backup_default = self.ploptions_config.getboolean("backups", "backup_default") self.backup_max = self.ploptions_config.getint("backups", "backup_max") self.backup_auto = self.ploptions_config.getboolean("backups", "backup_auto") self.enable_archives = self.ploptions_config.getboolean("archiver", "enable_archiver") self.currency = self.ploptions_config.get("bank", "currency") self.build_director = self.ploptions_config.get("build", "director") self.build_admin = self.ploptions_config.get("build", "admin") self.build_mod = self.ploptions_config.get("build", "mod") self.build_op = self.ploptions_config.get("build", "op") self.build_other = self.ploptions_config.get("build", "other") if self.backup_auto: reactor.callLater(float(self.backup_freq * 60),self.AutoBackup) except: logging.log(logging.ERROR, "You don't have a ploptions.conf file! You need to rename ploptions.example.conf to ploptions.conf") sys.exit(1) #if not os.path.exists("config/greeting.txt"): # logging.log(logging.ERROR, "You don't have a greeting.txt file! You need to rename greeting.example.txt to greeting.txt (If this error persists, you may have used Notepad.)") # sys.exit(1) #if not os.path.exists("config/rules.txt"): # logging.log(logging.ERROR, "You don't have a rules.txt file! You need to rename rules.example.txt to rules.txt (If this error persists, you may have used Notepad.)") # sys.exit(1) if self.use_irc: self.irc_nick = self.irc_config.get("irc", "nick") self.irc_pass = self.irc_config.get("irc", "password") self.irc_channel = self.irc_config.get("irc", "channel") self.irc_cmdlogs = self.irc_config.getboolean("irc", "cmdlogs") self.ircbot = self.irc_config.getboolean("irc", "ircbot") self.staffchat = self.irc_config.getboolean("irc", "staffchat") self.irc_relay = ChatBotFactory(self) if self.ircbot and not (self.irc_channel == "#icraft" or self.irc_channel == "#channel") and not self.irc_nick == "botname": reactor.connectTCP(self.irc_config.get("irc", "server"), self.irc_config.getint("irc", "port"), self.irc_relay) else: logging.log(logging.ERROR, "IRC Bot failed to connect, you could modify, rename or remove irc.conf") logging.log(logging.ERROR, "You need to change your 'botname' and 'channel' fields to fix this error or turn the bot off by disabling 'ircbot'") else: self.irc_relay = None self.default_loaded = False # Word Filter try: self.wordfilter.read("config/wordfilter.conf") except: logging.log(logging.ERROR, "Something is messed up with your wordfilter.conf file. (Did you edit it in Notepad?)") sys.exit(1) self.filter = [] try: number = int(self.wordfilter.get("filter","count")) except: logging.log(logging.ERROR, "You need to rename wordfilter.example.conf to wordfilter.conf") sys.exit(1); for x in range(number): self.filter = self.filter + [[self.wordfilter.get("filter","s"+str(x)),self.wordfilter.get("filter","r"+str(x))]] # Salt, for the heartbeat server/verify-names self.salt = hashlib.md5(hashlib.md5(str(random.getrandbits(128))).digest()).hexdigest()[-32:].strip("0") # Load up the plugins specified self.plugins_config = ConfigParser() try: self.plugins_config.read("config/plugins.conf") except: logging.log(logging.ERROR, "Something is messed up with your irc.conf file. (Did you edit it in Notepad?)") sys.exit(1) try: plugins = self.plugins_config.options("plugins") except: print ("NOTICE: You need to rename plugins.example.conf to plugins.conf") sys.exit(1); logging.log(logging.INFO, "Loading plugins...") load_plugins(plugins) # Open the chat log, ready for appending self.chatlog = open("logs/server.log", "a") self.chatlog = open("logs/chat.log", "a") # Create a default world, if there isn't one. if not os.path.isdir("worlds/%s" % self.default_name): logging.log(logging.INFO, "Generating %s world..." % self.default_name) sx, sy, sz = 64, 64, 64 grass_to = (sy // 2) world = World.create( "worlds/%s" % self.default_name, sx, sy, sz, # Size sx//2,grass_to+2, sz//2, 0, # Spawn ([BLOCK_DIRT]*(grass_to-1) + [BLOCK_GRASS] + [BLOCK_AIR]*(sy-grass_to)) # Levels ) logging.log(logging.INFO, "Generated.") # Initialise internal datastructures self.worlds = {} self.directors = set() self.admins = set() self.mods = set() self.globalbuilders = set() self.members = set() self.spectators = set() self.silenced = set() self.banned = {} self.ipbanned = {} self.lastseen = {} # Load up the contents of those. self.loadMeta() # Set up a few more things. self.queue = Queue() self.clients = {} self.usernames = {} self.console = StdinPlugin(self) self.console.start() self.heartbeat = Heartbeat(self) # Boot worlds that got loaded for world in self.worlds: self.loadWorld("worlds/%s" % world, world) # Set up tasks to run during execution reactor.callLater(0.1, self.sendMessages) reactor.callLater(1, self.printInfo) # Initial startup is instant, but it updates every 10 minutes. self.world_save_stack = [] reactor.callLater(60, self.saveWorlds) if self.enable_archives: self.loadPlugin('archives') reactor.callLater(1, self.loadArchives) gc.disable() self.cleanGarbage()