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)."
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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
|
||||
inlines = [AliasInline]#, ObjAttributeInline]
|
||||
#inlines = [AliasInline, ObjAttributeInline]
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
obj.save()
|
||||
if not change:
|
||||
# adding a new object
|
||||
obj = obj.typeclass
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@ transparently through the decorating TypeClass.
|
|||
import traceback
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.db.models.signals import post_init, pre_delete
|
||||
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
||||
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||
from src.server.caches import attr_post_init, attr_pre_delete
|
||||
from src.typeclasses.typeclass import TypeClass
|
||||
from src.players.models import PlayerNick
|
||||
from src.objects.manager import ObjectManager
|
||||
|
|
@ -53,6 +55,7 @@ _HERE = _("here")
|
|||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
|
||||
class ObjAttribute(Attribute):
|
||||
"Attributes for ObjectDB objects."
|
||||
db_obj = models.ForeignKey("ObjectDB")
|
||||
|
|
@ -62,6 +65,10 @@ class ObjAttribute(Attribute):
|
|||
verbose_name = "Object Attribute"
|
||||
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
|
||||
|
|
@ -241,7 +248,7 @@ class ObjectDB(TypedObject):
|
|||
"Deleter. Allows for del self.aliases"
|
||||
for alias in Alias.objects.filter(db_obj=self):
|
||||
alias.delete()
|
||||
del_prop_cache(self, "_aliases")
|
||||
#del_prop_cache(self, "_aliases")
|
||||
aliases = property(__aliases_get, __aliases_set, __aliases_del)
|
||||
|
||||
# player property (wraps db_player)
|
||||
|
|
@ -299,27 +306,16 @@ class ObjectDB(TypedObject):
|
|||
del_field_cache(self, "sessid")
|
||||
sessid = property(__sessid_get, __sessid_set, __sessid_del)
|
||||
|
||||
# location property (wraps db_location)
|
||||
#@property
|
||||
def __location_get(self):
|
||||
"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"
|
||||
def _db_location_handler(self, new_value, old_value=None):
|
||||
"This handles changes to the db_location field."
|
||||
print "db_location_handler:", new_value, old_value
|
||||
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
|
||||
old_loc = old_value
|
||||
# new_value can be dbref, typeclass or dbmodel
|
||||
if ObjectDB.objects.dbref(new_value, reqhash=False):
|
||||
loc = ObjectDB.objects.dbref_search(new_value)
|
||||
# this should not fail if new_value is valid.
|
||||
loc = _GA(loc, "dbobj")
|
||||
|
||||
# recursive location check
|
||||
def is_loc_loop(loc, depth=0):
|
||||
|
|
@ -333,32 +329,85 @@ class ObjectDB(TypedObject):
|
|||
except RuntimeWarning: pass
|
||||
|
||||
# set the location
|
||||
set_field_cache(self, "location", loc)
|
||||
_SA(self, "db_location", loc)
|
||||
# update the contents of each location
|
||||
if old_loc:
|
||||
_GA(_GA(old_loc, "dbobj"), "contents_update")()
|
||||
_GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True)
|
||||
if loc:
|
||||
_GA(loc, "contents_update")()
|
||||
_GA(loc, "contents_update")(self)
|
||||
except RuntimeError:
|
||||
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))
|
||||
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
|
||||
string += "%s is not a valid location." % new_value
|
||||
_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)
|
||||
|
||||
## location property (wraps db_location)
|
||||
##@property
|
||||
#def __location_get(self):
|
||||
# "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:
|
||||
# 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)
|
||||
#@property
|
||||
|
|
@ -515,19 +564,26 @@ class ObjectDB(TypedObject):
|
|||
exclude = make_iter(exclude)
|
||||
if cont == None:
|
||||
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)
|
||||
|
||||
def contents_update(self):
|
||||
def contents_update(self, obj=None, remove=False):
|
||||
"""
|
||||
Updates the contents property of the object with a new
|
||||
object Called by
|
||||
self.location_set.
|
||||
Updates the contents property of the object
|
||||
|
||||
obj -
|
||||
remove (true/false) - remove obj from content list
|
||||
add - object to add to 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)
|
||||
return cont
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,12 @@ from django.conf import settings
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
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_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.scripts.models import ScriptDB
|
||||
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
||||
|
|
@ -74,6 +77,9 @@ class PlayerAttribute(Attribute):
|
|||
"Define Django meta options"
|
||||
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
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ Common examples of uses of Scripts:
|
|||
"""
|
||||
from django.conf import settings
|
||||
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 django.contrib.contenttypes.models import ContentType
|
||||
from src.scripts.manager import ScriptManager
|
||||
|
|
@ -47,6 +50,9 @@ class ScriptAttribute(Attribute):
|
|||
verbose_name = "Script Attribute"
|
||||
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 django.conf import settings
|
||||
|
||||
_ENABLE_LOCAL_CACHES = settings.GAME_CACHE_TYPE
|
||||
from django.dispatch import Signal
|
||||
from django.core.cache import get_cache
|
||||
#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__
|
||||
_SA = object.__setattr__
|
||||
_DA = object.__delattr__
|
||||
|
||||
# OOB hooks (OOB not yet functional, don't use yet)
|
||||
_OOB_FIELD_UPDATE_HOOKS = defaultdict(dict)
|
||||
_OOB_PROP_UPDATE_HOOKS = defaultdict(dict)
|
||||
_OOB_ATTR_UPDATE_HOOKS = defaultdict(dict)
|
||||
_OOB_NDB_UPDATE_HOOKS = defaultdict(dict)
|
||||
_OOB_CUSTOM_UPDATE_HOOKS = defaultdict(dict)
|
||||
#
|
||||
# Open handles to the caches
|
||||
#
|
||||
|
||||
_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
|
||||
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")
|
||||
except AttributeError:
|
||||
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:
|
||||
try:
|
||||
# maybe a typeclass, try to go to 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:
|
||||
# this happens if hashing something like ndb. We have to
|
||||
# rely on memory adressing in this case.
|
||||
|
|
@ -48,289 +64,441 @@ def hashid(obj):
|
|||
if not idnum or not date:
|
||||
# this will happen if setting properties on an object which is not yet saved
|
||||
return None
|
||||
# build the hashid
|
||||
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)
|
||||
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.
|
||||
Given function will be called with function(obj, entityname, newvalue, *args, **kwargs)
|
||||
entity - one of "field", "property", "db", "ndb" or "custom"
|
||||
Called at the beginning of the save operation. The save method
|
||||
must be called with the update_fields keyword in order to
|
||||
"""
|
||||
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 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
|
||||
_ATTR_CACHE.set(hid, sender)
|
||||
|
||||
def unregister_oob_update_hook(obj, name, entity="property"):
|
||||
"""
|
||||
Un-register a report hook
|
||||
"""
|
||||
hid = hashid(obj)
|
||||
# connected to pre_delete signal (connected in respective Attribute model)
|
||||
def attr_pre_delete(sender, instance=None, **kwargs):
|
||||
"Called when attribute is deleted (del_attribute)"
|
||||
#print "attr_pre_delete:", instance, instance.db_obj, instance.db_key
|
||||
hid = hashid(_GA(instance, "db_obj"), "-%s" % _GA(instance, "db_key"))
|
||||
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
|
||||
#print "attr_pre_delete:", _GA(instance, "db_key")
|
||||
_ATTR_CACHE.delete(hid)
|
||||
|
||||
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)
|
||||
# access methods
|
||||
|
||||
def get_attr_cache(obj, attrname):
|
||||
"Called by get_attribute"
|
||||
hid = hashid(obj, "-%s" % attrname)
|
||||
_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:
|
||||
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])
|
||||
#print "get_prop_cache", hid, propname, _PROP_CACHE.get(hid, None)
|
||||
return _PROP_CACHE[hid].get(propname, None)
|
||||
|
||||
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)
|
||||
def set_prop_cache(obj, propname, propvalue):
|
||||
"Set property cache"
|
||||
hid = hashid(obj, "-%s" % propname)
|
||||
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])
|
||||
#print "set_prop_cache", propname, propvalue
|
||||
_PROP_CACHE[hid][propname] = propvalue
|
||||
#_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:
|
||||
|
||||
# Cache stores
|
||||
_ATTR_CACHE = defaultdict(dict)
|
||||
_FIELD_CACHE = defaultdict(dict)
|
||||
def flush_prop_cache():
|
||||
"Clear property cache"
|
||||
global _PROP_CACHE
|
||||
_PROP_CACHE = defaultdict(dict)
|
||||
#_PROP_CACHE.clear()
|
||||
|
||||
|
||||
def get_cache_sizes():
|
||||
"""
|
||||
Get cache sizes, expressed in number of objects and memory size in MB
|
||||
"""
|
||||
global _ATTR_CACHE, _FIELD_CACHE, _PROP_CACHE
|
||||
#_ENABLE_LOCAL_CACHES = settings.GAME_CACHE_TYPE
|
||||
## oob helper functions
|
||||
# OOB hooks (OOB not yet functional, don't use yet)
|
||||
#_OOB_FIELD_UPDATE_HOOKS = defaultdict(dict)
|
||||
#_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())
|
||||
attr_mb = sum(sum(getsizeof(obj) for obj in dic.values()) for dic in _ATTR_CACHE.values()) / 1024.0
|
||||
# # old cache system
|
||||
#
|
||||
# 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())
|
||||
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:
|
||||
#else:
|
||||
# local caches disabled. Use simple pass-through replacements
|
||||
|
||||
def get_cache_sizes():
|
||||
return (0, 0), (0, 0), (0, 0)
|
||||
def get_field_cache(obj, name):
|
||||
return _GA(obj, "db_%s" % name)
|
||||
def set_field_cache(obj, name, val):
|
||||
_SA(obj, "db_%s" % name, val)
|
||||
_GA(obj, "save")()
|
||||
hid = hashid(obj)
|
||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||
_OOB_HANDLER.update(hid, name, val)
|
||||
def del_field_cache(obj, name):
|
||||
_SA(obj, "db_%s" % name, None)
|
||||
_GA(obj, "save")()
|
||||
hid = hashid(obj)
|
||||
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||
_OOB_HANDLER.update(hid, name, None)
|
||||
def flush_field_cache(obj=None):
|
||||
pass
|
||||
# these should get oob handlers when oob is implemented.
|
||||
def get_prop_cache(obj, name, default=None):
|
||||
return None
|
||||
def set_prop_cache(obj, name, val):
|
||||
pass
|
||||
def del_prop_cache(obj, name):
|
||||
pass
|
||||
def flush_prop_cache(obj=None):
|
||||
pass
|
||||
def get_attr_cache(obj, attrname):
|
||||
return None
|
||||
def set_attr_cache(obj, attrname, attrobj):
|
||||
pass
|
||||
def del_attr_cache(obj, attrname):
|
||||
pass
|
||||
def flush_attr_cache(obj=None):
|
||||
pass
|
||||
def get_cache_sizes():
|
||||
return (0, 0), (0, 0), (0, 0)
|
||||
def get_field_cache(obj, name):
|
||||
return _GA(obj, "db_%s" % name)
|
||||
def set_field_cache(obj, name, val):
|
||||
_SA(obj, "db_%s" % name, val)
|
||||
_GA(obj, "save")()
|
||||
#hid = hashid(obj)
|
||||
#if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||
# _OOB_HANDLER.update(hid, name, val)
|
||||
def del_field_cache(obj, name):
|
||||
_SA(obj, "db_%s" % name, None)
|
||||
_GA(obj, "save")()
|
||||
#hid = hashid(obj)
|
||||
#if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||
# _OOB_HANDLER.update(hid, name, None)
|
||||
#def flush_field_cache(obj=None):
|
||||
# pass
|
||||
# these should get oob handlers when oob is implemented.
|
||||
#def get_prop_cache(obj, name, default=None):
|
||||
# return None
|
||||
#def set_prop_cache(obj, name, val):
|
||||
# pass
|
||||
#def del_prop_cache(obj, name):
|
||||
# pass
|
||||
#def flush_prop_cache(obj=None):
|
||||
# pass
|
||||
#def get_attr_cache(obj, attrname):
|
||||
# return None
|
||||
#def set_attr_cache(obj, attrname, attrobj):
|
||||
# pass
|
||||
#def del_attr_cache(obj, attrname):
|
||||
# passk
|
||||
#def flush_attr_cache(obj=None):
|
||||
# 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
|
||||
|
||||
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
|
||||
compress data when sending to supporting clients, reducing bandwidth
|
||||
by 70-90%.. The compression is done using Python's builtin zlib
|
||||
|
|
@ -11,15 +11,15 @@ import sys
|
|||
import os
|
||||
if os.name == 'nt':
|
||||
# For Windows batchfile we need an extra path insertion here.
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__))))))
|
||||
|
||||
from twisted.application import internet, service
|
||||
from twisted.internet import protocol, reactor
|
||||
from twisted.web import server, static
|
||||
from twisted.web import server
|
||||
from django.conf import settings
|
||||
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)]
|
||||
|
||||
|
|
@ -55,7 +55,8 @@ WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
|||
|
||||
AMP_HOST = settings.AMP_HOST
|
||||
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
|
||||
|
||||
print ' amp (to Server): %s' % AMP_PORT
|
||||
|
||||
factory = amp.AmpClientFactory(PORTAL)
|
||||
amp_client = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
|
||||
amp_client.setName('evennia_amp')
|
||||
|
|
@ -168,7 +171,7 @@ if TELNET_ENABLED:
|
|||
|
||||
# Start telnet game connections
|
||||
|
||||
from src.server import telnet
|
||||
from src.server.portal import telnet
|
||||
|
||||
for interface in TELNET_INTERFACES:
|
||||
if ":" in interface:
|
||||
|
|
@ -192,7 +195,7 @@ if SSL_ENABLED:
|
|||
|
||||
# Start SSL game connection (requires PyOpenSSL).
|
||||
|
||||
from src.server import ssl
|
||||
from src.server.portal import ssl
|
||||
|
||||
for interface in SSL_INTERFACES:
|
||||
if ":" in interface:
|
||||
|
|
@ -218,7 +221,7 @@ if SSH_ENABLED:
|
|||
|
||||
# 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:
|
||||
if ":" in interface:
|
||||
|
|
@ -240,29 +243,9 @@ if SSH_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 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)
|
||||
from twisted.web import proxy
|
||||
|
||||
for interface in WEBSERVER_INTERFACES:
|
||||
if ":" in interface:
|
||||
|
|
@ -271,14 +254,23 @@ if WEBSERVER_ENABLED:
|
|||
ifacestr = ""
|
||||
if interface != '0.0.0.0' or len(WEBSERVER_INTERFACES) > 1:
|
||||
ifacestr = "-%s" % interface
|
||||
for port in WEBSERVER_PORTS:
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
# create the webserver
|
||||
webserver = WSGIWebServer(threads, port, web_site, interface=interface)
|
||||
webserver.setName('EvenniaWebServer%s' % pstring)
|
||||
PORTAL.services.addService(webserver)
|
||||
for proxyport, serverport in WEBSERVER_PORTS:
|
||||
pstring = "%s:%s<->%s" % (ifacestr, proxyport, serverport)
|
||||
web_root = proxy.ReverseProxyResource('127.0.0.1', serverport, '')
|
||||
webclientstr = ""
|
||||
if WEBCLIENT_ENABLED:
|
||||
# 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:
|
||||
# external plugin services to start
|
||||
|
|
@ -286,7 +278,6 @@ for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
|
|||
|
||||
print '-' * 50 # end of terminal output
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
# Windows only: Set PID file manually
|
||||
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."
|
||||
sys.exit(5)
|
||||
|
||||
from src.server.telnet import TelnetProtocol
|
||||
from src.server.portal.telnet import TelnetProtocol
|
||||
|
||||
class SSLProtocol(TelnetProtocol):
|
||||
"""
|
||||
|
|
@ -10,8 +10,8 @@ sessions etc.
|
|||
import re
|
||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
||||
from src.server.session import Session
|
||||
from src.server import ttype, mssp
|
||||
from src.server.mccp import Mccp, mccp_compress, MCCP
|
||||
from src.server.portal import ttype, mssp
|
||||
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
|
||||
from src.utils import utils, ansi, logger
|
||||
|
||||
_RE_N = re.compile(r"\{n$")
|
||||
|
|
@ -63,11 +63,11 @@ class WebClient(resource.Resource):
|
|||
self.requests = {}
|
||||
self.databuffer = {}
|
||||
|
||||
def getChild(self, path, request):
|
||||
"""
|
||||
This is the place to put dynamic content.
|
||||
"""
|
||||
return self
|
||||
#def getChild(self, path, request):
|
||||
# """
|
||||
# This is the place to put dynamic content.
|
||||
# """
|
||||
# return self
|
||||
|
||||
def _responseFailed(self, failure, suid, request):
|
||||
"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.
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from twisted.web import server, static
|
||||
from twisted.application import internet, service
|
||||
from twisted.internet import reactor, defer
|
||||
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.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__
|
||||
|
||||
if os.name == 'nt':
|
||||
|
|
@ -57,10 +63,15 @@ AMP_HOST = settings.AMP_HOST
|
|||
AMP_PORT = settings.AMP_PORT
|
||||
AMP_INTERFACE = settings.AMP_INTERFACE
|
||||
|
||||
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
||||
WEBSERVER_INTERFACES = settings.WEBSERVER_INTERFACES
|
||||
|
||||
# server-channel mappings
|
||||
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
|
||||
IMC2_ENABLED = settings.IMC2_ENABLED
|
||||
IRC_ENABLED = settings.IRC_ENABLED
|
||||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -325,7 +336,7 @@ if AMP_ENABLED:
|
|||
ifacestr = ""
|
||||
if AMP_INTERFACE != '127.0.0.1':
|
||||
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
|
||||
|
||||
|
|
@ -334,6 +345,30 @@ if AMP_ENABLED:
|
|||
amp_service.setName("EvenniaPortal")
|
||||
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:
|
||||
|
||||
# IRC channel connections
|
||||
|
|
|
|||
|
|
@ -380,167 +380,4 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
||||
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()
|
||||
PORTAL_SESSIONS = PortalSessionHandler()
|
||||
|
|
|
|||
|
|
@ -44,13 +44,21 @@ WEBSERVER_ENABLED = True
|
|||
# attacks. It defaults to allowing all. In production, make
|
||||
# sure to change this to your actual host addresses/IPs.
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
# A list of ports the Evennia webserver listens on
|
||||
WEBSERVER_PORTS = [8000]
|
||||
# The webserver sits behind a Portal proxy. This is a list
|
||||
# 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.
|
||||
WEBSERVER_INTERFACES = ['0.0.0.0']
|
||||
# IP addresses that may talk to the server in a reverse proxy configuration,
|
||||
# like NginX.
|
||||
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
|
||||
# (the webserver must also be running)
|
||||
WEBCLIENT_ENABLED = True
|
||||
|
|
@ -152,14 +160,23 @@ DATABASES = {
|
|||
'HOST':'',
|
||||
'PORT':''
|
||||
}}
|
||||
# Engine Config style for Django versions < 1.2 only. See above.
|
||||
DATABASE_ENGINE = 'sqlite3'
|
||||
DATABASE_NAME = os.path.join(GAME_DIR, 'evennia.db3')
|
||||
DATABASE_USER = ''
|
||||
DATABASE_PASSWORD = ''
|
||||
DATABASE_HOST = ''
|
||||
DATABASE_PORT = ''
|
||||
|
||||
# This manages the object-level caches. Evennia will agressively cache
|
||||
# fields, properties and attribute lookup. Evennia uses a fast and
|
||||
# local in-memory cache by default. If a Memcached server is available
|
||||
# it can be used instead (see django docs). Cache performance can be
|
||||
# tweaked by adding options to each cache. Finally, any cache can
|
||||
# be completely turned off by pointing its backend
|
||||
# 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
|
||||
######################################################################
|
||||
|
|
|
|||
|
|
@ -38,11 +38,12 @@ from django.db import models, IntegrityError
|
|||
from django.conf import settings
|
||||
from django.utils.encoding import smart_str
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models.fields import AutoField, FieldDoesNotExist
|
||||
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_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 call_ndb_hooks
|
||||
#from src.server.caches import call_ndb_hooks
|
||||
from src.server.models import ServerConfig
|
||||
from src.typeclasses import managers
|
||||
from src.locks.lockhandler import LockHandler
|
||||
|
|
@ -59,8 +60,6 @@ _CTYPEGET = ContentType.objects.get
|
|||
_GA = object.__getattribute__
|
||||
_SA = object.__setattr__
|
||||
_DA = object.__delattr__
|
||||
#_PLOADS = pickle.loads
|
||||
#_PDUMPS = pickle.dumps
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -102,7 +101,7 @@ class Attribute(SharedMemoryModel):
|
|||
# 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.
|
||||
|
||||
db_key = models.CharField('key', max_length=255, db_index=True)
|
||||
|
|
@ -111,7 +110,7 @@ class Attribute(SharedMemoryModel):
|
|||
# Lock storage
|
||||
db_lock_storage = models.TextField('locks', blank=True)
|
||||
# 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")
|
||||
# time stamp
|
||||
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
|
||||
# is the object in question).
|
||||
|
||||
|
||||
# key property (wraps db_key)
|
||||
#@property
|
||||
def __key_get(self):
|
||||
"Getter. Allows for value = self.key"
|
||||
return get_field_cache(self, "key")
|
||||
#@key.setter
|
||||
def __key_set(self, value):
|
||||
"Setter. Allows for self.key = value"
|
||||
set_field_cache(self, "key", value)
|
||||
#@key.deleter
|
||||
def __key_del(self):
|
||||
"Deleter. Allows for del self.key"
|
||||
raise Exception("Cannot delete objectdb key!")
|
||||
key = property(__key_get, __key_set, __key_del)
|
||||
#def __key_get(self):
|
||||
# "Getter. Allows for value = self.key"
|
||||
# return get_field_cache(self, "key")
|
||||
##@key.setter
|
||||
#def __key_set(self, value):
|
||||
# "Setter. Allows for self.key = value"
|
||||
# set_field_cache(self, "key", value)
|
||||
##@key.deleter
|
||||
#def __key_del(self):
|
||||
# "Deleter. Allows for del self.key"
|
||||
# raise Exception("Cannot delete objectdb key!")
|
||||
#key = property(__key_get, __key_set, __key_del)
|
||||
|
||||
# name property (wraps db_key too - alias to self.key)
|
||||
#@property
|
||||
def __name_get(self):
|
||||
"Getter. Allows for value = self.name"
|
||||
return get_field_cache(self, "key")
|
||||
#@name.setter
|
||||
return self.key
|
||||
#@name.sette
|
||||
def __name_set(self, value):
|
||||
"Setter. Allows for self.name = value"
|
||||
set_field_cache(self, "key", value)
|
||||
self.key = value
|
||||
#@name.deleter
|
||||
def __name_del(self):
|
||||
"Deleter. Allows for del self.name"
|
||||
raise Exception("Cannot delete name!")
|
||||
name = property(__name_get, __name_set, __name_del)
|
||||
|
||||
# typeclass_path property
|
||||
# typeclass_path property - we don't cache this.
|
||||
#@property
|
||||
def __typeclass_path_get(self):
|
||||
"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
|
||||
def __typeclass_path_set(self, value):
|
||||
"Setter. Allows for self.typeclass_path = value"
|
||||
set_field_cache(self, "typeclass_path", value)
|
||||
_SA(self, "_cached_typeclass", None)
|
||||
_SA(self, "db_typeclass_path", value)
|
||||
_GA(self, "save")(update_fields=["db_typeclass_path"])
|
||||
#@typeclass_path.deleter
|
||||
def __typeclass_path_del(self):
|
||||
"Deleter. Allows for del self.typeclass_path"
|
||||
self.db_typeclass_path = ""
|
||||
self.save()
|
||||
del_field_cache(self, "typeclass_path")
|
||||
_SA(self, "_cached_typeclass", None)
|
||||
_GA(self, "save")(update_fields=["db_typeclass_path"])
|
||||
typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
|
||||
|
||||
# date_created property
|
||||
|
|
@ -932,7 +930,7 @@ class TypedObject(SharedMemoryModel):
|
|||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
||||
db_obj=self, db_key__iexact=attribute_name)
|
||||
if attrib_obj:
|
||||
set_attr_cache(self, attribute_name, attrib_obj[0])
|
||||
set_attr_cache(attrib_obj[0])
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
|
@ -959,8 +957,9 @@ class TypedObject(SharedMemoryModel):
|
|||
if attrib_obj:
|
||||
# use old attribute
|
||||
attrib_obj = attrib_obj[0]
|
||||
set_attr_cache(attrib_obj) # renew cache
|
||||
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)
|
||||
if lockstring:
|
||||
attrib_obj.locks.add(lockstring)
|
||||
|
|
@ -973,7 +972,6 @@ class TypedObject(SharedMemoryModel):
|
|||
flush_attr_cache(self)
|
||||
self.delete()
|
||||
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):
|
||||
"""
|
||||
|
|
@ -985,7 +983,7 @@ class TypedObject(SharedMemoryModel):
|
|||
db_obj=self, db_key__iexact=attribute_name)
|
||||
if not attrib_obj:
|
||||
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
|
||||
|
||||
|
|
@ -1004,7 +1002,7 @@ class TypedObject(SharedMemoryModel):
|
|||
db_obj=self, db_key__iexact=attribute_name)
|
||||
if not attrib_obj:
|
||||
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.value
|
||||
|
||||
|
|
@ -1021,7 +1019,7 @@ class TypedObject(SharedMemoryModel):
|
|||
db_obj=self, db_key__iexact=attribute_name)
|
||||
if not attrib_obj:
|
||||
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.value
|
||||
|
||||
|
|
@ -1033,8 +1031,7 @@ class TypedObject(SharedMemoryModel):
|
|||
"""
|
||||
attr_obj = get_attr_cache(self, attribute_name)
|
||||
if attr_obj:
|
||||
del_attr_cache(self, attribute_name)
|
||||
attr_obj.delete()
|
||||
attr_obj.delete() # this will clear attr cache automatically
|
||||
else:
|
||||
try:
|
||||
_GA(self, "_attribute_class").objects.filter(
|
||||
|
|
@ -1051,8 +1048,7 @@ class TypedObject(SharedMemoryModel):
|
|||
"""
|
||||
attr_obj = get_attr_cache(self, attribute_name)
|
||||
if attr_obj:
|
||||
del_attr_cache(self, attribute_name)
|
||||
attr_obj.delete()
|
||||
attr_obj.delete() # this will clear attr cache automatically
|
||||
else:
|
||||
try:
|
||||
_GA(self, "_attribute_class").objects.filter(
|
||||
|
|
@ -1244,7 +1240,7 @@ class TypedObject(SharedMemoryModel):
|
|||
return None
|
||||
def __setattr__(self, key, value):
|
||||
# hook the oob handler here
|
||||
call_ndb_hooks(self, key, value)
|
||||
#call_ndb_hooks(self, key, value)
|
||||
_SA(self, key, value)
|
||||
self._ndb_holder = NdbHolder()
|
||||
return self._ndb_holder
|
||||
|
|
|
|||
|
|
@ -28,13 +28,10 @@ except ImportError:
|
|||
from django.db import transaction
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
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 import logger
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle")
|
||||
|
||||
PICKLE_PROTOCOL = 2
|
||||
|
|
@ -47,13 +44,21 @@ _FROM_MODEL_MAP = None
|
|||
_TO_MODEL_MAP = None
|
||||
_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__'
|
||||
_TO_DATESTRING = lambda o: _GA(o, "db_date_created").strftime("%Y:%m:%d-%H:%M:%S:%f")
|
||||
if uses_database("mysql"):
|
||||
from src.server.models import ServerConfig
|
||||
mysql_version = ServerConfig.objects.get_mysql_db_version()
|
||||
if mysql_version < '5.6.4':
|
||||
# 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")
|
||||
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 _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():
|
||||
"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.
|
||||
"""
|
||||
|
||||
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.signals import post_save, pre_delete, \
|
||||
post_syncdb
|
||||
from django.db.models.signals import post_save, pre_delete, post_syncdb
|
||||
|
||||
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.
|
||||
# if we are in a subprocess or not)
|
||||
from src import PROC_MODIFIED_OBJS
|
||||
|
|
@ -37,11 +44,19 @@ def _get_pids():
|
|||
if server_pid and portal_pid:
|
||||
return int(server_pid), int(portal_pid)
|
||||
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):
|
||||
# CL: upstream had a __new__ method that skipped ModelBase's __new__ if
|
||||
|
|
@ -68,13 +83,44 @@ class SharedMemoryModelBase(ModelBase):
|
|||
if cached_instance is None:
|
||||
cached_instance = new_instance()
|
||||
cls.cache_instance(cached_instance)
|
||||
|
||||
return cached_instance
|
||||
|
||||
|
||||
def _prepare(cls):
|
||||
cls.__instance_cache__ = {} #WeakValueDictionary()
|
||||
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):
|
||||
# CL: setting abstract correctly to allow subclasses to inherit the default
|
||||
|
|
@ -116,6 +162,13 @@ class SharedMemoryModel(Model):
|
|||
return result
|
||||
_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):
|
||||
"""
|
||||
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()
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
|
||||
def save(cls, *args, **kwargs):
|
||||
"overload spot for saving"
|
||||
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)
|
||||
"save method tracking process/thread issues"
|
||||
|
||||
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)
|
||||
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.
|
||||
def flush_cache(**kwargs):
|
||||
|
|
|
|||
|
|
@ -467,9 +467,9 @@ def delay(delay=2, retval=None, callback=None):
|
|||
"""
|
||||
Delay the return of a value.
|
||||
Inputs:
|
||||
to_return (any) - this will be returned by this function after a delay
|
||||
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:
|
||||
deferred that will fire with to_return after delay seconds
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
This structures the url tree for the news application.
|
||||
This structures the url tree for the news application.
|
||||
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',
|
||||
(r'^show/(?P<entry_id>\d+)/$', 'show_news'),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# 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.contrib import admin
|
||||
from django.views.generic import RedirectView
|
||||
|
|
@ -17,12 +17,12 @@ from django.db.models.loading import cache as model_cache
|
|||
if not model_cache.loaded:
|
||||
model_cache.get_models()
|
||||
|
||||
# loop over all settings.INSTALLED_APPS and execute code in
|
||||
# loop over all settings.INSTALLED_APPS and execute code in
|
||||
# files named admin.py in each such app (this will add those
|
||||
# models to the admin site)
|
||||
admin.autodiscover()
|
||||
|
||||
# Setup the root url tree from /
|
||||
# Setup the root url tree from /
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# User Authentication
|
||||
|
|
@ -36,11 +36,11 @@ urlpatterns = patterns('',
|
|||
|
||||
# Page place-holder for things that aren't implemented yet.
|
||||
url(r'^tbi/', 'src.web.website.views.to_be_implemented'),
|
||||
|
||||
|
||||
# Admin interface
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
|
||||
|
||||
# favicon
|
||||
url(r'^favicon\.ico$', RedirectView.as_view(url='/media/images/favicon.ico')),
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
This structures the (simple) structure of the
|
||||
webpage 'application'.
|
||||
This structures the (simple) structure of the
|
||||
webpage 'application'.
|
||||
"""
|
||||
from django.conf.urls import *
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
|
||||
"""
|
||||
This contains a simple view for rendering the webclient
|
||||
This contains a simple view for rendering the webclient
|
||||
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.conf import settings
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
|
||||
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
|
||||
pagevars = {'num_players_connected': SESSIONS.player_count()}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
This structures the (simple) structure of the
|
||||
webpage 'application'.
|
||||
This structures the (simple) structure of the
|
||||
webpage 'application'.
|
||||
"""
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from django.conf.urls import *
|
||||
|
||||
urlpatterns = patterns('src.web.website.views',
|
||||
(r'^$', 'page_index'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue