This commit is contained in:
Griatch 2013-02-09 11:21:38 +01:00
commit e345d659fd
23 changed files with 935 additions and 295 deletions

View file

@ -41,7 +41,6 @@ from twisted.internet.defer import inlineCallbacks, returnValue
from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.commands.cmdparser import at_multimatch_cmd
from src.utils.utils import string_suggestions
@ -165,7 +164,7 @@ def get_and_merge_cmdsets(caller):
# Main command-handler function
@inlineCallbacks
def cmdhandler(caller, raw_string, testing=False):
def cmdhandler(caller, raw_string, testing=False, sessid=None):
"""
This is the main function to handle any string sent to the engine.
@ -251,6 +250,7 @@ def cmdhandler(caller, raw_string, testing=False):
cmd.cmdstring = cmdname
cmd.args = args
cmd.cmdset = cmdset
cmd.sessid = sessid
cmd.raw_string = unformatted_raw_string
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):

View file

@ -1701,13 +1701,16 @@ class CmdExamine(ObjManipCommand):
if not self.args:
# If no arguments are provided, examine the invoker's location.
obj = caller.location
if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead.
caller.execute_cmd('look %s' % obj.name)
return
# using callback for printing result whenever function returns.
get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback)
if hasattr(caller, "location"):
obj = caller.location
if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead.
caller.execute_cmd('look %s' % obj.name)
return
# using callback for printing result whenever function returns.
get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback)
else:
caller.msg("You need to supply a target to examine.")
return
# we have given a specific target object

View file

@ -8,6 +8,7 @@ a Player object as caller rather than a Character.
from src.commands.cmdset import CmdSet
from src.commands.default import help, comms, general, admin, system
from src.commands.default import building
class OOCCmdSet(CmdSet):
"""
Implements the player command set.
@ -23,10 +24,14 @@ class OOCCmdSet(CmdSet):
self.add(general.CmdOOCLook())
self.add(general.CmdIC())
self.add(general.CmdOOC())
self.add(general.CmdCharCreate())
self.add(general.CmdEncoding())
self.add(general.CmdQuit())
self.add(general.CmdPassword())
# test
self.add(building.CmdExamine())
# Help command
self.add(help.CmdHelp())
@ -34,6 +39,7 @@ class OOCCmdSet(CmdSet):
self.add(system.CmdReload())
self.add(system.CmdReset())
self.add(system.CmdShutdown())
self.add(system.CmdPy())
# Admin commands
self.add(admin.CmdDelPlayer())

View file

