def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick): """ This will create a new IRC<->channel connection. """ if not type(channel) == Channel: new_channel = Channel.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel) return False channel = new_channel[0] key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick) old_conns = ExternalChannelConnection.objects.filter(db_external_key=key) if old_conns: return False config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick) # how the channel will be able to contact this protocol send_code = "from src.comms.irc import IRC_CHANNELS\n" send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key send_code += "[irc.msg_irc(message, senders=[self]) for irc in matched_ircs]\n" conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code, db_external_config=config) conn.save() # connect connect_to_irc(conn) return True
def __delattr__(self, propname): """ Transparently deletes data from the typeclass or dbobj by first searching on the typeclass, secondly on the dbobj.db. Will not allow deletion of properties stored directly on dbobj. """ if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return try: _DA(self, propname) except AttributeError: # not on typeclass, try to delete on db/ndb try: dbobj = _GA(self, 'dbobj') except AttributeError: log_trace("This is probably due to an unsafe reload.") return # ignore delete try: dbobj.del_attribute(propname, raise_exception=True) except AttributeError: string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s." raise AttributeError(string % ( propname, dbobj, dbobj.dbid, dbobj.typeclass_path, ))
def add(self, scriptclass, key=None, autostart=True): """ Add an script to this object. scriptclass - either a class object inheriting from Script, an instantiated script object or a python path to such a class object. key - optional identifier for the script (often set in script definition) autostart - start the script upon adding it """ if self.obj.dbobj.__class__.__name__ == "PlayerDB": # we add to a Player, not an Object script = create.create_script(scriptclass, key=key, player=self.obj, autostart=autostart) else: # the normal - adding to an Object script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart) if not script: logger.log_errmsg( "Script %s could not be created and/or started." % scriptclass) return False return True
def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None): """ Returns all objects having a given db field property. candidates - list of objects to search typeclasses - list of typeclass-path strings to restrict matches with """ if isinstance(property_value, basestring): property_value = to_unicode(property_value) if isinstance(property_name, basestring): if not property_name.startswith("db_"): property_name = "db_%s" % property_name if hasattr(property_value, "dbobj"): property_value = property_value.dbobj querykwargs = {property_name: property_value} cand_restriction = ( candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() ) type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q() try: return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs))) except exceptions.FieldError: return [] except ValueError: from src.utils import logger logger.log_errmsg( "The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value)) ) return []
def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None): """ Returns all objects having a given db field property. candidates - list of objects to search typeclasses - list of typeclass-path strings to restrict matches with """ if isinstance(property_value, basestring): property_value = to_unicode(property_value) if isinstance(property_name, basestring): if not property_name.startswith('db_'): property_name = "db_%s" % property_name if hasattr(property_value, 'dbobj'): property_value = property_value.dbobj querykwargs = {property_name: property_value} cand_restriction = candidates != None and Q( pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() type_restriction = typeclasses and Q( db_typeclass_path__in=make_iter(typeclasses)) or Q() try: return list( self.filter(cand_restriction & type_restriction & Q(**querykwargs))) except exceptions.FieldError: return [] except ValueError: from src.utils import logger logger.log_errmsg( "The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value))) return []
def create_help_entry(key, entrytext, category="General", locks=None): """ Create a static help entry in the help database. Note that Command help entries are dynamic and directly taken from the __doc__ entries of the command. The database-stored help entries are intended for more general help on the game, more extensive info, in-game setting information and so on. """ global _HelpEntry if not _HelpEntry: from src.help.models import HelpEntry as _HelpEntry try: new_help = _HelpEntry() new_help.key = key new_help.entrytext = entrytext new_help.help_category = category if locks: new_help.locks.add(locks) new_help.save() return new_help except IntegrityError: string = "Could not add help entry: key '%s' already exists." % key logger.log_errmsg(string) return None except Exception: logger.log_trace() return None
def __setattr__(self, propname, value): """ Transparently save data to the dbobj object in all situations. Note that this does not necessarily mean storing it to the database unless data is stored into a propname corresponding to a field on ObjectDB model. """ #print "set %s -> %s" % (propname, value) if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return try: dbobj = _GA(self, 'dbobj') except AttributeError: dbobj = None log_trace("This is probably due to an unsafe reload.") if dbobj: try: # only set value on propname if propname already exists # on dbobj. __getattribute__ will raise attribute error otherwise. _GA(dbobj, propname) _SA(dbobj, propname, value) except AttributeError: #XXX deprecated dbobj.set_attribute(propname, value) else: _SA(self, propname, value)
def __setattr__(self, propname, value): """ Transparently save data. Use property on Typeclass only if that property is already defined, otherwise relegate to the dbobj object in all situations. Note that this does not necessarily mean storing it to the database. """ #print "set %s -> %s" % (propname, value) if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return try: _GA(self, propname) _SA(self, propname, value) except AttributeError: try: dbobj = _GA(self, 'dbobj') except AttributeError: dbobj = None if dbobj: _SA(dbobj, propname, value) else: # only as a last resort do we save on the typeclass object _SA(self, propname, value)
def __delattr__(self, propname): """ Transparently deletes data from the typeclass or dbobj by first searching on the typeclass, secondly on the dbobj.db. Will not allow deletion of properties stored directly on dbobj. """ if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return try: _DA(self, propname) except AttributeError: # not on typeclass, try to delete on db/ndb try: dbobj = _GA(self, 'dbobj') except AttributeError: log_trace("This is probably due to an unsafe reload.") return # ignore delete try: dbobj.del_attribute_raise(propname) except AttributeError: string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s." raise AttributeError(string % (propname, dbobj, dbobj.dbid, dbobj.typeclass_path,))
def __value_set(self, value): "Setter. Allows for self.value = value" if utils.has_parent('django.db.models.base.Model', value): # we have to protect against storing db objects. logger.log_errmsg("ServerConfig cannot store db objects! (%s)" % value) return self.db_value = pickle.dumps(value) self.save()
def _log_error(self, message): "Try to log errors back to object" if self.log_obj and hasattr(self.log_obj, 'msg'): self.log_obj.msg(message) elif hasattr(self.obj, 'msg'): self.obj.msg(message) else: logger.log_errmsg("%s: %s" % (self.obj, message))
def _save_tree(self): "recursively traverse back up the tree, save when we reach the root" if self._parent: self._parent._save_tree() elif self._db_obj: self._db_obj.value = self else: logger.log_errmsg("_SaverMutable %s has no root Attribute to save to." % self)
def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): """ This helper function is used by the cmdsethandler to load a cmdset instance from a python module, given a python_path. It's usually accessed through the cmdsethandler's add() and add_default() methods. path - This is the full path to the cmdset object on python dot-form cmdsetobj - the database object/typeclass on which this cmdset is to be assigned (this can be also channels and exits, as well as players but there will always be such an object) emit_to_obj - if given, error is emitted to this object (in addition to logging) no_logging - don't log/send error messages. This can be useful if import_cmdset is just used to check if this is a valid python path or not. function returns None if an error was encountered or path not found. """ python_paths = [path] + ["%s.%s" % (prefix, path) for prefix in _CMDSET_PATHS] errstring = "" for python_path in python_paths: try: #print "importing %s: _CACHED_CMDSETS=%s" % (python_path, _CACHED_CMDSETS) wanted_cache_key = python_path cmdsetclass = _CACHED_CMDSETS.get(wanted_cache_key, None) errstring = "" if not cmdsetclass: #print "cmdset '%s' not in cache. Reloading %s on %s." % (wanted_cache_key, python_path, cmdsetobj) # Not in cache. Reload from disk. modulepath, classname = python_path.rsplit('.', 1) module = __import__(modulepath, fromlist=[True]) cmdsetclass = module.__dict__[classname] _CACHED_CMDSETS[wanted_cache_key] = cmdsetclass #instantiate the cmdset (and catch its errors) if callable(cmdsetclass): cmdsetclass = cmdsetclass(cmdsetobj) return cmdsetclass except ImportError: errstring += _("Error loading cmdset: Couldn't import module '%s'.") errstring = errstring % modulepath except KeyError: errstring += _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.") errstring = errstring % {"classname": classname, "modulepath": modulepath} except Exception: errstring += _("Compile/Run error when loading cmdset '%s'. Error was logged.") errstring = errstring % (python_path) if errstring: # returning an empty error cmdset if not no_logging: logger.log_errmsg(errstring) if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"): emit_to_obj.msg(errstring) err_cmdset = _ErrorCmdSet() err_cmdset.errmessage = errstring return err_cmdset
def _save_tree(self): "recursively traverse back up the tree, save when we reach the root" if self._parent: self._parent._save_tree() elif self._db_obj: self._db_obj.value = self else: logger.log_errmsg( "_SaverMutable %s has no root Attribute to save to." % self)
def _step_err_callback(self, e): "callback for runner errors" cname = self.__class__.__name__ estring = _("Script %(key)s(#%(dbid)i) of type '%(cname)s': at_repeat() error '%(err)s'.") % \ {"key":self.key, "dbid":self.dbid, "cname":cname, "err":e.getErrorMessage()} try: self.dbobj.db_obj.msg(estring) except Exception: pass logger.log_errmsg(estring)
def func(err, *args, **kwargs): err.trap(Exception) err = err.getErrorMessage() if use_timeout and err == _PROC_ERR: err = "Process took longer than %ss and timed out." % use_timeout if f: return f(err, *args, **kwargs) else: err = "Error reported from subprocess: '%s'" % err logger.log_errmsg(err)
def alarm(codestring): "store the code of too-long-running scripts" global _LOGGER if not _LOGGER: from src.utils import logger as _LOGGER self.timedout_codestrings.append(codestring) err = "Evlang code '%s' exceeded allowed execution time (>%ss)." % (codestring, timeout) _LOGGER.log_errmsg("EVLANG time exceeded: caller: %s, scripter: %s, code: %s" % (caller, scripter, codestring)) if not self.msg(err, scripter, caller): raise EvlangError(err)
def _step_errback(self, e): "callback for runner errors" cname = self.__class__.__name__ estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \ {"key": self.key, "dbid": self.dbid, "cname": cname, "err": e.getErrorMessage()} try: self.dbobj.db_obj.msg(estring) except Exception: pass logger.log_errmsg(estring)
def _cache_lockfuncs(): "Updates the cache." global _LOCKFUNCS _LOCKFUNCS = {} for modulepath in settings.LOCK_FUNC_MODULES: modulepath = utils.pypath_to_realpath(modulepath) mod = utils.mod_import(modulepath) if mod: for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])): _LOCKFUNCS[tup[0]] = tup[1] else: logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
def has_connection(self, player): """ Checks so this player is actually listening to this channel. """ # also handle object.player calls player, typ = identify_object(player) if typ == 'object': player = player.player player, typ = identify_object(player) if player and not typ == "player": logger.log_errmsg("Channel.has_connection received object of type '%s'. It only accepts players/characters." % typ) return # do the check return PlayerChannelConnection.objects.has_player_connection(player, self)
def add(self, scriptclass, key=None, autostart=True): """ Add an script to this object. scriptclass - either a class object inheriting from Script, an instantiated script object or a python path to such a class object. key - optional identifier for the script (often set in script definition) autostart - start the script upon adding it """ script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart) if not script: logger.log_errmsg("Script %s could not be created and/or started." % scriptclass) return False return True
def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None): """ Create A communication Channel. A Channel serves as a central hub for distributing Msgs to groups of people without specifying the receivers explicitly. Instead players may 'connect' to the channel and follow the flow of messages. By default the channel allows access to all old messages, but this can be turned off with the keep_log switch. key - this must be unique. aliases - list of alternative (likely shorter) keynames. locks - lock string definitions """ global _ChannelDB, _channelhandler if not _ChannelDB: from src.comms.models import ChannelDB as _ChannelDB if not _channelhandler: from src.comms import channelhandler as _channelhandler if not typeclass: typeclass = settings.BASE_CHANNEL_TYPECLASS try: new_channel = _ChannelDB(typeclass=typeclass, db_key=key) new_channel.save() new_channel = new_channel.typeclass if aliases: if not utils.is_iter(aliases): aliases = [aliases] new_channel.aliases.add(aliases) new_channel.save() new_channel.db.desc = desc new_channel.db.keep_log = keep_log except IntegrityError: string = "Could not add channel: key '%s' already exists." % key logger.log_errmsg(string) return None if locks: new_channel.locks.add(locks) new_channel.save() _channelhandler.CHANNELHANDLER.add_channel(new_channel) new_channel.at_channel_create() return new_channel
def clear_contents(self): """ Moves all objects (players/things) to their home location or to default home. """ # Gather up everything that thinks this is its location. objs = ObjectDB.objects.filter(db_location=self) default_home_id = int(settings.DEFAULT_HOME.lstrip("#")) try: default_home = ObjectDB.objects.get(id=default_home_id) if default_home.dbid == _GA(self, "dbid"): # we are deleting default home! default_home = None except Exception: string = _("Could not find default home '(#%d)'.") logger.log_errmsg(string % default_home_id) default_home = None for obj in objs: home = obj.home # Obviously, we can't send it back to here. if not home or (home and home.dbid == _GA(self, "dbid")): obj.home = default_home home = default_home # If for some reason it's still None... if not home: string = "Missing default home, '%s(#%d)' " string += "now has a null location." obj.location = None obj.msg( _("Something went wrong! You are dumped into nowhere. Contact an admin." )) logger.log_errmsg(string % (obj.name, obj.dbid)) return if obj.has_player: if home: string = "Your current location has ceased to exist," string += " moving you to %s(#%d)." obj.msg(_(string) % (home.name, home.dbid)) else: # Famous last words: The player should never see this. string = "This place should not exist ... contact an admin." obj.msg(_(string)) obj.move_to(home)
def clear_contents(self): """ Moves all objects (players/things) to their home location or to default home. """ # Gather up everything that thinks this is its location. objs = ObjectDB.objects.filter(db_location=self) default_home_id = int(settings.DEFAULT_HOME.lstrip("#")) try: default_home = ObjectDB.objects.get(id=default_home_id) if default_home.dbid == _GA(self, "dbid"): # we are deleting default home! default_home = None except Exception: string = _("Could not find default home '(#%d)'.") logger.log_errmsg(string % default_home_id) default_home = None for obj in objs: home = obj.home # Obviously, we can't send it back to here. if not home or (home and home.dbid == _GA(self, "dbid")): obj.home = default_home home = default_home # If for some reason it's still None... if not home: string = "Missing default home, '%s(#%d)' " string += "now has a null location." obj.location = None obj.msg(_("Something went wrong! You are dumped into nowhere. Contact an admin.")) logger.log_errmsg(string % (obj.name, obj.dbid)) return if obj.has_player: if home: string = "Your current location has ceased to exist," string += " moving you to %s(#%d)." obj.msg(_(string) % (home.name, home.dbid)) else: # Famous last words: The player should never see this. string = "This place should not exist ... contact an admin." obj.msg(_(string)) obj.move_to(home)
def __location_set(self, location): "Set location, checking for loops and allowing dbref" if isinstance(location, (basestring, int)): # allow setting of #dbref dbid = dbref(location, reqhash=False) if dbid: try: location = ObjectDB.objects.get(id=dbid) except ObjectDoesNotExist: # maybe it is just a name that happens to look like a dbid pass try: def is_loc_loop(loc, depth=0): "Recursively traverse target location, trying to catch a loop." if depth > 10: return elif loc == self: raise RuntimeError elif loc == None: raise RuntimeWarning return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"), depth + 1) try: is_loc_loop(location) except RuntimeWarning: pass # actually set the field _SA(_GA(self, "dbobj"), "db_location", _GA(location, "dbobj") if location else location) _GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"]) except RuntimeError: errmsg = "Error: %s.location = %s creates a location loop." % ( self.key, location) logger.log_errmsg(errmsg) raise RuntimeError(errmsg) except Exception, e: errmsg = "Error (%s): %s is not a valid location." % (str(e), location) logger.log_errmsg(errmsg) raise Exception(errmsg)
def create_connection(channel, url, interval): """ This will create a new RSS->channel connection """ if not type(channel) == Channel: new_channel = Channel.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel) return False channel = new_channel[0] key = build_connection_key(channel, url) old_conns = ExternalChannelConnection.objects.filter(db_external_key=key) if old_conns: return False config = "%s|%i" % (url, interval) # There is no sendback from evennia to the rss, so we need not define any sendback code. conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_config=config) conn.save() connect_to_rss(conn) return True
def __setattr__(self, propname, value): """ Transparently save data to the dbobj object in all situations. Note that this does not necessarily mean storing it to the database. """ #print "set %s -> %s" % (propname, value) if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return try: dbobj = _GA(self, 'dbobj') except AttributeError: dbobj = None log_trace("This is probably due to an unsafe reload.") if dbobj: _SA(dbobj, propname, value) else: # only as a last resort do we save on the typeclass object _SA(self, propname, value)
def create_connection(channel, imc2_channel): """ This will create a new IMC2<->channel connection. """ if not type(channel) == ChannelDB: new_channel = ChannelDB.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg(_("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found") % channel) return False channel = new_channel[0] key = build_connection_key(channel, imc2_channel) old_conns = ExternalChannelConnection.objects.filter(db_external_key=key) if old_conns: # this evennia channel is already connected to imc. Check # if imc2_channel is different. # connection already exists. We try to only connect a new channel old_config = old_conns[0].db_external_config.split(",") if imc2_channel in old_config: return False # we already listen to this channel else: # We add a new imc2_channel to listen to old_config.append(imc2_channel) old_conns[0].db_external_config = ",".join(old_config) old_conns[0].save() return True else: # no old connection found; create a new one. config = imc2_channel # how the evennia channel will be able to contact this protocol in reverse send_code = "from src.comms.imc2 import IMC2_CLIENT\n" send_code += "data={'channel':from_channel}\n" send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n" conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code, db_external_config=config) conn.save() return True
def __location_set(self, location): "Set location, checking for loops and allowing dbref" if isinstance(location, (basestring, int)): # allow setting of #dbref dbid = dbref(location, reqhash=False) if dbid: try: location = ObjectDB.objects.get(id=dbid) except ObjectDoesNotExist: # maybe it is just a name that happens to look like a dbid pass try: def is_loc_loop(loc, depth=0): "Recursively traverse target location, trying to catch a loop." if depth > 10: return elif loc == self: raise RuntimeError elif loc == None: raise RuntimeWarning return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"), depth + 1) try: is_loc_loop(location) except RuntimeWarning: pass # actually set the field _SA(_GA(self, "dbobj"), "db_location", _GA(location, "dbobj") if location else location) _GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"]) except RuntimeError: errmsg = "Error: %s.location = %s creates a location loop." % (self.key, location) logger.log_errmsg(errmsg) raise RuntimeError(errmsg) except Exception, e: errmsg = "Error (%s): %s is not a valid location." % (str(e), location) logger.log_errmsg(errmsg) raise Exception(errmsg)
def add(self, scriptclass, key=None, autostart=True): """ Add an script to this object. scriptclass - either a class object inheriting from Script, an instantiated script object or a python path to such a class object. key - optional identifier for the script (often set in script definition) autostart - start the script upon adding it """ if self.obj.dbobj.__class__.__name__ == "PlayerDB": # we add to a Player, not an Object script = create.create_script(scriptclass, key=key, player=self.obj, autostart=autostart) else: # the normal - adding to an Object script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart) if not script: logger.log_errmsg("Script %s could not be created and/or started." % scriptclass) return False return True
def oob_data_in(self, data): """ This receives out-of-band data from the Portal. This method parses the data input (a dict) and uses it to launch correct methods from those plugged into the system. data = {funcname: ( [args], {kwargs]), funcname: ( [args], {kwargs}), ...} example: data = {"get_hp": ([], {}), "update_counter", (["counter1"], {"now":True}) } """ outdata = {} entity = self.get_character() if not entity: entity = self.get_player() if not entity: entity = self for funcname, argtuple in data.items(): # loop through the data, calling available functions. func = OOB_FUNC_MODULE.__dict__.get(funcname, None) if func: try: outdata[funcname] = func(entity, *argtuple[0], **argtuple[1]) except Exception: logger.log_trace() else: logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname) if outdata: # we have a result, send it back self.oob_data_out(outdata)
def oob_data_in(self, data): """ This receives out-of-band data from the Portal. OBS - preliminary. OOB not yet functional in Evennia. Don't use. This method parses the data input (a dict) and uses it to launch correct methods from those plugged into the system. data = {oobkey: (funcname, (args), {kwargs}), oobkey: (funcname, (args), {kwargs}), ...} example: data = {"get_hp": ("oob_get_hp, [], {}), "update_counter", ("counter", ["counter1"], {"now":True}) } All function names must be defined in settings.OOB_FUNC_MODULE. Each function will be called with the oobkey and a back-reference to this session as their first two arguments. """ outdata = {} for oobkey, functuple in data.items(): # loop through the data, calling available functions. func = OOB_FUNC_MODULE.__dict__.get(functuple[0]) if func: try: outdata[funcname] = func(oobkey, self, *functuple[1], **functuple[2]) except Exception: logger.log_trace() else: logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname) if outdata: # we have a direct result - send it back right away self.oob_data_out(outdata)
def _errback(self, fail): "Report error" logger.log_errmsg("RSS feed error: %s" % fail.value)
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sessid=None, **kwargs): """ This is the main function to handle any string sent to the engine. called_by - object on which this was called from. This is either a Session, a Player or an Object. raw_string - the command string given on the command line _testing - if we should actually execute the command or not. if True, the command instance will be returned instead. callertype - this is one of "session", "player" or "object", in decending order. So when the Session is the caller, it will merge its own cmdset into cmdsets from both Player and eventual puppeted Object (and cmdsets in its room etc). A Player will only include its own cmdset and the Objects and so on. Merge order is the same order, so that Object cmdsets are merged in last, giving them precendence for same-name and same-prio commands. sessid - Relevant if callertype is "player" - the session id will help retrieve the correct cmdsets from puppeted objects. **kwargs - other keyword arguments will be assigned as named variables on the retrieved command object *before* it is executed. This is unuesed in default Evennia but may be used by code to set custom flags or special operating conditions for a command as it executes. Note that this function returns a deferred! """ raw_string = to_unicode(raw_string, force_string=True) session, player, obj = None, None, None if callertype == "session": session = called_by player = session.player if player: obj = yield _GA(player.dbobj, "get_puppet")(session.sessid) elif callertype == "player": player = called_by if sessid: obj = yield _GA(player.dbobj, "get_puppet")(sessid) elif callertype == "object": obj = called_by else: raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype) # the caller will be the one to receive messages and excert its permissions. # we assign the caller with preference 'bottom up' caller = obj or player or session try: # catch bugs in cmdhandler itself try: # catch special-type commands cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid) if not cmdset: # this is bad and shouldn't happen. raise NoCmdSets unformatted_raw_string = raw_string raw_string = raw_string.strip() if not raw_string: # Empty input. Test for system command instead. syscmd = yield cmdset.get(CMD_NOINPUT) sysarg = "" raise ExecSystemCommand(syscmd, sysarg) # Parse the input string and match to available cmdset. # This also checks for permissions, so all commands in match # are commands the caller is allowed to call. matches = yield _COMMAND_PARSER(raw_string, cmdset, caller) # Deal with matches if len(matches) > 1: # We have a multiple-match syscmd = yield cmdset.get(CMD_MULTIMATCH) sysarg = _("There were multiple matches.") if syscmd: # use custom CMD_MULTIMATCH syscmd.matches = matches else: # fall back to default error handling sysarg = yield at_multimatch_cmd(caller, matches) raise ExecSystemCommand(syscmd, sysarg) if len(matches) == 1: # We have a unique command match. But it may still be invalid. match = matches[0] cmdname, args, cmd = match[0], match[1], match[2] # check if we allow this type of command if cmdset.no_channels and hasattr(cmd, "is_channel") and cmd.is_channel: matches = [] if cmdset.no_exits and hasattr(cmd, "is_exit") and cmd.is_exit: matches = [] if not matches: # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: # use custom CMD_NOMATH command sysarg = raw_string else: # fallback to default error text sysarg = _("Command '%s' is not available.") % raw_string suggestions = string_suggestions(raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3) if suggestions: sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True) else: sysarg += _(" Type \"help\" for help.") raise ExecSystemCommand(syscmd, sysarg) # Check if this is a Channel-cmd match. if hasattr(cmd, 'is_channel') and cmd.is_channel: # even if a user-defined syscmd is not defined, the # found cmd is already a system command in its own right. syscmd = yield cmdset.get(CMD_CHANNEL) if syscmd: # replace system command with custom version cmd = syscmd cmd.sessid = session.sessid if session else None sysarg = "%s:%s" % (cmdname, args) raise ExecSystemCommand(cmd, sysarg) # A normal command. # Assign useful variables to the instance cmd.caller = caller cmd.cmdstring = cmdname cmd.args = args cmd.cmdset = cmdset cmd.sessid = session.sessid if session else sessid cmd.session = session cmd.player = player cmd.raw_string = unformatted_raw_string #cmd.obj # set via on-object cmdset handler for each command, # since this may be different for every command when # merging multuple cmdsets if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): # cmd.obj is automatically made available by the cmdhandler. # we make sure to validate its scripts. yield cmd.obj.scripts.validate() if _testing: # only return the command instance returnValue(cmd) # assign custom kwargs to found cmd object for key, val in kwargs.items(): setattr(cmd, key, val) # pre-command hook abort = yield cmd.at_pre_cmd() if abort: # abort sequence returnValue(abort) # Parse and execute yield cmd.parse() # (return value is normally None) ret = yield cmd.func() # post-command hook yield cmd.at_post_cmd() if cmd.save_for_next: # store a reference to this command, possibly # accessible by the next command. caller.ndb.last_cmd = yield copy(cmd) else: caller.ndb.last_cmd = None # Done! This returns a deferred. By default, Evennia does # not use this at all. returnValue(ret) except ExecSystemCommand, exc: # Not a normal command: run a system command, if available, # or fall back to a return string. syscmd = exc.syscmd sysarg = exc.sysarg if syscmd: syscmd.caller = caller syscmd.cmdstring = syscmd.key syscmd.args = sysarg syscmd.cmdset = cmdset syscmd.sessid = session.sessid if session else None syscmd.raw_string = unformatted_raw_string if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'): # cmd.obj is automatically made available. # we make sure to validate its scripts. yield syscmd.obj.scripts.validate() if _testing: # only return the command instance returnValue(syscmd) # parse and run the command yield syscmd.parse() yield syscmd.func() elif sysarg: # return system arg caller.msg(exc.sysarg) except NoCmdSets: # Critical error. string = "No command sets found! This is a sign of a critical bug.\n" string += "The error was logged.\n" string += "If logging out/in doesn't solve the problem, try to " string += "contact the server admin through some other means " string += "for assistance." caller.msg(_(string)) logger.log_errmsg("No cmdsets found: %s" % caller) except Exception: # We should not end up here. If we do, it's a programming bug. string = "%s\nAbove traceback is from an untrapped error." string += " Please file a bug report." logger.log_trace(_(string)) caller.msg(string % format_exc())
def func(self): "Do checks and create account" session = self.caller args = self.args.strip() # extract quoted parts parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()] if len(parts) == 1: # this was (hopefully) due to no quotes being found parts = parts[0].split(None, 1) if len(parts) != 2: string = "\n Usage (without <>): create <name> <password>" string += "\nIf <name> or <password> contains spaces, enclose it in quotes." session.msg(string) return playername, password = parts # sanity checks if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): # this echoes the restrictions made by django's auth # module (except not allowing spaces, for convenience of # logging in). string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only." session.msg(string) return # strip excessive spaces in playername playername = re.sub(r"\s+", " ", playername).strip() if PlayerDB.objects.filter(username__iexact=playername): # player already exists (we also ignore capitalization here) session.msg("Sorry, there is already a player with the name '%s'." % playername) return if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)): string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only." string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of" string += "\nmany words if you enclose the password in quotes." session.msg(string) return # everything's ok. Create the new player account. try: default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) typeclass = settings.BASE_CHARACTER_TYPECLASS permissions = settings.PERMISSION_PLAYER_DEFAULT try: new_player = create.create_player(playername, None, password, permissions=permissions) except Exception, e: session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e) logger.log_trace() return # This needs to be called so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) utils.init_new_player(new_player) # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: pchannel = ChannelDB.objects.get_channel(pchanneldef[0]) if not pchannel.connect(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) if MULTISESSION_MODE < 2: # if we only allow one character, create one with the same name as Player # (in mode 2, the character must be created manually once logging in) start_location = ObjectDB.objects.get_id(settings.START_LOCATION) if not start_location: start_location = default_home # fallback new_character = create.create_object(typeclass, key=playername, location=start_location, home=default_home, permissions=permissions) # set playable character list new_player.db._playable_characters.append(new_character) # allow only the character itself and the player to puppet this character (and Immortals). new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" % (new_character.id, new_player.id)) # If no description is set, set a default description if not new_character.db.desc: new_character.db.desc = "This is a Player." # We need to set this to have @ic auto-connect to this character new_player.db._last_puppet = new_character # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" if " " in playername: string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'." else: string += "\n\nYou can now log with the command 'connect %s <your password>'." session.msg(string % (playername, playername))
def func(self): "Do checks and create account" session = self.caller args = self.args.strip() # extract quoted parts parts = [ part.strip() for part in re.split(r"\"|\'", args) if part.strip() ] if len(parts) == 1: # this was (hopefully) due to no quotes being found parts = parts[0].split(None, 1) if len(parts) != 2: string = "\n Usage (without <>): create <name> <password>" string += "\nIf <name> or <password> contains spaces, enclose it in quotes." session.msg(string) return playername, password = parts print "playername '%s', password: '******'" % (playername, password) # sanity checks if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): # this echoes the restrictions made by django's auth module (except not # allowing spaces, for convenience of logging in). string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only." session.msg(string) return # strip excessive spaces in playername playername = re.sub(r"\s+", " ", playername).strip() if PlayerDB.objects.filter( user__username__iexact=playername) or User.objects.filter( username__iexact=playername): # player already exists (we also ignore capitalization here) session.msg( "Sorry, there is already a player with the name '%s'." % playername) return if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)): string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only." string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of" string += "\nmany words if you enclose the password in quotes." session.msg(string) return # everything's ok. Create the new player account. try: default_home = ObjectDB.objects.get_id( settings.CHARACTER_DEFAULT_HOME) typeclass = settings.BASE_CHARACTER_TYPECLASS permissions = settings.PERMISSION_PLAYER_DEFAULT try: new_character = create.create_player( playername, None, password, permissions=permissions, character_typeclass=typeclass, character_location=default_home, character_home=default_home) except Exception: session.msg( "There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin." ) return new_player = new_character.player # This needs to be called so the engine knows this player is logging in for the first time. # (so it knows to call the right hooks during login later) utils.init_new_player(new_player) # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: pchannel = Channel.objects.get_channel(pchanneldef[0]) if not pchannel.connect_to(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) # allow only the character itself and the player to puppet this character (and Immortals). new_character.locks.add( "puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" % (new_character.id, new_player.id)) # If no description is set, set a default description if not new_character.db.desc: new_character.db.desc = "This is a Player." # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" if " " in playername: string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'." else: string += "\n\nYou can now log with the command 'connect %s <your password>'." session.msg(string % (playername, playername)) except Exception: # We are in the middle between logged in and -not, so we have to handle tracebacks # ourselves at this point. If we don't, we won't see any errors at all. string = "%s\nThis is a bug. Please e-mail an admin if the problem persists." session.msg(string % (traceback.format_exc())) logger.log_errmsg(traceback.format_exc())
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sessid=None, **kwargs): """ This is the main function to handle any string sent to the engine. called_by - object on which this was called from. This is either a Session, a Player or an Object. raw_string - the command string given on the command line _testing - if we should actually execute the command or not. if True, the command instance will be returned instead. callertype - this is one of "session", "player" or "object", in decending order. So when the Session is the caller, it will merge its own cmdset into cmdsets from both Player and eventual puppeted Object (and cmdsets in its room etc). A Player will only include its own cmdset and the Objects and so on. Merge order is the same order, so that Object cmdsets are merged in last, giving them precendence for same-name and same-prio commands. sessid - Relevant if callertype is "player" - the session id will help retrieve the correct cmdsets from puppeted objects. **kwargs - other keyword arguments will be assigned as named variables on the retrieved command object *before* it is executed. This is unuesed in default Evennia but may be used by code to set custom flags or special operating conditions for a command as it executes. Note that this function returns a deferred! """ raw_string = to_unicode(raw_string, force_string=True) session, player, obj = None, None, None if callertype == "session": session = called_by player = session.player if player: obj = yield _GA(player.dbobj, "get_puppet")(session.sessid) elif callertype == "player": player = called_by if sessid: obj = yield _GA(player.dbobj, "get_puppet")(sessid) elif callertype == "object": obj = called_by else: raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype) # the caller will be the one to receive messages and excert its permissions. # we assign the caller with preference 'bottom up' caller = obj or player or session try: # catch bugs in cmdhandler itself try: # catch special-type commands cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid) if not cmdset: # this is bad and shouldn't happen. raise NoCmdSets unformatted_raw_string = raw_string raw_string = raw_string.strip() if not raw_string: # Empty input. Test for system command instead. syscmd = yield cmdset.get(CMD_NOINPUT) sysarg = "" raise ExecSystemCommand(syscmd, sysarg) # Parse the input string and match to available cmdset. # This also checks for permissions, so all commands in match # are commands the caller is allowed to call. matches = yield _COMMAND_PARSER(raw_string, cmdset, caller) # Deal with matches if len(matches) > 1: # We have a multiple-match syscmd = yield cmdset.get(CMD_MULTIMATCH) sysarg = _("There were multiple matches.") if syscmd: # use custom CMD_MULTIMATCH syscmd.matches = matches else: # fall back to default error handling sysarg = yield at_multimatch_cmd(caller, matches) raise ExecSystemCommand(syscmd, sysarg) if len(matches) == 1: # We have a unique command match. But it may still be invalid. match = matches[0] cmdname, args, cmd = match[0], match[1], match[2] # check if we allow this type of command if cmdset.no_channels and hasattr( cmd, "is_channel") and cmd.is_channel: matches = [] if cmdset.no_exits and hasattr(cmd, "is_exit") and cmd.is_exit: matches = [] if not matches: # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: # use custom CMD_NOMATH command sysarg = raw_string else: # fallback to default error text sysarg = _("Command '%s' is not available.") % raw_string suggestions = string_suggestions( raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3) if suggestions: sysarg += _( " Maybe you meant %s?") % utils.list_to_string( suggestions, _('or'), addquote=True) else: sysarg += _(" Type \"help\" for help.") raise ExecSystemCommand(syscmd, sysarg) # Check if this is a Channel-cmd match. if hasattr(cmd, 'is_channel') and cmd.is_channel: # even if a user-defined syscmd is not defined, the # found cmd is already a system command in its own right. syscmd = yield cmdset.get(CMD_CHANNEL) if syscmd: # replace system command with custom version cmd = syscmd cmd.sessid = session.sessid if session else None sysarg = "%s:%s" % (cmdname, args) raise ExecSystemCommand(cmd, sysarg) # A normal command. # Assign useful variables to the instance cmd.caller = caller cmd.cmdstring = cmdname cmd.args = args cmd.cmdset = cmdset cmd.sessid = session.sessid if session else sessid cmd.session = session cmd.player = player cmd.raw_string = unformatted_raw_string #cmd.obj # set via on-object cmdset handler for each command, # since this may be different for every command when # merging multuple cmdsets if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): # cmd.obj is automatically made available by the cmdhandler. # we make sure to validate its scripts. yield cmd.obj.scripts.validate() if _testing: # only return the command instance returnValue(cmd) # assign custom kwargs to found cmd object for key, val in kwargs.items(): setattr(cmd, key, val) # pre-command hook abort = yield cmd.at_pre_cmd() if abort: # abort sequence returnValue(abort) # Parse and execute yield cmd.parse() # (return value is normally None) ret = yield cmd.func() # post-command hook yield cmd.at_post_cmd() if cmd.save_for_next: # store a reference to this command, possibly # accessible by the next command. caller.ndb.last_cmd = yield copy(cmd) else: caller.ndb.last_cmd = None # Done! This returns a deferred. By default, Evennia does # not use this at all. returnValue(ret) except ExecSystemCommand, exc: # Not a normal command: run a system command, if available, # or fall back to a return string. syscmd = exc.syscmd sysarg = exc.sysarg if syscmd: syscmd.caller = caller syscmd.cmdstring = syscmd.key syscmd.args = sysarg syscmd.cmdset = cmdset syscmd.sessid = session.sessid if session else None syscmd.raw_string = unformatted_raw_string if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'): # cmd.obj is automatically made available. # we make sure to validate its scripts. yield syscmd.obj.scripts.validate() if _testing: # only return the command instance returnValue(syscmd) # parse and run the command yield syscmd.parse() yield syscmd.func() elif sysarg: # return system arg caller.msg(exc.sysarg) except NoCmdSets: # Critical error. string = "No command sets found! This is a sign of a critical bug.\n" string += "The error was logged.\n" string += "If logging out/in doesn't solve the problem, try to " string += "contact the server admin through some other means " string += "for assistance." caller.msg(_(string)) logger.log_errmsg("No cmdsets found: %s" % caller) except Exception: # We should not end up here. If we do, it's a programming bug. string = "%s\nAbove traceback is from an untrapped error." string += " Please file a bug report." logger.log_trace(_(string)) caller.msg(string % format_exc())
def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): """ This helper function is used by the cmdsethandler to load a cmdset instance from a python module, given a python_path. It's usually accessed through the cmdsethandler's add() and add_default() methods. path - This is the full path to the cmdset object on python dot-form cmdsetobj - the database object/typeclass on which this cmdset is to be assigned (this can be also channels and exits, as well as players but there will always be such an object) emit_to_obj - if given, error is emitted to this object (in addition to logging) no_logging - don't log/send error messages. This can be useful if import_cmdset is just used to check if this is a valid python path or not. function returns None if an error was encountered or path not found. """ python_paths = [path ] + ["%s.%s" % (prefix, path) for prefix in _CMDSET_PATHS] errstring = "" for python_path in python_paths: try: #print "importing %s: _CACHED_CMDSETS=%s" % (python_path, _CACHED_CMDSETS) wanted_cache_key = python_path cmdsetclass = _CACHED_CMDSETS.get(wanted_cache_key, None) errstring = "" if not cmdsetclass: #print "cmdset '%s' not in cache. Reloading %s on %s." % (wanted_cache_key, python_path, cmdsetobj) # Not in cache. Reload from disk. modulepath, classname = python_path.rsplit('.', 1) module = __import__(modulepath, fromlist=[True]) cmdsetclass = module.__dict__[classname] _CACHED_CMDSETS[wanted_cache_key] = cmdsetclass #instantiate the cmdset (and catch its errors) if callable(cmdsetclass): cmdsetclass = cmdsetclass(cmdsetobj) return cmdsetclass except ImportError: errstring += _( "Error loading cmdset: Couldn't import module '%s'.") errstring = errstring % modulepath except KeyError: errstring += _( "Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s." ) errstring = errstring % { "classname": classname, "modulepath": modulepath } except Exception: errstring += _( "Compile/Run error when loading cmdset '%s'. Error was logged." ) errstring = errstring % (python_path) if errstring: # returning an empty error cmdset if not no_logging: logger.log_errmsg(errstring) if emit_to_obj and not ServerConfig.objects.conf( "server_starting_mode"): emit_to_obj.msg(errstring) err_cmdset = _ErrorCmdSet() err_cmdset.errmessage = errstring return err_cmdset
def clientConnectionFailed(self, connector, reason): msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason} msg_info(msg) logger.log_errmsg(msg)
def func(self): "Do checks and create account" session = self.caller args = self.args.strip() # extract quoted parts parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()] if len(parts) == 1: # this was (hopefully) due to no quotes being found parts = parts[0].split(None, 1) if len(parts) != 2: string = "\n Usage (without <>): create <name> <password>" string += "\nIf <name> or <password> contains spaces, enclose it in quotes." session.msg(string) return playername, password = parts print "playername '%s', password: '******'" % (playername, password) # sanity checks if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): # this echoes the restrictions made by django's auth module (except not # allowing spaces, for convenience of logging in). string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only." session.msg(string) return # strip excessive spaces in playername playername = re.sub(r"\s+", " ", playername).strip() if PlayerDB.objects.filter(user__username__iexact=playername) or PlayerDB.objects.filter(username__iexact=playername): # player already exists (we also ignore capitalization here) session.msg("Sorry, there is already a player with the name '%s'." % playername) return if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)): string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only." string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of" string += "\nmany words if you enclose the password in quotes." session.msg(string) return # everything's ok. Create the new player account. try: default_home = ObjectDB.objects.get_id(settings.CHARACTER_DEFAULT_HOME) typeclass = settings.BASE_CHARACTER_TYPECLASS permissions = settings.PERMISSION_PLAYER_DEFAULT try: new_character = create.create_player(playername, None, password, permissions=permissions, character_typeclass=typeclass, character_location=default_home, character_home=default_home) except Exception: session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.") return new_player = new_character.player # This needs to be called so the engine knows this player is logging in for the first time. # (so it knows to call the right hooks during login later) utils.init_new_player(new_player) # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: pchannel = Channel.objects.get_channel(pchanneldef[0]) if not pchannel.connect_to(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) # allow only the character itself and the player to puppet this character (and Immortals). new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" % (new_character.id, new_player.id)) # If no description is set, set a default description if not new_character.db.desc: new_character.db.desc = "This is a Player." # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" if " " in playername: string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'." else: string += "\n\nYou can now log with the command 'connect %s <your password>'." session.msg(string % (playername, playername)) except Exception: # We are in the middle between logged in and -not, so we have to handle tracebacks # ourselves at this point. If we don't, we won't see any errors at all. string = "%s\nThis is a bug. Please e-mail an admin if the problem persists." session.msg(string % (traceback.format_exc())) logger.log_errmsg(traceback.format_exc())
def errback(fail): logger.log_errmsg(fail.value)
"modulepath": modulepath } except SyntaxError, e: logger.log_trace() errstring += _( "SyntaxError encountered when loading cmdset '%s': %s.") errstring = errstring % (modulepath, e) except Exception, e: logger.log_trace() errstring += _("Compile/Run error when loading cmdset '%s': %s.") errstring = errstring % (python_path, e) if errstring: # returning an empty error cmdset if not no_logging: logger.log_errmsg(errstring) if emit_to_obj and not ServerConfig.objects.conf( "server_starting_mode"): emit_to_obj.msg(errstring) err_cmdset = _ErrorCmdSet() err_cmdset.errmessage = errstring + _("\n (See log for details.)") return err_cmdset # classes class CmdSetHandler(object): """ The CmdSetHandler is always stored on an object, this object is supplied as an argument.
def clientConnectionLost(self, connector, reason): message = _('Connection lost: %s') % reason.getErrorMessage() msg_info(message) logger.log_errmsg('IMC2: %s' % message)
def func(self): "Do checks and create account" session = self.caller args = self.args.strip() # extract quoted parts parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()] if len(parts) == 1: # this was (hopefully) due to no quotes being found parts = parts[0].split(None, 1) if len(parts) != 2: string = "\n Usage (without <>): create <name> <password>" string += "\nIf <name> or <password> contains spaces, enclose it in quotes." session.msg(string) return playername, password = parts # sanity checks if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): # this echoes the restrictions made by django's auth # module (except not allowing spaces, for convenience of # logging in). string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only." session.msg(string) return # strip excessive spaces in playername playername = re.sub(r"\s+", " ", playername).strip() if PlayerDB.objects.filter(username__iexact=playername): # player already exists (we also ignore capitalization here) session.msg("Sorry, there is already a player with the name '%s'." % playername) return # Reserve playernames found in GUEST_LIST if settings.GUEST_LIST and playername.lower() in map(str.lower, settings.GUEST_LIST): string = "\n\r That name is reserved. Please choose another Playername." session.msg(string) return if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)): string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only." string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of" string += "\nmany words if you enclose the password in quotes." session.msg(string) return # Check IP and/or name bans bans = ServerConfig.objects.conf("server_bans") if bans and (any(tup[0]==playername.lower() for tup in bans) or any(tup[2].match(session.address) for tup in bans if tup[2])): # this is a banned IP or name! string = "{rYou have been banned and cannot continue from here." string += "\nIf you feel this ban is in error, please email an admin.{x" session.msg(string) session.execute_cmd("quit") return # everything's ok. Create the new player account. try: default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) permissions = settings.PERMISSION_PLAYER_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_player = _create_player(session, playername, password, default_home, permissions) start_location = ObjectDB.objects.get_id(settings.START_LOCATION) if new_player: if MULTISESSION_MODE < 2: _create_character(session, new_player, typeclass, start_location, default_home, permissions) # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" if " " in playername: string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'." else: string += "\n\nYou can now log with the command 'connect %s <your password>'." session.msg(string % (playername, playername)) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, # we won't see any errors at all. string = "%s\nThis is a bug. Please e-mail an admin if the problem persists." session.msg(string % (traceback.format_exc())) logger.log_errmsg(traceback.format_exc())
def func(self): """ Uses the Django admin api. Note that unlogged-in commands have a unique position in that their func() receives a session object instead of a source_object like all other types of logged-in commands (this is because there is no object yet before the player has logged in) """ session = self.caller args = self.args # extract quoted parts parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()] if len(parts) == 1: # this was (hopefully) due to no quotes being found, or a guest login parts = parts[0].split(None, 1) # Guest login if len(parts) == 1 and parts[0].lower() == "guest" and settings.GUEST_ENABLED: try: # Find an available guest name. for playername in settings.GUEST_LIST: if not PlayerDB.objects.filter(username__iexact=playername): break playername = None if playername == None: session.msg("All guest accounts are in use. Please try again later.") return password = "******" % getrandbits(64) home = ObjectDB.objects.get_id(settings.GUEST_HOME) permissions = settings.PERMISSION_GUEST_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS ptypeclass = settings.BASE_GUEST_TYPECLASS start_location = ObjectDB.objects.get_id(settings.GUEST_START_LOCATION) new_player = _create_player(session, playername, password, home, permissions, ptypeclass) if new_player: _create_character(session, new_player, typeclass, start_location, home, permissions) session.sessionhandler.login(session, new_player) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, # we won't see any errors at all. string = "%s\nThis is a bug. Please e-mail an admin if the problem persists." session.msg(string % (traceback.format_exc())) logger.log_errmsg(traceback.format_exc()) finally: return if len(parts) != 2: session.msg("\n\r Usage (without <>): connect <name> <password>") return playername, password = parts # Match account name and check password player = PlayerDB.objects.get_player_from_name(playername) pswd = None if player: pswd = player.check_password(password) if not (player and pswd): # No playername or password match string = "Wrong login information given.\nIf you have spaces in your name or " string += "password, don't forget to enclose it in quotes. Also capitalization matters." string += "\nIf you are new you should first create a new account " string += "using the 'create' command." session.msg(string) return # Check IP and/or name bans bans = ServerConfig.objects.conf("server_bans") if bans and (any(tup[0]==player.name.lower() for tup in bans) or any(tup[2].match(session.address) for tup in bans if tup[2])): # this is a banned IP or name! string = "{rYou have been banned and cannot continue from here." string += "\nIf you feel this ban is in error, please email an admin.{x" session.msg(string) session.execute_cmd("quit") return # actually do the login. This will call all other hooks: # session.at_login() # player.at_init() # always called when object is loaded from disk # player.at_pre_login() # player.at_first_login() # only once # player.at_post_login(sessid=sessid) session.sessionhandler.login(session, player)
session.msg("There was an error creating the Player:\n%s\n If this problem persists, contact an admin." % e) logger.log_trace() return False # This needs to be called so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) utils.init_new_player(new_player) # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: pchannel = ChannelDB.objects.get_channel(pchanneldef[0]) if not pchannel.connect(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) return new_player def _create_character(session, new_player, typeclass, start_location, home, permissions): """ Helper function, creates a character based on a player's name. This is meant for Guest and MULTISESSION_MODE < 2 situations. """ try: if not start_location: start_location = home # fallback new_character = create.create_object(typeclass, key=new_player.key, location=start_location, home=home, permissions=permissions) # set playable character list
def func(self): "Do checks and create account" session = self.caller try: playername, email, password = self.playerinfo except ValueError: string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>" session.msg(string) return if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): session.msg( "\n\r Playername can max be 30 characters or fewer. Letters, spaces, dig\ its and @/./+/-/_ only." ) # this echoes the restrictions made by django's auth module. return if not email or not password: session.msg( "\n\r You have to supply an e-mail address followed by a password." ) return if not utils.validate_email_address(email): # check so the email at least looks ok. session.msg("'%s' is not a valid e-mail address." % email) return # Run sanity and security checks if PlayerDB.objects.filter(username=playername): # player already exists session.msg( "Sorry, there is already a player with the name '%s'." % playername) return if PlayerDB.objects.get_player_from_email(email): # email already set on a player session.msg( "Sorry, there is already a player with that email address.") return if len(password) < 3: # too short password string = "Your password must be at least 3 characters or longer." string += "\n\rFor best security, make it at least 8 characters long, " string += "avoid making it a real word and mix numbers into it." session.msg(string) return # everything's ok. Create the new player account. try: default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) typeclass = settings.BASE_CHARACTER_TYPECLASS permissions = settings.PERMISSION_PLAYER_DEFAULT try: new_player = create.create_player(playername, email, password, permissions=permissions) except Exception, e: session.msg( "There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e) logger.log_trace() return # This needs to be called so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) utils.init_new_player(new_player) # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: pchannel = ChannelDB.objects.get_channel(pchanneldef[0]) if not pchannel.connect(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) if MULTISESSION_MODE < 2: # if we only allow one character, create one with the same name as Player # (in mode 2, the character must be created manually once logging in) new_character = create.create_object(typeclass, key=playername, location=default_home, home=default_home, permissions=permissions) # set playable character list new_player.db._playable_characters.append(new_character) # allow only the character itself and the player to puppet this character (and Immortals). new_character.locks.add( "puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" % (new_character.id, new_player.id)) # If no description is set, set a default description if not new_character.db.desc: new_character.db.desc = "This is a Player." # We need to set this to have @ic auto-connect to this character new_player.db._last_puppet = new_character # tell the caller everything went well. string = "A new account '%s' was created. Welcome!" if " " in playername: string += "\n\nYou can now log in with the command 'connect %s <your password>'." else: string += "\n\nYou can now log with the command 'connect %s <your password>'." session.msg(string % (playername, email))