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.

This commit is contained in:
Griatch 2011-02-27 22:27:56 +00:00
parent 7ebcefae2e
commit 2bdaf034c8
17 changed files with 298 additions and 305 deletions

View file

@ -109,8 +109,9 @@ def get_and_merge_cmdsets(caller):
exit_cmdset = EXITHANDLER.get_cmdset(caller) exit_cmdset = EXITHANDLER.get_cmdset(caller)
location = caller.location location = caller.location
if location and not caller_cmdset.no_objs: if location and not caller_cmdset.no_objs:
# Gather all cmdsets stored on objects in the room. # Gather all cmdsets stored on objects in the room and
local_objlist = location.contents # also in the caller's inventory
local_objlist = location.contents + caller.contents
local_objects_cmdsets = [obj.cmdset.current local_objects_cmdsets = [obj.cmdset.current
for obj in local_objlist for obj in local_objlist
if obj.cmdset.outside_access] if obj.cmdset.outside_access]

View file

@ -81,8 +81,7 @@ class DefaultCmdSet(CmdSet):
# Comm commands # Comm commands
self.add(comms.CmdAddCom()) self.add(comms.CmdAddCom())
self.add(comms.CmdDelCom()) self.add(comms.CmdDelCom())
self.add(comms.CmdComlist()) self.add(comms.CmdChannels())
self.add(comms.CmdClist())
self.add(comms.CmdCdestroy()) self.add(comms.CmdCdestroy())
self.add(comms.CmdChannelCreate()) self.add(comms.CmdChannelCreate())
self.add(comms.CmdCdesc()) self.add(comms.CmdCdesc())

View file

