From d3808c1ea2ca4b818810b9c5c681decea24420d5 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Jun 2008 19:06:31 +0000 Subject: [PATCH] Committin a 'working' checkpoint before I break more stuff. --- apps/objects/exceptions.py | 11 + apps/objects/managers/attribute.py | 18 ++ apps/objects/managers/object.py | 342 +++++++++++++++++++++++++- apps/objects/models.py | 12 +- apps/objects/util/__init__.py | 0 apps/objects/util/object.py | 19 ++ cmdhandler.py | 4 +- commands/comsys.py | 9 +- commands/general.py | 22 +- commands/info.py | 4 +- commands/objmanip.py | 56 +++-- commands/privileged.py | 6 +- commands/unloggedin.py | 17 +- functions_db.py | 378 ----------------------------- session.py | 10 +- src/__init__.py | 0 src/exceptions_generic.py | 11 + src/flags.py | 19 ++ 18 files changed, 494 insertions(+), 444 deletions(-) create mode 100644 apps/objects/exceptions.py create mode 100644 apps/objects/managers/attribute.py create mode 100644 apps/objects/util/__init__.py create mode 100644 apps/objects/util/object.py delete mode 100644 functions_db.py create mode 100644 src/__init__.py create mode 100644 src/exceptions_generic.py create mode 100644 src/flags.py diff --git a/apps/objects/exceptions.py b/apps/objects/exceptions.py new file mode 100644 index 0000000000..2cacb1faa2 --- /dev/null +++ b/apps/objects/exceptions.py @@ -0,0 +1,11 @@ +""" +Exceptions for the object application. +""" +from src.exceptions_generic import GenericException + +class ObjectNotExist(GenericException): + """ + Raised when an object is queried for but does not exist. + """ + def __str__(self): + return repr("No such object: %s" % self.value) diff --git a/apps/objects/managers/attribute.py b/apps/objects/managers/attribute.py new file mode 100644 index 0000000000..a78670154f --- /dev/null +++ b/apps/objects/managers/attribute.py @@ -0,0 +1,18 @@ +""" +Custom manager for Attribute objects. +""" +from django.db import models + +import defines_global + +class AttributeManager(models.Manager): + def is_modifiable_attrib(self, attribname): + """ + Check to see if a particular attribute is modifiable. + + attribname: (string) An attribute name to check. + """ + if attribname.upper() not in defines_global.NOSET_ATTRIBS: + return True + else: + return False diff --git a/apps/objects/managers/object.py b/apps/objects/managers/object.py index 592840d44d..04668429bc 100644 --- a/apps/objects/managers/object.py +++ b/apps/objects/managers/object.py @@ -1,8 +1,348 @@ """ Custom manager for Object objects. """ +import sets +from datetime import datetime, timedelta + from django.db import models +from django.db import connection +from django.contrib.auth.models import User +from django.db.models import Q +from django.contrib.contenttypes.models import ContentType + +from apps.config.models import ConfigValue +from apps.objects.exceptions import ObjectNotExist +from apps.objects.util import object as util_object +import defines_global class ObjectManager(models.Manager): - pass + def num_total_players(self): + """ + Returns the total number of registered players. + """ + return User.objects.count() + def get_connected_players(self): + """ + Returns the a QuerySet containing the currently connected players. + """ + return self.filter(nosave_flags__contains="CONNECTED") + + def get_recently_created_users(self, days=7): + """ + Returns a QuerySet containing the player User accounts that have been + connected within the last days. + """ + end_date = datetime.now() + tdelta = timedelta(days) + start_date = end_date - tdelta + return User.objects.filter(date_joined__range=(start_date, end_date)) + + def get_recently_connected_users(self, days=7): + """ + Returns a QuerySet containing the player User accounts that have been + connected within the last days. + """ + end_date = datetime.now() + tdelta = timedelta(days) + start_date = end_date - tdelta + return User.objects.filter(last_login__range=(start_date, end_date)).order_by('-last_login') + + def get_nextfree_dbnum(self): + """ + Figure out what our next free database reference number is. + + If we need to recycle a GARBAGE object, return the object to recycle + Otherwise, return the first free dbref. + """ + # First we'll see if there's an object of type 6 (GARBAGE) that we + # can recycle. + nextfree = self.filter(type__exact=defines_global.OTYPE_GARBAGE) + if nextfree: + # We've got at least one garbage object to recycle. + return nextfree.id + else: + # No garbage to recycle, find the highest dbnum and increment it + # for our next free. + return int(self.order_by('-id')[0].id + 1) + + def global_object_name_search(self, ostring, exact_match=False): + """ + Searches through all objects for a name match. + """ + if exact_match: + o_query = self.filter(name__iexact=ostring) + else: + o_query = self.filter(name__icontains=ostring) + + return o_query.exclude(type=defines_global.OTYPE_GARBAGE) + + def list_search_object_namestr(self, searchlist, ostring, dbref_only=False, limit_types=False, match_type="fuzzy"): + """ + Iterates through a list of objects and returns a list of + name matches. + searchlist: (List of Objects) The objects to perform name comparisons on. + ostring: (string) The string to match against. + dbref_only: (bool) Only compare dbrefs. + limit_types: (list of int) A list of Object type numbers to filter by. + """ + if dbref_only: + if limit_types: + return [prospect for prospect in searchlist if prospect.dbref_match(ostring) and prospect.type in limit_types] + else: + return [prospect for prospect in searchlist if prospect.dbref_match(ostring)] + else: + if limit_types: + return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type) and prospect.type in limit_types] + else: + return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type)] + + + def standard_plr_objsearch(self, session, ostring, search_contents=True, search_location=True, dbref_only=False, limit_types=False): + """ + Perform a standard object search via a player session, handling multiple + results and lack thereof gracefully. + + session: (SessionProtocol) Reference to the player's session. + ostring: (str) The string to match object names against. + """ + pobject = session.get_pobject() + results = self.local_and_global_search(pobject, ostring, search_contents=search_contents, search_location=search_location, dbref_only=dbref_only, limit_types=limit_types) + + if len(results) > 1: + session.msg("More than one match found (please narrow target):") + for result in results: + session.msg(" %s" % (result.get_name(),)) + return False + elif len(results) == 0: + session.msg("I don't see that here.") + return False + else: + return results[0] + + def object_totals(self): + """ + Returns a dictionary with database object totals. + """ + dbtotals = { + "objects": self.count(), + "things": self.filter(type=defines_global.OTYPE_THING).count(), + "exits": self.filter(type=defines_global.OTYPE_EXIT).count(), + "rooms": self.filter(type=defines_global.OTYPE_ROOM).count(), + "garbage": self.filter(type=defines_global.OTYPE_GARBAGE).count(), + "players": self.filter(type=defines_global.OTYPE_PLAYER).count(), + } + return dbtotals + + def player_alias_search(self, searcher, ostring): + """ + Search players by alias. Returns a list of objects whose "ALIAS" attribute + exactly (not case-sensitive) matches ostring. + + searcher: (Object) The object doing the searching. + ostring: (string) The alias string to search for. + """ + search_query = ''.join(ostring) + Attribute = ContentType.objects.get(app_label="objects", model="attribute").get_model() + results = Attribute.objects.select_related().filter(attr_name__exact="ALIAS").filter(attr_value__iexact=ostring) + return [prospect.get_object() for prospect in results if prospect.get_object().is_player()] + + def player_name_search(self, search_string): + """ + Combines an alias and global search for a player's name. If there are + no alias matches, do a global search limiting by type PLAYER. + + search_string: (string) The name string to search for. + """ + # Handle the case where someone might have started the search_string + # with a * + if search_string.startswith('*') is True: + search_string = search_string[1:] + # Use Q objects to build complex OR query to look at either + # the player name or ALIAS attribute + player_filter = Q(name__iexact=search_string) + alias_filter = Q(attribute__attr_name__exact="ALIAS") & \ + Q(attribute__attr_value__iexact=search_string) + player_matches = self.filter( + player_filter | alias_filter).filter( + type=defines_global.OTYPE_PLAYER).distinct() + try: + return player_matches[0] + except IndexError: + return None + + def dbref_search(self, dbref_string, limit_types=False): + """ + Searches for a given dbref. + + dbref_number: (string) The dbref to search for + limit_types: (list of int) A list of Object type numbers to filter by. + """ + if not util_object.is_dbref(dbref_string): + return None + dbref_string = dbref_string[1:] + dbref_matches = self.filter(id=dbref_string).exclude( + type=defines_global.OTYPE_GARBAGE) + # Check for limiters + if limit_types is not False: + for limiter in limit_types: + dbref_matches.filter(type=limiter) + try: + return dbref_matches[0] + except IndexError: + return None + + def local_and_global_search(self, searcher, ostring, search_contents=True, search_location=True, dbref_only=False, limit_types=False): + """ + Searches an object's location then globally for a dbref or name match. + + searcher: (Object) The object performing the search. + ostring: (string) The string to compare names against. + search_contents: (bool) While true, check the contents of the searcher. + search_location: (bool) While true, check the searcher's surroundings. + dbref_only: (bool) Only compare dbrefs. + limit_types: (list of int) A list of Object type numbers to filter by. + """ + search_query = ''.join(ostring) + + # This is a global dbref search. Not applicable if we're only searching + # searcher's contents/locations, dbref comparisons for location/contents + # searches are handled by list_search_object_namestr() below. + if util_object.is_dbref(ostring): + search_num = search_query[1:] + dbref_match = dbref_search(search_num, limit_types) + if dbref_match is not None: + return [dbref_match] + + # If the search string is one of the following, return immediately with + # the appropriate result. + if searcher.get_location().dbref_match(ostring) or ostring == 'here': + return [searcher.get_location()] + elif ostring == 'me' and searcher: + return [searcher] + + if search_query[0] == "*": + # Player search- gotta search by name or alias + search_target = search_query[1:] + player_match = player_name_search(search_target) + if player_match is not None: + return [player_match] + + local_matches = [] + # Handle our location/contents searches. list_search_object_namestr() does + # name and dbref comparisons against search_query. + if search_contents: + local_matches += list_search_object_namestr(searcher.get_contents(), search_query, limit_types) + if search_location: + local_matches += list_search_object_namestr(searcher.get_location().get_contents(), search_query, limit_types=limit_types) + return local_matches + + def get_user_from_email(self, uemail): + """ + Returns a player's User object when given an email address. + """ + return User.objects.filter(email__iexact=uemail) + + def get_object_from_dbref(self, dbref): + """ + Returns an object when given a dbref. + """ + try: + return self.get(id=dbref) + except self.model.DoesNotExist: + raise ObjectNotExist(dbref) + + def create_object(self, odat): + """ + Create a new object. odat is a dictionary that contains the following keys. + REQUIRED KEYS: + * type: Integer representing the object's type. + * name: The name of the new object. + * location: Reference to another object for the new object to reside in. + * owner: The creator of the object. + OPTIONAL KEYS: + * home: Reference to another object to home to. If not specified, use + location key for home. + """ + next_dbref = get_nextfree_dbnum() + new_object = Object() + + new_object.id = next_dbref + new_object.type = odat["type"] + new_object.set_name(odat["name"]) + + # If this is a player, we don't want him owned by anyone. + # The get_owner() function will return that the player owns + # himself. + if odat["type"] == 1: + new_object.owner = None + new_object.zone = None + else: + new_object.owner = odat["owner"] + + if new_object.get_owner().get_zone(): + new_object.zone = new_object.get_owner().get_zone() + + # If we have a 'home' key, use that for our home value. Otherwise use + # the location key. + if odat.has_key("home"): + new_object.home = odat["home"] + else: + if new_object.is_exit(): + new_object.home = None + else: + new_object.home = odat["location"] + + new_object.save() + + # Rooms have a NULL location. + if not new_object.is_room(): + new_object.move_to(odat['location']) + + return new_object + + def create_user(self, cdat, uname, email, password): + """ + Handles the creation of new users. + """ + session = cdat['session'] + server = cdat['server'] + start_room = int(ConfigValue.objects.get_configvalue('player_dbnum_start')) + start_room_obj = get_object_from_dbref(start_room) + + # The user's entry in the User table must match up to an object + # on the object table. The id's are the same, we need to figure out + # the next free unique ID to use and make sure the two entries are + # the same number. + uid = get_nextfree_dbnum() + print 'UID', uid + + # If this is an object, we know to recycle it since it's garbage. We'll + # pluck the user ID from it. + if not str(uid).isdigit(): + uid = uid.id + print 'UID2', uid + + user = User.objects.create_user(uname, email, password) + # It stinks to have to do this but it's the only trivial way now. + user.save() + + # We can't use the user model to change the id because of the way keys + # are handled, so we actually need to fall back to raw SQL. Boo hiss. + cursor = connection.cursor() + cursor.execute("UPDATE auth_user SET id=%d WHERE id=%d" % (uid, user.id)) + + # Grab the user object again since we've changed it and the old reference + # is no longer valid. + user = User.objects.get(id=uid) + + # Create a player object of the same ID in the Objects table. + odat = {"id": uid, "name": uname, "type": 1, "location": start_room_obj, "owner": None} + user_object = create_object(odat) + + # Activate the player's session and set them loose. + session.login(user) + print 'Registration: %s' % (session,) + session.msg("Welcome to %s, %s.\n\r" % ( + ConfigValue.objects.get_configvalue('site_name'), + session.get_pobject().get_name(show_dbref=False))) diff --git a/apps/objects/models.py b/apps/objects/models.py index 049ee1222e..c907284056 100755 --- a/apps/objects/models.py +++ b/apps/objects/models.py @@ -6,9 +6,12 @@ from django.contrib.auth.models import User, Group import scripthandler import defines_global import ansi +import src.flags from apps.config.models import ConfigValue +from apps.objects.util import object as util_object from apps.objects.managers.commchannel import CommChannelManager from apps.objects.managers.object import ObjectManager +from apps.objects.managers.attribute import AttributeManager class Attribute(models.Model): """ @@ -24,6 +27,8 @@ class Attribute(models.Model): attr_hidden = models.BooleanField(default=0) attr_object = models.ForeignKey("Object") + objects = AttributeManager() + def __str__(self): return "%s(%s)" % (self.attr_name, self.id) @@ -550,7 +555,7 @@ class Object(models.Model): if value == False and has_flag: # Clear the flag. - if functions_db.is_unsavable_flag(flag): + if src.flags.is_unsavable_flag(flag): # Not a savable flag (CONNECTED, etc) flags = self.nosave_flags.split() flags.remove(flag) @@ -570,7 +575,7 @@ class Object(models.Model): pass else: # Setting a flag. - if functions_db.is_unsavable_flag(flag): + if src.flags.is_unsavable_flag(flag): # Not a savable flag (CONNECTED, etc) flags = str(self.nosave_flags).split() flags.append(flag) @@ -707,7 +712,7 @@ class Object(models.Model): oname: (str) Name to match against. """ - if not functions_db.is_dbref(oname): + if not util_object.is_dbref(oname): return False try: @@ -888,6 +893,5 @@ class CommChannelMessage(models.Model): class Admin: list_display = ('channel', 'message') -import functions_db import functions_general import session_mgr diff --git a/apps/objects/util/__init__.py b/apps/objects/util/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/objects/util/object.py b/apps/objects/util/object.py new file mode 100644 index 0000000000..c7d6f03e2e --- /dev/null +++ b/apps/objects/util/object.py @@ -0,0 +1,19 @@ +""" +Utility functions for the Object class. These functions should not import +any models or modify the database. +""" +def is_dbref(dbstring): + """ + Is the input a well-formed dbref number? + """ + try: + number = int(dbstring[1:]) + except ValueError: + return False + + if not dbstring.startswith("#"): + return False + elif number < 1: + return False + else: + return True diff --git a/cmdhandler.py b/cmdhandler.py index 00915e07d4..3b84e5af19 100755 --- a/cmdhandler.py +++ b/cmdhandler.py @@ -6,9 +6,9 @@ something. from traceback import format_exc import time +from apps.objects.models import Object import defines_global import cmdtable -import functions_db import functions_general import functions_log import functions_comsys @@ -23,7 +23,7 @@ def match_exits(pobject, searchstr): See if we can find an input match to exits. """ exits = pobject.get_location().get_contents(filter_type=4) - return functions_db.list_search_object_namestr(exits, searchstr, match_type="exact") + return Object.objects.list_search_object_namestr(exits, searchstr, match_type="exact") def parse_command(command_string): """ diff --git a/commands/comsys.py b/commands/comsys.py index 7a59e4e877..531417b86d 100644 --- a/commands/comsys.py +++ b/commands/comsys.py @@ -1,16 +1,15 @@ +""" +Comsys command module. Pretty much every comsys command should go here for +now. +""" import time from django.conf import settings import functions_general -import functions_db import functions_comsys import defines_global import ansi -""" -Comsys command module. Pretty much every comsys command should go here for -now. -""" def cmd_addcom(cdat): """ diff --git a/commands/general.py b/commands/general.py index 1f0262e3bf..c64e8240f6 100644 --- a/commands/general.py +++ b/commands/general.py @@ -8,8 +8,8 @@ from django.conf import settings from apps.config.models import ConfigValue from apps.helpsys.models import HelpEntry +from apps.objects.models import Object import functions_general -import functions_db import defines_global import session_mgr import ansi @@ -110,7 +110,7 @@ def cmd_look(cdat): if len(args) == 0: target_obj = pobject.get_location() else: - target_obj = functions_db.standard_plr_objsearch(session, ' '.join(args)) + target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args)) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -139,7 +139,7 @@ def cmd_get(cdat): session.msg("Get what?") return else: - target_obj = functions_db.standard_plr_objsearch(session, ' '.join(args), search_contents=False) + target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args), search_contents=False) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -178,7 +178,7 @@ def cmd_drop(cdat): session.msg("Drop what?") return else: - target_obj = functions_db.standard_plr_objsearch(session, ' '.join(args), search_location=False) + target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args), search_location=False) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -224,7 +224,7 @@ def cmd_examine(cdat): else: searchstr = ' '.join(args) - target_obj = functions_db.standard_plr_objsearch(session, searchstr) + target_obj = Object.objects.standard_plr_objsearch(session, searchstr) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -302,9 +302,9 @@ def cmd_page(cdat): last_paged_dbref_list = [ x.strip() for x in last_paged_dbrefs.split(',')] for dbref in last_paged_dbref_list: - if not functions_db.is_dbref(dbref): + if not Object.objects.is_dbref(dbref): raise ValueError - last_paged_object = functions_db.dbref_search(dbref) + last_paged_object = Object.objects.dbref_search(dbref) if last_paged_object is not None: last_paged_objects.append(last_paged_object) except ValueError: @@ -332,7 +332,7 @@ def cmd_page(cdat): targets = dict([(target, 1) for target in last_paged_objects]) else: # First try to match the entire target string against a single player - full_target_match = functions_db.player_name_search( + full_target_match = Object.objects.player_name_search( parsed_command['original_targets']) if full_target_match is not None: targets[full_target_match] = 1 @@ -341,9 +341,9 @@ def cmd_page(cdat): # it to the targets list for target in parsed_command['targets']: # If the target is a dbref, behave appropriately - if functions_db.is_dbref(target): + if Object.objects.is_dbref(target): session.msg("Is dbref.") - matched_object = functions_db.dbref_search(target, + matched_object = Object.objects.dbref_search(target, limit_types=[defines_global.OTYPE_PLAYER]) if matched_object is not None: targets[matched_object] = 1 @@ -353,7 +353,7 @@ def cmd_page(cdat): target)) else: # Not a dbref, so must be a username, treat it as such - matched_object = functions_db.player_name_search( + matched_object = Object.objects.player_name_search( target) if matched_object is not None: targets[matched_object] = 1 diff --git a/commands/info.py b/commands/info.py index 2194ba2a80..80e8ce7b35 100644 --- a/commands/info.py +++ b/commands/info.py @@ -11,7 +11,7 @@ if not functions_general.host_os_is('nt'): # Don't import the resource module if the host OS is Windows. import resource -import functions_db +from apps.objects.models import Object import scheduler import defines_global @@ -99,7 +99,7 @@ def cmd_stats(cdat): 4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage) """ session = cdat['session'] - stats_dict = functions_db.object_totals() + stats_dict = Object.objects.object_totals() session.msg("%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" % (stats_dict["objects"], stats_dict["rooms"], stats_dict["exits"], diff --git a/commands/objmanip.py b/commands/objmanip.py index c5e8ec8fe6..c29168e169 100644 --- a/commands/objmanip.py +++ b/commands/objmanip.py @@ -1,6 +1,10 @@ +""" +These commands typically are to do with building or modifying Objects. +""" +from apps.objects.models import Object +import src.flags import ansi import session_mgr -import functions_db def cmd_teleport(cdat): """ @@ -23,12 +27,12 @@ def cmd_teleport(cdat): # a direct teleport, @tel . if len(eq_args) > 1: # Equal sign teleport. - victim = functions_db.standard_plr_objsearch(session, eq_args[0]) + victim = Object.objects.standard_plr_objsearch(session, eq_args[0]) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not victim: return - destination = functions_db.standard_plr_objsearch(session, eq_args[1]) + destination = Object.objects.standard_plr_objsearch(session, eq_args[1]) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not destination: return @@ -53,7 +57,7 @@ def cmd_teleport(cdat): else: # Direct teleport (no equal sign) - target_obj = functions_db.standard_plr_objsearch(session, search_str) + target_obj = Object.objects.standard_plr_objsearch(session, search_str) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -71,7 +75,7 @@ def cmd_stats(cdat): 4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage) """ session = cdat['session'] - stats_dict = functions_db.object_totals() + stats_dict = Object.objects.object_totals() session.msg("%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" % (stats_dict["objects"], stats_dict["rooms"], stats_dict["exits"], @@ -98,13 +102,13 @@ def cmd_alias(cdat): session.msg("Alias missing.") return - target = functions_db.standard_plr_objsearch(session, eq_args[0]) + target = Object.objects.standard_plr_objsearch(session, eq_args[0]) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target: session.msg("Alias whom?") return - duplicates = functions_db.alias_search(pobject, eq_args[1]) + duplicates = Object.objects.player_alias_search(pobject, eq_args[1]) if duplicates: session.msg("Alias '%s' already exists." % (eq_args[1],)) @@ -145,7 +149,7 @@ def cmd_wipe(cdat): else: searchstr = ' '.join(args) - target_obj = functions_db.standard_plr_objsearch(session, searchstr) + target_obj = Object.objects.standard_plr_objsearch(session, searchstr) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -189,7 +193,7 @@ def cmd_set(cdat): session.msg("Set what?") return - victim = functions_db.standard_plr_objsearch(session, eq_args[0]) + victim = Object.objects.standard_plr_objsearch(session, eq_args[0]) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not victim: return @@ -207,7 +211,7 @@ def cmd_set(cdat): attrib_value = eq_args[1][splicenum:] # In global_defines.py, see NOSET_ATTRIBS for protected attribute names. - if not functions_db.is_modifiable_attrib(attrib_name) and not pobject.is_superuser(): + if not src.flags.is_modifiable_attrib(attrib_name) and not pobject.is_superuser(): session.msg("You can't modify that attribute.") return @@ -229,14 +233,14 @@ def cmd_set(cdat): if flag[0] == '!': # We're un-setting the flag. flag = flag[1:] - if not functions_db.is_modifiable_flag(flag): + if not src.flags.is_modifiable_flag(flag): session.msg("You can't set/unset the flag - %s." % (flag,)) else: session.msg('%s - %s cleared.' % (victim.get_name(), flag.upper(),)) victim.set_flag(flag, False) else: # We're setting the flag. - if not functions_db.is_modifiable_flag(flag): + if not src.flags.is_modifiable_flag(flag): session.msg("You can't set/unset the flag - %s." % (flag,)) else: session.msg('%s - %s set.' % (victim.get_name(), flag.upper(),)) @@ -256,7 +260,7 @@ def cmd_find(cdat): session.msg("No search pattern given.") return - results = functions_db.global_object_name_search(searchstring) + results = Object.objects.global_object_name_search(searchstring) if len(results) > 0: session.msg("Name matches for: %s" % (searchstring,)) @@ -281,7 +285,7 @@ def cmd_create(cdat): else: # Create and set the object up. odat = {"name": thingname, "type": 3, "location": pobject, "owner": pobject} - new_object = functions_db.create_object(odat) + new_object = Object.objects.create_object(odat) session.msg("You create a new thing: %s" % (new_object,)) @@ -291,7 +295,7 @@ def cmd_nextfree(cdat): """ session = cdat['session'] - nextfree = functions_db.get_nextfree_dbnum() + nextfree = Object.objects.get_nextfree_dbnum() session.msg("Next free object number: #%s" % (nextfree,)) def cmd_open(cdat): @@ -325,7 +329,7 @@ def cmd_open(cdat): if len(eq_args) > 1: # Opening an exit to another location via @open =[,]. comma_split = eq_args[1].split(',') - destination = functions_db.standard_plr_objsearch(session, comma_split[0]) + destination = Object.objects.standard_plr_objsearch(session, comma_split[0]) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not destination: return @@ -335,20 +339,20 @@ def cmd_open(cdat): return odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":destination} - new_object = functions_db.create_object(odat) + new_object = Object.objects.create_object(odat) session.msg("You open the an exit - %s to %s" % (new_object.get_name(),destination.get_name())) if len(comma_split) > 1: second_exit_name = ','.join(comma_split[1:]) odat = {"name": second_exit_name, "type": 4, "location": destination, "owner": pobject, "home": pobject.get_location()} - new_object = functions_db.create_object(odat) + new_object = Object.objects.create_object(odat) session.msg("You open the an exit - %s to %s" % (new_object.get_name(),pobject.get_location().get_name())) else: # Create an un-linked exit. odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":None} - new_object = functions_db.create_object(odat) + new_object = Object.objects.create_object(odat) session.msg("You open an unlinked exit - %s" % (new_object,)) @@ -377,7 +381,7 @@ def cmd_link(cdat): return if len(eq_args) > 1: - target_obj = functions_db.standard_plr_objsearch(session, target_name) + target_obj = Object.objects.standard_plr_objsearch(session, target_name) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -392,7 +396,7 @@ def cmd_link(cdat): session.msg("You have unlinked %s." % (target_obj,)) return - destination = functions_db.standard_plr_objsearch(session, dest_name) + destination = Object.objects.standard_plr_objsearch(session, dest_name) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not destination: return @@ -417,7 +421,7 @@ def cmd_unlink(cdat): session.msg("Unlink what?") return else: - target_obj = functions_db.standard_plr_objsearch(session, ' '.join(args)) + target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args)) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -443,7 +447,7 @@ def cmd_dig(cdat): else: # Create and set the object up. odat = {"name": roomname, "type": 2, "location": None, "owner": pobject} - new_object = functions_db.create_object(odat) + new_object = Object.objects.create_object(odat) session.msg("You create a new room: %s" % (new_object,)) @@ -462,7 +466,7 @@ def cmd_name(cdat): elif len(eq_args) < 2: session.msg("What would you like to name that object?") else: - target_obj = functions_db.standard_plr_objsearch(session, searchstring) + target_obj = Object.objects.standard_plr_objsearch(session, searchstring) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -489,7 +493,7 @@ def cmd_description(cdat): elif len(eq_args) < 2: session.msg("How would you like to describe that object?") else: - target_obj = functions_db.standard_plr_objsearch(session, searchstring) + target_obj = Object.objects.standard_plr_objsearch(session, searchstring) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return @@ -519,7 +523,7 @@ def cmd_destroy(cdat): session.msg("Destroy what?") return else: - target_obj = functions_db.standard_plr_objsearch(session, ' '.join(args)) + target_obj = Object.objects.standard_plr_objsearch(session, ' '.join(args)) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return diff --git a/commands/privileged.py b/commands/privileged.py index 9cb29cb0b3..babf092b76 100644 --- a/commands/privileged.py +++ b/commands/privileged.py @@ -1,6 +1,6 @@ +from apps.objects.models import Object import defines_global import functions_general -import functions_db import ansi """ @@ -52,7 +52,7 @@ def cmd_boot(cdat): break else: # Grab the objects that match - objs = functions_db.global_object_name_search(searchstring) + objs = Objects.object.global_object_name_search(searchstring) if len(objs) < 1: session.msg("Who would you like to boot?") @@ -98,7 +98,7 @@ def cmd_newpassword(cdat): session.msg("You must supply a new password.") return - target_obj = functions_db.standard_plr_objsearch(session, searchstring) + target_obj = Objects.object.standard_plr_objsearch(session, searchstring) # Use standard_plr_objsearch to handle duplicate/nonexistant results. if not target_obj: return diff --git a/commands/unloggedin.py b/commands/unloggedin.py index 45194fd93a..2e60e1b193 100644 --- a/commands/unloggedin.py +++ b/commands/unloggedin.py @@ -1,12 +1,11 @@ -from django.contrib.auth.models import User -from apps.objects.models import Attribute, Object -import functions_db -import functions_general -import defines_global - """ Commands that are available from the connect screen. """ +from django.contrib.auth.models import User + +from apps.objects.models import Attribute, Object +import functions_general +import defines_global def cmd_connect(cdat): """ @@ -24,7 +23,7 @@ def cmd_connect(cdat): password = cdat['uinput']['splitted'][2] # Match an email address to an account. - email_matches = functions_db.get_user_from_email(uemail) + email_matches = Object.objects.get_user_from_email(uemail) autherror = "Specified email does not match any accounts!" # No username match @@ -71,7 +70,7 @@ def cmd_create(cdat): # Search for a user object with the specified username. account = User.objects.filter(username=uname) # Match an email address to an account. - email_matches = functions_db.get_user_from_email(email) + email_matches = Object.objects.get_user_from_email(email) # Look for any objects with an 'Alias' attribute that matches # the requested username alias_matches = Object.objects.filter(attribute__attr_name__exact="ALIAS", @@ -85,7 +84,7 @@ def cmd_create(cdat): elif len(password) < 3: session.msg("Your password must be 3 characters or longer.") else: - functions_db.create_user(cdat, uname, email, password) + Object.objects.create_user(cdat, uname, email, password) def cmd_quit(cdat): """ diff --git a/functions_db.py b/functions_db.py deleted file mode 100644 index f5dc939422..0000000000 --- a/functions_db.py +++ /dev/null @@ -1,378 +0,0 @@ -import sets -from datetime import datetime, timedelta - -from django.db import connection -from django.contrib.auth.models import User -from django.db.models import Q - -from apps.objects.models import Object, Attribute -from apps.config.models import ConfigValue -import defines_global - -""" -Common database functions. -""" -def num_total_players(): - """ - Returns the total number of registered players. - """ - return User.objects.count() - -def get_connected_players(): - """ - Returns the a QuerySet containing the currently connected players. - """ - return Object.objects.filter(nosave_flags__contains="CONNECTED") - -def get_recently_created_players(days=7): - """ - Returns a QuerySet containing the player User accounts that have been - connected within the last days. - """ - end_date = datetime.now() - tdelta = timedelta(days) - start_date = end_date - tdelta - return User.objects.filter(date_joined__range=(start_date, end_date)) - -def get_recently_connected_players(days=7): - """ - Returns a QuerySet containing the player User accounts that have been - connected within the last days. - """ - end_date = datetime.now() - tdelta = timedelta(days) - start_date = end_date - tdelta - return User.objects.filter(last_login__range=(start_date, end_date)).order_by('-last_login') - -def is_unsavable_flag(flagname): - """ - Returns TRUE if the flag is an unsavable flag. - """ - return flagname.upper() in defines_global.NOSAVE_FLAGS - -def is_modifiable_flag(flagname): - """ - Check to see if a particular flag is modifiable. - """ - if flagname.upper() not in defines_global.NOSET_FLAGS: - return True - else: - return False - -def is_modifiable_attrib(attribname): - """ - Check to see if a particular attribute is modifiable. - - attribname: (string) An attribute name to check. - """ - if attribname.upper() not in defines_global.NOSET_ATTRIBS: - return True - else: - return False - -def get_nextfree_dbnum(): - """ - Figure out what our next free database reference number is. - - If we need to recycle a GARBAGE object, return the object to recycle - Otherwise, return the first free dbref. - """ - # First we'll see if there's an object of type 6 (GARBAGE) that we - # can recycle. - nextfree = Object.objects.filter(type__exact=defines_global.OTYPE_GARBAGE) - if nextfree: - # We've got at least one garbage object to recycle. - return nextfree.id - else: - # No garbage to recycle, find the highest dbnum and increment it - # for our next free. - return int(Object.objects.order_by('-id')[0].id + 1) - -def global_object_name_search(ostring, exact_match=False): - """ - Searches through all objects for a name match. - """ - if exact_match: - return Object.objects.filter(name__iexact=ostring).exclude(type=defines_global.OTYPE_GARBAGE) - else: - return Object.objects.filter(name__icontains=ostring).exclude(type=defines_global.OTYPE_GARBAGE) - -def list_search_object_namestr(searchlist, ostring, dbref_only=False, limit_types=False, match_type="fuzzy"): - """ - Iterates through a list of objects and returns a list of - name matches. - searchlist: (List of Objects) The objects to perform name comparisons on. - ostring: (string) The string to match against. - dbref_only: (bool) Only compare dbrefs. - limit_types: (list of int) A list of Object type numbers to filter by. - """ - if dbref_only: - if limit_types: - return [prospect for prospect in searchlist if prospect.dbref_match(ostring) and prospect.type in limit_types] - else: - return [prospect for prospect in searchlist if prospect.dbref_match(ostring)] - else: - if limit_types: - return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type) and prospect.type in limit_types] - else: - return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type)] - - -def standard_plr_objsearch(session, ostring, search_contents=True, search_location=True, dbref_only=False, limit_types=False): - """ - Perform a standard object search via a player session, handling multiple - results and lack thereof gracefully. - - session: (SessionProtocol) Reference to the player's session. - ostring: (str) The string to match object names against. - """ - pobject = session.get_pobject() - results = local_and_global_search(pobject, ostring, search_contents=search_contents, search_location=search_location, dbref_only=dbref_only, limit_types=limit_types) - - if len(results) > 1: - session.msg("More than one match found (please narrow target):") - for result in results: - session.msg(" %s" % (result.get_name(),)) - return False - elif len(results) == 0: - session.msg("I don't see that here.") - return False - else: - return results[0] - -def object_totals(): - """ - Returns a dictionary with database object totals. - """ - dbtotals = {} - dbtotals["objects"] = Object.objects.count() - dbtotals["things"] = Object.objects.filter(type=defines_global.OTYPE_THING).count() - dbtotals["exits"] = Object.objects.filter(type=defines_global.OTYPE_EXIT).count() - dbtotals["rooms"] = Object.objects.filter(type=defines_global.OTYPE_ROOM).count() - dbtotals["garbage"] = Object.objects.filter(type=defines_global.OTYPE_GARBAGE).count() - dbtotals["players"] = Object.objects.filter(type=defines_global.OTYPE_PLAYER).count() - return dbtotals - -def alias_search(searcher, ostring): - """ - Search players by alias. Returns a list of objects whose "ALIAS" attribute - exactly (not case-sensitive) matches ostring. - - searcher: (Object) The object doing the searching. - ostring: (string) The alias string to search for. - """ - search_query = ''.join(ostring) - results = Attribute.objects.select_related().filter(attr_name__exact="ALIAS").filter(attr_value__iexact=ostring) - return [prospect.get_object() for prospect in results if prospect.get_object().is_player()] - -def player_name_search(search_string): - """ - Combines an alias and global search for a player's name. If there are - no alias matches, do a global search limiting by type PLAYER. - - search_string: (string) The name string to search for. - """ - # Handle the case where someone might have started the search_string - # with a * - if search_string.startswith('*') is True: - search_string = search_string[1:] - # Use Q objects to build complex OR query to look at either - # the player name or ALIAS attribute - player_filter = Q(name__iexact=search_string) - alias_filter = Q(attribute__attr_name__exact="ALIAS") & \ - Q(attribute__attr_value__iexact=search_string) - player_matches = Object.objects.filter( - player_filter | alias_filter).filter( - type=defines_global.OTYPE_PLAYER).distinct() - try: - return player_matches[0] - except IndexError: - return None - -def dbref_search(dbref_string, limit_types=False): - """ - Searches for a given dbref. - - dbref_number: (string) The dbref to search for - limit_types: (list of int) A list of Object type numbers to filter by. - """ - if not is_dbref(dbref_string): - return None - dbref_string = dbref_string[1:] - dbref_matches = Object.objects.filter(id=dbref_string).exclude( - type=defines_global.OTYPE_GARBAGE) - # Check for limiters - if limit_types is not False: - for limiter in limit_types: - dbref_matches.filter(type=limiter) - try: - return dbref_matches[0] - except IndexError: - return None - -def local_and_global_search(searcher, ostring, search_contents=True, search_location=True, dbref_only=False, limit_types=False): - """ - Searches an object's location then globally for a dbref or name match. - - searcher: (Object) The object performing the search. - ostring: (string) The string to compare names against. - search_contents: (bool) While true, check the contents of the searcher. - search_location: (bool) While true, check the searcher's surroundings. - dbref_only: (bool) Only compare dbrefs. - limit_types: (list of int) A list of Object type numbers to filter by. - """ - search_query = ''.join(ostring) - - # This is a global dbref search. Not applicable if we're only searching - # searcher's contents/locations, dbref comparisons for location/contents - # searches are handled by list_search_object_namestr() below. - if is_dbref(ostring): - search_num = search_query[1:] - dbref_match = dbref_search(search_num, limit_types) - if dbref_match is not None: - return [dbref_match] - - # If the search string is one of the following, return immediately with - # the appropriate result. - if searcher.get_location().dbref_match(ostring) or ostring == 'here': - return [searcher.get_location()] - elif ostring == 'me' and searcher: - return [searcher] - - if search_query[0] == "*": - # Player search- gotta search by name or alias - search_target = search_query[1:] - player_match = player_name_search(search_target) - if player_match is not None: - return [player_match] - - local_matches = [] - # Handle our location/contents searches. list_search_object_namestr() does - # name and dbref comparisons against search_query. - if search_contents: - local_matches += list_search_object_namestr(searcher.get_contents(), search_query, limit_types) - if search_location: - local_matches += list_search_object_namestr(searcher.get_location().get_contents(), search_query, limit_types=limit_types) - return local_matches - -def is_dbref(dbstring): - """ - Is the input a well-formed dbref number? - """ - try: - number = int(dbstring[1:]) - except ValueError: - return False - if not dbstring.startswith("#"): - return False - elif number < 1: - return False - else: - return True - -def get_user_from_email(uemail): - """ - Returns a player's User object when given an email address. - """ - return User.objects.filter(email__iexact=uemail) - -def get_object_from_dbref(dbref): - """ - Returns an object when given a dbref. - """ - return Object.objects.get(id=dbref) - -def create_object(odat): - """ - Create a new object. odat is a dictionary that contains the following keys. - REQUIRED KEYS: - * type: Integer representing the object's type. - * name: The name of the new object. - * location: Reference to another object for the new object to reside in. - * owner: The creator of the object. - OPTIONAL KEYS: - * home: Reference to another object to home to. If not specified, use - location key for home. - """ - next_dbref = get_nextfree_dbnum() - new_object = Object() - - new_object.id = next_dbref - new_object.type = odat["type"] - new_object.set_name(odat["name"]) - - # If this is a player, we don't want him owned by anyone. - # The get_owner() function will return that the player owns - # himself. - if odat["type"] == 1: - new_object.owner = None - new_object.zone = None - else: - new_object.owner = odat["owner"] - - if new_object.get_owner().get_zone(): - new_object.zone = new_object.get_owner().get_zone() - - # If we have a 'home' key, use that for our home value. Otherwise use - # the location key. - if odat.has_key("home"): - new_object.home = odat["home"] - else: - if new_object.is_exit(): - new_object.home = None - else: - new_object.home = odat["location"] - - new_object.save() - - # Rooms have a NULL location. - if not new_object.is_room(): - new_object.move_to(odat['location']) - - return new_object - -def create_user(cdat, uname, email, password): - """ - Handles the creation of new users. - """ - session = cdat['session'] - server = cdat['server'] - start_room = int(ConfigValue.objects.get_configvalue('player_dbnum_start')) - start_room_obj = get_object_from_dbref(start_room) - - # The user's entry in the User table must match up to an object - # on the object table. The id's are the same, we need to figure out - # the next free unique ID to use and make sure the two entries are - # the same number. - uid = get_nextfree_dbnum() - print 'UID', uid - - # If this is an object, we know to recycle it since it's garbage. We'll - # pluck the user ID from it. - if not str(uid).isdigit(): - uid = uid.id - print 'UID2', uid - - user = User.objects.create_user(uname, email, password) - # It stinks to have to do this but it's the only trivial way now. - user.save() - - # We can't use the user model to change the id because of the way keys - # are handled, so we actually need to fall back to raw SQL. Boo hiss. - cursor = connection.cursor() - cursor.execute("UPDATE auth_user SET id=%d WHERE id=%d" % (uid, user.id)) - - # Grab the user object again since we've changed it and the old reference - # is no longer valid. - user = User.objects.get(id=uid) - - # Create a player object of the same ID in the Objects table. - odat = {"id": uid, "name": uname, "type": 1, "location": start_room_obj, "owner": None} - user_object = create_object(odat) - - # Activate the player's session and set them loose. - session.login(user) - print 'Registration: %s' % (session,) - session.msg("Welcome to %s, %s.\n\r" % ( - ConfigValue.objects.get_configvalue('site_name'), - session.get_pobject().get_name(show_dbref=False))) diff --git a/session.py b/session.py index bb6834f5a7..da053537b7 100755 --- a/session.py +++ b/session.py @@ -1,15 +1,19 @@ -import time, sys +""" +This module contains classes related to Sessions. session_mgr has the things +needed to manage them. +""" +import time +import sys from datetime import datetime -from django.utils import simplejson from twisted.conch.telnet import StatefulTelnetProtocol +from django.utils import simplejson from django.contrib.auth.models import User from apps.objects.models import Object from apps.config.models import ConnectScreen, ConfigValue import cmdhandler -import functions_db import functions_general import functions_log import session_mgr diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/exceptions_generic.py b/src/exceptions_generic.py new file mode 100644 index 0000000000..758ca75ae8 --- /dev/null +++ b/src/exceptions_generic.py @@ -0,0 +1,11 @@ +""" +This module contains exceptions used throughout the server +""" +class GenericException(Exception): + """ + The custom exception class from which all other exceptions are derived. + """ + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) diff --git a/src/flags.py b/src/flags.py new file mode 100644 index 0000000000..82fa8f0fee --- /dev/null +++ b/src/flags.py @@ -0,0 +1,19 @@ +""" +Everything related to flags and flag management. +""" +import defines_global + +def is_unsavable_flag(flagname): + """ + Returns TRUE if the flag is an unsavable flag. + """ + return flagname.upper() in defines_global.NOSAVE_FLAGS + +def is_modifiable_flag(flagname): + """ + Check to see if a particular flag is modifiable. + """ + if flagname.upper() not in defines_global.NOSET_FLAGS: + return True + else: + return False