@ -5,7 +5,7 @@ now.
import time
from django.conf import settings
from src.server.sessionhandler import SESSIONS
from src.utils import utils, search
from src.utils import utils, search, create
from src.objects.models import ObjectNick as Nick
from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC
@ -573,7 +573,7 @@ class CmdEncoding(MuxCommand):
Common encodings are utf-8 (default), latin-1, ISO-8859-1 etc.
If you don't submit an encoding, the current encoding will be displayed instead.
"""
"""
key = "@encoding"
aliases = "@encode"
@ -649,151 +649,6 @@ class CmdAccess(MuxCommand):
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
caller.msg(string)
# OOC commands
class CmdOOCLook(MuxCommandOOC, CmdLook):
"""
ooc look
Usage:
look
This is an OOC version of the look command. Since a
Player doesn't have an in-game existence, there is no
concept of location or "self". If we are controlling
a character, pass control over to normal look.
"""
key = "look"
aliases = ["l", "ls"]
locks = "cmd:all()"
help_category = "General"
def func(self):
"implement the ooc look command"
if not self.character:
string = "You are out-of-character (OOC). "
string += "Use {w@ic{n to get back to the game, {whelp{n for more info."
self.caller.msg(string)
else:
self.caller = self.character # we have to put this back for normal look to work.
super(CmdOOCLook, self).func()
class CmdIC(MuxCommandOOC):
"""
Switch control to an object
Usage:
@ic <character>
Go in-character (IC) as a given Character.
This will attempt to "become" a different object assuming you have
the right to do so. Note that it's the PLAYER character that puppets
characters/objects and which needs to have the correct permission!
You cannot become an object that is already controlled by another
player. In principle <character> can be any in-game object as long
as you the player have access right to puppet it.
"""
key = "@ic"
locks = "cmd:all()" # must be all() or different puppeted objects won't be able to access it.
aliases = "@puppet"
help_category = "General"
def func(self):
"""
Simple puppet method
"""
caller = self.caller
old_character = self.character
new_character = None
if not self.args:
new_character = caller.db.last_puppet
if not new_character:
caller.msg("Usage: @ic <character>")
return
if not new_character:
# search for a matching character
new_character = search.objects(self.args, caller)
if new_character:
new_character = new_character[0]
else:
# the search method handles error messages etc.
return
if new_character.player:
if new_character.player == caller:
caller.msg("{RYou already are {c%s{n." % new_character.name)
else:
caller.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
if not new_character.access(caller, "puppet"):
caller.msg("{rYou may not become %s.{n" % new_character.name)
return
if caller.swap_character(new_character):
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
caller.db.last_puppet = old_character
if not new_character.location:
# this might be due to being hidden away at logout; check
loc = new_character.db.prelogout_location
if not loc: # still no location; use home
loc = new_character.home
new_character.location = loc
if new_character.location:
new_character.location.msg_contents("%s has entered the game." % new_character.key, exclude=[new_character])
new_character.location.at_object_receive(new_character, new_character.location)
new_character.execute_cmd("look")
else:
caller.msg("{rYou cannot become {C%s{n." % new_character.name)
class CmdOOC(MuxCommandOOC):
"""
@ooc - go ooc
Usage:
@ooc
Go out-of-character (OOC).
This will leave your current character and put you in a incorporeal OOC state.
"""
key = "@ooc"
locks = "cmd:all()" # this must be all(), or different puppeted objects won't be able to access it.
aliases = "@unpuppet"
help_category = "General"
def func(self):
"Implement function"
caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"):
caller = self.caller.player
if not caller.character:
string = "You are already OOC."
caller.msg(string)
return
caller.db.last_puppet = caller.character
# save location as if we were disconnecting from the game entirely.
if caller.character.location:
caller.character.location.msg_contents("%s has left the game." % caller.character.key, exclude=[caller.character])
caller.character.db.prelogout_location = caller.character.location
caller.character.location = None
# disconnect
caller.character.player = None
caller.character = None
caller.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look")
class CmdColorTest(MuxCommand):
"""
testing colors
@ -851,3 +706,235 @@ class CmdColorTest(MuxCommand):
self.caller.msg(string)
self.caller.msg("(e.g. %%c123 and %%cb123 also work)")
#------------------------------------------------------------
# OOC commands
#------------------------------------------------------------
class CmdOOCLook(MuxCommandOOC, CmdLook):
"""
ooc look
Usage:
look
Look in the ooc state.
"""
#This is an OOC version of the look command. Since a
#Player doesn't have an in-game existence, there is no
#concept of location or "self". If we are controlling
#a character, pass control over to normal look.
key = "look"
aliases = ["l", "ls"]
locks = "cmd:all()"
help_category = "General"
def look_target(self):
"Hook method for when an argument is given."
# caller is assumed to be a player object here.
caller = self.caller
key = self.args.lower()
chars = dict((utils.to_str(char.key.lower()), char) for char in caller.db._playable_characters)
looktarget = chars.get(key)
if looktarget:
caller.msg(looktarget.return_appearance(caller))
else:
caller.msg("No such character.")
return
def no_look_target(self):
"Hook method for default look without a specified target"
# caller is always a player at this point.
player = self.caller
sessid = self.sessid
# get all our characters
characters = player.db._playable_characters
string = "You are logged in as {g%s{n." % player.key
string += "\nUse {w@ic <character>{n to enter the game, {w@occ{n to get back here."
if characters:
string += "\n\nAvailable character%s:" % (len(characters) > 1 and "s" or "")
for char in characters:
csessid = char.sessid
if csessid:
# character is already puppeted
if player.get_session(csessid):
string += "\n - {G%s{n (played by you in another session)" % char.key
else:
string += "\n - {R%s{n (played by someone else)" % char.key
else:
# character is "free to puppet"
string += "\n - %s" % char.key
player.msg(string)
def func(self):
"implement the ooc look command"
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# An object of some type is calling. Use default look instead.
super(CmdOOCLook, self).func()
elif self.args:
self.look_target()
else:
self.no_look_target()
class CmdCharCreate(MuxCommandOOC):
"""
Create a character
Usage:
@charcreate <charname> [= desc]
Create a new character, optionally giving it a description.
"""
key = "@charcreate"
locks = "cmd:all()"
help_category = "General"
MAX_NR_CHARACTERS = 2
def func(self):
"create the new character"
player = self.caller
if not self.args:
player.msg("Usage: @charcreate <charname> [= description]")
return
key = self.lhs
desc = self.rhs
if player.db._playable_characters and len(player.db._playable_characters) >= self.MAX_NR_CHARACTERS:
player.msg("You may only create a maximum of %i characters." % self.MAX_NR_CHARACTERS)
return
# create the character
from src.objects.models import ObjectDB
default_home = ObjectDB.objects.get_id(settings.CHARACTER_DEFAULT_HOME)
typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT
new_character = create.create_object(typeclass, key=key, location=default_home,
home=default_home, permissions=permissions)
player.db._playable_characters.append(new_character)
if desc:
new_character.db.desc = desc
player.msg("Created new character %s." % new_character.key)
class CmdIC(MuxCommandOOC):
"""
Switch control to an object
Usage:
@ic <character>
Go in-character (IC) as a given Character.
This will attempt to "become" a different object assuming you have
the right to do so. Note that it's the PLAYER character that puppets
characters/objects and which needs to have the correct permission!
You cannot become an object that is already controlled by another
player. In principle <character> can be any in-game object as long
as you the player have access right to puppet it.
"""
key = "@ic"
locks = "cmd:all()" # must be all() or different puppeted objects won't be able to access it.
aliases = "@puppet"
help_category = "General"
def func(self):
"""
Simple puppet method
"""
caller = self.caller
sessid = self.sessid
old_character = self.character
new_character = None
if not self.args:
new_character = caller.db.last_puppet
if not new_character:
caller.msg("Usage: @ic <character>")
return
if not new_character:
# search for a matching character
new_character = search.objects(self.args, caller)
if new_character:
new_character = new_character[0]
else:
# the search method handles error messages etc.
return
# permission checks
if caller.get_character(sessid=sessid, character=new_character):
caller.msg("{RYou already act as {c%s{n." % new_character.name)
return
if new_character.player:
if new_character.sessid == sessid:
caller.msg("{RYou already act as {c%s{n from another session." % new_character.name)
return
elif not caller.get_character(character=new_character):
caller.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
if not new_character.access(caller, "puppet"):
caller.msg("{rYou may not become %s.{n" % new_character.name)
return
if caller.connect_character(new_character, sessid=sessid):
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
caller.db.last_puppet = old_character
if not new_character.location:
# this might be due to being hidden away at logout; check
loc = new_character.db.prelogout_location
if not loc: # still no location; use home
loc = new_character.home
new_character.location = loc
if new_character.location:
new_character.location.msg_contents("%s has entered the game." % new_character.key, exclude=[new_character])
new_character.location.at_object_receive(new_character, new_character.location)
new_character.execute_cmd("look")
else:
caller.msg("{rYou cannot become {C%s{n." % new_character.name)
class CmdOOC(MuxCommandOOC):
"""
@ooc - go ooc
Usage:
@ooc
Go out-of-character (OOC).
This will leave your current character and put you in a incorporeal OOC state.
"""
key = "@ooc"
locks = "cmd:all()" # this must be all(), or different puppeted objects won't be able to access it.
aliases = "@unpuppet"
help_category = "General"
def func(self):
"Implement function"
caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"):
caller = self.caller.player
old_char = caller.get_character(sessid=self.sessid)
if not old_char:
string = "You are already OOC."
caller.msg(string)
return
caller.db.last_puppet = old_char
# save location as if we were disconnecting from the game entirely.
if old_char.location:
old_char.location.msg_contents("%s has left the game." % old_char.key, exclude=[old_char])
old_char.db.prelogout_location = old_char.location
old_char.location = None
# disconnect
err = caller.disconnect_character(self.character)
caller.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look")

View file

@ -189,6 +189,6 @@ class MuxCommandOOC(MuxCommand):
self.caller = self.caller.player
elif hasattr(self.caller, "character"):
# caller was already a Player
self.character = self.caller.character
self.character = self.caller.get_character(sessid=self.sessid)
else:
self.character = None

View file

@ -146,15 +146,17 @@ class CmdPy(MuxCommand):
caller.msg(string)
return
# check if caller is a player
# import useful variables
import ev
available_vars = {'self':caller,
'me':caller,
'here':caller.location,
'here':hasattr(caller, "location") and caller.location or None,
'ev':ev,
'inherits_from':utils.inherits_from}
caller.msg(">>> %s" % pycode, data={"raw":True})
caller.msg(">>> %s" % pycode, data={"raw":True}, sessid=self.sessid)
mode = "eval"
try:
@ -183,7 +185,7 @@ class CmdPy(MuxCommand):
ret = "\n".join("{n<<< %s" % line for line in errlist if line)
if ret != None:
caller.msg(ret)
caller.msg(ret, sessid=self.sessid)
# helper function. Kept outside so it can be imported and run
# by other commands.

View file

@ -161,15 +161,19 @@ class CmdUnconnectedCreate(MuxCommand):
permissions = settings.PERMISSION_PLAYER_DEFAULT
try:
new_character = create.create_player(playername, None, password,
permissions=permissions,
character_typeclass=typeclass,
character_location=default_home,
character_home=default_home)
new_player = create.create_player(playername, None, password,
permissions=permissions)
# create character to go with player
new_character = create_object(character_typeclass, key=name,
location=default_home, home=default_home,
permissions=permissions)
# set list
new_player.db._playable_characters.append(new_character)
except Exception:
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin.")
return
new_player = new_character.player
# This needs to be called so the engine knows this player is logging in for the first time.
# (so it knows to call the right hooks during login later)

View file

@ -365,3 +365,12 @@ class ObjectManager(TypedObjectManager):
ScriptDB.objects.copy_script(script, new_obj=new_object.dbobj)
return new_object
def clear_all_sessids(self):
"""
Clear the db_sessid field of all objects having also the db_player field
set.
"""
self.filter(db_sessid__isnull=False).update(db_sessid=None)

View file

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ObjectDB.db_sessid'
db.add_column('objects_objectdb', 'db_sessid',
self.gf('django.db.models.fields.IntegerField')(null=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'ObjectDB.db_sessid'
db.delete_column('objects_objectdb', 'db_sessid')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'obj_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_objs': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'objs_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -21,7 +21,7 @@ from django.conf import settings
from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, hashid
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
from src.typeclasses.typeclass import TypeClass
from src.players.models import PlayerNick
from src.objects.manager import ObjectManager
@ -176,6 +176,9 @@ class ObjectDB(TypedObject):
# If this is a character object, the player is connected here.
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
help_text='a Player connected to this object, if any.')
# the session id associated with this player, if any
db_sessid = models.IntegerField(null=True, verbose_name="session id",
help_text="unique session id of connected Player, if any.")
# The location in the game world. Since this one is likely
# to change often, we set this with the 'location' property
# to transparently handle Typeclassing.
@ -191,7 +194,6 @@ class ObjectDB(TypedObject):
# database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, blank=True,
help_text="optional python path to a cmdset class.")
# Database manager
objects = ObjectManager()
@ -260,6 +262,28 @@ class ObjectDB(TypedObject):
del_field_cache(self, "player")
player = property(__player_get, __player_set, __player_del)
# sessid property (wraps db_sessid)
#@property
def __sessid_get(self):
"""
Getter. Allows for value = self.sessid. Since sessid
is directly related to self.player, we cannot have
a sessid without a player being connected (but the
opposite could be true).
"""
if not get_field_cache(self, "sessid"):
del_field_cache(self, "sessid")
return get_field_cache(self, "sessid")
#@sessid.setter
def __sessid_set(self, sessid):
"Setter. Allows for self.player = value"
set_field_cache(self, "sessid", sessid)
#@sessid.deleter
def __sessid_del(self):
"Deleter. Allows for del self.player"
del_field_cache(self, "sessid")
sessid = property(__sessid_get, __sessid_set, __sessid_del)
# location property (wraps db_location)
#@property
def __location_get(self):
@ -612,7 +636,7 @@ class ObjectDB(TypedObject):
# Execution/action methods
#
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls
@ -644,11 +668,11 @@ class ObjectDB(TypedObject):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
return cmdhandler.cmdhandler(_GA(self, "typeclass"), raw_string)
return cmdhandler.cmdhandler(_GA(self, "typeclass"), raw_string, sessid=sessid)
def msg(self, message, from_obj=None, data=None):
"""
Emits something to any sessions attached to the object.
Emits something to a session attached to the object.
message (str): The message to send
from_obj (obj): object that is sending.
@ -656,8 +680,8 @@ class ObjectDB(TypedObject):
be used by the protocol.
"""
if _GA(self, 'player'):
# note that we check the typeclass' msg, otherwise one couldn't overload it.
_GA(_GA(self, 'player'), "typeclass").msg(message, from_obj=from_obj, data=data)
# note that we must call the player *typeclass'* msg(), otherwise one couldn't overload it.
_GA(_GA(self, 'player'), "typeclass").msg(message, from_obj=from_obj, data=data, sessid=_GA(self, "sessid"))
def emit_to(self, message, from_obj=None, data=None):
"Deprecated. Alias for msg"

View file

@ -201,7 +201,7 @@ class Object(TypeClass):
ignore_errors=ignore_errors,
player=player)
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls
@ -209,6 +209,7 @@ class Object(TypeClass):
Argument:
raw_string (string) - raw command input
sessid (int) - id of session executing the command. This sets the sessid property on the command.
Returns Deferred - this is an asynchronous Twisted object that will
not fire until the command has actually finished executing. To overload
@ -219,7 +220,7 @@ class Object(TypeClass):
This return is not used at all by Evennia by default, but might be useful
for coders intending to implement some sort of nested command structure.
"""
return self.dbobj.execute_cmd(raw_string)
return self.dbobj.execute_cmd(raw_string, sessid=sessid)
def msg(self, message, from_obj=None, data=None):
"""

View file

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding M2M table for field db_objs on 'PlayerDB'
db.create_table('players_playerdb_db_objs', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('playerdb', models.ForeignKey(orm['players.playerdb'], null=False)),
('objectdb', models.ForeignKey(orm['objects.objectdb'], null=False))
))
db.create_unique('players_playerdb_db_objs', ['playerdb_id', 'objectdb_id'])
if not db.dry_run:
for player in orm.PlayerDB.objects.all():
# move data from old field to new
if player.db_obj:
player.db_objs.add(player.db_obj)
player.save()
def backwards(self, orm):
# Removing M2M table for field db_objs on 'PlayerDB'
db.delete_table('players_playerdb_db_objs')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerattribute': {
'Meta': {'object_name': 'PlayerAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'obj_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_objs': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'objs_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'players.playernick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['players']

View file

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'PlayerDB.db_obj'
db.delete_column('players_playerdb', 'db_obj_id')
def backwards(self, orm):
# Adding field 'PlayerDB.db_obj'
db.add_column('players_playerdb', 'db_obj',
self.gf('django.db.models.fields.related.ForeignKey')(related_name='obj_set', null=True, to=orm['objects.ObjectDB'], blank=True),
keep_default=False)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_sessid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerattribute': {
'Meta': {'object_name': 'PlayerAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_objs': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'objs_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'players.playernick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['players']

View file

@ -161,8 +161,9 @@ class PlayerDB(TypedObject):
help_text="The <I>User</I> object holds django-specific authentication for each Player. A unique User should be created and tied to each Player, the two should never be switched or changed around. The User will be deleted automatically when the Player is.")
# the in-game object connected to this player (if any).
# Use the property 'obj' to access.
db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True,
verbose_name="character", help_text='In-game object.')
db_objs = models.ManyToManyField("objects.ObjectDB", null=True,
verbose_name="characters", related_name="objs_set",
help_text="In-game objects.")
# 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")
@ -195,11 +196,12 @@ class PlayerDB(TypedObject):
# obj property (wraps db_obj)
#@property
def obj_get(self):
def objs_get(self):
"Getter. Allows for value = self.obj"
return get_field_cache(self, "obj")
return self.db_objs.all()
#return get_field_cache(self, "objs").all()
#@obj.setter
def obj_set(self, value):
def objs_set(self, value):
"Setter. Allows for self.obj = value"
global _TYPECLASS
if not _TYPECLASS:
@ -208,15 +210,19 @@ class PlayerDB(TypedObject):
if isinstance(value, _TYPECLASS):
value = value.dbobj
try:
set_field_cache(self, "obj", value)
self.db_objs.add(value)
self.save()
#set_field_cache(self, "obj", value)
except Exception:
logger.log_trace()
raise Exception("Cannot assign %s as a player object!" % value)
#@obj.deleter
def obj_del(self):
def objs_del(self):
"Deleter. Allows for del self.obj"
del_field_cache(self, "obj")
obj = property(obj_get, obj_set, obj_del)
self.db_objs.clear()
self.save()
#del_field_cache(self, "obj")
objs = property(objs_get, objs_set, objs_del)
# whereas the name 'obj' is consistent with the rest of the code,
# 'character' is a more intuitive property name, so we
@ -236,6 +242,7 @@ class PlayerDB(TypedObject):
"Deleter. Allows for del self.character"
del_field_cache(self, "obj")
character = property(character_get, character_set, character_del)
# cmdset_storage property
# This seems very sensitive to caching, so leaving it be for now /Griatch
#@property
@ -358,32 +365,219 @@ class PlayerDB(TypedObject):
# PlayerDB class access methods
#
def msg(self, outgoing_string, from_obj=None, data=None):
def msg(self, outgoing_string, from_obj=None, data=None, sessid=None):
"""
Evennia -> User
This is the main route for sending data back to the user from the server.
outgoing_string (string) - text data to send
from_obj (Object/Player) - source object of message to send
data (dict) - arbitrary data object containing eventual protocol-specific options
sessid - the session id of the session to send to. If not given, return to
all sessions connected to this player. This is usually only
relevant when using msg() directly from a player-command (from
a command on a Character, the character automatically stores and
handles the sessid).
"""
if from_obj:
# call hook
try:
_GA(from_obj, "at_msg_send")(outgoing_string, to_obj=self, data=data)
except Exception:
pass
if (_GA(self, "character") and not
_GA(self, "character").at_msg_receive(outgoing_string, from_obj=from_obj, data=data)):
# the at_msg_receive() hook may block receiving of certain messages
return
outgoing_string = utils.to_str(outgoing_string, force_string=True)
for session in _GA(self, 'sessions'):
session = None
if sessid:
session = _GA(self, "get_session")(sessid)
if session:
char = _GA(self, "get_character")(sessid=sessid)
if char and not char.at_msg_receive(outgoing_string, from_obj=from_obj, data=data):
# if hook returns false, cancel send
return
session.msg(outgoing_string, data)
else:
# if no session was specified, send to them all
for sess in _GA(self, 'get_all_sessions')():
sess.msg(outgoing_string, data)
def inmsg(self, ingoing_string, sessid):
"""
User -> Evennia
This is the reverse of msg - used by sessions to relay
messages/data back into the game. It is normally not called
from inside game code but only by the serversessions directly.
ingoing_string - text string (i.e. command string)
data - dictionary of optional data
session - session sending this data
"""
character = _GA(self, "get_character")(sessid=sessid)
if character:
# execute command on character
_GA(character, "execute_cmd")(ingoing_string, sessid=sessid)
else:
# a non-character session; this goes to player directly
_GA(self, "execute_cmd")(ingoing_string, sessid=sessid)
def connect_session_to_character(self, sessid, character, force=False):
"""
Connect the given session to a character through this player.
Note that this assumes the character has previously been
linked to the player using self.connect_character().
force - drop existing connection to other character
Returns True if connection was successful, False otherwise
"""
# first check if we already have a character tied to this session
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
if char:
if force and char != character:
_GA(self, "disconnect_session_from_character")(sessid)
else:
return
# do the connection
character.sessid = sessid
# update cache
cache = get_prop_cache(self, "_characters") or {}
cache[sessid] = character
set_prop_cache(self, "_characters", cache)
# call hooks
character.at_init()
if character:
# start (persistent) scripts on this object
#ScriptDB.objects.validate(obj=character)
pass
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
character.at_pre_login()
character.at_post_login()
return True
def disconnect_session_from_character(self, sessid):
"""
Disconnect a session from the characterm (still keeping the
connection to the Player)
returns the newly disconnected character, if it existed
"""
if not sessid:
return
char = _GA(self, "get_character")(sessid=sessid, return_dbobj=True)
if char:
# call hook before disconnecting
_GA(char.typeclass, "at_disconnect")()
del char.sessid
# update cache
cache = get_prop_cache(self, "_characters") or {}
if sessid in cache:
del cache[sessid]
set_prop_cache(self, "_characters", cache)
return char
def get_session(self, sessid):
"""
Return session with given sessid connected to this player.
"""
return SESSIONS.sessions_from_player(self, sessid=sessid)
def get_all_sessions(self):
"Return all sessions connected to this player"
return SESSIONS.sessions_from_player(self)
def get_character(self, sessid=None, character=None, return_dbobj=False):
"""
Get the character connected to this player and sessid
sessid - return character connected to this sessid,
character - return character if connected to this player, else None.
Combining both keywords will check the entire connection - if the
given session is currently connected to the given char. If no
keywords are given, returns all connected characters.
"""
cache = get_prop_cache(self, "_characters") or {}
if sessid:
# try to return a character with a given sessid
char = cache.get(sessid)
if not char:
char = _GA(self, "db_objs").filter(db_player=self, db_sessid=sessid) or None
if char:
char = char[0]
cache[sessid] = char
set_prop_cache(self, "_characters", cache)
if character:
return char and (char == character.dbobj and (return_dbobj and char or char.typeclass)) or None
return char and (return_dbobj and char or char.typeclass) or None
elif character:
char = _GA(self, "db_objs").filter(id=_GA(character.dbobj, "id"))
return char and (return_dbobj and char[0] or char[0].typeclass) or None
else:
# no sessid given - return all available characters
return list(return_dbobj and o or o.typeclass for o in self.db_objs.all())
def get_all_characters(self):
"""
Readability-wrapper for getting all characters
"""
return _GA(self, "get_character")(sessid=None, character=None)
def connect_character(self, character, sessid=None):
"""
Use the Player to connect a Character to a session. Note that
we don't do any access checks at this point. Note that if the
game was fully restarted (including the Portal), this must be
used, since sessids will have changed as players reconnect.
if sessid is given, also connect the sessid to the character.
"""
# first disconnect any other character from this session
char = character.dbobj
_GA(self, "disconnect_character")(char)
char.player = self
_GA(self, "db_objs").add(char)
_GA(self, "save")()
if sessid:
return _GA(self, "connect_session_to_character")(sessid=sessid, character=char)
return True
def disconnect_character(self, character):
"""
Disconnect a character from this player, either based
on sessid or by giving the character object directly
Returns newly disconnected character.
"""
if not character:
return
char = _GA(self, "get_character")(character=character, return_dbobj=True)
if char:
err = _GA(self, "disconnect_session_from_character")(char.sessid)
_GA(self, "db_objs").remove(char)
del char.player
self.save()
# clear cache
cache = get_prop_cache(self, "_characters") or {}
[cache.pop(sessid) for sessid,stored_char in cache.items() if stored_char==char]
set_prop_cache(self, "_characters", cache)
return char
def swap_character(self, new_character, delete_old_character=False):
def disconnect_all_characters(self):
for char in self.db_objs.all():
_GA(self, "disconnect_character")(char)
def swap_character(self, old_character, new_character):
"""
Swaps character, if possible
Swaps character between sessions, if possible
"""
return _GA(self, "__class__").objects.swap_character(self, new_character, delete_old_character=delete_old_character)
this_sessid = old_character.sessid
other_sessd = new_character.sessid
this_char = _GA(self, "disconnect_session_from_character")(this_sessid)
other_char = _GA(self, "disconnect_session_from_character")(other_sessid)
_GA(self, "connect_session_to_character")(this_sessid, other_char)
_GA(self, "connect_session_to_character")(other_sessid, this_char)
def delete(self, *args, **kwargs):
"Make sure to delete user also when deleting player - the two may never exist separately."
@ -401,7 +595,7 @@ class PlayerDB(TypedObject):
# Execution/action methods
#
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this player. This command transparently
lets its typeclass execute the command.
@ -417,7 +611,7 @@ class PlayerDB(TypedObject):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
return cmdhandler.cmdhandler(self.typeclass, raw_string)
return cmdhandler.cmdhandler(self.typeclass, raw_string, sessid=sessid)
def search(self, ostring, return_character=False):
"""

