def menu_setattr(menu, choice, obj, string): """ Set the value at the specified attribute. Args: menu (BuildingMenu): the menu object. choice (Chocie): the specific choice. obj (Object): the object to modify. string (str): the string with the new value. Note: This function is supposed to be used as a default to `BuildingMenu.add_choice`, when an attribute name is specified (in the `attr` argument) but no function `on_nomatch` is defined. """ attr = getattr(choice, "attr", None) if choice else None if choice is None or string is None or attr is None or menu is None: log_err( dedent(""" The `menu_setattr` function was called to set the attribute {} of object {} to {}, but the choice {} of menu {} or another information is missing. """.format(attr, obj, repr(string), choice, menu)).strip("\n")).strip() return for part in attr.split(".")[:-1]: obj = getattr(obj, part) setattr(obj, attr.split(".")[-1], string) return True
def check_permission(prototype_key, action, default=True): """ Helper function to check access to actions on given prototype. Args: prototype_key (str): The prototype to affect. action (str): One of "spawn" or "edit". default (str): If action is unknown or prototype has no locks Returns: passes (bool): If permission for action is granted or not. """ if action == 'edit': if prototype_key in _MODULE_PROTOTYPES: mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key, "N/A") logger.log_err("{} is a read-only prototype " "(defined as code in {}).".format(prototype_key, mod)) return False prototype = search_prototype(key=prototype_key) if not prototype: logger.log_err("Prototype {} not found.".format(prototype_key)) return False lockstring = prototype.get("prototype_locks") if lockstring: return check_lockstring(None, lockstring, default=default, access_type=action) return default
def func(self): """Call the proper menu or redirect to nomatch.""" raw_string = self.args.rstrip() if self.menu is None: log_err( "When CMDNOMATCH was called, the building menu couldn't be found" ) self.caller.msg( "|rThe building menu couldn't be found, remove the CmdSet.|n") self.caller.cmdset.delete(BuildingMenuCmdSet) return choice = self.menu.current_choice if raw_string in self.menu.keys_go_back: if self.menu.keys: self.menu.move(back=True) elif self.menu.parents: self.menu.open_parent_menu() else: self.menu.display() elif choice: if choice.nomatch(raw_string): self.caller.msg(choice.format_text()) else: for choice in self.menu.relevant_choices: if choice.key.lower() == raw_string.lower() or any( raw_string.lower() == alias for alias in choice.aliases): self.menu.move(choice.key) return self.msg("|rUnknown command: {}|n.".format(raw_string))
def func(self): "Handle command" caller = self.caller if not caller: return if caller.db.level < settings.MIN_HONOUR_LEVEL: caller.msg({"alert":_("You need to reach level %s." % settings.MIN_HONOUR_LEVEL)}) return try: # getcandidates ids = HONOURS_MAPPER.get_characters(caller, settings.HONOUR_OPPONENTS_NUMBER) characters = [caller.search_dbref("#%s" % id) for id in ids] candidates = [char for char in characters if char and not char.is_in_combat()] if candidates: match = random.choice(candidates) # create a new combat handler chandler = create_script(settings.HONOUR_COMBAT_HANDLER) # set combat team and desc chandler.set_combat({1:[match], 2:[caller]}, _("Fight of Honour"), settings.AUTO_COMBAT_TIMEOUT) else: caller.msg({"alert":_("Can not make match.")}) except Exception, e: logger.log_err("Find match error: %s" % e) caller.msg({"alert":_("Can not make match.")})
def add(self, store_key, obj, interval, *args, **kwargs): """ Add new ticker subscriber. Args: store_key (str): Unique storage hash. obj (Object): Object subscribing. interval (int): How often to call the ticker. args (any, optional): Arguments to send to the hook method. Kwargs: _start_delay (int): If set, this will be used to delay the start of the trigger instead of `interval`. It is passed on as-is from this method. _hooK_key (str): This carries the name of the hook method to call. It is passed on as-is from this method. """ if not interval: log_err( _ERROR_ADD_INTERVAL.format(store_key=store_key, obj=obj, interval=interval, args=args, kwargs=kwargs)) return if interval not in self.tickers: self.tickers[interval] = self.ticker_class(interval) self.tickers[interval].add(store_key, obj, *args, **kwargs)
def _load_script(self, key): self.load_data() typeclass = self.typeclass_storage[key] found = typeclass.objects.filter(db_key=key).first() interval = self.loaded_data[key].get('interval', None) start_delay = self.loaded_data[key].get('start_delay', None) repeats = self.loaded_data[key].get('repeats', 0) desc = self.loaded_data[key].get('desc', '') if not found: logger.log_info( f"GLOBAL_SCRIPTS: (Re)creating {key} ({typeclass}).") new_script, errors = typeclass.create(key=key, persistent=True, interval=interval, start_delay=start_delay, repeats=repeats, desc=desc) if errors: logger.log_err("\n".join(errors)) return None new_script.start() return new_script if ((found.interval != interval) or (found.start_delay != start_delay) or (found.repeats != repeats)): found.restart(interval=interval, start_delay=start_delay, repeats=repeats) if found.desc != desc: found.desc = desc return found
def add(self, store_key, obj, interval, *args, **kwargs): """ Add new ticker subscriber. Args: store_key (str): Unique storage hash. obj (Object): Object subscribing. interval (int): How often to call the ticker. args (any, optional): Arguments to send to the hook method. Kwargs: _start_delay (int): If set, this will be used to delay the start of the trigger instead of `interval`. It is passed on as-is from this method. _hooK_key (str): This carries the name of the hook method to call. It is passed on as-is from this method. """ if not interval: log_err(_ERROR_ADD_INTERVAL.format(store_key=store_key, obj=obj, interval=interval, args=args, kwargs=kwargs)) return if interval not in self.tickers: self.tickers[interval] = self.ticker_class(interval) self.tickers[interval].add(store_key, obj, *args, **kwargs)
def _create_account(session, accountname, password, permissions, typeclass=None, email=None): """ Helper function, creates an account of the specified typeclass. """ try: new_account = create.create_account(accountname, email, password, permissions=permissions, typeclass=typeclass) except Exception as e: session.msg( "There was an error creating the Account:\n%s\n If this problem persists, contact an admin." % e) logger.log_trace() return False # This needs to be set so the engine knows this account is # logging in for the first time. (so it knows to call the right # hooks during login later) new_account.db.FIRST_LOGIN = True # join the new account to the public channel pchannel = ChannelDB.objects.get_channel( settings.DEFAULT_CHANNELS[0]["key"]) if not pchannel or not pchannel.connect(new_account): string = "New account '%s' could not connect to public channel!" % new_account.key logger.log_err(string) return new_account
def get(self, exclude=None): """ Return the contents of the cache. Args: exclude (Object or list of Object): object(s) to ignore Returns: objects (list): the Objects inside this location """ if exclude: pks = [ pk for pk in self._pkcache if pk not in [excl.pk for excl in make_iter(exclude)] ] else: pks = self._pkcache try: return [self._idcache[pk] for pk in pks] except KeyError: # this can happen if the idmapper cache was cleared for an object # in the contents cache. If so we need to re-initialize and try again. self.init() try: return [self._idcache[pk] for pk in pks] except KeyError: # this means an actual failure of caching. Return real database match. logger.log_err("contents cache failed for %s." % (self.obj.key)) return list(ObjectDB.objects.filter(db_location=self.obj))
def do_unpickle(data): """Retrieve pickle from pickled string""" try: return loads(to_bytes(data)) except Exception: logger.log_err(f"Could not unpickle data from storage: {data}") raise
def menu_quit(caller, menu): """ Quit the menu, closing the CmdSet. Args: caller (Account or Object): the caller. menu (BuildingMenu): the building menu to close. Note: This callback is used by default when using the `BuildingMenu.add_choice_quit` method. This method is called automatically if the menu has no parent. """ if caller is None or menu is None: log_err( "The function `menu_quit` was called with missing " "arguments: caller={}, menu={}".format(caller, menu) ) if caller.cmdset.has(BuildingMenuCmdSet): menu.close() caller.msg("Closing the building menu.") else: caller.msg("It looks like the building menu has already been closed.")
def _step_errback(self, e): """ Override to keep the user from getting useless script error, plus grabs real traceback for the log. """ estring = e.getTraceback() logger.log_err(estring)
def func(self): """Call the proper menu or redirect to nomatch.""" raw_string = self.args.rstrip() if self.menu is None: log_err("When CMDNOMATCH was called, the building menu couldn't be found") self.caller.msg("|rThe building menu couldn't be found, remove the CmdSet.|n") self.caller.cmdset.delete(BuildingMenuCmdSet) return choice = self.menu.current_choice if raw_string in self.menu.keys_go_back: if self.menu.keys: self.menu.move(back=True) elif self.menu.parents: self.menu.open_parent_menu() else: self.menu.display() elif choice: if choice.nomatch(raw_string): self.caller.msg(choice.format_text()) else: for choice in self.menu.relevant_choices: if choice.key.lower() == raw_string.lower() or any(raw_string.lower() == alias for alias in choice.aliases): self.menu.move(choice.key) return self.msg("|rUnknown command: {}|n.".format(raw_string))
def create_player(playername, password, permissions=None, typeclass=None): """ Helper function, creates a player of the specified typeclass. """ if not permissions: permissions = settings.PERMISSION_ACCOUNT_DEFAULT new_player = create.create_account(playername, None, password, permissions=permissions, typeclass=typeclass) # This needs to be set so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) new_player.db.FIRST_LOGIN = True # join the new player to the public channel pchannel = ChannelDB.objects.get_channel( settings.DEFAULT_CHANNELS[0]["key"]) if not pchannel.connect(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_err(string) return new_player
def do_pickle(data): """Perform pickle to string""" try: return dumps(data, protocol=PICKLE_PROTOCOL) except Exception: logger.log_err(f"Could not pickle data for storage: {data}") raise
def get(self, exclude=None): """ Return the contents of the cache. Args: exclude (Object or list of Object): object(s) to ignore Returns: objects (list): the Objects inside this location """ if exclude: pks = [pk for pk in self._pkcache if pk not in [excl.pk for excl in make_iter(exclude)]] else: pks = self._pkcache try: return [self._idcache[pk] for pk in pks] except KeyError: # this can happen if the idmapper cache was cleared for an object # in the contents cache. If so we need to re-initialize and try again. self.init() try: return [self._idcache[pk] for pk in pks] except KeyError: # this means an actual failure of caching. Return real database match. logger.log_err("contents cache failed for %s." % (self.obj.key)) return list(ObjectDB.objects.filter(db_location=self.obj))
def finalize(self): super(MagicDamageConsequence, self).finalize() from typeclasses.scripts.combat import attacks, combat_settings victims = [participant.character for participant in self.participants] names = [obj.name for obj in victims] commafied_names = commafy(names) attack = attacks.Attack(targets=victims, affect_real_dmg=True, damage=self.damage, use_mitigation=False, can_kill=True, private=False, story="Magic has consequences!", attack_tags=self.attack_tags) try: attack.execute() except combat_settings.CombatError as err: logger.log_err("Combat error when applying magical damage: " + str(err)) inform_staff( "|rCombat Error!|n Tried to apply %d damage to %s, but got error %s" % (self.damage, commafied_names, str(err))) else: part = "was" if len(victims) > 1: part = "were" inform_staff( "|y%s|n %s harmed for %d by a failed magical working." % (commafied_names, part, self.damage))
def func(self): "do action" caller = self.caller args = self.args if not caller.is_alive(): caller.msg({"alert": _("You are died.")}) return if not args: caller.msg({"alert": _("You should act to something.")}) return # Use search to handle duplicate/nonexistant results. obj = caller.search_dbref(args, location=caller.location) if not obj: caller.msg({"alert": _("Can not find it.")}) return try: obj.do_action(caller) except Exception, e: logger.log_err("Can not act to %s %s." % (caller.get_data_key(), e)) return
def choose_current_weather(): """ Picks a new emit for the current weather conditions, and locks it in. :return: The emit to use. """ weather_type = get_weather_type() weather_intensity = get_weather_intensity() emit = pick_emit(weather_type, intensity=weather_intensity) while not emit: # Just in case there's no available weather for a given # target intensity of during our current season/time; # we'll advance the weather until we do have something. season, time = gametime.get_time_and_season() logger.log_err( "Weather: No available weather for type {} intensity {} during {} {}".format( weather_type, weather_intensity, season, time ) ) weather_type, weather_intensity = advance_weather() emit = pick_emit(weather_type, intensity=weather_intensity) ServerConfig.objects.conf(key="weather_last_emit", value=emit) return emit
def create(cls, key, **kwargs): """ Provides a passthrough interface to the utils.create_script() function. Args: key (str): Name of the new object. Returns: object (Object): A newly created object of the given typeclass. errors (list): A list of errors in string form, if any. """ errors = [] obj = None kwargs["key"] = key # If no typeclass supplied, use this class kwargs["typeclass"] = kwargs.pop("typeclass", cls) try: obj = create.create_script(**kwargs) except Exception as e: errors.append( "The script '%s' encountered errors and could not be created." % key) logger.log_err(e) return obj, errors
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 menu_setattr(menu, choice, obj, string): """ Set the value at the specified attribute. Args: menu (BuildingMenu): the menu object. choice (Chocie): the specific choice. obj (Object): the object to modify. string (str): the string with the new value. Note: This function is supposed to be used as a default to `BuildingMenu.add_choice`, when an attribute name is specified (in the `attr` argument) but no function `on_nomatch` is defined. """ attr = getattr(choice, "attr", None) if choice else None if choice is None or string is None or attr is None or menu is None: log_err(dedent(""" The `menu_setattr` function was called to set the attribute {} of object {} to {}, but the choice {} of menu {} or another information is missing. """.format(attr, obj, repr(string), choice, menu)).strip("\n")).strip() return for part in attr.split(".")[:-1]: obj = getattr(obj, part) setattr(obj, attr.split(".")[-1], string) return True
def receive_pending_messenger(self): packed = self.get_packed_messenger() if not packed: return # get msg object and any delivered obj ( msg, obj, money, mats, messenger_name, forwarded_by, ) = self.unpack_oldest_pending_messenger(packed) # adds it to our list of old messages if msg and hasattr(msg, "id") and msg.id: self.add_messenger_to_history(msg) self.display_messenger(msg) else: from evennia.utils.logger import log_err self.msg("Error: The msg object no longer exists.") log_err( "%s has tried to receive a messenger that no longer exists." % self.obj) self.notify_of_messenger_arrival(messenger_name) # handle anything delivered self.handle_delivery(obj, money, mats) if forwarded_by: self.msg("{yThis message was forwarded by {c%s{n." % forwarded_by)
def add(self, scriptclass, key=None, autostart=True): """ Add a script to this object. Args: scriptclass (Scriptclass, Script or str): Either a class object inheriting from DefaultScript, an instantiated script object or a python path to such a class object. key (str, optional): Identifier for the script (often set in script definition and listings) autostart (bool, optional): Start the script upon adding it. """ if self.obj.__dbclass__.__name__ == "AccountDB": # we add to an Account, not an Object script = create.create_script(scriptclass, key=key, account=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_err("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): """ 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 != 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 add(self, scriptclass, key=None, autostart=True): """ Add a script to this object. Args: scriptclass (Scriptclass, Script or str): Either a class object inheriting from DefaultScript, an instantiated script object or a python path to such a class object. key (str, optional): Identifier for the script (often set in script definition and listings) autostart (bool, optional): Start the script upon adding it. """ if self.obj.__dbclass__.__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_err("Script %s could not be created and/or started." % scriptclass) return False return True
def data_in(self, data, **kwargs): """ Send data grapevine -> Evennia Keyword Args: data (dict): Converted json data. """ event = data["event"] if event == "authenticate": # server replies to our auth handshake if data["status"] != "success": log_err("Grapevine authentication failed.") self.disconnect() else: log_info("Connected and authenticated to Grapevine network.") elif event == "heartbeat": # server sends heartbeat - we have to send one back self.send_heartbeat() elif event == "restart": # set the expected downtime self.restart_downtime = data["payload"]["downtime"] elif event == "channels/subscribe": # subscription verification if data.get("status", "success") == "failure": err = data.get("error", "N/A") self.sessionhandler.data_in( bot_data_in=((f"Grapevine error: {err}"), { "event": event })) elif event == "channels/unsubscribe": # unsubscribe-verification pass elif event == "channels/broadcast": # incoming broadcast from network payload = data["payload"] print("channels/broadcast:", payload["channel"], self.channel) if str(payload["channel"]) != self.channel: # only echo from channels this particular bot actually listens to return else: # correct channel self.sessionhandler.data_in( self, bot_data_in=( str(payload["message"]), { "event": event, "grapevine_channel": str(payload["channel"]), "sender": str(payload["name"]), "game": str(payload["game"]), }, ), ) elif event == "channels/send": pass else: self.sessionhandler.data_in(self, bot_data_in=("", kwargs))
def after_data_loaded(self): """ Load goods data. Returns: None """ self.available = False self.shop_key = getattr(self.system, "shop", "") self.goods_key = getattr(self.system, "goods", "") self.goods_level = getattr(self.system, "level", 0) if not self.shop_key or not self.goods_key: if self.db.goods: self.db.goods.delete() self.db.goods = None return # set goods information self.price = getattr(self.system, "price", 0) self.unit_key = getattr(self.system, "unit", "") self.number = getattr(self.system, "number", 0) self.condition = getattr(self.system, "condition", "") # get price unit information unit_record = get_object_record(self.unit_key) if not unit_record: logger.log_errmsg("Can not find %s price unit %s." % (self.goods_key, self.unit_key)) return self.unit_name = unit_record.name # load goods object goods = self.db.goods if goods: if goods.get_data_key() == self.goods_key: # Load data. try: # Load db data. goods.load_data() except Exception as e: logger.log_errmsg("%s(%s) can not load data:%s" % (self.goods_key, self.dbref, e)) else: goods.set_data_key(self.goods_key, self.goods_level) else: goods = build_object(self.goods_key) if goods: self.db.goods = goods else: logger.log_err("Can not create goods %s." % self.goods_key) return self.name = goods.get_name() self.desc = goods.db.desc self.icon = getattr(goods, "icon", None) self.available = True
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_err("_SaverMutable %s has no root Attribute to save to." % self)
def func(self): """Display the menu or choice text.""" if self.menu: self.menu.display() else: log_err("When CMDNOINPUT was called, the building menu couldn't be found") self.caller.msg("|rThe building menu couldn't be found, remove the CmdSet.|n") self.caller.cmdset.delete(BuildingMenuCmdSet)
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_err("ServerConfig cannot store db objects! (%s)" % value) return self.db_value = pickle.dumps(value) self.save()
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_err("_SaverMutable %s has no root Attribute to save to." % self)
def remove_object(self, obj_key, number, mute=False): """ Remove objects from the inventory. Args: obj_key: object's key number: object's number mute: send inventory information Returns: boolean: success """ objects = self.search_inventory(obj_key) # get total number sum = 0 for obj in objects: obj_num = obj.get_number() sum += obj_num if sum < number: return False # remove objects to_remove = number try: for obj in objects: obj_num = obj.get_number() if obj_num > 0: if obj_num >= to_remove: obj.decrease_num(to_remove) to_remove = 0 else: obj.decrease_num(obj_num) to_remove -= obj_num if obj.get_number() <= 0: # If this object can be removed from the inventor. if obj.can_remove: # if it is an equipment, take off it first if getattr(obj, "equipped", False): self.take_off_equipment(obj) obj.delete() if to_remove <= 0: break except Exception as e: logger.log_tracemsg("Can not remove object %s: %s" % (obj_key, e)) return False if to_remove > 0: logger.log_err("Remove object error: %s" % obj_key) return False if not mute: self.show_inventory() return True
def restore(self, server_reload=True): """ Restore ticker_storage from database and re-initialize the handler from storage. This is triggered by the server at restart. Args: server_reload (bool, optional): If this is False, it means the server went through a cold reboot and all non-persistent tickers must be killed. """ # load stored command instructions and use them to re-initialize handler restored_tickers = ServerConfig.objects.conf(key=self.save_name) if restored_tickers: # the dbunserialize will convert all serialized dbobjs to real objects restored_tickers = dbunserialize(restored_tickers) self.ticker_storage = {} for store_key, (args, kwargs) in restored_tickers.iteritems(): try: # at this point obj is the actual object (or None) due to how # the dbunserialize works obj, callfunc, path, interval, idstring, persistent = store_key if not persistent and not server_reload: # this ticker will not be restarted continue if isinstance(callfunc, basestring) and not obj: # methods must have an existing object continue # we must rebuild the store_key here since obj must not be # stored as the object itself for the store_key to be hashable. store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) if obj and callfunc: kwargs["_callback"] = callfunc kwargs["_obj"] = obj elif path: modname, varname = path.rsplit(".", 1) callback = variable_from_module(modname, varname) kwargs["_callback"] = callback kwargs["_obj"] = None else: # Neither object nor path - discard this ticker log_err( "Tickerhandler: Removing malformed ticker: %s" % str(store_key)) continue except Exception: # this suggests a malformed save or missing objects log_trace("Tickerhandler: Removing malformed ticker: %s" % str(store_key)) continue # if we get here we should create a new ticker self.ticker_storage[store_key] = (args, kwargs) self.ticker_pool.add(store_key, *args, **kwargs)
def add(self, timedelay, callback, *args, **kwargs): """Add a new persistent task in the configuration. Args: timedelay (int or float): time in sedconds before calling the callback. callback (function or instance method): the callback itself any (any): any additional positional arguments to send to the callback Keyword Args: persistent (bool, optional): persist the task (store it). any (any): additional keyword arguments to send to the callback """ persistent = kwargs.get("persistent", False) if persistent: del kwargs["persistent"] now = datetime.now() delta = timedelta(seconds=timedelay) # Choose a free task_id safe_args = [] safe_kwargs = {} used_ids = list(self.tasks.keys()) task_id = 1 while task_id in used_ids: task_id += 1 # Check that args and kwargs contain picklable information for arg in args: try: dbserialize(arg) except (TypeError, AttributeError): log_err("The positional argument {} cannot be " "pickled and will not be present in the arguments " "fed to the callback {}".format(arg, callback)) else: safe_args.append(arg) for key, value in kwargs.items(): try: dbserialize(value) except (TypeError, AttributeError): log_err("The {} keyword argument {} cannot be " "pickled and will not be present in the arguments " "fed to the callback {}".format( key, value, callback)) else: safe_kwargs[key] = value self.tasks[task_id] = (now + delta, callback, safe_args, safe_kwargs) self.save() callback = self.do_task args = [task_id] kwargs = {} return deferLater(reactor, timedelay, callback, *args, **kwargs)
def start(self): "Connect protocol to remote server" try: from twisted.internet import ssl except ImportError: log_err("To use Grapevine, The PyOpenSSL module must be installed.") else: context_factory = ssl.ClientContextFactory() if self.isSecure else None connectWS(self, context_factory)
def add(self, timedelay, callback, *args, **kwargs): """Add a new persistent task in the configuration. Args: timedelay (int or float): time in sedconds before calling the callback. callback (function or instance method): the callback itself any (any): any additional positional arguments to send to the callback Kwargs: persistent (bool, optional): persist the task (store it). any (any): additional keyword arguments to send to the callback """ persistent = kwargs.get("persistent", False) if persistent: del kwargs["persistent"] now = datetime.now() delta = timedelta(seconds=timedelay) # Choose a free task_id safe_args = [] safe_kwargs = {} used_ids = self.tasks.keys() task_id = 1 while task_id in used_ids: task_id += 1 # Check that args and kwargs contain picklable information for arg in args: try: dbserialize(arg) except (TypeError, AttributeError): log_err("The positional argument {} cannot be " "pickled and will not be present in the arguments " "fed to the callback {}".format(arg, callback)) else: safe_args.append(arg) for key, value in kwargs.items(): try: dbserialize(value) except (TypeError, AttributeError): log_err("The {} keyword argument {} cannot be " "pickled and will not be present in the arguments " "fed to the callback {}".format(key, value, callback)) else: safe_kwargs[key] = value self.tasks[task_id] = (now + delta, callback, safe_args, safe_kwargs) self.save() callback = self.do_task args = [task_id] kwargs = {} return deferLater(reactor, timedelay, callback, *args, **kwargs)
def update(self, init_mode=False): """ Re-adds all sets in the handler to have an updated current set. Args: init_mode (bool, optional): Used automatically right after this handler was created; it imports all permanent cmdsets from the database. """ if init_mode: # reimport all permanent cmdsets storage = self.obj.cmdset_storage if storage: self.cmdset_stack = [] for pos, path in enumerate(storage): if pos == 0 and not path: self.cmdset_stack = [_EmptyCmdSet(cmdsetobj=self.obj)] elif path: cmdset = self._import_cmdset(path) if cmdset: if cmdset.key == "_CMDSET_ERROR": # If a cmdset fails to load, check if we have a fallback path to use fallback_path = _CMDSET_FALLBACKS.get(path, None) if fallback_path: err = _ERROR_CMDSET_FALLBACK.format( path=path, fallback_path=fallback_path ) logger.log_err(err) if _IN_GAME_ERRORS: self.obj.msg(err) cmdset = self._import_cmdset(fallback_path) # If no cmdset is returned from the fallback, we can't go further if not cmdset: err = _ERROR_CMDSET_NO_FALLBACK.format( fallback_path=fallback_path ) logger.log_err(err) if _IN_GAME_ERRORS: self.obj.msg(err) continue cmdset.permanent = cmdset.key != "_CMDSET_ERROR" self.cmdset_stack.append(cmdset) # merge the stack into a new merged cmdset new_current = None self.mergetype_stack = [] for cmdset in self.cmdset_stack: try: # for cmdset's '+' operator, order matters. new_current = cmdset + new_current except TypeError: continue self.mergetype_stack.append(new_current.actual_mergetype) self.current = new_current
def infuse_primum(self, amount): if self.practitioner or self.is_typeclass('typeclasses.characters.Character'): logger.log_err("Tried to infuse a Character with primum as though it were an Object! Not good.") raise ValueError self.db.primum = min(self.primum + amount, self.max_potential) if self.db.primum > self.potential: self.db.potential = self.db.primum if self.db.quality_level: self.db.quality_level = self.quality_level_from_primum(self.db.primum)
def add(self, store_key, obj, interval, *args, **kwargs): """ Add new ticker subscriber """ if not interval: log_err(_ERROR_ADD_INTERVAL.format(store_key=store_key, obj=obj, interval=interval, args=args, kwargs=kwargs)) return if interval not in self.tickers: self.tickers[interval] = self.ticker_class(interval) self.tickers[interval].add(store_key, obj, *args, **kwargs)
def restore(self, server_reload=True): """ Restore ticker_storage from database and re-initialize the handler from storage. This is triggered by the server at restart. Args: server_reload (bool, optional): If this is False, it means the server went through a cold reboot and all non-persistent tickers must be killed. """ # load stored command instructions and use them to re-initialize handler restored_tickers = ServerConfig.objects.conf(key=self.save_name) if restored_tickers: # the dbunserialize will convert all serialized dbobjs to real objects restored_tickers = dbunserialize(restored_tickers) self.ticker_storage = {} for store_key, (args, kwargs) in restored_tickers.iteritems(): try: # at this point obj is the actual object (or None) due to how # the dbunserialize works obj, callfunc, path, interval, idstring, persistent = store_key if not persistent and not server_reload: # this ticker will not be restarted continue if isinstance(callfunc, basestring) and not obj: # methods must have an existing object continue # we must rebuild the store_key here since obj must not be # stored as the object itself for the store_key to be hashable. store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) if obj and callfunc: kwargs["_callback"] = callfunc kwargs["_obj"] = obj elif path: modname, varname = path.rsplit(".", 1) callback = variable_from_module(modname, varname) kwargs["_callback"] = callback kwargs["_obj"] = None else: # Neither object nor path - discard this ticker log_err("Tickerhandler: Removing malformed ticker: %s" % str(store_key)) continue except Exception: # this suggests a malformed save or missing objects log_trace("Tickerhandler: Removing malformed ticker: %s" % str(store_key)) continue # if we get here we should create a new ticker self.ticker_storage[store_key] = (args, kwargs) self.ticker_pool.add(store_key, *args, **kwargs)
def _cache_lockfuncs(): """ Updates the cache. """ global _LOCKFUNCS _LOCKFUNCS = {} for modulepath in settings.LOCK_FUNC_MODULES: 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_err("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
def errback(self, e, info): """ Error callback. Handles errors to avoid dropping connections on server tracebacks. Args: e (Failure): Deferred error instance. info (str): Error string. """ e.trap(Exception) logger.log_err("AMP Error for %(info)s: %(e)s" % {'info': info, 'e': e.getErrorMessage()})
def update(self, init_mode=False): """ Re-adds all sets in the handler to have an updated current set. Args: init_mode (bool, optional): Used automatically right after this handler was created; it imports all permanent cmdsets from the database. """ if init_mode: # reimport all permanent cmdsets storage = self.obj.cmdset_storage if storage: self.cmdset_stack = [] for pos, path in enumerate(storage): if pos == 0 and not path: self.cmdset_stack = [_EmptyCmdSet(cmdsetobj=self.obj)] elif path: cmdset = self._import_cmdset(path) if cmdset: if cmdset.key == '_CMDSET_ERROR': # If a cmdset fails to load, check if we have a fallback path to use fallback_path = _CMDSET_FALLBACKS.get(path, None) if fallback_path: err = _ERROR_CMDSET_FALLBACK.format(path=path, fallback_path=fallback_path) logger.log_err(err) if _IN_GAME_ERRORS: self.obj.msg(err) cmdset = self._import_cmdset(fallback_path) # If no cmdset is returned from the fallback, we can't go further if not cmdset: err = _ERROR_CMDSET_NO_FALLBACK.format(fallback_path=fallback_path) logger.log_err(err) if _IN_GAME_ERRORS: self.obj.msg(err) continue cmdset.permanent = cmdset.key != '_CMDSET_ERROR' self.cmdset_stack.append(cmdset) # merge the stack into a new merged cmdset new_current = None self.mergetype_stack = [] for cmdset in self.cmdset_stack: try: # for cmdset's '+' operator, order matters. new_current = cmdset + new_current except TypeError: continue self.mergetype_stack.append(new_current.actual_mergetype) self.current = new_current
def after_data_loaded(self): """ Load goods data. Returns: None """ self.available = False self.shop_key = getattr(self.dfield, "shop", "") self.goods_key = getattr(self.dfield, "goods", "") if not self.shop_key or not self.goods_key: if self.db.goods: self.db.goods.delete() self.db.goods = None return # set goods information self.price = getattr(self.dfield, "price", 0) self.unit_key = getattr(self.dfield, "unit", "") self.number = getattr(self.dfield, "number", 0) self.condition = getattr(self.dfield, "condition", "") # get price unit information unit_record = get_object_record(self.unit_key) if not unit_record: logger.log_errmsg("Can not find %s price unit %s." % (self.goods_key, self.unit_key)) return self.unit_name = unit_record.name # load goods object goods = self.db.goods if goods: if goods.get_data_key() == self.goods_key: goods.load_data() else: goods.set_data_key(self.goods_key) else: goods = build_object(self.goods_key) if goods: self.db.goods = goods else: logger.log_err("Can not create goods %s." % self.goods_key) return self.name = goods.get_name() self.desc = goods.db.desc self.icon = getattr(goods, "icon", None) self.available = True
def default(session, cmdname, *args, **kwargs): """ Default catch-function. This is like all other input functions except it will get `cmdname` as the first argument. """ err = ( "Session {sessid}: Input command not recognized:\n" " name: '{cmdname}'\n" " args, kwargs: {args}, {kwargs}".format(sessid=session.sessid, cmdname=cmdname, args=args, kwargs=kwargs) ) if session.protocol_flags.get("INPUTDEBUG", False): session.msg(err) log_err(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.db_obj.msg(estring) except Exception: pass logger.log_err(estring)
def start(self): """ Connect session to sessionhandler. """ if self.port: if self.ssl: try: from twisted.internet import ssl service = reactor.connectSSL(self.network, int(self.port), self, ssl.ClientContextFactory()) except ImportError: logger.log_err("To use SSL, the PyOpenSSL module must be installed.") else: service = internet.TCPClient(self.network, int(self.port), self) self.sessionhandler.portal.services.addService(service)
def errback(self, e, info): """ Error callback. Handles errors to avoid dropping connections on server tracebacks. Args: e (Failure): Deferred error instance. info (str): Error string. """ global _LOGGER if not _LOGGER: from evennia.utils import logger as _LOGGER e.trap(Exception) _LOGGER.log_err("AMP Error for %(info)s: %(e)s" % {'info': info, 'e': e.getErrorMessage()})
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: if not self._db_obj.pk: cls_name = self.__class__.__name__ try: non_saver_name = cls_name.split("_Saver", 1)[1].lower() except IndexError: non_saver_name = cls_name raise ValueError(_ERROR_DELETED_ATTR.format(cls_name=cls_name, obj=self, non_saver_name=non_saver_name)) self._db_obj.value = self else: logger.log_err("_SaverMutable %s has no root Attribute to save to." % self)
def data_out(self, **kwargs): """ Generic hook for sending data out through the protocol. Kwargs: kwargs (any): Other data to the protocol. """ if AUDIT_CALLBACK and AUDIT_OUT: try: log = self.audit(src='server', **kwargs) if log: AUDIT_CALLBACK(log) except Exception as e: logger.log_err(e) super(AuditedServerSession, self).data_out(**kwargs)
def data_in(self, **kwargs): """ Hook for protocols to send incoming data to the engine. Kwargs: kwargs (any): Other data from the protocol. """ if AUDIT_CALLBACK and AUDIT_IN: try: log = self.audit(src='client', **kwargs) if log: AUDIT_CALLBACK(log) except Exception as e: logger.log_err(e) super(AuditedServerSession, self).data_in(**kwargs)
def add(self, store_key, *args, **kwargs): """ Add new ticker subscriber. Args: store_key (str): Unique storage hash. args (any, optional): Arguments to send to the hook method. """ _, _, _, interval, _, _ = store_key if not interval: log_err(_ERROR_ADD_TICKER.format(store_key=store_key)) return if interval not in self.tickers: self.tickers[interval] = self.ticker_class(interval) self.tickers[interval].add(store_key, *args, **kwargs)
def check_evennia_dependencies(): """ Checks the versions of Evennia's dependencies including making some checks for runtime libraries. Returns: result (bool): `False` if a show-stopping version mismatch is found. """ # check main dependencies from evennia.server.evennia_launcher import check_main_evennia_dependencies not_error = check_main_evennia_dependencies() errstring = "" # South is no longer used ... if "south" in settings.INSTALLED_APPS: errstring += ( "\n ERROR: 'south' found in settings.INSTALLED_APPS. " "\n South is no longer used. If this was added manually, remove it." ) not_error = False # IRC support if settings.IRC_ENABLED: try: import twisted.words twisted.words # set to avoid debug info about not-used import except ImportError: errstring += ( "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it." "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others" "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords." ) not_error = False errstring = errstring.strip() if errstring: mlen = max(len(line) for line in errstring.split("\n")) logger.log_err("%s\n%s\n%s" % ("-" * mlen, errstring, "-" * mlen)) return not_error
def mask(self, msg): """ Masks potentially sensitive user information within messages before writing to log. Recording cleartext password attempts is bad policy. Args: msg (str): Raw text string sent from client <-> server Returns: msg (str): Text string with sensitive information masked out. """ # Check to see if the command is embedded within server output _msg = msg is_embedded = False match = re.match(".*Command.*'(.+)'.*is not available.*", msg, flags=re.IGNORECASE) if match: msg = match.group(1).replace('\\', '') submsg = msg is_embedded = True for mask in AUDIT_MASKS: for command, regex in mask.iteritems(): try: match = re.match(regex, msg, flags=re.IGNORECASE) except Exception as e: logger.log_err(regex) logger.log_err(e) continue if match: term = match.group('secret') masked = re.sub(term, '*' * len(term.zfill(8)), msg) if is_embedded: msg = re.sub(submsg, '%s <Masked: %s>' % (masked, command), _msg, flags=re.IGNORECASE) else: msg = masked return msg return _msg
def func(self): "Buy a goods." caller = self.caller if not self.args: caller.msg({"alert":_("You should buy something.")}) return goods = caller.search(self.args) if not goods: caller.msg({"alert":_("Can not find this goods.")}) return # buy goods try: goods.sell_to(caller) except Exception, e: caller.msg({"alert":_("Can not buy this goods.")}) logger.log_err("Can not buy %s: %s" % (goods.get_data_key(), e)) return
def create_help_entry(key, entrytext, category="General", locks=None, aliases=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. Args: key (str): The name of the help entry. entrytext (str): The body of te help entry category (str, optional): The help category of the entry. locks (str, optional): A lockstring to restrict access. aliases (list of str): List of alternative (likely shorter) keynames. Returns: help (HelpEntry): A newly created help entry. """ global _HelpEntry if not _HelpEntry: from evennia.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) if aliases: new_help.aliases.add(aliases) new_help.save() return new_help except IntegrityError: string = "Could not add help entry: key '%s' already exists." % key logger.log_err(string) return None except Exception: logger.log_trace() return None