@ -3,38 +3,43 @@ Comsys command module.
""" """
from src.comms.models import Channel, Msg, ChannelConnection from src.comms.models import Channel, Msg, ChannelConnection
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils from src.utils import create, utils
from src.permissions.permissions import has_perm from src.permissions.permissions import has_perm
from src.commands.default.muxcommand import MuxCommand 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 Helper function for searching for a single channel with
some error handling. some error handling.
""" """
channels = Channel.objects.channel_search(channelname) channels = Channel.objects.channel_search(channelname)
if not channels: if not channels:
if not silent:
caller.msg("Channel '%s' not found." % channelname) caller.msg("Channel '%s' not found." % channelname)
return None return None
elif len(channels) > 1: elif len(channels) > 1:
matches = ", ".join(["%s(%s)" % (chan.key, chan.id) for chan in channels]) matches = ", ".join(["%s(%s)" % (chan.key, chan.id) for chan in channels])
if not silent:
caller.msg("Multiple channels match (be more specific): \n%s" % matches) caller.msg("Multiple channels match (be more specific): \n%s" % matches)
return None return None
return channels[0] return channels[0]
class CmdAddCom(MuxCommand): class CmdAddCom(MuxCommand):
""" """
addcom - join a channel with alias addcom - subscribe to a channel with optional alias
Usage: Usage:
addcom [alias=] <channel> addcom [alias=] <channel>
Allows adding an alias for a channel to make is easier and Joins a given channel. If alias is given, this will allow you to
faster to use. Subsequent calls of this command can refer to the channel by this alias rather than the full channel
be used to add multiple aliases. name. Subsequent calls of this command can be used to add multiple
aliases to an already joined channel.
""" """
key = "addcom" key = "addcom"
aliases = ["aliaschan","chanalias"]
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -78,9 +83,7 @@ class CmdAddCom(MuxCommand):
if alias: if alias:
# create a nick and add it to the caller. # create a nick and add it to the caller.
nicks = caller.nicks caller.nickhandler(alias, channel.key, nick_type="channel")
nicks[alias.strip()] = channel.key
caller.nicks = nicks # nicks auto-save to database.
string += "You can now refer to the channel %s with the alias '%s'." string += "You can now refer to the channel %s with the alias '%s'."
caller.msg(string % (channel.key, alias)) caller.msg(string % (channel.key, alias))
else: else:
@ -90,105 +93,54 @@ class CmdAddCom(MuxCommand):
class CmdDelCom(MuxCommand): class CmdDelCom(MuxCommand):
""" """
delcom - remove a channel alias delcom - unsubscribe from channel or remove channel alias
Usage: Usage:
delcom <alias> delcom <alias or channel>
Removes the specified alias to a channel. If this is the last alias, If the full channel name is given, unsubscribe from the
the user is effectively removed from the channel. channel. If an alias is given, remove the alias but don't
unsubscribe.
""" """
key = "delcom" key = "delcom"
aliases = ["delaliaschan, delchanalias"]
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
"Implementing the command. " "Implementing the command. "
caller = self.caller caller = self.caller
player = caller.player
if not self.args: if not self.args:
caller.msg("Usage: delcom <alias>") caller.msg("Usage: delcom <alias or channel>")
return return
ostring = self.args.lower()
#find all the nicks defining this channel channel = find_channel(caller, ostring, silent=True)
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: if channel:
break # we have given a channel name - unsubscribe
if not channel:
caller.msg("No channel with alias '%s' found." % searchnick)
return
player = caller.player
if not channel.has_connection(player): if not channel.has_connection(player):
caller.msg("You are not on that channel.") caller.msg("You are listening to that channel.")
else:
if len(channicks) > 1:
del nicks[searchnick]
caller.msg("Your alias '%s' for channel %s was cleared." % (searchnick,
channel.key))
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 return
chkey = channel.key.lower()
# get aliases: # find all nicks linked to this channel and delete them
nicks = caller.nicks for nick in [nick for nick in caller.nicks
channicks = {} if nick.db_type == "channel" and nick.db_real.lower() == chkey]:
for connection in connections: nick.delete()
channame = connection.channel.key.lower() channel.disconnect_from(player)
channicks[channame] = ", ".join([nick for nick in nicks caller.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key)
if nicks[nick].lower() == channame]) return
else:
string = "Your subscribed channels (use @clist for full chan list)\n" # we are removing a channel nick
string += "** Alias Channel Status\n" channame = caller.nickhandler(ostring, nick_type="channel")
channel = find_channel(caller, channame, silent=True)
for connection in connections: if not channel:
string += " %s%s %-15.14s%-22.15s\n" % ('-', '-', caller.msg("No channel with alias '%s' was found." % ostring)
channicks[connection.channel.key.lower()], else:
connection.channel.key) caller.nickhandler(ostring, nick_type="channel", delete=True)
string = string[:-1] caller.msg("Your alias '%s' for channel %s was cleared." % (ostring, channel.key))
caller.msg(string)
# def cmd_allcom(command): # def cmd_allcom(command):
# """ # """
@ -282,19 +234,20 @@ class CmdComlist(MuxCommand):
## GLOBAL_CMD_TABLE.add_self("clearcom", cmd_clearcom) ## GLOBAL_CMD_TABLE.add_self("clearcom", cmd_clearcom)
class CmdClist(MuxCommand): class CmdChannels(MuxCommand):
""" """
@clist @clist
Usage: Usage:
@channels
@clist @clist
list channels comlist
all channels
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" key = "@channels"
aliases = ["channellist", "all channels"] aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"]
help_category = "Comms" help_category = "Comms"
def func(self): def func(self):
@ -302,23 +255,53 @@ class CmdClist(MuxCommand):
caller = self.caller caller = self.caller
string = "All channels (use comlist to see your subscriptions)\n" # 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')]
string += "** Channel Perms Description\n"
channels = Channel.objects.get_all_channels()
if not channels: if not channels:
string += "(No channels) " 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: for chan in channels:
if has_perm(caller, chan, 'can_listen'): if chan in subs:
string += " %s%s %-15.14s%-22.15s%s\n" % \ cols[0].append(">")
('-', else:
'-', cols[0].append(" ")
chan.key, cols[1].append(chan.key)
chan.permissions, cols[2].append(",".join(chan.aliases))
#chan.get_owner().get_name(show_dbref=False), cols[3].append(",".join(chan.permissions))
chan.desc) cols[4].append(chan.desc)
string = string[:-1] # put into table
#s += "** End of Channel List **" 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) caller.msg(string)
class CmdCdestroy(MuxCommand): class CmdCdestroy(MuxCommand):
@ -349,11 +332,12 @@ class CmdCdestroy(MuxCommand):
caller.msg("You are not allowed to do that.") caller.msg("You are not allowed to do that.")
return 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) msgobj = create.create_message(caller, message, channel)
channel.msg(msgobj) channel.msg(msgobj)
channel.delete() channel.delete()
caller.msg("Channel %s was destroyed." % channel) CHANNELHANDLER.update()
caller.msg("%s was destroyed." % channel)
## def cmd_cset(self): ## def cmd_cset(self):