View file

@ -88,17 +88,21 @@ class Player(TypeClass):
## methods inherited from database model
def msg(self, outgoing_string, from_obj=None, data=None):
def msg(self, outgoing_string, from_obj=None, data=None, sessid=None):
"""
Evennia -> User
This is the main route for sending data back to the user from the server.
outgoing_string (string) - text data to send
from_obj (Object/Player) - source object of message to send
data (?) - arbitrary data object containing eventual protocol-specific options
"""
self.dbobj.msg(outgoing_string, from_obj=from_obj, data=data)
data (dict) - arbitrary data object containing eventual protocol-specific options
sessid - the session id of the session to send to. If not given, return to
all sessions connected to this player. This is usually only
relevant when using msg() directly from a player-command (from
a command on a Character, the character automatically stores and
handles the sessid).
"""
self.dbobj.msg(outgoing_string, from_obj=from_obj, data=data, sessid=sessid)
def swap_character(self, new_character, delete_old_character=False):
"""
@ -111,7 +115,7 @@ class Player(TypeClass):
"""
return self.dbobj.swap_character(new_character, delete_old_character=delete_old_character)
def execute_cmd(self, raw_string):
def execute_cmd(self, raw_string, sessid=None):
"""
Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls
@ -119,6 +123,7 @@ class Player(TypeClass):
Argument:
raw_string (string) - raw command input
sessid (int) - id of session executing the command. This sets the sessid property on the command
Returns Deferred - this is an asynchronous Twisted object that will
not fire until the command has actually finished executing. To overload
@ -129,7 +134,7 @@ class Player(TypeClass):
This return is not used at all by Evennia by default, but might be useful
for coders intending to implement some sort of nested command structure.
"""
return self.dbobj.execute_cmd(raw_string)
return self.dbobj.execute_cmd(raw_string, sessid=sessid)
def search(self, ostring, return_character=False):
"""
@ -241,8 +246,9 @@ class Player(TypeClass):
to store attributes all players should have,
like configuration values etc.
"""
pass
# set an attribute holding the characters this player has
lockstring = "attrread:perm(Admins);attredit:perm(Admins);attrcreate:perm(Admins)"
self.set_attribute("_playable_characters", [], lockstring=lockstring)
def at_init(self):
"""
@ -293,8 +299,7 @@ class Player(TypeClass):
"""
# Character.at_post_login also looks around. Only use
# this as a backup when logging in without a character
if not self.character:
self.execute_cmd("look")
self.execute_cmd("look")
def at_disconnect(self, reason=None):
"""

