This commit is contained in:
Griatch 2013-02-03 17:28:26 +01:00
commit 70b8f074f1
20 changed files with 905 additions and 253 deletions

View file

@ -41,7 +41,6 @@ from twisted.internet.defer import inlineCallbacks, returnValue
from django.conf import settings from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER from src.comms.channelhandler import CHANNELHANDLER
from src.utils import logger, utils from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.commands.cmdparser import at_multimatch_cmd from src.commands.cmdparser import at_multimatch_cmd
from src.utils.utils import string_suggestions from src.utils.utils import string_suggestions
@ -165,7 +164,7 @@ def get_and_merge_cmdsets(caller):
# Main command-handler function # Main command-handler function
@inlineCallbacks @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. 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.cmdstring = cmdname
cmd.args = args cmd.args = args
cmd.cmdset = cmdset cmd.cmdset = cmdset
cmd.sessid = sessid
cmd.raw_string = unformatted_raw_string cmd.raw_string = unformatted_raw_string
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):

View file

@ -23,6 +23,7 @@ class OOCCmdSet(CmdSet):
self.add(general.CmdOOCLook()) self.add(general.CmdOOCLook())
self.add(general.CmdIC()) self.add(general.CmdIC())
self.add(general.CmdOOC()) self.add(general.CmdOOC())
self.add(general.CmdCharCreate())
self.add(general.CmdEncoding()) self.add(general.CmdEncoding())
self.add(general.CmdQuit()) self.add(general.CmdQuit())
self.add(general.CmdPassword()) self.add(general.CmdPassword())
@ -34,6 +35,7 @@ class OOCCmdSet(CmdSet):
self.add(system.CmdReload()) self.add(system.CmdReload())
self.add(system.CmdReset()) self.add(system.CmdReset())
self.add(system.CmdShutdown()) self.add(system.CmdShutdown())
self.add(system.CmdPy())
# Admin commands # Admin commands
self.add(admin.CmdDelPlayer()) self.add(admin.CmdDelPlayer())

View file

@ -5,7 +5,7 @@ now.
import time import time
from django.conf import settings from django.conf import settings
from src.server.sessionhandler import SESSIONS 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.objects.models import ObjectNick as Nick
from src.commands.default.muxcommand import MuxCommand, MuxCommandOOC 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. 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. If you don't submit an encoding, the current encoding will be displayed instead.
""" """
key = "@encoding" key = "@encoding"
aliases = "@encode" aliases = "@encode"
@ -649,151 +649,6 @@ class CmdAccess(MuxCommand):
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms) string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
caller.msg(string) 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): class CmdColorTest(MuxCommand):
""" """
testing colors testing colors
@ -851,3 +706,239 @@ class CmdColorTest(MuxCommand):
self.caller.msg(string) self.caller.msg(string)
self.caller.msg("(e.g. %%c123 and %%cb123 also work)") 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
looktarget = caller.get_character(key=self.args)
if looktarget:
caller.msg(looktarget.return_appearance())
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.get_all_characters() # get all characters
string = "You are logged in as {g%s{n." % player.key
string += " Use {w@ic <character>{n to enter the game."
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 csessid == sessid:
# this should not happen.
string += "\n - {r%s{n (sessid not properly cleared! Contact an admin!)" % char.key
elif player.get_session(csessid):
string += "\n - {G%s{n (played by you in another session)"
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 not player.db._created_chars:
lockstring = "attrread:perm(Admins);attredit:perm(Admins);attrcreate:perm(Admins)"
player.set_attribute("_created_chars", [], lockstring=lockstring)
if len(player.db._created_chars) >= 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._created_chars.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
caller.disconnect_character(caller)
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 self.caller = self.caller.player
elif hasattr(self.caller, "character"): elif hasattr(self.caller, "character"):
# caller was already a Player # caller was already a Player
self.character = self.caller.character self.character = self.caller.get_character(sessid=self.sessid)
else: else:
self.character = None self.character = None

