NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

basesession.py (77585B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\script\sys\basesession.py
      2 import copy
      3 import random
      4 import sys
      5 import types
      6 import weakref
      7 from collections import deque, defaultdict
      8 import blue
      9 import bluepy
     10 import carbon.common.script.net.machobase as machobase
     11 import carbon.common.script.util.format as formatUtil
     12 import localstorage
     13 import log
     14 import uthread
     15 from carbon.common.script.sys.service import CallWrapper, FastCallWrapper, ServiceCallWrapper, UnlockedServiceCallWrapper
     16 from carbon.common.script.sys.serviceConst import ROLE_LOGIN, ROLE_PLAYER, ROLE_REMOTESERVICE, ROLE_SERVICE, SERVICE_FAILED, SERVICE_RUNNING, SERVICE_STOPPED
     17 from carbon.common.script.sys.serviceProxy import ServiceProxy
     18 from carbon.common.lib import const
     19 from eveexceptions import UserError
     20 from eveprefs import prefs
     21 from eveservices.roles import has_any_role, remove_role, add_role
     22 from markers import PopMark, PushMark
     23 from stringutil import strx
     24 SESSIONCHANGEDELAY = 10 * const.SEC
     25 allObjectConnections = weakref.WeakSet()
     26 allConnectedObjects = weakref.WeakSet()
     27 service_sessions = {}
     28 local_sid = 0
     29 sessionsByAttribute = {'userid': defaultdict(set),
     30  'userType': defaultdict(set),
     31  'charid': defaultdict(set),
     32  'objectID': defaultdict(dict)}
     33 sessionsBySID = {}
     34 allSessionsBySID = weakref.WeakValueDictionary({})
     35 __contextOnlyTypes__ = []
     36 dyingObjects = deque()
     37 
     38 def ObjectKillah():
     39     n = 0
     40     while 1:
     41         maxNumObjects = prefs.GetValue('objectKillahMaxObjects', -1)
     42         sleepTime = prefs.GetValue('objectKilla.SleepWallclockTime', 30)
     43         if dyingObjects and (maxNumObjects <= 0 or n < maxNumObjects):
     44             delay = (dyingObjects[0][0] - blue.os.GetWallclockTime()) / const.SEC
     45             delay = min(delay, 30)
     46             if delay > 0:
     47                 blue.pyos.synchro.SleepWallclock(1000 * delay)
     48             else:
     49                 dietime, diediedie = dyingObjects.popleft()
     50                 try:
     51                     with bluepy.Timer('sessions::' + diediedie.GetObjectConnectionLogClass()):
     52                         diediedie.DisconnectObject()
     53                 except Exception:
     54                     log.LogException()
     55                     sys.exc_clear()
     56 
     57                 del diediedie
     58                 n += 1
     59         else:
     60             if maxNumObjects > 0 and n >= maxNumObjects:
     61                 log.general.Log("ObjectKillah killed the maximum number of allowed objects for this round, %d, which means that we're lagging behind! Sleeping for %d seconds" % (maxNumObjects, sleepTime), log.LGWARN)
     62             elif n > 0:
     63                 if maxNumObjects > 0:
     64                     log.general.Log('ObjectKillah killed %d objects for this round out of a maximum of %d. Sleeping for %d seconds' % (n, maxNumObjects, sleepTime), log.LGINFO)
     65                 else:
     66                     log.general.Log('ObjectKillah killed %d objects for this round. Sleeping for %d seconds' % (n, sleepTime), log.LGINFO)
     67             n = 0
     68             blue.pyos.synchro.SleepWallclock(1000 * sleepTime)
     69 
     70 
     71 objectKillaThread = uthread.new(ObjectKillah)
     72 objectKillaThread.context = 'sessions::ObjectKillah'
     73 
     74 class ObjectConnection():
     75     __passbyvalue__ = 0
     76     __restrictedcalls__ = {'PseudoMethodCall': 1,
     77      'LogPseudoMethodCall': 1,
     78      'GetObjectConnectionLogClass': 1,
     79      'RedirectObject': 1,
     80      'Objectcast': 1}
     81 
     82     def __init__(self, sess, object, c2ooid, serviceName = None, bindParams = None):
     83         if object is None:
     84             sess.LogSessionError('Establishing an object connection to None')
     85             log.LogTraceback()
     86         self.__dict__['__last_used__'] = blue.os.GetWallclockTime()
     87         self.__dict__['__constructing__'] = 1
     88         self.__dict__['__deleting__'] = 0
     89         self.__dict__['__machoObjectUUID__'] = GetObjectUUID(object)
     90         self.__dict__['__redirectObject__'] = None
     91         self.__dict__['__disconnected__'] = 1
     92         self.__dict__['__session__'] = weakref.proxy(sess)
     93         self.__dict__['__c2ooid__'] = c2ooid
     94         self.__dict__['__object__'] = object
     95         self.__dict__['__serviceName__'] = serviceName
     96         self.__dict__['__bindParams__'] = bindParams
     97         self.__dict__['__publicattributes__'] = getattr(object, '__publicattributes__')
     98         try:
     99             lock = None
    100             if not object.sessionConnections:
    101                 if has_any_role(sess, ROLE_PLAYER):
    102                     sm.GetService(serviceName).LogInfo('Player session re-aquiring big lock.  Session:', sess)
    103                     lock = (sm.GetService(serviceName).LockService(bindParams), bindParams)
    104                 object.service = weakref.proxy(sm.GetService(serviceName))
    105                 object.session = weakref.proxy(object.service.session)
    106                 for depServiceName in getattr(object, '__dependencies__', []):
    107                     if not hasattr(object.service, depServiceName):
    108                         dep = object.service.session.ConnectToService(depServiceName)
    109                         setattr(object.service, depServiceName, dep)
    110                     setattr(object, depServiceName, getattr(object.service, depServiceName))
    111 
    112                 self.PseudoMethodCall(object, 'OnRun')
    113             if has_any_role(sess, ROLE_PLAYER) and lock is None:
    114                 charid = sess.charid
    115                 lock = (object.service.LockService((bindParams, charid)), (bindParams, charid))
    116             if sess.sid not in object.sessionConnections:
    117                 object.sessionConnections[sess.sid] = sess
    118                 object.objectConnections[sess.sid] = {}
    119                 try:
    120                     self.PseudoMethodCall(object, 'OnSessionAttach')
    121                 except Exception:
    122                     object.sessionConnections.pop(sess.sid, None)
    123                     object.objectConnections.pop(sess.sid, None)
    124                     log.LogTraceback('OnSessionAttach failed in ObjectConnection')
    125                     log.LogException()
    126                     raise
    127 
    128             else:
    129                 self.PseudoMethodCall(object, 'OnSessionReattach')
    130             object.objectConnections[sess.sid][c2ooid] = self
    131             sess.connectedObjects[self.__c2ooid__] = (self,)
    132             self.__disconnected__ = 0
    133             self.__pendingDisconnect__ = 0
    134             allObjectConnections.add(self)
    135             allConnectedObjects.add(object)
    136         finally:
    137             if lock is not None:
    138                 sm.GetService(serviceName).UnLockService(lock[1], lock[0])
    139 
    140         self.__dict__['__constructing__'] = 0
    141 
    142     def GetSession(self):
    143         return self.__session__
    144 
    145     def __del__(self):
    146         if not self.__deleting__:
    147             self.__deleting__ = 1
    148             if not self.__disconnected__:
    149                 if log.methodcalls.IsOpen(2):
    150                     log.methodcalls.Log("Curious....   Disconnecting during __del__ wasn't entirely expected...", 2, 1)
    151                 self.DisconnectObject(30)
    152 
    153     def __str__(self):
    154         return 'ObjectConnection to ' + strx(self.__object__)
    155 
    156     def __repr__(self):
    157         return self.__str__()
    158 
    159     def GetInstanceID(self):
    160         return self.__object__.machoInstanceID
    161 
    162     def PseudoMethodCall(self, object, method, *args, **keywords):
    163         global CallTimer
    164         self.__dict__['__last_used__'] = blue.os.GetWallclockTime()
    165         if hasattr(object, method):
    166             with self.__session__.Masquerade({'base.caller': weakref.ref(self)}):
    167                 with CallTimer(machobase.GetLogName(object) + '::' + method):
    168                     try:
    169                         result = getattr(object, method)(*args, **keywords)
    170                     except Exception as e:
    171                         self.LogPseudoMethodCall(e, method)
    172                         raise
    173 
    174                     self.LogPseudoMethodCall(result, method)
    175             return result
    176 
    177     def LogPseudoMethodCall(self, result, method, *args, **keywords):
    178         logChannel = log.methodcalls
    179         if logChannel.IsOpen(log.LGINFO):
    180             logname = machobase.GetLogName(self.__object__)
    181             if isinstance(result, Exception):
    182                 eorr = ', EXCEPTION='
    183             else:
    184                 eorr = ', retval='
    185             if keywords:
    186                 logwhat = [logname,
    187                  '::',
    188                  method,
    189                  ' args=',
    190                  args,
    191                  ', keywords={',
    192                  keywords,
    193                  '}',
    194                  eorr,
    195                  result]
    196             else:
    197                 logwhat = [logname,
    198                  '::',
    199                  method,
    200                  ' args=',
    201                  args,
    202                  eorr,
    203                  result]
    204             timer = PushMark(logname + '::LogPseudoMethodCall')
    205             try:
    206                 s = ''.join(map(strx, logwhat))
    207                 if len(s) > 2500:
    208                     s = s[:2500]
    209                 while len(s) > 255:
    210                     logChannel.Log(s[:253], log.LGINFO, 1)
    211                     s = '- ' + s[253:]
    212 
    213                 logChannel.Log(s, log.LGINFO, 1)
    214             except TypeError:
    215                 logChannel.Log('[X]'.join(map(strx, logwhat)).replace('\x00', '\\0'), log.LGINFO, 1)
    216                 sys.exc_clear()
    217             except UnicodeEncodeError:
    218                 logChannel.Log('[U]'.join(map(lambda x: x.encode('ascii', 'replace'), map(unicode, logwhat))), log.LGINFO, 1)
    219                 sys.exc_clear()
    220             finally:
    221                 PopMark(timer)
    222 
    223     def GetObjectConnectionLogClass(self):
    224         return machobase.AssignLogName(self.__object__)
    225 
    226     def __GetObjectType(self):
    227         return 'ObjectConnection to ' + self.GetObjectConnectionLogClass()
    228 
    229     def RedirectObject(self, serviceName = None, bindParams = None):
    230         if serviceName is None:
    231             serviceName = self.__serviceName__
    232         if bindParams is None:
    233             bindParams = self.__bindParams__
    234         self.__redirectObject__ = (serviceName, bindParams)
    235 
    236     def DisconnectObject(self, delaySecs = 0):
    237         if delaySecs:
    238             if not self.__pendingDisconnect__:
    239                 dyingObjects.append((blue.os.GetWallclockTime() + delaySecs * const.SEC, self))
    240                 self.__pendingDisconnect__ = 1
    241             else:
    242                 self.__pendingDisconnect__ += 1
    243                 if self.__pendingDisconnect__ in (10, 100, 1000, 10000, 100000, 1000000, 10000000):
    244                     if not prefs.GetValue('suppressObjectKillahDupSpam', 0):
    245                         log.LogTraceback('Many duplicate requests (%d) to add object to objectKillah dyingObjects list - %r %r' % (self.__pendingDisconnect__, self, self.GetObjectConnectionLogClass()), severity=log.LGWARN)
    246             return
    247         if not self.__disconnected__:
    248             self.__disconnected__ = 1
    249             try:
    250                 if not sm.IsServiceRunning(self.__serviceName__):
    251                     return
    252                 service = sm.GetService(self.__serviceName__)
    253                 lock = None
    254                 if self.__session__.role & ROLE_PLAYER:
    255                     charid = getattr(self.__session__, 'charid')
    256                     lock = sm.GetService(self.__serviceName__).LockService((self.__bindParams__, charid))
    257                 try:
    258                     objectID = GetObjectUUID(self)
    259                     if objectID in self.__session__.machoObjectsByID:
    260                         objectType = self.__GetObjectType()
    261                         self.__session__.LogSessionHistory('%s object %s disconnected from this session by the server' % (objectType, objectID), strx(self.__object__))
    262                         self.__session__.UnregisterMachoObject(objectID, None, 0)
    263                     service.RemoveSessionConnectionFromObject(self)
    264                 finally:
    265                     if lock:
    266                         sm.GetService(self.__serviceName__).UnLockService((self.__bindParams__, charid), lock)
    267 
    268             except ReferenceError:
    269                 pass
    270             except Exception:
    271                 log.LogException()
    272 
    273             self.__object__ = None
    274 
    275     def __getattr__(self, method):
    276         if method in self.__dict__:
    277             return self.__dict__[method]
    278         if not method.isupper():
    279             if method in self.__dict__['__publicattributes__']:
    280                 return getattr(self.__object__, method)
    281             if method.startswith('__'):
    282                 raise AttributeError(method)
    283         if self.__c2ooid__ not in self.__session__.connectedObjects:
    284             self.__session__.LogSessionHistory('Object no longer live:  c2ooid=' + strx(self.__c2ooid__) + ', serviceName=' + strx(self.__serviceName__) + ', bindParams=' + strx(self.__bindParams__) + ', uuid=' + strx(self.__machoObjectUUID__), None, 1)
    285             self.__session__.LogSessionError('This object connection is no longer live')
    286             raise RuntimeError('This object connection is no longer live')
    287         self.__dict__['__last_used__'] = blue.os.GetWallclockTime()
    288         if self.__session__.role & (ROLE_SERVICE | ROLE_REMOTESERVICE) == ROLE_SERVICE:
    289             if method.endswith('_Ex'):
    290                 return FastCallWrapper(self.__session__, self.__object__, method[:-3], self)
    291             else:
    292                 return FastCallWrapper(self.__session__, self.__object__, method, self)
    293         return CallWrapper(self.__session__, self.__object__, method, self, self.GetObjectConnectionLogClass())
    294 
    295 
    296 def FindClientsAndHoles(attr, val, maxCount):
    297     ret = []
    298     nf = []
    299     attributes = attr.split('&')
    300     if attributes[0] not in sessionsByAttribute:
    301         log.LogWarn('FindClientsAndHoles by non-existent attribute ', attributes[0])
    302         return (0, ret, nf)
    303     if len(attributes) > 1:
    304         if len(attributes) != len(val):
    305             raise RuntimeError('For a complex session query, the value must be a tuple of equal length to the complex query params')
    306         for i in range(len(val[0])):
    307             f = 0
    308             r = []
    309             for sid in sessionsByAttribute[attributes[0]].get(val[0][i], set()):
    310                 if sid in sessionsBySID:
    311                     sess = sessionsBySID[sid]
    312                     k = 1
    313                     for j in range(1, len(val)):
    314                         if attributes[j] in ('corprole', 'rolesAtAll', 'rolesAtHQ', 'rolesAtBase', 'rolesAtOther'):
    315                             corprole = getattr(sess, attributes[j])
    316                             k = 0
    317                             for r2 in val[j]:
    318                                 if r2 and r2 & corprole == r2:
    319                                     k = 1
    320                                     break
    321 
    322                         elif getattr(sess, attributes[j]) not in val[j]:
    323                             k = 0
    324                         if not k:
    325                             break
    326 
    327                     if k:
    328                         f = 1
    329                         clientID = getattr(sessionsBySID[sid], 'clientID', None)
    330                         if clientID is not None:
    331                             if maxCount is not None and len(ret) >= maxCount:
    332                                 return (1, [], [])
    333                             ret.append(clientID)
    334                 else:
    335                     r.append(sid)
    336 
    337             for each in r:
    338                 try:
    339                     sessionsByAttribute[attributes[0]][val[0][i]].discard(each)
    340                 except AttributeError:
    341                     sessionsByAttribute[attributes[0]][val[0][i]].pop(each, None)
    342 
    343             if not f:
    344                 nf.append(val[0][i])
    345 
    346         if len(nf):
    347             nf2 = list(copy.copy(val))
    348             nf2[0] = nf
    349             nf = nf2
    350         return (0, ret, nf)
    351     for v in val:
    352         f = 0
    353         r = []
    354         for sid in sessionsByAttribute[attr].get(v, set()):
    355             if sid in sessionsBySID:
    356                 clientID = getattr(sessionsBySID[sid], 'clientID', None)
    357                 if clientID is not None:
    358                     if maxCount is not None and len(ret) >= maxCount:
    359                         return (1, [], [])
    360                     ret.append(clientID)
    361                     f = 1
    362             else:
    363                 r.append(sid)
    364 
    365         if r:
    366             sessionsWithAttribute = sessionsByAttribute[attr][v]
    367             for each in r:
    368                 if isinstance(sessionsWithAttribute, dict):
    369                     sessionsWithAttribute.pop(each, None)
    370                 else:
    371                     sessionsWithAttribute.discard(each)
    372 
    373         if not f:
    374             nf.append(v)
    375 
    376     return (0, ret, nf)
    377 
    378 
    379 def FindSessions(attr, val):
    380     ret = []
    381     for v in val:
    382         try:
    383             r = []
    384             for sid in sessionsByAttribute[attr].get(v, set()):
    385                 if sid in sessionsBySID:
    386                     ret.append(sessionsBySID[sid])
    387                 else:
    388                     r.append(sid)
    389 
    390             if r:
    391                 sessionsWithAttribute = sessionsByAttribute[attr][v]
    392                 for each in r:
    393                     if isinstance(sessionsWithAttribute, dict):
    394                         sessionsWithAttribute.pop(each, None)
    395                     else:
    396                         sessionsWithAttribute.discard(each)
    397 
    398         except:
    399             srv = sm.services['sessionMgr']
    400             srv.LogError('Session map borked')
    401             srv.LogError('sessionsByAttribute=', sessionsByAttribute)
    402             srv.LogError('sessionsBySID=', sessionsBySID)
    403             log.LogTraceback()
    404             raise
    405 
    406     if len(ret) > 1 and attr in ('charid', 'userid'):
    407         return sorted(ret, key=lambda s: s.localSID, reverse=True)
    408     return ret
    409 
    410 
    411 def FindSessionClient(attr, val):
    412     session = FindSessions(attr, val)
    413     if session:
    414         return getattr(session[0], 'clientID', None)
    415 
    416 
    417 callTimerKeys = {}
    418 serviceCallTimes = {}
    419 webCallTimes = {}
    420 userCallTimes = {}
    421 outstandingCallTimers = []
    422 methodCallHistory = deque(maxlen=1000)
    423 
    424 class RealCallTimer():
    425     TimerType = 2
    426 
    427     def __init__(self, k):
    428         k = callTimerKeys.setdefault(k, k)
    429         self.key = k
    430         localstorage.UpdateLocalStorage({'calltimer.key': k})
    431         if not session:
    432             self.mask = GetServiceSession('DefaultCallTimer').Masquerade()
    433         else:
    434             self.mask = None
    435         self.start = blue.os.GetWallclockTimeNow()
    436         outstandingCallTimers.append((k, self.start))
    437 
    438     def Done(self):
    439         stop = blue.os.GetWallclockTimeNow()
    440         t = stop - self.start
    441         if t < 0:
    442             log.general.Log('blue.os.GetWallclockTimeNow() is running backwards... now=%s, start=%s' % (stop, self.start), 2, 1)
    443             t = 0
    444         if session and not session.role & ROLE_SERVICE:
    445             if getattr(session, 'clientID', 0):
    446                 other = userCallTimes
    447             else:
    448                 other = webCallTimes
    449         else:
    450             other = serviceCallTimes
    451         for calltimes in (session.calltimes, other):
    452             if self.key not in calltimes:
    453                 theCallTime = [0,
    454                  0,
    455                  -1,
    456                  -1,
    457                  0,
    458                  0]
    459                 calltimes[self.key] = theCallTime
    460             else:
    461                 theCallTime = calltimes[self.key]
    462             theCallTime[0] += 1
    463             theCallTime[1] += t
    464             if theCallTime[2] == -1 or t < theCallTime[2]:
    465                 theCallTime[2] = t
    466             if theCallTime[3] == -1 or t > theCallTime[3]:
    467                 theCallTime[3] = t
    468 
    469         if self.mask:
    470             self.mask.UnMask()
    471         if machobase.mode == 'client':
    472             k = (self.key, self.start, t)
    473             methodCallHistory.append(k)
    474         try:
    475             outstandingCallTimers.remove((self.key, self.start))
    476         except:
    477             sys.exc_clear()
    478 
    479     def __enter__(self):
    480         pass
    481 
    482     def __exit__(self, e, v, tb):
    483         self.Done()
    484 
    485 
    486 class BasicCallTimer(object):
    487     TimerType = 1
    488 
    489     def __init__(self, k):
    490         k = callTimerKeys.setdefault(k, k)
    491         self.key = k
    492         self.start = blue.os.GetWallclockTimeNow()
    493 
    494     def Done(self):
    495         stop = blue.os.GetWallclockTimeNow()
    496         elapsed = stop - self.start
    497         if session and not session.role & ROLE_SERVICE:
    498             if getattr(session, 'clientID', 0):
    499                 callTimes = userCallTimes
    500             else:
    501                 callTimes = webCallTimes
    502         else:
    503             callTimes = serviceCallTimes
    504         if elapsed < 0:
    505             log.general.Log('blue.os.GetWallclockTimeNow() is running backwards... now=%s, start=%s' % (stop, self.start), 2, 1)
    506             elapsed = 0
    507         if self.key not in callTimes:
    508             callEntry = [0,
    509              0,
    510              -1,
    511              -1,
    512              0,
    513              0]
    514             callTimes[self.key] = callEntry
    515         else:
    516             callEntry = callTimes[self.key]
    517         callEntry[0] += 1
    518         callEntry[1] += elapsed
    519         if callEntry[2] == -1 or elapsed < callEntry[2]:
    520             callEntry[2] = elapsed
    521         if callEntry[3] == -1 or elapsed > callEntry[3]:
    522             callEntry[3] = elapsed
    523 
    524     def __enter__(self):
    525         pass
    526 
    527     def __exit__(self, e, v, tb):
    528         self.Done()
    529 
    530 
    531 class DummyCallTimer(object):
    532     TimerType = 0
    533 
    534     def __init__(self, k):
    535         pass
    536 
    537     def Done(self):
    538         pass
    539 
    540     def __enter__(self):
    541         pass
    542 
    543     def __exit__(self, e, v, tb):
    544         pass
    545 
    546 
    547 CallTimer = DummyCallTimer
    548 
    549 def EnableCallTimers(timerType):
    550     global CallTimer
    551     was = CallTimer.TimerType
    552     if timerType == 0:
    553         CallTimer = DummyCallTimer
    554     elif timerType == 1:
    555         CallTimer = BasicCallTimer
    556     else:
    557         CallTimer = RealCallTimer
    558     return was
    559 
    560 
    561 def IsInClientContext():
    562     return 'base.ClientContext' in localstorage.GetLocalStorage()
    563 
    564 
    565 def CallTimersEnabled():
    566     return CallTimer.TimerType
    567 
    568 
    569 def GetCallTimes():
    570     return (userCallTimes, serviceCallTimes, webCallTimes)
    571 
    572 
    573 def GetNewSid():
    574     return random.getrandbits(63)
    575 
    576 
    577 def CloseSession(sess, isRemote = False):
    578     if sess is not None:
    579         if hasattr(sess, 'remoteServiceSessionRefCount'):
    580             sess.__dict__['remoteServiceSessionRefCount'] -= 1
    581             if sess.__dict__['remoteServiceSessionRefCount'] > 0:
    582                 return
    583         if sess.sid in sessionsBySID:
    584             sess.ClearAttributes(isRemote)
    585 
    586 
    587 class CoreSession():
    588     __persistvars__ = ['userid',
    589      'languageID',
    590      'role',
    591      'charid',
    592      'address',
    593      'userType',
    594      'maxSessionTime',
    595      'sessionType',
    596      'countryCode']
    597     __nonpersistvars__ = ['sid',
    598      'c2ooid',
    599      'connectedObjects',
    600      'connectedServices',
    601      'clientID',
    602      'localSID',
    603      'contextOnly',
    604      'receivedVersion',
    605      'irrelevanceTime']
    606     __attributesWithDefaultValueOfZero__ = []
    607     __characterDependantAttributes__ = {'objectID',
    608      'wingid',
    609      'constellationid',
    610      'regionid',
    611      'locationid',
    612      'shipid',
    613      'squadid',
    614      'solarsystemid',
    615      'charid',
    616      'corpid',
    617      'structureid',
    618      'stationid',
    619      'fleetid',
    620      'warfactionid',
    621      'allianceid',
    622      'solarsystemid2',
    623      'inDetention'}
    624     __userDependantAttributes__ = {'userid',
    625      'languageID',
    626      'address',
    627      'userType',
    628      'maxSessionTime',
    629      'countryCode',
    630      'role'}
    631 
    632     def __init__(self, sid, localSID, role, sessionType, defaultVarList = []):
    633         global __contextOnlyTypes__
    634         if sessionType not in const.session.VALID_SESSION_TYPES:
    635             raise ValueError('Trying to create a session with an invalid session type')
    636         d = self.__dict__
    637         d['additionalNoSetAttributes'] = []
    638         defaultVarList = self.__persistvars__ + defaultVarList
    639         for attName in defaultVarList:
    640             d[attName] = self.GetDefaultValueOfAttribute(attName)
    641 
    642         d['version'] = 1
    643         d['receivedVersion'] = 1
    644         d['irrelevanceTime'] = None
    645         d['sid'] = sid
    646         d['localSID'] = localSID
    647         d['role'] = None
    648         d['sessionType'] = sessionType
    649         d['c2ooid'] = 1
    650         d['connectedObjects'] = {}
    651         d['connectedServices'] = {}
    652         d['calltimes'] = {}
    653         d['notificationID'] = 0
    654         d['machoObjectsByID'] = {}
    655         d['machoObjectConnectionsByObjectID'] = {}
    656         d['sessionVariables'] = {}
    657         d['lastRemoteCall'] = blue.os.GetWallclockTime()
    658         d['nextSessionChange'] = None
    659         d['sessionChangeReason'] = 'Initial State'
    660         d['rwlock'] = None
    661         d['sessionhist'] = []
    662         d['hasproblems'] = 0
    663         d['mutating'] = 0
    664         d['changing'] = None
    665         d['additionalDistributedProps'] = []
    666         d['additionalNonIntegralAttributes'] = []
    667         d['longAttributes'] = []
    668         d['additionalAttributesToPrint'] = []
    669         d['additionalHexAttributes'] = []
    670         d['callThrottling'] = {}
    671         d['contextOnly'] = machobase.mode == 'server' and sessionType in __contextOnlyTypes__
    672         d['countryCode'] = None
    673         self.__ChangeAttribute('role', role)
    674         self.LogSessionHistory('Session created')
    675 
    676     def Throttle(self, key, throttleTimes, throttleInterval, userErrorMessage, userErrorParams = None):
    677         lastKeyTimes = self.callThrottling.get(key, [])
    678         now = blue.os.GetWallclockTime()
    679         if len(lastKeyTimes) >= throttleTimes:
    680             temp = []
    681             earliestTime = None
    682             for time in lastKeyTimes:
    683                 if now - time < throttleInterval:
    684                     temp.append(time)
    685                     if earliestTime is None or time < earliestTime:
    686                         earliestTime = time
    687 
    688             if len(temp) >= throttleTimes:
    689                 self.callThrottling[key] = temp
    690                 remainingTime = earliestTime + throttleInterval - now if earliestTime is not None else throttleInterval
    691                 mergedErrorParams = {'remainingTime': max(0, remainingTime)}
    692                 if userErrorParams is not None:
    693                     mergedErrorParams.update(userErrorParams)
    694                 raise UserError(userErrorMessage, mergedErrorParams)
    695             else:
    696                 lastKeyTimes = temp
    697         lastKeyTimes.append(now)
    698         self.callThrottling[key] = lastKeyTimes
    699 
    700     def IsMutating(self):
    701         return self.mutating
    702 
    703     def IsChanging(self):
    704         return self.changing is not None
    705 
    706     def IsItSafe(self):
    707         return not (self.IsMutating() or self.IsChanging())
    708 
    709     def IsItSemiSafe(self):
    710         return self.IsItSafe() or self.IsMutating() and self.IsChanging()
    711 
    712     def WaitUntilSafe(self):
    713         if session.IsItSafe():
    714             return
    715         timeWaited = 0
    716         while timeWaited <= 30000 and not session.IsItSafe():
    717             blue.pyos.synchro.SleepWallclock(100)
    718             timeWaited += 100
    719 
    720         if not session.IsItSafe():
    721             raise RuntimeError('Session did not become safe within 30secs')
    722 
    723     def RegisterMachoObjectConnection(self, objectID, connection, refID):
    724         connectionID = GetObjectUUID(connection)
    725         if objectID not in self.machoObjectConnectionsByObjectID:
    726             self.machoObjectConnectionsByObjectID[objectID] = [0, weakref.WeakValueDictionary({})]
    727         self.machoObjectConnectionsByObjectID[objectID][1][connectionID] = connection
    728         self.machoObjectConnectionsByObjectID[objectID][0] = max(self.machoObjectConnectionsByObjectID[objectID][0], refID)
    729 
    730     def UnregisterMachoObjectConnection(self, objectID, connection):
    731         connectionID = GetObjectUUID(connection)
    732         if objectID not in self.machoObjectConnectionsByObjectID:
    733             return None
    734         else:
    735             if connectionID in self.machoObjectConnectionsByObjectID[objectID][1]:
    736                 log.LogTraceback('Unexpected Crapola:  connectionID still found in machoObjectConnectionsByObjectID[objectID]')
    737                 del self.machoObjectConnectionsByObjectID[objectID][1][connectionID]
    738             if not self.machoObjectConnectionsByObjectID[objectID][1]:
    739                 refID = self.machoObjectConnectionsByObjectID[objectID][0]
    740                 del self.machoObjectConnectionsByObjectID[objectID]
    741                 return refID
    742             return None
    743 
    744     def RegisterMachoObject(self, objectID, object, refID):
    745         sessionsByAttribute['objectID'][objectID][self.sid] = max(refID, sessionsByAttribute['objectID'][objectID].get(self.sid, 0))
    746         if objectID in self.machoObjectsByID:
    747             self.machoObjectsByID[objectID][0] = max(refID, self.machoObjectsByID[objectID][0])
    748         else:
    749             self.machoObjectsByID[objectID] = [refID, object]
    750 
    751     def UnregisterMachoObject(self, objectID, refID, suppressnotification = 1):
    752         try:
    753             if objectID in self.machoObjectConnectionsByObjectID:
    754                 del self.machoObjectConnectionsByObjectID[objectID]
    755             if objectID in sessionsByAttribute['objectID']:
    756                 if self.sid in sessionsByAttribute['objectID'][objectID]:
    757                     if refID is None or refID >= sessionsByAttribute['objectID'][objectID][self.sid]:
    758                         del sessionsByAttribute['objectID'][objectID][self.sid]
    759                         if not sessionsByAttribute['objectID'][objectID]:
    760                             del sessionsByAttribute['objectID'][objectID]
    761             if objectID in self.machoObjectsByID:
    762                 if refID is None or refID >= self.machoObjectsByID[objectID][0]:
    763                     object = self.machoObjectsByID[objectID][1]
    764                     del self.machoObjectsByID[objectID]
    765                     if machobase.mode != 'client' and not suppressnotification and getattr(self, 'clientID', 0) and not getattr(self, 'clearing_session', 0):
    766                         sm.services['machoNet'].Scattercast('+clientID', [self.clientID], 'OnMachoObjectDisconnect', objectID, self.clientID, refID)
    767                     if isinstance(object, ObjectConnection):
    768                         object.DisconnectObject()
    769         except Exception:
    770             log.LogException()
    771             sys.exc_clear()
    772 
    773     def DumpSession(self, prefix, reason):
    774         loglines = [prefix + ':  ' + reason + ".  It's history is as follows:"]
    775         lastEntry = ''
    776         for eachHistoryRecord in self.sessionhist:
    777             header = prefix + ':  ' + formatUtil.FmtDateEng(eachHistoryRecord[0], 'll') + ': '
    778             lines = eachHistoryRecord[1].split('\n')
    779             tmp = eachHistoryRecord[2]
    780             if tmp == lastEntry:
    781                 txt = '< same >'
    782             else:
    783                 txt = tmp
    784             lastEntry = tmp
    785             footer = ', ' + txt
    786             for eachLine in lines[:len(lines) - 1]:
    787                 loglines.append(header + eachLine)
    788 
    789             loglines.append(header + lines[len(lines) - 1] + footer)
    790 
    791         loglines.append(prefix + ':  Current Session Data:  %s' % strx(self))
    792         currentcall = localstorage.GetLocalStorage().get('base.currentcall', None)
    793         if currentcall:
    794             try:
    795                 currentcall = currentcall()
    796                 loglines.append('currentcall was: ' + strx(currentcall))
    797             except ReferenceError:
    798                 sys.exc_clear()
    799 
    800         log.general.Log('\n'.join(loglines), 1, 2)
    801 
    802     def Masquerade(self, props = None):
    803         w = weakref.ref(self)
    804         if self.charid:
    805             tmp = {'base.session': w,
    806              'base.charsession': w}
    807         else:
    808             tmp = {'base.session': w}
    809         if props is not None:
    810             tmp.update(props)
    811         return MasqueradeMask(tmp)
    812 
    813     def GetActualSession(self):
    814         return self
    815 
    816     def LogSessionHistory(self, reason, details = None, noBlather = 0):
    817         if self.role & ROLE_SERVICE and not self.hasproblems:
    818             return
    819         timer = PushMark('LogSessionHistory')
    820         try:
    821             if details is None:
    822                 details = ''
    823                 for each in ['sid', 'clientID'] + self.__persistvars__:
    824                     if getattr(self, each, None) is not None:
    825                         details = details + each + ':' + strx(getattr(self, each)) + ', '
    826 
    827                 details = 'session=' + details[:-2]
    828             else:
    829                 details = 'info=' + strx(details)
    830             self.__dict__['sessionhist'].append((blue.os.GetWallclockTime(), strx(reason)[:255], strx(details)[:255]))
    831             if len(self.__dict__['sessionhist']) > 120:
    832                 self.__dict__['sessionhist'] = self.__dict__['sessionhist'][70:]
    833             if not noBlather and log.general.IsOpen(1) and 'sm' in globals() and not sm.services['machoNet'].IsClusterShuttingDown():
    834                 log.general.Log('SessionHistory:  reason=%s, %s' % (reason, strx(details)), 1, 1)
    835         finally:
    836             PopMark(timer)
    837 
    838     def LogSessionError(self, what, novalidate = 0):
    839         self.__LogSessionProblem(what, 4, novalidate)
    840 
    841     def __LogSessionProblem(self, what, how, novalidate = 0):
    842         self.hasproblems = 1
    843         self.LogSessionHistory(what)
    844         if log.general.IsOpen(how):
    845             lines = ['A session related error has occurred.  Session history:']
    846             for eachHistoryRecord in self.sessionhist:
    847                 s = ''.join(map(strx, formatUtil.FmtDateEng(eachHistoryRecord[0], 'll') + ': ' + eachHistoryRecord[1] + ', ' + eachHistoryRecord[2]))
    848                 if len(s) > 5000:
    849                     s = s[:5000]
    850                 while len(s) > 255:
    851                     lines.append(s[:253])
    852                     s = '- ' + s[253:]
    853 
    854                 lines.append(s)
    855 
    856             lines.append('Session Data (should be identical to last history record):  %s' % strx(self))
    857             try:
    858                 currentcall = localstorage.GetLocalStorage().get('base.currentcall', None)
    859                 if currentcall:
    860                     currentcall = currentcall()
    861                     lines.append('currentcall was: ' + strx(currentcall))
    862             except ReferenceError:
    863                 sys.exc_clear()
    864 
    865             log.general.Log('\n'.join(lines), how, 2)
    866         if not novalidate:
    867             self.ValidateSession('session-error-dump')
    868 
    869     def SetSessionVariable(self, k, v):
    870         if v is None:
    871             try:
    872                 del self.__dict__['sessionVariables'][k]
    873             except:
    874                 sys.exc_clear()
    875 
    876         else:
    877             self.__dict__['sessionVariables'][k] = v
    878 
    879     def GetSessionVariable(self, k, defaultValue = None):
    880         try:
    881             return self.__dict__['sessionVariables'][k]
    882         except:
    883             sys.exc_clear()
    884             if defaultValue is not None:
    885                 self.__dict__['sessionVariables'][k] = defaultValue
    886                 return defaultValue
    887             return
    888 
    889     def GetDistributedProps(self, getAll):
    890         retval = []
    891         if self.role & ROLE_SERVICE == 0:
    892             for attribute in self.__persistvars__ + self.additionalDistributedProps:
    893                 if getAll or self.__dict__[attribute] != self.GetDefaultValueOfAttribute(attribute):
    894                     retval.append(attribute)
    895 
    896         return retval
    897 
    898     __dependant_attributes__ = {'userid': ['role',
    899                 'charid',
    900                 'callback',
    901                 'languageID',
    902                 'userType',
    903                 'maxSessionTime',
    904                 'countryCode'],
    905      'sid': ['userid']}
    906 
    907     def DependantAttributes(self, attribute):
    908         retval = self.__dependant_attributes__.get(attribute, [])
    909         retval2 = set()
    910         for each in retval:
    911             retval2.add(each)
    912             for other in self.DependantAttributes(each):
    913                 retval2.add(other)
    914 
    915         return list(retval2)
    916 
    917     def GetDefaultValueOfAttribute(self, attribute):
    918         if attribute == 'role':
    919             return ROLE_LOGIN
    920         if attribute == 'sessionType':
    921             return self.__dict__.get('sessionType', None)
    922         if attribute in self.__attributesWithDefaultValueOfZero__:
    923             return 0
    924         if attribute == 'languageID' and machobase.mode == 'client':
    925             return strx(prefs.GetValue('languageID', 'EN'))
    926 
    927     def _DisconnectSessionObjects(self):
    928         for each in self.__dict__['connectedObjects'].values():
    929             each[0].DisconnectObject()
    930 
    931         for objectID in copy.copy(self.__dict__['machoObjectsByID']):
    932             self.UnregisterMachoObject(objectID, None)
    933 
    934     def ClearCharacterDependantAttributes(self):
    935         machoNet = sm.GetServiceIfRunning('machoNet')
    936         if machoNet and machoNet.IsClusterShuttingDown() and self.userid:
    937             log.general.Log('Cluster shutting down, rejecting session clearing %s' % self.userid, log.LGINFO)
    938             return
    939         self.StartSessionChange(reason='ClearCharacterDependantAttributes')
    940         try:
    941             if getattr(self, 'clearing_session', 0):
    942                 self.LogSessionHistory("Tried to clear a cleared/clearing session's attributes")
    943             else:
    944                 self.LogSessionHistory('Clearing character dependant session attributes')
    945                 self._DisconnectSessionObjects()
    946                 if self.sid in sessionsBySID:
    947                     self.ValidateSession('pre-clear')
    948                     for attr in self.__characterDependantAttributes__:
    949                         v = getattr(self, attr, None)
    950                         try:
    951                             del sessionsByAttribute[attr][v][self.sid]
    952                             if not sessionsByAttribute[attr][v]:
    953                                 del sessionsByAttribute[attr][v]
    954                         except KeyError:
    955                             sys.exc_clear()
    956 
    957                 self.__dict__['clearing_session'] = 1
    958                 try:
    959                     d = {}
    960                     for each in self.__persistvars__:
    961                         if each in self.__userDependantAttributes__:
    962                             d[each] = getattr(self, each, None)
    963                         elif each not in ('connectedObjects', 'c2ooid'):
    964                             d[each] = self.GetDefaultValueOfAttribute(each)
    965 
    966                     self.SetAttributes(d, False, True)
    967                     self.__dict__['connectedObjects'] = {}
    968                     self.__dict__['machoObjectsByID'] = {}
    969                     d['sessionVariables'] = {}
    970                     self.LogSessionHistory('Character dependant session attributes cleared')
    971                 finally:
    972                     self.__dict__['clearing_session'] = 0
    973 
    974         finally:
    975             self.changing = None
    976 
    977     def ClearAttributes(self, isRemote = 0, dontSendMessage = False):
    978         machoNet = sm.GetServiceIfRunning('machoNet')
    979         if machoNet and machoNet.IsClusterShuttingDown() and self.userid:
    980             log.general.Log('Cluster shutting down, rejecting session clearing %s' % self.userid, log.LGINFO)
    981             return
    982         self.StartSessionChange(reason='ClearAttributes')
    983         try:
    984             if getattr(self, 'clearing_session', 0):
    985                 self.LogSessionHistory("Tried to clear a cleared/clearing session's attributes")
    986             else:
    987                 self.LogSessionHistory('Clearing session attributes')
    988                 self._DisconnectSessionObjects()
    989                 if self.sid in sessionsBySID:
    990                     self.ValidateSession('pre-clear')
    991                     sid = self.sid
    992                     del sessionsBySID[sid]
    993                     for attr in sessionsByAttribute:
    994                         val = getattr(self, attr, None)
    995                         try:
    996                             try:
    997                                 sessionsByAttribute[attr][val].discard(sid)
    998                             except AttributeError:
    999                                 del sessionsByAttribute[attr][val][sid]
   1000 
   1001                             if not sessionsByAttribute[attr][val]:
   1002                                 del sessionsByAttribute[attr][val]
   1003                         except Exception:
   1004                             sys.exc_clear()
   1005 
   1006                 self.__dict__['clearing_session'] = 1
   1007                 d = {}
   1008                 for each in self.__persistvars__:
   1009                     if each not in ('connectedObjects', 'c2ooid'):
   1010                         d[each] = self.GetDefaultValueOfAttribute(each)
   1011 
   1012                 self.SetAttributes(d, isRemote, dontSendMessage=dontSendMessage)
   1013                 self.__dict__['connectedObjects'] = {}
   1014                 self.__dict__['machoObjectsByID'] = {}
   1015                 d['sessionVariables'] = {}
   1016                 sm.ScatterEvent('OnSessionEnd', self.sid)
   1017                 self.LogSessionHistory('Session attributes cleared')
   1018         finally:
   1019             self.changing = None
   1020 
   1021     def ValidateSession(self, prefix):
   1022         bad = False
   1023         if not self.contextOnly:
   1024             if not getattr(self, 'clearing_session', 0):
   1025                 for attribute in sessionsByAttribute:
   1026                     value = getattr(self, attribute, None)
   1027                     if value:
   1028                         valueSIDs = sessionsByAttribute[attribute]
   1029                         if value not in valueSIDs:
   1030                             self.LogSessionHistory('sessionsByAttribute[%s] broken, %s is not found' % (attribute, value))
   1031                             bad = True
   1032                         elif self.sid not in valueSIDs[value]:
   1033                             self.LogSessionHistory('sessionsByAttribute[%s][%s] broken, %s is not found' % (attribute, value, self.sid))
   1034                             bad = True
   1035                         elif attribute in ('userid', 'charid') and len(valueSIDs[value]) != 1:
   1036                             self.LogSessionHistory('sessionsByAttribute[%s][%s] broken, this user/char has multiple sessions (%d)' % (attribute, value, len(valueSIDs[value])))
   1037                             bad = True
   1038 
   1039                 if bad:
   1040                     self.LogSessionError("The session failed it's %s validation check.  Session dump and stack trace follows." % (prefix,), 1)
   1041                     log.LogTraceback()
   1042         return bad
   1043 
   1044     def DisconnectFilteredObjects(self, disappeared):
   1045         if disappeared:
   1046             objects = []
   1047             for oid in self.connectedObjects.iterkeys():
   1048                 objConn = self.connectedObjects[oid][0]
   1049                 object = objConn.__object__
   1050                 if object is not None and hasattr(object, '__sessionfilter__'):
   1051                     for attribute in disappeared:
   1052                         if attribute in object.__sessionfilter__:
   1053                             objects.append((objConn, oid))
   1054                             break
   1055 
   1056             for objConn, oid in objects:
   1057                 with self.Masquerade({'base.caller': weakref.ref(objConn)}):
   1058                     try:
   1059                         if oid in self.connectedObjects:
   1060                             objConn.DisconnectObject()
   1061                     except Exception:
   1062                         log.LogException()
   1063                         sys.exc_clear()
   1064 
   1065     def CallProcessChangeOnObjects(self, notify, isRemote):
   1066         objects = []
   1067         notifyNodes = []
   1068         for oid in self.connectedObjects.iterkeys():
   1069             objConn = self.connectedObjects[oid][0]
   1070             object = objConn.__object__
   1071             if object is not None and hasattr(object, '__sessionfilter__') and hasattr(object, 'ProcessSessionChange'):
   1072                 for attribute in notify.iterkeys():
   1073                     if attribute in object.__sessionfilter__:
   1074                         objects.append((objConn, object, oid))
   1075                         break
   1076 
   1077         for objConn, object, oid in objects:
   1078             with self.Masquerade({'base.caller': weakref.ref(objConn)}):
   1079                 try:
   1080                     if oid in self.connectedObjects:
   1081                         notifyNodes.append(object.ProcessSessionChange(isRemote, self, notify))
   1082                 except Exception:
   1083                     log.LogException()
   1084                     sys.exc_clear()
   1085 
   1086         return notifyNodes
   1087 
   1088     def ComputeNotifySets(self, changes, pairs):
   1089         notify = {}
   1090         disappeared = set()
   1091         for attribute in changes.iterkeys():
   1092             currentEffectiveValue = self.__dict__.get(attribute, self.GetDefaultValueOfAttribute(attribute))
   1093             newValue = changes[attribute][1] if pairs else changes[attribute]
   1094             if currentEffectiveValue != newValue:
   1095                 notify[attribute] = (currentEffectiveValue, newValue)
   1096                 if currentEffectiveValue and not newValue:
   1097                     disappeared.add(attribute)
   1098 
   1099         return (notify, disappeared)
   1100 
   1101     def __ChangeAttribute(self, attribute, newValue):
   1102         self.__dict__['nextSessionChange'] = blue.os.GetSimTime() + SESSIONCHANGEDELAY
   1103         if getattr(self, 'clearing_session', 0):
   1104             self.__dict__[attribute] = newValue
   1105         else:
   1106             valueSIDs = sessionsByAttribute.get(attribute, None)
   1107             if valueSIDs is None:
   1108                 self.__dict__[attribute] = newValue
   1109             else:
   1110                 try:
   1111                     if newValue and newValue not in valueSIDs:
   1112                         valueSIDs[newValue] = {}
   1113                     oldValue = self.__dict__[attribute]
   1114                     if not self.contextOnly:
   1115                         if oldValue and oldValue in valueSIDs and self.sid in valueSIDs[oldValue]:
   1116                             del valueSIDs[oldValue][self.sid]
   1117                     self.__dict__[attribute] = newValue
   1118                     if not self.contextOnly:
   1119                         if newValue:
   1120                             valueSIDs[newValue][self.sid] = 1
   1121                         if oldValue and oldValue in valueSIDs and not len(valueSIDs[oldValue]):
   1122                             del valueSIDs[oldValue]
   1123                     if newValue and attribute == 'charid' and not charsession:
   1124                         localstorage.UpdateLocalStorage({'base.charsession': weakref.ref(self)})
   1125                 except:
   1126                     self.DumpSession('ARGH!!!', 'This session is blowing up during change attribute')
   1127                     raise
   1128 
   1129     def RecalculateDependantAttributes(self, d):
   1130         pass
   1131 
   1132     def StartSessionChange(self, reason):
   1133         if not self.changing:
   1134             self.changing = reason
   1135 
   1136     def SetAttributes(self, requestedChanges, isRemote = 0, dontSendMessage = False):
   1137         machoNet = sm.GetServiceIfRunning('machoNet')
   1138         if machoNet and machoNet.IsClusterShuttingDown() and getattr(self, 'userid', None):
   1139             log.LogNotice('Cluster shutting down, rejecting session change %s' % self.userid)
   1140             return
   1141         self.StartSessionChange(reason='SetAttributes')
   1142         try:
   1143             self.LogSessionHistory('Setting session attributes')
   1144             try:
   1145                 requestedChanges = copy.copy(requestedChanges)
   1146                 nonPersisted = []
   1147                 for attribute in requestedChanges:
   1148                     if attribute not in self.__persistvars__:
   1149                         nonPersisted.append(attribute)
   1150 
   1151                 if nonPersisted:
   1152                     log.LogTraceback(extraText=strx(nonPersisted), severity=log.LGWARN)
   1153                     for attribute in nonPersisted:
   1154                         del requestedChanges[attribute]
   1155 
   1156                 dependantAttributes = []
   1157                 changes = {}
   1158                 for attribute in requestedChanges.iterkeys():
   1159                     dependantAttributes += self.DependantAttributes(attribute)
   1160                     if requestedChanges[attribute] is not None and attribute not in ['address', 'languageID', 'countryCode'] + self.additionalNonIntegralAttributes:
   1161                         try:
   1162                             if attribute in self.longAttributes:
   1163                                 changes[attribute] = long(requestedChanges[attribute])
   1164                             else:
   1165                                 changes[attribute] = int(requestedChanges[attribute])
   1166                         except TypeError:
   1167                             log.general.Log('%s is not an integer %s' % (attribute, strx(requestedChanges[attribute])), 4, 1)
   1168                             log.LogTraceback()
   1169                             raise
   1170 
   1171                     else:
   1172                         changes[attribute] = requestedChanges[attribute]
   1173 
   1174                 charID = requestedChanges.get('charid', None)
   1175                 if charID is not None and charID != self.charid:
   1176                     changes.update(sm.GetService('sessionMgr').GetInitialValuesFromCharID(charID))
   1177                 for attribute in dependantAttributes:
   1178                     if attribute not in changes:
   1179                         changes[attribute] = self.GetDefaultValueOfAttribute(attribute)
   1180 
   1181                 self.RecalculateDependantAttributes(changes)
   1182                 self.ValidateSession('pre-change')
   1183                 notify, disappeared = self.ComputeNotifySets(changes, pairs=False)
   1184                 if notify:
   1185                     if 'userid' in notify and notify['userid'][0] and notify['userid'][1]:
   1186                         self.LogSessionError("A session's userID may not change, %s=>%s" % notify['userid'])
   1187                         raise RuntimeError("A session's userID may not change")
   1188                     mask = None
   1189                     try:
   1190                         if self.role & ROLE_SERVICE == 0:
   1191                             mask = self.Masquerade()
   1192                             if not self.contextOnly:
   1193                                 sm.NotifySessionChange('DoSessionChanging', isRemote, self, notify)
   1194                             self.DisconnectFilteredObjects(disappeared)
   1195                         for attribute in notify:
   1196                             self.__ChangeAttribute(attribute, changes[attribute])
   1197 
   1198                         self.ValidateSession('post-change')
   1199                         if self.role & ROLE_SERVICE == 0 and not dontSendMessage:
   1200                             if not self.contextOnly:
   1201                                 notifyAdditionalNodes = list(sm.NotifySessionChange('ProcessSessionChange', isRemote, self, notify))
   1202                                 notifyAdditionalNodes.append(self.CallProcessChangeOnObjects(notify, isRemote))
   1203                             else:
   1204                                 notifyAdditionalNodes = sm.GetService('sessionMgr').ProcessSessionChange(isRemote, self, notify)
   1205                             clientID = getattr(self, 'clientID', None)
   1206                             if clientID and not isRemote:
   1207                                 if not (self.role & (ROLE_LOGIN | ROLE_PLAYER) or self.role & (ROLE_SERVICE | ROLE_REMOTESERVICE)):
   1208                                     self.LogSessionError("A distributed session's role should probably always have login and player rights, even before a change broadcast")
   1209                                 sessionChangeLayer = sm.services['machoNet'].GetGPCS('sessionchange')
   1210                                 if sessionChangeLayer is not None:
   1211                                     sessionChangeLayer.SessionChanged(clientID, self.sid, notify, notifyAdditionalNodes)
   1212                             if not self.contextOnly:
   1213                                 sm.NotifySessionChange('OnSessionChanged', isRemote, self, notify)
   1214                     finally:
   1215                         if mask is not None:
   1216                             mask.UnMask()
   1217 
   1218             finally:
   1219                 self.LogSessionHistory('Session attributes set')
   1220 
   1221         finally:
   1222             self.changing = None
   1223 
   1224     def RecalculateDependantAttributesWithChanges(self, changes):
   1225         pass
   1226 
   1227     def ApplyRemoteAttributeChanges(self, changes, initialState):
   1228         self.StartSessionChange(reason='ApplyInitialState' if initialState else 'ApplyRemoteAttributeChanges')
   1229         try:
   1230             self.LogSessionHistory('Receiving and performing %s' % self.changing)
   1231             if self.role & ROLE_SERVICE:
   1232                 errorString = 'A service session may not change via %s, changes=%s' % (self.changing, strx(changes))
   1233                 self.LogSessionError(errorString)
   1234                 raise RuntimeError(errorString)
   1235             if not self.role & (ROLE_LOGIN | ROLE_PLAYER):
   1236                 self.LogSessionError("A distributed session's role should probably always have login and player rights, even during a change broadcast")
   1237             self.ValidateSession('pre-change')
   1238             if initialState:
   1239                 self.RecalculateDependantAttributes(changes)
   1240             else:
   1241                 self.RecalculateDependantAttributesWithChanges(changes)
   1242             notify, disappeared = self.ComputeNotifySets(changes, pairs=not initialState)
   1243             if notify:
   1244                 if 'userid' in notify and notify['userid'][0] and notify['userid'][1]:
   1245                     self.LogSessionError("A session's userID may not change, %s=>%s" % notify['userid'])
   1246                     raise RuntimeError("A session's userID may not change")
   1247                 if not initialState and 'role' in notify and self.charid and notify['role'][1] != self.role:
   1248                     initial_roles, new_roles = changes['role']
   1249                     if initial_roles and new_roles:
   1250                         if new_roles not in (add_role(initial_roles, ROLE_PLAYER), remove_role(initial_roles, ROLE_PLAYER)):
   1251                             self.LogSessionError("A session's role should probably not change for active characters, even in remote attribute stuff")
   1252                 mask = None
   1253                 try:
   1254                     mask = self.Masquerade()
   1255                     if not self.contextOnly:
   1256                         sm.NotifySessionChange('DoSessionChanging', True, self, notify)
   1257                     self.DisconnectFilteredObjects(disappeared)
   1258                     for attribute in notify:
   1259                         self.__ChangeAttribute(attribute, changes[attribute] if initialState else changes[attribute][1])
   1260 
   1261                     self.ValidateSession('post-change')
   1262                     if not self.contextOnly:
   1263                         sm.NotifySessionChange('ProcessSessionChange', True, self, notify)
   1264                         self.CallProcessChangeOnObjects(notify, isRemote=True)
   1265                         sm.NotifySessionChange('OnSessionChanged', True, self, notify)
   1266                 finally:
   1267                     if mask is not None:
   1268                         mask.UnMask()
   1269                     self.LogSessionHistory(self.changing)
   1270 
   1271         finally:
   1272             self.changing = None
   1273 
   1274     def DelayedInitialStateChange(self):
   1275         if self.role & ROLE_SERVICE != 0:
   1276             errorString = 'A service session may not change via DelayedInitialStateChange'
   1277             self.LogSessionError(errorString)
   1278             raise RuntimeError(errorString)
   1279         if machobase.mode != 'server':
   1280             raise RuntimeError('DelayedInitialStateChange called on %s, can only be called on server', machobase.mode)
   1281         mask = None
   1282         try:
   1283             self.LogSessionHistory('CTXSESS: Performing a delayed initial state change on session', self.sid)
   1284             self.StartSessionChange(reason='ApplyInitialState')
   1285             changes = {attr:self.__dict__[attr] for attr in self.GetDistributedProps(False)}
   1286             notify = {}
   1287             for attribute, myValue in changes.iteritems():
   1288                 defaultValue = self.GetDefaultValueOfAttribute(attribute)
   1289                 if myValue != defaultValue:
   1290                     notify[attribute] = (defaultValue, myValue)
   1291 
   1292             if notify:
   1293                 mask = self.Masquerade()
   1294                 sm.NotifySessionChange('DoSessionChanging', True, self, notify)
   1295                 for attribute, value in changes.iteritems():
   1296                     valueSIDs = sessionsByAttribute.get(attribute, None)
   1297                     if valueSIDs is not None:
   1298                         if value not in valueSIDs:
   1299                             valueSIDs[value] = {}
   1300                         valueSIDs[value][self.sid] = 1
   1301 
   1302                 sessionsBySID[self.sid] = self
   1303                 self.ValidateSession('post-delayedinitialstatechange')
   1304                 sm.NotifySessionChange('ProcessSessionChange', True, self, notify)
   1305                 self.CallProcessChangeOnObjects(notify, isRemote=True)
   1306                 sm.NotifySessionChange('OnSessionChanged', True, self, notify)
   1307         finally:
   1308             if mask is not None:
   1309                 mask.UnMask()
   1310             self.LogSessionHistory('Delayed ApplyInitialState')
   1311             self.changing = None
   1312 
   1313     def __repr__(self):
   1314         ret = '<Session: ('
   1315         for each in ['sid',
   1316          'clientID',
   1317          'changing',
   1318          'mutating',
   1319          'contextOnly'] + self.additionalAttributesToPrint + self.__persistvars__:
   1320             if getattr(self, each, None) is not None:
   1321                 if each in ['role'] + self.additionalHexAttributes:
   1322                     ret = ret + each + ':' + strx(hex(getattr(self, each))) + ', '
   1323                 else:
   1324                     ret = ret + each + ':' + strx(getattr(self, each)) + ', '
   1325 
   1326         ret = ret[:-2] + ')>'
   1327         return ret
   1328 
   1329     def __setattr__(self, attr, value):
   1330         if attr in ['sid', 'clientID'] + self.additionalNoSetAttributes + self.__persistvars__:
   1331             raise RuntimeError('ReadOnly', attr)
   1332         else:
   1333             self.__dict__[attr] = value
   1334 
   1335     def DisconnectObject(self, object, key = None, delaySecs = 1):
   1336         if key is None:
   1337             caller = localstorage.GetLocalStorage().get('base.caller', None)
   1338             if caller:
   1339                 caller = caller()
   1340                 if isinstance(caller, ObjectConnection) and caller.__object__ is object:
   1341                     caller.DisconnectObject(delaySecs)
   1342                     return
   1343         for k, v in self.connectedObjects.items():
   1344             obConn, = v
   1345             if obConn.__object__ is object and (key is None or key == (obConn.__dict__['__session__'].sid, obConn.__dict__['__c2ooid__'])):
   1346                 obConn.DisconnectObject(delaySecs)
   1347 
   1348     def RedirectObject(self, object, serviceName = None, bindParams = None, key = None):
   1349         for k, v in self.connectedObjects.items():
   1350             obConn, = v
   1351             if obConn.__object__ is object and (key is None or key == (obConn.__dict__['__session__'].sid, obConn.__dict__['__c2ooid__'])):
   1352                 obConn.RedirectObject(serviceName, bindParams)
   1353 
   1354     def ConnectToObject(self, object, serviceName = None, bindParams = None):
   1355         c2ooid = self.__dict__['c2ooid']
   1356         self.__dict__['c2ooid'] += 1
   1357         return ObjectConnection(self, object, c2ooid, serviceName, bindParams)
   1358 
   1359     def ConnectToClientService(self, svc, idtype = None, theID = None):
   1360         if theID is None or idtype is None:
   1361             if self.role & ROLE_SERVICE:
   1362                 log.LogTraceback()
   1363                 raise RuntimeError('You must specify an ID type and ID to identify the client')
   1364             else:
   1365                 theID = self.clientID
   1366                 idtype = 'clientID'
   1367         elif not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1368             log.LogTraceback()
   1369             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1370         if currentcall:
   1371             currentcall.UnLockSession()
   1372         return sm.services['sessionMgr'].ConnectToClientService(svc, idtype, theID)
   1373 
   1374     def ConnectToService(self, svc, **keywords):
   1375         return ServiceConnection(self, svc, **keywords)
   1376 
   1377     def ConnectToAllServices(self, svc, batchInterval = 0):
   1378         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1379             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1380         return sm.services['machoNet'].ConnectToAllServices(svc, self, batchInterval=batchInterval)
   1381 
   1382     def ConnectToRemoteService(self, svc, nodeID = None):
   1383         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1384             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1385         if machobase.mode == 'server' and nodeID is None:
   1386             nodeID = sm.GetService(svc).MachoResolve(self)
   1387             if type(nodeID) == types.StringType:
   1388                 raise RuntimeError(nodeID)
   1389             elif nodeID is None:
   1390                 return self.ConnectToService(svc)
   1391         if nodeID is not None and nodeID == sm.services['machoNet'].GetNodeID():
   1392             return self.ConnectToService(svc)
   1393         return sm.services['machoNet'].ConnectToRemoteService(svc, nodeID, self)
   1394 
   1395     def ConnectToSolServerService(self, svc, nodeID = None):
   1396         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1397             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1398         if machobase.mode == 'server' and nodeID is None:
   1399             nodeID = sm.GetService(svc).MachoResolve(self)
   1400             if type(nodeID) == types.StringType:
   1401                 raise RuntimeError(nodeID)
   1402             elif nodeID is None:
   1403                 return self.ConnectToService(svc)
   1404         if machobase.mode == 'server' and (nodeID is None or nodeID == sm.services['machoNet'].GetNodeID()):
   1405             return self.ConnectToService(svc)
   1406         else:
   1407             return sm.services['machoNet'].ConnectToRemoteService(svc, nodeID, self)
   1408 
   1409     def ConnectToProxyServerService(self, svc, nodeID = None):
   1410         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1411             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1412         if machobase.mode == 'proxy' and (nodeID is None or nodeID == sm.services['machoNet'].GetNodeID()):
   1413             return self.ConnectToService(svc)
   1414         else:
   1415             return sm.services['machoNet'].ConnectToRemoteService(svc, nodeID, self)
   1416 
   1417     def ConnectToAnyService(self, svc):
   1418         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1419             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1420         if svc in sm.services:
   1421             return self.ConnectToService(svc)
   1422         else:
   1423             return self.ConnectToRemoteService(svc)
   1424 
   1425     def ConnectToAllNeighboringServices(self, svc, batchInterval = 0):
   1426         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1427             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1428         return sm.services['machoNet'].ConnectToAllNeighboringServices(svc, self, batchInterval=batchInterval)
   1429 
   1430     def ConnectToAllProxyServerServices(self, svc, batchInterval = 0):
   1431         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1432             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1433         if machobase.mode == 'proxy':
   1434             return sm.services['machoNet'].ConnectToAllSiblingServices(svc, self, batchInterval=batchInterval)
   1435         else:
   1436             return sm.services['machoNet'].ConnectToAllNeighboringServices(svc, self, batchInterval=batchInterval)
   1437 
   1438     def ConnectToAllSolServerServices(self, svc, batchInterval = 0):
   1439         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1440             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1441         if machobase.mode == 'server':
   1442             return sm.services['machoNet'].ConnectToAllSiblingServices(svc, self, batchInterval=batchInterval)
   1443         else:
   1444             return sm.services['machoNet'].ConnectToAllNeighboringServices(svc, self, batchInterval=batchInterval)
   1445 
   1446     def RemoteServiceCall(self, dest, service, method, *args, **keywords):
   1447         return self.RemoteServiceCallWithoutTheStars(dest, service, method, args, keywords)
   1448 
   1449     def RemoteServiceCallWithoutTheStars(self, dest, service, method, *args, **keywords):
   1450         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1451             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1452         return sm.services['machoNet'].RemoteServiceCallWithoutTheStars(self, dest, service, method, args, keywords)
   1453 
   1454     def RemoteServiceNotify(self, dest, service, method, *args, **keywords):
   1455         return self.RemoteServiceNotifyWithoutTheStars(dest, service, method, args, keywords)
   1456 
   1457     def RemoteServiceNotifyWithoutTheStars(self, dest, service, method, args, keywords):
   1458         if not self.role & ROLE_SERVICE and machobase.mode != 'client' and not IsInClientContext():
   1459             raise RuntimeError('You cannot cross the wire except in the context of a service')
   1460         sm.services['machoNet'].RemoteServiceNotifyWithoutTheStars(self, args, keywords)
   1461 
   1462     def ResetSessionChangeTimer(self, reason):
   1463         sm.GetService('sessionMgr').LogInfo("Resetting next legal session change timer, reason='", reason, "', was ", formatUtil.FmtDateEng(self.nextSessionChange or blue.os.GetSimTime()))
   1464         self.nextSessionChange = None
   1465 
   1466     def ServiceProxy(self, serviceName):
   1467         return ServiceProxy(serviceName, self)
   1468 
   1469 
   1470 class ServiceConnection():
   1471     __restrictedcalls__ = {'Lock': 1,
   1472      'UnLock': 1,
   1473      'GetMachoObjectBoundToSession': 1}
   1474 
   1475     def __init__(self, sess, service, **keywords):
   1476         self.__session__ = weakref.proxy(sess)
   1477         self.__service__ = service
   1478         self.__remote__ = keywords.get('remote', 0)
   1479         sm.StartService(self.__service__)
   1480 
   1481     def Lock(self, lockID):
   1482         return sm.GetService(self.__service__).LockService(lockID)
   1483 
   1484     def UnLock(self, lockID, lock):
   1485         sm.GetService(self.__service__).UnLockService(lockID, lock)
   1486 
   1487     def GetInstanceID(self):
   1488         return sm.GetService(self.__service__).startedWhen
   1489 
   1490     def __getitem__(self, key):
   1491         mn = sm.services['machoNet']
   1492         if type(key) == types.TupleType:
   1493             nodeID = mn.GetNodeFromAddress(key[0], key[1])
   1494         elif type(key) == types.IntType:
   1495             nodeID = key
   1496         else:
   1497             srv = sm.StartServiceAndWaitForRunningState(self.__service__)
   1498             nodeID = self.MachoResolve(key)
   1499         if nodeID == mn.GetNodeID():
   1500             return self
   1501         else:
   1502             return self.__session__.ConnectToRemoteService(self.__service__, nodeID)
   1503 
   1504     def __nonzero__(self):
   1505         return 1
   1506 
   1507     def __str__(self):
   1508         sess = 'unknown'
   1509         try:
   1510             sess = strx(self.__session__)
   1511         except:
   1512             pass
   1513 
   1514         return 'ServiceConnection, Service:' + strx(self.__service__) + '. Session:' + sess
   1515 
   1516     def __repr__(self):
   1517         return self.__str__()
   1518 
   1519     def __getattr__(self, method):
   1520         if method in self.__dict__:
   1521             return self.__dict__[method]
   1522         if method.startswith('__'):
   1523             raise AttributeError(method)
   1524         svc = sm.StartService(self.__service__)
   1525         if not self.__remote__ and (self.__session__.role & ROLE_SERVICE or machobase.mode == 'client' or method == 'MachoResolve'):
   1526             self.__WaitForRunningState(svc)
   1527             if method.endswith('_Ex'):
   1528                 return FastCallWrapper(self.__session__, svc, method[:-3], self)
   1529             else:
   1530                 return FastCallWrapper(self.__session__, svc, method, self)
   1531         if self.__session__.role & ROLE_SERVICE:
   1532             return UnlockedServiceCallWrapper(self.__session__, svc, method, self, self.__service__)
   1533         else:
   1534             return ServiceCallWrapper(self.__session__, svc, method, self, self.__service__)
   1535 
   1536     def __WaitForRunningState(self, svc):
   1537         desiredStates = (SERVICE_RUNNING,)
   1538         errorStates = (SERVICE_FAILED, SERVICE_STOPPED)
   1539         sm.WaitForServiceObjectState(svc, desiredStates, errorStates)
   1540 
   1541 
   1542 class MasqueradeMask(object):
   1543 
   1544     def __init__(self, props):
   1545         self.__prevStorage = localstorage.UpdateLocalStorage(props)
   1546 
   1547     def __enter__(self):
   1548         pass
   1549 
   1550     def __exit__(self, e, v, tb):
   1551         self.UnMask()
   1552 
   1553     def UnMask(self):
   1554         localstorage.SetLocalStorage(self.__prevStorage)
   1555         self.__prevStorage = None
   1556 
   1557 
   1558 class Session(CoreSession):
   1559     __persistvars__ = CoreSession.__persistvars__ + ['regionid',
   1560      'constellationid',
   1561      'allianceid',
   1562      'warfactionid',
   1563      'corpid',
   1564      'fleetid',
   1565      'fleetrole',
   1566      'wingid',
   1567      'squadid',
   1568      'shipid',
   1569      'stationid',
   1570      'structureid',
   1571      'solarsystemid',
   1572      'solarsystemid2',
   1573      'hqID',
   1574      'baseID',
   1575      'rolesAtAll',
   1576      'rolesAtHQ',
   1577      'rolesAtBase',
   1578      'rolesAtOther',
   1579      'genderID',
   1580      'bloodlineID',
   1581      'raceID',
   1582      'corpAccountKey',
   1583      'inDetention']
   1584     __nonpersistvars__ = CoreSession.__nonpersistvars__ + ['locationid', 'corprole']
   1585     __attributesWithDefaultValueOfZero__ = CoreSession.__attributesWithDefaultValueOfZero__ + ['corprole',
   1586      'rolesAtAll',
   1587      'rolesAtHQ',
   1588      'rolesAtBase',
   1589      'rolesAtOther']
   1590     __dependant_attributes_eve__ = {'userid': ['inDetention'],
   1591      'charid': ['corpid',
   1592                 'fleetid',
   1593                 'shipid',
   1594                 'stationid',
   1595                 'structureid',
   1596                 'solarsystemid',
   1597                 'bloodlineID',
   1598                 'raceID',
   1599                 'genderID'],
   1600      'corpid': ['baseID',
   1601                 'rolesAtAll',
   1602                 'rolesAtHQ',
   1603                 'rolesAtBase',
   1604                 'rolesAtOther',
   1605                 'hqID',
   1606                 'allianceid',
   1607                 'corpAccountKey',
   1608                 'warfactionid'],
   1609      'baseID': ['corprole'],
   1610      'rolesAtAll': ['corprole'],
   1611      'rolesAtHQ': ['corprole'],
   1612      'rolesAtBase': ['corprole'],
   1613      'rolesAtOther': ['corprole'],
   1614      'hqID': ['corprole'],
   1615      'corpAccountKey': [],
   1616      'stationid': ['locationid'],
   1617      'solarsystemid': ['locationid'],
   1618      'locationid': ['solarsystemid2'],
   1619      'solarsystemid2': ['constellationid'],
   1620      'constellationid': ['regionid'],
   1621      'fleetid': ['fleetrole', 'wingid', 'squadid'],
   1622      'wingid': ['squadid'],
   1623      'squadid': []}
   1624     __dependant_attributes__ = {}
   1625     for key, val in CoreSession.__dependant_attributes__.iteritems():
   1626         __dependant_attributes__[key] = val + __dependant_attributes_eve__.get(key, [])
   1627 
   1628     for key, val in __dependant_attributes_eve__.iteritems():
   1629         if key not in __dependant_attributes__:
   1630             __dependant_attributes__[key] = val
   1631 
   1632     def __init__(self, sid, localSID, role, sessionType):
   1633         CoreSession.__init__(self, sid, localSID, role, sessionType, ['locationid', 'corprole'])
   1634         self.additionalNoSetAttributes = ['locationid', 'corprole']
   1635         self.additionalDistributedProps = ['locationid', 'corprole']
   1636         self.additionalNonIntegralAttributes = ['fleetid',
   1637          'fleetrole',
   1638          'wingid',
   1639          'squadid']
   1640         self.longAttributes = ['corprole']
   1641         self.additionalAttributesToPrint = ['locationid', 'corprole']
   1642         self.additionalHexAttributes = ['corprole',
   1643          'rolesAtAll',
   1644          'rolesAtHQ',
   1645          'rolesAtBase',
   1646          'rolesAtOther']
   1647 
   1648     def RecalculateDependantAttributes(self, d):
   1649         if 'stationid' in d or 'solarsystemid' in d:
   1650             d['locationid'] = d.get('stationid') or d.get('solarsystemid')
   1651         for each in ('hqID', 'baseID', 'rolesAtAll', 'rolesAtHQ', 'rolesAtBase', 'rolesAtOther', 'stationid', 'solarsystemid'):
   1652             if each in d:
   1653                 rolesAtAll = d.get('rolesAtAll', self.rolesAtAll)
   1654                 rolesAtHQ = d.get('rolesAtHQ', self.rolesAtHQ)
   1655                 rolesAtBase = d.get('rolesAtBase', self.rolesAtBase)
   1656                 rolesAtOther = d.get('rolesAtOther', self.rolesAtOther)
   1657                 hqID = d.get('hqID', self.hqID)
   1658                 baseID = d.get('baseID', self.baseID)
   1659                 stationid = d.get('stationid', self.stationid)
   1660                 solarsystemid = d.get('solarsystemid', self.solarsystemid)
   1661                 corprole = rolesAtAll | rolesAtOther
   1662                 if stationid:
   1663                     if stationid == hqID:
   1664                         corprole = rolesAtAll | rolesAtHQ
   1665                     elif stationid == baseID:
   1666                         corprole = rolesAtAll | rolesAtBase
   1667                 d['corprole'] = corprole
   1668                 break
   1669 
   1670     def RecalculateDependantAttributesWithChanges(self, changes):
   1671         if 'stationid' in changes or 'solarsystemid' in changes:
   1672             old = self.locationid
   1673             new = changes.get('stationid', [None, None])[1]
   1674             if not new:
   1675                 new = changes.get('solarsystemid', [None, None])[1]
   1676             changes['locationid'] = (old, new)
   1677         for each in ('hqID', 'baseID', 'rolesAtAll', 'rolesAtHQ', 'rolesAtBase', 'rolesAtOther', 'stationid', 'solarsystemid'):
   1678             if each in changes:
   1679                 rolesAtAll = changes.get('rolesAtAll', [None, self.rolesAtAll])[1]
   1680                 rolesAtHQ = changes.get('rolesAtHQ', [None, self.rolesAtHQ])[1]
   1681                 rolesAtBase = changes.get('rolesAtBase', [None, self.rolesAtBase])[1]
   1682                 rolesAtOther = changes.get('rolesAtOther', [None, self.rolesAtOther])[1]
   1683                 hqID = changes.get('hqID', [None, self.hqID])[1]
   1684                 baseID = changes.get('baseID', [None, self.baseID])[1]
   1685                 stationid = changes.get('stationid', [None, self.stationid])[1]
   1686                 solarsystemid = changes.get('solarsystemid', [None, self.solarsystemid])[1]
   1687                 old = self.corprole
   1688                 corprole = rolesAtAll | rolesAtOther
   1689                 if stationid:
   1690                     if stationid == hqID:
   1691                         corprole = rolesAtAll | rolesAtHQ
   1692                     elif stationid == baseID:
   1693                         corprole = rolesAtAll | rolesAtBase
   1694                 if old != corprole:
   1695                     changes['corprole'] = (old, corprole)
   1696                 break
   1697 
   1698 
   1699 def CreateSession(sid = None, sessionType = const.session.SESSION_TYPE_GAME, role = ROLE_LOGIN):
   1700     global local_sid
   1701     if sessionType not in const.session.VALID_SESSION_TYPES:
   1702         raise ValueError('Trying to create a session with an invalid session type')
   1703     local_sid += 1
   1704     while sid is None:
   1705         sid = GetNewSid()
   1706         if sid in sessionsBySID:
   1707             log.general.Log('Session SID random collision!', log.LGWARN)
   1708             sid = None
   1709 
   1710     if sid in sessionsBySID:
   1711         log.general.Log('Session SID collision!', log.LGERR)
   1712         log.general.Log('Local session being broken %s' % (sessionsBySID[sid],), log.LGERR)
   1713     s = Session(sid, local_sid, role, sessionType)
   1714     if not s.contextOnly:
   1715         sessionsBySID[sid] = s
   1716     allSessionsBySID[sid] = s
   1717     return s
   1718 
   1719 
   1720 def GetServiceSession(serviceKey, refcounted = False):
   1721     if serviceKey not in service_sessions:
   1722         ret = CreateSession(GetNewSid(), const.session.SESSION_TYPE_SERVICE, ROLE_SERVICE)
   1723         ret.serviceName = serviceKey
   1724         service_sessions[serviceKey] = ret
   1725     else:
   1726         ret = service_sessions[serviceKey]
   1727     if refcounted:
   1728         if not hasattr(ret, 'remoteServiceSessionRefCount'):
   1729             ret.__dict__['remoteServiceSessionRefCount'] = 1
   1730         else:
   1731             ret.__dict__['remoteServiceSessionRefCount'] += 1
   1732     return ret
   1733 
   1734 
   1735 class ObjectcastCallWrapper():
   1736 
   1737     def __init__(self, object):
   1738         self.object = weakref.proxy(object)
   1739 
   1740     def __call__(self, method, *args):
   1741         sm.services['machoNet'].ObjectcastWithoutTheStars(self.object, method, args)
   1742 
   1743 
   1744 class UpdatePublicAttributesCallWrapper():
   1745 
   1746     def __init__(self, object):
   1747         self.object = weakref.proxy(object)
   1748 
   1749     def __call__(self, *args, **keywords):
   1750         pa = {}
   1751         k = keywords.get('partial', [])
   1752         for each in getattr(self.object, '__publicattributes__', []):
   1753             if k and each not in k:
   1754                 continue
   1755             if hasattr(self.object, each):
   1756                 pa[each] = getattr(self.object, each)
   1757 
   1758         sm.services['machoNet'].Objectcast(self.object, 'OnObjectPublicAttributesUpdated', GetObjectUUID(self.object), pa, args, keywords)
   1759 
   1760 
   1761 objectsByUUID = weakref.WeakValueDictionary({})
   1762 objectUUID = 0
   1763 
   1764 def GetObjectUUID(object):
   1765     global objectUUID
   1766     global objectsByUUID
   1767     if hasattr(object, '__machoObjectUUID__'):
   1768         return object.__machoObjectUUID__
   1769     else:
   1770         objectUUID += 1
   1771         if machobase.mode == 'client':
   1772             t = 'C=0:%s' % objectUUID
   1773         else:
   1774             t = 'N=%s:%s' % (sm.services['machoNet'].GetNodeID(), objectUUID)
   1775         setattr(object, '__machoObjectUUID__', t)
   1776         if not hasattr(object, '__publicattributes__'):
   1777             setattr(object, '__publicattributes__', [])
   1778         setattr(object, 'Objectcast', ObjectcastCallWrapper(object))
   1779         setattr(object, 'UpdatePublicAttributes', UpdatePublicAttributesCallWrapper(object))
   1780         objectsByUUID[t] = object
   1781         return t
   1782 
   1783 
   1784 def GetObjectByUUID(uuid):
   1785     try:
   1786         return objectsByUUID.get(uuid, None)
   1787     except ReferenceError:
   1788         sys.exc_clear()
   1789         return None
   1790 
   1791 
   1792 def ReadContextSessionTypesPrefs():
   1793     global __contextOnlyTypes__
   1794     PREFS_PARAMETER = 'ContextSessionTypes'
   1795     try:
   1796         __contextOnlyTypes__ = []
   1797         prefValue = prefs.GetValue(PREFS_PARAMETER, None)
   1798         if prefValue is not None:
   1799             log.LogInfo('CTXSESS: %s = %s', PREFS_PARAMETER, prefValue)
   1800             contextTypes = prefValue.split(',')
   1801             for typeName in contextTypes:
   1802                 if typeName in const.session.SESSION_NAME_TO_TYPE:
   1803                     __contextOnlyTypes__.append(const.session.SESSION_NAME_TO_TYPE[typeName])
   1804                 else:
   1805                     log.LogError("Ignoring invalid session type '%s' in prefs parameter '%s'" % (typeName, PREFS_PARAMETER))
   1806 
   1807         else:
   1808             log.LogInfo('CTXSESS: %s not provided, no context sessions will be created' % PREFS_PARAMETER)
   1809     except Exception:
   1810         log.LogException("Exception while parsing prefs parameter '%s'" % PREFS_PARAMETER)
   1811 
   1812 
   1813 ReadContextSessionTypesPrefs()