From 2bdaf034c8905bd09e3faa1c8962deaf2f05926f Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 27 Feb 2011 22:27:56 +0000 Subject: [PATCH] OBS: You need to resync your database! The Nickname system is now a separate database model with the result that also channel nicks are more robust. Also nickname replacement has been adjusted to fix some exceptional circumstances. Fixed a host of issues in the channel and nick handlers and adjoining commands that caused the channel-syscommands to fail in some situations. Resolves issue131. Resolves issue 132. Resolves issue 134. --- src/commands/cmdhandler.py | 7 +- src/commands/default/cmdset_default.py | 3 +- src/commands/default/comms.py | 218 ++++++++++++------------- src/commands/default/general.py | 116 +++++++------ src/commands/default/system.py | 4 +- src/comms/channelhandler.py | 12 +- src/comms/managers.py | 8 +- src/comms/models.py | 2 +- src/objects/exithandler.py | 2 +- src/objects/models.py | 200 +++++++++++------------ src/objects/objects.py | 2 +- src/server/server.py | 5 +- src/server/webclient.py | 4 +- src/server/webserver.py | 3 + src/settings_default.py | 2 +- src/utils/create.py | 4 +- src/utils/reloads.py | 11 +- 17 files changed, 298 insertions(+), 305 deletions(-) diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index de5135a477..fab1add4fc 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -109,8 +109,9 @@ def get_and_merge_cmdsets(caller): exit_cmdset = EXITHANDLER.get_cmdset(caller) location = caller.location if location and not caller_cmdset.no_objs: - # Gather all cmdsets stored on objects in the room. - local_objlist = location.contents + # Gather all cmdsets stored on objects in the room and + # also in the caller's inventory + local_objlist = location.contents + caller.contents local_objects_cmdsets = [obj.cmdset.current for obj in local_objlist if obj.cmdset.outside_access] @@ -154,7 +155,7 @@ def match_command(cmd_candidates, cmdset, logged_caller=None): # Searching possible command matches in the given cmdset matches = [] prev_found_cmds = [] # to avoid aliases clashing with themselves - for cmd_candidate in cmd_candidates: + for cmd_candidate in cmd_candidates: cmdmatches = list(set([cmd for cmd in cmdset if cmd == cmd_candidate.cmdname and cmd not in prev_found_cmds])) diff --git a/src/commands/default/cmdset_default.py b/src/commands/default/cmdset_default.py index ca68a75d2f..b53f29ae9f 100644 --- a/src/commands/default/cmdset_default.py +++ b/src/commands/default/cmdset_default.py @@ -81,8 +81,7 @@ class DefaultCmdSet(CmdSet): # Comm commands self.add(comms.CmdAddCom()) self.add(comms.CmdDelCom()) - self.add(comms.CmdComlist()) - self.add(comms.CmdClist()) + self.add(comms.CmdChannels()) self.add(comms.CmdCdestroy()) self.add(comms.CmdChannelCreate()) self.add(comms.CmdCdesc()) diff --git a/src/commands/default/comms.py b/src/commands/default/comms.py index 9a1ee943b7..fade6af511 100644 --- a/src/commands/default/comms.py +++ b/src/commands/default/comms.py @@ -3,38 +3,43 @@ Comsys command module. """ from src.comms.models import Channel, Msg, ChannelConnection +from src.comms.channelhandler import CHANNELHANDLER from src.utils import create, utils from src.permissions.permissions import has_perm from src.commands.default.muxcommand import MuxCommand -def find_channel(caller, channelname): +def find_channel(caller, channelname, silent=False): """ Helper function for searching for a single channel with some error handling. """ channels = Channel.objects.channel_search(channelname) if not channels: - caller.msg("Channel '%s' not found." % channelname) + if not silent: + caller.msg("Channel '%s' not found." % channelname) return None elif len(channels) > 1: matches = ", ".join(["%s(%s)" % (chan.key, chan.id) for chan in channels]) - caller.msg("Multiple channels match (be more specific): \n%s" % matches) + if not silent: + caller.msg("Multiple channels match (be more specific): \n%s" % matches) return None return channels[0] class CmdAddCom(MuxCommand): """ - addcom - join a channel with alias + addcom - subscribe to a channel with optional alias Usage: addcom [alias=] - Allows adding an alias for a channel to make is easier and - faster to use. Subsequent calls of this command can - be used to add multiple aliases. + Joins a given channel. If alias is given, this will allow you to + refer to the channel by this alias rather than the full channel + name. Subsequent calls of this command can be used to add multiple + aliases to an already joined channel. """ key = "addcom" + aliases = ["aliaschan","chanalias"] help_category = "Comms" def func(self): @@ -78,9 +83,7 @@ class CmdAddCom(MuxCommand): if alias: # create a nick and add it to the caller. - nicks = caller.nicks - nicks[alias.strip()] = channel.key - caller.nicks = nicks # nicks auto-save to database. + caller.nickhandler(alias, channel.key, nick_type="channel") string += "You can now refer to the channel %s with the alias '%s'." caller.msg(string % (channel.key, alias)) else: @@ -90,106 +93,55 @@ class CmdAddCom(MuxCommand): class CmdDelCom(MuxCommand): """ - delcom - remove a channel alias + delcom - unsubscribe from channel or remove channel alias Usage: - delcom + delcom - Removes the specified alias to a channel. If this is the last alias, - the user is effectively removed from the channel. + If the full channel name is given, unsubscribe from the + channel. If an alias is given, remove the alias but don't + unsubscribe. """ key = "delcom" + aliases = ["delaliaschan, delchanalias"] help_category = "Comms" def func(self): "Implementing the command. " caller = self.caller + player = caller.player if not self.args: - caller.msg("Usage: delcom ") + caller.msg("Usage: delcom ") return - - #find all the nicks defining this channel - searchnick = self.args.lower() - nicks = caller.nicks - channicks = [nick for nick in nicks.keys() - if nick == searchnick] - if not channicks: - caller.msg("You don't have any such alias defined.") - return - #if there are possible nick matches, look if they match a channel. - channel = None - for nick in channicks: - channel = find_channel(caller, nicks[nick]) - if channel: - break - if not channel: - caller.msg("No channel with alias '%s' found." % searchnick) - return - player = caller.player + ostring = self.args.lower() - if not channel.has_connection(player): - caller.msg("You are not on that channel.") + channel = find_channel(caller, ostring, silent=True) + if channel: + # we have given a channel name - unsubscribe + if not channel.has_connection(player): + caller.msg("You are listening to that channel.") + return + chkey = channel.key.lower() + # find all nicks linked to this channel and delete them + for nick in [nick for nick in caller.nicks + if nick.db_type == "channel" and nick.db_real.lower() == chkey]: + nick.delete() + channel.disconnect_from(player) + caller.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key) + return else: - if len(channicks) > 1: - del nicks[searchnick] - caller.msg("Your alias '%s' for channel %s was cleared." % (searchnick, - channel.key)) + # we are removing a channel nick + channame = caller.nickhandler(ostring, nick_type="channel") + channel = find_channel(caller, channame, silent=True) + if not channel: + caller.msg("No channel with alias '%s' was found." % ostring) else: - del nicks[searchnick] - channel.disconnect_from(player) - caller.msg("You stop listening to channel '%s'." % channel.key) - # have to save nicks back too - caller.nicks = nicks - -class CmdComlist(MuxCommand): - """ - comlist - list channel memberships - - Usage: - comlist - - Lists the channels a user is subscribed to. - """ - - key = "comlist" - aliases = ["channels"] - help_category = "Comms" - - def func(self): - "Implement the command" - - - caller = self.caller - player = caller.player - - connections = ChannelConnection.objects.get_all_player_connections(player) - - if not connections: - caller.msg("You don't listen to any channels.") - return - - # get aliases: - nicks = caller.nicks - channicks = {} - for connection in connections: - channame = connection.channel.key.lower() - channicks[channame] = ", ".join([nick for nick in nicks - if nicks[nick].lower() == channame]) + caller.nickhandler(ostring, nick_type="channel", delete=True) + caller.msg("Your alias '%s' for channel %s was cleared." % (ostring, channel.key)) - string = "Your subscribed channels (use @clist for full chan list)\n" - string += "** Alias Channel Status\n" - - for connection in connections: - string += " %s%s %-15.14s%-22.15s\n" % ('-', '-', - channicks[connection.channel.key.lower()], - connection.channel.key) - string = string[:-1] - caller.msg(string) - - # def cmd_allcom(command): # """ # allcom - operate on all channels @@ -282,43 +234,74 @@ class CmdComlist(MuxCommand): ## GLOBAL_CMD_TABLE.add_self("clearcom", cmd_clearcom) -class CmdClist(MuxCommand): +class CmdChannels(MuxCommand): """ @clist Usage: + @channels @clist - list channels - all channels + comlist - Lists all available channels in the game. + Lists all available channels available to you, wether you listen to them or not. + Use 'comlist" to only view your current channel subscriptions. """ - key = "@clist" - aliases = ["channellist", "all channels"] + key = "@channels" + aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"] help_category = "Comms" def func(self): "Implement function" caller = self.caller - - string = "All channels (use comlist to see your subscriptions)\n" - - string += "** Channel Perms Description\n" - channels = Channel.objects.get_all_channels() + + # all channels we have available to listen to + channels = [chan for chan in Channel.objects.get_all_channels() if has_perm(caller, chan, 'can_listen')] if not channels: - string += "(No channels) " - for chan in channels: - if has_perm(caller, chan, 'can_listen'): - string += " %s%s %-15.14s%-22.15s%s\n" % \ - ('-', - '-', - chan.key, - chan.permissions, - #chan.get_owner().get_name(show_dbref=False), - chan.desc) - string = string[:-1] - #s += "** End of Channel List **" + caller.msg("No channels available") + return + # all channel we are already subscribed to + subs = [conn.channel for conn in ChannelConnection.objects.get_all_player_connections(caller.player)] + + if self.cmdstring != "comlist": + + string = "\nAll available channels:" + cols = [[" "], ["Channel"], ["Aliases"], ["Perms"], ["Description"]] + for chan in channels: + if chan in subs: + cols[0].append(">") + else: + cols[0].append(" ") + cols[1].append(chan.key) + cols[2].append(",".join(chan.aliases)) + cols[3].append(",".join(chan.permissions)) + cols[4].append(chan.desc) + # put into table + for ir, row in enumerate(utils.format_table(cols)): + if ir == 0: + string += "\n{w" + "".join(row) + "{n" + else: + string += "\n" + "".join(row) + self.caller.msg(string) + + string = "\nYour channel subscriptions:" + if not subs: + string += "(None)" + else: + nicks = [nick for nick in caller.nicks if nick.db_type == 'channel'] + print nicks + cols = [["Channel"], ["Aliases"], ["Description"]] + for chan in subs: + cols[0].append(chan.key) + cols[1].append(",".join([nick.db_nick for nick in nicks + if nick.db_real.lower() == chan.key.lower()] + chan.aliases)) + cols[2].append(chan.desc) + # put into table + for ir, row in enumerate(utils.format_table(cols)): + if ir == 0: + string += "\n{w" + "".join(row) + "{n" + else: + string += "\n" + "".join(row) caller.msg(string) class CmdCdestroy(MuxCommand): @@ -349,11 +332,12 @@ class CmdCdestroy(MuxCommand): caller.msg("You are not allowed to do that.") return - message = "Channel %s is being destroyed. Make sure to change your aliases." % channel.key + message = "%s is being destroyed. Make sure to change your aliases." % channel msgobj = create.create_message(caller, message, channel) channel.msg(msgobj) channel.delete() - caller.msg("Channel %s was destroyed." % channel) + CHANNELHANDLER.update() + caller.msg("%s was destroyed." % channel) ## def cmd_cset(self): @@ -741,7 +725,7 @@ class CmdPage(MuxCommand): if 'list' in self.switches: pages = pages_we_sent + pages_we_got - pages.sort(lambda x,y: cmp(x.date_sent, y.date_sent)) + pages.sort(lambda x, y: cmp(x.date_sent, y.date_sent)) number = 10 if self.args: diff --git a/src/commands/default/general.py b/src/commands/default/general.py index 29cc65f048..51ed421cbb 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -9,6 +9,7 @@ from src.permissions.models import PermissionGroup from src.permissions.permissions import has_perm, has_perm_string from src.objects.models import HANDLE_SEARCH_ERRORS from src.utils import utils +from src.objects.models import Nick from src.commands.default.muxcommand import MuxCommand class CmdHome(MuxCommand): @@ -41,8 +42,9 @@ class CmdLook(MuxCommand): Usage: look look + look * - Observes your location or objects in your vicinity. + Observes your location or objects in your vicinity. """ key = "look" aliases = ["l"] @@ -56,7 +58,7 @@ class CmdLook(MuxCommand): if args: # Use search to handle duplicate/nonexistant results. - looking_at_obj = caller.search(args) + looking_at_obj = caller.search(args, use_nicks=True) if not looking_at_obj: return else: @@ -64,6 +66,9 @@ class CmdLook(MuxCommand): if not looking_at_obj: caller.msg("Location: None") return + if not hasattr(looking_at_obj, 'return_appearance'): + # this is likely due to us having a player instead + looking_at_obj = looking_at_obj.character # get object's appearance caller.msg(looking_at_obj.return_appearance(caller)) # the object's at_desc() method. @@ -109,16 +114,18 @@ class CmdNick(MuxCommand): Define a personal alias/nick Usage: - alias[/switches] = [] - nick '' + nick[/switches] = [] + alias '' - Switches: obj - alias an object + Switches: + object - alias an object player - alias a player clearall - clear all your aliases list - show all defined aliases - If no switch is given, a command/channel alias is created, used - to replace strings before sending the command. + If no switch is given, a command alias is created, used + to replace strings before sending the command. Give an empty + right-hand side to clear the nick Creates a personal nick for some in-game object or string. When you enter that string, it will be replaced @@ -129,8 +136,8 @@ class CmdNick(MuxCommand): if you want to change the inherent aliases of an object, use the @alias command instead. """ - key = "nickname" - aliases = ["nick, @nick, alias"] + key = "nick" + aliases = ["nickname", "nicks", "@nick", "alias"] def func(self): "Create the nickname" @@ -138,55 +145,58 @@ class CmdNick(MuxCommand): caller = self.caller switches = self.switches - if 'list' in switches: - string = "{wAliases:{n \n" - string = string + "\n\r".join(["%s = %s" % (alias, replace) - for alias, replace - in caller.nicks.items()]) + nicks = Nick.objects.filter(db_obj=caller.dbobj).exclude(db_type="channel") + if 'list' in switches or self.cmdstring == "nicks": + string = "{wDefined Nicks:{n" + cols = [["Type"],["Nickname"],["Translates-to"] ] + for nick in nicks: + cols[0].append(nick.db_type) + cols[1].append(nick.db_nick) + cols[2].append(nick.db_real) + for ir, row in enumerate(utils.format_table(cols)): + if ir == 0: + string += "\n{w" + "".join(row) + "{n" + else: + string += "\n" + "".join(row) caller.msg(string) return if 'clearall' in switches: - del caller.nicks + nicks.delete() caller.msg("Cleared all aliases.") + return + if not self.args or not self.lhs: + caller.msg("Usage: nick[/switches] nickname = [realname]") + return + nick = self.lhs + real = self.rhs + + if real == nick: + caller.msg("No point in setting nick same as the string to replace...") return - if not self.args or not self.lhs: - caller.msg("Usage: alias[/switches] string = [alias]") - return - - alias = self.lhs - rstring = self.rhs - err = None - if rstring == alias: - err = "No point in setting alias same as the string to replace..." - caller.msg(err) - return - elif 'obj' in switches: - # object alias, for adressing objects - # (including user-controlled ones) - err = caller.set_nick("_obj:%s" % alias, rstring) - atype = "Object" - elif 'player' in switches: - # player alias, used for messaging - err = caller.set_nick("_player:%s" % alias, rstring) - atype = "Player " - else: - # a command/channel alias - these are replaced if - # they begin a command string. - caller.msg(rstring) - caller.msg("going in: %s %s" % (alias, rstring)) - err = caller.set_nick(alias, rstring) - atype = "Command/channel " - if err: - if rstring: - err = "%salias %s changed from '%s' to '%s'." % (atype, alias, err, rstring) + # check so we have a suitable nick type + if not any(True for switch in switches if switch in ("object", "player", "inputline")): + switches = ["inputline"] + string = "" + for switch in switches: + 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.nickhandler(nick, nick_type=switch, delete=True) + else: + string += "\nNo nick '%s' found, so it could not be removed." % nick else: - err = "Cleared %salias '%s'(='%s')." % (atype, alias, err) - else: - err = "Set %salias '%s' = '%s'" % (atype, alias, rstring) - caller.msg(err.capitalize()) - - + # creating new nick + if oldnick: + string += "\nNick %s changed from '%s' to '%s'." % (nick, oldnick[0].db_real, real) + else: + string += "\nNick set: '%s' = '%s'." % (nick, real) + caller.nickhandler(nick, real, nick_type=switch) + caller.msg(string) + class CmdInventory(MuxCommand): """ inventory @@ -362,8 +372,8 @@ class CmdWho(MuxCommand): plr_pobject = session.get_character() if show_session_data: table[0].append(plr_pobject.name[:25]) - table[1].append(utils.time_format(delta_conn,0)) - table[2].append(utils.time_format(delta_cmd,1)) + table[1].append(utils.time_format(delta_conn, 0)) + table[2].append(utils.time_format(delta_cmd, 1)) table[3].append(plr_pobject.location.id) table[4].append(session.cmd_total) table[5].append(session.address[0]) diff --git a/src/commands/default/system.py b/src/commands/default/system.py index 7b4369f045..e20d705823 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -54,10 +54,10 @@ class CmdReload(MuxCommand): if attempt < max_attempts-1: caller.msg(" Waiting for modules(s) to finish (%s) ..." % attempt) else: - string = " ... The module(s) took too long to reload, " + string = "{r ... The module(s) took too long to reload, " string += "\n so the remaining reloads where skipped." string += "\n Re-run @reload again when modules have fully " - string += "\n re-initialized." + string += "\n re-initialized.{n" caller.msg(string) class CmdPy(MuxCommand): diff --git a/src/comms/channelhandler.py b/src/comms/channelhandler.py index 577ec27941..d3073bae46 100644 --- a/src/comms/channelhandler.py +++ b/src/comms/channelhandler.py @@ -34,14 +34,15 @@ class ChannelCommand(command.Command): Usage: - This is a channel. You can send to it by entering - its name or alias, followed by the text you want to send. + This is a channel. If you have subscribed to it, you can send to + it by entering its name or alias, followed by the text you want to + send. """ # this flag is what identifies this cmd as a channel cmd # and branches off to the system send-to-channel command # (which is customizable by admin) key = "general" - help_category = "Channels" + help_category = "Channel Names" permissions = "cmd:use_channels" is_channel = True obj = None @@ -64,6 +65,7 @@ class ChannelCommand(command.Command): caller.msg("Say what?") return channel = Channel.objects.get_channel(channelkey) + if not channel: caller.msg("Channel '%s' not found." % channelkey) return @@ -93,8 +95,8 @@ class ChannelHandler(object): def __str__(self): return ", ".join(str(cmd) for cmd in self.cached_channel_cmds) - - def reset(self): + + def clear(self): """ Reset the cache storage. """ diff --git a/src/comms/managers.py b/src/comms/managers.py index fdb04f78c1..bb37057e85 100644 --- a/src/comms/managers.py +++ b/src/comms/managers.py @@ -232,13 +232,7 @@ class ChannelManager(models.Manager): from src.comms.channelhandler import CHANNELHANDLER CHANNELHANDLER.update() return None - - def has_connection(self, player, channel): - "Check so the player is really listening to this channel." - ChannelConnection = ContentType.objects.get(app_label="comms", - model="channelconnection").model_class() - return ChannelConnection.objects.has_connection(player, channel) - + def get_all_connections(self, channel): """ Return the connections of all players listening diff --git a/src/comms/models.py b/src/comms/models.py index 8b13e9fbe1..666c88ae81 100644 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -474,7 +474,7 @@ class Channel(SharedMemoryModel): Checks so this player is actually listening to this channel. """ - return Channel.objects.has_connection(player, self) + return ChannelConnection.objects.has_connection(player, self) def msg(self, msgobj, from_obj=None): """ diff --git a/src/objects/exithandler.py b/src/objects/exithandler.py index 6ce105572a..bf665d7a85 100644 --- a/src/objects/exithandler.py +++ b/src/objects/exithandler.py @@ -34,7 +34,7 @@ class ExitHandler(object): "Setup cache storage" self.cached_exit_cmds = {} - def reset(self, exitcmd=None): + def clear(self, exitcmd=None): """ Reset cache storage. If obj is given, only remove that object from cache. diff --git a/src/objects/models.py b/src/objects/models.py index a4034191b3..07d52c71b1 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -13,10 +13,7 @@ Attributes are separate objects that store values persistently onto the database object. Like everything else, they can be accessed transparently through the decorating TypeClass. """ -try: - import cPickle as pickle -except ImportError: - import pickle + from django.db import models from django.conf import settings @@ -74,6 +71,39 @@ class Alias(SharedMemoryModel): verbose_name = "Object alias" verbose_name_plural = "Object aliases" +#------------------------------------------------------------ +# +# Nick +# +#------------------------------------------------------------ + +class Nick(SharedMemoryModel): + """ + This model holds whichever alternate names this object + has for OTHER objects, but also for arbitrary strings, + channels, players etc. Setting a nick does not affect + the nicknamed object at all (as opposed to Aliases above), + and only this object will be able to refer to the nicknamed + object by the given nick. + + The default nick types used by Evennia are: + inputline (default) - match against all input + player - match against player searches + obj - match against object searches + channel - used to store own names for channels + + """ + db_nick = models.CharField(max_length=255, db_index=True) # the nick + db_real = models.TextField() # the aliased string + db_type = models.CharField(default="inputline", max_length=16, null=True, blank=True) # the type of nick + db_obj = models.ForeignKey("ObjectDB") + + class Meta: + "Define Django meta options" + verbose_name = "Nickname" + verbose_name_plural = "Nicknames" + unique_together = ("db_nick", "db_type", "db_obj") + #------------------------------------------------------------ # # ObjectDB @@ -140,7 +170,7 @@ class ObjectDB(TypedObject): blank=True, null=True) # pickled dictionary storing the object's assigned nicknames # (use the 'nicks' property to access) - db_nicks = models.TextField(null=True, blank=True) + db_nicks = models.ForeignKey(Nick, blank=True, null=True, db_index=True) # Database manager objects = ObjectManager() @@ -157,7 +187,7 @@ class ObjectDB(TypedObject): #@property def aliases_get(self): "Getter. Allows for value = self.aliases" - return [alias.db_key for alias in Alias.objects.filter(db_obj=self)] + return list(Alias.objects.filter(db_obj=self)) #@aliases.setter def aliases_set(self, aliases): "Setter. Allows for self.aliases = value" @@ -277,30 +307,17 @@ class ObjectDB(TypedObject): # nicks property (wraps db_nicks) #@property def nicks_get(self): - """ - Getter. Allows for value = self.nicks. - This unpickles the nick dictionary. - """ - if self.db_nicks: - return pickle.loads(str(self.db_nicks)) - return {} - #@nicks.setter - def nicks_set(self, nick_dict): - """ - Setter. Allows for self.nicks = nick_dict. - This re-pickles the nick dictionary. - """ - if type(nick_dict) == dict: - # only allow storing dicts. - self.db_nicks = pickle.dumps(nick_dict) - self.save() - #@nicks.deleter + "Getter. Allows for value = self.aliases" + return list(Nick.objects.filter(db_obj=self)) + #@nick.setter + def nicks_set(self, nicks): + """Setter is disabled. Use the nickhandler instead.""" + logger.log_errmsg("Nicks (%s) cannot be set through obj.nicks. Use obj.nickhandler instead." % nicks) + #@nick.deleter def nicks_del(self): - """ - Deleter. Allows for del self.nicks. - Don't delete nicks, set to empty dict - """ - self.db_nicks = {} + "Deleter. Allows for del self.aliases" + for nick in Nick.objects.filter(db_obj=self): + nick.delete() nicks = property(nicks_get, nicks_set, nicks_del) @@ -361,66 +378,55 @@ class ObjectDB(TypedObject): return [exi for exi in self.contents if exi.has_attribute('_destination')] exits = property(exits_get) - - # - # Nicks - custom nicknames - # - # - # nicks - the object can with this create - # personalized aliases for in-game words. Essentially - # anything can be re-mapped, it's up to the implementation - # as to how often the nick is checked and converted - # to its real counterpart before entering into the system. - # - # Some system defaults: - # {"nick":"cmdname", # re-maps command names(also channels) - # "_player:nick":"player_name", # re-maps player names - # "_obj:nick":"realname"} # re-maps object names - # - # Example: a nick 'obj:red' mapped to the word "big red man" would - # allow you to search for the big red man with just 'red' (note - # that it's dumb substitution though; red will always translate - # to big red man when searching, regardless of if there is such - # a man or not. Such logics need to implemented for your particular - # game). - # - - def set_nick(self, nick, realname=None): + + def nickhandler(self, nick, realname=None, nick_type="inputline", startswith=False, delete=False): """ + This is a central method for getting and setting nicks + - mappings of nicks to strings, objects etc. This is the main + access method, use it in preference over the 'nicks' property. + Map a nick to a realname. Be careful if mapping an existing realname into a nick - you could make that realname inaccessible until you deleted the alias. - Don't set realname to delete a previously set nick. + To delete - don't set realname. - returns a string with the old realname that this alias - used to map (now overwritten), in case the - nickname was already defined before. - """ - if not nick: - return - if not realname: - nicks = self.nicks - delnick = "Old alias not found!" - if nick in nicks: - # delete the nick - delnick = nicks[nick] - del nicks[nick] - self.nicks = nicks - return delnick - nick = nick.strip() - realname = realname.strip() - if nick == realname: - return - # set the new alias - retval = None - nicks = self.nicks - if nick in nicks: - retval = nicks[nick] - nicks[nick] = realname - self.nicks = nicks - return retval - + nick_types can be defined freely depending on implementation. + The default nick_types used in Evennia are: + inputline (default) - match nick against all input + player - match nick against player searches + obj - match nick against object searches + channel - match nick when checking for channel aliases + the delete keyword will delete the given nick. + """ + if not nick or not nick.strip(): + return + nick = nick.strip() + query = Nick.objects.filter(db_obj=self, db_nick__iexact=nick) + if nick_type: + query = query.filter(db_type__iexact=nick_type) + if delete: + # remove the found nick(s) + query.delete() + elif realname == None: + # we want to get the real name for the nick. If none is + # found, we return the nick again + query = query.values_list("db_real", flat=True) + if query: + return query[0] + else: + return nick + else: + # we want to assign a new nick + real = realname.strip() + if query: + old_nick = query[0] + old_nick.db_real = real + old_nick.save() + else: + new_nick = Nick(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self) + new_nick.save() + # # Main Search method # @@ -460,20 +466,10 @@ class ObjectDB(TypedObject): if use_nicks: if ostring.startswith('*'): # player nick replace - for nick, real in ((nick.lstrip('_player:').strip(), real) - for nick, real in self.nicks.items() - if nick.strip().startswith('_player:')): - if ostring.lstrip('*').lower() == nick.lower(): - ostring = "*%s" % real - break + ostring = "*%s" % self.nickhandler(ostring.lstrip('*'), nick_type="player") else: # object nick replace - for nick, real in ((nick.lstrip('_obj:').strip(), real) - for nick, real in self.nicks.items() - if nick.strip().startswith('_obj:')): - if ostring.lower() == nick.lower(): - ostring = real - break + ostring = self.nickhandler(ostring, nick_type="object") results = ObjectDB.objects.object_search(self, ostring, global_search=global_search, @@ -512,11 +508,13 @@ class ObjectDB(TypedObject): lets its typeclass execute the command. raw_string - raw command input coming from the command line. """ - # nick replacement - for nick, real in self.nicks.items(): - if raw_string.startswith(nick): - raw_string = raw_string.replace(nick, real, 1) - break + # nick replacement - we require full-word matching. + raw_list = raw_string.split(None) + raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]] + for nick in Nick.objects.filter(db_obj=self, db_type__in=("inputline","channel")): + if nick.db_nick in raw_list: + raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1) + break cmdhandler.cmdhandler(self.typeclass(self), raw_string) def msg(self, message, from_obj=None, data=None): @@ -532,7 +530,7 @@ class ObjectDB(TypedObject): # we use a different __getattribute__ to avoid recursive loops. if object.__getattribute__(self, 'player'): - object.__getattribute__(self, 'player').msg(message, data) + object.__getattribute__(self, 'player').msg(message, from_obj, data) def emit_to(self, message, from_obj=None, data=None): "Deprecated. Alias for msg" diff --git a/src/objects/objects.py b/src/objects/objects.py index 352f69c4c8..61dee2d570 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -374,5 +374,5 @@ class Exit(Object): out of sync with the cache. You should do this also if overloading this function in a child class. """ - EXITHANDLER.reset(self.dbobj) + EXITHANDLER.clear(self.dbobj) return True diff --git a/src/server/server.py b/src/server/server.py index 2e20ee738f..4e3ec40c60 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -18,7 +18,6 @@ if os.name == 'nt': from twisted.application import internet, service from twisted.internet import protocol, reactor, defer from twisted.web import server, static -from twisted.python import threadpool from django.db import connection from django.conf import settings from src.utils import reloads @@ -203,7 +202,8 @@ if WEBSERVER_ENABLED: # a django-compatible webserver. - from src.server.webserver import DjangoWebRoot, WSGIWebServer#DjangoWebRoot + from twisted.python import threadpool + from src.server.webserver import DjangoWebRoot, WSGIWebServer # start a thread pool and define the root url (/) as a wsgi resource # recognized by Django @@ -221,7 +221,6 @@ if WEBSERVER_ENABLED: for port in WEBSERVER_PORTS: # create the webserver webserver = WSGIWebServer(threads, port, web_site) - #webserver = internet.TCPServer(port, web_site) #webserver = internet.SSLServer(port, web_site) webserver.setName('EvenniaWebServer%s' % port) EVENNIA.services.addService(webserver) diff --git a/src/server/webclient.py b/src/server/webclient.py index 431072df10..af03410dea 100644 --- a/src/server/webclient.py +++ b/src/server/webclient.py @@ -4,8 +4,8 @@ Web client server resource. The Evennia web client consists of two components running on twisted and django. They are both a part of the Evennia website url tree (so the testing website might be located -on http://localhost:8020/, whereas the webclient can be -found on http://localhost:8020/webclient.) +on http://localhost:8000/, whereas the webclient can be +found on http://localhost:8000/webclient.) /webclient - this url is handled through django's template system and serves the html page for the client diff --git a/src/server/webserver.py b/src/server/webserver.py index 57cfea06de..e17ac3bddd 100644 --- a/src/server/webserver.py +++ b/src/server/webserver.py @@ -44,6 +44,9 @@ class DjangoWebRoot(resource.Resource): path0 = request.prepath.pop(0) request.postpath.insert(0, path0) return self.wsgi_resource +# +# Threaded Webserver +# class WSGIWebServer(internet.TCPServer): """ diff --git a/src/settings_default.py b/src/settings_default.py index a6223f73e8..26485e2967 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -261,7 +261,7 @@ CHANNEL_PUBLIC = ("Public", 'ooc', 'Public discussion', CHANNEL_MUDINFO = ("MUDinfo", '', 'Informative messages', '''chan_admin:has_id(1), chan_listen:Immortals, - chan_send:Immportals''') + chan_send:Immortals''') # Channel showing when new people connecting CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'), 'Connection log', diff --git a/src/utils/create.py b/src/utils/create.py index 91d59e78ed..2c2cc92f71 100644 --- a/src/utils/create.py +++ b/src/utils/create.py @@ -331,7 +331,7 @@ def create_message(senderobj, message, channels=None, new_message.save() return new_message -def create_channel(key, aliases=None, description=None, +def create_channel(key, aliases=None, desc=None, permissions=None, keep_log=True): """ Create A communication Channel. A Channel serves as a central @@ -356,7 +356,7 @@ def create_channel(key, aliases=None, description=None, if not is_iter(aliases): aliases = [aliases] new_channel.aliases = ",".join([str(alias) for alias in aliases]) - new_channel.description = description + new_channel.desc = desc new_channel.keep_log = keep_log except IntegrityError: string = "Could not add channel: key '%s' already exists." % key diff --git a/src/utils/reloads.py b/src/utils/reloads.py index ff94dbc11b..b6e6fcbcd9 100644 --- a/src/utils/reloads.py +++ b/src/utils/reloads.py @@ -94,8 +94,8 @@ def reload_modules(): # clean out cache dictionary of typeclasses, exits and channe typeclassmodels.reset() - exithandler.EXITHANDLER.reset() - channelhandler.CHANNELHANDLER.reset() + exithandler.EXITHANDLER.clear() + channelhandler.CHANNELHANDLER.update() def reload_scripts(scripts=None, obj=None, key=None, dbref=None, init_mode=False): @@ -129,14 +129,17 @@ def cemit_info(message): Sends the info to a pre-set channel. This channel is set by CHANNEL_MUDINFO in settings. """ + logger.log_infomsg(message) try: infochan = settings.CHANNEL_MUDINFO infochan = Channel.objects.get_channel(infochan[0]) except Exception: - return + pass if infochan: cname = infochan.key cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')]) infochan.msg(cmessage) - + else: + cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')]) + logger.log_infomsg(cmessage)