View file

@ -146,11 +146,13 @@ class CmdPy(MuxCommand):
caller.msg(string) caller.msg(string)
return return
# check if caller is a player
# import useful variables # import useful variables
import ev import ev
available_vars = {'self':caller, available_vars = {'self':caller,
'me':caller, 'me':caller,
'here':caller.location, 'here':hasattr(caller, "location") and caller.location or None,
'ev':ev, 'ev':ev,
'inherits_from':utils.inherits_from} 'inherits_from':utils.inherits_from}

View file

@ -365,3 +365,12 @@ class ObjectManager(TypedObjectManager):
ScriptDB.objects.copy_script(script, new_obj=new_object.dbobj) ScriptDB.objects.copy_script(script, new_obj=new_object.dbobj)
return new_object 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.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler 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_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.typeclasses.typeclass import TypeClass
from src.players.models import PlayerNick from src.players.models import PlayerNick
from src.objects.manager import ObjectManager from src.objects.manager import ObjectManager
@ -176,6 +176,9 @@ class ObjectDB(TypedObject):
# If this is a character object, the player is connected here. # If this is a character object, the player is connected here.
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player', db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
help_text='a Player connected to this object, if any.') 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 # The location in the game world. Since this one is likely
# to change often, we set this with the 'location' property # to change often, we set this with the 'location' property
# to transparently handle Typeclassing. # to transparently handle Typeclassing.
@ -191,7 +194,6 @@ class ObjectDB(TypedObject):
# database storage of persistant cmdsets. # database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, blank=True, db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, blank=True,
help_text="optional python path to a cmdset class.") help_text="optional python path to a cmdset class.")
# Database manager # Database manager
objects = ObjectManager() objects = ObjectManager()
@ -260,6 +262,28 @@ class ObjectDB(TypedObject):
del_field_cache(self, "player") del_field_cache(self, "player")
player = property(__player_get, __player_set, __player_del) 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) # location property (wraps db_location)
#@property #@property
def __location_get(self): def __location_get(self):
@ -612,7 +636,7 @@ class ObjectDB(TypedObject):
# Execution/action methods # 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 Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls lets its typeclass execute the command. Evennia also calls
@ -644,11 +668,11 @@ class ObjectDB(TypedObject):
if nick.db_nick in raw_list: if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1) raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break 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): 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 message (str): The message to send
from_obj (obj): object that is sending. from_obj (obj): object that is sending.
@ -656,8 +680,8 @@ class ObjectDB(TypedObject):
be used by the protocol. be used by the protocol.
""" """
if _GA(self, 'player'): if _GA(self, 'player'):
# note that we check the typeclass' msg, otherwise one couldn't overload it. # 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) _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): def emit_to(self, message, from_obj=None, data=None):
"Deprecated. Alias for msg" "Deprecated. Alias for msg"

View file

