diff --git a/contrib/email-login.py b/contrib/email-login.py index 34ca98f8a..fd1020f4d 100644 --- a/contrib/email-login.py +++ b/contrib/email-login.py @@ -38,7 +38,7 @@ from django.contrib.auth.models import User from src.players.models import PlayerDB from src.objects.models import ObjectDB from src.server.models import ServerConfig -from src.comms.models import Channel +from src.comms.models import ChannelDB from src.commands.cmdset import CmdSet from src.utils import create, logger, utils, ansi @@ -231,7 +231,7 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: - pchannel = Channel.objects.get_channel(pchanneldef[0]) + pchannel = ChannelDB.objects.get_channel(pchanneldef[0]) if not pchannel.connect_to(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) diff --git a/contrib/evlang/evlang.py b/contrib/evlang/evlang.py index 0da2b90e9..21c2997ab 100644 --- a/contrib/evlang/evlang.py +++ b/contrib/evlang/evlang.py @@ -97,7 +97,7 @@ _LOGGER = None #------------------------------------------------------------ # specifically forbidden symbols -_EV_UNALLOWED_SYMBOLS = ["attr", "set_attribute", "delete"] +_EV_UNALLOWED_SYMBOLS = ["attr", "attributes", "delete"] try: _EV_UNALLOWED_SYMBOLS.expand(settings.EVLANG_UNALLOWED_SYMBOLS) except AttributeError: pass @@ -285,7 +285,7 @@ class Evlang(object): if scripts: self.evlang_scripts.update(scripts) if self.obj: - self.evlang_scripts.update(obj.attr(storage_attr)) + self.evlang_scripts.update(obj.attributes.get(storage_attr)) self.safe_context = _EV_SAFE_CONTEXT # set by default + settings if safe_context: self.safe_context.update(safe_context) @@ -401,7 +401,7 @@ class Evlang(object): self.evlang_scripts[scriptname] = (codestring, scripter) if self.obj: # save to database - self.obj.attr(self.evlang_storage_attr, self.evlang_scripts) + self.obj.attributes.add(self.evlang_storage_attr, self.evlang_scripts) def delete(self, scriptname): """ @@ -411,7 +411,7 @@ class Evlang(object): del self.evlang_scripts[scriptname] if self.obj: # update change to database - self.obj.attr(self.evlang_storage_attr, self.evlang_scripts) + self.obj.attributes.add(self.evlang_storage_attr, self.evlang_scripts) #---------------------------------------------------------------------- diff --git a/contrib/lineeditor.py b/contrib/lineeditor.py index 0248a408a..bd1667903 100644 --- a/contrib/lineeditor.py +++ b/contrib/lineeditor.py @@ -190,7 +190,7 @@ class CmdEditorGroup(CmdEditorBase): string = editor.display_buffer(buf=buf, offset=lstart, linenums=False) else: string = editor.display_buffer(linenums=False) - self.caller.msg(string, data={"raw":True}) + self.caller.msg(string, raw=True) return elif cmd == ":::": # Insert single colon alone on a line @@ -635,14 +635,14 @@ class CmdEditor(Command): # hook save/load functions def load_attr(): "inital loading of buffer data from given attribute." - target = self.obj.get_attribute(self.attrname) + target = self.obj.attributes.get(self.attrname) if target != None and not isinstance(target, basestring): typ = type(target).__name__ self.caller.msg("{RWARNING! Saving this buffer will overwrite the current attribute (of type %s) with a string!{n" % typ) return target and str(target) or "" def save_attr(): "Save line buffer to given attribute name. This should return True if successful and also report its status." - self.obj.set_attribute(self.attrname, self.editor.buffer) + self.obj.attributes.add(self.attrname, self.editor.buffer) self.caller.msg("Saved.") return True def quit_hook(): diff --git a/contrib/tutorial_world/objects.py b/contrib/tutorial_world/objects.py index 2d033372a..9e293a254 100644 --- a/contrib/tutorial_world/objects.py +++ b/contrib/tutorial_world/objects.py @@ -559,7 +559,7 @@ class CrumblingWall(TutorialObject, Exit): "called when the object is first created." super(CrumblingWall, self).at_object_creation() - self.aliases = ["secret passage", "passage", "crack", "opening", "secret door"] + self.aliases.add(["secret passage", "passage", "crack", "opening", "secret door"]) # this is assigned first when pushing button, so assign this at creation time! self.db.destination = 2 @@ -808,7 +808,7 @@ class CmdGetWeapon(Command): "Implement the command" rack_id = self.obj.db.rack_id - if self.caller.get_attribute(rack_id): + if self.caller.attributes.get(rack_id): # we don't allow a player to take more than one weapon from rack. self.caller.msg("%s has no more to offer you." % self.obj.name) else: @@ -826,7 +826,7 @@ class CmdGetWeapon(Command): else: self.caller.msg(ostring) # tag the caller so they cannot keep taking objects from the rack. - self.caller.set_attribute(rack_id, True) + self.caller.attributes.add(rack_id, True) class CmdSetWeaponRack(CmdSet): diff --git a/contrib/tutorial_world/scripts.py b/contrib/tutorial_world/scripts.py index 1cc42f7a3..cffb32efe 100644 --- a/contrib/tutorial_world/scripts.py +++ b/contrib/tutorial_world/scripts.py @@ -1,5 +1,5 @@ """ -This defines some generally useful scripts for the tutorial world. +This defines some generally useful scripts for the tutorial world. """ import random @@ -9,11 +9,11 @@ from ev import Script # # IrregularEvent - script firing at random intervals # -# This is a generally useful script for updating +# This is a generally useful script for updating # objects at irregular intervals. This is used by as diverse -# entities as Weather rooms and mobs. +# entities as Weather rooms and mobs. +# # -# # #------------------------------------------------------------ @@ -22,7 +22,7 @@ class IrregularEvent(Script): This script, which should be tied to a particular object upon instantiation, calls update_irregular on the object at random intervals. - """ + """ def at_script_creation(self): "This setups the script" @@ -30,7 +30,7 @@ class IrregularEvent(Script): self.desc = "Updates at irregular intervals" self.interval = random.randint(30, 70) # interval to call. self.start_delay = True # wait at least self.interval seconds before calling at_repeat the first time - self.persistent = True + self.persistent = True # this attribute determines how likely it is the # 'update_irregular' method gets called on self.obj (value is @@ -69,7 +69,7 @@ class FastIrregularEvent(IrregularEvent): # # # # Note that this will of course allow a single player to end up with multiple versions of objects if # # they just wait around between resets; In a real game environment this would have to be resolved e.g. -# # with custom versions of the 'get' command not accepting doublets. +# # with custom versions of the 'get' command not accepting doublets. # # # # setting up an event for reseting the world. @@ -91,20 +91,20 @@ class FastIrregularEvent(IrregularEvent): # #this you see when running @ps in game: # self.description = 'Reset the tutorial world .' # self.interval = UPDATE_INTERVAL -# self.persistent = True +# self.persistent = True # def event_function(self): # """ # This is called every self.interval seconds. # """ # #find all objects inheriting the subscribing parents -# for parent in RESET_SUBSCRIBERS: +# for parent in RESET_SUBSCRIBERS: # objects = Object.objects.global_object_script_parent_search(parent) # for obj in objects: -# try: +# try: # obj.scriptlink.reset() # except: # logger.log_errmsg(traceback.print_exc()) - + diff --git a/ev.py b/ev.py index ce7f3b85a..feb52d928 100644 --- a/ev.py +++ b/ev.py @@ -116,9 +116,10 @@ README = __doc__ # help entries from src.help.models import HelpEntry +from src.typeclasses.models import Attribute # players from src.players.player import Player -from src.players.models import PlayerDB, PlayerAttribute, PlayerNick +from src.players.models import PlayerDB # commands from src.commands.command import Command @@ -132,7 +133,7 @@ from src.locks import lockfuncs from src.scripts.scripts import Script # comms -from src.comms.models import Msg, Channel, PlayerChannelConnection, ExternalChannelConnection +from src.comms.models import Msg, ChannelDB, PlayerChannelConnection, ExternalChannelConnection # objects from src.objects.objects import Object, Character, Room, Exit @@ -193,7 +194,7 @@ class DBmanagers(_EvContainer): from src.help.models import HelpEntry from src.players.models import PlayerDB from src.scripts.models import ScriptDB - from src.comms.models import Msg, Channel, PlayerChannelConnection, ExternalChannelConnection + from src.comms.models import Msg, ChannelDB, PlayerChannelConnection, ExternalChannelConnection from src.objects.models import ObjectDB from src.server.models import ServerConfig @@ -201,12 +202,12 @@ class DBmanagers(_EvContainer): players = PlayerDB.objects scripts = ScriptDB.objects msgs = Msg.objects - channels = Channel.objects + channels = ChannelDB.objects connections = PlayerChannelConnection.objects externalconnections = ExternalChannelConnection.objects objects = ObjectDB.objects serverconfigs = ServerConfig.objects - del HelpEntry, PlayerDB, ScriptDB, Msg, Channel, PlayerChannelConnection, + del HelpEntry, PlayerDB, ScriptDB, Msg, ChannelDB, PlayerChannelConnection, del ExternalChannelConnection, ObjectDB, ServerConfig managers = DBmanagers() diff --git a/game/evennia.py b/game/evennia.py index a42835b35..b17a97743 100755 --- a/game/evennia.py +++ b/game/evennia.py @@ -129,11 +129,9 @@ PORTAL_LOGFILE = settings.PORTAL_LOG_FILE # Check so a database exists and is accessible from django.db import DatabaseError -from src.objects.models import ObjectDB +from src.players.models import PlayerDB try: - test = ObjectDB.objects.get(id=1) -except ObjectDB.DoesNotExist: - pass # this is fine at this point + superuser = PlayerDB.objects.get(id=1) except DatabaseError,e: print """ Your database does not seem to be set up correctly. @@ -147,6 +145,11 @@ except DatabaseError,e: When you have a database set up, rerun evennia.py. """ % e sys.exit() +except PlayerDB.DoesNotExist: + # no superuser yet. We need to create it. + from django.core.management import call_command + print "\nCreate a superuser below. The superuser is Player #1, the 'owner' account of the server.\n" + call_command("createsuperuser", interactive=True) # Add this to the environmental variable for the 'twistd' command. currpath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -403,6 +406,7 @@ def error_check_python_modules(): deprstring = "settings.%s should be renamed to %s. If defaults are used, their path/classname must be updated (see src/settings_default.py)." if hasattr(settings, "CMDSET_DEFAULT"): raise DeprecationWarning(deprstring % ("CMDSET_DEFAULT", "CMDSET_CHARACTER")) if hasattr(settings, "CMDSET_OOC"): raise DeprecationWarning(deprstring % ("CMDSET_OOC", "CMDSET_PLAYER")) + if settings.WEBSERVER_ENABLED and not isinstance(settings.WEBSERVER_PORTS[0], tuple): raise DeprecationWarning("settings.WEBSERVER_PORTS must be on the form [(proxyport, serverport), ...]") from src.commands import cmdsethandler if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None): print "Warning: CMDSET_UNLOGGED failed to load!" diff --git a/game/gamesrc/objects/examples/object.py b/game/gamesrc/objects/examples/object.py index 0939f7c48..bdd32e528 100644 --- a/game/gamesrc/objects/examples/object.py +++ b/game/gamesrc/objects/examples/object.py @@ -71,8 +71,8 @@ class Object(DefaultObject): search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False) execute_cmd(raw_string) - msg(message, from_obj=None, data=None) - msg_contents(message, exclude=None, from_obj=None, data=None) + msg(text=None, **kwargs) + msg_contents(message, exclude=None, from_obj=None, **kwargs) move_to(destination, quiet=False, emit_to_obj=None, use_destination=True) copy(new_key=None) delete() @@ -113,9 +113,9 @@ class Object(DefaultObject): at_after_traverse(traversing_object, source_location) - (exit-objects only) called just after a traversal has happened. at_failed_traverse(traversing_object) - (exit-objects only) called if traversal fails and property err_traverse is not defined. - at_msg_receive(self, msg, from_obj=None, data=None) - called when a message (via self.msg()) is sent to this obj. + at_msg_receive(self, msg, from_obj=None, **kwargs) - called when a message (via self.msg()) is sent to this obj. If returns false, aborts send. - at_msg_send(self, msg, to_obj=None, data=None) - called when this objects sends a message to someone via self.msg(). + at_msg_send(self, msg, to_obj=None, **kwargs) - called when this objects sends a message to someone via self.msg(). return_appearance(looker) - describes this object. Used by "look" command by default at_desc(looker=None) - called by 'look' whenever the appearance is requested. diff --git a/game/gamesrc/objects/examples/player.py b/game/gamesrc/objects/examples/player.py index aa113ed87..843e0cf2c 100644 --- a/game/gamesrc/objects/examples/player.py +++ b/game/gamesrc/objects/examples/player.py @@ -59,7 +59,7 @@ class Player(DefaultPlayer): * Helper methods - msg(outgoing_string, from_obj=None, data=None) + msg(text=None, **kwargs) swap_character(new_character, delete_old_character=False) execute_cmd(raw_string, sessid=None) search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False) diff --git a/game/runner.py b/game/runner.py index e1e0d292f..584183ac8 100644 --- a/game/runner.py +++ b/game/runner.py @@ -50,7 +50,7 @@ from django.conf import settings # Setup access of the evennia server itself SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py') -PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal.py') +PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal/portal.py') # Get logfile names SERVER_LOGFILE = settings.SERVER_LOG_FILE diff --git a/locale/sv/LC_MESSAGES/django.po b/locale/sv/LC_MESSAGES/django.po index c3eb93fd3..ab1016686 100644 --- a/locale/sv/LC_MESSAGES/django.po +++ b/locale/sv/LC_MESSAGES/django.po @@ -37,7 +37,7 @@ msgid " Type \"help\" for help." msgstr "Skriv \"help\" för hjälp." #: src/commands/cmdhandler.py:212 -msgid "There where multiple matches." +msgid "There were multiple matches." msgstr "Det fanns många träffar." #: src/commands/cmdparser.py:144 diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index 44e6860a3..0a3e7bbde 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -48,6 +48,8 @@ from django.utils.translation import ugettext as _ __all__ = ("cmdhandler",) +_GA = object.__getattribute__ + # This decides which command parser is to be used. # You have to restart the server for changes to take effect. _COMMAND_PARSER = utils.variable_from_module(*settings.COMMAND_PARSER.rsplit('.', 1)) @@ -83,65 +85,112 @@ class ExecSystemCommand(Exception): # Helper function @inlineCallbacks -def get_and_merge_cmdsets(caller): +def get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid=None): """ - Gather all relevant cmdsets and merge them. Note - that this is only relevant for logged-in callers. + Gather all relevant cmdsets and merge them. + + callertype is one of "session", "player" or "object" dependin + on which level the cmdhandler is invoked. Session includes the + cmdsets available to Session, Player and its eventual puppeted Object. + Player-level include cmdsets on Player and Object, while calling + the handler on an Object only includes cmdsets on itself. + + The cdmsets are merged in order generality, so that the Object's + cmdset is merged last (and will thus take precedence over + same-named and same-prio commands on Player and Session). Note that this function returns a deferred! """ - # The calling object's cmdset - try: - yield caller.at_cmdset_get() - except Exception: - logger.log_trace() - try: - caller_cmdset = caller.cmdset.current - except AttributeError: - caller_cmdset = None + local_obj_cmdsets = [None] - # Create cmdset for all player's available channels - channel_cmdset = None - if not caller_cmdset.no_channels: - channel_cmdset = yield CHANNELHANDLER.get_cmdset(caller) + @inlineCallbacks + def _get_channel_cmdsets(player, player_cmdset): + "Channel-cmdsets" + # Create cmdset for all player's available channels + channel_cmdset = None + if not player_cmdset.no_channels: + channel_cmdset = yield CHANNELHANDLER.get_cmdset(player) + returnValue(channel_cmdset) - # Gather cmdsets from location, objects in location or carried - local_objects_cmdsets = [None] - try: - location = caller.location - except Exception: - location = None - if location and not caller_cmdset.no_objs: - # Gather all cmdsets stored on objects in the room and - # also in the caller's inventory and the location itself - local_objlist = yield location.contents_get(exclude=caller.dbobj) + caller.contents + [location] - for obj in local_objlist: - try: - # call hook in case we need to do dynamic changing to cmdset - yield obj.at_cmdset_get() - except Exception: - logger.log_trace() - # the call-type lock is checked here, it makes sure a player is not seeing e.g. the commands - # on a fellow player (which is why the no_superuser_bypass must be True) - local_objects_cmdsets = yield [obj.cmdset.current for obj in local_objlist - if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))] - for cset in local_objects_cmdsets: - #This is necessary for object sets, or we won't be able to separate - #the command sets from each other in a busy room. - cset.old_duplicates = cset.duplicates - cset.duplicates = True + @inlineCallbacks + def _get_local_obj_cmdsets(obj, obj_cmdset): + "Object-level cmdsets" + # Gather cmdsets from location, objects in location or carried + local_obj_cmdsets = [None] + try: + location = obj.location + except Exception: + location = None + if location and not obj_cmdset.no_objs: + # Gather all cmdsets stored on objects in the room and + # also in the caller's inventory and the location itself + local_objlist = yield location.contents_get(exclude=obj.dbobj) + obj.contents + [location] + for lobj in local_objlist: + try: + # call hook in case we need to do dynamic changing to cmdset + _GA(lobj, "at_cmdset_get")() + except Exception: + logger.log_trace() + # the call-type lock is checked here, it makes sure a player is not seeing e.g. the commands + # on a fellow player (which is why the no_superuser_bypass must be True) + local_obj_cmdsets = yield [lobj.cmdset.current for lobj in local_objlist + if (lobj.cmdset.current and lobj.locks.check(caller, 'call', no_superuser_bypass=True))] + for cset in local_obj_cmdsets: + #This is necessary for object sets, or we won't be able to separate + #the command sets from each other in a busy room. + cset.old_duplicates = cset.duplicates + cset.duplicates = True + returnValue(local_obj_cmdsets) - # Player object's commandsets - try: - player_cmdset = caller.player.cmdset.current - except AttributeError: - player_cmdset = None + @inlineCallbacks + def _get_cmdset(obj): + "Get cmdset, triggering all hooks" + try: + yield obj.at_cmdset_get() + except Exception: + logger.log_trace() + try: + returnValue(obj.cmdset.current) + except AttributeError: + returnValue(None) + + if callertype == "session": + # we are calling the command from the session level + report_to = session + session_cmdset = yield _get_cmdset(session) + cmdsets = [session_cmdset] + if player: # this automatically implies logged-in + player_cmdset = yield _get_cmdset(player) + channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset) + cmdsets.extend([player_cmdset, channel_cmdset]) + if obj: + obj_cmdset = yield _get_cmdset(obj) + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset) + cmdsets.extend([obj_cmdset] + local_obj_cmdsets) + elif callertype == "player": + # we are calling the command from the player level + report_to = player + player_cmdset = yield _get_cmdset(player) + channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset) + cmdsets = [player_cmdset, channel_cmdset] + if obj: + obj_cmdset = yield _get_cmdset(obj) + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset) + cmdsets.extend([obj_cmdset] + local_obj_cmdsets) + elif callertype == "object": + # we are calling the command from the object level + report_to = obj + obj_cmdset = yield _get_cmdset(obj) + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset) + cmdsets = [obj_cmdset] + local_obj_cmdsets + else: + raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype) + #cmdsets = yield [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_obj_cmdsets - cmdsets = yield [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets # weed out all non-found sets cmdsets = yield [cmdset for cmdset in cmdsets if cmdset and cmdset.key!="Empty"] # report cmdset errors to user (these should already have been logged) - yield [caller.msg(cmdset.errmessage) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR"] + yield [report_to.msg(cmdset.errmessage) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR"] if cmdsets: # we group and merge all same-prio cmdsets separately (this avoids order-dependent @@ -167,7 +216,7 @@ def get_and_merge_cmdsets(caller): else: cmdset = None - for cset in (cset for cset in local_objects_cmdsets if cset): + for cset in (cset for cset in local_obj_cmdsets if cset): cset.duplicates = cset.old_duplicates returnValue(cmdset) @@ -175,21 +224,49 @@ def get_and_merge_cmdsets(caller): # Main command-handler function @inlineCallbacks -def cmdhandler(caller, raw_string, testing=False, sessid=None): +def cmdhandler(called_on, raw_string, testing=False, callertype="session", sessid=None): """ This is the main function to handle any string sent to the engine. - caller - calling object + called_on - 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. - sessid - the session id calling this handler, if any + 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. + Note that this function returns a deferred! """ + session, player, obj = None, None, None + if callertype == "session": + session = called_on + player = session.player + if player: + obj = yield _GA(player.dbobj, "get_puppet")(session.sessid) + elif callertype == "player": + player = called_on + if sessid: + obj = yield _GA(player.dbobj, "get_puppet")(sessid) + elif callertype == "object": + obj = called_on + else: + raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype) + + # the caller will be the one to receive messages and excert its permissions. + # we assign the caller with preference 'bottom up' + caller = obj or player or session + try: # catch bugs in cmdhandler itself try: # catch special-type commands - cmdset = yield get_and_merge_cmdsets(caller) + cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid) if not cmdset: # this is bad and shouldn't happen. raise NoCmdSets @@ -212,13 +289,15 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): 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. + # We have a unique command match. But it may still be invalid. match = matches[0] cmdname, args, cmd = match[0], match[1], match[2] @@ -232,9 +311,12 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: + # use custom CMD_NOMATH command sysarg = raw_string else: + # fallback to default error text sysarg = _("Command '%s' is not available.") % raw_string + cmdset.get_all_cmd_keys_and_aliases(caller) 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) @@ -243,7 +325,7 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): raise ExecSystemCommand(syscmd, sysarg) - # Check if this is a Channel match. + # 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. @@ -251,7 +333,7 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): if syscmd: # replace system command with custom version cmd = syscmd - cmd.sessid = sessid + cmd.sessid = session.sessid if session else None sysarg = "%s:%s" % (cmdname, args) raise ExecSystemCommand(cmd, sysarg) @@ -262,11 +344,16 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): cmd.cmdstring = cmdname cmd.args = args cmd.cmdset = cmdset - cmd.sessid = sessid + 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. + # cmd.obj is automatically made available by the cmdhandler. # we make sure to validate its scripts. yield cmd.obj.scripts.validate() @@ -312,7 +399,7 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): syscmd.cmdstring = syscmd.key syscmd.args = sysarg syscmd.cmdset = cmdset - syscmd.sessid = sessid + syscmd.sessid = session.sessid if session else None syscmd.raw_string = unformatted_raw_string if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'): diff --git a/src/commands/cmdparser.py b/src/commands/cmdparser.py index 386ed8f0c..514eb4340 100644 --- a/src/commands/cmdparser.py +++ b/src/commands/cmdparser.py @@ -245,7 +245,7 @@ def at_multimatch_cmd(caller, matches): """ Format multiple command matches to a useful error. """ - string = "There where multiple matches:" + string = "There were multiple matches:" for num, match in enumerate(matches): # each match is a tuple (candidate, cmd) cmdname, arg, cmd, dum, dum = match @@ -262,7 +262,7 @@ def at_multimatch_cmd(caller, matches): id1 = "" id2 = "" - if not (is_channel or is_exit) and (hasattr(cmd, 'obj') and cmd.obj != caller): + if not (is_channel or is_exit) and (hasattr(cmd, 'obj') and cmd.obj != caller) and hasattr(cmd.obj, "key"): # the command is defined on some other object id1 = "%s-%s" % (num + 1, cmdname) id2 = " (%s)" % (cmd.obj.key) diff --git a/src/commands/command.py b/src/commands/command.py index 0c65a9087..280788515 100644 --- a/src/commands/command.py +++ b/src/commands/command.py @@ -206,7 +206,7 @@ class Command(object): """ return self.lockhandler.check(srcobj, access_type, default=default) - def msg(self, msg="", to_obj=None, from_obj=None, data=None, sessid=None, all_sessions=False): + def msg(self, msg="", to_obj=None, from_obj=None, sessid=None, all_sessions=False, **kwargs): """ This is a shortcut instad of calling msg() directly on an object - it will detect if caller is an Object or a Player and also appends self.sessid @@ -234,7 +234,7 @@ class Command(object): # if to_obj is a different Player, all their sessions # will be notified unless sessid was given specifically sessid = None - to_obj.msg(msg, from_obj=from_obj, data=data, sessid=sessid) + to_obj.msg(msg, from_obj=from_obj, sessid=sessid, **kwargs) # Common Command hooks diff --git a/src/commands/default/admin.py b/src/commands/default/admin.py index 4eb8e6ec7..51d4afbdb 100644 --- a/src/commands/default/admin.py +++ b/src/commands/default/admin.py @@ -298,12 +298,8 @@ class CmdDelPlayer(MuxCommand): string = "No Player nor User found matching '%s'." % args self.msg(string) return - try: - player = user.get_profile() - except Exception: - player = None - if player and not player.access(caller, 'delete'): + if user and not user.access(caller, 'delete'): string = "You don't have the permissions to delete this player." self.msg(string) return @@ -311,9 +307,9 @@ class CmdDelPlayer(MuxCommand): string = "" name = user.username user.delete() - if player: - name = player.name - player.delete() + if user: + name = user.name + user.delete() string = "Player %s was deleted." % name else: string += "The User %s was deleted. It had no Player associated with it." % name @@ -322,16 +318,16 @@ class CmdDelPlayer(MuxCommand): elif utils.is_iter(players): string = "There were multiple matches:" - for player in players: - string += "\n %s %s" % (player.id, player.key) + for user in players: + string += "\n %s %s" % (user.id, user.key) return else: # one single match - player = players - user = player.user + user = players + user = user.user - if not player.access(caller, 'delete'): + if not user.access(caller, 'delete'): string = "You don't have the permissions to delete that player." self.msg(string) return @@ -342,12 +338,12 @@ class CmdDelPlayer(MuxCommand): string = "\nYour account '%s' is being *permanently* deleted.\n" % uname if reason: string += " Reason given:\n '%s'" % reason - player.unpuppet_all() - for session in SESSIONS.sessions_from_player(player): - player.msg(string, sessid=session.sessid) - player.disconnect_session_from_player(session.sessid) + user.unpuppet_all() + for session in SESSIONS.sessions_from_player(user): + user.msg(string, sessid=session.sessid) + user.disconnect_session_from_player(session.sessid) + user.delete() user.delete() - player.delete() self.msg("Player %s was successfully deleted." % uname) @@ -420,9 +416,8 @@ class CmdEmit(MuxCommand): continue if obj.access(caller, 'tell'): obj.msg(message) - if send_to_contents: - for content in obj.contents: - content.msg(message) + if send_to_contents and hasattr(obj, "msg_contents"): + obj.msg_contents(message) caller.msg("Emitted to %s and its contents." % objname) else: caller.msg("Emitted to %s." % objname) @@ -512,10 +507,10 @@ class CmdPerm(MuxCommand): return string = "Permissions on {w%s{n: " % obj.key - if not obj.permissions: + if not obj.permissions.all(): string += "" else: - string += ", ".join(obj.permissions) + string += ", ".join(obj.permissions.all()) if hasattr(obj, 'player') and hasattr(obj.player, 'is_superuser') and obj.player.is_superuser: string += "\n(... but this object is currently controlled by a SUPERUSER! " string += "All access checks are passed automatically.)" @@ -532,20 +527,12 @@ class CmdPerm(MuxCommand): tstring = "" if 'del' in switches: # delete the given permission(s) from object. - for perm in self.rhslist: - try: - index = obj.permissions.index(perm) - except ValueError: - cstring += "\nPermission '%s' was not defined on %s." % (perm, obj.name) - continue - permissions = obj.permissions - del permissions[index] - obj.permissions = permissions - cstring += "\nPermission '%s' was removed from %s." % (perm, obj.name) - tstring += "\n%s revokes the permission '%s' from you." % (caller.name, perm) + obj.permissions.remove(self.rhslist) + cstring += "\nPermission(s) %s removed from %s (if they existed)." % (", ".join(self.rhslist), obj.name) + tstring += "\n%s revokes the permission(s) %s from you." % (caller.name, ", ".join(self.rhslist)) else: # add a new permission - permissions = obj.permissions + permissions = obj.permissions.all() for perm in self.rhslist: @@ -558,8 +545,7 @@ class CmdPerm(MuxCommand): if perm in permissions: cstring += "\nPermission '%s' is already defined on %s." % (rhs, obj.name) else: - permissions.append(perm) - obj.permissions = permissions + obj.permissions.add(perm) cstring += "\nPermission '%s' given to %s." % (rhs, obj.name) tstring += "\n%s gives you the permission '%s'." % (caller.name, rhs) caller.msg(cstring.strip()) diff --git a/src/commands/default/building.py b/src/commands/default/building.py index 82d229d02..94e60424d 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -4,8 +4,7 @@ Building and world design commands """ from django.conf import settings -from src.objects.models import ObjectDB, ObjAttribute -from src.players.models import PlayerAttribute +from src.objects.models import ObjectDB from src.utils import create, utils from src.utils.ansi import raw from src.commands.default.muxcommand import MuxCommand @@ -124,7 +123,7 @@ class CmdSetObjAlias(MuxCommand): return if self.rhs == None: # no =, so we just list aliases on object. - aliases = obj.aliases + aliases = obj.aliases.all() if aliases: caller.msg("Aliases for '%s': %s" % (obj.key, ", ".join(aliases))) else: @@ -137,24 +136,24 @@ class CmdSetObjAlias(MuxCommand): if not self.rhs: # we have given an empty =, so delete aliases - old_aliases = obj.aliases + old_aliases = obj.aliases.all() if old_aliases: caller.msg("Cleared aliases from %s: %s" % (obj.key, ", ".join(old_aliases))) - del obj.dbobj.aliases + obj.dbobj.db_aliases.clear() else: caller.msg("No aliases to clear.") return # merge the old and new aliases (if any) - old_aliases = obj.aliases + old_aliases = obj.aliases.all() new_aliases = [alias.strip().lower() for alias in self.rhs.split(',') if alias.strip()] # make the aliases only appear once old_aliases.extend(new_aliases) aliases = list(set(old_aliases)) # save back to object. - obj.aliases = aliases + obj.aliases.add(aliases) # we treat this as a re-caching (relevant for exits to re-build their exit commands with the correct aliases) - caller.msg("Aliases for '%s' are now set to %s." % (obj.key, ", ".join(obj.aliases))) + caller.msg("Alias(es) for '%s' set to %s." % (obj.key, str(obj.aliases))) class CmdCopy(ObjManipCommand): """ @@ -192,7 +191,7 @@ class CmdCopy(ObjManipCommand): if not from_obj: return to_obj_name = "%s_copy" % from_obj_name - to_obj_aliases = ["%s_copy" % alias for alias in from_obj.aliases] + to_obj_aliases = ["%s_copy" % alias for alias in from_obj.aliases.all()] copiedobj = ObjectDB.objects.copy_object(from_obj, new_key=to_obj_name, new_aliases=to_obj_aliases) if copiedobj: @@ -282,10 +281,10 @@ class CmdCpAttr(ObjManipCommand): if not from_obj or not to_objs: caller.msg("You have to supply both source object and target(s).") return - if not from_obj.has_attribute(from_obj_attrs[0]): + if not from_obj.attributes.has(from_obj_attrs[0]): caller.msg("%s doesn't have an attribute %s." % (from_obj_name, from_obj_attrs[0])) return - srcvalue = from_obj.get_attribute(from_obj_attrs[0]) + srcvalue = from_obj.attributes.get(from_obj_attrs[0]) #copy to all to_obj:ects if "move" in self.switches: @@ -308,7 +307,7 @@ class CmdCpAttr(ObjManipCommand): # if there are too few attributes given # on the to_obj, we copy the original name instead. to_attr = from_attr - to_obj.set_attribute(to_attr, srcvalue) + to_obj.attributes.add(to_attr, srcvalue) if "move" in self.switches and not (from_obj == to_obj and from_attr == to_attr): from_obj.del_attribute(from_attr) string += "\nMoved %s.%s -> %s.%s." % (from_obj.name, from_attr, @@ -599,8 +598,8 @@ class CmdDig(ObjManipCommand): aliases=room["aliases"], report_to=caller) new_room.locks.add(lockstring) alias_string = "" - if new_room.aliases: - alias_string = " (%s)" % ", ".join(new_room.aliases) + if new_room.aliases.all(): + alias_string = " (%s)" % ", ".join(new_room.aliases.all()) room_string = "Created room %s(%s)%s of type %s." % (new_room, new_room.dbref, alias_string, typeclass) @@ -627,8 +626,8 @@ class CmdDig(ObjManipCommand): aliases=to_exit["aliases"], locks=lockstring, destination=new_room, report_to=caller) alias_string = "" - if new_to_exit.aliases: - alias_string = " (%s)" % ", ".join(new_to_exit.aliases) + if new_to_exit.aliases.all(): + alias_string = " (%s)" % ", ".join(new_to_exit.aliases.all()) exit_to_string = "\nCreated Exit from %s to %s: %s(%s)%s." exit_to_string = exit_to_string % (location.name, new_room.name, new_to_exit, new_to_exit.dbref, alias_string) @@ -652,8 +651,8 @@ class CmdDig(ObjManipCommand): new_room, aliases=back_exit["aliases"], locks=lockstring, destination=location, report_to=caller) alias_string = "" - if new_back_exit.aliases: - alias_string = " (%s)" % ", ".join(new_back_exit.aliases) + if new_back_exit.aliases.all(): + alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all()) exit_back_string = "\nCreated Exit back from %s to %s: %s(%s)%s." exit_back_string = exit_back_string % (new_room.name, location.name, new_back_exit, new_back_exit.dbref, alias_string) @@ -980,7 +979,7 @@ class CmdName(ObjManipCommand): obj.name = newname astring = "" if aliases: - obj.aliases = aliases + [obj.aliases.add(alias) for alias in aliases] astring = " (%s)" % (", ".join(aliases)) # fix for exits - we need their exit-command to change name too if obj.destination: @@ -1038,7 +1037,7 @@ class CmdOpen(ObjManipCommand): if old_destination.id != destination.id: # reroute the old exit. exit_obj.destination = destination - exit_obj.aliases = exit_aliases + [exit_obj.aliases.add(alias) for alias in exit_aliases] string += " Rerouted its old destination '%s' to '%s' and changed aliases." % \ (old_destination.name, destination.name) else: @@ -1229,8 +1228,8 @@ class CmdSetAttribute(ObjManipCommand): if not attrs: attrs = [attr.key for attr in obj.get_all_attributes()] for attr in attrs: - if obj.has_attribute(attr): - string += "\nAttribute %s/%s = %s" % (obj.name, attr, obj.get_attribute(attr)) + if obj.attributes.has(attr): + string += "\nAttribute %s/%s = %s" % (obj.name, attr, obj.attributes.get(attr)) else: string += "\n%s has no attribute '%s'." % (obj.name, attr) # we view it without parsing markup. @@ -1239,9 +1238,9 @@ class CmdSetAttribute(ObjManipCommand): else: # deleting the attribute(s) for attr in attrs: - if obj.has_attribute(attr): - val = obj.get_attribute(attr) - obj.del_attribute(attr) + if obj.attributes.has(attr): + val = obj.attributes.has(attr) + obj.attributes.remove(attr) string += "\nDeleted attribute '%s' (= %s) from %s." % (attr, val, obj.name) else: string += "\n%s has no attribute '%s'." % (obj.name, attr) @@ -1249,7 +1248,7 @@ class CmdSetAttribute(ObjManipCommand): # setting attribute(s). Make sure to convert to real Python type before saving. for attr in attrs: try: - obj.set_attribute(attr, self.convert_from_string(value)) + obj.attributes.add(attr, self.convert_from_string(value)) string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value) except SyntaxError: # this means literal_eval tried to parse a faulty string @@ -1332,20 +1331,20 @@ class CmdTypeclass(MuxCommand): caller.msg("This object cannot have a type at all!") return - is_same = obj.is_typeclass(typeclass) + is_same = obj.is_typeclass(typeclass, exact=True) if is_same and not 'force' in self.switches: string = "%s already has the typeclass '%s'. Use /force to override." % (obj.name, typeclass) else: reset = "reset" in self.switches - old_typeclass_path = obj.typeclass.path + old_typeclass_path = obj.typeclass_path ok = obj.swap_typeclass(typeclass, clean_attributes=reset) if ok: if is_same: string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path) else: - string = "%s's changed typeclass from %s to %s.\n" % (obj.name, + string = "%s changed typeclass from %s to %s.\n" % (obj.name, old_typeclass_path, - obj.typeclass.path) + obj.typeclass_path) string += "Creation hooks were run." if reset: string += " All old attributes where deleted before the swap." @@ -1357,7 +1356,6 @@ class CmdTypeclass(MuxCommand): string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name, old_typeclass_path, typeclass) - caller.msg(string) @@ -1407,7 +1405,7 @@ class CmdWipe(ObjManipCommand): string = "Wiped all attributes on %s." % obj.name else: for attrname in attrs: - obj.attr(attrname, delete=True ) + obj.attributes.remove(attrname) string = "Wiped attributes %s on %s." string = string % (",".join(attrs), obj.name) caller.msg(string) @@ -1539,16 +1537,13 @@ class CmdExamine(ObjManipCommand): """ if attrname: - db_attr = [(attrname, obj.attr(attrname))] + db_attr = [(attrname, obj.attributes.get(attrname))] try: ndb_attr = [(attrname, object.__getattribute__(obj.ndb, attrname))] except Exception: ndb_attr = None else: - if self.player_mode: - db_attr = [(attr.key, attr.value) for attr in PlayerAttribute.objects.filter(db_obj=obj)] - else: - db_attr = [(attr.key, attr.value) for attr in ObjAttribute.objects.filter(db_obj=obj)] + db_attr = [(attr.key, attr.value) for attr in obj.db_attributes.all()] try: ndb_attr = [(aname, avalue) for aname, avalue in obj.ndb.__dict__.items() if not aname.startswith("_")] except Exception: @@ -1572,15 +1567,15 @@ class CmdExamine(ObjManipCommand): """ string = "\n{wName/key{n: {c%s{n (%s)" % (obj.name, obj.dbref) - if hasattr(obj, "aliases") and obj.aliases: - string += "\n{wAliases{n: %s" % (", ".join(utils.make_iter(obj.aliases))) + if hasattr(obj, "aliases") and obj.aliases.all(): + string += "\n{wAliases{n: %s" % (", ".join(utils.make_iter(str(obj.aliases)))) if hasattr(obj, "sessid") and obj.sessid: string += "\n{wsession{n: %s" % obj.sessid elif hasattr(obj, "sessions") and obj.sessions: string += "\n{wsession(s){n: %s" % (", ".join(str(sess.sessid) for sess in obj.sessions)) if hasattr(obj, "has_player") and obj.has_player: string += "\n{wPlayer{n: {c%s{n" % obj.player.name - perms = obj.player.permissions + perms = obj.player.permissions.all() if obj.player.is_superuser: perms = [""] elif not perms: @@ -1595,7 +1590,7 @@ class CmdExamine(ObjManipCommand): string += "\n{wDestination{n: %s" % obj.destination if obj.destination: string += " (#%s)" % obj.destination.id - perms = obj.permissions + perms = obj.permissions.all() if perms: perms_string = (", ".join(perms)) else: @@ -1677,7 +1672,7 @@ class CmdExamine(ObjManipCommand): caller.execute_cmd('look %s' % obj.name) return # using callback for printing result whenever function returns. - get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback) + get_and_merge_cmdsets(obj, self.session, self.player, obj, "session").addCallback(get_cmdset_callback) else: self.msg("You need to supply a target to examine.") return @@ -1712,7 +1707,7 @@ class CmdExamine(ObjManipCommand): caller.msg(self.format_attributes(obj, attrname, crop=False)) else: # using callback to print results whenever function returns. - get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback) + get_and_merge_cmdsets(obj, self.session, self.player, obj, "session").addCallback(get_cmdset_callback) class CmdFind(MuxCommand): @@ -1789,7 +1784,7 @@ class CmdFind(MuxCommand): nresults = results.count() if not nresults: # no matches on the keys. Try aliases instead. - results = results = ObjectDB.alias_set.related.model.objects.filter(db_key=searchstring) + results = ObjectDB.db_aliases.filter(db_key=searchstring) if "room" in switches: results = results.filter(db_obj__db_location__isnull=True) if "exit" in switches: diff --git a/src/commands/default/cmdset_player.py b/src/commands/default/cmdset_player.py index e448c2fca..2a63e3ee3 100644 --- a/src/commands/default/cmdset_player.py +++ b/src/commands/default/cmdset_player.py @@ -29,7 +29,7 @@ class PlayerCmdSet(CmdSet): self.add(player.CmdIC()) self.add(player.CmdOOC()) self.add(player.CmdCharCreate()) - self.add(player.CmdSessions()) + #self.add(player.CmdSessions()) self.add(player.CmdWho()) self.add(player.CmdEncoding()) self.add(player.CmdQuit()) @@ -60,7 +60,7 @@ class PlayerCmdSet(CmdSet): self.add(comms.CmdChannels()) self.add(comms.CmdCdestroy()) self.add(comms.CmdChannelCreate()) - self.add(comms.CmdCset()) + self.add(comms.CmdClock()) self.add(comms.CmdCBoot()) self.add(comms.CmdCemit()) self.add(comms.CmdCWho()) diff --git a/src/commands/default/cmdset_session.py b/src/commands/default/cmdset_session.py new file mode 100644 index 000000000..46c038da1 --- /dev/null +++ b/src/commands/default/cmdset_session.py @@ -0,0 +1,16 @@ +""" +This module stores session-level commands. +""" +from src.commands.cmdset import CmdSet +from src.commands.default import player + +class SessionCmdSet(CmdSet): + """ + Sets up the unlogged cmdset. + """ + key = "DefaultSession" + priority = 0 + + def at_cmdset_creation(self): + "Populate the cmdset" + self.add(player.CmdSessions()) diff --git a/src/commands/default/comms.py b/src/commands/default/comms.py index b886306c4..b36d89e75 100644 --- a/src/commands/default/comms.py +++ b/src/commands/default/comms.py @@ -8,10 +8,11 @@ for easy handling. """ from django.conf import settings -from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection +from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection from src.comms import irc, imc2, rss from src.comms.channelhandler import CHANNELHANDLER from src.utils import create, utils, prettytable +from src.utils.utils import make_iter from src.commands.default.muxcommand import MuxCommand, MuxPlayerCommand # limit symbol import for API @@ -26,10 +27,10 @@ def find_channel(caller, channelname, silent=False, noaliases=False): Helper function for searching for a single channel with some error handling. """ - channels = Channel.objects.channel_search(channelname) + channels = ChannelDB.objects.channel_search(channelname) if not channels: if not noaliases: - channels = [chan for chan in Channel.objects.all() if channelname in chan.aliases] + channels = [chan for chan in ChannelDB.objects.get_all_channels() if channelname in chan.aliases.all()] if channels: return channels[0] if not silent: @@ -103,7 +104,7 @@ class CmdAddCom(MuxPlayerCommand): if alias: # create a nick and add it to the caller. - caller.nicks.add(alias, channel.key, nick_type="channel") + caller.nicks.add(alias, channel.key, category="channel") string += " You can now refer to the channel %s with the alias '%s'." self.msg(string % (channel.key, alias)) else: @@ -147,21 +148,22 @@ class CmdDelCom(MuxPlayerCommand): return chkey = channel.key.lower() # find all nicks linked to this channel and delete them - for nick in [nick for nick in caller.nicks.get(nick_type="channel") - if nick.db_real.lower() == chkey]: + for nick in [nick for nick in caller.nicks.get(category="channel") + if nick.db_data.lower() == chkey]: nick.delete() - channel.disconnect_from(player) - self.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key) + disconnect = channel.disconnect_from(player) + if disconnect: + self.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key) return else: # we are removing a channel nick - channame = caller.nicks.get(ostring, nick_type="channel") + channame = caller.nicks.get(key=ostring, category="channel") channel = find_channel(caller, channame, silent=True) if not channel: self.msg("No channel with alias '%s' was found." % ostring) else: - if caller.nicks.has(ostring, nick_type="channel"): - caller.nicks.delete(ostring, nick_type="channel") + if caller.nicks.get(ostring, category="channel"): + caller.nicks.remove(ostring, category="channel") self.msg("Your alias '%s' for channel %s was cleared." % (ostring, channel.key)) else: self.msg("You had no such alias defined for this channel.") @@ -196,7 +198,7 @@ class CmdAllCom(MuxPlayerCommand): if args == "on": # get names of all channels available to listen to and activate them all - channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')] + channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')] for channel in channels: caller.execute_cmd("addcom %s" % channel.key) elif args == "off": @@ -206,13 +208,13 @@ class CmdAllCom(MuxPlayerCommand): caller.execute_cmd("delcom %s" % channel.key) elif args == "destroy": # destroy all channels you control - channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'control')] + channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'control')] for channel in channels: caller.execute_cmd("@cdestroy %s" % channel.key) elif args == "who": # run a who, listing the subscribers on visible channels. string = "\n{CChannel subscriptions{n" - channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')] + channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')] if not channels: string += "No channels." for channel in channels: @@ -236,8 +238,8 @@ class CmdChannels(MuxPlayerCommand): @clist comlist - Lists all channels available to you, wether you listen to them or not. - Use 'comlist" to only view your current channel subscriptions. + Lists all channels available to you, whether you listen to them or not. + Use 'comlist' to only view your current channel subscriptions. Use addcom/delcom to join and leave channels """ key = "@channels" @@ -251,33 +253,37 @@ class CmdChannels(MuxPlayerCommand): caller = self.caller # all channels we have available to listen to - channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')] + channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')] + print channels if not channels: self.msg("No channels available.") return # all channel we are already subscribed to subs = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller)] + print subs if self.cmdstring == "comlist": # just display the subscribed channels with no extra info comtable = prettytable.PrettyTable(["{wchannel","{wmy aliases", "{wdescription"]) for chan in subs: clower = chan.key.lower() - nicks = [nick for nick in caller.nicks.get(nick_type="channel")] - comtable.add_row(["%s%s" % (chan.key, chan.aliases and "(%s)" % ",".join(chan.aliases) or ""), - "%s".join(nick.db_nick for nick in nicks if nick.db_real.lower()==clower()), - chan.desc]) + nicks = caller.nicks.get(category="channel") + comtable.add_row(["%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""), + "%s".join(nick for nick in make_iter(nicks) if nick and nick.lower()==clower), + chan.db.desc]) caller.msg("\n{wChannel subscriptions{n (use {w@channels{n to list all, {waddcom{n/{wdelcom{n to sub/unsub):{n\n%s" % comtable) else: # full listing (of channels caller is able to listen to) comtable = prettytable.PrettyTable(["{wsub","{wchannel","{wmy aliases","{wlocks","{wdescription"]) for chan in channels: - nicks = [nick for nick in caller.nicks.get(nick_type="channel")] + clower = chan.key.lower() + nicks = caller.nicks.get(category="channel") + nicks = nicks or [] comtable.add_row([chan in subs and "{gYes{n" or "{rNo{n", - "%s%s" % (chan.key, chan.aliases and "(%s)" % ",".join(chan.aliases) or ""), - "%s".join(nick.db_nick for nick in nicks if nick.db_real.lower()==clower()), - chan.locks, - chan.desc]) + "%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""), + "%s".join(nick for nick in make_iter(nicks) if nick.lower()==clower), + str(chan.locks), + chan.db.desc]) caller.msg("\n{wAvailable channels{n (use {wcomlist{n,{waddcom{n and {wdelcom{n to manage subscriptions):\n%s" % comtable) class CmdCdestroy(MuxPlayerCommand): @@ -351,7 +357,7 @@ class CmdCBoot(MuxPlayerCommand): searchstring = playername.lstrip('*') else: searchstring = self.rhs.lstrip('*') - player = self.search(searchstring, player=True) + player = self.caller.search(searchstring, player=True) if not player: return if reason: @@ -360,7 +366,7 @@ class CmdCBoot(MuxPlayerCommand): string = "You don't control this channel." self.msg(string) return - if not PlayerChannelConnection.objects.has_connection(player, channel): + if not PlayerChannelConnection.objects.has_player_connection(player, channel): string = "Player %s is not connected to channel %s." % (player.key, channel.key) self.msg(string) return @@ -368,7 +374,8 @@ class CmdCBoot(MuxPlayerCommand): string = "%s boots %s from channel.%s" % (self.caller, player.key, reason) channel.msg(string) # find all player's nicks linked to this channel and delete them - for nick in [nick for nick in player.character.nicks.get(nick_type="channel") + for nick in [nick for nick in + player.character.nicks.get(category="channel") or [] if nick.db_real.lower() == channel.key]: nick.delete() # disconnect player @@ -495,7 +502,7 @@ class CmdChannelCreate(MuxPlayerCommand): for part in lhs.split(';', 1) if part.strip()] aliases = [alias.strip().lower() for alias in aliases.split(';') if alias.strip()] - channel = Channel.objects.channel_search(channame) + channel = ChannelDB.objects.channel_search(channame) if channel: self.msg("A channel with that name already exists.") return @@ -506,27 +513,27 @@ class CmdChannelCreate(MuxPlayerCommand): self.msg("Created channel %s and connected to it." % new_chan.key) -class CmdCset(MuxPlayerCommand): +class CmdClock(MuxPlayerCommand): """ - @cset - changes channel access restrictions + @clock - changes channel access restrictions Usage: - @cset [= ] + @clock [= ] Changes the lock access restrictions of a channel. If no lockstring was given, view the current lock definitions. """ - key = "@cset" + key = "@clock" locks = "cmd:not pperm(channel_banned)" - aliases = ["@cclock"] + aliases = ["@clock"] help_category = "Comms" def func(self): "run the function" if not self.args: - string = "Usage: @cset channel [= lockstring]" + string = "Usage: @clock channel [= lockstring]" self.msg(string) return @@ -584,7 +591,7 @@ class CmdCdesc(MuxPlayerCommand): self.msg("You cannot admin this channel.") return # set the description - channel.desc = self.rhs + channel.db.desc = self.rhs channel.save() self.msg("Description of channel '%s' set to '%s'." % (channel.key, self.rhs)) @@ -949,7 +956,7 @@ class CmdIMCInfo(MuxCommand): return from src.comms.imc2 import IMC2_CLIENT self.msg("Sending IMC whois request. If you receive no response, no matches were found.") - IMC2_CLIENT.msg_imc2(None, from_obj=self.caller, packet_type="imcwhois", data={"target":self.args}) + IMC2_CLIENT.msg_imc2(None, from_obj=self.caller, packet_type="imcwhois", target=self.args) elif not self.switches or "channels" in self.switches or self.cmdstring == "@imcchanlist": # show channels @@ -1007,7 +1014,7 @@ class CmdIMCTell(MuxCommand): data = {"target":target, "destination":destination} # send to imc2 - IMC2_CLIENT.msg_imc2(message, from_obj=self.caller, packet_type="imctell", data=data) + IMC2_CLIENT.msg_imc2(message, from_obj=self.caller, packet_type="imctell", **data) self.msg("You paged {c%s@%s{n (over IMC): '%s'." % (target, destination, message)) diff --git a/src/commands/default/general.py b/src/commands/default/general.py index 1c8c313d1..c6d410630 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -3,7 +3,6 @@ General Character commands usually availabe to all characters """ from django.conf import settings from src.utils import utils, prettytable -from src.objects.models import ObjectNick as Nick from src.commands.default.muxcommand import MuxCommand @@ -124,17 +123,17 @@ class CmdNick(MuxCommand): caller = self.caller switches = self.switches - nicks = Nick.objects.filter(db_obj=caller.dbobj).exclude(db_type="channel") + nicks = caller.nicks.get(category="channel") if 'list' in switches: table = prettytable.PrettyTable(["{wNickType", "{wNickname", "{wTranslates-to"]) for nick in nicks: - table.add_row([nick.db_type, nick.db_nick, nick.db_real]) + table.add_row([nick.db_category, nick.db_key, nick.db_data]) string = "{wDefined Nicks:{n\n%s" % table caller.msg(string) return if 'clearall' in switches: - nicks.delete() + caller.nicks.clear() caller.msg("Cleared all aliases.") return if not self.args or not self.lhs: @@ -152,13 +151,14 @@ class CmdNick(MuxCommand): switches = ["inputline"] string = "" for switch in switches: - oldnick = Nick.objects.filter(db_obj=caller.dbobj, db_nick__iexact=nick, db_type__iexact=switch) + oldnick = caller.nicks.get(key=nick, category=switch) + #oldnick = Nick.objects.filter(db_obj=caller.dbobj, db_nick__iexact=nick, db_type__iexact=switch) if not real: # removal of nick if oldnick: # clear the alias string += "\nNick '%s' (= '%s') was cleared." % (nick, oldnick[0].db_real) - caller.nicks.delete(nick, nick_type=switch) + caller.nicks.delete(nick, category=switch) else: string += "\nNo nick '%s' found, so it could not be removed." % nick else: @@ -167,7 +167,7 @@ class CmdNick(MuxCommand): string += "\nNick %s changed from '%s' to '%s'." % (nick, oldnick[0].db_real, real) else: string += "\nNick set: '%s' = '%s'." % (nick, real) - caller.nicks.add(nick, real, nick_type=switch) + caller.nicks.add(nick, real, category=switch) caller.msg(string) class CmdInventory(MuxCommand): @@ -220,6 +220,7 @@ class CmdGet(MuxCommand): if not self.args: caller.msg("Get what?") return + #print "general/get:", caller, caller.location, self.args, caller.location.contents obj = caller.search(self.args, location=caller.location) if not obj: return @@ -432,8 +433,8 @@ class CmdAccess(MuxCommand): cperms = "" pperms = "" else: - cperms = ", ".join(caller.permissions) - pperms = ", ".join(caller.player.permissions) + cperms = ", ".join(caller.permissions.all()) + pperms = ", ".join(caller.player.permissions.all()) string += "\n{wYour access{n:" string += "\nCharacter {c%s{n: %s" % (caller.key, cperms) diff --git a/src/commands/default/player.py b/src/commands/default/player.py index cec03d2cb..c0889b731 100644 --- a/src/commands/default/player.py +++ b/src/commands/default/player.py @@ -110,12 +110,12 @@ class CmdOOCLook(MuxPlayerCommand): sess = player.get_session(csessid) sid = sess in sessions and sessions.index(sess) + 1 if sess and sid: - string += "\n - {G%s{n [%s] (played by you in session %i)" % (char.key, ", ".join(char.permissions), sid) + string += "\n - {G%s{n [%s] (played by you in session %i)" % (char.key, ", ".join(char.permissions.all()), sid) else: - string += "\n - {R%s{n [%s] (played by someone else)" % (char.key, ", ".join(char.permissions)) + string += "\n - {R%s{n [%s] (played by someone else)" % (char.key, ", ".join(char.permissions.all())) else: # character is "free to puppet" - string += "\n - %s [%s]" % (char.key, ", ".join(char.permissions)) + string += "\n - %s [%s]" % (char.key, ", ".join(char.permissions.all())) string = ("-" * 68) + "\n" + string + "\n" + ("-" * 68) self.msg(string) @@ -370,7 +370,7 @@ class CmdWho(MuxPlayerCommand): session.protocol_key, isinstance(session.address, tuple) and session.address[0] or session.address]) else: - table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{Idle"]) + table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible @@ -465,18 +465,13 @@ class CmdPassword(MuxPlayerCommand): return oldpass = self.lhslist[0] # this is already stripped by parse() newpass = self.rhslist[0] # '' - try: - uaccount = player.user - except AttributeError: - self.msg("This is only applicable for players.") - return - if not uaccount.check_password(oldpass): + if not player.check_password(oldpass): self.msg("The specified old password isn't correct.") elif len(newpass) < 3: self.msg("Passwords must be at least three characters long.") else: - uaccount.set_password(newpass) - uaccount.save() + player.set_password(newpass) + player.save() self.msg("Password changed.") class CmdQuit(MuxPlayerCommand): @@ -625,18 +620,18 @@ class CmdQuell(MuxPlayerCommand): def func(self): "Perform the command" player = self.caller - permstr = player.is_superuser and " (superuser)" or " (%s)" % (", ".join(player.permissions)) + permstr = player.is_superuser and " (superuser)" or " (%s)" % (", ".join(player.permissions.all())) if self.cmdstring == '@unquell': - if not player.get_attribute('_quell'): + if not player.attributes.get('_quell'): self.msg("Already using normal Player permissions%s." % permstr) else: - player.del_attribute('_quell') + player.attributes.remove('_quell') self.msg("Player permissions%s restored." % permstr) else: - if player.get_attribute('_quell'): + if player.attributes.get('_quell'): self.msg("Already quelling Player%s permissions." % permstr) return - player.set_attribute('_quell', True) + player.attributes.add('_quell', True) self.msg("Quelling Player permissions%s. Use @unquell to get them back." % permstr) self._recache_locks(player) diff --git a/src/commands/default/syscommands.py b/src/commands/default/syscommands.py index ccd46f9e9..bc264d98d 100644 --- a/src/commands/default/syscommands.py +++ b/src/commands/default/syscommands.py @@ -18,7 +18,7 @@ line with a command (if there is no match to a known command, the line is just added to the editor buffer). """ -from src.comms.models import Channel +from src.comms.models import ChannelDB from src.utils import create # The command keys the engine is calling @@ -88,7 +88,7 @@ class SystemMultimatch(MuxCommand): src.commands.cmdhandler. """ - string = "There where multiple matches:" + string = "There were multiple matches:" for num, match in enumerate(matches): # each match is a tuple (candidate, cmd) candidate, cmd = match @@ -153,7 +153,7 @@ class SystemSendToChannel(MuxCommand): if not msg: caller.msg("Say what?") return - channel = Channel.objects.get_channel(channelkey) + channel = ChannelDB.objects.get_channel(channelkey) if not channel: caller.msg("Channel '%s' not found." % channelkey) return diff --git a/src/commands/default/system.py b/src/commands/default/system.py index 6288b8b09..b563d03dd 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -17,6 +17,7 @@ from src.scripts.models import ScriptDB from src.objects.models import ObjectDB from src.players.models import PlayerDB from src.utils import logger, utils, gametime, create, is_pypy, prettytable +from src.utils.utils import crop from src.commands.default.muxcommand import MuxCommand # delayed imports @@ -163,9 +164,9 @@ class CmdPy(MuxCommand): 'inherits_from':utils.inherits_from} try: - self.msg(">>> %s" % pycode, data={"raw":True}, sessid=self.sessid) + self.msg(">>> %s" % pycode, raw=True, sessid=self.sessid) except TypeError: - self.msg(">>> %s" % pycode, data={"raw":True}) + self.msg(">>> %s" % pycode, raw=True) mode = "eval" @@ -214,14 +215,14 @@ def format_script_list(scripts): for script in scripts: nextrep = script.time_until_next_repeat() table.add_row([script.id, - (not hasattr(script, 'obj') or not script.obj) and "" or script.obj.key, + script.obj.key if (hasattr(script, 'obj') and script.obj) else "", script.key, - (not hasattr(script, 'interval') or script.interval < 0) and "--" or "%ss" % script.interval, - not nextrep and "--" or "%ss" % nextrep, - (not hasattr(script, 'repeats') or not script.repeats) and "--" or "%i" % script.repeats, - script.persistent and "*" or "-", + script.interval if script.interval > 0 else "--", + "%ss" % nextrep if nextrep else "--", + "%i" % script.repeats if script.repeats else "--", + "*" if script.persistent else "-", script.typeclass_path.rsplit('.', 1)[-1], - script.desc]) + crop(script.desc, width=20)]) return "%s" % table @@ -395,7 +396,7 @@ class CmdPlayers(MuxCommand): """ key = "@players" aliases = ["@listplayers"] - locks = "cmd:perm(listplayers) or perm(Admins)" + locks = "cmd:perm(listplayers) or perm(Wizards)" def func(self): "List the players" diff --git a/src/commands/default/tests.py b/src/commands/default/tests.py index ccad26bf5..22d4fa7e5 100644 --- a/src/commands/default/tests.py +++ b/src/commands/default/tests.py @@ -15,8 +15,17 @@ main test suite started with import re from django.conf import settings from django.utils.unittest import TestCase +from src.server.serversession import ServerSession +from src.objects.objects import Object, Character from src.players.player import Player from src.utils import create, utils, ansi +from src.server.sessionhandler import SESSIONS + +from django.db.models.signals import pre_save +from src.server.caches import field_pre_save +pre_save.connect(field_pre_save, dispatch_uid="fieldcache") + +# set up signal here since we are not starting the server _RE = re.compile(r"^\+|-+\+|\+-+|--*|\|", re.MULTILINE) @@ -24,12 +33,31 @@ _RE = re.compile(r"^\+|-+\+|\+-+|--*|\|", re.MULTILINE) # Command testing # ------------------------------------------------------------ +def dummy(self, *args, **kwargs): + pass + +SESSIONS.data_out = dummy +SESSIONS.disconnect = dummy + +class TestObjectClass(Object): + def msg(self, text="", **kwargs): + "test message" + pass +class TestCharacterClass(Character): + def msg(self, text="", **kwargs): + "test message" + if self.player: + self.player.msg(text=text, **kwargs) + else: + if not self.ndb.stored_msg: + self.ndb.stored_msg = [] + self.ndb.stored_msg.append(text) class TestPlayerClass(Player): - def msg(self, message, **kwargs): + def msg(self, text="", **kwargs): "test message" if not self.ndb.stored_msg: self.ndb.stored_msg = [] - self.ndb.stored_msg.append(message) + self.ndb.stored_msg.append(text) def _get_superuser(self): "test with superuser flag" return self.ndb.is_superuser @@ -42,22 +70,30 @@ class CommandTest(TestCase): CID = 0 # we must set a different CID in every test to avoid unique-name collisions creating the objects def setUp(self): "sets up testing environment" - self.room1 = create.create_object("src.objects.objects.Room", key="Room%i"%self.CID) - self.room1.db.desc = "room_desc" - self.room2 = create.create_object("src.objects.objects.Room", key="Room%ib" % self.CID) - self.obj1 = create.create_object("src.objects.objects.Object", key="Obj%i" % self.CID, location=self.room1, home=self.room1) - self.obj2 = create.create_object("src.objects.objects.Object", key="Obj%ib" % self.CID, location=self.room1, home=self.room1) - self.char1 = create.create_object("src.objects.objects.Character", key="Char%i" % self.CID, location=self.room1, home=self.room1) - self.char2 = create.create_object("src.objects.objects.Character", key="Char%ib" % self.CID, location=self.room1, home=self.room1) - self.script = create.create_script("src.scripts.scripts.Script", key="Script%i" % self.CID) self.player = create.create_player("TestPlayer%i" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass) self.player2 = create.create_player("TestPlayer%ib" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass) - - self.player.permissions = "Immortals" + self.room1 = create.create_object("src.objects.objects.Room", key="Room%i"%self.CID, nohome=True) + self.room1.db.desc = "room_desc" + self.room2 = create.create_object("src.objects.objects.Room", key="Room%ib" % self.CID) + self.obj1 = create.create_object(TestObjectClass, key="Obj%i" % self.CID, location=self.room1, home=self.room1) + self.obj2 = create.create_object(TestObjectClass, key="Obj%ib" % self.CID, location=self.room1, home=self.room1) + self.char1 = create.create_object(TestCharacterClass, key="Char%i" % self.CID, location=self.room1, home=self.room1) + self.char2 = create.create_object(TestCharacterClass, key="Char%ib" % self.CID, location=self.room1, home=self.room1) self.char1.player = self.player - self.char1.sessid = 1 + self.char2.player = self.player2 + self.script = create.create_script("src.scripts.scripts.Script", key="Script%i" % self.CID) + self.player.permissions.add("Immortals") - def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True): + # set up a fake session + + global SESSIONS + session = ServerSession() + session.init_session("telnet", ("localhost", "testmode"), SESSIONS) + session.sessid = self.CID + SESSIONS.portal_connect(session.get_sync_data()) + SESSIONS.login(SESSIONS.session_from_sessid(self.CID), self.player, testmode=True) + + def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None): """ Test a command by assigning all the needed properties to cmdobj and running @@ -68,13 +104,17 @@ class CommandTest(TestCase): The msgreturn value is compared to eventual output sent to caller.msg in the game """ - cmdobj.caller = self.char1 + cmdobj.caller = caller if caller else self.char1 + #print "call:", cmdobj.key, cmdobj.caller, caller if caller else cmdobj.caller.player + #print "perms:", cmdobj.caller.permissions.all() cmdobj.cmdstring = cmdobj.key cmdobj.args = args cmdobj.cmdset = cmdset + cmdobj.sessid = self.CID + cmdobj.session = SESSIONS.session_from_sessid(self.CID) + cmdobj.player = self.player cmdobj.raw_string = cmdobj.key + " " + args - cmdobj.obj = self.char1 - cmdobj.sessid = 1 + cmdobj.obj = caller if caller else self.char1 # test self.char1.player.ndb.stored_msg = [] cmdobj.at_pre_cmd() @@ -82,7 +122,8 @@ class CommandTest(TestCase): cmdobj.func() cmdobj.at_post_cmd() # clean out prettytable sugar - returned_msg = "|".join(_RE.sub("", mess) for mess in self.char1.player.ndb.stored_msg) + stored_msg = self.char1.player.ndb.stored_msg if self.char1.player else self.char1.ndb.stored_msg + returned_msg = "|".join(_RE.sub("", mess) for mess in stored_msg) #returned_msg = "|".join(self.char1.player.ndb.stored_msg) returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip() if msg != None: @@ -111,8 +152,8 @@ class TestGeneral(CommandTest): self.call(general.CmdNick(), "/player testalias = testaliasedstring2", "Nick set:") self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Nick set:") self.assertEqual(u"testaliasedstring1", self.char1.nicks.get("testalias")) - self.assertEqual(u"testaliasedstring2", self.char1.nicks.get("testalias", nick_type="player")) - self.assertEqual(u"testaliasedstring3", self.char1.nicks.get("testalias", nick_type="object")) + self.assertEqual(u"testaliasedstring2", self.char1.nicks.get("testalias", category="player")) + self.assertEqual(u"testaliasedstring3", self.char1.nicks.get("testalias", category="object")) self.call(general.CmdGet(), "Obj1", "You pick up Obj1.") self.call(general.CmdDrop(), "Obj1", "You drop Obj1.") self.call(general.CmdSay(), "Testing", "You say, \"Testing\"") @@ -152,26 +193,26 @@ class TestAdmin(CommandTest): from src.commands.default import player class TestPlayer(CommandTest): - CID = 5 - def test_cmds(self): - self.call(player.CmdOOCLook(), "", "Account TestPlayer5 (you are OutofCharacter)") - self.call(player.CmdIC(), "Char5","Char5 is now acted from another") - self.call(player.CmdOOC(), "", "You are already") - self.call(player.CmdPassword(), "testpassword = testpassword", "Password changed.") - self.call(player.CmdEncoding(), "", "Default encoding:") - self.call(player.CmdWho(), "", "Players:") - self.call(player.CmdQuit(), "", "Quitting. Hope to see you soon again.") - self.call(player.CmdSessions(), "", "Your current session(s):") - self.call(player.CmdColorTest(), "ansi", "ANSI colors:") - self.call(player.CmdCharCreate(), "Test1=Test char","Created new character Test1. Use @ic Test1 to enter the game") - self.call(player.CmdQuell(), "", "Quelling Player permissions (Immortals). Use @unquell to get them back.") + CID = 5 + def test_cmds(self): + self.call(player.CmdOOCLook(), "", "Account TestPlayer5 (you are OutofCharacter)", caller=self.player) + self.call(player.CmdOOC(), "", "You are already", caller=self.player) + self.call(player.CmdIC(), "Char5","You become Char5.", caller=self.player) + self.call(player.CmdPassword(), "testpassword = testpassword", "Password changed.", caller=self.player) + self.call(player.CmdEncoding(), "", "Default encoding:", caller=self.player) + self.call(player.CmdWho(), "", "Players:", caller=self.player) + self.call(player.CmdQuit(), "", "Quitting. Hope to see you soon again.", caller=self.player) + self.call(player.CmdSessions(), "", "Your current session(s):", caller=self.player) + self.call(player.CmdColorTest(), "ansi", "ANSI colors:", caller=self.player) + self.call(player.CmdCharCreate(), "Test1=Test char","Created new character Test1. Use @ic Test1 to enter the game", caller=self.player) + self.call(player.CmdQuell(), "", "Quelling Player permissions (immortals). Use @unquell to get them back.", caller=self.player) from src.commands.default import building class TestBuilding(CommandTest): CID = 6 def test_cmds(self): self.call(building.CmdCreate(), "/drop TestObj1", "You create a new Object: TestObj1.") - self.call(building.CmdSetObjAlias(), "TestObj1 = TestObj1b","Aliases for 'TestObj1' are now set to testobj1b.") + self.call(building.CmdSetObjAlias(), "TestObj1 = TestObj1b","Alias(es) for 'TestObj1' set to testobj1b.") self.call(building.CmdCopy(), "TestObj1 = TestObj2;TestObj2b, TestObj3;TestObj3b", "Copied TestObj1 to 'TestObj3' (aliases: ['TestObj3b']") self.call(building.CmdSetAttribute(), "Obj6/test1=\"value1\"", "Created attribute Obj6/test1 = \"value1\"") self.call(building.CmdSetAttribute(), "Obj6b/test2=\"value2\"", "Created attribute Obj6b/test2 = \"value2\"") @@ -188,7 +229,8 @@ class TestBuilding(CommandTest): self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.") self.call(building.CmdSetHome(), "Obj6 = Room6b", "Obj6's home location was changed from Room6") self.call(building.CmdListCmdSets(), "", ":") - self.call(building.CmdTypeclass(), "Obj6 = src.objects.objects.Character", "Obj6's changed typeclass from src.objects.objects.Object to") + self.call(building.CmdTypeclass(), "Obj6 = src.objects.objects.Exit", + "Obj6 changed typeclass from src.commands.default.tests.TestObjectClass to src.objects.objects.Exit") self.call(building.CmdLock(), "Obj6 = test:perm(Immortals)", "Added lock 'test:perm(Immortals)' to Obj6.") self.call(building.CmdExamine(), "Obj6", "Name/key: Obj6") self.call(building.CmdFind(), "TestRoom1", "One Match") @@ -205,7 +247,7 @@ class TestComms(CommandTest): self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.") self.call(comms.CmdChannels(), "" ,"Available channels (use comlist,addcom and delcom to manage") self.call(comms.CmdAllCom(), "", "Available channels (use comlist,addcom and delcom to manage") - self.call(comms.CmdCset(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:") + self.call(comms.CmdClock(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:") self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.") self.call(comms.CmdCemit(), "testchan = Test Message", "Sent to channel testchan: [testchan] Test Message") self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestPlayer7") diff --git a/src/commands/default/unloggedin.py b/src/commands/default/unloggedin.py index d9c5145f7..1c04aef84 100644 --- a/src/commands/default/unloggedin.py +++ b/src/commands/default/unloggedin.py @@ -4,11 +4,10 @@ Commands that are available from the connect screen. import re import traceback from django.conf import settings -from django.contrib.auth.models import User from src.players.models import PlayerDB from src.objects.models import ObjectDB from src.server.models import ServerConfig -from src.comms.models import Channel +from src.comms.models import ChannelDB from src.utils import create, logger, utils, ansi from src.commands.default.muxcommand import MuxCommand @@ -68,7 +67,7 @@ class CmdUnconnectedConnect(MuxCommand): player = PlayerDB.objects.get_player_from_name(playername) pswd = None if player: - pswd = player.user.check_password(password) + pswd = player.check_password(password) if not (player and pswd): # No playername or password match @@ -142,7 +141,7 @@ class CmdUnconnectedCreate(MuxCommand): return # strip excessive spaces in playername playername = re.sub(r"\s+", " ", playername).strip() - if PlayerDB.objects.filter(user__username__iexact=playername) or User.objects.filter(username__iexact=playername): + if PlayerDB.objects.filter(username__iexact=playername): # player already exists (we also ignore capitalization here) session.msg("Sorry, there is already a player with the name '%s'." % playername) return @@ -167,6 +166,7 @@ class CmdUnconnectedCreate(MuxCommand): except Exception, e: session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e) + logger.log_trace() return # This needs to be called so the engine knows this player is logging in for the first time. @@ -176,7 +176,7 @@ class CmdUnconnectedCreate(MuxCommand): # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC if pchanneldef: - pchannel = Channel.objects.get_channel(pchanneldef[0]) + pchannel = ChannelDB.objects.get_channel(pchanneldef[0]) if not pchannel.connect_to(new_player): string = "New player '%s' could not connect to public channel!" % new_player.key logger.log_errmsg(string) diff --git a/src/comms/__init__.py b/src/comms/__init__.py index 6338f63c9..0fd02fb3a 100644 --- a/src/comms/__init__.py +++ b/src/comms/__init__.py @@ -9,4 +9,4 @@ Also, the initiated object manager is available as src.comms.msgmanager and src. from src.comms.models import * msgmanager = Msg.objects -channelmanager = Channel.objects +channelmanager = ChannelDB.objects diff --git a/src/comms/admin.py b/src/comms/admin.py index e207fb266..6de3c1471 100644 --- a/src/comms/admin.py +++ b/src/comms/admin.py @@ -4,7 +4,7 @@ # from django.contrib import admin -from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection +from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection class MsgAdmin(admin.ModelAdmin): list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage') @@ -37,7 +37,7 @@ class ExternalChannelConnectionInline(admin.StackedInline): class ChannelAdmin(admin.ModelAdmin): inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline) - list_display = ('id', 'db_key', 'db_desc', 'db_aliases', 'db_keep_log', 'db_lock_storage') + list_display = ('id', 'db_key', 'db_lock_storage') list_display_links = ("id", 'db_key') ordering = ["db_key"] search_fields = ['id', 'db_key', 'db_aliases'] @@ -45,28 +45,7 @@ class ChannelAdmin(admin.ModelAdmin): save_on_top = True list_select_related = True fieldsets = ( - (None, {'fields':(('db_key', 'db_aliases', 'db_desc'),'db_lock_storage', 'db_keep_log')}), + (None, {'fields':(('db_key',),'db_lock_storage',)}), ) -admin.site.register(Channel, ChannelAdmin) - -# class PlayerChannelConnectionAdmin(admin.ModelAdmin): -# list_display = ('db_channel', 'db_player') -# list_display_links = ("db_player", 'db_channel') -# ordering = ["db_channel"] -# search_fields = ['db_channel', 'db_player'] -# save_as = True -# save_on_top = True -# list_select_related = True -# admin.site.register(PlayerChannelConnection, PlayerChannelConnectionAdmin) - -# class ExternalChannelConnectionAdmin(admin.ModelAdmin): -# list_display = ('db_channel', 'db_external_key', 'db_external_config') -# list_display_links = ("db_channel", 'db_external_key', 'db_external_config') -# ordering = ["db_channel"] -# search_fields = ['db_channel', 'db_external_key'] -# save_as = True -# save_on_top = True -# list_select_related = True -# admin.site.register(ExternalChannelConnection, ExternalChannelConnectionAdmin) - +admin.site.register(ChannelDB, ChannelAdmin) \ No newline at end of file diff --git a/src/comms/channelhandler.py b/src/comms/channelhandler.py index e020e88e9..48b4cc884 100644 --- a/src/comms/channelhandler.py +++ b/src/comms/channelhandler.py @@ -23,7 +23,7 @@ update() on the channelhandler. Or use Channel.objects.delete() which does this for you. """ -from src.comms.models import Channel, Msg +from src.comms.models import ChannelDB, Msg from src.commands import cmdset, command from src.utils import utils @@ -64,7 +64,7 @@ class ChannelCommand(command.Command): if not msg: self.msg("Say what?") return - channel = Channel.objects.get_channel(channelkey) + channel = ChannelDB.objects.get_channel(channelkey) if not channel: self.msg("Channel '%s' not found." % channelkey) @@ -77,20 +77,8 @@ class ChannelCommand(command.Command): string = "You are not permitted to send to channel '%s'." self.msg(string % channelkey) return - msg = "[%s] %s: %s" % (channel.key, caller.name, msg) - # we can't use the utils.create function to make the Msg, - # since that creates an import recursive loop. - try: - sender = caller.player - except AttributeError: - # this could happen if a player is calling directly. - sender = caller.dbobj - msgobj = Msg(db_message=msg) - msgobj.save() - msgobj.senders = sender - msgobj.channels = channel - # send new message object to channel - channel.msg(msgobj, senders=sender, online=True) + channel.msg(msg, senders=self.caller, online=True) + class ChannelHandler(object): """ @@ -112,11 +100,9 @@ class ChannelHandler(object): def _format_help(self, channel): "builds a doc string" key = channel.key - aliases = channel.aliases - if not utils.is_iter(aliases): - aliases = [aliases] + aliases = channel.aliases.all() ustring = "%s " % key.lower() + "".join(["\n %s " % alias.lower() for alias in aliases]) - desc = channel.desc + desc = channel.db.desc string = \ """ Channel '%s' @@ -137,7 +123,7 @@ class ChannelHandler(object): """ # map the channel to a searchable command cmd = ChannelCommand(key=channel.key.strip().lower(), - aliases=channel.aliases if channel.aliases else [], + aliases=channel.aliases.all(), locks="cmd:all();%s" % channel.locks, obj=channel) cmd.__doc__= self._format_help(channel) @@ -148,7 +134,7 @@ class ChannelHandler(object): "Updates the handler completely." self.cached_channel_cmds = [] self.cached_cmdsets = {} - for channel in Channel.objects.all(): + for channel in ChannelDB.objects.get_all_channels(): self.add_channel(channel) def get_cmdset(self, source_object): diff --git a/src/comms/comms.py b/src/comms/comms.py new file mode 100644 index 000000000..179e08d1f --- /dev/null +++ b/src/comms/comms.py @@ -0,0 +1,230 @@ +""" +Default Typeclass for Comms. + +See objects.objects for more information on Typeclassing. +""" +from src.comms import Msg, TempMsg, ChannelDB +from src.typeclasses.typeclass import TypeClass +from src.utils import logger +from src.utils.utils import make_iter + + +class Comm(TypeClass): + """ + This is the base class for all Comms. Inherit from this to create different + types of communication channels. + """ + def __init__(self, dbobj): + super(Comm, self).__init__(dbobj) + + def channel_prefix(self, msg=None, emit=False): + """ + How the channel should prefix itself for users. Return a string. + """ + return '[%s] ' % self.key + + def format_senders(self, senders=None): + """ + Function used to format a list of sender names. + + This function exists separately so that external sources can use + it to format source names in the same manner as normal object/player + names. + """ + if not senders: + return '' + return ', '.join(senders) + + def pose_transform(self, msg, sender_string): + """ + Detects if the sender is posing, and modifies the message accordingly. + """ + pose = False + message = msg.message + message_start = message.lstrip() + if message_start.startswith((':', ';')): + pose = True + message = message[1:] + if not message.startswith((':', "'", ',')): + if not message.startswith(' '): + message = ' ' + message + if pose: + return '%s%s' % (sender_string, message) + else: + return '%s: %s' % (sender_string, message) + + + def format_external(self, msg, senders, emit=False): + """ + Used for formatting external messages. This is needed as a separate + operation because the senders of external messages may not be in-game + objects/players, and so cannot have things like custom user + preferences. + + senders should be a list of strings, each containing a sender. + msg should contain the body of the message to be sent. + """ + if not senders: + emit = True + if emit: + return msg.message + senders = ', '.join(senders) + return self.pose_transform(msg, senders) + + + def format_message(self, msg, emit=False): + """ + Formats a message body for display. + + If emit is True, it means the message is intended to be posted detached + from an identity. + """ + # We don't want to count things like external sources as senders for + # the purpose of constructing the message string. + senders = [sender for sender in msg.senders if hasattr(sender, 'key')] + if not senders: + emit = True + if emit: + return msg.message + else: + senders = [sender.key for sender in msg.senders] + senders = ', '.join(senders) + return self.pose_transform(msg, senders) + + def message_transform(self, msg, emit=False, prefix=True, + sender_strings=None, external=False): + """ + Generates the formatted string sent to listeners on a channel. + """ + if sender_strings or external: + body = self.format_external(msg, sender_strings, emit=emit) + else: + body = self.format_message(msg, emit=emit) + if prefix: + body = "%s%s" % (self.channel_prefix(msg, emit=emit), body) + msg.message = body + return msg + + def at_channel_create(self): + """ + Run at channel creation. + """ + + def pre_join_channel(self, joiner): + """ + Run right before a channel is joined. If this returns a false value, + channel joining is aborted. + """ + + def post_join_channel(self, joiner): + """ + Run right after an object or player joins a channel. + """ + return True + + def pre_leave_channel(self, leaver): + """ + Run right before a user leaves a channel. If this returns a false + value, leaving the channel will be aborted. + """ + return True + + def post_leave_channel(self, leaver): + """ + Run right after an object or player leaves a channel. + """ + + def pre_send_message(self, msg): + """ + Run before a message is sent to the channel. + + This should return the message object, after any transformations. + If the message is to be discarded, return a false value. + """ + return msg + + def post_send_message(self, msg): + """ + Run after a message is sent to the channel. + """ + + def at_init(self): + """ + This is always called whenever this channel is initiated -- + that is, whenever it its typeclass is cached from memory. This + happens on-demand first time the channel is used or activated + in some way after being created but also after each server + restart or reload. + """ + + def distribute_message(self, msg, online=False): + """ + Method for grabbing all listeners that a message should be sent to on + this channel, and sending them a message. + """ + # get all players connected to this channel and send to them + for conn in ChannelDB.objects.get_all_connections(self, online=online): + try: + conn.player.msg(msg.message, from_obj=msg.senders) + except AttributeError: + try: + conn.to_external(msg.message, senders=msg.senders, from_channel=self) + except Exception: + logger.log_trace("Cannot send msg to connection '%s'" % conn) + + + def msg(self, msgobj, header=None, senders=None, sender_strings=None, + persistent=True, online=False, emit=False, external=False): + """ + Send the given message to all players connected to channel. Note that + no permission-checking is done here; it is assumed to have been + done before calling this method. The optional keywords are not used if persistent is False. + + msgobj - a Msg/TempMsg instance or a message string. If one of the former, the remaining + keywords will be ignored. If a string, this will either be sent as-is (if persistent=False) or + it will be used together with header and senders keywords to create a Msg instance on the fly. + senders - an object, player or a list of objects or players. Optional if persistent=False. + sender_strings - Name strings of senders. Used for external connections where the sender + is not a player or object. When this is defined, external will be assumed. + external - Treat this message agnostic of its sender. + persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using + header and senders keywords. If False, other keywords will be ignored. + online (bool) - If this is set true, only messages people who are online. Otherwise, messages all players + connected. This can make things faster, but may not trigger listeners on players that are offline. + emit (bool) - Signals to the message formatter that this message is not to be directly associated with a name. + """ + if senders: + senders = make_iter(senders) + else: + senders = [] + if isinstance(msgobj, basestring): + # given msgobj is a string + msg = msgobj + if persistent and self.db.keep_log: + msgobj = Msg() + msgobj.save() + else: + # Use TempMsg, so this message is not stored. + msgobj = TempMsg() + msgobj.header = header + msgobj.message = msg + msgobj.channels = [self.dbobj] # add this channel + + if not msgobj.senders: + msgobj.senders = senders + msgobj = self.pre_send_message(msgobj) + if not msgobj: + return False + msgobj = self.message_transform(msgobj, emit=emit, + sender_strings=sender_strings, + external=external) + self.distribute_message(msgobj, online=online) + self.post_send_message(msgobj) + return True + + def tempmsg(self, message, header=None, senders=None): + """ + A wrapper for sending non-persistent messages. + """ + self.msg(message, senders=senders, header=header, persistent=False) + diff --git a/src/comms/imc2.py b/src/comms/imc2.py index 07cfac24d..a64056f30 100644 --- a/src/comms/imc2.py +++ b/src/comms/imc2.py @@ -11,7 +11,7 @@ from django.conf import settings from src.utils import logger, create, search, utils from src.server.sessionhandler import SESSIONS from src.scripts.scripts import Script -from src.comms.models import Channel, ExternalChannelConnection +from src.comms.models import ChannelDB, ExternalChannelConnection from src.comms.imc2lib import imc2_packets as pck from src.comms.imc2lib.imc2_trackers import IMC2MudList, IMC2ChanList from src.comms.imc2lib.imc2_listeners import handle_whois_reply @@ -26,7 +26,7 @@ IMC2_CLIENT_PWD = settings.IMC2_CLIENT_PWD IMC2_SERVER_PWD = settings.IMC2_SERVER_PWD # channel to send info to -INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0]) +INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0]) # all linked channel connections IMC2_CLIENT = None # IMC2 debug mode @@ -407,8 +407,8 @@ def create_connection(channel, imc2_channel): This will create a new IMC2<->channel connection. """ - if not type(channel) == Channel: - new_channel = Channel.objects.filter(db_key=channel) + if not type(channel) == ChannelDB: + new_channel = ChannelDB.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg(_("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found") % channel) return False diff --git a/src/comms/irc.py b/src/comms/irc.py index dea4a60ae..6915a60e3 100644 --- a/src/comms/irc.py +++ b/src/comms/irc.py @@ -7,13 +7,13 @@ from twisted.application import internet from twisted.words.protocols import irc from twisted.internet import protocol from django.conf import settings -from src.comms.models import ExternalChannelConnection, Channel +from src.comms.models import ExternalChannelConnection, ChannelDB from src.utils import logger, utils from src.server.sessionhandler import SESSIONS from django.utils.translation import ugettext as _ -INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0]) +INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0]) IRC_CHANNELS = [] def msg_info(message): @@ -52,9 +52,10 @@ class IRC_Bot(irc.IRCClient): msg_info(msg) logger.log_infomsg(msg) - - def privmsg(self, user, irc_channel, msg): - "Someone has written something in irc channel. Echo it to the evennia channel" + def get_mesg_info(self, user, irc_channel, msg): + """ + Get basic information about a message posted in IRC. + """ #find irc->evennia channel mappings conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key) if not conns: @@ -65,28 +66,31 @@ class IRC_Bot(irc.IRCClient): user.strip() else: user = _("Unknown") - msg = "[%s] %s@%s: %s" % (self.factory.evennia_channel, user, irc_channel, msg.strip()) + msg = msg.strip() + sender_strings = ["%s@%s" % (user, irc_channel)] + return conns, msg, sender_strings + + def privmsg(self, user, irc_channel, msg): + "Someone has written something in irc channel. Echo it to the evennia channel" + conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg) #logger.log_infomsg("" #find irc->evennia channel mappings conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key) if not conns: return - #format message: - user = user.split("!")[0] - if user: - user.strip() - else: - user = _("Unknown") - msg = "[%s] *%s@%s %s*" % (self.factory.evennia_channel, user, irc_channel, msg.strip()) + conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg) + # Transform this into a pose. + msg = ':' + msg #logger.log_infomsg("channel connection. """ - if not type(channel) == Channel: - new_channel = Channel.objects.filter(db_key=channel) + if not type(channel) == ChannelDB: + new_channel = ChannelDB.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel) return False diff --git a/src/comms/managers.py b/src/comms/managers.py index 6362471ae..018aa79ed 100644 --- a/src/comms/managers.py +++ b/src/comms/managers.py @@ -6,11 +6,12 @@ import itertools from django.db import models from django.db.models import Q from django.contrib.contenttypes.models import ContentType +from src.typeclasses.managers import returns_typeclass_list, returns_typeclass _GA = object.__getattribute__ _PlayerDB = None _ObjectDB = None -_Channel = None +_ChannelDB = None _SESSIONS = None _ExternalConnection = None _User = None @@ -45,13 +46,13 @@ def dbref(dbref, reqhash=True): def identify_object(inp): "identify if an object is a player or an object; return its database model" # load global stores - global _PlayerDB, _ObjectDB, _Channel, _ExternalConnection, _User + global _PlayerDB, _ObjectDB, _ChannelDB, _ExternalConnection, _User if not _PlayerDB: from src.players.models import PlayerDB as _PlayerDB if not _ObjectDB: from src.objects.models import ObjectDB as _ObjectDB - if not _Channel: - from src.comms.models import Channel as _Channel + if not _ChannelDB: + from src.comms.models import ChannelDB as _ChannelDB if not _ExternalConnection: from src.comms.models import ExternalChannelConnection as _ExternalConnection if not _User: @@ -65,9 +66,8 @@ def identify_object(inp): obj = inp typ = type(obj) if typ == _PlayerDB: return obj, "player" - if typ == _User: return obj.get_profile(), "player" elif typ == _ObjectDB: return obj, "object" - elif typ == _Channel: return obj, "channel" + elif typ == _ChannelDB: return obj, "channel" elif dbref(obj): return dbref(obj), "dbref" elif typ == basestring: return obj, "string" elif typ == _ExternalConnection: return obj, "external" @@ -97,8 +97,8 @@ def to_object(inp, objtype='player'): print objtype, inp, obj, typ, type(inp) raise CommError() elif objtype == 'channel': - if typ == 'string': return _Channel.objects.get(db_key__iexact=obj) - if typ == 'dbref': return _Channel.objects.get(id=obj) + if typ == 'string': return _ChannelDB.objects.get(db_key__iexact=obj) + if typ == 'dbref': return _ChannelDB.objects.get(id=obj) print objtype, inp, obj, typ, type(inp) raise CommError() elif objtype == 'external': @@ -263,13 +263,14 @@ class ChannelManager(models.Manager): channel_search (equivalent to ev.search_channel) """ - + @returns_typeclass_list def get_all_channels(self): """ Returns all channels in game. """ return self.all() + @returns_typeclass def get_channel(self, channelkey): """ Return the channel object if given its key. @@ -280,7 +281,7 @@ class ChannelManager(models.Manager): if not channels: # also check aliases channels = [channel for channel in self.all() - if channelkey in channel.aliases] + if channelkey in channel.aliases.all()] if channels: return channels[0] return None @@ -320,7 +321,8 @@ class ChannelManager(models.Manager): unique_online_users = set(sess.uid for sess in session_list if sess.logged_in) online_players = (sess.get_player() for sess in session_list if sess.uid in unique_online_users) for player in online_players: - players.extend(PlayerChannelConnection.objects.filter(db_player=player, db_channel=channel)) + players.extend(PlayerChannelConnection.objects.filter( + db_player=player.dbobj, db_channel=channel.dbobj)) else: players.extend(PlayerChannelConnection.objects.get_all_connections(channel)) @@ -328,6 +330,7 @@ class ChannelManager(models.Manager): return itertools.chain(players, external_connections) + @returns_typeclass_list def channel_search(self, ostring): """ Search the channel database for a particular channel. @@ -346,7 +349,7 @@ class ChannelManager(models.Manager): channels = self.filter(db_key__iexact=ostring) if not channels: # still no match. Search by alias. - channels = [channel for channel in self.all() if ostring.lower in [a.lower for a in channel.aliases]] + channels = [channel for channel in self.all() if ostring.lower() in [a.lower for a in channel.aliases.all()]] return channels # @@ -372,7 +375,7 @@ class PlayerChannelConnectionManager(models.Manager): break_connection """ - + @returns_typeclass_list def get_all_player_connections(self, player): "Get all connections that the given player has." player = to_object(player) @@ -381,7 +384,8 @@ class PlayerChannelConnectionManager(models.Manager): def has_player_connection(self, player, channel): "Checks so a connection exists player<->channel" if player and channel: - return self.filter(db_player=player).filter(db_channel=channel).count() > 0 + return self.filter(db_player=player.dbobj).filter( + db_channel=channel.dbobj).count() > 0 return False def get_all_connections(self, channel): diff --git a/src/comms/migrations/0011_renaming_channles_to_channels.py b/src/comms/migrations/0011_renaming_channles_to_channels.py new file mode 100644 index 000000000..b85944695 --- /dev/null +++ b/src/comms/migrations/0011_renaming_channles_to_channels.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Renaming M2M table for field db_hide_from_channles on 'Msg' + db.rename_table('comms_msg_db_hide_from_channles', 'comms_msg_db_hide_from_channels') + + def backwards(self, orm): + raise RuntimeException("Cannot revert this migration.") + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'comms.channel': { + 'Meta': {'object_name': 'Channel'}, + 'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] diff --git a/src/comms/migrations/0012_auto__add_interimchannel.py b/src/comms/migrations/0012_auto__add_interimchannel.py new file mode 100644 index 000000000..a811b8538 --- /dev/null +++ b/src/comms/migrations/0012_auto__add_interimchannel.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'InterimChannel' + db.create_table(u'comms_interimchannel', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('db_key', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)), + ('db_typeclass_path', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)), + ('db_date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('db_lock_storage', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal(u'comms', ['InterimChannel']) + + # Adding M2M table for field db_attributes on 'InterimChannel' + m2m_table_name = db.shorten_name(u'comms_interimchannel_db_attributes') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('interimchannel', models.ForeignKey(orm[u'comms.interimchannel'], null=False)), + ('attribute', models.ForeignKey(orm[u'typeclasses.attribute'], null=False)) + )) + db.create_unique(m2m_table_name, ['interimchannel_id', 'attribute_id']) + + # Adding M2M table for field db_tags on 'InterimChannel' + m2m_table_name = db.shorten_name(u'comms_interimchannel_db_tags') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('interimchannel', models.ForeignKey(orm[u'comms.interimchannel'], null=False)), + ('tag', models.ForeignKey(orm[u'typeclasses.tag'], null=False)) + )) + db.create_unique(m2m_table_name, ['interimchannel_id', 'tag_id']) + + + def backwards(self, orm): + # Deleting model 'InterimChannel' + db.delete_table(u'comms_interimchannel') + + # Removing M2M table for field db_attributes on 'InterimChannel' + db.delete_table(db.shorten_name(u'comms_interimchannel_db_attributes')) + + # Removing M2M table for field db_tags on 'InterimChannel' + db.delete_table(db.shorten_name(u'comms_interimchannel_db_tags')) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'comms.channel': { + 'Meta': {'object_name': 'Channel'}, + 'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.interimchannel': { + 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'InterimChannel'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] \ No newline at end of file diff --git a/src/comms/migrations/0013_rename_channel.py b/src/comms/migrations/0013_rename_channel.py new file mode 100644 index 000000000..18bda6c12 --- /dev/null +++ b/src/comms/migrations/0013_rename_channel.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + db.rename_table('comms_channel', 'comms_channeldb') + db.rename_column('comms_externalchannelconnection', 'channel_id', 'channeldb_id') + db.rename_column('comms_msg_db_hide_from_channels', 'channel_id', 'channeldb_id') + db.rename_column('comms_msg_db_receivers_channels', 'channel_id', 'channeldb_id') + + def backwards(self, orm): + db.rename_table('comms_channeldb', 'comms_channel') + db.rename_column('comms_externalchannelconnection', 'channeldb_id', 'channel_id') + db.rename_column('comms_msg_db_hide_from_channels', 'channeldb_id', 'channel_id') + db.rename_column('comms_msg_db_receivers_channels', 'channeldb_id', 'channel_id') + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'comms.channel': { + 'Meta': {'object_name': 'Channel'}, + 'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.Channel']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.Channel']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] \ No newline at end of file diff --git a/src/comms/migrations/0014_transfer_channels.py b/src/comms/migrations/0014_transfer_channels.py new file mode 100644 index 000000000..984fb6f6d --- /dev/null +++ b/src/comms/migrations/0014_transfer_channels.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Don't use "from appname.models import ModelName". + # Use orm.ModelName to refer to models in this application, + # and orm['appname.ModelName'] for models in other applications. + ChannelDB = orm['comms.ChannelDB'] + InterimChannel = orm['comms.InterimChannel'] + Attribute = orm['typeclasses.Attribute'] + Tag = orm['typeclasses.Tag'] + for channel in ChannelDB.objects.all(): + new_channel = InterimChannel(id=channel.id, db_key=channel.db_key, + db_lock_storage=channel.db_lock_storage) + new_channel.save() + desc = Attribute(db_key='desc', db_value=channel.db_desc) + desc.save() + keep_log = Attribute(db_key='keep_log', + db_value=channel.db_keep_log) + keep_log.save() + new_channel.db_attributes.add(desc) + new_channel.db_attributes.add(keep_log) + for name in [alias.strip() for alias in + channel.db_aliases.split(',')]: + tag = Tag.objects.filter(db_key=name.lower().strip(), db_category='comm_alias') + if tag: + tag = tag[0] + else: + tag = Tag(db_key=name.lower().strip(), db_category='comm_alias') + tag.save() + new_channel.db_tags.add(tag) + new_channel.save() + + def backwards(self, orm): + "Remove all InterimChannels." + orm['comms.InterimChannel'].objects.all().delete() + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'comms.channeldb': { + 'Meta': {'object_name': 'ChannelDB'}, + 'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.ChannelDB']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.interimchannel': { + 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'InterimChannel'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.ChannelDB']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.ChannelDB']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.ChannelDB']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] + symmetrical = True diff --git a/src/comms/migrations/0015_update_foreign_keys_remove_old_channels.py b/src/comms/migrations/0015_update_foreign_keys_remove_old_channels.py new file mode 100644 index 000000000..c1d822a0f --- /dev/null +++ b/src/comms/migrations/0015_update_foreign_keys_remove_old_channels.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Changing field 'ExternalChannelConnection.db_channel' + db.alter_column(u'comms_externalchannelconnection', 'db_channel_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.InterimChannel'])) + + # Changing field 'PlayerChannelConnection.db_channel' + db.alter_column(u'comms_playerchannelconnection', 'db_channel_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.InterimChannel'])) + + # Deleting model 'ChannelDB' + db.delete_table(u'comms_channeldb') + + def backwards(self, orm): + # Adding model 'ChannelDB' + db.create_table(u'comms_channeldb', ( + ('db_desc', self.gf('django.db.models.fields.CharField')(max_length=80, null=True, blank=True)), + ('db_aliases', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('db_lock_storage', self.gf('django.db.models.fields.TextField')(blank=True)), + ('db_key', self.gf('django.db.models.fields.CharField')(max_length=255, unique=True, db_index=True)), + ('db_keep_log', self.gf('django.db.models.fields.BooleanField')(default=True)), + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal(u'comms', ['ChannelDB']) + + InterimChannel = orm['comms.InterimTable'] + Attribute = orm['typeclasses.Attribute'] + ChannelDB = orm['comms.ChannelDB'] + + for channel in InterimChannel.objects.all(): + try: + desc = channel.db_attributes.objects.get(db_key='desc').db_value + except Attribute.DoesNotExist: + desc = '' + try: + keep_log = channel.db_attributes.objects.get( + db_key='keep_log').db_value + except Attribute.DoesNotExist: + keep_log = False + aliases = [alias.db_key for alias in + channel.db_tags.filter('comm_alias')] + aliases = ','.join(aliases) + new_channel = ChannelDB(db_desc=desc, db_keep_log=False, id=channel.id, + db_key=channel.db_key, db_lock_storage=channel.db_lock_storage, + db_aliases=aliases) + new_channel.save() + + + # Changing field 'ExternalChannelConnection.db_channel' + db.alter_column(u'comms_externalchannelconnection', 'db_channel_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.ChannelDB'])) + + # Changing field 'PlayerChannelConnection.db_channel' + db.alter_column(u'comms_playerchannelconnection', 'db_channel_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.ChannelDB'])) + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.InterimChannel']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.interimchannel': { + 'Meta': {'object_name': 'InterimChannel'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.InterimChannel']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.InterimChannel']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.InterimChannel']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] \ No newline at end of file diff --git a/src/comms/migrations/0016_finalize_tables.py b/src/comms/migrations/0016_finalize_tables.py new file mode 100644 index 000000000..692ef26b2 --- /dev/null +++ b/src/comms/migrations/0016_finalize_tables.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Don't use "from appname.models import ModelName". + # Use orm.ModelName to refer to models in this application, + # and orm['appname.ModelName'] for models in other applications. + db.rename_table('comms_interimchannel', 'comms_channeldb') + db.rename_table('comms_interimchannel_db_attributes', 'comms_channeldb_db_attributes') + db.rename_table('comms_interimchannel_db_tags', 'comms_channeldb_db_tags') + db.rename_column('comms_channeldb_db_attributes', 'interimchannel_id', 'channeldb_id') + db.rename_column('comms_channeldb_db_tags', 'interimchannel_id', 'channeldb_id') + + + def backwards(self, orm): + "Write your backwards methods here." + db.rename_column('comms_channeldb_db_attributes', 'channeldb_id', 'interimchannel_id') + db.rename_column('comms_channeldb_db_tags', 'channeldb_id', 'interimchannel_id') + db.rename_table('comms_channeldb', 'comms_interimchannel') + db.rename_table('comms_channeldb_db_attributes', 'comms_interimchannel_db_attributes') + db.rename_table('comms_channeldb_db_tags', 'comms_interimchannel_db_tags') + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'comms.externalchannelconnection': { + 'Meta': {'object_name': 'ExternalChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.ChannelDB']"}), + 'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.interimchannel': { + 'Meta': {'object_name': 'InterimChannel'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.channeldb': { + 'Meta': {'object_name': 'ChannelDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.msg': { + 'Meta': {'object_name': 'Msg'}, + 'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'db_header': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_hide_from_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_channels_set'", 'null': 'True', 'to': u"orm['comms.ChannelDB']"}), + 'db_hide_from_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_objects_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_hide_from_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hide_from_players_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_message': ('django.db.models.fields.TextField', [], {}), + 'db_receivers_channels': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'channel_set'", 'null': 'True', 'to': u"orm['comms.ChannelDB']"}), + 'db_receivers_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_object_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_receivers_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'receiver_player_set'", 'null': 'True', 'to': u"orm['players.PlayerDB']"}), + 'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'db_sender_objects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_object_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_sender_players': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sender_player_set'", 'null': 'True', 'db_index': 'True', 'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'comms.playerchannelconnection': { + 'Meta': {'object_name': 'PlayerChannelConnection'}, + 'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['comms.ChannelDB']"}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': u"orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'db_attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Attribute']", 'null': 'True', 'symmetrical': 'False'}), + 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'typeclasses.attribute': { + 'Meta': {'object_name': 'Attribute'}, + 'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['comms'] + symmetrical = True diff --git a/src/comms/models.py b/src/comms/models.py index a0110f5ff..0ef8f5776 100644 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -20,7 +20,9 @@ be able to delete connections on the fly). """ from datetime import datetime +from django.conf import settings from django.db import models +from src.typeclasses.models import TypedObject, TagHandler, AttributeHandler, AliasHandler from src.utils.idmapper.models import SharedMemoryModel from src.comms import managers from src.comms.managers import identify_object @@ -28,7 +30,11 @@ from src.locks.lockhandler import LockHandler from src.utils import logger from src.utils.utils import is_iter, to_str, crop, make_iter -__all__ = ("Msg", "TempMsg", "Channel", "PlayerChannelConnection", "ExternalChannelConnection") +__all__ = ("Msg", "TempMsg", "ChannelDB", "PlayerChannelConnection", "ExternalChannelConnection") + +_GA = object.__getattribute__ +_SA = object.__setattr__ +_DA = object.__delattr__ #------------------------------------------------------------ # @@ -72,7 +78,7 @@ class Msg(SharedMemoryModel): # with channels below. db_receivers_players = models.ManyToManyField('players.PlayerDB', related_name='receiver_player_set', null=True, help_text="player receivers") db_receivers_objects = models.ManyToManyField('objects.ObjectDB', related_name='receiver_object_set', null=True, help_text="object receivers") - db_receivers_channels = models.ManyToManyField("Channel", related_name='channel_set', null=True, help_text="channel recievers") + db_receivers_channels = models.ManyToManyField("ChannelDB", related_name='channel_set', null=True, help_text="channel recievers") # header could be used for meta-info about the message if your system needs it, or as a separate # store for the mail subject line maybe. @@ -88,7 +94,7 @@ class Msg(SharedMemoryModel): # these can be used to filter/hide a given message from supplied objects/players/channels db_hide_from_players = models.ManyToManyField("players.PlayerDB", related_name='hide_from_players_set', null=True) db_hide_from_objects = models.ManyToManyField("objects.ObjectDB", related_name='hide_from_objects_set', null=True) - db_hide_from_channles = models.ManyToManyField("Channel", related_name='hide_from_channels_set', null=True) + db_hide_from_channels = models.ManyToManyField("ChannelDB", related_name='hide_from_channels_set', null=True) # Database manager objects = managers.MsgManager() @@ -96,6 +102,7 @@ class Msg(SharedMemoryModel): def __init__(self, *args, **kwargs): SharedMemoryModel.__init__(self, *args, **kwargs) self.locks = LockHandler(self) + self.extra_senders = [] class Meta: "Define Django meta options" @@ -114,7 +121,9 @@ class Msg(SharedMemoryModel): def __senders_get(self): "Getter. Allows for value = self.sender" return [hasattr(o, "typeclass") and o.typeclass or o for o in - list(self.db_sender_players.all()) + list(self.db_sender_objects.all())] + list(self.db_sender_players.all()) + + list(self.db_sender_objects.all()) + + self.extra_senders] #@sender.setter def __senders_set(self, value): "Setter. Allows for self.sender = value" @@ -124,6 +133,10 @@ class Msg(SharedMemoryModel): self.db_sender_players.add(obj) elif typ == 'object': self.db_sender_objects.add(obj) + elif typ == 'external': + self.db_sender_external = "1" + self.extra_senders.append(obj) + print "I ran!" elif isinstance(typ, basestring): self.db_sender_external = obj elif not obj: @@ -137,6 +150,7 @@ class Msg(SharedMemoryModel): self.db_sender_players.clear() self.db_sender_objects.clear() self.db_sender_external = "" + self.extra_senders = [] self.save() senders = property(__senders_get, __senders_set, __senders_del) @@ -148,8 +162,11 @@ class Msg(SharedMemoryModel): self.db_sender_players.remove(obj) elif typ == 'object': self.db_sender_objects.remove(obj) - elif isinstance(obj, basestring) and self.db_sender_external == obj: - self.db_sender_external = "" + elif typ == 'external': + self.extra_senders = [receiver for receiver in + self.extra_senders if receiver != obj] + elif isinstance(obj, basestring): + self.db_sender_external = obj else: raise ValueError(obj) self.save() @@ -179,6 +196,7 @@ class Msg(SharedMemoryModel): "Deleter. Clears all receivers" self.db_receivers_players.clear() self.db_receivers_objects.clear() + self.extra_senders = [] self.save() receivers = property(__receivers_get, __receivers_set, __receivers_del) @@ -201,7 +219,7 @@ class Msg(SharedMemoryModel): #@channels.setter def __channels_set(self, value): "Setter. Allows for self.channels = value. Requires a channel to be added." - for val in (v for v in make_iter(value) if v): + for val in (v.dbobj for v in make_iter(value) if v): self.db_receivers_channels.add(val) #@channels.deleter def __channels_del(self): @@ -210,58 +228,6 @@ class Msg(SharedMemoryModel): self.save() channels = property(__channels_get, __channels_set, __channels_del) - # header property (wraps db_header) - #@property - def __header_get(self): - "Getter. Allows for value = self.message" - return self.db_header - #@message.setter - def __header_set(self, value): - "Setter. Allows for self.message = value" - if value: - self.db_header = value - self.save() - #@message.deleter - def __header_del(self): - "Deleter. Allows for del self.message" - self.db_header = "" - self.save() - header = property(__header_get, __header_set, __header_del) - - # message property (wraps db_message) - #@property - def __message_get(self): - "Getter. Allows for value = self.message" - return self.db_message - #@message.setter - def __message_set(self, value): - "Setter. Allows for self.message = value" - self.db_message = value - self.save() - #@message.deleter - def __message_del(self): - "Deleter. Allows for del self.message" - self.db_message = "" - self.save() - message = property(__message_get, __message_set, __message_del) - - # date_sent property (wraps db_date_sent) - #@property - def __date_sent_get(self): - "Getter. Allows for value = self.date_sent" - return self.db_date_sent - #@date_sent.setter - def __date_sent_set(self, value): - "Setter. Allows for self.date_sent = value" - raise Exception("You cannot edit date_sent!") - #@date_sent.deleter - def __date_sent_del(self): - "Deleter. Allows for del self.date_sent" - raise Exception("You cannot delete the date_sent property!") - date_sent = property(__date_sent_get, __date_sent_set, __date_sent_del) - - # hide_from property - #@property def __hide_from_get(self): "Getter. Allows for value = self.hide_from. Returns 3 lists of players, objects and channels" return self.db_hide_from_players.all(), self.db_hide_from_objects.all(), self.db_hide_from_channels.all() @@ -287,24 +253,6 @@ class Msg(SharedMemoryModel): self.save() hide_from = property(__hide_from_get, __hide_from_set, __hide_from_del) - # lock_storage property (wraps db_lock_storage) - #@property - def __lock_storage_get(self): - "Getter. Allows for value = self.lock_storage" - return self.db_lock_storage - #@nick.setter - def __lock_storage_set(self, value): - """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - self.db_lock_storage = value - self.save() - #@nick.deleter - def __lock_storage_del(self): - "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) - - _db_model_name = "msg" # used by attributes to safely store objects - # # Msg class methods # @@ -315,15 +263,6 @@ class Msg(SharedMemoryModel): receivers = ",".join(["[%s]" % obj.key for obj in self.channels] + [obj.key for obj in self.receivers]) return "%s->%s: %s" % (senders, receivers, crop(self.message, width=40)) - def access(self, accessing_obj, access_type='read', default=False): - """ - Determines if another object has permission to access. - accessing_obj - object trying to access this one - access_type - type of access sought - default - what to return if no lock of access_type was found - """ - return self.locks.check(accessing_obj, access_type=access_type, default=default) - #------------------------------------------------------------ # @@ -377,14 +316,13 @@ class TempMsg(object): "checks lock access" return self.locks.check(accessing_obj, access_type=access_type, default=default) - #------------------------------------------------------------ # # Channel # #------------------------------------------------------------ -class Channel(SharedMemoryModel): +class ChannelDB(TypedObject): """ This is the basis of a comm channel, only implementing the very basics of distributing messages. @@ -398,134 +336,18 @@ class Channel(SharedMemoryModel): """ - # - # Channel database model setup - # - # - # These databse fields are all set using their corresponding properties, - # named same as the field, but withtout the db_* prefix. - - # unique identifier for this channel - db_key = models.CharField('key', max_length=255, unique=True, db_index=True) - # optional description of channel - db_desc = models.CharField('description', max_length=80, blank=True, null=True) - # aliases for the channel. These are searched by cmdhandler - # as well to determine if a command is the name of a channel. - # Several aliases are separated by commas. - db_aliases = models.CharField('aliases', max_length=255) - # Whether this channel should remember its past messages - db_keep_log = models.BooleanField(default=True) - # Storage of lock definitions - db_lock_storage = models.TextField('locks', blank=True) - - # Database manager objects = managers.ChannelManager() - class Meta: - "Define Django meta options" - verbose_name = "Channel" + _typeclass_paths = settings.COMM_TYPECLASS_PATHS + _default_typeclass_path = settings.BASE_COMM_TYPECLASS or "src.comms.comms.Comm" def __init__(self, *args, **kwargs): - SharedMemoryModel.__init__(self, *args, **kwargs) - self.locks = LockHandler(self) + TypedObject.__init__(self, *args, **kwargs) + _SA(self, "tags", TagHandler(self, category_prefix="comm_")) + _SA(self, "aliases", AliasHandler(self, category_prefix="comm_")) + _SA(self, "attributes", AttributeHandler(self)) - # Wrapper properties to easily set database fields. These are - # @property decorators that allows to access these fields using - # normal python operations (without having to remember to save() - # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self - # is the object in question). - - # key property (wraps db_key) - #@property - def key_get(self): - "Getter. Allows for value = self.key" - return self.db_key - #@key.setter - def key_set(self, value): - "Setter. Allows for self.key = value" - self.db_key = value - self.save() - #@key.deleter - def key_del(self): - "Deleter. Allows for del self.key" - raise Exception("You cannot delete the channel key!") - key = property(key_get, key_set, key_del) - - # desc property (wraps db_desc) - #@property - def desc_get(self): - "Getter. Allows for value = self.desc" - return self.db_desc - #@desc.setter - def desc_set(self, value): - "Setter. Allows for self.desc = value" - self.db_desc = value - self.save() - #@desc.deleter - def desc_del(self): - "Deleter. Allows for del self.desc" - self.db_desc = "" - self.save() - desc = property(desc_get, desc_set, desc_del) - - # aliases property - #@property - def aliases_get(self): - "Getter. Allows for value = self.aliases. Returns a list of aliases." - if self.db_aliases: - return [perm.strip() for perm in self.db_aliases.split(',')] - return [] - #@aliases.setter - def aliases_set(self, value): - "Setter. Allows for self.aliases = value. Stores as a comma-separated string." - if is_iter(value): - value = ",".join([str(val).strip().lower() for val in value]) - self.db_aliases = value - self.save() - #@aliases_del.deleter - def aliases_del(self): - "Deleter. Allows for del self.aliases" - self.db_aliases = "" - self.save() - aliases = property(aliases_get, aliases_set, aliases_del) - - # keep_log property (wraps db_keep_log) - #@property - def keep_log_get(self): - "Getter. Allows for value = self.keep_log" - return self.db_keep_log - #@keep_log.setter - def keep_log_set(self, value): - "Setter. Allows for self.keep_log = value" - self.db_keep_log = value - self.save() - #@keep_log.deleter - def keep_log_del(self): - "Deleter. Allows for del self.keep_log" - self.db_keep_log = False - self.save() - keep_log = property(keep_log_get, keep_log_set, keep_log_del) - - # lock_storage property (wraps db_lock_storage) - #@property - def lock_storage_get(self): - "Getter. Allows for value = self.lock_storage" - return self.db_lock_storage - #@nick.setter - def lock_storage_set(self, value): - """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - self.db_lock_storage = value - self.save() - #@nick.deleter - def lock_storage_del(self): - "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del) - - db_model_name = "channel" # used by attributes to safely store objects class Meta: "Define Django meta options" @@ -537,7 +359,7 @@ class Channel(SharedMemoryModel): # def __str__(self): - return "Channel '%s' (%s)" % (self.key, self.desc) + return "Channel '%s' (%s)" % (self.key, self.typeclass.db.desc) def has_connection(self, player): """ @@ -555,77 +377,36 @@ class Channel(SharedMemoryModel): # do the check return PlayerChannelConnection.objects.has_player_connection(player, self) - def msg(self, msgobj, header=None, senders=None, persistent=True, online=False): - """ - Send the given message to all players connected to channel. Note that - no permission-checking is done here; it is assumed to have been - done before calling this method. The optional keywords are not used if persistent is False. - - msgobj - a Msg/TempMsg instance or a message string. If one of the former, the remaining - keywords will be ignored. If a string, this will either be sent as-is (if persistent=False) or - it will be used together with header and senders keywords to create a Msg instance on the fly. - senders (object, player or a list of objects or players) - ignored if msgobj is a Msg or TempMsg, or if - persistent=False. - persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using - header and senders keywords. If False, other keywords will be ignored. - online (bool) - If this is set true, only messages people who are online. Otherwise, messages all players - connected. This can make things faster, but may not trigger listeners on players that are offline. - """ - - if isinstance(msgobj, basestring): - # given msgobj is a string - if persistent: - msg = msgobj - msgobj = Msg() - msgobj.save() - if senders: - msgobj.senders = make_iter(senders) - msgobj.header = header - msgobj.message = msg - msgobj.channels = [self] # add this channel - else: - # just use the msg as-is - msg = msgobj - else: - # already in a Msg/TempMsg - msg = msgobj.message - - # get all players connected to this channel and send to them - for conn in Channel.objects.get_all_connections(self, online=online): - try: - conn.player.msg(msg, senders) - except AttributeError: - try: - conn.to_external(msg, senders, from_channel=self) - except Exception: - logger.log_trace("Cannot send msg to connection '%s'" % conn) - return True - - def tempmsg(self, message, header=None, senders=None): - """ - A wrapper for sending non-persistent messages. - """ - self.msg(message, senders=senders, header=header, persistent=False) - def connect_to(self, player): "Connect the user to this channel" + self.typeclass.pre_join_channel(player) if not self.access(player, 'listen'): return False + connect = self.typeclass.pre_join_channel(player) + if not connect: + return False player = player.dbobj conn = PlayerChannelConnection.objects.create_connection(player, self) if conn: + self.typeclass.post_join_channel(player) return True return False def disconnect_from(self, player): "Disconnect user from this channel." + disconnect = self.typeclass.pre_leave_channel(self, player) + if not disconnect: + return False PlayerChannelConnection.objects.break_connection(player, self) + self.typeclass.post_leave_channel(self, player) + return True def delete(self): "Clean out all connections to this channel and delete it." - for connection in Channel.objects.get_all_connections(self): + for connection in ChannelDB.objects.get_all_connections(self): connection.delete() - super(Channel, self).delete() + super(ChannelDB, self).delete() + def access(self, accessing_obj, access_type='listen', default=False): """ Determines if another object has permission to access. @@ -645,7 +426,7 @@ class PlayerChannelConnection(SharedMemoryModel): # Player connected to a channel db_player = models.ForeignKey("players.PlayerDB", verbose_name='player') # Channel the player is connected to - db_channel = models.ForeignKey(Channel, verbose_name='channel') + db_channel = models.ForeignKey(ChannelDB, verbose_name='channel') # Database manager objects = managers.PlayerChannelConnectionManager() @@ -670,11 +451,11 @@ class PlayerChannelConnection(SharedMemoryModel): #@property def channel_get(self): "Getter. Allows for value = self.channel" - return self.db_channel + return self.db_channel.typeclass #@channel.setter def channel_set(self, value): "Setter. Allows for self.channel = value" - self.db_channel = value + self.db_channel = value.dbobj self.save() #@channel.deleter def channel_del(self): @@ -698,7 +479,7 @@ class ExternalChannelConnection(SharedMemoryModel): that connection. """ # evennia channel connecting to - db_channel = models.ForeignKey(Channel, verbose_name='channel', + db_channel = models.ForeignKey(ChannelDB, verbose_name='channel', help_text='which channel this connection is tied to.') # external connection identifier db_external_key = models.CharField('external key', max_length=128, @@ -807,17 +588,18 @@ class ExternalChannelConnection(SharedMemoryModel): # methods # - def to_channel(self, message, from_obj=None): + def to_channel(self, message, *args, **kwargs): "Send external -> channel" - if not from_obj: + if 'from_obj' in kwargs and kwargs.pop('from_obj'): from_obj = self.external_key - self.channel.msg(message, senders=[self]) + self.channel.msg(message, senders=[self], *args, **kwargs) def to_external(self, message, senders=None, from_channel=None): "Send channel -> external" # make sure we are not echoing back our own message to ourselves # (this would result in a nasty infinite loop) + print senders if self in make_iter(senders):#.external_key: return diff --git a/src/comms/rss.py b/src/comms/rss.py index dbfec7a65..0852368fc 100644 --- a/src/comms/rss.py +++ b/src/comms/rss.py @@ -9,13 +9,13 @@ to the channel whenever the feed updates. import re from twisted.internet import task from django.conf import settings -from src.comms.models import ExternalChannelConnection, Channel +from src.comms.models import ExternalChannelConnection, ChannelDB from src.utils import logger, utils from src.scripts.models import ScriptDB RSS_ENABLED = settings.RSS_ENABLED RSS_UPDATE_INTERVAL = settings.RSS_UPDATE_INTERVAL -INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0]) +INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0]) RETAG = re.compile(r'<[^>]*?>') # holds rss readers they can be shut down at will. @@ -102,8 +102,8 @@ def create_connection(channel, url, interval): """ This will create a new RSS->channel connection """ - if not type(channel) == Channel: - new_channel = Channel.objects.filter(db_key=channel) + if not type(channel) == ChannelDB: + new_channel = ChannelDB.objects.filter(db_key=channel) if not new_channel: logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel) return False diff --git a/src/help/manager.py b/src/help/manager.py index 6bc6a2c60..61bf5a1c6 100644 --- a/src/help/manager.py +++ b/src/help/manager.py @@ -91,7 +91,7 @@ class HelpEntryManager(models.Manager): category - limit the search to a particular help topic """ ostring = ostring.strip().lower() - if help_categories: - return self.filter(db_topicstr__iexact=ostring, db_help_category__iexact=help_category) + if help_category: + return self.filter(db_key__iexact=ostring, db_help_category__iexact=help_category) else: - return self.filter(db_topicstr__iexact=ostring) + return self.filter(db_key__iexact=ostring) diff --git a/src/help/migrations/0004_auto__del_field_helpentry_db_permissions.py b/src/help/migrations/0004_auto__del_field_helpentry_db_permissions.py new file mode 100644 index 000000000..602f9cb9c --- /dev/null +++ b/src/help/migrations/0004_auto__del_field_helpentry_db_permissions.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting field 'HelpEntry.db_permissions' + db.delete_column(u'help_helpentry', 'db_permissions') + + # Adding M2M table for field db_tags on 'HelpEntry' + m2m_table_name = db.shorten_name(u'help_helpentry_db_tags') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('helpentry', models.ForeignKey(orm[u'help.helpentry'], null=False)), + ('tag', models.ForeignKey(orm[u'typeclasses.tag'], null=False)) + )) + db.create_unique(m2m_table_name, ['helpentry_id', 'tag_id']) + + + def backwards(self, orm): + # Adding field 'HelpEntry.db_permissions' + db.add_column(u'help_helpentry', 'db_permissions', + self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True), + keep_default=False) + + # Removing M2M table for field db_tags on 'HelpEntry' + db.delete_table(db.shorten_name(u'help_helpentry_db_tags')) + + + models = { + u'help.helpentry': { + 'Meta': {'object_name': 'HelpEntry'}, + 'db_entrytext': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_help_category': ('django.db.models.fields.CharField', [], {'default': "'General'", 'max_length': '255'}), + 'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_staff_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['typeclasses.Tag']", 'null': 'True', 'symmetrical': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'typeclasses.tag': { + 'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"}, + 'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}), + 'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['help'] \ No newline at end of file diff --git a/src/help/models.py b/src/help/models.py index e61474e67..1c3880b53 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -13,6 +13,7 @@ from django.db import models from src.utils.idmapper.models import SharedMemoryModel from src.help.manager import HelpEntryManager from src.utils import ansi +from src.typeclasses.models import Tag, TagHandler from src.locks.lockhandler import LockHandler from src.utils.utils import is_iter __all__ = ("HelpEntry",) @@ -45,17 +46,18 @@ class HelpEntry(SharedMemoryModel): # These database fields are all set using their corresponding properties, # named same as the field, but withtout the db_* prefix. - # title of the help + # title of the help entry db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for') # help category db_help_category = models.CharField("help category", max_length=255, default="General", help_text='organizes help entries in lists') # the actual help entry text, in any formatting. db_entrytext = models.TextField('help entry', blank=True, help_text='the main body of help text') - # a string of permissionstrings, separated by commas. Not used by help entries. - db_permissions = models.CharField('permissions', max_length=255, blank=True) # lock string storage db_lock_storage = models.TextField('locks', blank=True, help_text='normally view:all().') + # tags are primarily used for permissions + db_tags = models.ManyToManyField(Tag, null=True, + help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.') # (deprecated, only here to allow MUX helpfile load (don't use otherwise)). # TODO: remove this when not needed anymore. db_staff_only = models.BooleanField(default=False) @@ -66,15 +68,13 @@ class HelpEntry(SharedMemoryModel): def __init__(self, *args, **kwargs): SharedMemoryModel.__init__(self, *args, **kwargs) self.locks = LockHandler(self) + self.tags = TagHandler(self) class Meta: "Define Django meta options" verbose_name = "Help Entry" verbose_name_plural = "Help Entries" - # used by Attributes to safely retrieve stored object - _db_model_name = "helpentry" - # Wrapper properties to easily set database fields. These are # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() @@ -85,88 +85,88 @@ class HelpEntry(SharedMemoryModel): # key property (wraps db_key) #@property - def __key_get(self): - "Getter. Allows for value = self.key" - return self.db_key - #@key.setter - def __key_set(self, value): - "Setter. Allows for self.key = value" - self.db_key = value - self.save() - #@key.deleter - def __key_del(self): - "Deleter. Allows for del self.key. Deletes entry." - self.delete() - key = property(__key_get, __key_set, __key_del) + #def __key_get(self): + # "Getter. Allows for value = self.key" + # return self.db_key + ##@key.setter + #def __key_set(self, value): + # "Setter. Allows for self.key = value" + # self.db_key = value + # self.save() + ##@key.deleter + #def __key_del(self): + # "Deleter. Allows for del self.key. Deletes entry." + # self.delete() + #key = property(__key_get, __key_set, __key_del) - # help_category property (wraps db_help_category) - #@property - def __help_category_get(self): - "Getter. Allows for value = self.help_category" - return self.db_help_category - #@help_category.setter - def __help_category_set(self, value): - "Setter. Allows for self.help_category = value" - self.db_help_category = value - self.save() - #@help_category.deleter - def __help_category_del(self): - "Deleter. Allows for del self.help_category" - self.db_help_category = "General" - self.save() - help_category = property(__help_category_get, __help_category_set, __help_category_del) + ## help_category property (wraps db_help_category) + ##@property + #def __help_category_get(self): + # "Getter. Allows for value = self.help_category" + # return self.db_help_category + ##@help_category.setter + #def __help_category_set(self, value): + # "Setter. Allows for self.help_category = value" + # self.db_help_category = value + # self.save() + ##@help_category.deleter + #def __help_category_del(self): + # "Deleter. Allows for del self.help_category" + # self.db_help_category = "General" + # self.save() + #help_category = property(__help_category_get, __help_category_set, __help_category_del) - # entrytext property (wraps db_entrytext) - #@property - def __entrytext_get(self): - "Getter. Allows for value = self.entrytext" - return self.db_entrytext - #@entrytext.setter - def __entrytext_set(self, value): - "Setter. Allows for self.entrytext = value" - self.db_entrytext = value - self.save() - #@entrytext.deleter - def __entrytext_del(self): - "Deleter. Allows for del self.entrytext" - self.db_entrytext = "" - self.save() - entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) + ## entrytext property (wraps db_entrytext) + ##@property + #def __entrytext_get(self): + # "Getter. Allows for value = self.entrytext" + # return self.db_entrytext + ##@entrytext.setter + #def __entrytext_set(self, value): + # "Setter. Allows for self.entrytext = value" + # self.db_entrytext = value + # self.save() + ##@entrytext.deleter + #def __entrytext_del(self): + # "Deleter. Allows for del self.entrytext" + # self.db_entrytext = "" + # self.save() + #entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) - # permissions property - #@property - def __permissions_get(self): - "Getter. Allows for value = self.permissions. Returns a list of permissions." - return [perm.strip() for perm in self.db_permissions.split(',')] - #@permissions.setter - def __permissions_set(self, value): - "Setter. Allows for self.permissions = value. Stores as a comma-separated string." - if is_iter(value): - value = ",".join([str(val).strip().lower() for val in value]) - self.db_permissions = value - self.save() - #@permissions.deleter - def __permissions_del(self): - "Deleter. Allows for del self.permissions" - self.db_permissions = "" - self.save() - permissions = property(__permissions_get, __permissions_set, __permissions_del) + ## permissions property + ##@property + #def __permissions_get(self): + # "Getter. Allows for value = self.permissions. Returns a list of permissions." + # return [perm.strip() for perm in self.db_permissions.split(',')] + ##@permissions.setter + #def __permissions_set(self, value): + # "Setter. Allows for self.permissions = value. Stores as a comma-separated string." + # if is_iter(value): + # value = ",".join([str(val).strip().lower() for val in value]) + # self.db_permissions = value + # self.save() + ##@permissions.deleter + #def __permissions_del(self): + # "Deleter. Allows for del self.permissions" + # self.db_permissions = "" + # self.save() + #permissions = property(__permissions_get, __permissions_set, __permissions_del) # lock_storage property (wraps db_lock_storage) - #@property - def __lock_storage_get(self): - "Getter. Allows for value = self.lock_storage" - return self.db_lock_storage - #@nick.setter - def __lock_storage_set(self, value): - """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - self.db_lock_storage = value - self.save() - #@nick.deleter - def __lock_storage_del(self): - "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) + ##@property + #def __lock_storage_get(self): + # "Getter. Allows for value = self.lock_storage" + # return self.db_lock_storage + ##@nick.setter + #def __lock_storage_set(self, value): + # """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" + # self.db_lock_storage = value + # self.save() + ##@nick.deleter + #def __lock_storage_del(self): + # "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" + # logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) + #lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) # diff --git a/src/help/mux_help_db.json b/src/help/mux_help_db.json deleted file mode 100644 index 09e6aea44..000000000 --- a/src/help/mux_help_db.json +++ /dev/null @@ -1 +0,0 @@ -[{"pk": 2, "model": "help.helpentry", "fields": {"db_entrytext": "\"\n\n COMMAND: \"\n\n Says out loud to everyone in your current location (usually a\n room).\n\n Example:\n > \"Where is the movie theater?\n You say, \"Where is the movie theater?\"\n\n Note that the closing double quote is automatically added.\n\n Related Topics: page, pose, say, :, \".\n\n", "db_key": "\"", "db_staff_only": false}}, {"pk": 3, "model": "help.helpentry", "fields": {"db_entrytext": "#\n\n COMMAND: # \n\n Forces the object whose database number is to perform .\n Example: '#1033 move north' forces object #1033 to go north (assuming that\n you control it). The same restrictions that apply to @force also apply to\n this command.\n\n Related Topics: @force.\n\n", "db_key": "#", "db_staff_only": false}}, {"pk": 4, "model": "help.helpentry", "fields": {"db_entrytext": "$-COMMANDS\n\n These commands are called arbitrary commands, user-defined commands, or\n $-commands (for how they are defined). See 'arbitrary commands' for the\n full description.\n\n Related Topics: arbitrary commands\n\n", "db_key": "$-COMMANDS", "db_staff_only": false}}, {"pk": 5, "model": "help.helpentry", "fields": {"db_entrytext": "\n COMMAND: & [=]\n SYNONYM: @set = :[]\n\n Sets the attribute named on to . If\n is not a predefined attribute (like ofail or va), then it is\n created. Attributes so created are called user-named attributes.\n Attribute names may only contain letters, numbers, and the characters\n < -_.@#$^&*~?=+| >, and must start with a letter. The names of user-named\n attributes may not be abbreviated (an attempt to get the value of the\n attribute will fail, and an attempt to set will create a new attribute).\n The & command may be used to set predefined attributes (in this instance,\n '& =' is equivalent to '@ =').\n\n Related Topics: @set.\n\n", "db_key": "&", "db_staff_only": false}}, {"pk": 6, "model": "help.helpentry", "fields": {"db_entrytext": ":\n\n COMMAND: :\n\n Displays to everyone in your current room, preceded by your name\n and a space. However, if starts with a space, no space is\n inserted between your name and the pose -- like the ';' command.\n\n Example:\n >:jumps for joy.\n Player jumps for joy.\n >: 's cat meows.\n Player's cat meows.\n\n Related Topics: page, pose, say, whisper, ;, \".\n\n", "db_key": ":", "db_staff_only": false}}, {"pk": 7, "model": "help.helpentry", "fields": {"db_entrytext": ";\n\n COMMAND: ;\n\n This command is much like the ':' command, except that no space is inserted\n between your name and the pose. However, can start with its own\n space -- a result similar to using the ':' command.\n\n Example:\n > ;'s watch beeps.\n Player's watch beeps.\n > ; meows.\n Player meows.\n\n Warning: This command does not work in command lists run from an attribute\n because the ';' is treated as the command separator. Use pose/nospace\n instead.\n\n Related Topics: page, pose, say, whisper, :, \".\n\n", "db_key": ";", "db_staff_only": false}}, {"pk": 8, "model": "help.helpentry", "fields": {"db_entrytext": "@@\n\n COMMAND: @@ \n\n This command and everything after it (excluding the command-separator\n ';') is not evaluated by the parser. This is useful for adding comments\n in softcode or, more commonly, writing comment headers for softcode\n installers (such as /quotable text files). Note that @@ must be\n followed by a space or it will return an error message.\n\n Always be doubly sure that ()'s and {}'s in the (otherwise ignored)\n arguments are nested correctly, so that any semicolons used to separate it\n from subsequent code in the line are recognized by the parser.\n\n Example:\n > @va me=$foobar *:@fo #1234=%0;@@ This controls my foobar puppet.\n > foobar say Hello World!\n foobar says \"Hello World!\"\n > @@ --- Begin Code ---\n > @emit Hello World!\n > @@ --- End Code ---\n Hello World!\n\n Related Topics: think, @@(), null().\n\n", "db_key": "@@", "db_staff_only": false}}, {"pk": 9, "model": "help.helpentry", "fields": {"db_entrytext": "@AAHEAR\n\n COMMAND: @aahear = \n ATTRIBUTE: Aahear\n\n An Aahear on an object is activated whenever the listen pattern\n matches anything done/said by anything else in the room, including\n itself. (The Ahear ignores itself, helpful for keeping machines from\n triggering itself)\n\n Example:\n @aahear listener = \"I heard someone (maybe me?) say the word!\n\n Related Topics: @ahear, @amhear, @listen.\n\n", "db_key": "@AAHEAR", "db_staff_only": false}}, {"pk": 10, "model": "help.helpentry", "fields": {"db_entrytext": "@ACLONE\n\n COMMAND: @aclone = \n ATTRIBUTE: Aclone\n\n Sets the actions to be taken by a new object that has just been created\n as the result of a @clone command. The contents of the Aclone attribute\n are run by the new object and not by the old object.\n\n This attribute is only meaningful for things, and will never be\n automatically triggered on other object types.\n It is also possible to check the zone object/objects in the zone parent\n room for an @adisconnect. If one is found, it will be executed when a\n player disconnects in that zone.\n\n Example: @aclone Time bomb = @wait 600=@trig me/va;@wait 10=@trig me/vb\n @va time bomb = :EXPLODES with a thundering roar;@destroy me\n @vb time bomb = :ticks.; @wait 10=@trig me/vb\n\n Related Topics: @clone.\n\n", "db_key": "@ACLONE", "db_staff_only": false}}, {"pk": 11, "model": "help.helpentry", "fields": {"db_entrytext": "@ACONNECT\n\n COMMAND: @aconnect = \n ATTRIBUTE: Aconnect\n\n Sets the actions to be taken by a player upon connecting to the MUX. The\n following steps occur in the following order whenever a player connects to\n the MUX. Parentage is observed.\n\n - Execute the connecting player's @aconnect.\n - Execute the master room's @aconnect.\n - Execute any @aconnect found on any of the master room's contents.\n - If the location that the player is connecting into belongs to a zone,\n then\n\n a) if the zone object is a thing, execute its @aconnect.\n b) if the zone object is a room, execute any @aconnect found on any of\n that room's contents.\n\n Example:\n > @aconnect me = check.my.mailbox\n\n Related Topics: @adisconnect.\n\n", "db_key": "@ACONNECT", "db_staff_only": false}}, {"pk": 445, "model": "help.helpentry", "fields": {"db_entrytext": "@ACREATE\n\n COMMAND: @acreate =\n\n Sets the @acreate attribute on . @acreate is only valid for objects\n within the master room. Upon creation of any object, the @acreate attribute\n on all relevant objects in the master room is invoked. Arguments passed are\n %# as the dbref of the creator, %0 dbref of the object created, and %1 with\n the type of the object created.\n\n Related Topics: @adestroy\n\n", "db_key": "@ACREATE", "db_staff_only": false}}, {"pk": 443, "model": "help.helpentry", "fields": {"db_entrytext": "@ADDCOMMAND\n\n COMMAND: @addcommand =/\n\n This command has the capability of adding a softcoded command to the MUX's\n built-in command table. If already exists as a built-in command,\n such as 'look' or 'WHO', it will actually replace the functionality of\n that command, and all aliases that currently point to it. As in look's\n case, the aliases 'l', 'lo', and 'loo', and the main command 'look' will\n all point to these softcoded commands. The syntax of / is\n like any normal $command.\n\n For example:\n\n > &LOOK me=$look:say I looked here!\n > &LOOK_WITH_ARG me=$look *:say I looked at %0!\n > &LOOK_OUTSIDE me=$look/outside *:say I looked outside at %0!\n > @addcommand look=me/look\n > @addcommand look=me/look_with_arg\n > @addcommand look=me/look_outside\n\n{ 'wizhelp @addcommand2' for more }\n\n", "db_key": "@ADDCOMMAND", "db_staff_only": false}}, {"pk": 444, "model": "help.helpentry", "fields": {"db_entrytext": "@ADDCOMMAND (continued)\n\n Yes, you can specify more than one attribute to go with . When you\n type 'look' or 'look sofa', the game will execute all attributes that\n match what you have typed in. Aliases like 'l' are expanded to 'look' so\n you don't have to worry about those when you take command matching into\n consideration. If none of the attributes attached match (the attribute\n *must* begin with $ in order to match at all!), no output will\n be generated.\n\n Another use is to speed up use of a particular global command. Just use\n @addcommand to add it to the built-in list, and it will be looked up\n much faster.\n\n Related Topics: @delcommand, @listcommands.\n\n", "db_key": "@ADDCOMMAND2", "db_staff_only": false}}, {"pk": 12, "model": "help.helpentry", "fields": {"db_entrytext": "@ADESCRIBE\n\n COMMAND: @adescribe = \n ATTRIBUTE: Adescribe\n\n Sets the actions to be taken when is looked at.\n\n Example: @adesc kitten = :rubs against %n's legs affectionately.\n\n Related Topics: look, @desc, @idesc, @odesc, think\n\n", "db_key": "@ADESCRIBE", "db_staff_only": false}}, {"pk": 446, "model": "help.helpentry", "fields": {"db_entrytext": "@ADESTROY\n\n COMMAND: @adestroy =\n\n Sets the @adestroy attribute on . @adestroy is only valid for\n objects within the master room. Upon destruction of any object, the\n @adestroy attribute on all relevant objects in the master room is invoked.\n Arguments passed are %# as the dbref of the destroying object, %0 dbref of\n the object being destroyed, and %1 with the type of the object being\n destroyed.\n\n If the dbref of the destroyer and the destroyed are the same, the ADESTROY\n attribute is not executed.\n\n Note: The destroyed object may already be GARBAGE upon execution of the\n ADESTROY.\n\n Related Topics: @acreate\n\n", "db_key": "@ADESTROY", "db_staff_only": false}}, {"pk": 13, "model": "help.helpentry", "fields": {"db_entrytext": "@ADFAIL\n\n COMMAND: @adfail = \n ATTRIBUTE: Adfail\n\n Sets the action to be taken by an object when someone tries to drop it\n but fails because they didn't pass the object's drop lock.\n\n Example: @adfail sword = @name me=Cursed Sword;:laughs maniacally.\n\n Related Topics: drop, @dfail, @odfail, @lock.\n\n", "db_key": "@ADFAIL", "db_staff_only": false}}, {"pk": 14, "model": "help.helpentry", "fields": {"db_entrytext": "@ADISCONNECT\n\n COMMAND: @adisconnect = \n ATTRIBUTE: Adisconnect\n\n Sets the actions to be taken by a player upon disconnecting from the MUX.\n The following steps occur in the following order whenever a player connects\n to the MUX. Parentage is observed.\n\n - Execute the connecting player's @adisconnect.\n - Execute the master room's @adisconnect.\n - Execute any @adisconnect found on any of the master room's contents.\n - If the location that the player is connecting into belongs to a zone,\n then\n\n a) if the zone object is a thing, execute its @adisconnect.\n b) if the zone object is a room, execute any @adisconnect found on any of\n that room's contents.\n\n Example:\n > @adisconnect me = home\n\n Related Topics: @aconnect.\n\n", "db_key": "@ADISCONNECT", "db_staff_only": false}}, {"pk": 447, "model": "help.helpentry", "fields": {"db_entrytext": "@ADMIN\n\n COMMAND: @admin =\n\n Sets a TinyMUX configuration parameter to the indicated value. Type\n 'wizhelp config parameters' for a list of the config parameters that\n may be set.\n\n By default, only #1 can use @admin, but like most commands, this can be\n configured with 'access'. The wrinkle is that each has its own\n permissions which would prevent @admin from being useful to anyone but #1\n without also changine the 's permissions with 'config_access'.\n\n Related Topics: access, config_access.\n\n", "db_key": "@ADMIN", "db_staff_only": false}}, {"pk": 15, "model": "help.helpentry", "fields": {"db_entrytext": "@ADROP\n\n COMMAND: @adrop = \n ATTRIBUTE: Adrop\n\n Sets the action to be taken by an object when it is dropped, or by an exit\n when it is successfully used.\n\n Example: @adrop plastique = kill %n=100; @destroy me\n\n Related Topics: drop, @drop, @odrop, DROP-TO, EXITS.\n\n", "db_key": "@ADROP", "db_staff_only": false}}, {"pk": 16, "model": "help.helpentry", "fields": {"db_entrytext": "@AEFAIL\n\n COMMAND: @aefail = \n ATTRIBUTE: Aefail\n\n Sets the action to be taken by an object when someone tries to enter it\n but fails because the object is not ENTER_OK or the player fails the\n object's enter lock.\n\n The enter lock only affects the 'enter' command and its aliases (set via\n the @ealias command), it does not affect exits that lead to the object or\n teleporting in.\n\n This attribute is meaningful for players and things, and will never be\n automatically triggered on rooms or exits.\n\n Example: @aefail car = @emit ;'s alarm starts wailing when %n tries\n to break in.\n Related Topics: @aenter, @efail, @ealias, @enter, @oefail, @oenter, enter,\n ENTER_OK.\n\n", "db_key": "@AEFAIL", "db_staff_only": false}}, {"pk": 17, "model": "help.helpentry", "fields": {"db_entrytext": "@AENTER\n\n COMMAND: @aenter = \n ATTRIBUTE: Aenter\n\n Sets the action to be taken by an object or room when someone enters it,\n whether by using an exit, the enter or leave commands, or by teleporting.\n\n This attribute is meaningful for players, things, and rooms, and will never\n be automatically triggered on exits.\n\n Example: @aenter car = :starts its engine, eagerly awaiting a road trip.;\n \"Beep Beep!\n\n Related Topics: enter, @enter, @oenter, ENTER_OK.\n\n", "db_key": "@AENTER", "db_staff_only": false}}, {"pk": 18, "model": "help.helpentry", "fields": {"db_entrytext": "@AFAIL\n\n COMMAND: @afail = \n ATTRIBUTE: Afail\n\n Sets the commands to be performed by when one of these events\n occurs:\n\n - For exits: Someone tries to traverse the exit but cannot because they\n fail the exit's default lock or the exit is not linked.\n - For players and things: Someone tries to pick up the object but cannot\n because they fail the object's default lock.\n - For rooms, players, and things: Someone looks around inside the room,\n player, or thing and fails the object's default lock.\n\n Example:\n > @afail vase = :falls to the floor and smashes to pieces.;@destroy me\n\n Related Topics: @fail, @ofail, FAILURE.\n\n", "db_key": "@AFAIL", "db_staff_only": false}}, {"pk": 19, "model": "help.helpentry", "fields": {"db_entrytext": "@AGFAIL\n\n COMMAND: @agfail = \n ATTRIBUTE: Agfail\n\n Sets the action to be taken by an object when someone tries to give it\n away but fails because they didn't pass the object's give lock.\n\n Example: @agfail sword = @name me=Cursed Sword;:laughs maniacally.\n\n Related Topics: give, @gfail, @ogfail, @lock.\n\n", "db_key": "@AGFAIL", "db_staff_only": false}}, {"pk": 20, "model": "help.helpentry", "fields": {"db_entrytext": "@AHEAR\n\n COMMAND: @ahear = \n ATTRIBUTE: Ahear\n\n Sets the actions to be taken after the object hears a string that matches\n the pattern in the Listen attribute which was not produced by the object\n itself. Messages that are produced by the object itself are ignored.\n\n Example: @ahear clock = \"The time is now [time()]. >> BONNNNGGGGG <<\n\n Related Topics: @aahear, @amhear, @listen.\n\n", "db_key": "@AHEAR", "db_staff_only": false}}, {"pk": 21, "model": "help.helpentry", "fields": {"db_entrytext": "@AKILL\n\n COMMAND: @akill = \n ATTRIBUTE: Akill\n\n Sets the actions to be taken by an object after it is killed and has\n returned to its home.\n\n This attribute is only meaningful for players and things, and will never be\n automatically triggered on other object types.\n\n Example: @akill lion = south; :leaps onto %n, roaring loudly.;kill %n=100\n\n Related Topics: kill, @kill and @okill, BEING KILLED, IMMORTAL, WIZARD.\n\n", "db_key": "@AKILL", "db_staff_only": false}}, {"pk": 22, "model": "help.helpentry", "fields": {"db_entrytext": "@ALEAD\n\n COMMAND: @alead = \n ATTRIBUTE: Alead\n\n Sets the executed when object is lead.\n\n This attribute is only available with --enable-firanmux.\n\n Related Topics: @lead, @olead.\n\n", "db_key": "@ALEAD", "db_staff_only": false}}, {"pk": 23, "model": "help.helpentry", "fields": {"db_entrytext": "@ALEAVE\n\n COMMAND: @aleave = \n ATTRIBUTE: Aleave\n\n Sets the action to be taken by an object or room when someone leaves it,\n whether by using an exit, the enter or leave commands, or by teleporting.\n\n This attribute is meaningful for players, things, and rooms, and will never\n be automatically triggered on exits.\n\n Example: @aleave car = :stops to let %n out.;:revs its engine, hoping\n another brave soul would like a ride.\n\n Related Topics: leave, @leave, @oleave.\n\n", "db_key": "@ALEAVE", "db_staff_only": false}}, {"pk": 24, "model": "help.helpentry", "fields": {"db_entrytext": "@ALFAIL\n\n COMMAND: @alfail = \n ATTRIBUTE: Alfail\n\n Sets the action to be taken by an object when someone tries to leave it\n but fails because the player fails the object's leave lock.\n\n The leave lock only affects the 'leave' command and its aliases (set via\n the @ealias command), it does not affect going home, using an exit in the\n location, or teleporting out.\n\n This attribute is meaningful for players and things, and will never be\n automatically triggered on rooms or exits.\n\n Example: @alfail box = :rattles around as %n tries to escape.\n\n Related Topics: @aleave, @lalias, @leave, @lfail, @oleave, @olfail, leave.\n\n", "db_key": "@ALFAIL", "db_staff_only": false}}, {"pk": 25, "model": "help.helpentry", "fields": {"db_entrytext": "@ALIAS\n\n COMMAND: @alias = \n ATTRIBUTE: Alias\n\n Provides an alternate name by which the player is known. The alternate\n name is only used for players when referenced as '*' or by commands\n that only take playernames (such as page or @stats). You may not set\n an alias on any other object type.\n\n When setting an alias, the alias is checked to see that it is both a legal\n player name and not already in use. Only if both checks succeed is the\n alias set.\n\nRelated Topics: @name.\n\n", "db_key": "@ALIAS", "db_staff_only": false}}, {"pk": 448, "model": "help.helpentry", "fields": {"db_entrytext": "@ALLOWANCE\n\n ATTRIBUTE: Allowance\n\n COMMAND: @allowance [=]\n\n Sets the amount of money that the player receives each day he/she connects\n to the MUX. The Allowance attribute overrides the default allowance\n specified by the paycheck config parameter.\n\n This attribute is only visible and settable by wizards. It is only\n meaningful for players, and has no effect on other object types.\n\n", "db_key": "@ALLOWANCE", "db_staff_only": false}}, {"pk": 26, "model": "help.helpentry", "fields": {"db_entrytext": "@AMAIL\n\n COMMAND: @amail = \n ATTRIBUTE: Amail\n\n Sets the actions to be taken after a player receives @mail. This should\n *never* @mail another player, as this could cause an infinite loop.\n\n Example: @amail me=@mail/file [mail()]=2\n This would place all incoming messages in folder #2.\n\n Related Topics: @mailsucc, @signature, @mail.\n\n", "db_key": "@AMAIL", "db_staff_only": false}}, {"pk": 27, "model": "help.helpentry", "fields": {"db_entrytext": "@AMHEAR\n\n COMMAND: @amhear = \n ATTRIBUTE: Amhear\n\n Sets the actions to be taken after the object hears a string that matches\n the pattern in the Listen attribute which was produced by the object\n itself. Messages that are produced by anything other than the object itself\n are ignored.\n\n Example: @amhear listener = \"Wait a minute. I said the trigger word!\n\n Related Topics: @aahear, @ahear, @listen.\n\n", "db_key": "@AMHEAR", "db_staff_only": false}}, {"pk": 28, "model": "help.helpentry", "fields": {"db_entrytext": "@AMOVE\n\n COMMAND: @amove = \n ATTRIBUTE: Amove\n\n Sets the action to be taken by an object whenever it moves from one\n location to another, whether by using an exit, entering or leaving an\n object, teleporting, or going home.\n\n This attribute is meaningful for players, and things and will never be\n automatically triggered on other object types.\n\n Example: @amove car = @vz me=[extract(%vz,1,19)] [loc(me)]\n\n Related Topics: @move, @omove.\n\n", "db_key": "@AMOVE", "db_staff_only": false}}, {"pk": 30, "model": "help.helpentry", "fields": {"db_entrytext": "@APARENT\n\n COMMAND: @aparent = \n Attribute: Aparent\n\n Sets the actions to be taken when another object is parented to .\n The action list is executed with the permission of . Several\n arguments are passed during evaluation:\n\n %0 - dbref of the child being added or removed.\n %1 - 0 or 1 indicating if the parent is being removed (1) or added (0).\n %2 - dbref of the object issuing the @parent command.\n\n The @aparent attribute is not inherited or copied during @clone.\n\n Related Topics: @parent\n\n", "db_key": "@APARENT", "db_staff_only": false}}, {"pk": 29, "model": "help.helpentry", "fields": {"db_entrytext": "@APAY\n\n COMMAND: @apay = \n ATTRIBUTE: Apay\n\n Sets the actions to be taken after the object is given the number of coins\n specified in its Cost attribute. If the giver tries to give more than that\n number of coins, the excess is refunded, and if less than the necessary\n amount is given then it is all given back and a snide message is sent to\n the giver.\n\n This attribute is only meaningful for players and things, and will never be\n automatically triggered on other object types.\n\n Example: @apay Coke machine = @clone Can of Coke; :drops a can on the\n floor.\n\n Related Topics: give, @cost, @opay, @pay.\n\n", "db_key": "@APAY", "db_staff_only": false}}, {"pk": 449, "model": "help.helpentry", "fields": {"db_entrytext": "@APPLY_MARKED\n\n COMMAND: @apply_marked \n\n Performs once for each object in the database that has its MARK\n flag set, substituting the characters ## for the object number of the\n marked object. The command is performed by the player invoking the\n @apply_marked command, not by the marked object. This command may only\n be used when database cleaning is disabled (via @disable cleaning),\n as cleaning uses the MARKED flag to check connectivity.\n\n Related Topics: @mark, @mark_all, MARKED.\n\n", "db_key": "@APPLY_MARKED", "db_staff_only": false}}, {"pk": 31, "model": "help.helpentry", "fields": {"db_entrytext": "@ARFAIL\n\n COMMAND: @arfail = \n ATTRIBUTE: Arfail\n\n Sets the action to be taken by an object when someone tries to give it\n something that fails its give lock.\n\n Example: @arfail merchant = \"I don't buy such junk. Begone!;\n @tel %#=cheater_exit\n\n Related Topics: give, @agfail, @gfail, @ogfail, @orfail, @rfail, @lock.\n\n", "db_key": "@ARFAIL", "db_staff_only": false}}, {"pk": 32, "model": "help.helpentry", "fields": {"db_entrytext": "@ASSERT\n\n COMMAND: @assert [=]\n\n @assert stops the execution of further commands in the current action\n list if its argument is a false value. It doesn't affect new queue\n entries made by previous commands in the action list. Very useful to\n people who don't like @switch. If is given, they are\n executed instead of the rest of the commands in the current action list.\n\n Examples:\n > @va obj=$testme *:@pemit %#=Before;@assert %0;@pemit %#=After\n > testme 1\n Before\n After\n > testme 0\n Before\n\n > @force me={@switch 1=1,think 3rd;think 1st;@assert 0;think 2nd}\n 1st\n 3rd\n\n In the last example, the @switch is run, which queues 'think 3rd',\n 'think 1st' is run, displaying '1st', command execution is broken\n (so we never 'think 2nd', and then the queued 'think 3rd' is run,\n displaying '3rd'.\n\n If you follow that, you have a very good understanding of the\n TinyMUX queue.\n\n Related Topics: @break, BOOLEAN VALUES\n\n", "db_key": "@ASSERT", "db_staff_only": false}}, {"pk": 33, "model": "help.helpentry", "fields": {"db_entrytext": "@ASUCCESS\n\n COMMAND: @asuccess = \n ATTRIBUTE: Asucc\n\n Sets the actions to be taken by an object when someone successfully picks\n it up (because they passed the lock), by an exit when someone passes\n through it, or when someone looks at a room and passes the room's lock.\n\n Example: @asucc kitten = :climbs up your sleeve and nuzzles your face.\n\n Related Topics: @osucc, @success, SUCCESS.\n\n", "db_key": "@ASUCCESS", "db_staff_only": false}}, {"pk": 34, "model": "help.helpentry", "fields": {"db_entrytext": "@ATFAIL\n\n COMMAND: @atfail = \n ATTRIBUTE: Atfail\n\n Sets the action to be taken by an object when someone tries to teleport\n there but fails.\n\n Example: @atfail here = @page [owner(me)]=%N tried to teleport here.\n\n Related Topics: @teleport, @tfail, @otfail, @lock.\n\n", "db_key": "@ATFAIL", "db_staff_only": false}}, {"pk": 35, "model": "help.helpentry", "fields": {"db_entrytext": "@ATOFAIL\n\n COMMAND: @atofail = \n ATTRIBUTE: Atofail\n\n Sets the action to be taken by an object when someone tries to teleport\n out but fails.\n\n Example: @atofail here = @page [owner(me)]=%N tried to teleport out.\n\n Related Topics: @teleport, @tofail, @otofail, @lock.\n\n", "db_key": "@ATOFAIL", "db_staff_only": false}}, {"pk": 36, "model": "help.helpentry", "fields": {"db_entrytext": "@ATPORT\n\n COMMAND: @atport = \n ATTRIBUTE: Atport\n\n Sets the actions to be performed by object whenever it teleports.\n The actions are performed after the object moves to its new location.\n\n This attribute is only meaningful for players and things, and will never be\n automatically triggered on other object types.\n\n Example: @atport me = &TEL.COUNT me=add(v(TEL.COUNT),1)\n\n Related Topics: @otport, @oxtport, @tport, @teleport.\n\n", "db_key": "@ATPORT", "db_staff_only": false}}, {"pk": 450, "model": "help.helpentry", "fields": {"db_entrytext": "@ATTRIBUTE\n\n COMMAND: @attribute[/] [=]\n\n Performs operations on user-named attributes depending on the switch used.\n The following switches are available:\n\n /access - Changes the access to the named attribute. is a\n space-separated list of permissions to add or remove from\n the attribute's access permissions. For a list of possible\n values, see the related topic given below.\n\n /delete - Removes the named attribute from the attribute table.\n This switch does not remove instances of the attribute from\n objects, and any that remain will be renamed to the\n user-named attribute that re-uses the attribute number\n of the deleted attribute.\n\n /rename - Changes the name of the named attribute to .\n\n Note that changes to user-named attributes performed by this command\n are permanent and do not need to be performed each time the MUX is\n restarted. Further this also only affects user-named attributes.\n\n Related Topics: attribute permissions, attr_access\n\n", "db_key": "@ATTRIBUTE", "db_staff_only": false}}, {"pk": 37, "model": "help.helpentry", "fields": {"db_entrytext": "@AUFAIL\n\n COMMAND: @aufail = \n ATTRIBUTE: Aufail\n\n Sets the list of commands to be run when someone 'use's the object but\n fails the object's use lock. Note that the other functions controlled\n by the use lock (paying, listening, and $-commands) do not trigger\n Aufail.\n\n Example: @aufail robot = \"I _told_ you to leave me alone; kill %n=100\n\n Related Topics: @oufail, @ufail, @use.\n\n", "db_key": "@AUFAIL", "db_staff_only": false}}, {"pk": 38, "model": "help.helpentry", "fields": {"db_entrytext": "@AUSE\n\n COMMAND: @ause = \n ATTRIBUTE: Ause\n\n Sets the actions to be taken when someone uses the object with the use\n command.\n\n This attribute is only meaningful for players and things, and will never be\n automatically triggered on other object types.\n\n Example: @ause grenade = :EXPLODES with a thundering roar; kill %n=100;\n @destroy me\n Related Topics: use, @ouse, @use.\n\n", "db_key": "@AUSE", "db_staff_only": false}}, {"pk": 39, "model": "help.helpentry", "fields": {"db_entrytext": "@AWAY\n\n COMMAND: @away = \n ATTRIBUTE: Away\n\n This attribute is sent as a message to anyone who tries to page you when\n you are not connected.\n\n This attribute is only meaningful for players, and will never be\n automatically referenced on other object types.\n\n Example: @away me = Hey, I'm not even connected. So why are you paging me?\n\n Related Topics: @idle, @idletimeout, @reject, page.\n\n", "db_key": "@AWAY", "db_staff_only": false}}, {"pk": 451, "model": "help.helpentry", "fields": {"db_entrytext": "@BACKUP\n\n COMMAND: @backup\n\n This command will dump a flatfile of the database to the data directory.\n The flatfile will be named dump..tgz. IE, dump.0219-1613.tgz where\n 0219-1613 stands for February 19, 4:13pm.\n\n This file is tarred and gziped. It includes the game flatfile, and the\n mail and comsys databases. It does not include any of the text files, the\n .conf files or the mux.config file. Those would need to be backed up\n separately.\n\n Related Topics:\n\n", "db_key": "@BACKUP", "db_staff_only": false}}, {"pk": 452, "model": "help.helpentry", "fields": {"db_entrytext": "@BOOT\n\n COMMAND: @boot[/quiet] \n @boot/port \n\n Severs the named player's connection to the game. The player is given a\n notice that they have been booted. If the player is connected to the game\n more than once, then all connections to that player are severed.\n\n The following switches are available:\n\n /quiet - Don't give the booted player any special notice.\n\n /port - Disconnects only a specific port. In this case, must\n be the port number obtained from the SESSION command.\n\n Related Topics: @destroy, @toad, SESSION.\n\n", "db_key": "@BOOT", "db_staff_only": false}}, {"pk": 40, "model": "help.helpentry", "fields": {"db_entrytext": "@BREAK\n\n COMMAND: @break [=]\n\n @break stops the execution of further commands in the current action\n list if its argument is a true value. It doesn't affect new queue\n entries made by previous commands in the action list. Very useful to\n people who don't like @switch. If is given, they are\n executed instead of the rest of the commands in the current action list.\n\n Examples:\n > @va obj=$testme *:@pemit %#=Before;@break %0;@pemit %#=After\n > testme 0\n Before\n After\n > testme 1\n Before\n\n > @force me={@switch 1=1,think 3rd;think 1st;@break 1;think 2nd}\n 1st\n 3rd\n\n In the last example, the @switch is run, which queues 'think 3rd',\n 'think 1st' is run, displaying '1st', command execution is broken\n (so we never 'think 2nd', and then the queued 'think 3rd' is run,\n displaying '3rd'.\n\n If you follow that, you have a very good understanding of the\n TinyMUX queue.\n\n Related Topics: @assert, BOOLEAN VALUES\n\n", "db_key": "@BREAK", "db_staff_only": false}}, {"pk": 41, "model": "help.helpentry", "fields": {"db_entrytext": "@CBOOT\n\n COMMAND: @cboot[/quiet] =\n\n Only wizards or the owner of the channel can use this command. It\n forcefully removes an object from that channel.\n\n You may specify a player name as if you prefix it with an '*'\n (e.g., '*Player1'), otherwise should be a dbref or if you are\n in the same room, the name of an object or player.\n\n Example:\n > @cboot Public=Player1\n [Public] Staff1 boots Player1 off the channel.\n [Public] Player1 has left this channel.\n\n Related Topics: comsys commands\n\n", "db_key": "@CBOOT", "db_staff_only": false}}, {"pk": 42, "model": "help.helpentry", "fields": {"db_entrytext": "@CCHARGE\n\n COMMAND: @ccharge =\n\n This command imposes a charge of coins on transmitting over a\n channel. The default fee when a channel is created is 0. All proceeds\n benefit the channel owner.\n\n Example:\n > @ccharge Public=1\n Set.\n\n Related Topics: comsys commands\n\n", "db_key": "@CCHARGE", "db_staff_only": false}}, {"pk": 43, "model": "help.helpentry", "fields": {"db_entrytext": "@CCHOWN\n\n COMMAND: @cchown =\n\n Changes ownership of to . If the player is not in the\n same room, you will need to add a '*' before the name (e.g., '*player1').\n\n Example:\n >@cchown Public=Staff1\n Set.\n\n Related Topics: @clist, @cwho.\n\n", "db_key": "@CCHOWN", "db_staff_only": false}}, {"pk": 44, "model": "help.helpentry", "fields": {"db_entrytext": "@CCREATE\n\n COMMAND: @ccreate \n\n Only Wizards can create new channels.\n\n Creates a new channel with default settings. Once the channel is created,\n it can be associated with a object with the @cset command. That object can\n be used to set locks and descriptions on the channel.\n\n Example:\n > @ccreate Public\n Channel Public created.\n\n Related Topics: comsys, @cset, @cdestroy, @clist, @cchown.\n\n", "db_key": "@CCREATE", "db_staff_only": false}}, {"pk": 45, "model": "help.helpentry", "fields": {"db_entrytext": "@CDESTROY\n\n COMMAND: @cdestroy \n\n Deletes permanently from the comsystem database. It does not\n destroy all aliases that exist for -- those are left to the\n owners of those aliases. Players are notified both at login when they\n own aliases for which a channel no longer exists and when they try to\n use them. Even if a Channel is destroyed and then recreated, the alias\n will still be non functioning. The alias must be removed with the\n 'delcom' command and added with the 'addcom' command.\n\n Example:\n > @cdestroy Staff\n Channel Staff destroyed.\n\n Related Topics: @clist, @ccreate, @cchown, addcom, delcom.\n\n", "db_key": "@CDESTROY", "db_staff_only": false}}, {"pk": 46, "model": "help.helpentry", "fields": {"db_entrytext": "@CEMIT\n\n COMMAND: @cemit[/] =\n\n Sends over prefixed by the channel's name. You must own\n or control the channel to do this.\n\n The following switches are available:\n\n /noheader - Sends the message to everyone on without the\n channel's name prefixed.\n\n Example:\n > @cemit Public=This is a test!\n [Public] This is a test!\n\n > @cemit/noheader Public=This is a test!\n This is a test.\n\n Related Topics: comsys commands, alias, addcom, cemit()\n\n", "db_key": "@CEMIT", "db_staff_only": false}}, {"pk": 47, "model": "help.helpentry", "fields": {"db_entrytext": "@CHARGES\n\n COMMAND: @charges = \n ATTRIBUTE: Charges\n\n This attribute allows you to limit the number of times an object can be\n used. If there is a charges attribute it is decremented each time an\n action on the object is triggered. Once it reaches zero, normal triggering\n stops and the Runout attribute (if one is present) is run instead.\n\n Example: @charges Fireball wand = 5\n\n Related Topics: @runout.\n\n", "db_key": "@CHARGES", "db_staff_only": false}}, {"pk": 48, "model": "help.helpentry", "fields": {"db_entrytext": "@CHOWN\n\n COMMAND: @chown[/nostrip] [=]\n @chown /[=]\n\n The first form changes the ownership of to . By default,\n this is yourself. Objects may be things, rooms or exits. To @chown things,\n you have to be carrying the thing. For rooms or exits, you have to be in\n the room. Objects must have the CHOWN_OK flag set before they may be\n @chowned. In a room, the command used must be @chown here=, and for\n an object, you must be very specific. Players can't be @chowned; they\n always own themselves.\n\n When an object is @chowned, all unlocked attributes on the object are\n automatically @chowned as well. Locked attributes remain owned by their\n original owners.\n\n Without the /nostrip switch, CHOWN_OK, all flags specified in the\n stripped_flags configuration option, and all @powers are stripped. Also,\n HALT is set. With /nostrip, ROYALTY and INHERIT are preserved (albeit with\n warnings). #1 can use /nostrip to further preserve WIZARD and @powers.\n\n By default, stripped_flags includes: BLIND, CONNECTED, GAGGED, HEAD_FLAG,\n IMMORTAL, INHERIT, ROYALTY, SLAVE, STAFF, SUSPECT, UNINSPECTED, and WIZARD.\n\n{ 'help @chown2' for more }\n\n", "db_key": "@CHOWN", "db_staff_only": false}}, {"pk": 49, "model": "help.helpentry", "fields": {"db_entrytext": "@CHOWN (continued)\n\n The second form changes the ownership of the indicated attribute on \n to . The default is the owner of the object. You may only @chown\n unlocked attributes. You may @chown unlocked attributes on objects that you\n own to yourself, and you may also @chown attributes that you own on objects\n owned by others to the owner of the object.\n\n Related Topics: ATTRIBUTE OWNERSHIP, CHOWN_OK, @lock, stripped_flags, and\n @unlock.\n\n", "db_key": "@CHOWN2", "db_staff_only": false}}, {"pk": 453, "model": "help.helpentry", "fields": {"db_entrytext": "@CHOWNALL\n\n COMMAND: @chownall[/nostrip] [=]\n\n Changes the ownership of all of the victim's objects, rooms, and exits to\n the indicated recipient (or to the wizard performing the @chownall if\n no recipient is specified). All objects, rooms, and exits are set HALTED.\n\n Without the /nostrip switch, CHOWN_OK, all flags specified in the\n stripped_flags configuration option, and all @powers are stripped. Also,\n HALT is set. With /nostrip, ROYALTY and INHERIT are preserved. #1 can use\n /nostrip to further preserve WIZARD and @powers.\n\n By default, stripped_flags includes: BLIND, CONNECTED, GAGGED, HEAD_FLAG,\n IMMORTAL, INHERIT, ROYALTY, SLAVE, STAFF, SUSPECT, UNINSPECTED, and WIZARD.\n\n Related Topics: @chown.\n\n", "db_key": "@CHOWNALL", "db_staff_only": false}}, {"pk": 50, "model": "help.helpentry", "fields": {"db_entrytext": "@CHZONE\n\n COMMAND: @chzone =.\n\n Changes the zone of to . If is \"none\",\n the zone is reset to NOTHING.\n\n @chzone'ing a player does not automatically change the zone of their\n objects. Anyone may reset the zone of an object they own; \n must either be \"none\", or must be owned by them. Only wizards may @chzone\n an object to an arbitrary zone object. Players may @chzone themselves to\n an object they own; otherwise, only wizards may @chzone players. For\n non-player objects, @chzone'ing strips all flags in the stripped_flags.\n\n By default, stripped_flags includes: BLIND, CONNECTED, GAGGED, HEAD_FLAG,\n IMMORTAL, INHERIT, ROYALTY, SLAVE, STAFF, SUSPECT, UNINSPECTED, and WIZARD.\n\n Related Topics: stripped_flags, ZONE OBJECTS\n\n", "db_key": "@CHZONE", "db_staff_only": false}}, {"pk": 51, "model": "help.helpentry", "fields": {"db_entrytext": "@CLIST\n\n COMMAND: @clist[/