View file

@ -9,6 +9,7 @@ from src.permissions.models import PermissionGroup
from src.permissions.permissions import has_perm, has_perm_string from src.permissions.permissions import has_perm, has_perm_string
from src.objects.models import HANDLE_SEARCH_ERRORS from src.objects.models import HANDLE_SEARCH_ERRORS
from src.utils import utils from src.utils import utils
from src.objects.models import Nick
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
class CmdHome(MuxCommand): class CmdHome(MuxCommand):
@ -41,6 +42,7 @@ class CmdLook(MuxCommand):
Usage: Usage:
look look
look <obj> look <obj>
look *<player>
Observes your location or objects in your vicinity. Observes your location or objects in your vicinity.
""" """
@ -56,7 +58,7 @@ class CmdLook(MuxCommand):
if args: if args:
# Use search to handle duplicate/nonexistant results. # 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: if not looking_at_obj:
return return
else: else:
@ -64,6 +66,9 @@ class CmdLook(MuxCommand):
if not looking_at_obj: if not looking_at_obj:
caller.msg("Location: None") caller.msg("Location: None")
return 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 # get object's appearance
caller.msg(looking_at_obj.return_appearance(caller)) caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method. # the object's at_desc() method.
@ -109,16 +114,18 @@ class CmdNick(MuxCommand):
Define a personal alias/nick Define a personal alias/nick
Usage: Usage:
alias[/switches] <alias> = [<string>] nick[/switches] <nickname> = [<string>]
nick '' alias ''
Switches: obj - alias an object Switches:
object - alias an object
player - alias a player player - alias a player
clearall - clear all your aliases clearall - clear all your aliases
list - show all defined aliases list - show all defined aliases
If no switch is given, a command/channel alias is created, used If no switch is given, a command alias is created, used
to replace strings before sending the command. 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 Creates a personal nick for some in-game object or
string. When you enter that string, it will be replaced 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, if you want to change the inherent aliases of an object,
use the @alias command instead. use the @alias command instead.
""" """
key = "nickname" key = "nick"
aliases = ["nick, @nick, alias"] aliases = ["nickname", "nicks", "@nick", "alias"]
def func(self): def func(self):
"Create the nickname" "Create the nickname"
@ -138,54 +145,57 @@ class CmdNick(MuxCommand):
caller = self.caller caller = self.caller
switches = self.switches switches = self.switches
if 'list' in switches: nicks = Nick.objects.filter(db_obj=caller.dbobj).exclude(db_type="channel")
string = "{wAliases:{n \n" if 'list' in switches or self.cmdstring == "nicks":
string = string + "\n\r".join(["%s = %s" % (alias, replace) string = "{wDefined Nicks:{n"
for alias, replace cols = [["Type"],["Nickname"],["Translates-to"] ]
in caller.nicks.items()]) 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) caller.msg(string)
return return
if 'clearall' in switches: if 'clearall' in switches:
del caller.nicks nicks.delete()
caller.msg("Cleared all aliases.") caller.msg("Cleared all aliases.")
return return
if not self.args or not self.lhs: if not self.args or not self.lhs:
caller.msg("Usage: alias[/switches] string = [alias]") 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 return
alias = self.lhs # check so we have a suitable nick type
rstring = self.rhs if not any(True for switch in switches if switch in ("object", "player", "inputline")):
err = None switches = ["inputline"]
if rstring == alias: string = ""
err = "No point in setting alias same as the string to replace..." for switch in switches:
caller.msg(err) oldnick = Nick.objects.filter(db_obj=caller.dbobj, db_nick__iexact=nick, db_type__iexact=switch)
return if not real:
elif 'obj' in switches: # removal of nick
# object alias, for adressing objects if oldnick:
# (including user-controlled ones) # clear the alias
err = caller.set_nick("_obj:%s" % alias, rstring) string += "\nNick '%s' (= '%s') was cleared." % (nick, oldnick[0].db_real)
atype = "Object" caller.nickhandler(nick, nick_type=switch, delete=True)
elif 'player' in switches:
# player alias, used for messaging
err = caller.set_nick("_player:%s" % alias, rstring)
atype = "Player "
else: else:
# a command/channel alias - these are replaced if string += "\nNo nick '%s' found, so it could not be removed." % nick
# 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)
else: else:
err = "Cleared %salias '%s'(='%s')." % (atype, alias, err) # creating new nick
if oldnick:
string += "\nNick %s changed from '%s' to '%s'." % (nick, oldnick[0].db_real, real)
else: else:
err = "Set %salias '%s' = '%s'" % (atype, alias, rstring) string += "\nNick set: '%s' = '%s'." % (nick, real)
caller.msg(err.capitalize()) caller.nickhandler(nick, real, nick_type=switch)
caller.msg(string)
class CmdInventory(MuxCommand): class CmdInventory(MuxCommand):
""" """