@ -201,7 +201,7 @@ class Object(TypeClass):
ignore_errors=ignore_errors, ignore_errors=ignore_errors,
player=player) player=player)
def execute_cmd(self, raw_string): def execute_cmd(self, raw_string, sessid=None):
""" """
Do something as this object. This command transparently Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls lets its typeclass execute the command. Evennia also calls
@ -209,6 +209,7 @@ class Object(TypeClass):
Argument: Argument:
raw_string (string) - raw command input 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 Returns Deferred - this is an asynchronous Twisted object that will
not fire until the command has actually finished executing. To overload 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 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. 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): 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.") 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). # the in-game object connected to this player (if any).
# Use the property 'obj' to access. # Use the property 'obj' to access.
db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True, db_objs = models.ManyToManyField("objects.ObjectDB", null=True,
verbose_name="character", help_text='In-game object.') verbose_name="characters", related_name="objs_set",
help_text="In-game objects.")
# store a connected flag here too, not just in sessionhandler. # store a connected flag here too, not just in sessionhandler.
# This makes it easier to track from various out-of-process locations # 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") 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) # obj property (wraps db_obj)
#@property #@property
def obj_get(self): def objs_get(self):
"Getter. Allows for value = self.obj" "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 #@obj.setter
def obj_set(self, value): def objs_set(self, value):
"Setter. Allows for self.obj = value" "Setter. Allows for self.obj = value"
global _TYPECLASS global _TYPECLASS
if not _TYPECLASS: if not _TYPECLASS:
@ -208,15 +210,19 @@ class PlayerDB(TypedObject):
if isinstance(value, _TYPECLASS): if isinstance(value, _TYPECLASS):
value = value.dbobj value = value.dbobj
try: try:
set_field_cache(self, "obj", value) self.db_objs.add(value)
self.save()
#set_field_cache(self, "obj", value)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
raise Exception("Cannot assign %s as a player object!" % value) raise Exception("Cannot assign %s as a player object!" % value)
#@obj.deleter #@obj.deleter
def obj_del(self): def objs_del(self):
"Deleter. Allows for del self.obj" "Deleter. Allows for del self.obj"
del_field_cache(self, "obj") self.db_objs.clear()
obj = property(obj_get, obj_set, obj_del) 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, # whereas the name 'obj' is consistent with the rest of the code,
# 'character' is a more intuitive property name, so we # 'character' is a more intuitive property name, so we
@ -236,6 +242,7 @@ class PlayerDB(TypedObject):
"Deleter. Allows for del self.character" "Deleter. Allows for del self.character"
del_field_cache(self, "obj") del_field_cache(self, "obj")
character = property(character_get, character_set, character_del) character = property(character_get, character_set, character_del)
# cmdset_storage property # cmdset_storage property
# This seems very sensitive to caching, so leaving it be for now /Griatch # This seems very sensitive to caching, so leaving it be for now /Griatch
#@property #@property
@ -358,32 +365,220 @@ class PlayerDB(TypedObject):
# PlayerDB class access methods # 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 Evennia -> User
This is the main route for sending data back to the user from the server. 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: if from_obj:
# call hook
try: try:
_GA(from_obj, "at_msg_send")(outgoing_string, to_obj=self, data=data) _GA(from_obj, "at_msg_send")(outgoing_string, to_obj=self, data=data)
except Exception: except Exception:
pass 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) 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) 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:
_GA(self, "disconnect_session_from_character")(char.sessid)
_GA(self, "db_objs").remove(char)
del char.player
del char.sessid
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): def delete(self, *args, **kwargs):
"Make sure to delete user also when deleting player - the two may never exist separately." "Make sure to delete user also when deleting player - the two may never exist separately."
@ -401,7 +596,7 @@ class PlayerDB(TypedObject):
# Execution/action methods # 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 Do something as this player. This command transparently
lets its typeclass execute the command. lets its typeclass execute the command.
@ -417,7 +612,7 @@ class PlayerDB(TypedObject):
if nick.db_nick in raw_list: if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1) raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break break
return cmdhandler.cmdhandler(self.typeclass, raw_string) return cmdhandler.cmdhandler(self.typeclass, raw_string, sessid=sessid)
def search(self, ostring, return_character=False): def search(self, ostring, return_character=False):
""" """

View file

@ -88,17 +88,21 @@ class Player(TypeClass):
## methods inherited from database model ## 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 Evennia -> User
This is the main route for sending data back to the user from the server. This is the main route for sending data back to the user from the server.
outgoing_string (string) - text data to send outgoing_string (string) - text data to send
from_obj (Object/Player) - source object of message to send from_obj (Object/Player) - source object of message to send
data (?) - arbitrary data object containing eventual protocol-specific options 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
self.dbobj.msg(outgoing_string, from_obj=from_obj, data=data) 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): 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) 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 Do something as this object. This command transparently
lets its typeclass execute the command. Evennia also calls lets its typeclass execute the command. Evennia also calls
@ -119,6 +123,7 @@ class Player(TypeClass):
Argument: Argument:
raw_string (string) - raw command input 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 Returns Deferred - this is an asynchronous Twisted object that will
not fire until the command has actually finished executing. To overload 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 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. 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): def search(self, ostring, return_character=False):
""" """
@ -293,8 +298,7 @@ class Player(TypeClass):
""" """
# Character.at_post_login also looks around. Only use # Character.at_post_login also looks around. Only use
# this as a backup when logging in without a character # 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): def at_disconnect(self, reason=None):
""" """

View file

@ -190,6 +190,9 @@ class Evennia(object):
from src.objects.models import ObjectDB from src.objects.models import ObjectDB
#from src.players.models import PlayerDB #from src.players.models import PlayerDB
# clear eventual lingering session storages
ObjectDB.objects.clear_all_sessids()
#update eventual changed defaults #update eventual changed defaults
self.update_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 [(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 [(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") ServerConfig.objects.conf("server_restart_mode", "reset")
if SERVER_STARTSTOP_MODULE: if SERVER_STARTSTOP_MODULE:

View file

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

View file

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

View file

@ -59,12 +59,12 @@ SSL_ENABLED = False
SSL_PORTS = [4001] SSL_PORTS = [4001]
# Interface addresses to listen to. If 0.0.0.0, listen to all. # Interface addresses to listen to. If 0.0.0.0, listen to all.
SSL_INTERFACES = ['0.0.0.0'] SSL_INTERFACES = ['0.0.0.0']
# If multisessions are allowed, a user can log into the game # Multisession modes allow a player (=account) to connect to the game simultaneously
# from several different computers/clients at the same time. # with multiple clients in various ways according to the set mode:
# All feedback from the game will be echoed to all sessions. # 0 - no multisession - when a new session is connected, the old one is disconnected
# If false, only one session is allowed, all other are logged off # 1 - multiple sessions, one player, one character, each session getting the same data
# when a new connects. # 2 - multiple sessions, one player, each session controlling different characters
ALLOW_MULTISESSION = False MULTISESSION_MODE = 0
# The path that contains this settings.py file (no trailing slash). # The path that contains this settings.py file (no trailing slash).
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 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. # 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 False
return True 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 Sets an attribute on an object. Creates the attribute if need
be. be.
@ -1319,6 +1319,10 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. attribute_name: (str) The attribute's name.
new_value: (python obj) The value to set the attribute to. If this is not 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. 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) attrib_obj = get_attr_cache(self, attribute_name)
if not attrib_obj: if not attrib_obj:
@ -1332,6 +1336,8 @@ class TypedObject(SharedMemoryModel):
else: else:
# no match; create new attribute # no match; create new attribute
attrib_obj = attrclass(db_key=attribute_name, db_obj=self) attrib_obj = attrclass(db_key=attribute_name, db_obj=self)
if lockstring:
attrib_obj.locks.add(lockstring)
# re-set an old attribute value # re-set an old attribute value
try: try:
attrib_obj.value = new_value attrib_obj.value = new_value

View file

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

View file

@ -500,6 +500,7 @@ def create_player(name, email, password,
# call hook method (may override default permissions) # call hook method (may override default permissions)
new_player.at_player_creation() new_player.at_player_creation()
print
# custom given arguments potentially overrides the hook # custom given arguments potentially overrides the hook
if permissions: if permissions:
new_player.permissions = permissions new_player.permissions = permissions
@ -521,7 +522,7 @@ def create_player(name, email, password,
player=new_player, report_to=report_to) player=new_player, report_to=report_to)
return new_character return new_character
return new_player return new_player
except Exception, e: except Exception:
# a failure in creating the character # a failure in creating the character
if not user: if not user:
# in there was a failure we clean up everything we can # in there was a failure we clean up everything we can
@ -538,7 +539,7 @@ def create_player(name, email, password,
del new_character del new_character
except Exception: except Exception:
pass pass
raise e raise
# alias # alias
player = create_player player = create_player