View file

@ -52,10 +52,8 @@ def create_objects():
# exists. Also, all properties (name, email, password, is_superuser)
# is inherited from the user so we don't specify it again here.
god_character = create.create_player(god_user.username, None, None,
user=god_user,
create_character=True,
character_typeclass=character_typeclass)
god_player = create.create_player(god_user.username, None, None, user=god_user)
god_character = create.create_object(character_typeclass, key=god_user.username)
god_character.id = 1
god_character.db.desc = _('This is User #1.')
@ -83,6 +81,8 @@ def create_objects():
god_character.location = limbo_obj
if not god_character.home:
god_character.home = limbo_obj
# store in list as playable character
god_player.db._playable_characters.append(god_character)
def create_channels():
"""

View file

@ -190,6 +190,9 @@ class Evennia(object):
from src.objects.models import ObjectDB
#from src.players.models import PlayerDB
# clear eventual lingering session storages
ObjectDB.objects.clear_all_sessids()
#update eventual changed defaults
self.update_defaults()
@ -288,7 +291,7 @@ class Evennia(object):
yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
yield [(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()]
yield ObjectDB.objects.clear_all_sessids()
ServerConfig.objects.conf("server_restart_mode", "reset")
if SERVER_STARTSTOP_MODULE:

View file

@ -57,10 +57,6 @@ class ServerSession(Session):
self.cmdset.update(init_mode=True)
return
character = self.get_character()
if character:
# start (persistent) scripts on this object
ScriptDB.objects.validate(obj=character)
def session_login(self, player):
"""
@ -91,38 +87,24 @@ class ServerSession(Session):
self.user.save()
# player init
#print "at_init() - player"
player.at_init()
# Check if this is the first time the *player* logs in
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
player.at_pre_login()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in
character.at_init()
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
player.at_pre_login()
self.log(_('Logged in: %(self)s') % {'self': self})
# start (persistent) scripts on this object
ScriptDB.objects.validate(obj=self.player.character)
#ScriptDB.objects.validate(obj=self.player.character)
#add session to connected list
self.sessionhandler.login(self)
# post-login hooks
player.at_post_login()
if character:
character.at_post_login()
def session_disconnect(self):
"""
@ -131,10 +113,10 @@ class ServerSession(Session):
accounts.
"""
if self.logged_in:
player = self.get_player()
character = self.get_character()
if character:
character.at_disconnect()
sessid = self.sessid
player = self.player
if player.get_character(sessid):
player.disconnect_session_from_character(sessid)
uaccount = player.user
uaccount.last_login = datetime.now()
uaccount.save()
@ -154,10 +136,7 @@ class ServerSession(Session):
Returns the in-game character associated with this session.
This returns the typeclass of the object.
"""
player = self.get_player()
if player:
return player.character
return None
return self.logged_in and self.player.get_character(self.sessid) or None
def log(self, message, channel=True):
"""
@ -193,20 +172,13 @@ class ServerSession(Session):
if str(command_string).strip() == IDLE_COMMAND:
self.update_session_counters(idle=True)
return
# all other inputs, including empty inputs
character = self.get_character()
if character:
character.execute_cmd(command_string)
if self.logged_in:
# the inmsg handler will relay to the right place
self.player.inmsg(command_string, self.sessid)
else:
if self.logged_in:
# there is no character, but we are logged in. Use player instead.
self.get_player().execute_cmd(command_string)
else:
# we are not logged in. Use the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string)
self.update_session_counters()
# we are not logged in. Use the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string, sessid=self.sessid)
def data_out(self, msg, data=None):
"""
@ -243,11 +215,11 @@ class ServerSession(Session):
func = OOB_FUNC_MODULE.__dict__.get(functuple[0])
if func:
try:
outdata[funcname] = func(oobkey, self, *functuple[1], **functuple[2])
outdata[functuple[0]] = func(oobkey, self, *functuple[1], **functuple[2])
except Exception:
logger.log_trace()
else:
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % functuple[0])
if outdata:
# we have a direct result - send it back right away
self.oob_data_out(outdata)

View file

@ -32,7 +32,8 @@ SSYNC = chr(8) # server session sync
from django.utils.translation import ugettext as _
SERVERNAME = settings.SERVERNAME
ALLOW_MULTISESSION = settings.ALLOW_MULTISESSION
#ALLOW_MULTISESSION = settings.ALLOW_MULTISESSION
MULTISESSION_MODE = settings.MULTISESSION_MODE
IDLE_TIMEOUT = settings.IDLE_TIMEOUT
#-----------------------------------------------------------
@ -163,8 +164,8 @@ class ServerSessionHandler(SessionHandler):
"""
# prep the session with player/user info
if not ALLOW_MULTISESSION:
# disconnect previous sessions.
if MULTISESSION_MODE == 0:
# disconnect all previous sessions.
self.disconnect_duplicate_sessions(session)
session.logged_in = True
# sync the portal to this session
@ -222,26 +223,30 @@ class ServerSessionHandler(SessionHandler):
def player_count(self):
"""
Get the number of connected players (not sessions since a player
may have more than one session connected if ALLOW_MULTISESSION is True)
Get the number of connected players (not sessions since a
player may have more than one session depending on settings).
Only logged-in players are counted here.
"""
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
def sessions_from_player(self, player):
def sessions_from_player(self, player, sessid=None):
"""
Given a player, return any matching sessions.
"""
uid = player.uid
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
if sessid:
session = self.sessions.get(sessid)
return session and session.logged_in and session.uid == uid and session or None
else:
return [session for session in self.sessions.values() if session.logged_in and session.uid == uid]
def sessions_from_character(self, character):
"""
Given a game character, return any matching sessions.
"""
player = character.player
if player:
return self.sessions_from_player(player)
sessid = character.sessid
if sessid:
return self.sessions.get(sessid)
return None
def announce_all(self, message):