View file

@ -54,10 +54,10 @@ class CmdReload(MuxCommand):
if attempt < max_attempts-1: if attempt < max_attempts-1:
caller.msg(" Waiting for modules(s) to finish (%s) ..." % attempt) caller.msg(" Waiting for modules(s) to finish (%s) ..." % attempt)
else: 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 so the remaining reloads where skipped."
string += "\n Re-run @reload again when modules have fully " string += "\n Re-run @reload again when modules have fully "
string += "\n re-initialized." string += "\n re-initialized.{n"
caller.msg(string) caller.msg(string)
class CmdPy(MuxCommand): class CmdPy(MuxCommand):

View file

@ -34,14 +34,15 @@ class ChannelCommand(command.Command):
Usage: Usage:
<channel name or alias> <message> <channel name or alias> <message>
This is a channel. You can send to it by entering This is a channel. If you have subscribed to it, you can send to
its name or alias, followed by the text you want to send. 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 # this flag is what identifies this cmd as a channel cmd
# and branches off to the system send-to-channel command # and branches off to the system send-to-channel command
# (which is customizable by admin) # (which is customizable by admin)
key = "general" key = "general"
help_category = "Channels" help_category = "Channel Names"
permissions = "cmd:use_channels" permissions = "cmd:use_channels"
is_channel = True is_channel = True
obj = None obj = None
@ -64,6 +65,7 @@ class ChannelCommand(command.Command):
caller.msg("Say what?") caller.msg("Say what?")
return return
channel = Channel.objects.get_channel(channelkey) channel = Channel.objects.get_channel(channelkey)
if not channel: if not channel:
caller.msg("Channel '%s' not found." % channelkey) caller.msg("Channel '%s' not found." % channelkey)
return return
@ -94,7 +96,7 @@ class ChannelHandler(object):
def __str__(self): def __str__(self):
return ", ".join(str(cmd) for cmd in self.cached_channel_cmds) return ", ".join(str(cmd) for cmd in self.cached_channel_cmds)
def reset(self): def clear(self):
""" """
Reset the cache storage. Reset the cache storage.
""" """

View file

@ -233,12 +233,6 @@ class ChannelManager(models.Manager):
CHANNELHANDLER.update() CHANNELHANDLER.update()
return None 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): def get_all_connections(self, channel):
""" """
Return the connections of all players listening Return the connections of all players listening

View file

@ -474,7 +474,7 @@ class Channel(SharedMemoryModel):
Checks so this player is actually listening Checks so this player is actually listening
to this channel. to this channel.
""" """
return Channel.objects.has_connection(player, self) return ChannelConnection.objects.has_connection(player, self)
def msg(self, msgobj, from_obj=None): def msg(self, msgobj, from_obj=None):
""" """

View file

@ -34,7 +34,7 @@ class ExitHandler(object):
"Setup cache storage" "Setup cache storage"
self.cached_exit_cmds = {} self.cached_exit_cmds = {}
def reset(self, exitcmd=None): def clear(self, exitcmd=None):
""" """
Reset cache storage. If obj is given, only remove Reset cache storage. If obj is given, only remove
that object from cache. that object from cache.

View file

@ -13,10 +13,7 @@ Attributes are separate objects that store values persistently onto
the database object. Like everything else, they can be accessed the database object. Like everything else, they can be accessed
transparently through the decorating TypeClass. transparently through the decorating TypeClass.
""" """
try:
import cPickle as pickle
except ImportError:
import pickle
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
@ -74,6 +71,39 @@ class Alias(SharedMemoryModel):
verbose_name = "Object alias" verbose_name = "Object alias"
verbose_name_plural = "Object aliases" 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 # ObjectDB
@ -140,7 +170,7 @@ class ObjectDB(TypedObject):
blank=True, null=True) blank=True, null=True)
# pickled dictionary storing the object's assigned nicknames # pickled dictionary storing the object's assigned nicknames
# (use the 'nicks' property to access) # (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 # Database manager
objects = ObjectManager() objects = ObjectManager()
@ -157,7 +187,7 @@ class ObjectDB(TypedObject):
#@property #@property
def aliases_get(self): def aliases_get(self):
"Getter. Allows for value = self.aliases" "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 #@aliases.setter
def aliases_set(self, aliases): def aliases_set(self, aliases):
"Setter. Allows for self.aliases = value" "Setter. Allows for self.aliases = value"
@ -277,30 +307,17 @@ class ObjectDB(TypedObject):
# nicks property (wraps db_nicks) # nicks property (wraps db_nicks)
#@property #@property
def nicks_get(self): def nicks_get(self):
""" "Getter. Allows for value = self.aliases"
Getter. Allows for value = self.nicks. return list(Nick.objects.filter(db_obj=self))
This unpickles the nick dictionary. #@nick.setter
""" def nicks_set(self, nicks):
if self.db_nicks: """Setter is disabled. Use the nickhandler instead."""
return pickle.loads(str(self.db_nicks)) logger.log_errmsg("Nicks (%s) cannot be set through obj.nicks. Use obj.nickhandler instead." % nicks)
return {} #@nick.deleter
#@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
def nicks_del(self): def nicks_del(self):
""" "Deleter. Allows for del self.aliases"
Deleter. Allows for del self.nicks. for nick in Nick.objects.filter(db_obj=self):
Don't delete nicks, set to empty dict nick.delete()
"""
self.db_nicks = {}
nicks = property(nicks_get, nicks_set, nicks_del) nicks = property(nicks_get, nicks_set, nicks_del)
@ -362,64 +379,53 @@ class ObjectDB(TypedObject):
if exi.has_attribute('_destination')] if exi.has_attribute('_destination')]
exits = property(exits_get) exits = property(exits_get)
# def nickhandler(self, nick, realname=None, nick_type="inputline", startswith=False, delete=False):
# 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):
""" """
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 Map a nick to a realname. Be careful if mapping an
existing realname into a nick - you could make that existing realname into a nick - you could make that
realname inaccessible until you deleted the alias. 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 nick_types can be defined freely depending on implementation.
used to map (now overwritten), in case the The default nick_types used in Evennia are:
nickname was already defined before. 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: if not nick or not nick.strip():
return 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() nick = nick.strip()
realname = realname.strip() query = Nick.objects.filter(db_obj=self, db_nick__iexact=nick)
if nick == realname: if nick_type:
return query = query.filter(db_type__iexact=nick_type)
# set the new alias if delete:
retval = None # remove the found nick(s)
nicks = self.nicks query.delete()
if nick in nicks: elif realname == None:
retval = nicks[nick] # we want to get the real name for the nick. If none is
nicks[nick] = realname # found, we return the nick again
self.nicks = nicks query = query.values_list("db_real", flat=True)
return retval 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 # Main Search method
@ -460,20 +466,10 @@ class ObjectDB(TypedObject):
if use_nicks: if use_nicks:
if ostring.startswith('*'): if ostring.startswith('*'):
# player nick replace # player nick replace
for nick, real in ((nick.lstrip('_player:').strip(), real) ostring = "*%s" % self.nickhandler(ostring.lstrip('*'), nick_type="player")
for nick, real in self.nicks.items()
if nick.strip().startswith('_player:')):
if ostring.lstrip('*').lower() == nick.lower():
ostring = "*%s" % real
break
else: else:
# object nick replace # object nick replace
for nick, real in ((nick.lstrip('_obj:').strip(), real) ostring = self.nickhandler(ostring, nick_type="object")
for nick, real in self.nicks.items()
if nick.strip().startswith('_obj:')):
if ostring.lower() == nick.lower():
ostring = real
break
results = ObjectDB.objects.object_search(self, ostring, results = ObjectDB.objects.object_search(self, ostring,
global_search=global_search, global_search=global_search,
@ -512,10 +508,12 @@ class ObjectDB(TypedObject):
lets its typeclass execute the command. lets its typeclass execute the command.
raw_string - raw command input coming from the command line. raw_string - raw command input coming from the command line.
""" """
# nick replacement # nick replacement - we require full-word matching.
for nick, real in self.nicks.items(): raw_list = raw_string.split(None)
if raw_string.startswith(nick): raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
raw_string = raw_string.replace(nick, real, 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 break
cmdhandler.cmdhandler(self.typeclass(self), raw_string) cmdhandler.cmdhandler(self.typeclass(self), raw_string)
@ -532,7 +530,7 @@ class ObjectDB(TypedObject):
# we use a different __getattribute__ to avoid recursive loops. # we use a different __getattribute__ to avoid recursive loops.
if object.__getattribute__(self, 'player'): 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): def emit_to(self, message, from_obj=None, data=None):
"Deprecated. Alias for msg" "Deprecated. Alias for msg"

View file

@ -374,5 +374,5 @@ class Exit(Object):
out of sync with the cache. You should do this also if out of sync with the cache. You should do this also if
overloading this function in a child class. overloading this function in a child class.
""" """
EXITHANDLER.reset(self.dbobj) EXITHANDLER.clear(self.dbobj)
return True return True

View file

@ -18,7 +18,6 @@ if os.name == 'nt':
from twisted.application import internet, service from twisted.application import internet, service
from twisted.internet import protocol, reactor, defer from twisted.internet import protocol, reactor, defer
from twisted.web import server, static from twisted.web import server, static
from twisted.python import threadpool
from django.db import connection from django.db import connection
from django.conf import settings from django.conf import settings
from src.utils import reloads from src.utils import reloads
@ -203,7 +202,8 @@ if WEBSERVER_ENABLED:
# a django-compatible webserver. # 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 # start a thread pool and define the root url (/) as a wsgi resource
# recognized by Django # recognized by Django
@ -221,7 +221,6 @@ if WEBSERVER_ENABLED:
for port in WEBSERVER_PORTS: for port in WEBSERVER_PORTS:
# create the webserver # create the webserver
webserver = WSGIWebServer(threads, port, web_site) webserver = WSGIWebServer(threads, port, web_site)
#webserver = internet.TCPServer(port, web_site)
#webserver = internet.SSLServer(port, web_site) #webserver = internet.SSLServer(port, web_site)
webserver.setName('EvenniaWebServer%s' % port) webserver.setName('EvenniaWebServer%s' % port)
EVENNIA.services.addService(webserver) EVENNIA.services.addService(webserver)

View file

@ -4,8 +4,8 @@ Web client server resource.
The Evennia web client consists of two components running The Evennia web client consists of two components running
on twisted and django. They are both a part of the Evennia on twisted and django. They are both a part of the Evennia
website url tree (so the testing website might be located website url tree (so the testing website might be located
on http://localhost:8020/, whereas the webclient can be on http://localhost:8000/, whereas the webclient can be
found on http://localhost:8020/webclient.) found on http://localhost:8000/webclient.)
/webclient - this url is handled through django's template /webclient - this url is handled through django's template
system and serves the html page for the client system and serves the html page for the client

View file

@ -44,6 +44,9 @@ class DjangoWebRoot(resource.Resource):
path0 = request.prepath.pop(0) path0 = request.prepath.pop(0)
request.postpath.insert(0, path0) request.postpath.insert(0, path0)
return self.wsgi_resource return self.wsgi_resource
#
# Threaded Webserver
#
class WSGIWebServer(internet.TCPServer): class WSGIWebServer(internet.TCPServer):
""" """

View file

@ -261,7 +261,7 @@ CHANNEL_PUBLIC = ("Public", 'ooc', 'Public discussion',
CHANNEL_MUDINFO = ("MUDinfo", '', 'Informative messages', CHANNEL_MUDINFO = ("MUDinfo", '', 'Informative messages',
'''chan_admin:has_id(1), '''chan_admin:has_id(1),
chan_listen:Immortals, chan_listen:Immortals,
chan_send:Immportals''') chan_send:Immortals''')
# Channel showing when new people connecting # Channel showing when new people connecting
CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'), CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'),
'Connection log', 'Connection log',

View file

@ -331,7 +331,7 @@ def create_message(senderobj, message, channels=None,
new_message.save() new_message.save()
return new_message return new_message
def create_channel(key, aliases=None, description=None, def create_channel(key, aliases=None, desc=None,
permissions=None, keep_log=True): permissions=None, keep_log=True):
""" """
Create A communication Channel. A Channel serves as a central 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): if not is_iter(aliases):
aliases = [aliases] aliases = [aliases]
new_channel.aliases = ",".join([str(alias) for alias in 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 new_channel.keep_log = keep_log
except IntegrityError: except IntegrityError:
string = "Could not add channel: key '%s' already exists." % key string = "Could not add channel: key '%s' already exists." % key

View file

@ -94,8 +94,8 @@ def reload_modules():
# clean out cache dictionary of typeclasses, exits and channe # clean out cache dictionary of typeclasses, exits and channe
typeclassmodels.reset() typeclassmodels.reset()
exithandler.EXITHANDLER.reset() exithandler.EXITHANDLER.clear()
channelhandler.CHANNELHANDLER.reset() channelhandler.CHANNELHANDLER.update()
def reload_scripts(scripts=None, obj=None, key=None, def reload_scripts(scripts=None, obj=None, key=None,
dbref=None, init_mode=False): dbref=None, init_mode=False):
@ -129,14 +129,17 @@ def cemit_info(message):
Sends the info to a pre-set channel. This channel is Sends the info to a pre-set channel. This channel is
set by CHANNEL_MUDINFO in settings. set by CHANNEL_MUDINFO in settings.
""" """
logger.log_infomsg(message) logger.log_infomsg(message)
try: try:
infochan = settings.CHANNEL_MUDINFO infochan = settings.CHANNEL_MUDINFO
infochan = Channel.objects.get_channel(infochan[0]) infochan = Channel.objects.get_channel(infochan[0])
except Exception: except Exception:
return pass
if infochan: if infochan:
cname = infochan.key cname = infochan.key
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')]) cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')])
infochan.msg(cmessage) infochan.msg(cmessage)
else:
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
logger.log_infomsg(cmessage)