Merge.
This commit is contained in:
commit
965e236d9a
30 changed files with 979 additions and 634 deletions
|
|
@ -403,6 +403,7 @@ def error_check_python_modules():
|
||||||
deprstring = "settings.%s should be renamed to %s. If defaults are used, their path/classname must be updated (see src/settings_default.py)."
|
deprstring = "settings.%s should be renamed to %s. If defaults are used, their path/classname must be updated (see src/settings_default.py)."
|
||||||
if hasattr(settings, "CMDSET_DEFAULT"): raise DeprecationWarning(deprstring % ("CMDSET_DEFAULT", "CMDSET_CHARACTER"))
|
if hasattr(settings, "CMDSET_DEFAULT"): raise DeprecationWarning(deprstring % ("CMDSET_DEFAULT", "CMDSET_CHARACTER"))
|
||||||
if hasattr(settings, "CMDSET_OOC"): raise DeprecationWarning(deprstring % ("CMDSET_OOC", "CMDSET_PLAYER"))
|
if hasattr(settings, "CMDSET_OOC"): raise DeprecationWarning(deprstring % ("CMDSET_OOC", "CMDSET_PLAYER"))
|
||||||
|
if settings.WEBSERVER_ENABLED and not isinstance(settings.WEBSERVER_PORTS[0], tuple): raise DeprecationWarning("settings.WEBSERVER_PORTS must be on the form [(proxyport, serverport), ...]")
|
||||||
|
|
||||||
from src.commands import cmdsethandler
|
from src.commands import cmdsethandler
|
||||||
if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None): print "Warning: CMDSET_UNLOGGED failed to load!"
|
if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None): print "Warning: CMDSET_UNLOGGED failed to load!"
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ from django.conf import settings
|
||||||
|
|
||||||
# Setup access of the evennia server itself
|
# Setup access of the evennia server itself
|
||||||
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py')
|
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py')
|
||||||
PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal.py')
|
PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal/portal.py')
|
||||||
|
|
||||||
# Get logfile names
|
# Get logfile names
|
||||||
SERVER_LOGFILE = settings.SERVER_LOG_FILE
|
SERVER_LOGFILE = settings.SERVER_LOG_FILE
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
)
|
)
|
||||||
|
|
||||||
#deactivated temporarily, they cause empty objects to be created in admin
|
#deactivated temporarily, they cause empty objects to be created in admin
|
||||||
inlines = [AliasInline]#, ObjAttributeInline]
|
#inlines = [AliasInline, ObjAttributeInline]
|
||||||
|
|
||||||
|
|
||||||
# Custom modification to give two different forms wether adding or not.
|
# Custom modification to give two different forms wether adding or not.
|
||||||
|
|
@ -111,6 +111,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
return super(ObjectDBAdmin, self).get_form(request, obj, **defaults)
|
return super(ObjectDBAdmin, self).get_form(request, obj, **defaults)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.save()
|
||||||
if not change:
|
if not change:
|
||||||
# adding a new object
|
# adding a new object
|
||||||
obj = obj.typeclass
|
obj = obj.typeclass
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@ transparently through the decorating TypeClass.
|
||||||
import traceback
|
import traceback
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db.models.signals import post_init, pre_delete
|
||||||
|
|
||||||
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
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||||
|
from src.server.caches import attr_post_init, attr_pre_delete
|
||||||
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
|
||||||
|
|
@ -53,6 +55,7 @@ _HERE = _("here")
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class ObjAttribute(Attribute):
|
class ObjAttribute(Attribute):
|
||||||
"Attributes for ObjectDB objects."
|
"Attributes for ObjectDB objects."
|
||||||
db_obj = models.ForeignKey("ObjectDB")
|
db_obj = models.ForeignKey("ObjectDB")
|
||||||
|
|
@ -62,6 +65,10 @@ class ObjAttribute(Attribute):
|
||||||
verbose_name = "Object Attribute"
|
verbose_name = "Object Attribute"
|
||||||
verbose_name_plural = "Object Attributes"
|
verbose_name_plural = "Object Attributes"
|
||||||
|
|
||||||
|
# attach the cache handlers
|
||||||
|
post_init.connect(attr_post_init, sender=ObjAttribute, dispatch_uid="objattrcache")
|
||||||
|
pre_delete.connect(attr_pre_delete, sender=ObjAttribute, dispatch_uid="objattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Alias
|
# Alias
|
||||||
|
|
@ -241,7 +248,7 @@ class ObjectDB(TypedObject):
|
||||||
"Deleter. Allows for del self.aliases"
|
"Deleter. Allows for del self.aliases"
|
||||||
for alias in Alias.objects.filter(db_obj=self):
|
for alias in Alias.objects.filter(db_obj=self):
|
||||||
alias.delete()
|
alias.delete()
|
||||||
del_prop_cache(self, "_aliases")
|
#del_prop_cache(self, "_aliases")
|
||||||
aliases = property(__aliases_get, __aliases_set, __aliases_del)
|
aliases = property(__aliases_get, __aliases_set, __aliases_del)
|
||||||
|
|
||||||
# player property (wraps db_player)
|
# player property (wraps db_player)
|
||||||
|
|
@ -299,27 +306,16 @@ class ObjectDB(TypedObject):
|
||||||
del_field_cache(self, "sessid")
|
del_field_cache(self, "sessid")
|
||||||
sessid = property(__sessid_get, __sessid_set, __sessid_del)
|
sessid = property(__sessid_get, __sessid_set, __sessid_del)
|
||||||
|
|
||||||
# location property (wraps db_location)
|
def _db_location_handler(self, new_value, old_value=None):
|
||||||
#@property
|
"This handles changes to the db_location field."
|
||||||
def __location_get(self):
|
print "db_location_handler:", new_value, old_value
|
||||||
"Getter. Allows for value = self.location."
|
|
||||||
loc = get_field_cache(self, "location")
|
|
||||||
if loc:
|
|
||||||
return _GA(loc, "typeclass")
|
|
||||||
return None
|
|
||||||
#@location.setter
|
|
||||||
def __location_set(self, location):
|
|
||||||
"Setter. Allows for self.location = location"
|
|
||||||
try:
|
try:
|
||||||
old_loc = _GA(self, "location")
|
old_loc = old_value
|
||||||
if ObjectDB.objects.dbref(location):
|
# new_value can be dbref, typeclass or dbmodel
|
||||||
# dbref search
|
if ObjectDB.objects.dbref(new_value, reqhash=False):
|
||||||
loc = ObjectDB.objects.dbref_search(location)
|
loc = ObjectDB.objects.dbref_search(new_value)
|
||||||
loc = loc and _GA(loc, "dbobj")
|
# this should not fail if new_value is valid.
|
||||||
elif location and type(location) != ObjectDB:
|
loc = _GA(loc, "dbobj")
|
||||||
loc = _GA(location, "dbobj")
|
|
||||||
else:
|
|
||||||
loc = location
|
|
||||||
|
|
||||||
# recursive location check
|
# recursive location check
|
||||||
def is_loc_loop(loc, depth=0):
|
def is_loc_loop(loc, depth=0):
|
||||||
|
|
@ -333,32 +329,85 @@ class ObjectDB(TypedObject):
|
||||||
except RuntimeWarning: pass
|
except RuntimeWarning: pass
|
||||||
|
|
||||||
# set the location
|
# set the location
|
||||||
set_field_cache(self, "location", loc)
|
_SA(self, "db_location", loc)
|
||||||
# update the contents of each location
|
# update the contents of each location
|
||||||
if old_loc:
|
if old_loc:
|
||||||
_GA(_GA(old_loc, "dbobj"), "contents_update")()
|
_GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True)
|
||||||
if loc:
|
if loc:
|
||||||
_GA(loc, "contents_update")()
|
_GA(loc, "contents_update")(self)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
string = "Cannot set location, "
|
string = "Cannot set location, "
|
||||||
string += "%s.location = %s would create a location-loop." % (self.key, loc)
|
string += "%s.location = %s would create a location-loop." % (self.key, new_value)
|
||||||
_GA(self, "msg")(_(string))
|
_GA(self, "msg")(_(string))
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise RuntimeError(string)
|
raise RuntimeError(string)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
string = "Cannot set location (%s): " % str(e)
|
string = "Cannot set location (%s): " % str(e)
|
||||||
string += "%s is not a valid location." % location
|
string += "%s is not a valid location." % new_value
|
||||||
_GA(self, "msg")(_(string))
|
_GA(self, "msg")(_(string))
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise Exception(string)
|
raise Exception(string)
|
||||||
#@location.deleter
|
|
||||||
def __location_del(self):
|
## location property (wraps db_location)
|
||||||
"Deleter. Allows for del self.location"
|
##@property
|
||||||
_GA(self, "location").contents_update()
|
#def __location_get(self):
|
||||||
_SA(self, "db_location", None)
|
# "Getter. Allows for value = self.location."
|
||||||
_GA(self, "save")()
|
# loc = get_field_cache(self, "location")
|
||||||
del_field_cache(self, "location")
|
# if loc:
|
||||||
location = property(__location_get, __location_set, __location_del)
|
# return _GA(loc, "typeclass")
|
||||||
|
# return None
|
||||||
|
##@location.setter
|
||||||
|
#def __location_set(self, location):
|
||||||
|
# "Setter. Allows for self.location = location"
|
||||||
|
# try:
|
||||||
|
# old_loc = _GA(self, "location")
|
||||||
|
# if ObjectDB.objects.dbref(location):
|
||||||
|
# # dbref search
|
||||||
|
# loc = ObjectDB.objects.dbref_search(location)
|
||||||
|
# loc = loc and _GA(loc, "dbobj")
|
||||||
|
# elif location and type(location) != ObjectDB:
|
||||||
|
# loc = _GA(location, "dbobj")
|
||||||
|
# else:
|
||||||
|
# loc = location
|
||||||
|
|
||||||
|
# # recursive location check
|
||||||
|
# def is_loc_loop(loc, depth=0):
|
||||||
|
# "Recursively traverse the target location to make sure we are not in it."
|
||||||
|
# if depth > 10: return
|
||||||
|
# elif loc == self: raise RuntimeError
|
||||||
|
# elif loc == None: raise RuntimeWarning # just to quickly get out
|
||||||
|
# return is_loc_loop(_GA(loc, "db_location"), depth+1)
|
||||||
|
# # check so we don't create a location loop - if so, RuntimeError will be raised.
|
||||||
|
# try: is_loc_loop(loc)
|
||||||
|
# except RuntimeWarning: pass
|
||||||
|
|
||||||
|
# # set the location
|
||||||
|
# set_field_cache(self, "location", loc)
|
||||||
|
# # update the contents of each location
|
||||||
|
# if old_loc:
|
||||||
|
# _GA(_GA(old_loc, "dbobj"), "contents_update")()
|
||||||
|
# if loc:
|
||||||
|
# _GA(loc, "contents_update")()
|
||||||
|
# except RuntimeError:
|
||||||
|
# string = "Cannot set location, "
|
||||||
|
# string += "%s.location = %s would create a location-loop." % (self.key, loc)
|
||||||
|
# _GA(self, "msg")(_(string))
|
||||||
|
# logger.log_trace(string)
|
||||||
|
# raise RuntimeError(string)
|
||||||
|
# except Exception, e:
|
||||||
|
# string = "Cannot set location (%s): " % str(e)
|
||||||
|
# string += "%s is not a valid location." % location
|
||||||
|
# _GA(self, "msg")(_(string))
|
||||||
|
# logger.log_trace(string)
|
||||||
|
# raise Exception(string)
|
||||||
|
##@location.deleter
|
||||||
|
#def __location_del(self):
|
||||||
|
# "Deleter. Allows for del self.location"
|
||||||
|
# _GA(self, "location").contents_update()
|
||||||
|
# _SA(self, "db_location", None)
|
||||||
|
# _GA(self, "save")()
|
||||||
|
# del_field_cache(self, "location")
|
||||||
|
#location = property(__location_get, __location_set, __location_del)
|
||||||
|
|
||||||
# home property (wraps db_home)
|
# home property (wraps db_home)
|
||||||
#@property
|
#@property
|
||||||
|
|
@ -515,19 +564,26 @@ class ObjectDB(TypedObject):
|
||||||
exclude = make_iter(exclude)
|
exclude = make_iter(exclude)
|
||||||
if cont == None:
|
if cont == None:
|
||||||
cont = _GA(self, "contents_update")()
|
cont = _GA(self, "contents_update")()
|
||||||
return [obj for obj in cont if obj not in exclude]
|
return [obj for obj in cont.values() if obj not in exclude]
|
||||||
contents = property(contents_get)
|
contents = property(contents_get)
|
||||||
|
|
||||||
def contents_update(self):
|
def contents_update(self, obj=None, remove=False):
|
||||||
"""
|
"""
|
||||||
Updates the contents property of the object with a new
|
Updates the contents property of the object
|
||||||
object Called by
|
|
||||||
self.location_set.
|
|
||||||
|
|
||||||
obj -
|
add - object to add to content list
|
||||||
remove (true/false) - remove obj from content list
|
remove object to remove from content list
|
||||||
"""
|
"""
|
||||||
cont = ObjectDB.objects.get_contents(self)
|
cont = get_prop_cache(self, "_contents")
|
||||||
|
if not cont:
|
||||||
|
cont = {}
|
||||||
|
if obj:
|
||||||
|
if remove:
|
||||||
|
cont.pop(self.dbid, None)
|
||||||
|
else:
|
||||||
|
cont[self.dbid] = obj
|
||||||
|
else:
|
||||||
|
cont = dict((o.dbid, o) for o in ObjectDB.objects.get_contents(self))
|
||||||
set_prop_cache(self, "_contents", cont)
|
set_prop_cache(self, "_contents", cont)
|
||||||
return cont
|
return cont
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,12 @@ from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
|
from django.db.models.signals import post_init, pre_delete
|
||||||
|
|
||||||
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
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||||
|
from src.server.caches import attr_post_init, attr_pre_delete
|
||||||
|
|
||||||
from src.players import manager
|
from src.players import manager
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
||||||
|
|
@ -74,6 +77,9 @@ class PlayerAttribute(Attribute):
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Player Attribute"
|
verbose_name = "Player Attribute"
|
||||||
|
|
||||||
|
post_init.connect(attr_post_init, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
||||||
|
pre_delete.connect(attr_pre_delete, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Player Nicks
|
# Player Nicks
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ Common examples of uses of Scripts:
|
||||||
"""
|
"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models.signals import post_init, pre_delete
|
||||||
|
|
||||||
|
from src.server.caches import attr_post_init, attr_pre_delete
|
||||||
from src.typeclasses.models import Attribute, TypedObject
|
from src.typeclasses.models import Attribute, TypedObject
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from src.scripts.manager import ScriptManager
|
from src.scripts.manager import ScriptManager
|
||||||
|
|
@ -47,6 +50,9 @@ class ScriptAttribute(Attribute):
|
||||||
verbose_name = "Script Attribute"
|
verbose_name = "Script Attribute"
|
||||||
verbose_name_plural = "Script Attributes"
|
verbose_name_plural = "Script Attributes"
|
||||||
|
|
||||||
|
# attach cache handlers for attribute lookup
|
||||||
|
post_init.connect(attr_post_init, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
||||||
|
pre_delete.connect(attr_pre_delete, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,42 @@ Central caching module.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from sys import getsizeof
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from django.conf import settings
|
from django.dispatch import Signal
|
||||||
|
from django.core.cache import get_cache
|
||||||
_ENABLE_LOCAL_CACHES = settings.GAME_CACHE_TYPE
|
#from django.db.models.signals import pre_save, pre_delete, post_init
|
||||||
|
from src.server.models import ServerConfig
|
||||||
|
from src.utils.utils import uses_database, to_str
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_DA = object.__delattr__
|
_DA = object.__delattr__
|
||||||
|
|
||||||
# OOB hooks (OOB not yet functional, don't use yet)
|
#
|
||||||
_OOB_FIELD_UPDATE_HOOKS = defaultdict(dict)
|
# Open handles to the caches
|
||||||
_OOB_PROP_UPDATE_HOOKS = defaultdict(dict)
|
#
|
||||||
_OOB_ATTR_UPDATE_HOOKS = defaultdict(dict)
|
|
||||||
_OOB_NDB_UPDATE_HOOKS = defaultdict(dict)
|
|
||||||
_OOB_CUSTOM_UPDATE_HOOKS = defaultdict(dict)
|
|
||||||
|
|
||||||
_OOB_HANDLER = None # set by oob handler when it initializes
|
_FIELD_CACHE = get_cache("field_cache")
|
||||||
|
_ATTR_CACHE = get_cache("attr_cache")
|
||||||
|
#_PROP_CACHE = get_cache("prop_cache")
|
||||||
|
_PROP_CACHE = defaultdict(dict)
|
||||||
|
|
||||||
def hashid(obj):
|
# make sure caches are empty at startup
|
||||||
|
_FIELD_CACHE.clear()
|
||||||
|
_ATTR_CACHE.clear()
|
||||||
|
#_PROP_CACHE.clear()
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Cache key hash generation
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
if uses_database("mysql") and ServerConfig.objects.get_mysql_db_version() < '5.6.4':
|
||||||
|
# mysql <5.6.4 don't support millisecond precision
|
||||||
|
_DATESTRING = "%Y:%m:%d-%H:%M:%S:000000"
|
||||||
|
else:
|
||||||
|
_DATESTRING = "%Y:%m:%d-%H:%M:%S:%f"
|
||||||
|
|
||||||
|
def hashid(obj, suffix=""):
|
||||||
"""
|
"""
|
||||||
Returns a per-class unique that combines the object's
|
Returns a per-class unique that combines the object's
|
||||||
class name with its idnum and creation time. This makes this id unique also
|
class name with its idnum and creation time. This makes this id unique also
|
||||||
|
|
@ -35,12 +51,12 @@ def hashid(obj):
|
||||||
hid = _GA(obj, "_hashid")
|
hid = _GA(obj, "_hashid")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
date, idnum = _GA(obj, "db_date_created"), _GA(obj, "id")
|
date, idnum = _GA(obj, "db_date_created").strftime(_DATESTRING), _GA(obj, "id")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
# maybe a typeclass, try to go to dbobj
|
# maybe a typeclass, try to go to dbobj
|
||||||
obj = _GA(obj, "dbobj")
|
obj = _GA(obj, "dbobj")
|
||||||
date, idnum = _GA(obj, "db_date_created"), _GA(obj, "id")
|
date, idnum = _GA(obj, "db_date_created").strftime(_DATESTRING), _GA(obj, "id")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# this happens if hashing something like ndb. We have to
|
# this happens if hashing something like ndb. We have to
|
||||||
# rely on memory adressing in this case.
|
# rely on memory adressing in this case.
|
||||||
|
|
@ -48,289 +64,441 @@ def hashid(obj):
|
||||||
if not idnum or not date:
|
if not idnum or not date:
|
||||||
# this will happen if setting properties on an object which is not yet saved
|
# this will happen if setting properties on an object which is not yet saved
|
||||||
return None
|
return None
|
||||||
# build the hashid
|
|
||||||
hid = "%s-%s-#%s" % (_GA(obj, "__class__"), date, idnum)
|
hid = "%s-%s-#%s" % (_GA(obj, "__class__"), date, idnum)
|
||||||
|
hid = hid.replace(" ", "") # we have to remove the class-name's space, for memcached's sake
|
||||||
|
# we cache the object part of the hashid to avoid too many object lookups
|
||||||
_SA(obj, "_hashid", hid)
|
_SA(obj, "_hashid", hid)
|
||||||
return hid
|
# build the complete hashid
|
||||||
|
hid = "%s%s" % (hid, suffix)
|
||||||
|
return to_str(hid)
|
||||||
|
|
||||||
# oob helper functions
|
|
||||||
def register_oob_update_hook(obj,name, entity="field"):
|
#------------------------------------------------------------
|
||||||
|
# Cache callback handlers
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Field cache - makes sure to cache all database fields when
|
||||||
|
# they are saved, no matter from where.
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
# callback to pre_save signal (connected in src.server.server)
|
||||||
|
def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Register hook function to be called when field/property/db/ndb is updated.
|
Called at the beginning of the save operation. The save method
|
||||||
Given function will be called with function(obj, entityname, newvalue, *args, **kwargs)
|
must be called with the update_fields keyword in order to
|
||||||
entity - one of "field", "property", "db", "ndb" or "custom"
|
|
||||||
"""
|
"""
|
||||||
hid = hashid(obj)
|
if raw:
|
||||||
|
return
|
||||||
|
if update_fields:
|
||||||
|
# this is a list of strings at this point. We want field objects
|
||||||
|
update_fields = (instance._meta.get_field_by_name(field)[0] for field in update_fields)
|
||||||
|
else:
|
||||||
|
# meta.fields are already field objects
|
||||||
|
update_fields = instance._meta.fields
|
||||||
|
for field in update_fields:
|
||||||
|
fieldname = field.name
|
||||||
|
new_value = field.value_from_object(instance)
|
||||||
|
handlername = "_%s_handler" % fieldname
|
||||||
|
try:
|
||||||
|
handler = _GA(instance, handlername)
|
||||||
|
except AttributeError:
|
||||||
|
handler = None
|
||||||
|
hid = hashid(instance, "-%s" % fieldname)
|
||||||
|
if callable(handler):
|
||||||
|
old_value = _FIELD_CACHE.get(hid) if hid else None
|
||||||
|
# the handler may modify the stored value in various ways
|
||||||
|
# don't catch exceptions, the handler must work!
|
||||||
|
new_value = handler(new_value, old_value=old_value)
|
||||||
|
# we re-assign this to the field, save() will pick it up from there
|
||||||
|
_SA(instance, fieldname, new_value)
|
||||||
|
if hid:
|
||||||
|
# update cache
|
||||||
|
_FIELD_CACHE.set(hid, new_value)
|
||||||
|
|
||||||
|
# access method
|
||||||
|
|
||||||
|
def flush_field_cache():
|
||||||
|
"Clear the field cache"
|
||||||
|
_FIELD_CACHE.clear()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Attr cache - caching the attribute objects related to a given object to
|
||||||
|
# avoid lookups more than necessary (this makes Attributes en par in speed
|
||||||
|
# to any property).
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
# connected to post_init signal (connected in respective Attribute model)
|
||||||
|
def attr_post_init(sender, instance=None, **kwargs):
|
||||||
|
"Called when attribute is created or retrieved in connection with obj."
|
||||||
|
#print "attr_post_init:", instance, instance.db_obj, instance.db_key
|
||||||
|
hid = hashid(_GA(instance, "db_obj"), "-%s" % _GA(instance, "db_key"))
|
||||||
if hid:
|
if hid:
|
||||||
if entity == "field":
|
_ATTR_CACHE.set(hid, sender)
|
||||||
global _OOB_FIELD_UPDATE_HOOKS
|
|
||||||
_OOB_FIELD_UPDATE_HOOKS[hid][name] = True
|
|
||||||
return
|
|
||||||
elif entity == "property":
|
|
||||||
global _OOB_PROP_UPDATE_HOOKS
|
|
||||||
_OOB_PROP_UPDATE_HOOKS[hid][name] = True
|
|
||||||
elif entity == "db":
|
|
||||||
global _OOB_ATTR_UPDATE_HOOKS
|
|
||||||
_OOB_ATTR_UPDATE_HOOKS[hid][name] = True
|
|
||||||
elif entity == "ndb":
|
|
||||||
global _OOB_NDB_UPDATE_HOOKS
|
|
||||||
_OOB_NDB_UPDATE_HOOKS[hid][name] = True
|
|
||||||
elif entity == "custom":
|
|
||||||
global _OOB_CUSTOM_UPDATE_HOOKS
|
|
||||||
_OOB_CUSTOM_UPDATE_HOOKS[hid][name] = True
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def unregister_oob_update_hook(obj, name, entity="property"):
|
# connected to pre_delete signal (connected in respective Attribute model)
|
||||||
"""
|
def attr_pre_delete(sender, instance=None, **kwargs):
|
||||||
Un-register a report hook
|
"Called when attribute is deleted (del_attribute)"
|
||||||
"""
|
#print "attr_pre_delete:", instance, instance.db_obj, instance.db_key
|
||||||
hid = hashid(obj)
|
hid = hashid(_GA(instance, "db_obj"), "-%s" % _GA(instance, "db_key"))
|
||||||
if hid:
|
if hid:
|
||||||
global _OOB_FIELD_UPDATE_HOOKS,_OOB_PROP_UPDATE_HOOKS, _OOB_ATTR_UPDATE_HOOKS
|
#print "attr_pre_delete:", _GA(instance, "db_key")
|
||||||
global _OOB_CUSTOM_UPDATE_HOOKS, _OOB_NDB_UPDATE_HOOKS
|
_ATTR_CACHE.delete(hid)
|
||||||
if entity == "field" and name in _OOB_FIELD_UPDATE_HOOKS:
|
|
||||||
del _OOB_FIELD_UPDATE_HOOKS[hid][name]
|
|
||||||
elif entity == "property" and name in _OOB_PROP_UPDATE_HOOKS:
|
|
||||||
del _OOB_PROP_UPDATE_HOOKS[hid][name]
|
|
||||||
elif entity == "db" and name in _OOB_ATTR_UPDATE_HOOKS:
|
|
||||||
del _OOB_ATTR_UPDATE_HOOKS[hid][name]
|
|
||||||
elif entity == "ndb" and name in _OOB_NDB_UPDATE_HOOKS:
|
|
||||||
del _OOB_NDB_UPDATE_HOOKS[hid][name]
|
|
||||||
elif entity == "custom" and name in _OOB_CUSTOM_UPDATE_HOOKS:
|
|
||||||
del _OOB_CUSTOM_UPDATE_HOOKS[hid][name]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def call_ndb_hooks(obj, attrname, value):
|
# access methods
|
||||||
"""
|
|
||||||
No caching is done of ndb here, but
|
def get_attr_cache(obj, attrname):
|
||||||
we use this as a way to call OOB hooks.
|
"Called by get_attribute"
|
||||||
"""
|
hid = hashid(obj, "-%s" % attrname)
|
||||||
hid = hashid(obj)
|
_ATTR_CACHE.delete(hid)
|
||||||
|
return hid and _ATTR_CACHE.get(hid, None) or None
|
||||||
|
|
||||||
|
def set_attr_cache(attrobj):
|
||||||
|
"Set the attr cache manually; this can be used to update"
|
||||||
|
attr_post_init(None, instance=attrobj)
|
||||||
|
|
||||||
|
def flush_attr_cache():
|
||||||
|
"Clear attribute cache"
|
||||||
|
_ATTR_CACHE.clear()
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Property cache - this is a generic cache for properties stored on models.
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
# access methods
|
||||||
|
|
||||||
|
def get_prop_cache(obj, propname):
|
||||||
|
"retrieve data from cache"
|
||||||
|
hid = hashid(obj, "-%s" % propname)
|
||||||
if hid:
|
if hid:
|
||||||
oob_hook = _OOB_NDB_UPDATE_HOOKS[hid].get(attrname)
|
#print "get_prop_cache", hid, propname, _PROP_CACHE.get(hid, None)
|
||||||
if oob_hook:
|
return _PROP_CACHE[hid].get(propname, None)
|
||||||
oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
|
||||||
|
|
||||||
def call_custom_hooks(obj, attrname, value):
|
def set_prop_cache(obj, propname, propvalue):
|
||||||
"""
|
"Set property cache"
|
||||||
Custom handler for developers adding their own oob hooks, e.g. to
|
hid = hashid(obj, "-%s" % propname)
|
||||||
custom typeclass properties.
|
|
||||||
"""
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
if hid:
|
||||||
oob_hook = _OOB_CUSTOM_UPDATE_HOOKS[hid].get(attrname)
|
#print "set_prop_cache", propname, propvalue
|
||||||
if oob_hook:
|
_PROP_CACHE[hid][propname] = propvalue
|
||||||
oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
#_PROP_CACHE.set(hid, propvalue)
|
||||||
|
|
||||||
|
def del_prop_cache(obj, propname):
|
||||||
|
"Delete element from property cache"
|
||||||
|
hid = hashid(obj, "-%s" % propname)
|
||||||
|
if hid and propname in _PROP_CACHE[hid]:
|
||||||
|
del _PROP_CACHE[hid][propname]
|
||||||
|
#_PROP_CACHE.delete(hid)
|
||||||
|
|
||||||
if _ENABLE_LOCAL_CACHES:
|
def flush_prop_cache():
|
||||||
|
"Clear property cache"
|
||||||
# Cache stores
|
global _PROP_CACHE
|
||||||
_ATTR_CACHE = defaultdict(dict)
|
|
||||||
_FIELD_CACHE = defaultdict(dict)
|
|
||||||
_PROP_CACHE = defaultdict(dict)
|
_PROP_CACHE = defaultdict(dict)
|
||||||
|
#_PROP_CACHE.clear()
|
||||||
|
|
||||||
|
|
||||||
def get_cache_sizes():
|
#_ENABLE_LOCAL_CACHES = settings.GAME_CACHE_TYPE
|
||||||
"""
|
## oob helper functions
|
||||||
Get cache sizes, expressed in number of objects and memory size in MB
|
# OOB hooks (OOB not yet functional, don't use yet)
|
||||||
"""
|
#_OOB_FIELD_UPDATE_HOOKS = defaultdict(dict)
|
||||||
global _ATTR_CACHE, _FIELD_CACHE, _PROP_CACHE
|
#_OOB_PROP_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
#_OOB_ATTR_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
#_OOB_NDB_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
#_OOB_CUSTOM_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
#
|
||||||
|
#_OOB_HANDLER = None # set by oob handler when it initializes
|
||||||
|
#def register_oob_update_hook(obj,name, entity="field"):
|
||||||
|
# """
|
||||||
|
# Register hook function to be called when field/property/db/ndb is updated.
|
||||||
|
# Given function will be called with function(obj, entityname, newvalue, *args, **kwargs)
|
||||||
|
# entity - one of "field", "property", "db", "ndb" or "custom"
|
||||||
|
# """
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# if entity == "field":
|
||||||
|
# global _OOB_FIELD_UPDATE_HOOKS
|
||||||
|
# _OOB_FIELD_UPDATE_HOOKS[hid][name] = True
|
||||||
|
# return
|
||||||
|
# elif entity == "property":
|
||||||
|
# global _OOB_PROP_UPDATE_HOOKS
|
||||||
|
# _OOB_PROP_UPDATE_HOOKS[hid][name] = True
|
||||||
|
# elif entity == "db":
|
||||||
|
# global _OOB_ATTR_UPDATE_HOOKS
|
||||||
|
# _OOB_ATTR_UPDATE_HOOKS[hid][name] = True
|
||||||
|
# elif entity == "ndb":
|
||||||
|
# global _OOB_NDB_UPDATE_HOOKS
|
||||||
|
# _OOB_NDB_UPDATE_HOOKS[hid][name] = True
|
||||||
|
# elif entity == "custom":
|
||||||
|
# global _OOB_CUSTOM_UPDATE_HOOKS
|
||||||
|
# _OOB_CUSTOM_UPDATE_HOOKS[hid][name] = True
|
||||||
|
# else:
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
#def unregister_oob_update_hook(obj, name, entity="property"):
|
||||||
|
# """
|
||||||
|
# Un-register a report hook
|
||||||
|
# """
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# global _OOB_FIELD_UPDATE_HOOKS,_OOB_PROP_UPDATE_HOOKS, _OOB_ATTR_UPDATE_HOOKS
|
||||||
|
# global _OOB_CUSTOM_UPDATE_HOOKS, _OOB_NDB_UPDATE_HOOKS
|
||||||
|
# if entity == "field" and name in _OOB_FIELD_UPDATE_HOOKS:
|
||||||
|
# del _OOB_FIELD_UPDATE_HOOKS[hid][name]
|
||||||
|
# elif entity == "property" and name in _OOB_PROP_UPDATE_HOOKS:
|
||||||
|
# del _OOB_PROP_UPDATE_HOOKS[hid][name]
|
||||||
|
# elif entity == "db" and name in _OOB_ATTR_UPDATE_HOOKS:
|
||||||
|
# del _OOB_ATTR_UPDATE_HOOKS[hid][name]
|
||||||
|
# elif entity == "ndb" and name in _OOB_NDB_UPDATE_HOOKS:
|
||||||
|
# del _OOB_NDB_UPDATE_HOOKS[hid][name]
|
||||||
|
# elif entity == "custom" and name in _OOB_CUSTOM_UPDATE_HOOKS:
|
||||||
|
# del _OOB_CUSTOM_UPDATE_HOOKS[hid][name]
|
||||||
|
# else:
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
#def call_ndb_hooks(obj, attrname, value):
|
||||||
|
# """
|
||||||
|
# No caching is done of ndb here, but
|
||||||
|
# we use this as a way to call OOB hooks.
|
||||||
|
# """
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# oob_hook = _OOB_NDB_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
# if oob_hook:
|
||||||
|
# oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
||||||
|
#
|
||||||
|
#def call_custom_hooks(obj, attrname, value):
|
||||||
|
# """
|
||||||
|
# Custom handler for developers adding their own oob hooks, e.g. to
|
||||||
|
# custom typeclass properties.
|
||||||
|
# """
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# oob_hook = _OOB_CUSTOM_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
# if oob_hook:
|
||||||
|
# oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
attr_n = sum(len(dic) for dic in _ATTR_CACHE.values())
|
# # old cache system
|
||||||
attr_mb = sum(sum(getsizeof(obj) for obj in dic.values()) for dic in _ATTR_CACHE.values()) / 1024.0
|
#
|
||||||
|
# if _ENABLE_LOCAL_CACHES:
|
||||||
|
# # Cache stores
|
||||||
|
# _ATTR_CACHE = defaultdict(dict)
|
||||||
|
# _FIELD_CACHE = defaultdict(dict)
|
||||||
|
# _PROP_CACHE = defaultdict(dict)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def get_cache_sizes():
|
||||||
|
# """
|
||||||
|
# Get cache sizes, expressed in number of objects and memory size in MB
|
||||||
|
# """
|
||||||
|
# global _ATTR_CACHE, _FIELD_CACHE, _PROP_CACHE
|
||||||
|
#
|
||||||
|
# attr_n = sum(len(dic) for dic in _ATTR_CACHE.values())
|
||||||
|
# attr_mb = sum(sum(getsizeof(obj) for obj in dic.values()) for dic in _ATTR_CACHE.values()) / 1024.0
|
||||||
|
#
|
||||||
|
# field_n = sum(len(dic) for dic in _FIELD_CACHE.values())
|
||||||
|
# field_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _FIELD_CACHE.values()) / 1024.0
|
||||||
|
#
|
||||||
|
# prop_n = sum(len(dic) for dic in _PROP_CACHE.values())
|
||||||
|
# prop_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _PROP_CACHE.values()) / 1024.0
|
||||||
|
#
|
||||||
|
# return (attr_n, attr_mb), (field_n, field_mb), (prop_n, prop_mb)
|
||||||
|
#
|
||||||
|
# # on-object database field cache
|
||||||
|
# def get_field_cache(obj, name):
|
||||||
|
# "On-model Cache handler."
|
||||||
|
# global _FIELD_CACHE
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# try:
|
||||||
|
# return _FIELD_CACHE[hid][name]
|
||||||
|
# except KeyError:
|
||||||
|
# val = _GA(obj, "db_%s" % name)
|
||||||
|
# _FIELD_CACHE[hid][name] = val
|
||||||
|
# return val
|
||||||
|
# return _GA(obj, "db_%s" % name)
|
||||||
|
#
|
||||||
|
# def set_field_cache(obj, name, val):
|
||||||
|
# "On-model Cache setter. Also updates database."
|
||||||
|
# _SA(obj, "db_%s" % name, val)
|
||||||
|
# _GA(obj, "save")()
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# global _FIELD_CACHE
|
||||||
|
# _FIELD_CACHE[hid][name] = val
|
||||||
|
# # oob hook functionality
|
||||||
|
# if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||||
|
# _OOB_HANDLER.update(hid, name, val)
|
||||||
|
#
|
||||||
|
# def del_field_cache(obj, name):
|
||||||
|
# "On-model cache deleter"
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# _SA(obj, "db_%s" % name, None)
|
||||||
|
# _GA(obj, "save")()
|
||||||
|
# if hid:
|
||||||
|
# try:
|
||||||
|
# del _FIELD_CACHE[hid][name]
|
||||||
|
# except KeyError:
|
||||||
|
# pass
|
||||||
|
# if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||||
|
# _OOB_HANDLER.update(hid, name, None)
|
||||||
|
#
|
||||||
|
# def flush_field_cache(obj=None):
|
||||||
|
# "On-model cache resetter"
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# global _FIELD_CACHE
|
||||||
|
# if hid:
|
||||||
|
# try:
|
||||||
|
# del _FIELD_CACHE[hashid(obj)]
|
||||||
|
# except KeyError, e:
|
||||||
|
# pass
|
||||||
|
# else:
|
||||||
|
# # clean cache completely
|
||||||
|
# _FIELD_CACHE = defaultdict(dict)
|
||||||
|
#
|
||||||
|
# # on-object property cache (unrelated to database)
|
||||||
|
# # Note that the get/set_prop_cache handler do not actually
|
||||||
|
# # get/set the property "on" the object but only reads the
|
||||||
|
# # value to/from the cache. This is intended to be used
|
||||||
|
# # with a get/setter property on the object.
|
||||||
|
#
|
||||||
|
# def get_prop_cache(obj, name, default=None):
|
||||||
|
# "On-model Cache handler."
|
||||||
|
# global _PROP_CACHE
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# try:
|
||||||
|
# val = _PROP_CACHE[hid][name]
|
||||||
|
# except KeyError:
|
||||||
|
# return default
|
||||||
|
# _PROP_CACHE[hid][name] = val
|
||||||
|
# return val
|
||||||
|
# return default
|
||||||
|
#
|
||||||
|
# def set_prop_cache(obj, name, val):
|
||||||
|
# "On-model Cache setter. Also updates database."
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# global _PROP_CACHE
|
||||||
|
# _PROP_CACHE[hid][name] = val
|
||||||
|
# # oob hook functionality
|
||||||
|
# oob_hook = _OOB_PROP_UPDATE_HOOKS[hid].get(name)
|
||||||
|
# if oob_hook:
|
||||||
|
# oob_hook[0](obj.typeclass, name, val, *oob_hook[1], **oob_hook[2])
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def del_prop_cache(obj, name):
|
||||||
|
# "On-model cache deleter"
|
||||||
|
# try:
|
||||||
|
# del _PROP_CACHE[hashid(obj)][name]
|
||||||
|
# except KeyError:
|
||||||
|
# pass
|
||||||
|
# def flush_prop_cache(obj=None):
|
||||||
|
# "On-model cache resetter"
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# global _PROP_CACHE
|
||||||
|
# if hid:
|
||||||
|
# try:
|
||||||
|
# del _PROP_CACHE[hid]
|
||||||
|
# except KeyError,e:
|
||||||
|
# pass
|
||||||
|
# else:
|
||||||
|
# # clean cache completely
|
||||||
|
# _PROP_CACHE = defaultdict(dict)
|
||||||
|
#
|
||||||
|
# # attribute cache
|
||||||
|
#
|
||||||
|
# def get_attr_cache(obj, attrname):
|
||||||
|
# """
|
||||||
|
# Attribute cache store
|
||||||
|
# """
|
||||||
|
# return _ATTR_CACHE[hashid(obj)].get(attrname, None)
|
||||||
|
#
|
||||||
|
# def set_attr_cache(obj, attrname, attrobj):
|
||||||
|
# """
|
||||||
|
# Cache an attribute object
|
||||||
|
# """
|
||||||
|
# hid = hashid(obj)
|
||||||
|
# if hid:
|
||||||
|
# global _ATTR_CACHE
|
||||||
|
# _ATTR_CACHE[hid][attrname] = attrobj
|
||||||
|
# # oob hook functionality
|
||||||
|
# oob_hook = _OOB_ATTR_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
# if oob_hook:
|
||||||
|
# oob_hook[0](obj.typeclass, attrname, attrobj.value, *oob_hook[1], **oob_hook[2])
|
||||||
|
#
|
||||||
|
# def del_attr_cache(obj, attrname):
|
||||||
|
# """
|
||||||
|
# Remove attribute from cache
|
||||||
|
# """
|
||||||
|
# global _ATTR_CACHE
|
||||||
|
# try:
|
||||||
|
# _ATTR_CACHE[hashid(obj)][attrname].no_cache = True
|
||||||
|
# del _ATTR_CACHE[hashid(obj)][attrname]
|
||||||
|
# except KeyError:
|
||||||
|
# pass
|
||||||
|
#
|
||||||
|
# def flush_attr_cache(obj=None):
|
||||||
|
# """
|
||||||
|
# Flush the attribute cache for this object.
|
||||||
|
# """
|
||||||
|
# global _ATTR_CACHE
|
||||||
|
# if obj:
|
||||||
|
# for attrobj in _ATTR_CACHE[hashid(obj)].values():
|
||||||
|
# attrobj.no_cache = True
|
||||||
|
# del _ATTR_CACHE[hashid(obj)]
|
||||||
|
# else:
|
||||||
|
# # clean cache completely
|
||||||
|
# for objcache in _ATTR_CACHE.values():
|
||||||
|
# for attrobj in objcache.values():
|
||||||
|
# attrobj.no_cache = True
|
||||||
|
# _ATTR_CACHE = defaultdict(dict)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def flush_obj_caches(obj=None):
|
||||||
|
# "Clean all caches on this object"
|
||||||
|
# flush_field_cache(obj)
|
||||||
|
# flush_prop_cache(obj)
|
||||||
|
# flush_attr_cache(obj)
|
||||||
|
#
|
||||||
|
|
||||||
field_n = sum(len(dic) for dic in _FIELD_CACHE.values())
|
#else:
|
||||||
field_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _FIELD_CACHE.values()) / 1024.0
|
|
||||||
|
|
||||||
prop_n = sum(len(dic) for dic in _PROP_CACHE.values())
|
|
||||||
prop_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _PROP_CACHE.values()) / 1024.0
|
|
||||||
|
|
||||||
return (attr_n, attr_mb), (field_n, field_mb), (prop_n, prop_mb)
|
|
||||||
|
|
||||||
# on-object database field cache
|
|
||||||
def get_field_cache(obj, name):
|
|
||||||
"On-model Cache handler."
|
|
||||||
global _FIELD_CACHE
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
|
||||||
try:
|
|
||||||
return _FIELD_CACHE[hid][name]
|
|
||||||
except KeyError:
|
|
||||||
val = _GA(obj, "db_%s" % name)
|
|
||||||
_FIELD_CACHE[hid][name] = val
|
|
||||||
return val
|
|
||||||
return _GA(obj, "db_%s" % name)
|
|
||||||
|
|
||||||
def set_field_cache(obj, name, val):
|
|
||||||
"On-model Cache setter. Also updates database."
|
|
||||||
_SA(obj, "db_%s" % name, val)
|
|
||||||
_GA(obj, "save")()
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
|
||||||
global _FIELD_CACHE
|
|
||||||
_FIELD_CACHE[hid][name] = val
|
|
||||||
# oob hook functionality
|
|
||||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
|
||||||
_OOB_HANDLER.update(hid, name, val)
|
|
||||||
|
|
||||||
def del_field_cache(obj, name):
|
|
||||||
"On-model cache deleter"
|
|
||||||
hid = hashid(obj)
|
|
||||||
_SA(obj, "db_%s" % name, None)
|
|
||||||
_GA(obj, "save")()
|
|
||||||
if hid:
|
|
||||||
try:
|
|
||||||
del _FIELD_CACHE[hid][name]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
|
||||||
_OOB_HANDLER.update(hid, name, None)
|
|
||||||
|
|
||||||
def flush_field_cache(obj=None):
|
|
||||||
"On-model cache resetter"
|
|
||||||
hid = hashid(obj)
|
|
||||||
global _FIELD_CACHE
|
|
||||||
if hid:
|
|
||||||
del _FIELD_CACHE[hashid(obj)]
|
|
||||||
else:
|
|
||||||
# clean cache completely
|
|
||||||
_FIELD_CACHE = defaultdict(dict)
|
|
||||||
|
|
||||||
# on-object property cache (unrelated to database)
|
|
||||||
# Note that the get/set_prop_cache handler do not actually
|
|
||||||
# get/set the property "on" the object but only reads the
|
|
||||||
# value to/from the cache. This is intended to be used
|
|
||||||
# with a get/setter property on the object.
|
|
||||||
|
|
||||||
def get_prop_cache(obj, name, default=None):
|
|
||||||
"On-model Cache handler."
|
|
||||||
global _PROP_CACHE
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
|
||||||
try:
|
|
||||||
val = _PROP_CACHE[hid][name]
|
|
||||||
except KeyError:
|
|
||||||
return default
|
|
||||||
_PROP_CACHE[hid][name] = val
|
|
||||||
return val
|
|
||||||
return default
|
|
||||||
|
|
||||||
def set_prop_cache(obj, name, val):
|
|
||||||
"On-model Cache setter. Also updates database."
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
|
||||||
global _PROP_CACHE
|
|
||||||
_PROP_CACHE[hid][name] = val
|
|
||||||
# oob hook functionality
|
|
||||||
oob_hook = _OOB_PROP_UPDATE_HOOKS[hid].get(name)
|
|
||||||
if oob_hook:
|
|
||||||
oob_hook[0](obj.typeclass, name, val, *oob_hook[1], **oob_hook[2])
|
|
||||||
|
|
||||||
|
|
||||||
def del_prop_cache(obj, name):
|
|
||||||
"On-model cache deleter"
|
|
||||||
try:
|
|
||||||
del _PROP_CACHE[hashid(obj)][name]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
def flush_prop_cache(obj=None):
|
|
||||||
"On-model cache resetter"
|
|
||||||
hid = hashid(obj)
|
|
||||||
global _PROP_CACHE
|
|
||||||
if hid:
|
|
||||||
del _PROP_CACHE[hashid(obj)]
|
|
||||||
else:
|
|
||||||
# clean cache completely
|
|
||||||
_PROP_CACHE = defaultdict(dict)
|
|
||||||
|
|
||||||
# attribute cache
|
|
||||||
|
|
||||||
def get_attr_cache(obj, attrname):
|
|
||||||
"""
|
|
||||||
Attribute cache store
|
|
||||||
"""
|
|
||||||
return _ATTR_CACHE[hashid(obj)].get(attrname, None)
|
|
||||||
|
|
||||||
def set_attr_cache(obj, attrname, attrobj):
|
|
||||||
"""
|
|
||||||
Cache an attribute object
|
|
||||||
"""
|
|
||||||
hid = hashid(obj)
|
|
||||||
if hid:
|
|
||||||
global _ATTR_CACHE
|
|
||||||
_ATTR_CACHE[hid][attrname] = attrobj
|
|
||||||
# oob hook functionality
|
|
||||||
oob_hook = _OOB_ATTR_UPDATE_HOOKS[hid].get(attrname)
|
|
||||||
if oob_hook:
|
|
||||||
oob_hook[0](obj.typeclass, attrname, attrobj.value, *oob_hook[1], **oob_hook[2])
|
|
||||||
|
|
||||||
def del_attr_cache(obj, attrname):
|
|
||||||
"""
|
|
||||||
Remove attribute from cache
|
|
||||||
"""
|
|
||||||
global _ATTR_CACHE
|
|
||||||
try:
|
|
||||||
_ATTR_CACHE[hashid(obj)][attrname].no_cache = True
|
|
||||||
del _ATTR_CACHE[hashid(obj)][attrname]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def flush_attr_cache(obj=None):
|
|
||||||
"""
|
|
||||||
Flush the attribute cache for this object.
|
|
||||||
"""
|
|
||||||
global _ATTR_CACHE
|
|
||||||
if obj:
|
|
||||||
for attrobj in _ATTR_CACHE[hashid(obj)].values():
|
|
||||||
attrobj.no_cache = True
|
|
||||||
del _ATTR_CACHE[hashid(obj)]
|
|
||||||
else:
|
|
||||||
# clean cache completely
|
|
||||||
for objcache in _ATTR_CACHE.values():
|
|
||||||
for attrobj in objcache.values():
|
|
||||||
attrobj.no_cache = True
|
|
||||||
_ATTR_CACHE = defaultdict(dict)
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
# local caches disabled. Use simple pass-through replacements
|
# local caches disabled. Use simple pass-through replacements
|
||||||
|
|
||||||
def get_cache_sizes():
|
def get_cache_sizes():
|
||||||
return (0, 0), (0, 0), (0, 0)
|
return (0, 0), (0, 0), (0, 0)
|
||||||
def get_field_cache(obj, name):
|
def get_field_cache(obj, name):
|
||||||
return _GA(obj, "db_%s" % name)
|
return _GA(obj, "db_%s" % name)
|
||||||
def set_field_cache(obj, name, val):
|
def set_field_cache(obj, name, val):
|
||||||
_SA(obj, "db_%s" % name, val)
|
_SA(obj, "db_%s" % name, val)
|
||||||
_GA(obj, "save")()
|
_GA(obj, "save")()
|
||||||
hid = hashid(obj)
|
#hid = hashid(obj)
|
||||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
#if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||||
_OOB_HANDLER.update(hid, name, val)
|
# _OOB_HANDLER.update(hid, name, val)
|
||||||
def del_field_cache(obj, name):
|
def del_field_cache(obj, name):
|
||||||
_SA(obj, "db_%s" % name, None)
|
_SA(obj, "db_%s" % name, None)
|
||||||
_GA(obj, "save")()
|
_GA(obj, "save")()
|
||||||
hid = hashid(obj)
|
#hid = hashid(obj)
|
||||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
#if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||||
_OOB_HANDLER.update(hid, name, None)
|
# _OOB_HANDLER.update(hid, name, None)
|
||||||
def flush_field_cache(obj=None):
|
#def flush_field_cache(obj=None):
|
||||||
pass
|
# pass
|
||||||
# these should get oob handlers when oob is implemented.
|
# these should get oob handlers when oob is implemented.
|
||||||
def get_prop_cache(obj, name, default=None):
|
#def get_prop_cache(obj, name, default=None):
|
||||||
return None
|
# return None
|
||||||
def set_prop_cache(obj, name, val):
|
#def set_prop_cache(obj, name, val):
|
||||||
pass
|
# pass
|
||||||
def del_prop_cache(obj, name):
|
#def del_prop_cache(obj, name):
|
||||||
pass
|
# pass
|
||||||
def flush_prop_cache(obj=None):
|
#def flush_prop_cache(obj=None):
|
||||||
pass
|
# pass
|
||||||
def get_attr_cache(obj, attrname):
|
#def get_attr_cache(obj, attrname):
|
||||||
return None
|
# return None
|
||||||
def set_attr_cache(obj, attrname, attrobj):
|
#def set_attr_cache(obj, attrname, attrobj):
|
||||||
pass
|
# pass
|
||||||
def del_attr_cache(obj, attrname):
|
#def del_attr_cache(obj, attrname):
|
||||||
pass
|
# passk
|
||||||
def flush_attr_cache(obj=None):
|
#def flush_attr_cache(obj=None):
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
|
|
|
||||||
0
src/server/portal/__init__.py
Normal file
0
src/server/portal/__init__.py
Normal file
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
MCCP - Mud Client Compression Protocol
|
MCCP - Mud Client Compression Protocol
|
||||||
|
|
||||||
The implements the MCCP v2 telnet protocol as per
|
This implements the MCCP v2 telnet protocol as per
|
||||||
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
||||||
compress data when sending to supporting clients, reducing bandwidth
|
compress data when sending to supporting clients, reducing bandwidth
|
||||||
by 70-90%.. The compression is done using Python's builtin zlib
|
by 70-90%.. The compression is done using Python's builtin zlib
|
||||||
|
|
@ -11,15 +11,15 @@ import sys
|
||||||
import os
|
import os
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
# For Windows batchfile we need an extra path insertion here.
|
# For Windows batchfile we need an extra path insertion here.
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
|
||||||
os.path.dirname(os.path.abspath(__file__)))))
|
os.path.dirname(os.path.abspath(__file__))))))
|
||||||
|
|
||||||
from twisted.application import internet, service
|
from twisted.application import internet, service
|
||||||
from twisted.internet import protocol, reactor
|
from twisted.internet import protocol, reactor
|
||||||
from twisted.web import server, static
|
from twisted.web import server
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.utils.utils import get_evennia_version, mod_import, make_iter
|
from src.utils.utils import get_evennia_version, mod_import, make_iter
|
||||||
from src.server.sessionhandler import PORTAL_SESSIONS
|
from src.server.portal.portalsessionhandler import PORTAL_SESSIONS
|
||||||
|
|
||||||
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
||||||
|
|
||||||
|
|
@ -55,7 +55,8 @@ WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||||
|
|
||||||
AMP_HOST = settings.AMP_HOST
|
AMP_HOST = settings.AMP_HOST
|
||||||
AMP_PORT = settings.AMP_PORT
|
AMP_PORT = settings.AMP_PORT
|
||||||
AMP_ENABLED = AMP_HOST and AMP_PORT
|
AMP_INTERFACE = settings.AMP_INTERFACE
|
||||||
|
AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -156,6 +157,8 @@ if AMP_ENABLED:
|
||||||
|
|
||||||
from src.server import amp
|
from src.server import amp
|
||||||
|
|
||||||
|
print ' amp (to Server): %s' % AMP_PORT
|
||||||
|
|
||||||
factory = amp.AmpClientFactory(PORTAL)
|
factory = amp.AmpClientFactory(PORTAL)
|
||||||
amp_client = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
|
amp_client = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
|
||||||
amp_client.setName('evennia_amp')
|
amp_client.setName('evennia_amp')
|
||||||
|
|
@ -168,7 +171,7 @@ if TELNET_ENABLED:
|
||||||
|
|
||||||
# Start telnet game connections
|
# Start telnet game connections
|
||||||
|
|
||||||
from src.server import telnet
|
from src.server.portal import telnet
|
||||||
|
|
||||||
for interface in TELNET_INTERFACES:
|
for interface in TELNET_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -192,7 +195,7 @@ if SSL_ENABLED:
|
||||||
|
|
||||||
# Start SSL game connection (requires PyOpenSSL).
|
# Start SSL game connection (requires PyOpenSSL).
|
||||||
|
|
||||||
from src.server import ssl
|
from src.server.portal import ssl
|
||||||
|
|
||||||
for interface in SSL_INTERFACES:
|
for interface in SSL_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -218,7 +221,7 @@ if SSH_ENABLED:
|
||||||
|
|
||||||
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
||||||
|
|
||||||
from src.server import ssh
|
from src.server.portal import ssh
|
||||||
|
|
||||||
for interface in SSH_INTERFACES:
|
for interface in SSH_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -240,29 +243,9 @@ if SSH_ENABLED:
|
||||||
|
|
||||||
if WEBSERVER_ENABLED:
|
if WEBSERVER_ENABLED:
|
||||||
|
|
||||||
# Start a django-compatible webserver.
|
# Start a reverse proxy to relay data to the Server-side webserver
|
||||||
|
|
||||||
from twisted.python import threadpool
|
from twisted.web import proxy
|
||||||
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
|
||||||
|
|
||||||
# start a thread pool and define the root url (/) as a wsgi resource
|
|
||||||
# recognized by Django
|
|
||||||
threads = threadpool.ThreadPool()
|
|
||||||
web_root = DjangoWebRoot(threads)
|
|
||||||
# point our media resources to url /media
|
|
||||||
web_root.putChild("media", static.File(settings.MEDIA_ROOT))
|
|
||||||
|
|
||||||
webclientstr = ""
|
|
||||||
if WEBCLIENT_ENABLED:
|
|
||||||
# create ajax client processes at /webclientdata
|
|
||||||
from src.server.webclient import WebClient
|
|
||||||
webclient = WebClient()
|
|
||||||
webclient.sessionhandler = PORTAL_SESSIONS
|
|
||||||
web_root.putChild("webclientdata", webclient)
|
|
||||||
|
|
||||||
webclientstr = "/client"
|
|
||||||
|
|
||||||
web_site = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
|
|
||||||
|
|
||||||
for interface in WEBSERVER_INTERFACES:
|
for interface in WEBSERVER_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -271,14 +254,23 @@ if WEBSERVER_ENABLED:
|
||||||
ifacestr = ""
|
ifacestr = ""
|
||||||
if interface != '0.0.0.0' or len(WEBSERVER_INTERFACES) > 1:
|
if interface != '0.0.0.0' or len(WEBSERVER_INTERFACES) > 1:
|
||||||
ifacestr = "-%s" % interface
|
ifacestr = "-%s" % interface
|
||||||
for port in WEBSERVER_PORTS:
|
for proxyport, serverport in WEBSERVER_PORTS:
|
||||||
pstring = "%s:%s" % (ifacestr, port)
|
pstring = "%s:%s<->%s" % (ifacestr, proxyport, serverport)
|
||||||
# create the webserver
|
web_root = proxy.ReverseProxyResource('127.0.0.1', serverport, '')
|
||||||
webserver = WSGIWebServer(threads, port, web_site, interface=interface)
|
webclientstr = ""
|
||||||
webserver.setName('EvenniaWebServer%s' % pstring)
|
if WEBCLIENT_ENABLED:
|
||||||
PORTAL.services.addService(webserver)
|
# create ajax client processes at /webclientdata
|
||||||
|
from src.server.portal.webclient import WebClient
|
||||||
|
webclient = WebClient()
|
||||||
|
webclient.sessionhandler = PORTAL_SESSIONS
|
||||||
|
web_root.putChild("webclientdata", webclient)
|
||||||
|
webclientstr = "/client"
|
||||||
|
|
||||||
print " webserver%s%s: %s" % (webclientstr, ifacestr, port)
|
web_root = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
|
||||||
|
proxy_service = internet.TCPServer(proxyport, web_root, interface=interface)
|
||||||
|
proxy_service.setName('EvenniaWebProxy%s' % pstring)
|
||||||
|
PORTAL.services.addService(proxy_service)
|
||||||
|
print " webproxy%s%s:%s (<-> %s)" % (webclientstr, ifacestr, proxyport, serverport)
|
||||||
|
|
||||||
for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
|
for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
|
||||||
# external plugin services to start
|
# external plugin services to start
|
||||||
|
|
@ -286,7 +278,6 @@ for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
|
||||||
|
|
||||||
print '-' * 50 # end of terminal output
|
print '-' * 50 # end of terminal output
|
||||||
|
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
# Windows only: Set PID file manually
|
# Windows only: Set PID file manually
|
||||||
f = open(os.path.join(settings.GAME_DIR, 'portal.pid'), 'w')
|
f = open(os.path.join(settings.GAME_DIR, 'portal.pid'), 'w')
|
||||||
167
src/server/portal/portalsessionhandler.py
Normal file
167
src/server/portal/portalsessionhandler.py
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
"""
|
||||||
|
Sessionhandler for portal sessions
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
from src.server.sessionhandler import SessionHandler, PCONN, PDISCONN
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Portal-SessionHandler class
|
||||||
|
#------------------------------------------------------------
|
||||||
|
class PortalSessionHandler(SessionHandler):
|
||||||
|
"""
|
||||||
|
This object holds the sessions connected to the portal at any time.
|
||||||
|
It is synced with the server's equivalent SessionHandler over the AMP
|
||||||
|
connection.
|
||||||
|
|
||||||
|
Sessions register with the handler using the connect() method. This
|
||||||
|
will assign a new unique sessionid to the session and send that sessid
|
||||||
|
to the server using the AMP connection.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Init the handler
|
||||||
|
"""
|
||||||
|
self.portal = None
|
||||||
|
self.sessions = {}
|
||||||
|
self.latest_sessid = 0
|
||||||
|
self.uptime = time.time()
|
||||||
|
self.connection_time = 0
|
||||||
|
|
||||||
|
def at_server_connection(self):
|
||||||
|
"""
|
||||||
|
Called when the Portal establishes connection with the
|
||||||
|
Server. At this point, the AMP connection is already
|
||||||
|
established.
|
||||||
|
"""
|
||||||
|
self.connection_time = time.time()
|
||||||
|
|
||||||
|
def connect(self, session):
|
||||||
|
"""
|
||||||
|
Called by protocol at first connect. This adds a not-yet authenticated session
|
||||||
|
using an ever-increasing counter for sessid.
|
||||||
|
"""
|
||||||
|
self.latest_sessid += 1
|
||||||
|
sessid = self.latest_sessid
|
||||||
|
session.sessid = sessid
|
||||||
|
sessdata = session.get_sync_data()
|
||||||
|
self.sessions[sessid] = session
|
||||||
|
# sync with server-side
|
||||||
|
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||||
|
operation=PCONN,
|
||||||
|
data=sessdata)
|
||||||
|
def disconnect(self, session):
|
||||||
|
"""
|
||||||
|
Called from portal side when the connection is closed from the portal side.
|
||||||
|
"""
|
||||||
|
sessid = session.sessid
|
||||||
|
if sessid in self.sessions:
|
||||||
|
del self.sessions[sessid]
|
||||||
|
del session
|
||||||
|
# tell server to also delete this session
|
||||||
|
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||||
|
operation=PDISCONN)
|
||||||
|
|
||||||
|
def server_disconnect(self, sessid, reason=""):
|
||||||
|
"""
|
||||||
|
Called by server to force a disconnect by sessid
|
||||||
|
"""
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.disconnect(reason)
|
||||||
|
if sessid in self.sessions:
|
||||||
|
# in case sess.disconnect doesn't delete it
|
||||||
|
del self.sessions[sessid]
|
||||||
|
del session
|
||||||
|
|
||||||
|
def server_disconnect_all(self, reason=""):
|
||||||
|
"""
|
||||||
|
Called by server when forcing a clean disconnect for everyone.
|
||||||
|
"""
|
||||||
|
for session in self.sessions.values():
|
||||||
|
session.disconnect(reason)
|
||||||
|
del session
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
|
def server_logged_in(self, sessid, data):
|
||||||
|
"The server tells us that the session has been authenticated. Updated it."
|
||||||
|
sess = self.get_session(sessid)
|
||||||
|
sess.load_sync_data(data)
|
||||||
|
|
||||||
|
def server_session_sync(self, serversessions):
|
||||||
|
"""
|
||||||
|
Server wants to save data to the portal, maybe because it's about to shut down.
|
||||||
|
We don't overwrite any sessions here, just update them in-place and remove
|
||||||
|
any that are out of sync (which should normally not be the case)
|
||||||
|
|
||||||
|
serversessions - dictionary {sessid:{property:value},...} describing the properties
|
||||||
|
to sync on all sessions
|
||||||
|
"""
|
||||||
|
to_save = [sessid for sessid in serversessions if sessid in self.sessions]
|
||||||
|
to_delete = [sessid for sessid in self.sessions if sessid not in to_save]
|
||||||
|
# save protocols
|
||||||
|
for sessid in to_save:
|
||||||
|
self.sessions[sessid].load_sync_data(serversessions[sessid])
|
||||||
|
# disconnect out-of-sync missing protocols
|
||||||
|
for sessid in to_delete:
|
||||||
|
self.server_disconnect(sessid)
|
||||||
|
|
||||||
|
def count_loggedin(self, include_unloggedin=False):
|
||||||
|
"""
|
||||||
|
Count loggedin connections, alternatively count all connections.
|
||||||
|
"""
|
||||||
|
return len(self.get_sessions(include_unloggedin=include_unloggedin))
|
||||||
|
|
||||||
|
def session_from_suid(self, suid):
|
||||||
|
"""
|
||||||
|
Given a session id, retrieve the session (this is primarily
|
||||||
|
intended to be called by web clients)
|
||||||
|
"""
|
||||||
|
return [sess for sess in self.get_sessions(include_unloggedin=True)
|
||||||
|
if hasattr(sess, 'suid') and sess.suid == suid]
|
||||||
|
|
||||||
|
def data_in(self, session, string="", data=""):
|
||||||
|
"""
|
||||||
|
Called by portal sessions for relaying data coming
|
||||||
|
in from the protocol to the server. data is
|
||||||
|
serialized before passed on.
|
||||||
|
"""
|
||||||
|
#print "portal_data_in:", string
|
||||||
|
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
||||||
|
msg=string,
|
||||||
|
data=data)
|
||||||
|
def announce_all(self, message):
|
||||||
|
"""
|
||||||
|
Send message to all connection sessions
|
||||||
|
"""
|
||||||
|
for session in self.sessions.values():
|
||||||
|
session.data_out(message)
|
||||||
|
|
||||||
|
def data_out(self, sessid, string="", data=""):
|
||||||
|
"""
|
||||||
|
Called by server for having the portal relay messages and data
|
||||||
|
to the correct session protocol.
|
||||||
|
"""
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.data_out(string, data=data)
|
||||||
|
|
||||||
|
def oob_data_in(self, session, data):
|
||||||
|
"""
|
||||||
|
OOB (Out-of-band) data Portal -> Server
|
||||||
|
"""
|
||||||
|
print "portal_oob_data_in:", data
|
||||||
|
self.portal.amp_protocol.call_remote_OOBPortal2Server(session.sessid,
|
||||||
|
data=data)
|
||||||
|
|
||||||
|
def oob_data_out(self, sessid, data):
|
||||||
|
"""
|
||||||
|
OOB (Out-of-band) data Server -> Portal
|
||||||
|
"""
|
||||||
|
print "portal_oob_data_out:", data
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.oob_data_out(data)
|
||||||
|
|
||||||
|
PORTAL_SESSIONS = PortalSessionHandler()
|
||||||
|
|
@ -11,7 +11,7 @@ except ImportError:
|
||||||
print " SSL_ENABLED requires PyOpenSSL."
|
print " SSL_ENABLED requires PyOpenSSL."
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
|
|
||||||
from src.server.telnet import TelnetProtocol
|
from src.server.portal.telnet import TelnetProtocol
|
||||||
|
|
||||||
class SSLProtocol(TelnetProtocol):
|
class SSLProtocol(TelnetProtocol):
|
||||||
"""
|
"""
|
||||||
|
|
@ -10,8 +10,8 @@ sessions etc.
|
||||||
import re
|
import re
|
||||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
||||||
from src.server.session import Session
|
from src.server.session import Session
|
||||||
from src.server import ttype, mssp
|
from src.server.portal import ttype, mssp
|
||||||
from src.server.mccp import Mccp, mccp_compress, MCCP
|
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
|
||||||
from src.utils import utils, ansi, logger
|
from src.utils import utils, ansi, logger
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\{n$")
|
||||||
|
|
@ -63,11 +63,11 @@ class WebClient(resource.Resource):
|
||||||
self.requests = {}
|
self.requests = {}
|
||||||
self.databuffer = {}
|
self.databuffer = {}
|
||||||
|
|
||||||
def getChild(self, path, request):
|
#def getChild(self, path, request):
|
||||||
"""
|
# """
|
||||||
This is the place to put dynamic content.
|
# This is the place to put dynamic content.
|
||||||
"""
|
# """
|
||||||
return self
|
# return self
|
||||||
|
|
||||||
def _responseFailed(self, failure, suid, request):
|
def _responseFailed(self, failure, suid, request):
|
||||||
"callback if a request is lost/timed out"
|
"callback if a request is lost/timed out"
|
||||||
|
|
@ -14,7 +14,7 @@ if os.name == 'nt':
|
||||||
# For Windows batchfile we need an extra path insertion here.
|
# For Windows batchfile we need an extra path insertion here.
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||||
os.path.dirname(os.path.abspath(__file__)))))
|
os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
from twisted.web import server, static
|
||||||
from twisted.application import internet, service
|
from twisted.application import internet, service
|
||||||
from twisted.internet import reactor, defer
|
from twisted.internet import reactor, defer
|
||||||
import django
|
import django
|
||||||
|
|
@ -30,6 +30,12 @@ from src.utils.utils import get_evennia_version, mod_import, make_iter
|
||||||
from src.comms import channelhandler
|
from src.comms import channelhandler
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
||||||
|
# setting up server-side field cache
|
||||||
|
|
||||||
|
from django.db.models.signals import pre_save
|
||||||
|
from src.server.caches import field_pre_save
|
||||||
|
pre_save.connect(field_pre_save, dispatch_uid="fieldcache")
|
||||||
|
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
|
@ -57,10 +63,15 @@ AMP_HOST = settings.AMP_HOST
|
||||||
AMP_PORT = settings.AMP_PORT
|
AMP_PORT = settings.AMP_PORT
|
||||||
AMP_INTERFACE = settings.AMP_INTERFACE
|
AMP_INTERFACE = settings.AMP_INTERFACE
|
||||||
|
|
||||||
|
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
||||||
|
WEBSERVER_INTERFACES = settings.WEBSERVER_INTERFACES
|
||||||
|
|
||||||
# server-channel mappings
|
# server-channel mappings
|
||||||
|
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
|
||||||
IMC2_ENABLED = settings.IMC2_ENABLED
|
IMC2_ENABLED = settings.IMC2_ENABLED
|
||||||
IRC_ENABLED = settings.IRC_ENABLED
|
IRC_ENABLED = settings.IRC_ENABLED
|
||||||
RSS_ENABLED = settings.RSS_ENABLED
|
RSS_ENABLED = settings.RSS_ENABLED
|
||||||
|
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -325,7 +336,7 @@ if AMP_ENABLED:
|
||||||
ifacestr = ""
|
ifacestr = ""
|
||||||
if AMP_INTERFACE != '127.0.0.1':
|
if AMP_INTERFACE != '127.0.0.1':
|
||||||
ifacestr = "-%s" % AMP_INTERFACE
|
ifacestr = "-%s" % AMP_INTERFACE
|
||||||
print ' amp (to Portal)%s:%s' % (ifacestr, AMP_PORT)
|
print ' amp (to Portal)%s: %s' % (ifacestr, AMP_PORT)
|
||||||
|
|
||||||
from src.server import amp
|
from src.server import amp
|
||||||
|
|
||||||
|
|
@ -334,6 +345,30 @@ if AMP_ENABLED:
|
||||||
amp_service.setName("EvenniaPortal")
|
amp_service.setName("EvenniaPortal")
|
||||||
EVENNIA.services.addService(amp_service)
|
EVENNIA.services.addService(amp_service)
|
||||||
|
|
||||||
|
if WEBSERVER_ENABLED:
|
||||||
|
|
||||||
|
# Start a django-compatible webserver.
|
||||||
|
|
||||||
|
from twisted.python import threadpool
|
||||||
|
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
||||||
|
|
||||||
|
# start a thread pool and define the root url (/) as a wsgi resource
|
||||||
|
# recognized by Django
|
||||||
|
threads = threadpool.ThreadPool(minthreads=max(1, settings.WEBSERVER_THREADPOOL_LIMITS[0]),
|
||||||
|
maxthreads=max(1, settings.WEBSERVER_THREADPOOL_LIMITS[1]))
|
||||||
|
web_root = DjangoWebRoot(threads)
|
||||||
|
# point our media resources to url /media
|
||||||
|
web_root.putChild("media", static.File(settings.MEDIA_ROOT))
|
||||||
|
web_site = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
|
||||||
|
|
||||||
|
for proxyport, serverport in WEBSERVER_PORTS:
|
||||||
|
# create the webserver (we only need the port for this)
|
||||||
|
webserver = WSGIWebServer(threads, serverport, web_site, interface='127.0.0.1')
|
||||||
|
webserver.setName('EvenniaWebServer%s' % serverport)
|
||||||
|
EVENNIA.services.addService(webserver)
|
||||||
|
|
||||||
|
print " webserver: %s" % serverport
|
||||||
|
|
||||||
if IRC_ENABLED:
|
if IRC_ENABLED:
|
||||||
|
|
||||||
# IRC channel connections
|
# IRC channel connections
|
||||||
|
|
|
||||||
|
|
@ -380,167 +380,4 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
||||||
data=data)
|
data=data)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# Portal-SessionHandler class
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
|
||||||
class PortalSessionHandler(SessionHandler):
|
|
||||||
"""
|
|
||||||
This object holds the sessions connected to the portal at any time.
|
|
||||||
It is synced with the server's equivalent SessionHandler over the AMP
|
|
||||||
connection.
|
|
||||||
|
|
||||||
Sessions register with the handler using the connect() method. This
|
|
||||||
will assign a new unique sessionid to the session and send that sessid
|
|
||||||
to the server using the AMP connection.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""
|
|
||||||
Init the handler
|
|
||||||
"""
|
|
||||||
self.portal = None
|
|
||||||
self.sessions = {}
|
|
||||||
self.latest_sessid = 0
|
|
||||||
self.uptime = time.time()
|
|
||||||
self.connection_time = 0
|
|
||||||
|
|
||||||
def at_server_connection(self):
|
|
||||||
"""
|
|
||||||
Called when the Portal establishes connection with the
|
|
||||||
Server. At this point, the AMP connection is already
|
|
||||||
established.
|
|
||||||
"""
|
|
||||||
self.connection_time = time.time()
|
|
||||||
|
|
||||||
def connect(self, session):
|
|
||||||
"""
|
|
||||||
Called by protocol at first connect. This adds a not-yet authenticated session
|
|
||||||
using an ever-increasing counter for sessid.
|
|
||||||
"""
|
|
||||||
self.latest_sessid += 1
|
|
||||||
sessid = self.latest_sessid
|
|
||||||
session.sessid = sessid
|
|
||||||
sessdata = session.get_sync_data()
|
|
||||||
self.sessions[sessid] = session
|
|
||||||
# sync with server-side
|
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
|
||||||
operation=PCONN,
|
|
||||||
data=sessdata)
|
|
||||||
def disconnect(self, session):
|
|
||||||
"""
|
|
||||||
Called from portal side when the connection is closed from the portal side.
|
|
||||||
"""
|
|
||||||
sessid = session.sessid
|
|
||||||
if sessid in self.sessions:
|
|
||||||
del self.sessions[sessid]
|
|
||||||
del session
|
|
||||||
# tell server to also delete this session
|
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
|
||||||
operation=PDISCONN)
|
|
||||||
|
|
||||||
def server_disconnect(self, sessid, reason=""):
|
|
||||||
"""
|
|
||||||
Called by server to force a disconnect by sessid
|
|
||||||
"""
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.disconnect(reason)
|
|
||||||
if sessid in self.sessions:
|
|
||||||
# in case sess.disconnect doesn't delete it
|
|
||||||
del self.sessions[sessid]
|
|
||||||
del session
|
|
||||||
|
|
||||||
def server_disconnect_all(self, reason=""):
|
|
||||||
"""
|
|
||||||
Called by server when forcing a clean disconnect for everyone.
|
|
||||||
"""
|
|
||||||
for session in self.sessions.values():
|
|
||||||
session.disconnect(reason)
|
|
||||||
del session
|
|
||||||
self.sessions = {}
|
|
||||||
|
|
||||||
def server_logged_in(self, sessid, data):
|
|
||||||
"The server tells us that the session has been authenticated. Updated it."
|
|
||||||
sess = self.get_session(sessid)
|
|
||||||
sess.load_sync_data(data)
|
|
||||||
|
|
||||||
def server_session_sync(self, serversessions):
|
|
||||||
"""
|
|
||||||
Server wants to save data to the portal, maybe because it's about to shut down.
|
|
||||||
We don't overwrite any sessions here, just update them in-place and remove
|
|
||||||
any that are out of sync (which should normally not be the case)
|
|
||||||
|
|
||||||
serversessions - dictionary {sessid:{property:value},...} describing the properties
|
|
||||||
to sync on all sessions
|
|
||||||
"""
|
|
||||||
to_save = [sessid for sessid in serversessions if sessid in self.sessions]
|
|
||||||
to_delete = [sessid for sessid in self.sessions if sessid not in to_save]
|
|
||||||
# save protocols
|
|
||||||
for sessid in to_save:
|
|
||||||
self.sessions[sessid].load_sync_data(serversessions[sessid])
|
|
||||||
# disconnect out-of-sync missing protocols
|
|
||||||
for sessid in to_delete:
|
|
||||||
self.server_disconnect(sessid)
|
|
||||||
|
|
||||||
def count_loggedin(self, include_unloggedin=False):
|
|
||||||
"""
|
|
||||||
Count loggedin connections, alternatively count all connections.
|
|
||||||
"""
|
|
||||||
return len(self.get_sessions(include_unloggedin=include_unloggedin))
|
|
||||||
|
|
||||||
def session_from_suid(self, suid):
|
|
||||||
"""
|
|
||||||
Given a session id, retrieve the session (this is primarily
|
|
||||||
intended to be called by web clients)
|
|
||||||
"""
|
|
||||||
return [sess for sess in self.get_sessions(include_unloggedin=True)
|
|
||||||
if hasattr(sess, 'suid') and sess.suid == suid]
|
|
||||||
|
|
||||||
def data_in(self, session, string="", data=""):
|
|
||||||
"""
|
|
||||||
Called by portal sessions for relaying data coming
|
|
||||||
in from the protocol to the server. data is
|
|
||||||
serialized before passed on.
|
|
||||||
"""
|
|
||||||
#print "portal_data_in:", string
|
|
||||||
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
|
||||||
msg=string,
|
|
||||||
data=data)
|
|
||||||
def announce_all(self, message):
|
|
||||||
"""
|
|
||||||
Send message to all connection sessions
|
|
||||||
"""
|
|
||||||
for session in self.sessions.values():
|
|
||||||
session.data_out(message)
|
|
||||||
|
|
||||||
def data_out(self, sessid, string="", data=""):
|
|
||||||
"""
|
|
||||||
Called by server for having the portal relay messages and data
|
|
||||||
to the correct session protocol.
|
|
||||||
"""
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.data_out(string, data=data)
|
|
||||||
|
|
||||||
def oob_data_in(self, session, data):
|
|
||||||
"""
|
|
||||||
OOB (Out-of-band) data Portal -> Server
|
|
||||||
"""
|
|
||||||
print "portal_oob_data_in:", data
|
|
||||||
self.portal.amp_protocol.call_remote_OOBPortal2Server(session.sessid,
|
|
||||||
data=data)
|
|
||||||
|
|
||||||
def oob_data_out(self, sessid, data):
|
|
||||||
"""
|
|
||||||
OOB (Out-of-band) data Server -> Portal
|
|
||||||
"""
|
|
||||||
print "portal_oob_data_out:", data
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.oob_data_out(data)
|
|
||||||
|
|
||||||
SESSIONS = ServerSessionHandler()
|
SESSIONS = ServerSessionHandler()
|
||||||
PORTAL_SESSIONS = PortalSessionHandler()
|
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,21 @@ WEBSERVER_ENABLED = True
|
||||||
# attacks. It defaults to allowing all. In production, make
|
# attacks. It defaults to allowing all. In production, make
|
||||||
# sure to change this to your actual host addresses/IPs.
|
# sure to change this to your actual host addresses/IPs.
|
||||||
ALLOWED_HOSTS = ["*"]
|
ALLOWED_HOSTS = ["*"]
|
||||||
# A list of ports the Evennia webserver listens on
|
# The webserver sits behind a Portal proxy. This is a list
|
||||||
WEBSERVER_PORTS = [8000]
|
# of tuples (proxyport,serverport) used. The proxyports are what
|
||||||
|
# the Portal proxy presents to the world. The serverports are
|
||||||
|
# the internal ports the proxy uses to forward data to the Server-side
|
||||||
|
# webserver (these should not be publicly open)
|
||||||
|
WEBSERVER_PORTS = [(8000, 5001)]
|
||||||
# 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.
|
||||||
WEBSERVER_INTERFACES = ['0.0.0.0']
|
WEBSERVER_INTERFACES = ['0.0.0.0']
|
||||||
# IP addresses that may talk to the server in a reverse proxy configuration,
|
# IP addresses that may talk to the server in a reverse proxy configuration,
|
||||||
# like NginX.
|
# like NginX.
|
||||||
UPSTREAM_IPS = ['127.0.0.1']
|
UPSTREAM_IPS = ['127.0.0.1']
|
||||||
|
# The webserver uses threadpool for handling requests. This will scale
|
||||||
|
# with server load. Set the minimum and maximum number of threads it
|
||||||
|
# may use as (min, max) (must be > 0)
|
||||||
|
WEBSERVER_THREADPOOL_LIMITS = (1, 20)
|
||||||
# Start the evennia ajax client on /webclient
|
# Start the evennia ajax client on /webclient
|
||||||
# (the webserver must also be running)
|
# (the webserver must also be running)
|
||||||
WEBCLIENT_ENABLED = True
|
WEBCLIENT_ENABLED = True
|
||||||
|
|
@ -152,14 +160,23 @@ DATABASES = {
|
||||||
'HOST':'',
|
'HOST':'',
|
||||||
'PORT':''
|
'PORT':''
|
||||||
}}
|
}}
|
||||||
# Engine Config style for Django versions < 1.2 only. See above.
|
# This manages the object-level caches. Evennia will agressively cache
|
||||||
DATABASE_ENGINE = 'sqlite3'
|
# fields, properties and attribute lookup. Evennia uses a fast and
|
||||||
DATABASE_NAME = os.path.join(GAME_DIR, 'evennia.db3')
|
# local in-memory cache by default. If a Memcached server is available
|
||||||
DATABASE_USER = ''
|
# it can be used instead (see django docs). Cache performance can be
|
||||||
DATABASE_PASSWORD = ''
|
# tweaked by adding options to each cache. Finally, any cache can
|
||||||
DATABASE_HOST = ''
|
# be completely turned off by pointing its backend
|
||||||
DATABASE_PORT = ''
|
# to 'django.core.cache.backends.dummy.DummyCache'.
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
|
||||||
|
'field_cache': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
|
||||||
|
'prop_cache': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
|
||||||
|
'attr_cache': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'},
|
||||||
|
}
|
||||||
######################################################################
|
######################################################################
|
||||||
# Evennia pluggable modules
|
# Evennia pluggable modules
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,12 @@ from django.db import models, IntegrityError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.models.fields import AutoField, FieldDoesNotExist
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
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_attr_cache, set_attr_cache, del_attr_cache
|
from src.server.caches import get_attr_cache, set_attr_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, flush_attr_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, flush_attr_cache
|
||||||
from src.server.caches import call_ndb_hooks
|
#from src.server.caches import call_ndb_hooks
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
from src.typeclasses import managers
|
from src.typeclasses import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
|
|
@ -59,8 +60,6 @@ _CTYPEGET = ContentType.objects.get
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_DA = object.__delattr__
|
_DA = object.__delattr__
|
||||||
#_PLOADS = pickle.loads
|
|
||||||
#_PDUMPS = pickle.dumps
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -102,7 +101,7 @@ class Attribute(SharedMemoryModel):
|
||||||
# Attribute Database Model setup
|
# Attribute Database Model setup
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# These databse fields are all set using their corresponding properties,
|
# These database fields are all set using their corresponding properties,
|
||||||
# named same as the field, but withtout the db_* prefix.
|
# named same as the field, but withtout the db_* prefix.
|
||||||
|
|
||||||
db_key = models.CharField('key', max_length=255, db_index=True)
|
db_key = models.CharField('key', max_length=255, db_index=True)
|
||||||
|
|
@ -111,7 +110,7 @@ class Attribute(SharedMemoryModel):
|
||||||
# Lock storage
|
# Lock storage
|
||||||
db_lock_storage = models.TextField('locks', blank=True)
|
db_lock_storage = models.TextField('locks', blank=True)
|
||||||
# references the object the attribute is linked to (this is set
|
# references the object the attribute is linked to (this is set
|
||||||
# by each child class to this abstact class)
|
# by each child class to this abstract class)
|
||||||
db_obj = None # models.ForeignKey("RefencedObject")
|
db_obj = None # models.ForeignKey("RefencedObject")
|
||||||
# time stamp
|
# time stamp
|
||||||
db_date_created = models.DateTimeField('date_created', editable=False, auto_now_add=True)
|
db_date_created = models.DateTimeField('date_created', editable=False, auto_now_add=True)
|
||||||
|
|
@ -455,53 +454,52 @@ class TypedObject(SharedMemoryModel):
|
||||||
# value = self.attr and del self.attr respectively (where self
|
# value = self.attr and del self.attr respectively (where self
|
||||||
# is the object in question).
|
# is the object in question).
|
||||||
|
|
||||||
|
|
||||||
# key property (wraps db_key)
|
# key property (wraps db_key)
|
||||||
#@property
|
#@property
|
||||||
def __key_get(self):
|
#def __key_get(self):
|
||||||
"Getter. Allows for value = self.key"
|
# "Getter. Allows for value = self.key"
|
||||||
return get_field_cache(self, "key")
|
# return get_field_cache(self, "key")
|
||||||
#@key.setter
|
##@key.setter
|
||||||
def __key_set(self, value):
|
#def __key_set(self, value):
|
||||||
"Setter. Allows for self.key = value"
|
# "Setter. Allows for self.key = value"
|
||||||
set_field_cache(self, "key", value)
|
# set_field_cache(self, "key", value)
|
||||||
#@key.deleter
|
##@key.deleter
|
||||||
def __key_del(self):
|
#def __key_del(self):
|
||||||
"Deleter. Allows for del self.key"
|
# "Deleter. Allows for del self.key"
|
||||||
raise Exception("Cannot delete objectdb key!")
|
# raise Exception("Cannot delete objectdb key!")
|
||||||
key = property(__key_get, __key_set, __key_del)
|
#key = property(__key_get, __key_set, __key_del)
|
||||||
|
|
||||||
# name property (wraps db_key too - alias to self.key)
|
# name property (wraps db_key too - alias to self.key)
|
||||||
#@property
|
#@property
|
||||||
def __name_get(self):
|
def __name_get(self):
|
||||||
"Getter. Allows for value = self.name"
|
"Getter. Allows for value = self.name"
|
||||||
return get_field_cache(self, "key")
|
return self.key
|
||||||
#@name.setter
|
#@name.sette
|
||||||
def __name_set(self, value):
|
def __name_set(self, value):
|
||||||
"Setter. Allows for self.name = value"
|
"Setter. Allows for self.name = value"
|
||||||
set_field_cache(self, "key", value)
|
self.key = value
|
||||||
#@name.deleter
|
#@name.deleter
|
||||||
def __name_del(self):
|
def __name_del(self):
|
||||||
"Deleter. Allows for del self.name"
|
"Deleter. Allows for del self.name"
|
||||||
raise Exception("Cannot delete name!")
|
raise Exception("Cannot delete name!")
|
||||||
name = property(__name_get, __name_set, __name_del)
|
name = property(__name_get, __name_set, __name_del)
|
||||||
|
|
||||||
# typeclass_path property
|
# typeclass_path property - we don't cache this.
|
||||||
#@property
|
#@property
|
||||||
def __typeclass_path_get(self):
|
def __typeclass_path_get(self):
|
||||||
"Getter. Allows for value = self.typeclass_path"
|
"Getter. Allows for value = self.typeclass_path"
|
||||||
return get_field_cache(self, "typeclass_path")
|
return _GA(self, "db_typeclass_path")#get_field_cache(self, "typeclass_path")
|
||||||
#@typeclass_path.setter
|
#@typeclass_path.setter
|
||||||
def __typeclass_path_set(self, value):
|
def __typeclass_path_set(self, value):
|
||||||
"Setter. Allows for self.typeclass_path = value"
|
"Setter. Allows for self.typeclass_path = value"
|
||||||
set_field_cache(self, "typeclass_path", value)
|
_SA(self, "db_typeclass_path", value)
|
||||||
_SA(self, "_cached_typeclass", None)
|
_GA(self, "save")(update_fields=["db_typeclass_path"])
|
||||||
#@typeclass_path.deleter
|
#@typeclass_path.deleter
|
||||||
def __typeclass_path_del(self):
|
def __typeclass_path_del(self):
|
||||||
"Deleter. Allows for del self.typeclass_path"
|
"Deleter. Allows for del self.typeclass_path"
|
||||||
self.db_typeclass_path = ""
|
self.db_typeclass_path = ""
|
||||||
self.save()
|
_GA(self, "save")(update_fields=["db_typeclass_path"])
|
||||||
del_field_cache(self, "typeclass_path")
|
|
||||||
_SA(self, "_cached_typeclass", None)
|
|
||||||
typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
|
typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
|
||||||
|
|
||||||
# date_created property
|
# date_created property
|
||||||
|
|
@ -932,7 +930,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
db_obj=self, db_key__iexact=attribute_name)
|
||||||
if attrib_obj:
|
if attrib_obj:
|
||||||
set_attr_cache(self, attribute_name, attrib_obj[0])
|
set_attr_cache(attrib_obj[0])
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -959,8 +957,9 @@ class TypedObject(SharedMemoryModel):
|
||||||
if attrib_obj:
|
if attrib_obj:
|
||||||
# use old attribute
|
# use old attribute
|
||||||
attrib_obj = attrib_obj[0]
|
attrib_obj = attrib_obj[0]
|
||||||
|
set_attr_cache(attrib_obj) # renew cache
|
||||||
else:
|
else:
|
||||||
# no match; create new attribute
|
# no match; create new attribute (this will cache automatically)
|
||||||
attrib_obj = attrclass(db_key=attribute_name, db_obj=self)
|
attrib_obj = attrclass(db_key=attribute_name, db_obj=self)
|
||||||
if lockstring:
|
if lockstring:
|
||||||
attrib_obj.locks.add(lockstring)
|
attrib_obj.locks.add(lockstring)
|
||||||
|
|
@ -973,7 +972,6 @@ class TypedObject(SharedMemoryModel):
|
||||||
flush_attr_cache(self)
|
flush_attr_cache(self)
|
||||||
self.delete()
|
self.delete()
|
||||||
raise IntegrityError("Attribute could not be saved - object %s was deleted from database." % self.key)
|
raise IntegrityError("Attribute could not be saved - object %s was deleted from database." % self.key)
|
||||||
set_attr_cache(self, attribute_name, attrib_obj)
|
|
||||||
|
|
||||||
def get_attribute_obj(self, attribute_name, default=None):
|
def get_attribute_obj(self, attribute_name, default=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -985,7 +983,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
db_obj=self, db_key__iexact=attribute_name)
|
||||||
if not attrib_obj:
|
if not attrib_obj:
|
||||||
return default
|
return default
|
||||||
set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
|
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
||||||
return attrib_obj[0]
|
return attrib_obj[0]
|
||||||
return attrib_obj
|
return attrib_obj
|
||||||
|
|
||||||
|
|
@ -1004,7 +1002,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
db_obj=self, db_key__iexact=attribute_name)
|
||||||
if not attrib_obj:
|
if not attrib_obj:
|
||||||
return default
|
return default
|
||||||
set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
|
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
||||||
return attrib_obj[0].value
|
return attrib_obj[0].value
|
||||||
return attrib_obj.value
|
return attrib_obj.value
|
||||||
|
|
||||||
|
|
@ -1021,7 +1019,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
db_obj=self, db_key__iexact=attribute_name)
|
||||||
if not attrib_obj:
|
if not attrib_obj:
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
|
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
||||||
return attrib_obj[0].value
|
return attrib_obj[0].value
|
||||||
return attrib_obj.value
|
return attrib_obj.value
|
||||||
|
|
||||||
|
|
@ -1033,8 +1031,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
attr_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if attr_obj:
|
if attr_obj:
|
||||||
del_attr_cache(self, attribute_name)
|
attr_obj.delete() # this will clear attr cache automatically
|
||||||
attr_obj.delete()
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
_GA(self, "_attribute_class").objects.filter(
|
_GA(self, "_attribute_class").objects.filter(
|
||||||
|
|
@ -1051,8 +1048,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
attr_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if attr_obj:
|
if attr_obj:
|
||||||
del_attr_cache(self, attribute_name)
|
attr_obj.delete() # this will clear attr cache automatically
|
||||||
attr_obj.delete()
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
_GA(self, "_attribute_class").objects.filter(
|
_GA(self, "_attribute_class").objects.filter(
|
||||||
|
|
@ -1244,7 +1240,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
return None
|
return None
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
# hook the oob handler here
|
# hook the oob handler here
|
||||||
call_ndb_hooks(self, key, value)
|
#call_ndb_hooks(self, key, value)
|
||||||
_SA(self, key, value)
|
_SA(self, key, value)
|
||||||
self._ndb_holder = NdbHolder()
|
self._ndb_holder = NdbHolder()
|
||||||
return self._ndb_holder
|
return self._ndb_holder
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,10 @@ except ImportError:
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from src.server.models import ServerConfig
|
||||||
from src.utils.utils import to_str, uses_database
|
from src.utils.utils import to_str, uses_database
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle")
|
__all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle")
|
||||||
|
|
||||||
PICKLE_PROTOCOL = 2
|
PICKLE_PROTOCOL = 2
|
||||||
|
|
@ -47,13 +44,21 @@ _FROM_MODEL_MAP = None
|
||||||
_TO_MODEL_MAP = None
|
_TO_MODEL_MAP = None
|
||||||
_TO_TYPECLASS = lambda o: hasattr(o, 'typeclass') and o.typeclass or o
|
_TO_TYPECLASS = lambda o: hasattr(o, 'typeclass') and o.typeclass or o
|
||||||
_IS_PACKED_DBOBJ = lambda o: type(o) == tuple and len(o) == 4 and o[0] == '__packed_dbobj__'
|
_IS_PACKED_DBOBJ = lambda o: type(o) == tuple and len(o) == 4 and o[0] == '__packed_dbobj__'
|
||||||
_TO_DATESTRING = lambda o: _GA(o, "db_date_created").strftime("%Y:%m:%d-%H:%M:%S:%f")
|
if uses_database("mysql") and ServerConfig.objects.get_mysql_db_version() < '5.6.4':
|
||||||
if uses_database("mysql"):
|
# mysql <5.6.4 don't support millisecond precision
|
||||||
from src.server.models import ServerConfig
|
_DATESTRING = "%Y:%m:%d-%H:%M:%S:000000"
|
||||||
mysql_version = ServerConfig.objects.get_mysql_db_version()
|
else:
|
||||||
if mysql_version < '5.6.4':
|
_DATESTRING = "%Y:%m:%d-%H:%M:%S:%f"
|
||||||
# mysql <5.6.4 don't support millisecond precision
|
|
||||||
_TO_DATESTRING = lambda o: _GA(o, "db_date_created").strftime("%Y:%m:%d-%H:%M:%S:000000")
|
def _TO_DATESTRING(obj):
|
||||||
|
"this will only be called with valid database objects. Returns datestring on correct form."
|
||||||
|
try:
|
||||||
|
return _GA(obj, "db_date_created").strftime(_DATESTRING)
|
||||||
|
except AttributeError:
|
||||||
|
# this can happen if object is not yet saved - no datestring is then set
|
||||||
|
obj.save()
|
||||||
|
return _GA(obj, "db_date_created").strftime(_DATESTRING)
|
||||||
|
|
||||||
|
|
||||||
def _init_globals():
|
def _init_globals():
|
||||||
"Lazy importing to avoid circular import issues"
|
"Lazy importing to avoid circular import issues"
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,20 @@ leave caching unexpectedly (no use if WeakRefs).
|
||||||
Also adds cache_size() for monitoring the size of the cache.
|
Also adds cache_size() for monitoring the size of the cache.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os, threading
|
||||||
|
#from twisted.internet import reactor
|
||||||
|
#from twisted.internet.threads import blockingCallFromThread
|
||||||
|
from twisted.internet.reactor import callFromThread
|
||||||
from django.db.models.base import Model, ModelBase
|
from django.db.models.base import Model, ModelBase
|
||||||
from django.db.models.signals import post_save, pre_delete, \
|
from django.db.models.signals import post_save, pre_delete, post_syncdb
|
||||||
post_syncdb
|
|
||||||
|
|
||||||
from manager import SharedMemoryManager
|
from manager import SharedMemoryManager
|
||||||
|
|
||||||
|
_GA = object.__getattribute__
|
||||||
|
_SA = object.__setattr__
|
||||||
|
_DA = object.__delattr__
|
||||||
|
|
||||||
|
|
||||||
# determine if our current pid is different from the server PID (i.e.
|
# determine if our current pid is different from the server PID (i.e.
|
||||||
# if we are in a subprocess or not)
|
# if we are in a subprocess or not)
|
||||||
from src import PROC_MODIFIED_OBJS
|
from src import PROC_MODIFIED_OBJS
|
||||||
|
|
@ -37,11 +44,19 @@ def _get_pids():
|
||||||
if server_pid and portal_pid:
|
if server_pid and portal_pid:
|
||||||
return int(server_pid), int(portal_pid)
|
return int(server_pid), int(portal_pid)
|
||||||
return None, None
|
return None, None
|
||||||
_SELF_PID = os.getpid()
|
|
||||||
_SERVER_PID = None
|
|
||||||
_PORTAL_PID = None
|
|
||||||
_IS_SUBPROCESS = False
|
|
||||||
|
|
||||||
|
# get info about the current process and thread
|
||||||
|
|
||||||
|
_SELF_PID = os.getpid()
|
||||||
|
_SERVER_PID, _PORTAL_PID = _get_pids()
|
||||||
|
_IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and not _SELF_PID in (_SERVER_PID, _PORTAL_PID)
|
||||||
|
_IS_MAIN_THREAD = threading.currentThread().getName() == "MainThread"
|
||||||
|
|
||||||
|
#_SERVER_PID = None
|
||||||
|
#_PORTAL_PID = None
|
||||||
|
# #global _SERVER_PID, _PORTAL_PID, _IS_SUBPROCESS, _SELF_PID
|
||||||
|
# if not _SERVER_PID and not _PORTAL_PID:
|
||||||
|
# _IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and not _SELF_PID in (_SERVER_PID, _PORTAL_PID)
|
||||||
|
|
||||||
class SharedMemoryModelBase(ModelBase):
|
class SharedMemoryModelBase(ModelBase):
|
||||||
# CL: upstream had a __new__ method that skipped ModelBase's __new__ if
|
# CL: upstream had a __new__ method that skipped ModelBase's __new__ if
|
||||||
|
|
@ -68,13 +83,44 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
if cached_instance is None:
|
if cached_instance is None:
|
||||||
cached_instance = new_instance()
|
cached_instance = new_instance()
|
||||||
cls.cache_instance(cached_instance)
|
cls.cache_instance(cached_instance)
|
||||||
|
|
||||||
return cached_instance
|
return cached_instance
|
||||||
|
|
||||||
|
|
||||||
def _prepare(cls):
|
def _prepare(cls):
|
||||||
cls.__instance_cache__ = {} #WeakValueDictionary()
|
cls.__instance_cache__ = {} #WeakValueDictionary()
|
||||||
super(SharedMemoryModelBase, cls)._prepare()
|
super(SharedMemoryModelBase, cls)._prepare()
|
||||||
|
|
||||||
|
def __init__(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Takes field names db_* and creates property wrappers named without the db_ prefix. So db_key -> key
|
||||||
|
This wrapper happens on the class level, so there is no overhead when creating objects. If a class
|
||||||
|
already has a wrapper of the given name, the automatic creation is skipped. Note: Remember to
|
||||||
|
document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise.
|
||||||
|
"""
|
||||||
|
super(SharedMemoryModelBase, cls).__init__(*args, **kwargs)
|
||||||
|
def create_wrapper(cls, fieldname, wrappername):
|
||||||
|
"Helper method to create property wrappers with unique names (must be in separate call)"
|
||||||
|
def _get(cls, fname):
|
||||||
|
return _GA(cls, fname)
|
||||||
|
def _set(cls, fname, value):
|
||||||
|
_SA(cls, fname, value)
|
||||||
|
_GA(cls, "save")(update_fields=[fname]) # important!
|
||||||
|
def _del(cls, fname):
|
||||||
|
raise RuntimeError("You cannot delete field %s on %s; set it to None instead." % (fname, cls))
|
||||||
|
type(cls).__setattr__(cls, wrappername, property(lambda cls: _get(cls, fieldname),
|
||||||
|
lambda cls,val: _set(cls, fieldname, val),
|
||||||
|
lambda cls: _del(cls, fieldname)))
|
||||||
|
# eclude some models that should not auto-create wrapper fields
|
||||||
|
if cls.__name__ in ("ServerConfig", "TypeNick"):
|
||||||
|
return
|
||||||
|
# dynamically create the properties
|
||||||
|
for field in cls._meta.fields:
|
||||||
|
fieldname = field.name
|
||||||
|
wrappername = fieldname == "id" and "dbid" or fieldname.replace("db_", "")
|
||||||
|
if not hasattr(cls, wrappername):
|
||||||
|
# make sure not to overload manually created wrappers on the model
|
||||||
|
#print "wrapping %s -> %s" % (fieldname, wrappername)
|
||||||
|
create_wrapper(cls, fieldname, wrappername)
|
||||||
|
|
||||||
class SharedMemoryModel(Model):
|
class SharedMemoryModel(Model):
|
||||||
# CL: setting abstract correctly to allow subclasses to inherit the default
|
# CL: setting abstract correctly to allow subclasses to inherit the default
|
||||||
|
|
@ -116,6 +162,13 @@ class SharedMemoryModel(Model):
|
||||||
return result
|
return result
|
||||||
_get_cache_key = classmethod(_get_cache_key)
|
_get_cache_key = classmethod(_get_cache_key)
|
||||||
|
|
||||||
|
def _flush_cached_by_key(cls, key):
|
||||||
|
try:
|
||||||
|
del cls.__instance_cache__[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
_flush_cached_by_key = classmethod(_flush_cached_by_key)
|
||||||
|
|
||||||
def get_cached_instance(cls, id):
|
def get_cached_instance(cls, id):
|
||||||
"""
|
"""
|
||||||
Method to retrieve a cached instance by pk value. Returns None when not found
|
Method to retrieve a cached instance by pk value. Returns None when not found
|
||||||
|
|
@ -138,13 +191,6 @@ class SharedMemoryModel(Model):
|
||||||
return cls.__instance_cache__.values()
|
return cls.__instance_cache__.values()
|
||||||
get_all_cached_instances = classmethod(get_all_cached_instances)
|
get_all_cached_instances = classmethod(get_all_cached_instances)
|
||||||
|
|
||||||
def _flush_cached_by_key(cls, key):
|
|
||||||
try:
|
|
||||||
del cls.__instance_cache__[key]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
_flush_cached_by_key = classmethod(_flush_cached_by_key)
|
|
||||||
|
|
||||||
def flush_cached_instance(cls, instance):
|
def flush_cached_instance(cls, instance):
|
||||||
"""
|
"""
|
||||||
Method to flush an instance from the cache. The instance will always be flushed from the cache,
|
Method to flush an instance from the cache. The instance will always be flushed from the cache,
|
||||||
|
|
@ -158,15 +204,22 @@ class SharedMemoryModel(Model):
|
||||||
flush_instance_cache = classmethod(flush_instance_cache)
|
flush_instance_cache = classmethod(flush_instance_cache)
|
||||||
|
|
||||||
def save(cls, *args, **kwargs):
|
def save(cls, *args, **kwargs):
|
||||||
"overload spot for saving"
|
"save method tracking process/thread issues"
|
||||||
global _SERVER_PID, _PORTAL_PID, _IS_SUBPROCESS, _SELF_PID
|
|
||||||
if not _SERVER_PID and not _PORTAL_PID:
|
|
||||||
_SERVER_PID, _PORTAL_PID = _get_pids()
|
|
||||||
_IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and (_SERVER_PID != _SELF_PID) and (_PORTAL_PID != _SELF_PID)
|
|
||||||
if _IS_SUBPROCESS:
|
if _IS_SUBPROCESS:
|
||||||
#print "storing in PROC_MODIFIED_OBJS:", cls.db_key, cls.id
|
# we keep a store of objects modified in subprocesses so
|
||||||
|
# we know to update their caches in the central process
|
||||||
PROC_MODIFIED_OBJS.append(cls)
|
PROC_MODIFIED_OBJS.append(cls)
|
||||||
super(SharedMemoryModel, cls).save(*args, **kwargs)
|
|
||||||
|
if _IS_MAIN_THREAD:
|
||||||
|
# in main thread - normal operation
|
||||||
|
super(SharedMemoryModel, cls).save(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
# in another thread; make sure to save in reactor thread
|
||||||
|
def _save_callback(cls, *args, **kwargs):
|
||||||
|
super(SharedMemoryModel, cls).save(*args, **kwargs)
|
||||||
|
#blockingCallFromThread(reactor, _save_callback, cls, *args, **kwargs)
|
||||||
|
callFromThread(_save_callback, cls, *args, **kwargs)
|
||||||
|
|
||||||
# Use a signal so we make sure to catch cascades.
|
# Use a signal so we make sure to catch cascades.
|
||||||
def flush_cache(**kwargs):
|
def flush_cache(**kwargs):
|
||||||
|
|
|
||||||
|
|
@ -467,9 +467,9 @@ def delay(delay=2, retval=None, callback=None):
|
||||||
"""
|
"""
|
||||||
Delay the return of a value.
|
Delay the return of a value.
|
||||||
Inputs:
|
Inputs:
|
||||||
to_return (any) - this will be returned by this function after a delay
|
|
||||||
delay (int) - the delay in seconds
|
delay (int) - the delay in seconds
|
||||||
callback (func(r)) - if given, this will be called with the to_return after delay seconds
|
retval (any) - this will be returned by this function after a delay
|
||||||
|
callback (func(retval)) - if given, this will be called with retval after delay seconds
|
||||||
Returns:
|
Returns:
|
||||||
deferred that will fire with to_return after delay seconds
|
deferred that will fire with to_return after delay seconds
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ This structures the url tree for the news application.
|
||||||
It is imported from the root handler, game.web.urls.py.
|
It is imported from the root handler, game.web.urls.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls import *
|
||||||
|
|
||||||
urlpatterns = patterns('src.web.news.views',
|
urlpatterns = patterns('src.web.news.views',
|
||||||
(r'^show/(?P<entry_id>\d+)/$', 'show_news'),
|
(r'^show/(?P<entry_id>\d+)/$', 'show_news'),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# http://diveintopython.org/regular_expressions/street_addresses.html#re.matching.2.3
|
# http://diveintopython.org/regular_expressions/street_addresses.html#re.matching.2.3
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls import *
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ page and serve it eventual static content.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response, redirect
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
@ -15,6 +15,12 @@ def webclient(request):
|
||||||
Webclient page template loading.
|
Webclient page template loading.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# analyze request to find which port we are on
|
||||||
|
if int(request.META["SERVER_PORT"]) == 8000:
|
||||||
|
# we relay webclient to the portal port
|
||||||
|
print "Called from port 8000!"
|
||||||
|
#return redirect("http://localhost:8001/webclient/", permanent=True)
|
||||||
|
|
||||||
# as an example we send the number of connected players to the template
|
# as an example we send the number of connected players to the template
|
||||||
pagevars = {'num_players_connected': SESSIONS.player_count()}
|
pagevars = {'num_players_connected': SESSIONS.player_count()}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ This structures the (simple) structure of the
|
||||||
webpage 'application'.
|
webpage 'application'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls import *
|
||||||
|
|
||||||
urlpatterns = patterns('src.web.website.views',
|
urlpatterns = patterns('src.web.website.views',
|
||||||
(r'^$', 'page_index'),
|
(r'^$', 'page_index'),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue