mirror of
https://github.com/evennia/evennia.git
synced 2026-03-20 14:56:30 +01:00
589 lines
21 KiB
Python
589 lines
21 KiB
Python
"""
|
|
Models for the comsystem.
|
|
|
|
The comsystem's main component is the Message, which
|
|
carries the actual information between two parties.
|
|
Msgs are stored in the database and usually not
|
|
deleted.
|
|
A Msg always have one sender (a user), but can have
|
|
any number targets, both users and channels.
|
|
|
|
Channels are central objects that act as targets for
|
|
Msgs. Players can connect to channels by use of a
|
|
ChannelConnect object (this object is necessary to easily
|
|
be able to delete connections on the fly).
|
|
"""
|
|
|
|
from django.db import models
|
|
from src.utils.idmapper.models import SharedMemoryModel
|
|
from src.players.models import PlayerDB
|
|
from src.comms import managers
|
|
from src.server import sessionhandler
|
|
from src.permissions.permissions import has_perm
|
|
from src.utils.utils import is_iter
|
|
from src.utils.utils import dbref as is_dbref
|
|
|
|
|
|
#------------------------------------------------------------
|
|
#
|
|
# Utils
|
|
#
|
|
#------------------------------------------------------------
|
|
|
|
def obj_to_id(inp):
|
|
"""
|
|
Converts input object to an id string.
|
|
"""
|
|
dbref = is_dbref(inp)
|
|
if dbref:
|
|
return str(dbref)
|
|
if hasattr(inp, 'id'):
|
|
return str(inp.id)
|
|
if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'):
|
|
return str(inp.dbobj.id)
|
|
return str(inp)
|
|
|
|
def id_to_obj(dbref, db_model='PlayerDB'):
|
|
"""
|
|
loads from dbref to object. Uses the db_model to search
|
|
for the id.
|
|
"""
|
|
if db_model == 'PlayerDB':
|
|
from src.players.models import PlayerDB as db_model
|
|
else:
|
|
db_model = Channel
|
|
try:
|
|
dbref = int(dbref.strip())
|
|
return db_model.objects.get(id=dbref)
|
|
except Exception:
|
|
return None
|
|
|
|
#------------------------------------------------------------
|
|
#
|
|
# Msg
|
|
#
|
|
#------------------------------------------------------------
|
|
|
|
class Msg(SharedMemoryModel):
|
|
"""
|
|
A single message. This model describes all ooc messages
|
|
sent in-game, both to channels and between players.
|
|
|
|
The Msg class defines the following properties:
|
|
sender - sender of message
|
|
receivers - list of target objects for message
|
|
channels - list of channels message was sent to
|
|
message - the text being sent
|
|
date_sent - time message was sent
|
|
hide_from_sender - bool if message should be hidden from sender
|
|
hide_from_receivers - list of receiver objects to hide message from
|
|
hide_from_channels - list of channels objects to hide message from
|
|
permissions - perm strings
|
|
|
|
"""
|
|
|
|
#
|
|
# Msg database model setup
|
|
#
|
|
#
|
|
# These databse fields are all set using their corresponding properties,
|
|
# named same as the field, but withtout the db_* prefix.
|
|
|
|
# There must always be one sender of the message.
|
|
db_sender = models.ForeignKey(PlayerDB, related_name='sender_set')
|
|
# The destination objects of this message. Stored as a
|
|
# comma-separated string of object dbrefs. Can be defined along
|
|
# with channels below.
|
|
db_receivers = models.CharField(max_length=255, null=True, blank=True)
|
|
# The channels this message was sent to. Stored as a
|
|
# comma-separated string of channel dbrefs. A message can both
|
|
# have channel targets and destination objects.
|
|
db_channels = models.CharField(max_length=255, null=True, blank=True)
|
|
# The actual message and a timestamp. The message field
|
|
# should itself handle eventual headers etc.
|
|
db_message = models.TextField()
|
|
db_date_sent = models.DateTimeField(editable=False, auto_now_add=True)
|
|
# These are settable by senders/receivers/channels respectively.
|
|
# Stored as a comma-separated string of dbrefs. Can be used by the
|
|
# game to mask out messages from being visible in the archive (no
|
|
# messages are actually deleted)
|
|
db_hide_from_sender = models.BooleanField(default=False)
|
|
db_hide_from_receivers = models.CharField(max_length=255, null=True, blank=True)
|
|
db_hide_from_channels = models.CharField(max_length=255, null=True, blank=True)
|
|
# permission strings, separated by commas
|
|
db_permissions = models.CharField(max_length=255, blank=True)
|
|
|
|
# Database manager
|
|
objects = managers.MsgManager()
|
|
|
|
class Meta:
|
|
"Define Django meta options"
|
|
verbose_name = "Message"
|
|
verbose_name_plural = "Messages"
|
|
|
|
# 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).
|
|
|
|
# sender property (wraps db_sender)
|
|
#@property
|
|
def sender_get(self):
|
|
"Getter. Allows for value = self.sender"
|
|
return self.db_sender
|
|
#@sender.setter
|
|
def sender_set(self, value):
|
|
"Setter. Allows for self.sender = value"
|
|
self.db_sender = value
|
|
self.save()
|
|
#@sender.deleter
|
|
def sender_del(self):
|
|
"Deleter. Allows for del self.sender"
|
|
raise Exception("You cannot delete the sender of a message!")
|
|
sender = property(sender_get, sender_set, sender_del)
|
|
|
|
# receivers property
|
|
#@property
|
|
def receivers_get(self):
|
|
"Getter. Allows for value = self.receivers. Returns a list of receivers."
|
|
if self.db_receivers:
|
|
return [id_to_obj(dbref) for dbref in self.db_receivers.split(',')]
|
|
return []
|
|
#@receivers.setter
|
|
def receivers_set(self, value):
|
|
"Setter. Allows for self.receivers = value. Stores as a comma-separated string."
|
|
if is_iter(value):
|
|
value = ",".join([obj_to_id(val) for val in value])
|
|
self.db_receivers = obj_to_id(value)
|
|
self.save()
|
|
#@receivers.deleter
|
|
def receivers_del(self):
|
|
"Deleter. Allows for del self.receivers"
|
|
self.db_receivers = ""
|
|
self.save()
|
|
receivers = property(receivers_get, receivers_set, receivers_del)
|
|
|
|
# channels property
|
|
#@property
|
|
def channels_get(self):
|
|
"Getter. Allows for value = self.channels. Returns a list of channels."
|
|
if self.db_channels:
|
|
return [id_to_obj(dbref, 'Channel') for dbref in self.db_channels.split(',')]
|
|
return []
|
|
#@channels.setter
|
|
def channels_set(self, value):
|
|
"Setter. Allows for self.channels = value. Stores as a comma-separated string."
|
|
if is_iter(value):
|
|
value = ",".join([obj_to_id(val) for val in value])
|
|
self.db_channels = obj_to_id(value)
|
|
self.save()
|
|
#@channels.deleter
|
|
def channels_del(self):
|
|
"Deleter. Allows for del self.channels"
|
|
self.db_channels = ""
|
|
self.save()
|
|
channels = property(channels_get, channels_set, channels_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_sender property
|
|
#@property
|
|
def hide_from_sender_get(self):
|
|
"Getter. Allows for value = self.hide_from_sender."
|
|
return self.db_hide_from_sender
|
|
#@hide_from_sender.setter
|
|
def hide_from_sender_set(self, value):
|
|
"Setter. Allows for self.hide_from_senders = value."
|
|
self.db_hide_from_sender = value
|
|
self.save()
|
|
#@hide_from_sender.deleter
|
|
def hide_from_sender_del(self):
|
|
"Deleter. Allows for del self.hide_from_senders"
|
|
self.db_hide_from_sender = False
|
|
self.save()
|
|
hide_from_sender = property(hide_from_sender_get, hide_from_sender_set, hide_from_sender_del)
|
|
|
|
# hide_from_receivers property
|
|
#@property
|
|
def hide_from_receivers_get(self):
|
|
"Getter. Allows for value = self.hide_from_receivers. Returns a list of hide_from_receivers."
|
|
if self.db_hide_from_receivers:
|
|
return [id_to_obj(dbref) for dbref in self.db_hide_from_receivers.split(',')]
|
|
return []
|
|
#@hide_from_receivers.setter
|
|
def hide_from_receivers_set(self, value):
|
|
"Setter. Allows for self.hide_from_receivers = value. Stores as a comma-separated string."
|
|
if is_iter(value):
|
|
value = ",".join([obj_to_id(val) for val in value])
|
|
self.db_hide_from_receivers = obj_to_id(value)
|
|
self.save()
|
|
#@hide_from_receivers.deleter
|
|
def hide_from_receivers_del(self):
|
|
"Deleter. Allows for del self.hide_from_receivers"
|
|
self.db_hide_from_receivers = ""
|
|
self.save()
|
|
hide_from_receivers = property(hide_from_receivers_get, hide_from_receivers_set, hide_from_receivers_del)
|
|
|
|
# hide_from_channels property
|
|
#@property
|
|
def hide_from_channels_get(self):
|
|
"Getter. Allows for value = self.hide_from_channels. Returns a list of hide_from_channels."
|
|
if self.db_hide_from_channels:
|
|
return [id_to_obj(dbref) for dbref in self.db_hide_from_channels.split(',')]
|
|
return []
|
|
#@hide_from_channels.setter
|
|
def hide_from_channels_set(self, value):
|
|
"Setter. Allows for self.hide_from_channels = value. Stores as a comma-separated string."
|
|
if is_iter(value):
|
|
value = ",".join([obj_to_id(val) for val in value])
|
|
self.db_hide_from_channels = obj_to_id(value)
|
|
self.save()
|
|
#@hide_from_channels.deleter
|
|
def hide_from_channels_del(self):
|
|
"Deleter. Allows for del self.hide_from_channels"
|
|
self.db_hide_from_channels = ""
|
|
self.save()
|
|
hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del)
|
|
|
|
# permissions property
|
|
#@property
|
|
def permissions_get(self):
|
|
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
|
if self.db_permissions:
|
|
return [perm.strip() for perm in self.db_permissions.split(',')]
|
|
return []
|
|
#@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)
|
|
|
|
#
|
|
# Msg class method
|
|
#
|
|
|
|
def __str__(self):
|
|
"Print text"
|
|
if self.channels:
|
|
return "%s -> %s: %s" % (self.sender.key,
|
|
", ".join([chan.key for chan in self.channels]),
|
|
self.message)
|
|
else:
|
|
return "%s -> %s: %s" % (self.sender.key,
|
|
", ".join([rec.key for rec in self.receivers]),
|
|
self.message)
|
|
|
|
#------------------------------------------------------------
|
|
#
|
|
# Channel
|
|
#
|
|
#------------------------------------------------------------
|
|
|
|
class Channel(SharedMemoryModel):
|
|
"""
|
|
This is the basis of a comm channel, only implementing
|
|
the very basics of distributing messages.
|
|
|
|
The Channel class defines the following properties:
|
|
key - main name for channel
|
|
desc - optional description of channel
|
|
aliases - alternative names for the channel
|
|
keep_log - bool if the channel should remember messages
|
|
permissions - perm strings
|
|
|
|
"""
|
|
|
|
#
|
|
# 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(max_length=255, unique=True)
|
|
# optional description of channel
|
|
db_desc = models.CharField(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(max_length=255)
|
|
# Whether this channel should remember its past messages
|
|
db_keep_log = models.BooleanField(default=True)
|
|
# Permission strings, separated by commas
|
|
db_permissions = models.CharField(max_length=255, blank=True)
|
|
|
|
# Database manager
|
|
objects = managers.ChannelManager()
|
|
|
|
# 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)
|
|
|
|
# permissions property
|
|
#@property
|
|
def permissions_get(self):
|
|
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
|
if self.db_permissions:
|
|
return [perm.strip() for perm in self.db_permissions.split(',')]
|
|
return []
|
|
#@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)
|
|
|
|
class Meta:
|
|
"Define Django meta options"
|
|
verbose_name = "Channel"
|
|
verbose_name_plural = "Channels"
|
|
|
|
#
|
|
# Channel class methods
|
|
#
|
|
|
|
def __str__(self):
|
|
return "Channel '%s' (%s)" % (self.key, self.desc)
|
|
|
|
def has_connection(self, player):
|
|
"""
|
|
Checks so this player is actually listening
|
|
to this channel.
|
|
"""
|
|
return Channel.objects.has_connection(player, self)
|
|
|
|
def msg(self, msgobj, from_obj=None):
|
|
"""
|
|
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.
|
|
|
|
msgobj - a Msg instance. May be a message string.
|
|
from_obj - if msgobj is not an Msg-instance, this is used to create
|
|
a message on the fly. The advantage of this is that such
|
|
messages are logged.
|
|
|
|
"""
|
|
if not type(msgobj) == Msg:
|
|
# the given msgobj is not an Msg instance. If it is a string and from_obj
|
|
# was given, we create the message on the fly instead.
|
|
if from_obj and isinstance(msgobj, basestring):
|
|
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
|
msgobj.save()
|
|
msgobj.channels = [self]
|
|
msg = msgobj.message
|
|
else:
|
|
# this just sends a message, without any sender
|
|
# (and without storing it in a persistent Msg object)
|
|
msg = str(msgobj)
|
|
else:
|
|
msg = msgobj.message
|
|
|
|
# get all players connected to this channel
|
|
conns = Channel.objects.get_all_connections(self)
|
|
|
|
# send message to all connected players
|
|
for conn in conns:
|
|
for session in \
|
|
sessionhandler.find_sessions_from_username(conn.player.name):
|
|
session.msg(msg)
|
|
return True
|
|
|
|
def connect_to(self, player):
|
|
"Connect the user to this channel"
|
|
if not has_perm(player, self, 'chan_listen'):
|
|
return False
|
|
conn = ChannelConnection.objects.create_connection(player, self)
|
|
if conn:
|
|
return True
|
|
return False
|
|
|
|
def disconnect_from(self, player):
|
|
"Disconnect user from this channel."
|
|
ChannelConnection.objects.break_connection(player, self)
|
|
|
|
def delete(self):
|
|
"Clean out all connections to this channel and delete it."
|
|
for connection in Channel.objects.get_all_connections(self):
|
|
connection.delete()
|
|
super(Channel, self).delete()
|
|
|
|
|
|
class ChannelConnection(SharedMemoryModel):
|
|
"""
|
|
This connects a user object to a particular comm channel.
|
|
The advantage of making it like this is that one can easily
|
|
break the connection just by deleting this object.
|
|
"""
|
|
# Player connected to a channel
|
|
db_player = models.ForeignKey(PlayerDB)
|
|
# Channel the player is connected to
|
|
db_channel = models.ForeignKey(Channel)
|
|
|
|
# Database manager
|
|
objects = managers.ChannelConnectionManager()
|
|
|
|
# player property (wraps db_player)
|
|
#@property
|
|
def player_get(self):
|
|
"Getter. Allows for value = self.player"
|
|
return self.db_player
|
|
#@player.setter
|
|
def player_set(self, value):
|
|
"Setter. Allows for self.player = value"
|
|
self.db_player = value
|
|
self.save()
|
|
#@player.deleter
|
|
def player_del(self):
|
|
"Deleter. Allows for del self.player. Deletes connection."
|
|
self.delete()
|
|
player = property(player_get, player_set, player_del)
|
|
|
|
# channel property (wraps db_channel)
|
|
#@property
|
|
def channel_get(self):
|
|
"Getter. Allows for value = self.channel"
|
|
return self.db_channel
|
|
#@channel.setter
|
|
def channel_set(self, value):
|
|
"Setter. Allows for self.channel = value"
|
|
self.db_channel = value
|
|
self.save()
|
|
#@channel.deleter
|
|
def channel_del(self):
|
|
"Deleter. Allows for del self.channel. Deletes connection."
|
|
self.delete()
|
|
channel = property(channel_get, channel_set, channel_del)
|
|
|
|
def __str__(self):
|
|
return "Connection Player '%s' <-> %s" % (self.player, self.channel)
|
|
|
|
class Meta:
|
|
"Define Django meta options"
|
|
verbose_name = "Channel<->Player link"
|
|
verbose_name_plural = "Channel<->Player links"
|
|
|