From 120ddb7f81b2906656672510eb8507e76bd02855 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 8 Jul 2017 16:23:16 +0200 Subject: [PATCH] Remove player app- and directory. --- evennia/players/__init__.py | 6 - evennia/players/admin.py | 255 ----- evennia/players/bots.py | 419 -------- evennia/players/manager.py | 180 ---- evennia/players/migrations/0001_initial.py | 49 - .../players/migrations/0002_move_defaults.py | 20 - .../migrations/0003_auto_20150209_2234.py | 36 - .../migrations/0004_auto_20150403_2339.py | 48 - .../migrations/0005_auto_20160905_0902.py | 21 - .../migrations/0006_auto_20170606_1731.py | 31 - evennia/players/migrations/__init__.py | 1 - evennia/players/models.py | 169 --- evennia/players/players.py | 971 ------------------ evennia/settings_default.py | 1 - 14 files changed, 2207 deletions(-) delete mode 100644 evennia/players/__init__.py delete mode 100644 evennia/players/admin.py delete mode 100644 evennia/players/bots.py delete mode 100644 evennia/players/manager.py delete mode 100644 evennia/players/migrations/0001_initial.py delete mode 100644 evennia/players/migrations/0002_move_defaults.py delete mode 100644 evennia/players/migrations/0003_auto_20150209_2234.py delete mode 100644 evennia/players/migrations/0004_auto_20150403_2339.py delete mode 100644 evennia/players/migrations/0005_auto_20160905_0902.py delete mode 100644 evennia/players/migrations/0006_auto_20170606_1731.py delete mode 100644 evennia/players/migrations/__init__.py delete mode 100644 evennia/players/models.py delete mode 100644 evennia/players/players.py diff --git a/evennia/players/__init__.py b/evennia/players/__init__.py deleted file mode 100644 index aa4f2e4c4..000000000 --- a/evennia/players/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -This sub-package defines the out-of-character entities known as -Players. These are equivalent to 'accounts' and can puppet one or -more Objects depending on settings. A Player has no in-game existence. - -""" diff --git a/evennia/players/admin.py b/evennia/players/admin.py deleted file mode 100644 index 6e43e6dd7..000000000 --- a/evennia/players/admin.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# This sets up how models are displayed -# in the web admin interface. -# -from builtins import object - -from django import forms -from django.conf import settings -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from django.contrib.auth.forms import UserChangeForm, UserCreationForm -from evennia.players.models import PlayerDB -from evennia.typeclasses.admin import AttributeInline, TagInline -from evennia.utils import create - - -# handle the custom User editor -class PlayerDBChangeForm(UserChangeForm): - """ - Modify the playerdb class. - - """ - class Meta(object): - model = PlayerDB - fields = '__all__' - - username = forms.RegexField( - label="Username", - max_length=30, - regex=r'^[\w. @+-]+$', - widget=forms.TextInput( - attrs={'size': '30'}), - error_messages={ - 'invalid': "This value may contain only letters, spaces, numbers " - "and @/./+/-/_ characters."}, - help_text="30 characters or fewer. Letters, spaces, digits and " - "@/./+/-/_ only.") - - def clean_username(self): - """ - Clean the username and check its existence. - - """ - username = self.cleaned_data['username'] - if username.upper() == self.instance.username.upper(): - return username - elif PlayerDB.objects.filter(username__iexact=username): - raise forms.ValidationError('A player with that name ' - 'already exists.') - return self.cleaned_data['username'] - - -class PlayerDBCreationForm(UserCreationForm): - """ - Create a new PlayerDB instance. - """ - - class Meta(object): - model = PlayerDB - fields = '__all__' - - username = forms.RegexField( - label="Username", - max_length=30, - regex=r'^[\w. @+-]+$', - widget=forms.TextInput( - attrs={'size': '30'}), - error_messages={ - 'invalid': "This value may contain only letters, spaces, numbers " - "and @/./+/-/_ characters."}, - help_text="30 characters or fewer. Letters, spaces, digits and " - "@/./+/-/_ only.") - - def clean_username(self): - """ - Cleanup username. - """ - username = self.cleaned_data['username'] - if PlayerDB.objects.filter(username__iexact=username): - raise forms.ValidationError('A player with that name already ' - 'exists.') - return username - - -class PlayerForm(forms.ModelForm): - """ - Defines how to display Players - - """ - class Meta(object): - model = PlayerDB - fields = '__all__' - - db_key = forms.RegexField( - label="Username", - initial="PlayerDummy", - max_length=30, - regex=r'^[\w. @+-]+$', - required=False, - widget=forms.TextInput(attrs={'size': '30'}), - error_messages={ - 'invalid': "This value may contain only letters, spaces, numbers" - " and @/./+/-/_ characters."}, - help_text="This should be the same as the connected Player's key " - "name. 30 characters or fewer. Letters, spaces, digits and " - "@/./+/-/_ only.") - - db_typeclass_path = forms.CharField( - label="Typeclass", - initial=settings.BASE_PLAYER_TYPECLASS, - widget=forms.TextInput( - attrs={'size': '78'}), - help_text="Required. Defines what 'type' of entity this is. This " - "variable holds a Python path to a module with a valid " - "Evennia Typeclass. Defaults to " - "settings.BASE_PLAYER_TYPECLASS.") - - db_permissions = forms.CharField( - label="Permissions", - initial=settings.PERMISSION_PLAYER_DEFAULT, - required=False, - widget=forms.TextInput( - attrs={'size': '78'}), - help_text="In-game permissions. A comma-separated list of text " - "strings checked by certain locks. They are often used for " - "hierarchies, such as letting a Player have permission " - "'Admin', 'Builder' etc. A Player permission can be " - "overloaded by the permissions of a controlled Character. " - "Normal players use 'Players' by default.") - - db_lock_storage = forms.CharField( - label="Locks", - widget=forms.Textarea(attrs={'cols': '100', 'rows': '2'}), - required=False, - help_text="In-game lock definition string. If not given, defaults " - "will be used. This string should be on the form " - "type:lockfunction(args);type2:lockfunction2(args);...") - db_cmdset_storage = forms.CharField( - label="cmdset", - initial=settings.CMDSET_PLAYER, - widget=forms.TextInput(attrs={'size': '78'}), - required=False, - help_text="python path to player cmdset class (set in " - "settings.CMDSET_PLAYER by default)") - - -class PlayerInline(admin.StackedInline): - """ - Inline creation of Player - - """ - model = PlayerDB - template = "admin/players/stacked.html" - form = PlayerForm - fieldsets = ( - ("In-game Permissions and Locks", - {'fields': ('db_lock_storage',), - #{'fields': ('db_permissions', 'db_lock_storage'), - 'description': "These are permissions/locks for in-game use. " - "They are unrelated to website access rights."}), - ("In-game Player data", - {'fields': ('db_typeclass_path', 'db_cmdset_storage'), - 'description': "These fields define in-game-specific properties " - "for the Player object in-game."})) - - extra = 1 - max_num = 1 - - -class PlayerTagInline(TagInline): - """ - Inline Player Tags. - - """ - model = PlayerDB.db_tags.through - related_field = "playerdb" - - -class PlayerAttributeInline(AttributeInline): - """ - Inline Player Attributes. - - """ - model = PlayerDB.db_attributes.through - related_field = "playerdb" - - -class PlayerDBAdmin(BaseUserAdmin): - """ - This is the main creation screen for Users/players - - """ - - list_display = ('username', 'email', 'is_staff', 'is_superuser') - form = PlayerDBChangeForm - add_form = PlayerDBCreationForm - inlines = [PlayerTagInline, PlayerAttributeInline] - fieldsets = ( - (None, {'fields': ('username', 'password', 'email')}), - ('Website profile', { - 'fields': ('first_name', 'last_name'), - 'description': "These are not used " - "in the default system."}), - ('Website dates', { - 'fields': ('last_login', 'date_joined'), - 'description': 'Relevant only to the website.'}), - ('Website Permissions', { - 'fields': ('is_active', 'is_staff', 'is_superuser', - 'user_permissions', 'groups'), - 'description': "These are permissions/permission groups for " - "accessing the admin site. They are unrelated to " - "in-game access rights."}), - ('Game Options', { - 'fields': ('db_typeclass_path', 'db_cmdset_storage', - 'db_lock_storage'), - 'description': 'These are attributes that are more relevant ' - 'to gameplay.'})) - # ('Game Options', {'fields': ( - # 'db_typeclass_path', 'db_cmdset_storage', - # 'db_permissions', 'db_lock_storage'), - # 'description': 'These are attributes that are ' - # 'more relevant to gameplay.'})) - - add_fieldsets = ( - (None, - {'fields': ('username', 'password1', 'password2', 'email'), - 'description': "These account details are shared by the admin " - "system and the game."},),) - - def save_model(self, request, obj, form, change): - """ - Custom save actions. - - Args: - request (Request): Incoming request. - obj (Object): Object to save. - form (Form): Related form instance. - change (bool): False if this is a new save and not an update. - - """ - obj.save() - if not change: - #calling hooks for new player - obj.set_class_from_typeclass(typeclass_path=settings.BASE_PLAYER_TYPECLASS) - obj.basetype_setup() - obj.at_player_creation() - - def response_add(self, request, obj, post_url_continue=None): - from django.http import HttpResponseRedirect - from django.core.urlresolvers import reverse - if '_continue' in request.POST: - return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id])) - return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id])) - -admin.site.register(PlayerDB, PlayerDBAdmin) diff --git a/evennia/players/bots.py b/evennia/players/bots.py deleted file mode 100644 index ddf012cc1..000000000 --- a/evennia/players/bots.py +++ /dev/null @@ -1,419 +0,0 @@ -""" -Bots are a special child typeclasses of -Player that are controlled by the server. - -""" -from __future__ import print_function -import time -from django.conf import settings -from evennia.players.players import DefaultPlayer -from evennia.scripts.scripts import DefaultScript -from evennia.utils import search -from evennia.utils import utils - -_IDLE_TIMEOUT = settings.IDLE_TIMEOUT - -_IRC_ENABLED = settings.IRC_ENABLED -_RSS_ENABLED = settings.RSS_ENABLED - -_SESSIONS = None - - -# Bot helper utilities - -class BotStarter(DefaultScript): - """ - This non-repeating script has the - sole purpose of kicking its bot - into gear when it is initialized. - - """ - def at_script_creation(self): - """ - Called once, when script is created. - - """ - self.key = "botstarter" - self.desc = "bot start/keepalive" - self.persistent = True - self.db.started = False - if _IDLE_TIMEOUT > 0: - # call before idle_timeout triggers - self.interval = int(max(60, _IDLE_TIMEOUT * 0.90)) - self.start_delay = True - - def at_start(self): - """ - Kick bot into gear. - - """ - if not self.db.started: - self.player.start() - self.db.started = True - - def at_repeat(self): - """ - Called self.interval seconds to keep connection. We cannot use - the IDLE command from inside the game since the system will - not catch it (commands executed from the server side usually - has no sessions). So we update the idle counter manually here - instead. This keeps the bot getting hit by IDLE_TIMEOUT. - - """ - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - for session in _SESSIONS.sessions_from_player(self.player): - session.update_session_counters(idle=True) - - def at_server_reload(self): - """ - If server reloads we don't need to reconnect the protocol - again, this is handled by the portal reconnect mechanism. - - """ - self.db.started = True - - def at_server_shutdown(self): - """ - Make sure we are shutdown. - - """ - self.db.started = False - -# -# Bot base class - - -class Bot(DefaultPlayer): - """ - A Bot will start itself when the server starts (it will generally - not do so on a reload - that will be handled by the normal Portal - session resync) - - """ - - def basetype_setup(self): - """ - This sets up the basic properties for the bot. - - """ - # the text encoding to use. - self.db.encoding = "utf-8" - # A basic security setup - lockstring = "examine:perm(Admin);edit:perm(Admin);delete:perm(Admin);boot:perm(Admin);msg:false()" - self.locks.add(lockstring) - # set the basics of being a bot - script_key = "%s" % self.key - self.scripts.add(BotStarter, key=script_key) - self.is_bot = True - - def start(self, **kwargs): - """ - This starts the bot, whatever that may mean. - - """ - pass - - def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs): - """ - Evennia -> outgoing protocol - - """ - super(Bot, self).msg(text=text, from_obj=from_obj, session=session, options=options, **kwargs) - - def execute_cmd(self, raw_string, session=None): - """ - Incoming protocol -> Evennia - - """ - super(Bot, self).msg(raw_string, session=session) - - def at_server_shutdown(self): - """ - We need to handle this case manually since the shutdown may be - a reset. - - """ - for session in self.sessions.all(): - session.sessionhandler.disconnect(session) - - -# Bot implementations - -# IRC - -class IRCBot(Bot): - """ - Bot for handling IRC connections. - - """ - def start(self, ev_channel=None, irc_botname=None, irc_channel=None, irc_network=None, irc_port=None, irc_ssl=None): - """ - Start by telling the portal to start a new session. - - Args: - ev_channel (str): Key of the Evennia channel to connect to. - irc_botname (str): Name of bot to connect to irc channel. If - not set, use `self.key`. - irc_channel (str): Name of channel on the form `#channelname`. - irc_network (str): URL of the IRC network, like `irc.freenode.net`. - irc_port (str): Port number of the irc network, like `6667`. - irc_ssl (bool): Indicates whether to use SSL connection. - - """ - if not _IRC_ENABLED: - # the bot was created, then IRC was turned off. We delete - # ourselves (this will also kill the start script) - self.delete() - return - - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - - # if keywords are given, store (the BotStarter script - # will not give any keywords, so this should normally only - # happen at initialization) - if irc_botname: - self.db.irc_botname = irc_botname - elif not self.db.irc_botname: - self.db.irc_botname = self.key - if ev_channel: - # connect to Evennia channel - channel = search.channel_search(ev_channel) - if not channel: - raise RuntimeError("Evennia Channel '%s' not found." % ev_channel) - channel = channel[0] - channel.connect(self) - self.db.ev_channel = channel - if irc_channel: - self.db.irc_channel = irc_channel - if irc_network: - self.db.irc_network = irc_network - if irc_port: - self.db.irc_port = irc_port - if irc_ssl: - self.db.irc_ssl = irc_ssl - - # instruct the server and portal to create a new session with - # the stored configuration - configdict = {"uid": self.dbid, - "botname": self.db.irc_botname, - "channel": self.db.irc_channel, - "network": self.db.irc_network, - "port": self.db.irc_port, - "ssl": self.db.irc_ssl} - _SESSIONS.start_bot_session("evennia.server.portal.irc.IRCBotFactory", configdict) - - def get_nicklist(self, caller): - """ - Retrive the nick list from the connected channel. - - Args: - caller (Object or Player): The requester of the list. This will - be stored and echoed to when the irc network replies with the - requested info. - - Notes: Since the return is asynchronous, the caller is stored internally - in a list; all callers in this list will get the nick info once it - returns (it is a custom OOB inputfunc option). The callback will not - survive a reload (which should be fine, it's very quick). - """ - if not hasattr(self, "_nicklist_callers"): - self._nicklist_callers = [] - self._nicklist_callers.append(caller) - super(IRCBot, self).msg(request_nicklist="") - return - - def ping(self, caller): - """ - Fire a ping to the IRC server. - - Args: - caller (Object or Player): The requester of the ping. - - """ - if not hasattr(self, "_ping_callers"): - self._ping_callers = [] - self._ping_callers.append(caller) - super(IRCBot, self).msg(ping="") - - def reconnect(self): - """ - Force a protocol-side reconnect of the client without - having to destroy/recreate the bot "player". - - """ - super(IRCBot, self).msg(reconnect="") - - def msg(self, text=None, **kwargs): - """ - Takes text from connected channel (only). - - Args: - text (str, optional): Incoming text from channel. - - Kwargs: - options (dict): Options dict with the following allowed keys: - - from_channel (str): dbid of a channel this text originated from. - - from_obj (list): list of objects this text. - - """ - from_obj = kwargs.get("from_obj", None) - options = kwargs.get("options", None) or {} - if not self.ndb.ev_channel and self.db.ev_channel: - # cache channel lookup - self.ndb.ev_channel = self.db.ev_channel - if "from_channel" in options and text and self.ndb.ev_channel.dbid == options["from_channel"]: - if not from_obj or from_obj != [self.id]: - super(IRCBot, self).msg(channel=text) - - def execute_cmd(self, session=None, txt=None, **kwargs): - """ - Take incoming data and send it to connected channel. This is - triggered by the bot_data_in Inputfunc. - - Args: - session (Session, optional): Session responsible for this - command. Note that this is the bot. - txt (str, optional): Command string. - Kwargs: - user (str): The name of the user who sent the message. - channel (str): The name of channel the message was sent to. - type (str): Nature of message. Either 'msg', 'action', 'nicklist' or 'ping'. - nicklist (list, optional): Set if `type='nicklist'`. This is a list of nicks returned by calling - the `self.get_nicklist`. It must look for a list `self._nicklist_callers` - which will contain all callers waiting for the nicklist. - timings (float, optional): Set if `type='ping'`. This is the return (in seconds) of a - ping request triggered with `self.ping`. The return must look for a list - `self._ping_callers` which will contain all callers waiting for the ping return. - - """ - if kwargs["type"] == "nicklist": - # the return of a nicklist request - if hasattr(self, "_nicklist_callers") and self._nicklist_callers: - chstr = "%s (%s:%s)" % (self.db.irc_channel, self.db.irc_network, self.db.irc_port) - nicklist = ", ".join(sorted(kwargs["nicklist"], key=lambda n: n.lower())) - for obj in self._nicklist_callers: - obj.msg("Nicks at %s:\n %s" % (chstr, nicklist)) - self._nicklist_callers = [] - return - - elif kwargs["type"] == "ping": - # the return of a ping - if hasattr(self, "_ping_callers") and self._ping_callers: - chstr = "%s (%s:%s)" % (self.db.irc_channel, self.db.irc_network, self.db.irc_port) - for obj in self._ping_callers: - obj.msg("IRC ping return from %s took %ss." % (chstr, kwargs["timing"])) - self._ping_callers = [] - return - - elif kwargs["type"] == "privmsg": - # A private message to the bot - a command. - user = kwargs["user"] - - if txt.lower().startswith("who"): - # return server WHO list (abbreviated for IRC) - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - whos = [] - t0 = time.time() - for sess in _SESSIONS.get_sessions(): - delta_cmd = t0 - sess.cmd_last_visible - delta_conn = t0 - session.conn_time - player = sess.get_player() - whos.append("%s (%s/%s)" % (utils.crop("|w%s|n" % player.name, width=25), - utils.time_format(delta_conn, 0), - utils.time_format(delta_cmd, 1))) - text = "Who list (online/idle): %s" % ", ".join(sorted(whos, key=lambda w: w.lower())) - elif txt.lower().startswith("about"): - # some bot info - text = "This is an Evennia IRC bot connecting from '%s'." % settings.SERVERNAME - else: - text = "I understand 'who' and 'about'." - super(IRCBot, self).msg(privmsg=((text,), {"user": user})) - else: - # something to send to the main channel - if kwargs["type"] == "action": - # An action (irc pose) - text = "%s@%s %s" % (kwargs["user"], kwargs["channel"], txt) - else: - # msg - A normal channel message - text = "%s@%s: %s" % (kwargs["user"], kwargs["channel"], txt) - - if not self.ndb.ev_channel and self.db.ev_channel: - # cache channel lookup - self.ndb.ev_channel = self.db.ev_channel - if self.ndb.ev_channel: - self.ndb.ev_channel.msg(text, senders=self.id) - -# -# RSS - - -class RSSBot(Bot): - """ - An RSS relayer. The RSS protocol itself runs a ticker to update - its feed at regular intervals. - - """ - def start(self, ev_channel=None, rss_url=None, rss_rate=None): - """ - Start by telling the portal to start a new RSS session - - Args: - ev_channel (str): Key of the Evennia channel to connect to. - rss_url (str): Full URL to the RSS feed to subscribe to. - rss_rate (int): How often for the feedreader to update. - - Raises: - RuntimeError: If `ev_channel` does not exist. - - """ - if not _RSS_ENABLED: - # The bot was created, then RSS was turned off. Delete ourselves. - self.delete() - return - - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - - if ev_channel: - # connect to Evennia channel - channel = search.channel_search(ev_channel) - if not channel: - raise RuntimeError("Evennia Channel '%s' not found." % ev_channel) - channel = channel[0] - self.db.ev_channel = channel - if rss_url: - self.db.rss_url = rss_url - if rss_rate: - self.db.rss_rate = rss_rate - # instruct the server and portal to create a new session with - # the stored configuration - configdict = {"uid": self.dbid, - "url": self.db.rss_url, - "rate": self.db.rss_rate} - _SESSIONS.start_bot_session("evennia.server.portal.rss.RSSBotFactory", configdict) - - def execute_cmd(self, txt=None, session=None, **kwargs): - """ - Take incoming data and send it to connected channel. This is - triggered by the bot_data_in Inputfunc. - - Args: - session (Session, optional): Session responsible for this - command. - txt (str, optional): Command string. - kwargs (dict, optional): Additional Information passed from bot. - Not used by the RSSbot by default. - - """ - if not self.ndb.ev_channel and self.db.ev_channel: - # cache channel lookup - self.ndb.ev_channel = self.db.ev_channel - if self.ndb.ev_channel: - self.ndb.ev_channel.msg(txt, senders=self.id) diff --git a/evennia/players/manager.py b/evennia/players/manager.py deleted file mode 100644 index 2c5cb48b7..000000000 --- a/evennia/players/manager.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -The managers for the custom Player object and permissions. -""" - -import datetime -from django.utils import timezone -from django.contrib.auth.models import UserManager -from evennia.typeclasses.managers import (TypedObjectManager, TypeclassManager) -__all__ = ("PlayerManager",) - - -# -# Player Manager -# - -class PlayerDBManager(TypedObjectManager, UserManager): - """ - This PlayerManager implements methods for searching - and manipulating Players directly from the database. - - Evennia-specific search methods (will return Characters if - possible or a Typeclass/list of Typeclassed objects, whereas - Django-general methods will return Querysets or database objects): - - dbref (converter) - dbref_search - get_dbref_range - object_totals - typeclass_search - num_total_players - get_connected_players - get_recently_created_players - get_recently_connected_players - get_player_from_email - get_player_from_uid - get_player_from_name - player_search (equivalent to evennia.search_player) - #swap_character - - """ - def num_total_players(self): - """ - Get total number of players. - - Returns: - count (int): The total number of registered players. - - """ - return self.count() - - def get_connected_players(self): - """ - Get all currently connected players. - - Returns: - count (list): Player objects with currently - connected sessions. - - """ - return self.filter(db_is_connected=True) - - def get_recently_created_players(self, days=7): - """ - Get players recently created. - - Args: - days (int, optional): How many days in the past "recently" means. - - Returns: - players (list): The Players created the last `days` interval. - - """ - end_date = timezone.now() - tdelta = datetime.timedelta(days) - start_date = end_date - tdelta - return self.filter(date_joined__range=(start_date, end_date)) - - def get_recently_connected_players(self, days=7): - """ - Get players recently connected to the game. - - Args: - days (int, optional): Number of days backwards to check - - Returns: - players (list): The Players connected to the game in the - last `days` interval. - - """ - end_date = timezone.now() - tdelta = datetime.timedelta(days) - start_date = end_date - tdelta - return self.filter(last_login__range=( - start_date, end_date)).order_by('-last_login') - - def get_player_from_email(self, uemail): - """ - Search player by - Returns a player object based on email address. - - Args: - uemail (str): An email address to search for. - - Returns: - player (Player): A found player, if found. - - """ - return self.filter(email__iexact=uemail) - - def get_player_from_uid(self, uid): - """ - Get a player by id. - - Args: - uid (int): Player database id. - - Returns: - player (Player): The result. - - """ - try: - return self.get(id=uid) - except self.model.DoesNotExist: - return None - - def get_player_from_name(self, uname): - """ - Get player object based on name. - - Args: - uname (str): The Player name to search for. - - Returns: - player (Player): The found player. - - """ - try: - return self.get(username__iexact=uname) - except self.model.DoesNotExist: - return None - - def search_player(self, ostring, exact=True, typeclass=None): - """ - Searches for a particular player by name or - database id. - - Args: - ostring (str or int): A key string or database id. - exact (bool, optional): Only valid for string matches. If - `True`, requires exact (non-case-sensitive) match, - otherwise also match also keys containing the `ostring` - (non-case-sensitive fuzzy match). - typeclass (str or Typeclass, optional): Limit the search only to - players of this typeclass. - - """ - dbref = self.dbref(ostring) - if dbref or dbref == 0: - # bref search is always exact - matches = self.filter(id=dbref) - if matches: - return matches - query = {"username__iexact" if exact else "username__icontains": ostring} - if typeclass: - # we accept both strings and actual typeclasses - if callable(typeclass): - typeclass = u"%s.%s" % (typeclass.__module__, typeclass.__name__) - else: - typeclass = u"%s" % typeclass - query["db_typeclass_path"] = typeclass - if exact: - return self.filter(**query) - else: - return self.filter(**query) - # back-compatibility alias - player_search = search_player - - -class PlayerManager(PlayerDBManager, TypeclassManager): - pass diff --git a/evennia/players/migrations/0001_initial.py b/evennia/players/migrations/0001_initial.py deleted file mode 100644 index de2b93f5b..000000000 --- a/evennia/players/migrations/0001_initial.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import django.utils.timezone -import django.core.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('auth', '0001_initial'), - ('typeclasses', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='PlayerDB', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=30, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')])), - ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), - ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), - ('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('db_key', models.CharField(max_length=255, verbose_name=b'key', db_index=True)), - ('db_typeclass_path', models.CharField(help_text=b"this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.", max_length=255, null=True, verbose_name=b'typeclass')), - ('db_date_created', models.DateTimeField(auto_now_add=True, verbose_name=b'creation date')), - ('db_lock_storage', models.TextField(help_text=b"locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.", verbose_name=b'locks', blank=True)), - ('db_is_connected', models.BooleanField(default=False, help_text=b'If player is connected to game or not', verbose_name=b'is_connected')), - ('db_cmdset_storage', models.CharField(help_text=b'optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.', max_length=255, null=True, verbose_name=b'cmdset')), - ('db_is_bot', models.BooleanField(default=False, help_text=b'Used to identify irc/rss bots', verbose_name=b'is_bot')), - ('db_attributes', models.ManyToManyField(help_text=b'attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute', null=True)), - ('db_tags', models.ManyToManyField(help_text=b'tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag', null=True)), - ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')), - ], - options={ - 'verbose_name': 'Player', - 'verbose_name_plural': 'Players', - }, - bases=(models.Model,), - ), - ] diff --git a/evennia/players/migrations/0002_move_defaults.py b/evennia/players/migrations/0002_move_defaults.py deleted file mode 100644 index cb16bfd5e..000000000 --- a/evennia/players/migrations/0002_move_defaults.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - -def convert_defaults(apps, schema_editor): - PlayerDB = apps.get_model("players", "PlayerDB") - for player in PlayerDB.objects.filter(db_typeclass_path="src.players.player.Player"): - player.db_typeclass_path = "typeclasses.players.Player" - player.save() - -class Migration(migrations.Migration): - - dependencies = [ - ('players', '0001_initial'), - ] - - operations = [ - migrations.RunPython(convert_defaults), - ] diff --git a/evennia/players/migrations/0003_auto_20150209_2234.py b/evennia/players/migrations/0003_auto_20150209_2234.py deleted file mode 100644 index 71d588bbc..000000000 --- a/evennia/players/migrations/0003_auto_20150209_2234.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('players', '0002_move_defaults'), - ] - - operations = [ - migrations.CreateModel( - name='DefaultPlayer', - fields=[ - ], - options={ - 'proxy': True, - }, - bases=('players.playerdb',), - ), - migrations.CreateModel( - name='DefaultGuest', - fields=[ - ], - options={ - 'proxy': True, - }, - bases=('players.defaultplayer',), - ), - migrations.AlterModelOptions( - name='playerdb', - options={'verbose_name': 'Player'}, - ), - ] diff --git a/evennia/players/migrations/0004_auto_20150403_2339.py b/evennia/players/migrations/0004_auto_20150403_2339.py deleted file mode 100644 index f09faba2b..000000000 --- a/evennia/players/migrations/0004_auto_20150403_2339.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import evennia.players.manager -import django.core.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('players', '0003_auto_20150209_2234'), - ] - - operations = [ - migrations.DeleteModel( - name='DefaultGuest', - ), - migrations.DeleteModel( - name='DefaultPlayer', - ), - migrations.AlterModelManagers( - name='playerdb', - managers=[ - (b'objects', evennia.players.manager.PlayerDBManager()), - ], - ), - migrations.AlterField( - model_name='playerdb', - name='email', - field=models.EmailField(max_length=254, verbose_name='email address', blank=True), - ), - migrations.AlterField( - model_name='playerdb', - name='groups', - field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'), - ), - migrations.AlterField( - model_name='playerdb', - name='last_login', - field=models.DateTimeField(null=True, verbose_name='last login', blank=True), - ), - migrations.AlterField( - model_name='playerdb', - name='username', - field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username'), - ), - ] diff --git a/evennia/players/migrations/0005_auto_20160905_0902.py b/evennia/players/migrations/0005_auto_20160905_0902.py deleted file mode 100644 index e62ab4cbb..000000000 --- a/evennia/players/migrations/0005_auto_20160905_0902.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.9 on 2016-09-05 09:02 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('players', '0004_auto_20150403_2339'), - ] - - operations = [ - migrations.AlterField( - model_name='playerdb', - name='username', - field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'), - ), - ] diff --git a/evennia/players/migrations/0006_auto_20170606_1731.py b/evennia/players/migrations/0006_auto_20170606_1731.py deleted file mode 100644 index bff35ca15..000000000 --- a/evennia/players/migrations/0006_auto_20170606_1731.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.2 on 2017-06-06 17:31 -from __future__ import unicode_literals - -import django.contrib.auth.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('players', '0005_auto_20160905_0902'), - ] - - operations = [ - migrations.AlterField( - model_name='playerdb', - name='db_attributes', - field=models.ManyToManyField(help_text=b'attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute'), - ), - migrations.AlterField( - model_name='playerdb', - name='db_tags', - field=models.ManyToManyField(help_text=b'tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag'), - ), - migrations.AlterField( - model_name='playerdb', - name='username', - field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator()], verbose_name='username'), - ), - ] diff --git a/evennia/players/migrations/__init__.py b/evennia/players/migrations/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/evennia/players/migrations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/evennia/players/models.py b/evennia/players/models.py deleted file mode 100644 index d33985de3..000000000 --- a/evennia/players/models.py +++ /dev/null @@ -1,169 +0,0 @@ -""" -Player - -The player class is an extension of the default Django user class, -and is customized for the needs of Evennia. - -We use the Player to store a more mud-friendly style of permission -system as well as to allow the admin more flexibility by storing -attributes on the Player. Within the game we should normally use the -Player manager's methods to create users so that permissions are set -correctly. - -To make the Player model more flexible for your own game, it can also -persistently store attributes of its own. This is ideal for extra -account info and OOC account configuration variables etc. - -""" -from builtins import object - -from django.conf import settings -from django.db import models -from django.contrib.auth.models import AbstractUser -from django.utils.encoding import smart_str - -from evennia.players.manager import PlayerDBManager -from evennia.typeclasses.models import TypedObject -from evennia.utils.utils import make_iter - -__all__ = ("PlayerDB",) - -#_ME = _("me") -#_SELF = _("self") - -_MULTISESSION_MODE = settings.MULTISESSION_MODE - -_GA = object.__getattribute__ -_SA = object.__setattr__ -_DA = object.__delattr__ - -_TYPECLASS = None - - -#------------------------------------------------------------ -# -# PlayerDB -# -#------------------------------------------------------------ - -class PlayerDB(TypedObject, AbstractUser): - """ - This is a special model using Django's 'profile' functionality - and extends the default Django User model. It is defined as such - by use of the variable AUTH_PROFILE_MODULE in the settings. - One accesses the fields/methods. We try use this model as much - as possible rather than User, since we can customize this to - our liking. - - The TypedObject supplies the following (inherited) properties: - - - key - main name - - typeclass_path - the path to the decorating typeclass - - typeclass - auto-linked typeclass - - date_created - time stamp of object creation - - permissions - perm strings - - dbref - #id of object - - db - persistent attribute storage - - ndb - non-persistent attribute storage - - The PlayerDB adds the following properties: - - - is_connected - If any Session is currently connected to this Player - - name - alias for user.username - - sessions - sessions connected to this player - - is_superuser - bool if this player is a superuser - - is_bot - bool if this player is a bot and not a real player - - """ - - # - # PlayerDB Database model setup - # - # inherited fields (from TypedObject): - # db_key, db_typeclass_path, db_date_created, db_permissions - - # store a connected flag here too, not just in sessionhandler. - # This makes it easier to track from various out-of-process locations - db_is_connected = models.BooleanField(default=False, - verbose_name="is_connected", - help_text="If player is connected to game or not") - # database storage of persistant cmdsets. - db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, - help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.") - # marks if this is a "virtual" bot player object - db_is_bot = models.BooleanField(default=False, verbose_name="is_bot", help_text="Used to identify irc/rss bots") - - # Database manager - objects = PlayerDBManager() - - # defaults - __settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS - __defaultclasspath__ = "evennia.players.players.DefaultPlayer" - __applabel__ = "players" - - class Meta(object): - verbose_name = 'Player' - - # cmdset_storage property - # This seems very sensitive to caching, so leaving it be for now /Griatch - #@property - def __cmdset_storage_get(self): - """ - Getter. Allows for value = self.name. Returns a list of cmdset_storage. - """ - storage = self.db_cmdset_storage - # we need to check so storage is not None - return [path.strip() for path in storage.split(',')] if storage else [] - - #@cmdset_storage.setter - def __cmdset_storage_set(self, value): - """ - Setter. Allows for self.name = value. Stores as a comma-separated - string. - """ - _SA(self, "db_cmdset_storage", ",".join(str(val).strip() for val in make_iter(value))) - _GA(self, "save")() - - #@cmdset_storage.deleter - def __cmdset_storage_del(self): - "Deleter. Allows for del self.name" - _SA(self, "db_cmdset_storage", None) - _GA(self, "save")() - cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del) - - # - # property/field access - # - - def __str__(self): - return smart_str("%s(player %s)" % (self.name, self.dbid)) - - def __unicode__(self): - return u"%s(player#%s)" % (self.name, self.dbid) - - #@property - def __username_get(self): - return self.username - - def __username_set(self, value): - self.username = value - self.save(update_fields=["username"]) - - def __username_del(self): - del self.username - - # aliases - name = property(__username_get, __username_set, __username_del) - key = property(__username_get, __username_set, __username_del) - - #@property - def __uid_get(self): - "Getter. Retrieves the user id" - return self.id - - def __uid_set(self, value): - raise Exception("User id cannot be set!") - - def __uid_del(self): - raise Exception("User id cannot be deleted!") - uid = property(__uid_get, __uid_set, __uid_del) diff --git a/evennia/players/players.py b/evennia/players/players.py deleted file mode 100644 index 2c67e360f..000000000 --- a/evennia/players/players.py +++ /dev/null @@ -1,971 +0,0 @@ -""" -Typeclass for Player objects - -Note that this object is primarily intended to -store OOC information, not game info! This -object represents the actual user (not their -character) and has NO actual precence in the -game world (this is handled by the associated -character object, so you should customize that -instead for most things). - -""" - -import time -from django.conf import settings -from django.utils import timezone -from evennia.typeclasses.models import TypeclassBase -from evennia.players.manager import PlayerManager -from evennia.players.models import PlayerDB -from evennia.objects.models import ObjectDB -from evennia.comms.models import ChannelDB -from evennia.commands import cmdhandler -from evennia.utils import logger -from evennia.utils.utils import (lazy_property, - make_iter, to_unicode, is_iter, - variable_from_module) -from evennia.typeclasses.attributes import NickHandler -from evennia.scripts.scripthandler import ScriptHandler -from evennia.commands.cmdsethandler import CmdSetHandler - -from django.utils.translation import ugettext as _ -from future.utils import with_metaclass - -__all__ = ("DefaultPlayer",) - -_SESSIONS = None - -_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) -_MULTISESSION_MODE = settings.MULTISESSION_MODE -_MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS -_CMDSET_PLAYER = settings.CMDSET_PLAYER -_CONNECT_CHANNEL = None - - -class PlayerSessionHandler(object): - """ - Manages the session(s) attached to a player. - - """ - - def __init__(self, player): - """ - Initializes the handler. - - Args: - player (Player): The Player on which this handler is defined. - - """ - self.player = player - - def get(self, sessid=None): - """ - Get the sessions linked to this object. - - Args: - sessid (int, optional): Specify a given session by - session id. - - Returns: - sessions (list): A list of Session objects. If `sessid` - is given, this is a list with one (or zero) elements. - - """ - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - if sessid: - return make_iter(_SESSIONS.session_from_player(self.player, sessid)) - else: - return _SESSIONS.sessions_from_player(self.player) - - def all(self): - """ - Alias to get(), returning all sessions. - - Returns: - sessions (list): All sessions. - - """ - return self.get() - - def count(self): - """ - Get amount of sessions connected. - - Returns: - sesslen (int): Number of sessions handled. - - """ - return len(self.get()) - - -class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)): - """ - This is the base Typeclass for all Players. Players represent - the person playing the game and tracks account info, password - etc. They are OOC entities without presence in-game. A Player - can connect to a Character Object in order to "enter" the - game. - - Player Typeclass API: - - * Available properties (only available on initiated typeclass objects) - - - key (string) - name of player - - name (string)- wrapper for user.username - - aliases (list of strings) - aliases to the object. Will be saved to - database as AliasDB entries but returned as strings. - - dbref (int, read-only) - unique #id-number. Also "id" can be used. - - date_created (string) - time stamp of object creation - - permissions (list of strings) - list of permission strings - - user (User, read-only) - django User authorization object - - obj (Object) - game object controlled by player. 'character' can also - be used. - - sessions (list of Sessions) - sessions connected to this player - - is_superuser (bool, read-only) - if the connected user is a superuser - - * Handlers - - - locks - lock-handler: use locks.add() to add new lock strings - - db - attribute-handler: store/retrieve database attributes on this - self.db.myattr=val, val=self.db.myattr - - ndb - non-persistent attribute handler: same as db but does not - create a database entry when storing data - - scripts - script-handler. Add new scripts to object with scripts.add() - - cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object - - nicks - nick-handler. New nicks with nicks.add(). - - * Helper methods - - - msg(text=None, from_obj=None, session=None, options=None, **kwargs) - - execute_cmd(raw_string) - - search(ostring, global_search=False, attribute_name=None, - use_nicks=False, location=None, - ignore_errors=False, player=False) - - is_typeclass(typeclass, exact=False) - - swap_typeclass(new_typeclass, clean_attributes=False, no_default=True) - - access(accessing_obj, access_type='read', default=False, no_superuser_bypass=False) - - check_permstring(permstring) - - * Hook methods - - basetype_setup() - at_player_creation() - - > note that the following hooks are also found on Objects and are - usually handled on the character level: - - - at_init() - - at_access() - - at_cmdset_get(**kwargs) - - at_first_login() - - at_post_login(session=None) - - at_disconnect() - - at_message_receive() - - at_message_send() - - at_server_reload() - - at_server_shutdown() - - """ - - objects = PlayerManager() - - # properties - @lazy_property - def cmdset(self): - return CmdSetHandler(self, True) - - @lazy_property - def scripts(self): - return ScriptHandler(self) - - @lazy_property - def nicks(self): - return NickHandler(self) - - @lazy_property - def sessions(self): - return PlayerSessionHandler(self) - - # session-related methods - - def disconnect_session_from_player(self, session): - """ - Access method for disconnecting a given session from the - player (connection happens automatically in the - sessionhandler) - - Args: - session (Session): Session to disconnect. - - """ - global _SESSIONS - if not _SESSIONS: - from evennia.server.sessionhandler import SESSIONS as _SESSIONS - _SESSIONS.disconnect(session) - - # puppeting operations - - def puppet_object(self, session, obj): - """ - Use the given session to control (puppet) the given object (usually - a Character type). - - Args: - session (Session): session to use for puppeting - obj (Object): the object to start puppeting - - Raises: - RuntimeError: If puppeting is not possible, the - `exception.msg` will contain the reason. - - - """ - # safety checks - if not obj: - raise RuntimeError("Object not found") - if not session: - raise RuntimeError("Session not found") - if self.get_puppet(session) == obj: - # already puppeting this object - self.msg("You are already puppeting this object.") - return - if not obj.access(self, 'puppet'): - # no access - self.msg("You don't have permission to puppet '%s'." % obj.key) - return - if obj.player: - # object already puppeted - if obj.player == self: - if obj.sessions.count(): - # we may take over another of our sessions - # output messages to the affected sessions - if _MULTISESSION_MODE in (1, 3): - txt1 = "Sharing |c%s|n with another of your sessions." - txt2 = "|c%s|n|G is now shared from another of your sessions.|n" - self.msg(txt1 % obj.name, session=session) - self.msg(txt2 % obj.name, session=obj.sessions.all()) - else: - txt1 = "Taking over |c%s|n from another of your sessions." - txt2 = "|c%s|n|R is now acted from another of your sessions.|n" - self.msg(txt1 % obj.name, session=session) - self.msg(txt2 % obj.name, session=obj.sessions.all()) - self.unpuppet_object(obj.sessions.get()) - elif obj.player.is_connected: - # controlled by another player - self.msg("|c%s|R is already puppeted by another Player." % obj.key) - return - - # do the puppeting - if session.puppet: - # cleanly unpuppet eventual previous object puppeted by this session - self.unpuppet_object(session) - # if we get to this point the character is ready to puppet or it - # was left with a lingering player/session reference from an unclean - # server kill or similar - - obj.at_pre_puppet(self, session=session) - - # do the connection - obj.sessions.add(session) - obj.player = self - session.puid = obj.id - session.puppet = obj - # validate/start persistent scripts on object - obj.scripts.validate() - - # re-cache locks to make sure superuser bypass is updated - obj.locks.cache_lock_bypass(obj) - # final hook - obj.at_post_puppet() - - def unpuppet_object(self, session): - """ - Disengage control over an object. - - Args: - session (Session or list): The session or a list of - sessions to disengage from their puppets. - - Raises: - RuntimeError With message about error. - - """ - for session in make_iter(session): - obj = session.puppet - if obj: - # do the disconnect, but only if we are the last session to puppet - obj.at_pre_unpuppet() - obj.sessions.remove(session) - if not obj.sessions.count(): - del obj.player - obj.at_post_unpuppet(self, session=session) - # Just to be sure we're always clear. - session.puppet = None - session.puid = None - - def unpuppet_all(self): - """ - Disconnect all puppets. This is called by server before a - reset/shutdown. - """ - self.unpuppet_object(self.sessions.all()) - - def get_puppet(self, session): - """ - Get an object puppeted by this session through this player. This is - the main method for retrieving the puppeted object from the - player's end. - - Args: - session (Session): Find puppeted object based on this session - - Returns: - puppet (Object): The matching puppeted object, if any. - - """ - return session.puppet - - def get_all_puppets(self): - """ - Get all currently puppeted objects. - - Returns: - puppets (list): All puppeted objects currently controlled - by this Player. - - """ - return list(set(session.puppet for session in self.sessions.all() if session.puppet)) - - def __get_single_puppet(self): - """ - This is a legacy convenience link for use with `MULTISESSION_MODE`. - - Returns: - puppets (Object or list): Users of `MULTISESSION_MODE` 0 or 1 will - always get the first puppet back. Users of higher `MULTISESSION_MODE`s will - get a list of all puppeted objects. - - """ - puppets = self.get_all_puppets() - if _MULTISESSION_MODE in (0, 1): - return puppets and puppets[0] or None - return puppets - character = property(__get_single_puppet) - puppet = property(__get_single_puppet) - - # utility methods - - def delete(self, *args, **kwargs): - """ - Deletes the player permanently. - - Notes: - `*args` and `**kwargs` are passed on to the base delete - mechanism (these are usually not used). - - """ - for session in self.sessions.all(): - # unpuppeting all objects and disconnecting the user, if any - # sessions remain (should usually be handled from the - # deleting command) - try: - self.unpuppet_object(session) - except RuntimeError: - # no puppet to disconnect from - pass - session.sessionhandler.disconnect(session, reason=_("Player being deleted.")) - self.scripts.stop() - self.attributes.clear() - self.nicks.clear() - self.aliases.clear() - super(DefaultPlayer, self).delete(*args, **kwargs) - # methods inherited from database model - - def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs): - """ - Evennia -> User - This is the main route for sending data back to the user from the - server. - - Args: - text (str, optional): text data to send - from_obj (Object or Player, optional): Object sending. If given, - its at_msg_send() hook will be called. - session (Session or list, optional): Session object or a list of - Sessions to receive this send. If given, overrules the - default send behavior for the current - MULTISESSION_MODE. - options (list): Protocol-specific options. Passed on to the protocol. - Kwargs: - any (dict): All other keywords are passed on to the protocol. - - """ - if from_obj: - # call hook - try: - from_obj.at_msg_send(text=text, to_obj=self, **kwargs) - except Exception: - # this may not be assigned. - pass - try: - if not self.at_msg_receive(text=text, **kwargs): - # abort message to this player - return - except Exception: - # this may not be assigned. - pass - - kwargs["options"] = options - - # session relay - sessions = make_iter(session) if session else self.sessions.all() - for session in sessions: - session.data_out(text=text, **kwargs) - - def execute_cmd(self, raw_string, session=None, **kwargs): - """ - Do something as this player. This method is never called normally, - but only when the player object itself is supposed to execute the - command. It takes player nicks into account, but not nicks of - eventual puppets. - - Args: - raw_string (str): Raw command input coming from the command line. - session (Session, optional): The session to be responsible - for the command-send - - Kwargs: - kwargs (any): Other keyword arguments will be added to the - found command object instance as variables before it - executes. This is unused by default Evennia but may be - used to set flags and change operating paramaters for - commands at run-time. - - """ - raw_string = to_unicode(raw_string) - raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=False) - if not session and _MULTISESSION_MODE in (0, 1): - # for these modes we use the first/only session - sessions = self.sessions.get() - session = sessions[0] if sessions else None - - return cmdhandler.cmdhandler(self, raw_string, - callertype="player", session=session, **kwargs) - - def search(self, searchdata, return_puppet=False, search_object=False, - typeclass=None, nofound_string=None, multimatch_string=None, **kwargs): - """ - This is similar to `DefaultObject.search` but defaults to searching - for Players only. - - Args: - searchdata (str or int): Search criterion, the Player's - key or dbref to search for. - return_puppet (bool, optional): Instructs the method to - return matches as the object the Player controls rather - than the Player itself (or None) if nothing is puppeted). - search_object (bool, optional): Search for Objects instead of - Players. This is used by e.g. the @examine command when - wanting to examine Objects while OOC. - typeclass (Player typeclass, optional): Limit the search - only to this particular typeclass. This can be used to - limit to specific player typeclasses or to limit the search - to a particular Object typeclass if `search_object` is True. - nofound_string (str, optional): A one-time error message - to echo if `searchdata` leads to no matches. If not given, - will fall back to the default handler. - multimatch_string (str, optional): A one-time error - message to echo if `searchdata` leads to multiple matches. - If not given, will fall back to the default handler. - - Return: - match (Player, Object or None): A single Player or Object match. - Notes: - Extra keywords are ignored, but are allowed in call in - order to make API more consistent with - objects.objects.DefaultObject.search. - - """ - # handle me, self and *me, *self - if isinstance(searchdata, basestring): - # handle wrapping of common terms - if searchdata.lower() in ("me", "*me", "self", "*self",): - return self - if search_object: - matches = ObjectDB.objects.object_search(searchdata, typeclass=typeclass) - else: - matches = PlayerDB.objects.player_search(searchdata, typeclass=typeclass) - matches = _AT_SEARCH_RESULT(matches, self, query=searchdata, - nofound_string=nofound_string, - multimatch_string=multimatch_string) - if matches and return_puppet: - try: - return matches.puppet - except AttributeError: - return None - return matches - - def access(self, accessing_obj, access_type='read', default=False, no_superuser_bypass=False, **kwargs): - """ - Determines if another object has permission to access this - object in whatever way. - - Args: - accessing_obj (Object): Object trying to access this one. - access_type (str, optional): Type of access sought. - default (bool, optional): What to return if no lock of - access_type was found - no_superuser_bypass (bool, optional): Turn off superuser - lock bypassing. Be careful with this one. - - Kwargs: - kwargs (any): Passed to the at_access hook along with the result. - - Returns: - result (bool): Result of access check. - - """ - result = super(DefaultPlayer, self).access(accessing_obj, access_type=access_type, - default=default, no_superuser_bypass=no_superuser_bypass) - self.at_access(result, accessing_obj, access_type, **kwargs) - return result - - @property - def idle_time(self): - """ - Returns the idle time of the least idle session in seconds. If - no sessions are connected it returns nothing. - """ - idle = [session.cmd_last_visible for session in self.sessions.all()] - if idle: - return time.time() - float(max(idle)) - return None - - @property - def connection_time(self): - """ - Returns the maximum connection time of all connected sessions - in seconds. Returns nothing if there are no sessions. - """ - conn = [session.conn_time for session in self.sessions.all()] - if conn: - return time.time() - float(min(conn)) - return None - - # player hooks - - def basetype_setup(self): - """ - This sets up the basic properties for a player. Overload this - with at_player_creation rather than changing this method. - - """ - # A basic security setup - lockstring = "examine:perm(Admin);edit:perm(Admin);" \ - "delete:perm(Admin);boot:perm(Admin);msg:all()" - self.locks.add(lockstring) - - # The ooc player cmdset - self.cmdset.add_default(_CMDSET_PLAYER, permanent=True) - - def at_player_creation(self): - """ - This is called once, the very first time the player is created - (i.e. first time they register with the game). It's a good - place to store attributes all players should have, like - configuration values etc. - - """ - # set an (empty) attribute holding the characters this player has - lockstring = "attrread:perm(Admins);attredit:perm(Admins);" \ - "attrcreate:perm(Admins)" - self.attributes.add("_playable_characters", [], lockstring=lockstring) - self.attributes.add("_saved_protocol_flags", {}, lockstring=lockstring) - - def at_init(self): - """ - This is always called whenever this object is initiated -- - that is, whenever it its typeclass is cached from memory. This - happens on-demand first time the object is used or activated - in some way after being created but also after each server - restart or reload. In the case of player objects, this usually - happens the moment the player logs in or reconnects after a - reload. - - """ - pass - - # Note that the hooks below also exist in the character object's - # typeclass. You can often ignore these and rely on the character - # ones instead, unless you are implementing a multi-character game - # and have some things that should be done regardless of which - # character is currently connected to this player. - - def at_first_save(self): - """ - This is a generic hook called by Evennia when this object is - saved to the database the very first time. You generally - don't override this method but the hooks called by it. - - """ - self.basetype_setup() - self.at_player_creation() - - permissions = settings.PERMISSION_PLAYER_DEFAULT - if hasattr(self, "_createdict"): - # this will only be set if the utils.create_player - # function was used to create the object. - cdict = self._createdict - if cdict.get("locks"): - self.locks.add(cdict["locks"]) - if cdict.get("permissions"): - permissions = cdict["permissions"] - del self._createdict - - self.permissions.batch_add(*permissions) - - def at_access(self, result, accessing_obj, access_type, **kwargs): - """ - This is triggered after an access-call on this Player has - completed. - - Args: - result (bool): The result of the access check. - accessing_obj (any): The object requesting the access - check. - access_type (str): The type of access checked. - - Kwargs: - kwargs (any): These are passed on from the access check - and can be used to relay custom instructions from the - check mechanism. - - Notes: - This method cannot affect the result of the lock check and - its return value is not used in any way. It can be used - e.g. to customize error messages in a central location or - create other effects based on the access result. - - """ - pass - - def at_cmdset_get(self, **kwargs): - """ - Called just *before* cmdsets on this player are requested by - the command handler. The cmdsets are available as - `self.cmdset`. If changes need to be done on the fly to the - cmdset before passing them on to the cmdhandler, this is the - place to do it. This is called also if the player currently - have no cmdsets. kwargs are usually not used unless the - cmdset is generated dynamically. - - """ - pass - - def at_first_login(self, **kwargs): - """ - Called the very first time this player logs into the game. - Note that this is called *before* at_pre_login, so no session - is established and usually no character is yet assigned at - this point. This hook is intended for player-specific setup - like configurations. - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - pass - - def at_pre_login(self, **kwargs): - """ - Called every time the user logs in, just before the actual - login-state is set. - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - pass - - def _send_to_connect_channel(self, message): - """ - Helper method for loading and sending to the comm channel - dedicated to connection messages. - - Args: - message (str): A message to send to the connect channel. - - """ - global _CONNECT_CHANNEL - if not _CONNECT_CHANNEL: - try: - _CONNECT_CHANNEL = ChannelDB.objects.filter(db_key=settings.DEFAULT_CHANNELS[1]["key"])[0] - except Exception: - logger.log_trace() - now = timezone.now() - now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month, - now.day, now.hour, now.minute) - if _CONNECT_CHANNEL: - _CONNECT_CHANNEL.tempmsg("[%s, %s]: %s" % (_CONNECT_CHANNEL.key, now, message)) - else: - logger.log_info("[%s]: %s" % (now, message)) - - def at_post_login(self, session=None, **kwargs): - """ - Called at the end of the login process, just before letting - the player loose. - - Args: - session (Session, optional): Session logging in, if any. - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - Notes: - This is called *before* an eventual Character's - `at_post_login` hook. By default it is used to set up - auto-puppeting based on `MULTISESSION_MODE`. - - """ - # if we have saved protocol flags on ourselves, load them here. - protocol_flags = self.attributes.get("_saved_protocol_flags", None) - if session and protocol_flags: - session.update_flags(**protocol_flags) - - # inform the client that we logged in through an OOB message - if session: - session.msg(logged_in={}) - - self._send_to_connect_channel("|G%s connected|n" % self.key) - if _MULTISESSION_MODE == 0: - # in this mode we should have only one character available. We - # try to auto-connect to our last conneted object, if any - try: - self.puppet_object(session, self.db._last_puppet) - except RuntimeError: - self.msg("The Character does not exist.") - return - elif _MULTISESSION_MODE == 1: - # in this mode all sessions connect to the same puppet. - try: - self.puppet_object(session, self.db._last_puppet) - except RuntimeError: - self.msg("The Character does not exist.") - return - elif _MULTISESSION_MODE in (2, 3): - # In this mode we by default end up at a character selection - # screen. We execute look on the player. - # we make sure to clean up the _playable_characers list in case - # any was deleted in the interim. - self.db._playable_characters = [char for char in self.db._playable_characters if char] - self.msg(self.at_look(target=self.db._playable_characters, - session=session)) - - def at_failed_login(self, session, **kwargs): - """ - Called by the login process if a user account is targeted correctly - but provided with an invalid password. By default it does nothing, - but exists to be overriden. - - Args: - session (session): Session logging in. - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - """ - pass - - def at_disconnect(self, reason=None, **kwargs): - """ - Called just before user is disconnected. - - Args: - reason (str, optional): The reason given for the disconnect, - (echoed to the connection channel by default). - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - - """ - reason = reason and "(%s)" % reason or "" - self._send_to_connect_channel("|R%s disconnected %s|n" % (self.key, reason)) - - def at_post_disconnect(self, **kwargs): - """ - This is called *after* disconnection is complete. No messages - can be relayed to the player from here. After this call, the - player should not be accessed any more, making this a good - spot for deleting it (in the case of a guest player account, - for example). - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - pass - - def at_message_receive(self, message, from_obj=None, **kwargs): - """ - This is currently unused. - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - return True - - def at_message_send(self, message, to_object, **kwargs): - """ - This is currently unused. - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - pass - - def at_server_reload(self): - """ - This hook is called whenever the server is shutting down for - restart/reboot. If you want to, for example, save - non-persistent properties across a restart, this is the place - to do it. - """ - pass - - def at_server_shutdown(self): - """ - This hook is called whenever the server is shutting down fully - (i.e. not for a restart). - """ - pass - - def at_look(self, target=None, session=None, **kwargs): - """ - Called when this object executes a look. It allows to customize - just what this means. - - Args: - target (Object or list, optional): An object or a list - objects to inspect. - session (Session, optional): The session doing this look. - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - Returns: - look_string (str): A prepared look string, ready to send - off to any recipient (usually to ourselves) - - """ - - if target and not is_iter(target): - # single target - just show it - return target.return_appearance(self) - else: - # list of targets - make list to disconnect from db - characters = list(tar for tar in target if tar) if target else [] - sessions = self.sessions.all() - is_su = self.is_superuser - - # text shown when looking in the ooc area - result = ["Account |g%s|n (you are Out-of-Character)" % self.key] - - nsess = len(sessions) - result.append(nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess) - for isess, sess in enumerate(sessions): - csessid = sess.sessid - addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) - and str(sess.address[0]) or str(sess.address)) - result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) - or " %s" % (isess + 1), addr)) - result.append("\n\n |whelp|n - more commands") - result.append("\n |wooc |n - talk on public channel") - - charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1 - - if is_su or len(characters) < charmax: - if not characters: - result.append("\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one.") - else: - result.append("\n |w@charcreate [=description]|n - create new character") - result.append("\n |w@chardelete |n - delete a character (cannot be undone!)") - - if characters: - string_s_ending = len(characters) > 1 and "s" or "" - result.append("\n |w@ic |n - enter the game (|w@ooc|n to get back here)") - if is_su: - result.append("\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))) - else: - result.append("\n\nAvailable character%s%s:" - % (string_s_ending, charmax > 1 and " (%i/%i)" % (len(characters), charmax) or "")) - - for char in characters: - csessions = char.sessions.all() - if csessions: - for sess in csessions: - # character is already puppeted - sid = sess in sessions and sessions.index(sess) + 1 - if sess and sid: - result.append("\n - |G%s|n [%s] (played by you in session %i)" - % (char.key, ", ".join(char.permissions.all()), sid)) - else: - result.append("\n - |R%s|n [%s] (played by someone else)" - % (char.key, ", ".join(char.permissions.all()))) - else: - # character is "free to puppet" - result.append("\n - %s [%s]" % (char.key, ", ".join(char.permissions.all()))) - look_string = ("-" * 68) + "\n" + "".join(result) + "\n" + ("-" * 68) - return look_string - - -class DefaultGuest(DefaultPlayer): - """ - This class is used for guest logins. Unlike Players, Guests and - their characters are deleted after disconnection. - """ - def at_post_login(self, session=None, **kwargs): - """ - In theory, guests only have one character regardless of which - MULTISESSION_MODE we're in. They don't get a choice. - - Args: - session (Session, optional): Session connecting. - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - self._send_to_connect_channel("|G%s connected|n" % self.key) - self.puppet_object(session, self.db._last_puppet) - - def at_server_shutdown(self): - """ - We repeat the functionality of `at_disconnect()` here just to - be on the safe side. - """ - super(DefaultGuest, self).at_server_shutdown() - characters = self.db._playable_characters - for character in characters: - if character: - print "deleting Character:", character - character.delete() - - def at_post_disconnect(self, **kwargs): - """ - Once having disconnected, destroy the guest's characters and - - Args: - **kwargs (dict): Arbitrary, optional arguments for users - overriding the call (unused by default). - - """ - super(DefaultGuest, self).at_post_disconnect() - characters = self.db._playable_characters - for character in characters: - if character: - character.delete() - self.delete() diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 158666542..c604a67ec 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -717,7 +717,6 @@ INSTALLED_APPS = ( 'evennia.server', 'evennia.typeclasses', 'evennia.accounts', - 'evennia.accounts', 'evennia.objects', 'evennia.comms', 'evennia.help',