View file

@ -62,12 +62,12 @@ SSL_ENABLED = False
SSL_PORTS = [4001]
# Interface addresses to listen to. If 0.0.0.0, listen to all.
SSL_INTERFACES = ['0.0.0.0']
# If multisessions are allowed, a user can log into the game
# from several different computers/clients at the same time.
# All feedback from the game will be echoed to all sessions.
# If false, only one session is allowed, all other are logged off
# when a new connects.
ALLOW_MULTISESSION = False
# Multisession modes allow a player (=account) to connect to the game simultaneously
# with multiple clients in various ways according to the set mode:
# 0 - no multisession - when a new session is connected, the old one is disconnected
# 1 - multiple sessions, one player, one character, each session getting the same data
# 2 - multiple sessions, one player, each session controlling different characters
MULTISESSION_MODE = 0
# The path that contains this settings.py file (no trailing slash).
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Path to the src directory containing the bulk of the codebase's code.

View file

@ -1311,7 +1311,7 @@ class TypedObject(SharedMemoryModel):
return False
return True
def set_attribute(self, attribute_name, new_value=None):
def set_attribute(self, attribute_name, new_value=None, lockstring=""):
"""
Sets an attribute on an object. Creates the attribute if need
be.
@ -1319,6 +1319,10 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name.
new_value: (python obj) The value to set the attribute to. If this is not
a str, the object will be stored as a pickle.
lockstring - this sets an access restriction on the attribute object. Note that
this is normally NOT checked - use the secureattr() access method
below to perform access-checked modification of attributes. Lock
types checked by secureattr are 'attrread','attredit','attrcreate'.
"""
attrib_obj = get_attr_cache(self, attribute_name)
if not attrib_obj:
@ -1332,6 +1336,8 @@ class TypedObject(SharedMemoryModel):
else:
# no match; create new attribute
attrib_obj = attrclass(db_key=attribute_name, db_obj=self)
if lockstring:
attrib_obj.locks.add(lockstring)
# re-set an old attribute value
try:
attrib_obj.value = new_value

