diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 9ea9707498..9e8199d546 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -10,7 +10,7 @@ from evennia.objects.models import ObjectDB from evennia.locks.lockhandler import LockException from evennia.commands.cmdhandler import get_and_merge_cmdsets from evennia.utils import create, utils, search -from evennia.utils.utils import inherits_from, class_from_module +from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses from evennia.utils.eveditor import EvEditor from evennia.utils.evmore import EvMore from evennia.utils.spawner import (spawn, search_prototype, list_prototypes, @@ -1702,6 +1702,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): object - basically making this a new clean object. force - change to the typeclass also if the object already has a typeclass of the same name. + list - show available typeclasses. Example: @type button = examples.red_button.RedButton @@ -1733,6 +1734,26 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): caller = self.caller + if 'list' in self.switches: + tclasses = get_all_typeclasses() + print(list(tclasses.keys())) + contribs = [key for key in sorted(tclasses) + if key.startswith("evennia.contrib")] or [""] + core = [key for key in sorted(tclasses) + if key.startswith("evennia") and key not in contribs] or [""] + game = [key for key in sorted(tclasses) + if not key.startswith("evennia")] or [""] + string = ("|wCore typeclasses|n\n" + " {core}\n" + "|wLoaded Contrib typeclasses|n\n" + " {contrib}\n" + "|wGame-dir typeclasses|n\n" + " {game}").format(core="\n ".join(core), + contrib="\n ".join(contribs), + game="\n ".join(game)) + caller.msg(string) + return + if not self.args: caller.msg("Usage: %s [= typeclass]" % self.cmdstring) return diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 10621f0feb..a8d2171f75 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -20,12 +20,13 @@ import textwrap import random from os.path import join as osjoin from importlib import import_module -from inspect import ismodule, trace, getmembers, getmodule +from inspect import ismodule, trace, getmembers, getmodule, getmro from collections import defaultdict, OrderedDict from twisted.internet import threads, reactor, task from django.conf import settings from django.utils import timezone from django.utils.translation import ugettext as _ +from django.apps import apps from evennia.utils import logger _MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE @@ -1879,3 +1880,22 @@ def get_game_dir_path(): else: os.chdir(os.pardir) raise RuntimeError("server/conf/settings.py not found: Must start from inside game dir.") + + +def get_all_typeclasses(): + """ + List available typeclasses from all available modules. + + Returns: + typeclasses (dict): On the form {"typeclass.path": typeclass, ...} + + Notes: + This will dynamicall retrieve all abstract django models inheriting at any distance + from the TypedObject base (aka a Typeclass) so it will work fine with any custom + classes being added. + + """ + from evennia.typeclasses.models import TypedObject + typeclasses = {"{}.{}".format(model.__module__, model.__name__): model + for model in apps.get_models() if TypedObject in getmro(model)} + return typeclasses