def _validate(data): "Helper function to convert data to AMP-safe (picketable) values" if isinstance(data, dict): newdict = {} for key, part in data.items(): newdict[key] = _validate(part) return newdict elif hasattr(data, "__iter__"): return [_validate(part) for part in data] elif isinstance(data, basestring): # make sure strings are in a valid encoding try: data = data and to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) except LookupError: # wrong encoding set on the session. Set it to a safe one session.protocol_flags["ENCODING"] = "utf-8" data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler): # only parse inlinefuncs on the outgoing path (sessionhandler->) data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) return data elif hasattr(data, "id") and hasattr(data, "db_date_created") \ and hasattr(data, '__dbclass__'): # convert database-object to their string representation. return _validate(unicode(data)) else: return data
def _validate(data): "Helper function to convert data to AMP-safe (picketable) values" if isinstance(data, dict): newdict = {} for key, part in data.items(): newdict[key] = _validate(part) return newdict elif hasattr(data, "__iter__"): return [_validate(part) for part in data] elif isinstance(data, basestring): # make sure strings are in a valid encoding try: data = data and to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) except LookupError: # wrong encoding set on the session. Set it to a safe one session.protocol_flags["ENCODING"] = "utf-8" data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler): # only parse inlinefuncs on the outgoing path (sessionhandler->) data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) # At this point the object is certainly the right encoding, but may still be a unicode object-- # to_str does not actually force objects to become bytestrings. # If the unicode object is a subclass of unicode, such as ANSIString, this can cause a problem, # as special behavior for that class will still be in play. Since we're now transferring raw data, # we must now force this to be a proper bytestring. return str(data) elif hasattr(data, "id") and hasattr(data, "db_date_created") \ and hasattr(data, '__dbclass__'): # convert database-object to their string representation. return _validate(unicode(data)) else: return data
def data_in(self, sessid, text="", **kwargs): """ Data Portal -> Server. We also intercept OOB communication here. Args: sessid (int): Session id. Kwargs: text (str): Text from protocol. kwargs (any): Other data from protocol. """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_in") session = self.sessions.get(sessid, None) if session: text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding) if "oob" in kwargs: # incoming data is always on the form (cmdname, args, kwargs) global _OOB_HANDLER if not _OOB_HANDLER: from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER funcname, args, kwargs = kwargs.pop("oob") #print "OOB session.data_in:", funcname, args, kwargs if funcname: _OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs) # pass the rest off to the session session.data_in(text=text, **kwargs)
def validate_encoding(new_encoding): # helper: change encoding try: utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding) except LookupError: raise RuntimeError("The encoding '|w%s|n' is invalid. " % new_encoding) return val
def reload(self, filename=None, form=None, **kwargs): """ Creates the form from a stored file name """ # clean kwargs (these cannot be overridden) kwargs.pop("enforce_size", None) kwargs.pop("width", None) kwargs.pop("height", None) if form or self.input_form_dict: datadict = form if form else self.input_form_dict self.input_form_dict = datadict elif filename or self.filename: filename = filename if filename else self.filename datadict = all_from_module(filename) self.filename = filename else: datadict = {} cellchar = to_str(datadict.get("FORMCHAR", "x")) self.cellchar = to_str(cellchar[0] if len(cellchar) > 1 else cellchar) tablechar = datadict.get("TABLECHAR", "c") self.tablechar = tablechar[0] if len(tablechar) > 1 else tablechar # split into a list of list of lines. Form can be indexed with form[iy][ix] self.raw_form = _to_ansi(to_unicode(datadict.get("FORM", "")).split("\n")) # strip first line self.raw_form = self.raw_form[1:] if self.raw_form else self.raw_form self.options.update(kwargs) # parse and replace self.mapping = self._parse_rectangles(self.cellchar, self.tablechar, self.raw_form, **kwargs) self.form = self._populate_form(self.raw_form, self.mapping)
def validate_encoding(val): # helper: change encoding try: to_str(to_unicode("test-string"), encoding=val) except LookupError: raise RuntimeError("The encoding '|w%s|n' is invalid. " % val) return val
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 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 evennia.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 execute_cmd(self, raw_string, session=None, **kwargs): """ Do something as this account. This method is never called normally, but only when the account object itself is supposed to execute the command. It takes account nicks into account, but not nicks of eventual puppets. Args: raw_string (str): Raw command input coming from the command line. session (Session, optional): The session to be responsible for the command-send Kwargs: kwargs (any): Other keyword arguments will be added to the found command object instance as variables before it executes. This is unused by default Evennia but may be used to set flags and change operating paramaters for commands at run-time. """ raw_string = to_unicode(raw_string) raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_account=False) if not session and _MULTISESSION_MODE in (0, 1): # for these modes we use the first/only session sessions = self.sessions.get() session = sessions[0] if sessions else None return cmdhandler.cmdhandler(self, raw_string, callertype="account", session=session, **kwargs)
def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None): """ Get objects with a specific field name and value. Args: property_name (str): Field name to search for. property_value (any): Value required for field with `property_name` to have. candidates (list, optional): List of objects to limit search to. typeclasses (list, optional): 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 querykwargs = {property_name: property_value} cand_restriction = candidates is not 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 evennia.utils import logger logger.log_err("The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value))) return []
def execute_cmd(self, raw_string, sessid=None, **kwargs): """ Do something as this player. This method is never called normally, but only when the player object itself is supposed to execute the command. It takes player nicks into account, but not nicks of eventual puppets. raw_string - raw command input coming from the command line. sessid - the optional session id to be responsible for the command-send **kwargs - other keyword arguments will be added to the found command object instace as variables before it executes. This is unused by default Evennia but may be used to set flags and change operating paramaters for commands at run-time. """ raw_string = to_unicode(raw_string) raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=False) if not sessid and _MULTISESSION_MODE in (0, 1): # in this case, we should either have only one sessid, or the sessid # should not matter (since the return goes to all of them we can # just use the first one as the source) try: sessid = self.get_all_sessions()[0].sessid except IndexError: # this can happen for bots sessid = None return cmdhandler.cmdhandler(self, raw_string, callertype="player", sessid=sessid, **kwargs)
def execute_cmd(self, raw_string, session=None, **kwargs): """ Do something as this player. This method is never called normally, but only when the player object itself is supposed to execute the command. It takes player nicks into account, but not nicks of eventual puppets. Args: raw_string (str): Raw command input coming from the command line. session (Session, optional): The session to be responsible for the command-send Kwargs: kwargs (any): Other keyword arguments will be added to the found command object instance as variables before it executes. This is unused by default Evennia but may be used to set flags and change operating paramaters for commands at run-time. """ raw_string = to_unicode(raw_string) raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=False) if not session and _MULTISESSION_MODE in (0, 1): # for these modes we use the first/only session sessions = self.sessions.get() session = sessions[0] if sessions else None return cmdhandler.cmdhandler(self, raw_string, callertype="player", session=session, **kwargs)
def get_object_with_player(self, ostring, exact=True, candidates=None): """ Search for an object based on its player's name or dbref. This search is sometimes initiated by appending an `*` to the beginning of the search criterion (e.g. in local_and_global_search). search_string: (string) The name or dbref to search for. """ ostring = to_unicode(ostring).lstrip('*') # simplest case - search by dbref dbref = self.dbref(ostring) if dbref: return dbref # not a dbref. Search by name. cand_restriction = candidates != None and Q( pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() if exact: return self.filter(cand_restriction & Q(db_player__username__iexact=ostring)) else: # fuzzy matching ply_cands = self.filter(cand_restriction & Q( playerdb__username__istartswith=ostring)).values_list( "db_key", flat=True) if candidates: index_matches = string_partial_matching(ply_cands, ostring, ret_index=True) return [ obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches ] else: return string_partial_matching(ply_cands, ostring, ret_index=False)
def validate_encoding(val): # helper: change encoding try: utils.to_str(utils.to_unicode("test-string"), encoding=val) except LookupError: raise RuntimeError("The encoding '|w%s|n' is invalid. " % val) return val
def get_object_with_player(self, ostring, exact=True, candidates=None): """ Search for an object based on its player's name or dbref. Args: ostring (str or int): Search criterion or dbref. Searching for a player is sometimes initiated by appending an `*` to the beginning of the search criterion (e.g. in local_and_global_search). This is stripped here. exact (bool, optional): Require an exact player match. candidates (list, optional): Only search among this list of possible object candidates. Return: match (Object or list): One or more matching results. """ ostring = to_unicode(ostring).lstrip('*') # simplest case - search by dbref dbref = self.dbref(ostring) if dbref: return dbref # not a dbref. Search by name. cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() if exact: return self.filter(cand_restriction & Q(db_player__username__iexact=ostring)) else: # fuzzy matching ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True) if candidates: index_matches = string_partial_matching(ply_cands, ostring, ret_index=True) return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches] else: return string_partial_matching(ply_cands, ostring, ret_index=False)
def __new__(cls, *args, **kwargs): """ When creating a new ANSIString, you may use a custom parser that has the same attributes as the standard one, and you may declare the string to be handled as already decoded. It is important not to double decode strings, as escapes can only be respected once. Internally, ANSIString can also passes itself precached code/character indexes and clean strings to avoid doing extra work when combining ANSIStrings. """ string = args[0] if not isinstance(string, basestring): string = to_str(string, force_string=True) parser = kwargs.get('parser', ANSI_PARSER) decoded = kwargs.get('decoded', False) or hasattr( string, '_raw_string') code_indexes = kwargs.pop('code_indexes', None) char_indexes = kwargs.pop('char_indexes', None) clean_string = kwargs.pop('clean_string', None) # All True, or All False, not just one. checks = [ x is None for x in [code_indexes, char_indexes, clean_string] ] if not len(set(checks)) == 1: raise ValueError("You must specify code_indexes, char_indexes, " "and clean_string together, or not at all.") if not all(checks): decoded = True if not decoded: # Completely new ANSI String clean_string = to_unicode( parser.parse_ansi(string, strip_ansi=True, mxp=True)) string = parser.parse_ansi(string, xterm256=True, mxp=True) elif clean_string is not None: # We have an explicit clean string. pass elif hasattr(string, '_clean_string'): # It's already an ANSIString clean_string = string._clean_string code_indexes = string._code_indexes char_indexes = string._char_indexes string = string._raw_string else: # It's a string that has been pre-ansi decoded. clean_string = parser.strip_raw_codes(string) if not isinstance(string, unicode): string = string.decode('utf-8') ansi_string = super(ANSIString, cls).__new__(ANSIString, to_str(clean_string), "utf-8") ansi_string._raw_string = string ansi_string._clean_string = clean_string ansi_string._code_indexes = code_indexes ansi_string._char_indexes = char_indexes return ansi_string
def _to_ansi(obj, regexable=False): "convert to ANSIString" if isinstance(obj, dict): return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items()) elif hasattr(obj, "__iter__"): return [_to_ansi(o) for o in obj] else: return ANSIString(to_unicode(obj), regexable=regexable)
def _to_ansi(obj, regexable=False): "convert to ANSIString" if isinstance(obj, basestring): # since ansi will be parsed twice (here and in the normal ansi send), we have to # escape the |-structure twice. obj = _ANSI_ESCAPE.sub(r"||||", obj) if isinstance(obj, dict): return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items()) elif hasattr(obj, "__iter__"): return [_to_ansi(o) for o in obj] else: return ANSIString(to_unicode(obj), regexable=regexable)
def __new__(cls, *args, **kwargs): """ When creating a new ANSIString, you may use a custom parser that has the same attributes as the standard one, and you may declare the string to be handled as already decoded. It is important not to double decode strings, as escapes can only be respected once. Internally, ANSIString can also passes itself precached code/character indexes and clean strings to avoid doing extra work when combining ANSIStrings. """ string = args[0] if not isinstance(string, basestring): string = to_str(string, force_string=True) parser = kwargs.get('parser', ANSI_PARSER) decoded = kwargs.get('decoded', False) or hasattr(string, '_raw_string') code_indexes = kwargs.pop('code_indexes', None) char_indexes = kwargs.pop('char_indexes', None) clean_string = kwargs.pop('clean_string', None) # All True, or All False, not just one. checks = [x is None for x in [code_indexes, char_indexes, clean_string]] if not len(set(checks)) == 1: raise ValueError("You must specify code_indexes, char_indexes, " "and clean_string together, or not at all.") if not all(checks): decoded = True if not decoded: # Completely new ANSI String clean_string = to_unicode(parser.parse_ansi(string, strip_ansi=True, mxp=True)) string = parser.parse_ansi(string, xterm256=True, mxp=True) elif clean_string is not None: # We have an explicit clean string. pass elif hasattr(string, '_clean_string'): # It's already an ANSIString clean_string = string._clean_string code_indexes = string._code_indexes char_indexes = string._char_indexes string = string._raw_string else: # It's a string that has been pre-ansi decoded. clean_string = parser.strip_raw_codes(string) if not isinstance(string, unicode): string = string.decode('utf-8') ansi_string = super(ANSIString, cls).__new__(ANSIString, to_str(clean_string), "utf-8") ansi_string._raw_string = string ansi_string._clean_string = clean_string ansi_string._code_indexes = code_indexes ansi_string._char_indexes = char_indexes return ansi_string
def data_out(self, session, text="", **kwargs): """ Sending data Server -> Portal Args: session (Session): Session object text (str, optional): text data to return _nomulti (bool, optional): if given, only this session will receive the rest of the data, regardless of MULTISESSION_MODE. This is an internal variable that will not be passed on. This is ignored for MULTISESSION_MODE = 1, since all messages are mirrored everywhere for that. _forced_nomulti (bool, optional): Like _nomulti, but works even when MULTISESSION_MODE = 1. Useful for connection handling messages. """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_out") sessions = make_iter(session) session = sessions[0] text = text and to_str(to_unicode(text), encoding=session.encoding) multi = not kwargs.pop("_nomulti", None) forced_nomulti = kwargs.pop("_forced_nomulti", None) # Mode 1 mirrors to all. if _MULTISESSION_MODE == 1: multi = True # ...Unless we're absolutely sure. if forced_nomulti: multi = False if multi: if _MULTISESSION_MODE == 1: if session.player: sessions = self.sessions_from_player(session.player) if _MULTISESSION_MODE == 2: if session.player: sessions = self.sessions_from_player(session.player) elif _MULTISESSION_MODE == 3: if session.puppet: sessions = self.sessions_from_puppet(session.puppet) elif session.player: sessions = self.sessions_from_player(session.player) # send to all found sessions for session in sessions: self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid, msg=text, data=kwargs)
def data_out(self, session, text="", **kwargs): """ Sending data Server -> Portal Args: session (Session): Session object text (str, optional): text data to return _nomulti (bool, optional): if given, only this session will receive the rest of the data, regardless of MULTISESSION_MODE. This is an internal variable that will not be passed on. This is ignored for MULTISESSION_MODE = 1, since all messages are mirrored everywhere for that. _forced_nomulti (bool, optional): Like _nomulti, but works even when MULTISESSION_MODE = 1. Useful for connection handling messages. """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_out") sessions = make_iter(session) session = sessions[0] text = text and to_str(to_unicode(text), encoding=session.encoding) multi = not kwargs.pop("_nomulti", None) forced_nomulti = kwargs.pop("_forced_nomulti", None) # Mode 1 mirrors to all. if _MULTISESSION_MODE == 1: multi = True # ...Unless we're absolutely sure. if forced_nomulti: multi = False if multi: if _MULTISESSION_MODE == 1: if session.player: sessions = self.sessions_from_player(session.player) if _MULTISESSION_MODE == 2: if session.player: sessions = self.sessions_from_player(session.player) elif _MULTISESSION_MODE == 3: if session.puppet: sessions = self.sessions_from_puppet(session.puppet) elif session.player: sessions = self.sessions_from_player(session.player) # send to all found sessions for session in sessions: self.server.amp_protocol.send_MsgServer2Portal( sessid=session.sessid, msg=text, data=kwargs)
def _validate(data): "Helper function to convert data to AMP-safe (picketable) values" if isinstance(data, dict): newdict = {} for key, part in data.items(): newdict[key] = _validate(part) return newdict elif hasattr(data, "__iter__"): return [_validate(part) for part in data] elif isinstance(data, basestring): # make sure strings are in a valid encoding try: data = data and to_str( to_unicode(data), encoding=session.protocol_flags["ENCODING"]) except LookupError: # wrong encoding set on the session. Set it to a safe one session.protocol_flags["ENCODING"] = "utf-8" data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) if _INLINEFUNC_ENABLED and not raw and isinstance( self, ServerSessionHandler): # only parse inlinefuncs on the outgoing path (sessionhandler->) data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) # At this point the object is certainly the right encoding, but may still be a unicode object-- # to_str does not actually force objects to become bytestrings. # If the unicode object is a subclass of unicode, such as ANSIString, this can cause a problem, # as special behavior for that class will still be in play. Since we're now transferring raw data, # we must now force this to be a proper bytestring. return str(data) elif hasattr(data, "id") and hasattr(data, "db_date_created") \ and hasattr(data, '__dbclass__'): # convert database-object to their string representation. return _validate(unicode(data)) else: return data
def smart_search(self, query): """ Search by supplying a string with optional extra search criteria to aid the query. Args: query (str): A search criteria that accepts extra search criteria on the following forms: [key|alias|#dbref...] [tag==<tagstr>[:category]...] [attr==<key>:<value>:category...] " != " != " Returns: matches (queryset): A queryset result matching all queries exactly. If wanting to use spaces or ==, != in tags or attributes, enclose them in quotes. Note: The flexibility of this method is limited by the input line format. Tag/attribute matching only works for matching primitives. For even more complex queries, such as 'in' operations or object field matching, use the full django query language. """ # shlex splits by spaces unless escaped by quotes querysplit = shlex.split(to_unicode(query, force_string=True)) queries, plustags, plusattrs, negtags, negattrs = [], [], [], [], [] for ipart, part in enumerate(querysplit): key, rest = part, "" if ":" in part: key, rest = part.split(':', 1) # tags are on the form tag or tag:category if key.startswith('tag=='): plustags.append((key[5:], rest)) continue elif key.startswith('tag!='): negtags.append((key[5:], rest)) continue # attrs are on the form attr:value or attr:value:category elif rest: value, category = rest, "" if ":" in rest: value, category = rest.split(':', 1) if key.startswith('attr=='): plusattrs.append((key[7:], value, category)) continue elif key.startswith('attr!='): negattrs.append((key[7:], value, category)) continue # if we get here, we are entering a key search criterion which # we assume is one word. queries.append(part) # build query from components query = ' '.join(queries)
def get_object_with_account(self, ostring, exact=True, candidates=None): """ Search for an object based on its account's name or dbref. Args: ostring (str or int): Search criterion or dbref. Searching for an account is sometimes initiated by appending an `*` to the beginning of the search criterion (e.g. in local_and_global_search). This is stripped here. exact (bool, optional): Require an exact account match. candidates (list, optional): Only search among this list of possible object candidates. Return: match (Object or list): One or more matching results. """ ostring = to_unicode(ostring).lstrip('*') # simplest case - search by dbref dbref = self.dbref(ostring) if dbref: try: return self.get(id=dbref) except self.model.DoesNotExist: pass # not a dbref. Search by name. cand_restriction = candidates is not None and Q( pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() if exact: return self.filter(cand_restriction & Q(db_account__username__iexact=ostring)) else: # fuzzy matching ply_cands = self.filter(cand_restriction & Q( accountdb__username__istartswith=ostring)).values_list( "db_key", flat=True) if candidates: index_matches = string_partial_matching(ply_cands, ostring, ret_index=True) return [ obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches ] else: return string_partial_matching(ply_cands, ostring, ret_index=False)
def data_out(self, session, text="", **kwargs): """ Sending data Server -> Portal Args: session (Session): Session to relay to. text (str, optional): text data to return """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_out") try: text = text and to_str(to_unicode(text), encoding=session.encoding) except LookupError: # wrong encoding set on the session. Set it to a safe one session.encoding = "utf-8" text = to_str(to_unicode(text), encoding=session.encoding) # send across AMP self.server.amp_protocol.send_MsgServer2Portal(session, text=text, **kwargs)
def func(self): """ Sets the encoding. """ if self.session is None: return sync = False if 'clear' in self.switches: # remove customization old_encoding = self.session.protocol_flags.get("ENCODING", None) if old_encoding: string = "Your custom text encoding ('%s') was cleared." % old_encoding else: string = "No custom encoding was set." self.session.protocol_flags["ENCODING"] = "utf-8" sync = True elif not self.args: # just list the encodings supported pencoding = self.session.protocol_flags.get("ENCODING", None) string = "" if pencoding: string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding encodings = settings.ENCODINGS if encodings: string += "\nServer's alternative encodings (tested in this order):\n |g%s|n" % ", ".join( encodings) if not string: string = "No encodings found." else: # change encoding old_encoding = self.session.protocol_flags.get("ENCODING", None) encoding = self.args try: utils.to_str(utils.to_unicode("test-string"), encoding=encoding) except LookupError: string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\ % (encoding, old_encoding) else: self.session.protocol_flags["ENCODING"] = encoding string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % ( old_encoding, encoding) sync = True if sync: self.session.sessionhandler.session_portal_sync(self.session) self.caller.msg(string.strip())
def func(self): """ Sets the encoding. """ if self.session is None: return sync = False if 'clear' in self.switches: # remove customization old_encoding = self.session.protocol_flags.get("ENCODING", None) if old_encoding: string = "Your custom text encoding ('%s') was cleared." % old_encoding else: string = "No custom encoding was set." self.session.protocol_flags["ENCODING"] = "utf-8" sync = True elif not self.args: # just list the encodings supported pencoding = self.session.protocol_flags.get("ENCODING", None) string = "" if pencoding: string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding encodings = settings.ENCODINGS if encodings: string += "\nServer's alternative encodings (tested in this order):\n |g%s|n" % ", ".join(encodings) if not string: string = "No encodings found." else: # change encoding old_encoding = self.session.protocol_flags.get("ENCODING", None) encoding = self.args try: utils.to_str(utils.to_unicode("test-string"), encoding=encoding) except LookupError: string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\ % (encoding, old_encoding) else: self.session.protocol_flags["ENCODING"] = encoding string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, encoding) sync = True if sync: self.session.sessionhandler.session_portal_sync(self.session) self.caller.msg(string.strip())
def data_out(self, session, text="", **kwargs): """ Sending data Server -> Portal Args: session (Session): Session to relay to. text (str, optional): text data to return """ #from evennia.server.profiling.timetrace import timetrace #text = timetrace(text, "ServerSessionHandler.data_out") text = text and to_str(to_unicode(text), encoding=session.encoding) # send across AMP self.server.amp_protocol.send_MsgServer2Portal(session, text=text, **kwargs)
def func(self): """ Implements the command """ if self.session is None: return if not self.args: # list the option settings string = "{wEncoding{n:\n" pencoding = self.session.encoding or "None" sencodings = settings.ENCODINGS string += " Custom: %s\n Server: %s" % (pencoding, ", ".join(sencodings)) string += "\n{wScreen Reader mode:{n %s" % self.session.screenreader self.caller.msg(string) return if not self.rhs: self.caller.msg("Usage: @option [name = [value]]") return if self.lhs == "encoding": # change encoding old_encoding = self.session.encoding new_encoding = self.rhs.strip() or "utf-8" try: utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding) except LookupError: string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n" % ( new_encoding, old_encoding) else: self.session.encoding = new_encoding string = "Encoding was changed from '|w%s|n' to '|w%s|n'." % ( old_encoding, new_encoding) self.caller.msg(string) return if self.lhs == "screenreader": onoff = self.rhs.lower() == "on" self.session.screenreader = onoff self.caller.msg("Screen reader mode was turned {w%s{n." % ("on" if onoff else "off"))
def data_in(self, sessid, text="", **kwargs): """ Data Portal -> Server. We also intercept OOB communication here. """ session = self.sessions.get(sessid, None) if session: text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding) if "oob" in kwargs: # incoming data is always on the form (cmdname, args, kwargs) global _OOB_HANDLER if not _OOB_HANDLER: from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER funcname, args, kwargs = kwargs.pop("oob") #print "OOB session.data_in:", funcname, args, kwargs if funcname: _OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs) # pass the rest off to the session session.data_in(text=text, **kwargs)
def func(self): """ Implements the command """ if self.session is None: return if not self.args: # list the option settings string = "{wEncoding{n:\n" pencoding = self.session.encoding or "None" sencodings = settings.ENCODINGS string += " Custom: %s\n Server: %s" % (pencoding, ", ".join(sencodings)) string += "\n{wScreen Reader mode:{n %s" % self.session.screenreader self.msg(string) return if not self.rhs: self.msg("Usage: @option [name = [value]]") return if self.lhs == "encoding": # change encoding old_encoding = self.session.encoding new_encoding = self.rhs.strip() or "utf-8" try: utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding) except LookupError: string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n" % (new_encoding, old_encoding) else: self.session.encoding = new_encoding string = "Encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, new_encoding) self.msg(string) return if self.lhs == "screenreader": onoff = self.rhs.lower() == "on" self.session.screenreader = onoff self.msg("Screen reader mode was turned {w%s{n." % ("on" if onoff else "off"))
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 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 evennia.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_object_with_player(self, ostring, exact=True, candidates=None): """ Search for an object based on its player's name or dbref. This search is sometimes initiated by appending an `*` to the beginning of the search criterion (e.g. in local_and_global_search). search_string: (string) The name or dbref to search for. """ ostring = to_unicode(ostring).lstrip('*') # simplest case - search by dbref dbref = self.dbref(ostring) if dbref: return dbref # not a dbref. Search by name. cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q() if exact: return self.filter(cand_restriction & Q(db_player__username__iexact=ostring)) else: # fuzzy matching ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True) if candidates: index_matches = string_partial_matching(ply_cands, ostring, ret_index=True) return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches] else: return string_partial_matching(ply_cands, ostring, ret_index=False)
def build_map(caller, game_map, legend, iterations=1, build_exits=True): """ Receives the fetched map and legend vars provided by the player. Args: caller (Object): The creator of the map. game_map (str): An ASCII map string. legend (dict): Mapping of map symbols to object types. iterations (int): The number of iteration passes. build_exits (bool): Create exits between new rooms. Notes: The map is iterated over character by character, comparing it to the trigger characters in the legend var and executing the build instructions on finding a match. The map is iterated over according to the `iterations` value and exits are optionally generated between adjacent rooms according to the `build_exits` value. """ # Split map string to list of rows and create reference list. caller.msg("Creating Map...") caller.msg(game_map) game_map = _map_to_list(game_map) # Create a reference dictionary which be passed to build functions and # will store obj returned by build functions so objs can be referenced. room_dict = {} caller.msg("Creating Landmass...") for iteration in xrange(iterations): for y in xrange(len(game_map)): for x in xrange(len(game_map[y])): for key in legend: # obs - we must use == for unicode if utils.to_unicode( game_map[y][x]) == utils.to_unicode(key): room = legend[key](x, y, iteration=iteration, room_dict=room_dict, caller=caller) if iteration == 0: room_dict[(x, y)] = room if build_exits: # Creating exits. Assumes single room object in dict entry caller.msg("Connecting Areas...") for loc_key, location in room_dict.iteritems(): x = loc_key[0] y = loc_key[1] # north if (x, y - 1) in room_dict: if room_dict[(x, y - 1)]: create_object(exits.Exit, key="north", aliases=["n"], location=location, destination=room_dict[(x, y - 1)]) # east if (x + 1, y) in room_dict: if room_dict[(x + 1, y)]: create_object(exits.Exit, key="east", aliases=["e"], location=location, destination=room_dict[(x + 1, y)]) # south if (x, y + 1) in room_dict: if room_dict[(x, y + 1)]: create_object(exits.Exit, key="south", aliases=["s"], location=location, destination=room_dict[(x, y + 1)]) # west if (x - 1, y) in room_dict: if room_dict[(x - 1, y)]: create_object(exits.Exit, key="west", aliases=["w"], location=location, destination=room_dict[(x - 1, y)]) caller.msg("Map Created.")
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", session=None, **kwargs): """ This is the main mechanism that handles any string sent to the engine. Args: called_by (Session, Player or Object): Object from which this command was called. which this was called from. What this is depends on the game state. raw_string (str): The command string as given on the command line. _testing (bool, optional): Used for debug purposes and decides if we should actually execute the command or not. If True, the command instance will be returned. callertype (str, optional): One of "session", "player" or "object". These are treated 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. session (Session, optional): Relevant if callertype is "player" - the session will help retrieve the correct cmdsets from puppeted objects. Kwargs: kwargs (any): other keyword arguments will be assigned as named variables on the retrieved command object *before* it is executed. This is unused in default Evennia but may be used by code to set custom flags or special operating conditions for a command as it executes. Returns: deferred (Deferred): This deferred is fired with the return value of the command's `func` method. This is not used in default Evennia. """ @inlineCallbacks def _run_command(cmd, cmdname, args): """ Helper function: This initializes and runs the Command instance once the parser has identified it as either a normal command or one of the system commands. Args: cmd (Command): command object cmdname (str): name of command args (str): extra text entered after the identified command Returns: deferred (Deferred): this will fire with the return of the command's `func` method. Raises: RuntimeError: If command recursion limit was reached. """ global _COMMAND_NESTING try: # Assign useful variables to the instance cmd.caller = caller cmd.cmdstring = cmdname cmd.args = args cmd.cmdset = cmdset 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) _COMMAND_NESTING[called_by] += 1 if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT: err = _ERROR_RECURSION_LIMIT.format(recursion_limit=_COMMAND_RECURSION_LIMIT, raw_string=unformatted_raw_string, cmdclass=cmd.__class__) raise RuntimeError(err) # pre-command hook abort = yield cmd.at_pre_cmd() if abort: # abort sequence returnValue(abort) # Parse and execute yield cmd.parse() # main command code # (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 # return result to the deferred returnValue(ret) except Exception: _msg_err(caller, _ERROR_UNTRAPPED) raise ErrorReported finally: _COMMAND_NESTING[called_by] -= 1 raw_string = to_unicode(raw_string, force_string=True) session, player, obj = session, None, None if callertype == "session": session = called_by player = session.player obj = session.puppet elif callertype == "player": player = called_by if session: obj = yield session.puppet 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 # The error_to is the default recipient for errors. Tries to make sure a player # does not get spammed for errors while preserving character mirroring. error_to = obj or session or player try: # catch bugs in cmdhandler itself try: # catch special-type commands cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype) 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 _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=match[0]) 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] if not matches: # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: # use custom CMD_NOMATCH 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.session = session sysarg = "%s:%s" % (cmdname, args) raise ExecSystemCommand(cmd, sysarg) # A normal command. ret = yield _run_command(cmd, cmdname, args) returnValue(ret) except ErrorReported: # this error was already reported, so we # catch it here and don't pass it on. pass except ExecSystemCommand as 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: ret = yield _run_command(syscmd, syscmd.key, sysarg) returnValue(ret) elif sysarg: # return system arg error_to.msg(exc.sysarg) except NoCmdSets: # Critical error. logger.log_err("No cmdsets found: %s" % caller) error_to.msg(_ERROR_NOCMDSETS) except Exception: # We should not end up here. If we do, it's a programming bug. _msg_err(error_to, _ERROR_UNTRAPPED) except Exception: # This catches exceptions in cmdhandler exceptions themselves _msg_err(error_to, _ERROR_CMDHANDLER)
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! """ @inlineCallbacks def _run_command(cmd, cmdname, args): """ This initializes and runs the Command instance once the parser has identified it as either a normal command or one of the system commands. Args: cmd (Command): command object cmdname (str): name of command args (str): extra text entered after the identified command Returns: deferred (Deferred): this will fire when the func() method returns. """ try: # 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() # main command code # (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 # return result to the deferred returnValue(ret) except Exception: logger.log_trace() _msg_err(caller, _ERROR_UNTRAPPED) raise ErrorReported 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 player.get_puppet(session.sessid) elif callertype == "player": player = called_by if sessid: session = player.get_session(sessid) obj = yield player.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 # The error_to is the default recipient for errors. Tries to make sure a player # does not get spammed for errors while preserving character mirroring. error_to = obj or session or player 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_NOMATCH 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. ret = yield _run_command(cmd, cmdname, args) returnValue(ret) except ErrorReported: # this error was already reported, so we # catch it here and don't pass it on. pass 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: ret = yield _run_command(syscmd, syscmd.key, sysarg) returnValue(ret) elif sysarg: # return system arg error_to.msg(exc.sysarg, _nomulti=True) except NoCmdSets: # Critical error. logger.log_errmsg("No cmdsets found: %s" % caller) error_to.msg(_ERROR_NOCMDSETS, _nomulti=True) except Exception: # We should not end up here. If we do, it's a programming bug. logger.log_trace() _msg_err(error_to, _ERROR_UNTRAPPED)
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", session=None, cmdobj=None, cmdobj_key=None, **kwargs): """ This is the main mechanism that handles any string sent to the engine. Args: called_by (Session, Player or Object): Object from which this command was called. which this was called from. What this is depends on the game state. raw_string (str): The command string as given on the command line. _testing (bool, optional): Used for debug purposes and decides if we should actually execute the command or not. If True, the command instance will be returned. callertype (str, optional): One of "session", "player" or "object". These are treated 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. session (Session, optional): Relevant if callertype is "player" - the session will help retrieve the correct cmdsets from puppeted objects. cmdobj (Command, optional): If given a command instance, this will be executed using `called_by` as the caller, `raw_string` representing its arguments and (optionally) `cmdobj_key` as its input command name. No cmdset lookup will be performed but all other options apply as normal. This allows for running a specific Command within the command system mechanism. cmdobj_key (string, optional): Used together with `cmdobj` keyword to specify which cmdname should be assigned when calling the specified Command instance. This is made available as `self.cmdstring` when the Command runs. If not given, the command will be assumed to be called as `cmdobj.key`. Kwargs: kwargs (any): other keyword arguments will be assigned as named variables on the retrieved command object *before* it is executed. This is unused in default Evennia but may be used by code to set custom flags or special operating conditions for a command as it executes. Returns: deferred (Deferred): This deferred is fired with the return value of the command's `func` method. This is not used in default Evennia. """ @inlineCallbacks def _run_command(cmd, cmdname, args, raw_string, cmdset, session, player): """ Helper function: This initializes and runs the Command instance once the parser has identified it as either a normal command or one of the system commands. Args: cmd (Command): Command object. cmdname (str): Name of command. args (str): Extra text entered after the identified command. raw_string (str): Full input string. cmdset (CmdSet): Command sert the command belongs to (if any).. session (Session): Session of caller (if any). player (Player): Player of caller (if any). Returns: deferred (Deferred): this will fire with the return of the command's `func` method. Raises: RuntimeError: If command recursion limit was reached. """ global _COMMAND_NESTING try: # Assign useful variables to the instance cmd.caller = caller cmd.cmdstring = cmdname cmd.args = args cmd.cmdset = cmdset cmd.session = session cmd.player = player cmd.raw_string = 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) _COMMAND_NESTING[called_by] += 1 if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT: err = _ERROR_RECURSION_LIMIT.format( recursion_limit=_COMMAND_RECURSION_LIMIT, raw_string=raw_string, cmdclass=cmd.__class__) raise RuntimeError(err) # pre-command hook abort = yield cmd.at_pre_cmd() if abort: # abort sequence returnValue(abort) # Parse and execute yield cmd.parse() # main command code # (return value is normally None) ret = cmd.func() if isinstance(ret, types.GeneratorType): # cmd.func() is a generator, execute progressively _progressive_cmd_run(cmd, ret) yield None else: ret = yield ret # 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 # return result to the deferred returnValue(ret) except Exception: _msg_err(caller, _ERROR_UNTRAPPED) raise ErrorReported(raw_string) finally: _COMMAND_NESTING[called_by] -= 1 raw_string = to_unicode(raw_string, force_string=True) session, player, obj = session, None, None if callertype == "session": session = called_by player = session.player obj = session.puppet elif callertype == "player": player = called_by if session: obj = yield session.puppet 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 # The error_to is the default recipient for errors. Tries to make sure a player # does not get spammed for errors while preserving character mirroring. error_to = obj or session or player try: # catch bugs in cmdhandler itself try: # catch special-type commands if cmdobj: # the command object is already given cmd = cmdobj() if callable(cmdobj) else cmdobj cmdname = cmdobj_key if cmdobj_key else cmd.key args = raw_string unformatted_raw_string = "%s%s" % (cmdname, args) cmdset = None session = session player = player else: # no explicit cmdobject given, figure it out cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string) 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 _SEARCH_AT_RESULT( [match[2] for match in matches], caller, query=matches[0][0]) raise ExecSystemCommand(syscmd, sysarg) cmdname, args, cmd = "", "", None 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] if not matches: # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: # use custom CMD_NOMATCH 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.session = session sysarg = "%s:%s" % (cmdname, args) raise ExecSystemCommand(cmd, sysarg) # A normal command. ret = yield _run_command(cmd, cmdname, args, unformatted_raw_string, cmdset, session, player) returnValue(ret) except ErrorReported as exc: # this error was already reported, so we # catch it here and don't pass it on. logger.log_err("User input was: '%s'." % exc.raw_string) except ExecSystemCommand as 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: ret = yield _run_command(syscmd, syscmd.key, sysarg, unformatted_raw_string, cmdset, session, player) returnValue(ret) elif sysarg: # return system arg error_to.msg(exc.sysarg) except NoCmdSets: # Critical error. logger.log_err("No cmdsets found: %s" % caller) error_to.msg(_ERROR_NOCMDSETS) except Exception: # We should not end up here. If we do, it's a programming bug. _msg_err(error_to, _ERROR_UNTRAPPED) except Exception: # This catches exceptions in cmdhandler exceptions themselves _msg_err(error_to, _ERROR_CMDHANDLER)
def build_map(caller, game_map, legend, iterations=1, build_exits=True): """ Receives the fetched map and legend vars provided by the player. Args: caller (Object): The creator of the map. game_map (str): An ASCII map string. legend (dict): Mapping of map symbols to object types. iterations (int): The number of iteration passes. build_exits (bool): Create exits between new rooms. Notes: The map is iterated over character by character, comparing it to the trigger characters in the legend var and executing the build instructions on finding a match. The map is iterated over according to the `iterations` value and exits are optionally generated between adjacent rooms according to the `build_exits` value. """ # Split map string to list of rows and create reference list. caller.msg("Creating Map...") caller.msg(game_map) game_map = _map_to_list(game_map) # Create a reference dictionary which be passed to build functions and # will store obj returned by build functions so objs can be referenced. room_dict = {} caller.msg("Creating Landmass...") for iteration in xrange(iterations): for y in xrange(len(game_map)): for x in xrange(len(game_map[y])): for key in legend: # obs - we must use == for unicode if utils.to_unicode(game_map[y][x]) == utils.to_unicode(key): room = legend[key](x, y, iteration=iteration, room_dict=room_dict, caller=caller) if iteration == 0: room_dict[(x, y)] = room if build_exits: # Creating exits. Assumes single room object in dict entry caller.msg("Connecting Areas...") for loc_key, location in room_dict.iteritems(): x = loc_key[0] y = loc_key[1] # north if (x, y-1) in room_dict: if room_dict[(x, y-1)]: create_object(exits.Exit, key="north", aliases=["n"], location=location, destination=room_dict[(x, y-1)]) # east if (x+1, y) in room_dict: if room_dict[(x+1, y)]: create_object(exits.Exit, key="east", aliases=["e"], location=location, destination=room_dict[(x+1, y)]) # south if (x, y+1) in room_dict: if room_dict[(x, y+1)]: create_object(exits.Exit, key="south", aliases=["s"], location=location, destination=room_dict[(x, y+1)]) # west if (x-1, y) in room_dict: if room_dict[(x-1, y)]: create_object(exits.Exit, key="west", aliases=["w"], location=location, destination=room_dict[(x-1, y)]) caller.msg("Map Created.")