View file

@ -97,6 +97,8 @@ class TypeClass(object):
"""
if propname == 'dbobj':
return _GA(self, 'dbobj')
if propname == 'typeclass':
return self
if propname.startswith('__') and propname.endswith('__'):
# python specials are parsed as-is (otherwise things like
# isinstance() fail to identify the typeclass)

View file

@ -53,7 +53,7 @@ _GA = object.__getattribute__
#
def create_object(typeclass, key=None, location=None,
home=None, player=None, permissions=None, locks=None,
home=None, permissions=None, locks=None,
aliases=None, destination=None, report_to=None):
"""
Create a new in-game object. Any game object is a combination
@ -116,11 +116,6 @@ def create_object(typeclass, key=None, location=None,
# from now on we can use the typeclass object
# as if it was the database object.
if player:
# link a player and the object together
new_object.player = player
player.obj = new_object
new_object.destination = destination
# call the hook method. This is where all at_creation
@ -397,11 +392,8 @@ def create_player(name, email, password,
typeclass=None,
is_superuser=False,
locks=None, permissions=None,
create_character=True, character_typeclass=None,
character_location=None, character_home=None,
player_dbobj=None, report_to=None):
"""
This creates a new player, handling the creation of the User
object and its associated Player object.
@ -500,6 +492,7 @@ def create_player(name, email, password,
# call hook method (may override default permissions)
new_player.at_player_creation()
print
# custom given arguments potentially overrides the hook
if permissions:
new_player.permissions = permissions
@ -509,19 +502,8 @@ def create_player(name, email, password,
if locks:
new_player.locks.add(locks)
# create *in-game* 'player' object
if create_character:
if not character_typeclass:
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
# creating the object automatically links the player
# and object together by player.obj <-> obj.player
new_character = create_object(character_typeclass, key=name,
location=character_location, home=character_location,
permissions=permissions,
player=new_player, report_to=report_to)
return new_character
return new_player
except Exception, e:
except Exception:
# a failure in creating the character
if not user:
# in there was a failure we clean up everything we can
@ -538,7 +520,7 @@ def create_player(name, email, password,
del new_character
except Exception:
pass
raise e
raise
# alias
player = create_player