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

invCache.py (60339B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\invCache.py
      2 from future.utils import raise_
      3 import copy
      4 import sys
      5 import traceback
      6 from functools import wraps
      7 import blue
      8 import dogma.data
      9 import evetypes
     10 import localization
     11 import log
     12 import stackless
     13 import telemetry
     14 import uthread
     15 from caching.memoize import Memoize
     16 from carbon.common.script.net.moniker import Moniker
     17 from carbon.common.script.sys.crowset import CRowset
     18 from carbon.common.script.sys.row import Row
     19 from carbon.common.script.sys.service import Service
     20 from carbon.common.script.sys.serviceConst import ROLE_ADMIN
     21 from carbon.common.script.util.format import FmtDate
     22 from carbonui import uiconst
     23 from eve.client.script.ui.inflight.shipHud import ActiveShipController
     24 from eve.client.script.ui.util import utilWindows
     25 from eve.common.lib import appConst as const
     26 from eve.common.script.net import eveMoniker
     27 from eve.common.script.sys import eveCfg, idCheckers
     28 from eve.common.script.util import inventoryFlagsCommon
     29 from eveexceptions import UserError
     30 from inventorycommon.const import flagFrigateEscapeBay
     31 from inventorycommon.util import GetItemVolume, IsFittingFlag, IsFittable
     32 from inventoryrestrictions import can_be_unfitted, ItemCannotBeUnfitted
     33 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     34 from resourcewars.common.const import RW_CORPORATIONS
     35 CAPACITY_ATTRIBUTES = (const.attributeCapacity,
     36  const.attributeDroneCapacity,
     37  const.attributeShipMaintenanceBayCapacity,
     38  const.attributeFleetHangarCapacity)
     39 SPECIAL_CONTAINER_IDS = [const.containerWallet,
     40  const.containerStationCharacters,
     41  const.containerGlobal,
     42  const.containerSolarSystem,
     43  const.containerRecycler,
     44  const.containerCharacter]
     45 PRIVATE_SHIP_FLAGS = [const.flagCargo]
     46 PRIVATE_STRUCTURE_FLAGS = [const.flagHangar]
     47 PRIVATE_STATION_FLAGS = [const.flagHangar, const.flagShipHangar]
     48 PRIVATE_STATION_CONTAINERS = [const.containerHangar]
     49 
     50 class UnavailableInventoryLocation(Exception):
     51 
     52     def __init__(self, locationName):
     53         super(UnavailableInventoryLocation, self).__init__(self)
     54         self.message = 'Failed to retrieve items; character is not located in a %s' % locationName
     55 
     56     def __str__(self):
     57         return self.message
     58 
     59 
     60 def RaiseIfInventoryLocationUnavailable(sessionCheck, locationName):
     61 
     62     def RaiseIfInventoryLocationUnavailableDecorator(f):
     63 
     64         @wraps(f)
     65         def wrapped(*args, **kwargs):
     66             if not sessionCheck():
     67                 raise UnavailableInventoryLocation(locationName)
     68             try:
     69                 return f(*args, **kwargs)
     70             except Exception:
     71                 raise UnavailableInventoryLocation(locationName)
     72 
     73         return wrapped
     74 
     75     return RaiseIfInventoryLocationUnavailableDecorator
     76 
     77 
     78 class invCache(Service):
     79     __guid__ = 'svc.invCache'
     80     __exportedcalls__ = {'GetInventory': [],
     81      'GetInventoryFromId': [],
     82      'GetInventoryMgr': [],
     83      'GetItemHeader': [],
     84      'IsItemLocked': [],
     85      'TryLockItem': [],
     86      'UnlockItem': [],
     87      'GetStationIDOfItem': [],
     88      'IsInventoryPrimedAndListed': [],
     89      'InvalidateLocationCache': [],
     90      'AcceptPossibleRemovalTax': []}
     91     __dependencies__ = []
     92     __notifyevents__ = ['ProcessSessionChange',
     93      'OnSessionChanged',
     94      'OnItemsChanged',
     95      'OnItemChange',
     96      'OnCapacityChange',
     97      'OnPasswordChanged',
     98      'DoBallRemove',
     99      'OnDogmaAttributeChanged',
    100      'ProcessActiveShipChanged',
    101      'DoBallsRemove',
    102      'OnSessionReset',
    103      'OnInvalidateCacheForLocations']
    104 
    105     def __init__(self):
    106         super(invCache, self).__init__()
    107         self.Initialize()
    108 
    109     def Initialize(self):
    110         self.__invmgr = None
    111         self.__invlocID = 0
    112         self.__stationinvmgr = None
    113         self.__stationID = 0
    114         self.__itemhdr = None
    115         self.inventories = {}
    116         self.containerGlobal = None
    117         self.lockedItems = {}
    118         self.lockedItemsSemaphore = uthread.Semaphore()
    119         self.trace = 0
    120         self.locks = {}
    121 
    122     def Run(self, ms = None):
    123         sm.FavourMe(self.OnItemChange)
    124         sm.FavourMe(self.ProcessSessionChange)
    125         sm.FavourMe(self.OnCapacityChange)
    126         self.LogInfo('invCache lock tracing:', ['OFF', 'ON'][self.trace])
    127 
    128     def Stop(self, memStream = None):
    129         self.InvalidateCache()
    130 
    131     @telemetry.ZONE_METHOD
    132     def DoBallsRemove(self, pythonBalls, isRelease):
    133         for ball, slimItem, terminal in pythonBalls:
    134             self.DoBallRemove(ball, slimItem, terminal)
    135 
    136     def DoBallRemove(self, ball, slimItem, terminal):
    137         if ball is None:
    138             return
    139         if (ball.id, None) in self.inventories:
    140             del self.inventories[ball.id, None]
    141 
    142     def ProcessSessionChange(self, isRemote, session, change):
    143         try:
    144             if not session.charid:
    145                 return self.InvalidateCache()
    146             if 'corprole' in change and session.stationid:
    147                 self.LogInfo('ProcessSessionChange corprole in station')
    148                 office = sm.GetService('officeManager').GetCorpOfficeAtLocation()
    149                 if office is not None:
    150                     key = (office.officeID, None)
    151                     if key in self.inventories:
    152                         self.LogInfo('Removing inventory for corp hangar')
    153                         del self.inventories[key]
    154             if 'shipid' in change and session.solarsystemid is not None:
    155                 key = (change['shipid'][0], None)
    156                 if key in self.inventories:
    157                     del self.inventories[key]
    158                 return
    159             if not self._ShouldInvalidateCache(change):
    160                 return
    161             self.InvalidateCache()
    162         finally:
    163             sm.services['godma'].ProcessSessionChange(isRemote, session, change)
    164 
    165     def _ShouldInvalidateCache(self, change):
    166         return 'stationid' in change or 'structureid' in change or 'solarsystemid' in change
    167 
    168     def OnSessionChanged(self, isRemote, sess, change):
    169         if ROLE_ADMIN & session.role == ROLE_ADMIN:
    170             self.trace = 1
    171         else:
    172             self.trace = 0
    173         if 'corprole' in change and (session.stationid or session.structureid):
    174             self.LogInfo('OnSessionChanged corprole in station')
    175             self.InvalidateCorpCache()
    176 
    177     def OnSessionReset(self):
    178         self.InvalidateCache()
    179         self.Initialize()
    180 
    181     def OnPasswordChanged(self, which, containerItem):
    182         self.RemoveInventoryContainer(containerItem)
    183 
    184     def __getattr__(self, key):
    185         if key == 'inventorymgr':
    186             while session.IsMutating() and not session.IsChanging():
    187                 self.LogInfo('Sleeping while waiting for a session change or mutation to complete')
    188                 blue.pyos.synchro.SleepSim(250)
    189 
    190             if self.__invmgr is None or self.__invlocID != session.locationid:
    191                 self.__invlocID = session.locationid
    192                 self.__invmgr = eveMoniker.GetInventoryMgr()
    193             return self.__invmgr
    194         if key == 'stationInventoryMgr':
    195             while session.IsMutating() and not session.IsChanging():
    196                 self.LogInfo('Sleeping while waiting for a session change or mutation to complete')
    197                 blue.pyos.synchro.SleepSim(250)
    198 
    199             if self.__stationinvmgr is None or self.__stationID != session.stationid:
    200                 if session.stationid is None:
    201                     raise RuntimeError('CharacterNotAtStation')
    202                 self.__stationID = session.stationid
    203                 self.__stationinvmgr = eveMoniker.GetStationInventoryMgr(session.stationid)
    204             return self.__stationinvmgr
    205         if key in self.__dict__:
    206             return self.__dict__[key]
    207         raise_(AttributeError, key)
    208 
    209     def OnItemsChanged(self, items, change, location):
    210         self.LogInfo('server says do change ', change, ' for ', len(items), ' items:', items)
    211         self.ValidateLocationCacheForItems(items)
    212         for i, item in enumerate(items):
    213             sm.ScatterEvent('OnItemChange', item, change, location)
    214             blue.pyos.BeNice()
    215 
    216         sm.ScatterEvent('OnMultipleItemChange', items, change)
    217 
    218     def OnItemChange(self, item, change, location):
    219         if const.ixQuantity in change:
    220             log.LogTraceback('invCache processing a ixQuantity change')
    221             if change[const.ixQuantity] < 0 or item.quantity < 0:
    222                 raise RuntimeError('OnItemChange in invCache: negative qty', item, change)
    223         try:
    224             lockKey = item.itemID
    225             uthread.Lock(self, lockKey)
    226             try:
    227                 self.LockedItemChange(item, change)
    228                 oldItem = blue.DBRow(item)
    229                 for k, v in change.iteritems():
    230                     if k in (const.ixStackSize, const.ixSingleton):
    231                         k = const.ixQuantity
    232                     oldItem[k] = v
    233 
    234                 keyIs, keyWas = (None, None)
    235                 if session.stationid:
    236                     if item.locationID == session.stationid:
    237                         if item.ownerID == session.charid or item.ownerID == session.corpid:
    238                             keyIs = (const.containerHangar, None)
    239                         if item.flagID == const.flagCorpDeliveries:
    240                             keyIs = (const.containerCorpMarket, item.ownerID)
    241                         if item.flagID == const.flagCapsuleerDeliveries:
    242                             keyIs = (const.containerCapsuleerDeliveries, item.ownerID)
    243                         elif keyIs is None:
    244                             keyIs = (const.containerHangar, item.ownerID)
    245                     if oldItem.locationID == session.stationid:
    246                         if oldItem.ownerID == session.charid or oldItem.ownerID == session.corpid:
    247                             keyWas = (const.containerHangar, None)
    248                         if oldItem.flagID == const.flagCorpDeliveries:
    249                             keyWas = (const.containerCorpMarket, oldItem.ownerID)
    250                         if oldItem.flagID == const.flagCapsuleerDeliveries:
    251                             keyWas = (const.containerCapsuleerDeliveries, oldItem.ownerID)
    252                         elif keyWas is None:
    253                             keyWas = (const.containerHangar, oldItem.ownerID)
    254                 if keyIs is None:
    255                     keyIs = (item.locationID, None)
    256                 if keyWas is None:
    257                     keyWas = (oldItem.locationID, None)
    258                 if keyWas and keyWas in self.inventories:
    259                     inv = self.inventories[keyWas]
    260                     inv.OnItemChange(item, change, location)
    261                 if keyIs != keyWas or const.ixFlag in change:
    262                     if keyIs and keyIs in self.inventories:
    263                         inv = self.inventories[keyIs]
    264                         inv.OnItemChange(item, change, location)
    265                 if idCheckers.IsJunkLocation(item.locationID) or const.ixOwnerID in change or (const.ixQuantity in change or const.ixStackSize in change) and item.stacksize == 0:
    266                     if idCheckers.IsJunkLocation(item.locationID):
    267                         self.LogInfo('REMOVING:', item.itemID, 'item is in junk location')
    268                     elif const.ixOwnerID in change:
    269                         self.LogInfo('REMOVING:', item.itemID, ' ownership changed')
    270                     elif const.ixStackSize in change and item.stacksize == 0:
    271                         self.LogInfo('REMOVING:', item.itemID, ' new stacksize is 0')
    272                     if const.ixLocationID in change:
    273                         if self.containerGlobal:
    274                             self.containerGlobal.TrashItem(change[const.ixLocationID], item.itemID, item.locationID)
    275                 if (item.itemID, None) in self.inventories:
    276                     if oldItem.ownerID == session.charid != item.ownerID:
    277                         del self.inventories[item.itemID, None]
    278                     else:
    279                         self.inventories[item.itemID, None].item = item
    280             finally:
    281                 sm.services['godma'].OnItemChange(item, change, location)
    282                 sm.services['inv'].OnItemChange(item, change, location)
    283                 sm.ScatterEvent('OnItemChanged', item, change, location)
    284 
    285         finally:
    286             uthread.UnLock(self, lockKey)
    287 
    288     def LockedItemChange(self, item, change):
    289         self.UnlockItem(item.itemID)
    290 
    291     def TryLockItem(self, itemID, userErrorName = '', userErrorDict = {}, raiseIfLocked = 0):
    292         try:
    293             self.lockedItemsSemaphore.acquire()
    294             self.LogInfo('TryLockItem', itemID)
    295             bIsLocked, xlockTime, xuserErrorName, xuserErrorDict, co_name, co_filename, co_lineno, thread = self.IsItemLocked_NoLock(itemID)
    296             if bIsLocked:
    297                 lockTime, userErrorName, userErrorDict = xlockTime, xuserErrorName, xuserErrorDict
    298             else:
    299                 co_name = None
    300                 co_filname = None
    301                 co_lineno = None
    302                 if self.trace:
    303                     frames = traceback.extract_stack(limit=2)
    304                     frame = frames[1]
    305                     if frame[0].endswith('service.py'):
    306                         frame = frames[0]
    307                     co_name = frame[2]
    308                     co_filname = frame[0]
    309                     co_lineno = frame[1]
    310                     self.LogInfo('TryLockItem called by ', co_name, ' ', co_filename, '(', co_lineno, ')')
    311                 now = blue.os.GetWallclockTime()
    312                 self.lockedItems[itemID] = (now,
    313                  userErrorName,
    314                  userErrorDict,
    315                  co_name,
    316                  co_filname,
    317                  co_lineno,
    318                  stackless.getcurrent())
    319                 self.LogInfo('Locked Item', itemID)
    320                 sm.ScatterEvent('OnItemLocked', itemID)
    321                 return 1
    322         finally:
    323             self.lockedItemsSemaphore.release()
    324 
    325         if bIsLocked and raiseIfLocked:
    326             self.RaiseLockError(itemID, lockTime, userErrorName, userErrorDict, co_name, co_filename, co_lineno)
    327         return bIsLocked
    328 
    329     def RaiseLockError(self, itemID, lockTime, userErrorName, userErrorDict, co_name, co_filename, co_lineno):
    330         if len(userErrorName) == 0:
    331             userErrorName = 'ItemIsLocked'
    332             userErrorDict = {'reason': localization.GetByLabel('UI/Generic/NoReasonWasSpecified'),
    333              'lockTime': FmtDate(lockTime)}
    334         if userErrorDict == type({}):
    335             userErrorDict['lockTime'] = lockTime
    336         if not self.trace:
    337             raise UserError(userErrorName, userErrorDict)
    338         message = cfg.GetMessage(userErrorName, userErrorDict)
    339         msgDict = {}
    340         if co_name is None:
    341             co_name = localization.GetByLabel('UI/Generic/Unknown')
    342         if co_filename is None:
    343             co_filename = localization.GetByLabel('UI/Generic/Unknown')
    344         if co_lineno is None:
    345             co_lineno = localization.GetByLabel('UI/Generic/Unknown')
    346         msgDict['title'] = message.title
    347         msgDict['text'] = message.text
    348         msgDict['itemID'] = itemID
    349         msgDict['co_name'] = co_name
    350         msgDict['co_filename'] = co_filename
    351         msgDict['co_lineno'] = co_lineno
    352         msgDict['lockTime'] = lockTime
    353         raise UserError('AdminReportItemLocked', msgDict)
    354 
    355     def UnlockItem(self, itemID):
    356         try:
    357             self.lockedItemsSemaphore.acquire()
    358             self.UnlockItem_NoLock(itemID)
    359         finally:
    360             self.lockedItemsSemaphore.release()
    361 
    362     def UnlockItem_NoLock(self, itemID):
    363         self.LogInfo('UnlockItem_NoLock', itemID)
    364         if itemID in self.lockedItems:
    365             del self.lockedItems[itemID]
    366             self.LogInfo('Unlocked Item:', itemID)
    367             sm.ScatterEvent('OnItemUnlocked', itemID)
    368         else:
    369             self.LogInfo('UnlockItem_NoLock irrelevant item:', itemID)
    370 
    371     def IsItemLocked(self, itemID, raiseIfLocked = 0):
    372         try:
    373             self.lockedItemsSemaphore.acquire()
    374             bIsLocked, lockTime, userErrorName, userErrorDict, co_name, co_filename, co_lineno, thread = self.IsItemLocked_NoLock(itemID)
    375         finally:
    376             self.lockedItemsSemaphore.release()
    377 
    378         if bIsLocked and raiseIfLocked:
    379             self.RaiseLockError(itemID, lockTime, userErrorName, userErrorDict, co_name, co_filename, co_lineno)
    380         return bIsLocked
    381 
    382     def IsItemLocked_NoLock(self, itemID):
    383         if itemID not in self.lockedItems:
    384             return (0,
    385              0,
    386              '',
    387              {},
    388              None,
    389              None,
    390              None,
    391              None)
    392         lockTime, userErrorName, userErrorDict, co_name, co_filename, co_lineno, thread = self.lockedItems[itemID]
    393         return (1,
    394          lockTime,
    395          userErrorName,
    396          userErrorDict,
    397          co_name,
    398          co_filename,
    399          co_lineno,
    400          thread)
    401 
    402     def RemoveInventoryContainer(self, containerItem):
    403         self.LogInfo('Removing', evetypes.GetName(containerItem.typeID), 'container', containerItem.itemID, 'from cache as ownership has changed.')
    404         if (containerItem.itemID, None) not in self.inventories:
    405             self.LogInfo('RemoveInventoryContainer container not found in inventories')
    406             return
    407         for itemID, item in self.inventories[containerItem.itemID, None].cachedItems.iteritems():
    408             if cfg.IsContainer(item):
    409                 self.RemoveInventoryContainer(item)
    410 
    411         del self.inventories[containerItem.itemID, None]
    412 
    413     def GetInventoryMgr(self):
    414         return self.inventorymgr
    415 
    416     def GetItemHeader(self, force = 0):
    417         if self.__itemhdr is None or force:
    418             self.__itemhdr = sm.RemoteSvc('invbroker').GetItemDescriptor()
    419             self.LogInfo('invCache got remote itemhdr', self.__itemhdr)
    420         return self.__itemhdr
    421 
    422     def ValidateItemCanBeOpened(self, item):
    423         if not cfg.IsCargoContainer(item):
    424             return True
    425         if self.__invmgr:
    426             return self.__invmgr.ValidateItemCanBeOpened(item)
    427         return True
    428 
    429     def ValidateItemListCanBeOpened(self, items):
    430         cargoContainerItems = []
    431         for item in items:
    432             if cfg.IsCargoContainer(item):
    433                 cargoContainerItems.append(item)
    434 
    435         if cargoContainerItems and self.__invmgr:
    436             return self.__invmgr.ValidateItemListCanBeOpened(cargoContainerItems)
    437         return []
    438 
    439     def GetItemsInCurrentShip(self, inventoryFlags):
    440         items = set()
    441         inventory = self.GetInventoryFromId(session.shipid)
    442         for inventoryFlag in inventoryFlags:
    443             for item in inventory.List(inventoryFlag):
    444                 items.add(item.itemID)
    445 
    446         return items
    447 
    448     def _GetShipsInHangarInventory(self, inventory):
    449         items = set()
    450         for item in inventory.List(const.flagHangar):
    451             if idCheckers.IsShip(item.categoryID):
    452                 items.add(item.itemID)
    453 
    454         return items
    455 
    456     def _GetItemsInHangarInventory(self, inventory):
    457         items = set()
    458         for item in inventory.List(const.flagHangar):
    459             if not idCheckers.IsShip(item.categoryID):
    460                 items.add(item.itemID)
    461 
    462         return items
    463 
    464     @RaiseIfInventoryLocationUnavailable(sessionCheck=lambda : bool(session.shipid), locationName='ship')
    465     def GetItemsInCurrentShipCargo(self):
    466         inventoryFlags = [const.flagCargo]
    467         return self.GetItemsInCurrentShip(inventoryFlags)
    468 
    469     @RaiseIfInventoryLocationUnavailable(sessionCheck=lambda : bool(session.stationid), locationName='station')
    470     def GetItemsInCurrentStationShipHangar(self):
    471         inventory = self.GetInventory(const.containerHangar)
    472         return self._GetShipsInHangarInventory(inventory)
    473 
    474     @RaiseIfInventoryLocationUnavailable(sessionCheck=lambda : bool(session.stationid), locationName='station')
    475     def GetItemsInCurrentStationItemHangar(self):
    476         inventory = self.GetInventory(const.containerHangar)
    477         return self._GetItemsInHangarInventory(inventory)
    478 
    479     @RaiseIfInventoryLocationUnavailable(sessionCheck=lambda : bool(session.structureid), locationName='structure')
    480     def GetItemsInCurrentStructureShipHangar(self):
    481         inventory = self.GetInventoryFromId(session.structureid)
    482         return self._GetShipsInHangarInventory(inventory)
    483 
    484     @RaiseIfInventoryLocationUnavailable(sessionCheck=lambda : bool(session.structureid), locationName='structure')
    485     def GetItemsInCurrentStructureItemHangar(self):
    486         inventory = self.GetInventoryFromId(session.structureid)
    487         return self._GetItemsInHangarInventory(inventory)
    488 
    489     def GetInventory(self, containerid, param1 = None):
    490         if containerid in (const.containerHangar, const.containerCorpMarket, const.containerCapsuleerDeliveries) and session.structureid:
    491             containerid = const.containerStructure
    492             key = (session.structureid, None)
    493         else:
    494             key = (containerid, param1)
    495         if containerid in (const.containerHangar, const.containerCorpMarket, const.containerCapsuleerDeliveries):
    496             if self.__stationID != 0 and self.__stationID != session.stationid:
    497                 self.InvalidateCache()
    498             inventoryMgr = self.stationInventoryMgr
    499             sessionCheckParams = {'stationid': self.__stationID}
    500         else:
    501             if self.__invlocID != session.locationid:
    502                 self.InvalidateCache()
    503             inventoryMgr = self.inventorymgr
    504             sessionCheckParams = {'locationid': self.__invlocID}
    505         if containerid in SPECIAL_CONTAINER_IDS:
    506             if containerid == const.containerGlobal:
    507                 if self.containerGlobal is None:
    508                     inv = Moniker('charMgr', (session.charid, containerid))
    509                     self.containerGlobal = inv5MinCacheContainer(inv)
    510                 return self.containerGlobal
    511             inv = self.inventorymgr.GetInventory(containerid, param1)
    512             inv.SetSessionCheck({'locationid': self.__invlocID})
    513             return inv
    514         if key not in self.inventories:
    515             inv = inventoryMgr.GetInventory(containerid, param1)
    516             inv.SetSessionCheck(sessionCheckParams)
    517             self.inventories[key] = invCacheContainer(inv, key, self)
    518         return self.inventories[key]
    519 
    520     def GetInventoryFromId(self, itemid, passive = 0, locationID = None):
    521         if itemid is None:
    522             log.LogTraceback('Thou shalt not send None in as itemid to GetInventoryFromId')
    523             raise IndexError
    524         if locationID is None:
    525             locationID = session.locationid
    526         if locationID == session.locationid:
    527             if locationID != self.__invlocID:
    528                 self.InvalidateCache()
    529             inventoryMgr = self.inventorymgr
    530             sessionCheckParams = {'locationid': self.__invlocID}
    531         elif locationID == session.structureid:
    532             if session.solarsystemid != self.__invlocID:
    533                 self.InvalidateCache()
    534             inventoryMgr = self.inventorymgr
    535             sessionCheckParams = {'locationid': self.__invlocID,
    536              'structureid': session.structureid}
    537         elif locationID == session.stationid:
    538             if locationID != self.__stationID:
    539                 self.InvalidateCache()
    540             inventoryMgr = self.stationInventoryMgr
    541             sessionCheckParams = {'stationid': self.__stationID}
    542         key = (itemid, None)
    543         if key in self.inventories:
    544             return self.inventories[key]
    545         inv = inventoryMgr.GetInventoryFromId(itemid, passive)
    546         inv.SetSessionCheck(sessionCheckParams)
    547         self.inventories[key] = invCacheContainer(inv, key, self)
    548         return self.inventories[key]
    549 
    550     def IsInventoryPrimedAndListed(self, itemid):
    551         key = (itemid, None)
    552         inv = self.inventories.get(key, None)
    553         if inv is not None and inv.listed:
    554             return True
    555         return False
    556 
    557     def InvalidateCache(self):
    558         self.__invmgr = None
    559         self.__stationinvmgr = None
    560         self.containerGlobal = None
    561         for containerGuard in self.inventories.itervalues():
    562             containerGuard.moniker = None
    563 
    564         self.inventories = {}
    565         self.lockedItems = {}
    566         lockKeys = self.locks.keys()
    567         for lockKey in lockKeys:
    568             uthread.UnLock(self, lockKey)
    569 
    570         self.locks = {}
    571 
    572     def ValidateLocationCacheForItems(self, items):
    573         invalidItemIDs = self.ValidateItemListCanBeOpened(items)
    574         for itemID in invalidItemIDs:
    575             self.InvalidateLocationCache(itemID)
    576 
    577     def InvalidateLocationCache(self, itemID):
    578         key = itemID if isinstance(itemID, tuple) else (itemID, None)
    579         try:
    580             self.inventories[key].moniker = None
    581             del self.inventories[key]
    582             self.LogInfo('Invalidated location cache for ', key)
    583         except KeyError:
    584             pass
    585 
    586     def InvalidateCorpCache(self):
    587         keysToInvalidate = self.GetOfficeKeys() | self.GetCorpKeys()
    588         for key in keysToInvalidate:
    589             if key in self.inventories:
    590                 del self.inventories[key]
    591 
    592         if keysToInvalidate:
    593             self.LogInfo('Removed inventory for corp hangar')
    594 
    595     def OnInvalidateCacheForLocations(self, locationIDs):
    596         for locID in locationIDs:
    597             self.InvalidateLocationCache(locID)
    598 
    599     def GetOfficeKeys(self):
    600         officeItemsKeys = set()
    601         office = sm.GetService('officeManager').GetCorpOfficeAtLocation()
    602         if office is None:
    603             return officeItemsKeys
    604         officeKey = (office.officeID, None)
    605         officeItemsKeys.add(officeKey)
    606         for key in self.inventories.keys():
    607             item = getattr(self.inventories[key], 'item', None)
    608             if item:
    609                 locationID = getattr(item, 'locationID', None)
    610                 if locationID and locationID == office.officeID:
    611                     officeItemsKeys.add(key)
    612 
    613         return officeItemsKeys
    614 
    615     def GetCorpKeys(self):
    616         corpItemsKeys = set()
    617         for key in self.inventories.keys():
    618             item = getattr(self.inventories[key], 'item', None)
    619             if item:
    620                 ownerID = getattr(item, 'ownerID', None)
    621                 if ownerID and ownerID == session.corpid:
    622                     corpItemsKeys.add(key)
    623 
    624         return corpItemsKeys
    625 
    626     def OnCapacityChange(self, moduleID):
    627         key = (moduleID, None)
    628         if key in self.inventories:
    629             self.inventories[key].capacityByFlag = {}
    630             self.inventories[key].capacityByLocation = None
    631 
    632     def GetStationIDOfItem(self, item):
    633         stationID = self.GetStationIDandOfficeIDOfItem(item)[0]
    634         if stationID:
    635             return stationID
    636         if item.flagID in const.flagCorpSAGs:
    637             if idCheckers.IsStation(item.locationID) or idCheckers.IsSolarSystem(item.locationID):
    638                 return None
    639             if item.locationID in (session.locationid, session.structureid):
    640                 return item.locationID
    641             invCache = sm.GetService('invCache')
    642             if self.IsInventoryPrimedAndListed(item.locationID):
    643                 locationItem = invCache.GetInventoryFromId(item.locationID).GetItem()
    644                 while locationItem and locationItem.itemID not in (session.stationid, session.structureid) and locationItem.locationID > const.minStation:
    645                     if locationItem.typeID == const.typeAssetSafetyWrap:
    646                         return locationItem.locationID
    647                     locationItem = invCache.GetInventoryFromId(locationItem.locationID).GetItem()
    648 
    649     def GetStationIDandOfficeIDOfItem(self, item):
    650         if idCheckers.IsStation(item.locationID) or item.locationID == session.structureid:
    651             return (item.locationID, None)
    652         if item.flagID in const.flagCorpSAGs:
    653             for office in sm.GetService('officeManager').GetMyCorporationsOffices():
    654                 if item.locationID == office.officeID:
    655                     return (office.stationID, office.officeID)
    656 
    657         if sm.GetService('structureDirectory').GetStructureInfo(item.locationID):
    658             return (item.locationID, item.locationID)
    659         return (None, None)
    660 
    661     def FetchItem(self, itemID, fromLocationID):
    662         if idCheckers.IsStation(fromLocationID):
    663             inv = self.inventories.get((const.containerHangar, None), None)
    664         else:
    665             inv = self.inventories.get((fromLocationID, None), None)
    666         if inv is None:
    667             self.LogInfo('invCache::FetchItem - Could not find inventory', itemID, fromLocationID)
    668             return
    669         return inv.cachedItems.get(itemID, None)
    670 
    671     def GetCachedItem(self, itemID):
    672         for invCacheCont in self.inventories.values():
    673             if itemID in invCacheCont.cachedItems:
    674                 return invCacheCont.cachedItems[itemID]
    675 
    676     def IsMyItemAtCurrentDockable(self, item):
    677         currentDockable = session.stationid or session.structureid
    678         if not currentDockable:
    679             return False
    680         if not item:
    681             self.LogError('Failed to find if item is at current dockable: specified item is None')
    682             return False
    683         if item.ownerID != session.charid:
    684             return False
    685         isItemAtDockable = self._IsItemAtDockableBasedOnItemCache(item, currentDockable)
    686         if isItemAtDockable is not None:
    687             return isItemAtDockable
    688         return self.GetInventoryMgr().IsMyItemAtDockableLocation(currentDockable, item)
    689 
    690     def _IsItemAtDockableBasedOnItemCache(self, item, dockableID):
    691         if item.locationID == dockableID:
    692             return True
    693         elif item.locationID < const.minStation:
    694             return False
    695         parentItem = self.GetCachedItem(item.locationID)
    696         if not parentItem:
    697             return None
    698         else:
    699             return self._IsItemAtDockableBasedOnItemCache(parentItem, dockableID)
    700 
    701     def OnDogmaAttributeChanged(self, shipID, itemID, attributeID, value):
    702         if attributeID in CAPACITY_ATTRIBUTES:
    703             sm.ScatterEvent('OnCapacityChange', itemID)
    704 
    705     def ProcessActiveShipChanged(self, shipID, oldShipID):
    706         if shipID:
    707             sm.ScatterEvent('OnCapacityChange', shipID)
    708         key = (oldShipID, None)
    709         if key in self.inventories:
    710             del self.inventories[key]
    711 
    712     def AcceptPossibleRemovalTax(self, items):
    713         if not items:
    714             return True
    715         assetWrapItem = None
    716         containerID = items[0].locationID
    717         parentLocationItem = self.GetCachedItem(containerID)
    718         while parentLocationItem is not None and assetWrapItem is None:
    719             if parentLocationItem.typeID == const.typeAssetSafetyWrap:
    720                 assetWrapItem = parentLocationItem
    721             parentLocationItem = self.GetCachedItem(parentLocationItem.locationID)
    722 
    723         if assetWrapItem is None:
    724             return True
    725         assetInv = self.GetInventoryFromId(assetWrapItem.itemID)
    726         iskToPay = assetInv.GetPriceForItems([ item.itemID for item in items ])
    727         if iskToPay == 0:
    728             return True
    729         response = prompt_player_fsd_dialog('CustomQuestion', {'header': localization.GetByLabel('UI/Common/Confirm'),
    730          'question': localization.GetByLabel('UI/Inventory/AssetSafetyRecoveryConfirmation', tax=iskToPay)}, uiconst.YESNO)
    731         if response == uiconst.ID_YES:
    732             return True
    733         return False
    734 
    735     def StripFitting(self, itemID):
    736         inventory = self.GetInventoryFromId(itemID)
    737         fittedItems = ActiveShipController().GetModules()
    738         fittedTypes = {item.typeID for item in fittedItems}
    739         typesThatCannotBeUnfitted = {typeID for typeID in fittedTypes if not can_be_unfitted(typeID)}
    740         if typesThatCannotBeUnfitted:
    741             raise ItemCannotBeUnfitted(typesThatCannotBeUnfitted)
    742         ret = inventory.StripFitting()
    743         sm.ScatterEvent('OnFittingStripped', itemID)
    744         return ret
    745 
    746 
    747 class inv5MinCacheContainer():
    748     __update_on_reload__ = 1
    749 
    750     def __init__(self, moniker):
    751         self.moniker = moniker
    752         self.listed = None
    753         self.cachedItems = {}
    754         self.listTime = None
    755         self.listedAll = None
    756         self.cachedItemsAll = {}
    757         self.cachedStationItems = {}
    758         self.cachedStations = (0, [])
    759         self.crits = {}
    760         self.deleteCacheThread = None
    761 
    762     def __EnterCriticalSection(self, k, v = None):
    763         if (k, v) not in self.crits:
    764             self.crits[k, v] = uthread.CriticalSection((k, v))
    765         self.crits[k, v].acquire()
    766 
    767     def __LeaveCriticalSection(self, k, v = None):
    768         self.crits[k, v].release()
    769         if (k, v) in self.crits and self.crits[k, v].IsCool():
    770             del self.crits[k, v]
    771 
    772     def __str__(self):
    773         try:
    774             return 'Global inventory for %s:%d' % (cfg.eveowners.Get(session.charid).name, session.charid)
    775         except Exception:
    776             sys.exc_clear()
    777             return 'inventory.Global instance - failed to __str__ self'
    778 
    779     def ListStations(self):
    780         self.__EnterCriticalSection('ListStations')
    781         try:
    782             now = blue.os.GetWallclockTime()
    783             if self.cachedStations[0] + const.MIN * 5 < now:
    784                 self.cachedStations = (now, self.moniker.ListStations())
    785             return self.cachedStations[1]
    786         finally:
    787             self.__LeaveCriticalSection('ListStations')
    788 
    789     def ListStationItems(self, stationID):
    790         try:
    791             self.__EnterCriticalSection('ListStationItems', stationID)
    792             now = blue.os.GetWallclockTime()
    793             if stationID in self.cachedStationItems:
    794                 if self.cachedStationItems[stationID][0] + const.MIN * 5 < now:
    795                     del self.cachedStationItems[stationID]
    796             if stationID not in self.cachedStationItems:
    797                 self.cachedStationItems[stationID] = (now, self.moniker.ListStationItems(stationID))
    798             return self.cachedStationItems[stationID][1]
    799         finally:
    800             self.__LeaveCriticalSection('ListStationItems', stationID)
    801 
    802     def InvalidateStationItemsCache(self, stationID):
    803         if stationID in self.cachedStationItems:
    804             del self.cachedStationItems[stationID]
    805 
    806     def List(self):
    807         now = blue.os.GetWallclockTime()
    808         if self.listed is not None:
    809             if self.listTime + const.MIN * 5 < now:
    810                 self.listed = None
    811                 self.cachedItems = {}
    812         if self.listed is None:
    813             self.listTime = now
    814             if self.moniker is None:
    815                 return []
    816             realItems = self.moniker.List()
    817             for realItem in realItems:
    818                 self.cachedItems[realItem.itemID] = realItem
    819 
    820             self.listed = 1
    821         return self.cachedItems.values()
    822 
    823     def ListIncludingContainers(self):
    824         if self.listedAll:
    825             return self.cachedItemsAll.values()
    826         realItems = self.moniker.ListIncludingContainers()
    827         for realItem in realItems:
    828             self.cachedItemsAll[realItem.itemID] = realItem
    829 
    830         self.deleteCacheThread = uthread.new(self._DeleteCachedItemsAll)
    831         self.deleteCacheThread.context = 'DeleteCahcedItemsAll'
    832         self.listedAll = 1
    833         return self.cachedItemsAll.values()
    834 
    835     def GetAssetWorth(self):
    836         self.moniker.Bind()
    837         return self.moniker.GetAssetWorth()
    838 
    839     def _DeleteCachedItemsAll(self):
    840         blue.pyos.synchro.Sleep(const.MIN * 5 / 10000)
    841         self.listedAll = 0
    842         self.cachedItemsAll = {}
    843 
    844     def TrashItem(self, stationID, itemID, locationID):
    845         if stationID in self.cachedStationItems:
    846             for item in self.cachedStationItems[stationID][1]:
    847                 if item.itemID == itemID:
    848                     item.locationID = locationID
    849                     break
    850 
    851 
    852 class invCacheContainer():
    853     __update_on_reload__ = 1
    854 
    855     def __init__(self, moniker, key, broker):
    856         self.broker = broker
    857         self.key = key
    858         self.moniker = PasswordHandlerObjectWrapper(moniker)
    859         self.cachedItems = {}
    860         self.listed = set()
    861         self._itemID = None
    862         self._typeID = None
    863         self._item = None
    864         self.capacityByFlag = {}
    865         self.capacityByLocation = None
    866         containerIDs = (const.containerHangar,
    867          const.containerWallet,
    868          const.containerStationCharacters,
    869          const.containerGlobal,
    870          const.containerSolarSystem,
    871          const.containerRecycler,
    872          const.containerCharacter,
    873          const.containerCorpMarket,
    874          const.containerCapsuleerDeliveries)
    875         if key[0] not in containerIDs:
    876             self._itemID = key[0]
    877 
    878     @property
    879     def itemID(self):
    880         if self._itemID is None:
    881             try:
    882                 self._SetItemData()
    883             except AttributeError as error:
    884                 raise_(RuntimeError, error, sys.exc_info()[2])
    885 
    886         return self._itemID
    887 
    888     @property
    889     def typeID(self):
    890         if self._typeID is None:
    891             try:
    892                 self._SetItemData()
    893             except AttributeError as error:
    894                 raise_(RuntimeError, error, sys.exc_info()[2])
    895 
    896         return self._typeID
    897 
    898     @property
    899     def item(self):
    900         if self._item is None:
    901             try:
    902                 self._SetItemData()
    903             except AttributeError as error:
    904                 raise_(RuntimeError, error, sys.exc_info()[2])
    905 
    906         return self._item
    907 
    908     def _SetItemData(self):
    909         if None in (self._itemID, self._typeID, self._item):
    910             session.WaitUntilSafe()
    911             if self._item is None and self.moniker is not None:
    912                 self._item = self.moniker.GetSelfInvItem()
    913                 self._itemID = self._item.itemID
    914                 self._typeID = self._item.typeID
    915 
    916     def __str__(self):
    917         try:
    918             return 'invCacheContainer %d for %s' % (self.itemID, self.key)
    919         except Exception:
    920             sys.exc_clear()
    921             return 'invCacheContainer instance - failed to __str__ self'
    922 
    923     def LogInfo(self, *k, **v):
    924         self.broker.LogInfo('invCacheContainer', self.key, ':', *k, **v)
    925 
    926     def LogWarn(self, *k, **v):
    927         self.broker.LogWarn('invCacheContainer', self.key, ':', *k, **v)
    928 
    929     def LogError(self, *k, **v):
    930         self.broker.LogError('invCacheContainer', self.key, ':', *k, **v)
    931 
    932     def Add(self, itemID, sourceID, **kw):
    933         activeShipID = eveCfg.GetActiveShip()
    934         if itemID == activeShipID:
    935             raise UserError('CanNotPlaceActiveShipInAnotherHangar')
    936         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    937         if self.itemID == activeShipID:
    938             flagID = kw.get('flag', None)
    939             if flagID is not None and IsFittingFlag(flagID) or flagID == const.flagAutoFit:
    940                 dogmaLocation.CheckCanFit(self.itemID, itemID, flagID, sourceID)
    941         if 'qty' in kw and kw['qty'] == 0:
    942             self.LogError('Trying to add a stack with 0 quantity to a container. Fix your code.')
    943             return
    944         if sm.GetService('lockedItems').IsItemIDLocked(itemID):
    945             raise UserError('CrpItemIsLocked')
    946         if itemID == self.itemID:
    947             raise UserError('CannotPlaceItemInsideItself')
    948         self.broker.TryLockItem(itemID, 'lockAddItemToContainer', {'containerType': self.typeID}, 1)
    949         try:
    950             if type(itemID) is tuple:
    951                 return dogmaLocation.UnloadAmmoToContainer(activeShipID, itemID, self.itemID, kw.get('qty'))
    952             return self.moniker.Add(itemID, sourceID, **kw)
    953         finally:
    954             self.broker.UnlockItem(itemID)
    955 
    956     def MultiAdd(self, itemIDs, sourceID, **kw):
    957         activeShipID = eveCfg.GetActiveShip()
    958         if activeShipID in itemIDs:
    959             raise UserError('CanNotPlaceActiveShipInAnotherHangar')
    960         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    961         if self.itemID == activeShipID:
    962             flagID = kw.get('flag', None)
    963             if flagID is not None and IsFittingFlag(flagID) or flagID == const.flagAutoFit:
    964                 for itemID in itemIDs:
    965                     dogmaLocation.CheckCanFit(self.itemID, itemID, flagID, sourceID)
    966 
    967         lockedItems = set()
    968         charges = set()
    969         for itemID in itemIDs:
    970             if self.itemID is not None and itemID == self.itemID:
    971                 continue
    972             if sm.GetService('lockedItems').IsItemIDLocked(itemID):
    973                 continue
    974             if not self.broker.IsItemLocked(itemID):
    975                 if self.broker.TryLockItem(itemID):
    976                     lockedItems.add(itemID)
    977                     if type(itemID) is tuple:
    978                         charges.add(itemID)
    979                         dogmaLocation.UnloadAmmoToContainer(activeShipID, itemID, self.itemID)
    980 
    981         if not len(lockedItems):
    982             return
    983         try:
    984             nonCharges = lockedItems - charges
    985             if len(nonCharges) > 0:
    986                 self.moniker.MultiAdd(list(nonCharges), sourceID, **kw)
    987         finally:
    988             for itemID in lockedItems:
    989                 self.broker.UnlockItem(itemID)
    990 
    991         return True
    992 
    993     def StackAll(self, *args, **kw):
    994         if idCheckers.IsFakeItem(self.itemID):
    995             self.LogInfo("StackAll called on a fake location. Why bother.. I won't do it")
    996             return
    997         itemIDsToLock = []
    998         types = set()
    999         hasSomethingToStack = False
   1000         for each in self.List(*args, **kw):
   1001             if self.itemID is not None and each.itemID == self.itemID:
   1002                 continue
   1003             if each.quantity < 0:
   1004                 continue
   1005             if sm.GetService('lockedItems').IsItemLocked(each):
   1006                 continue
   1007             if not hasSomethingToStack:
   1008                 if each.quantity != const.maxInt:
   1009                     if each.typeID in types:
   1010                         hasSomethingToStack = True
   1011                     types.add(each.typeID)
   1012             if not self.broker.IsItemLocked(each.itemID):
   1013                 if self.broker.TryLockItem(each.itemID):
   1014                     itemIDsToLock.append(each.itemID)
   1015 
   1016         if not len(itemIDsToLock):
   1017             return
   1018         try:
   1019             if hasSomethingToStack:
   1020                 return self.moniker.StackAll(*args, **kw)
   1021         finally:
   1022             for itemID in itemIDsToLock:
   1023                 self.broker.UnlockItem(itemID)
   1024 
   1025     def MultiMerge(self, ops, sourceContainerID):
   1026         itemIDsToLock = []
   1027         for i, (sourceid, destid, qty) in enumerate(ops):
   1028             if self.itemID is not None and sourceid == self.itemID:
   1029                 continue
   1030             if sm.GetService('lockedItems').IsItemIDLocked(sourceid):
   1031                 continue
   1032             if not self.broker.IsItemLocked(sourceid):
   1033                 if self.broker.TryLockItem(sourceid):
   1034                     itemIDsToLock.append(sourceid)
   1035 
   1036         if not len(itemIDsToLock):
   1037             return
   1038         try:
   1039             dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
   1040             activeShipID = eveCfg.GetActiveShip()
   1041             for sourceid, destid, qty in copy.copy(ops):
   1042                 if type(sourceid) is tuple:
   1043                     dogmaLocation.UnloadAmmoToContainer(activeShipID, sourceid, destid, qty)
   1044                     ops.remove((sourceid, destid, qty))
   1045 
   1046             self.LogInfo('MultiMerge - merging ops = ', len(ops))
   1047             if len(ops) > 0:
   1048                 self.moniker.MultiMerge(ops, sourceContainerID)
   1049         finally:
   1050             for itemID in itemIDsToLock:
   1051                 self.broker.UnlockItem(itemID)
   1052 
   1053     def List(self, *args, **kw):
   1054         flag = None
   1055         if args is not None and len(args):
   1056             flag = args[0]
   1057         elif kw is not None and 'flag' in kw:
   1058             flag = kw['flag']
   1059         if flag not in self.listed:
   1060             realItems = self.moniker.List(flag=flag)
   1061             for realItem in realItems:
   1062                 self.cachedItems[realItem.itemID] = realItem
   1063                 self.listed.add(realItem.flagID)
   1064 
   1065             godmaSM = sm.GetService('godma').GetStateManager()
   1066             if godmaSM.IsLocationLoaded(self.itemID):
   1067                 godmaItem = godmaSM.GetItem(self.itemID)
   1068                 if godmaItem is not None:
   1069                     for subloc in godmaItem.sublocations:
   1070                         self.cachedItems[subloc.itemID] = subloc.invItem
   1071 
   1072             self.listed.add(flag)
   1073             self.LogInfo('We now have listed', self.listed, 'in', self.key)
   1074         res = CRowset(self.broker.GetItemHeader(), [])
   1075         for itemID in self.cachedItems.iterkeys():
   1076             item = self.cachedItems[itemID]
   1077             if flag is not None:
   1078                 if item.flagID != flag:
   1079                     continue
   1080             if 'typeID' in kw and item.typeID != kw['typeID']:
   1081                 continue
   1082             if len(item) != len(res.header):
   1083                 self.LogError('Invalid Item', item)
   1084                 continue
   1085             res.append(item)
   1086 
   1087         return res
   1088 
   1089     def ListByFlags(self, flagIDs, **kw):
   1090         res = CRowset(self.broker.GetItemHeader(), [])
   1091         if not flagIDs:
   1092             return res
   1093         uncachedFlags = [ flagID for flagID in flagIDs if flagID not in self.listed ]
   1094         if uncachedFlags:
   1095             uncachedRealItems = self.moniker.ListByFlags(flags=uncachedFlags)
   1096             for realItem in uncachedRealItems:
   1097                 self.cachedItems[realItem.itemID] = realItem
   1098                 self.listed.add(realItem.flagID)
   1099 
   1100             godmaSM = sm.GetService('godma').GetStateManager()
   1101             if godmaSM.IsLocationLoaded(self.itemID):
   1102                 godmaItem = godmaSM.GetItem(self.itemID)
   1103                 if godmaItem is not None:
   1104                     for subloc in godmaItem.sublocations:
   1105                         self.cachedItems[subloc.itemID] = subloc.invItem
   1106 
   1107             for flagID in uncachedFlags:
   1108                 self.listed.add(flagID)
   1109 
   1110             self.LogInfo('We now have listed', self.listed, 'in', self.key)
   1111         for itemID in self.cachedItems.iterkeys():
   1112             item = self.cachedItems[itemID]
   1113             if item.flagID not in flagIDs:
   1114                 continue
   1115             if 'typeID' in kw and item.typeID != kw['typeID']:
   1116                 continue
   1117             if len(item) != len(res.header):
   1118                 self.LogError('Invalid Item', item)
   1119                 continue
   1120             res.append(item)
   1121 
   1122         return res
   1123 
   1124     def ListHangar(self):
   1125         return self.List(const.flagHangar)
   1126 
   1127     def ListCargo(self):
   1128         return self.List(const.flagCargo)
   1129 
   1130     def ListDroneBay(self):
   1131         return self.List(const.flagDroneBay)
   1132 
   1133     def ListHardware(self):
   1134         return [ i for i in self.List() if const.flagSlotFirst <= i.flagID <= const.flagSlotLast ]
   1135 
   1136     def ListHardwareModules(self):
   1137         return [ i for i in self.List() if IsFittable(i.categoryID) and IsFittingFlag(i.flagID) ]
   1138 
   1139     @staticmethod
   1140     def IsFlagCapacityLocationWide(item):
   1141         if item.groupID in (const.groupCorporateHangarArray, const.groupAssemblyArray, const.groupMobileLaboratory):
   1142             return True
   1143         return False
   1144 
   1145     def GetCapacity(self, flag = None):
   1146         if self.item.groupID in (const.groupStation,) or self.item.typeID in (const.typeOffice,):
   1147             capacity = Row(['capacity', 'used'], [9000000000000000.0, 0.0])
   1148             return capacity
   1149         if flag == flagFrigateEscapeBay:
   1150             return self.GetFrigateEscapeBayCapacity(flag)
   1151         if flag is None:
   1152             flags = {i.flagID for i in self.List() if i.flagID is not None}
   1153             if flags:
   1154                 capacity = Row(['capacity', 'used'], [0, 0.0])
   1155                 for flag in flags:
   1156                     cap = self.GetCapacity(flag)
   1157                     if capacity.capacity == 0:
   1158                         capacity.capacity = cap.capacity
   1159                     capacity.used += cap.used
   1160 
   1161                 return capacity
   1162         flag = flag or const.flagNone
   1163         itemID = self.itemID
   1164         if itemID is not None:
   1165             godmaItem = sm.GetService('godma').GetItem(itemID)
   1166             if godmaItem is not None and godmaItem.itemID in godmaItem.statemanager.invByID:
   1167                 if godmaItem.statemanager.invByID[godmaItem.itemID] != self:
   1168                     self.LogWarn('FIXUP: godma is using an old thrown away invCacheContainer')
   1169                     godmaItem.statemanager.invByID[godmaItem.itemID] = self
   1170                 capacity = godmaItem.GetCapacity(flag)
   1171                 return capacity
   1172         capacityAttributeID = None
   1173         if flag == const.flagDroneBay:
   1174             capacityAttributeID = const.attributeDroneCapacity
   1175         elif flag in inventoryFlagsCommon.inventoryFlagData and flag != const.flagCargo:
   1176             capacityAttributeID = inventoryFlagsCommon.inventoryFlagData[flag]['attribute']
   1177         elif self.item.categoryID == const.categoryShip:
   1178             if flag == const.flagShipHangar:
   1179                 capacityAttributeID = const.attributeShipMaintenanceBayCapacity
   1180             elif flag == const.flagFleetHangar:
   1181                 capacityAttributeID = const.attributeFleetHangarCapacity
   1182         elif flag == const.flagSecondaryStorage:
   1183             capacityAttributeID = const.attributeCapacitySecondary
   1184         elif self.item.groupID == const.groupSilo:
   1185             capacityAttributeID = const.attributeCapacity
   1186         typeID = self.typeID
   1187         dogmaIM = sm.GetService('clientDogmaIM')
   1188         if capacityAttributeID is not None:
   1189             actualCapacity = dogmaIM.GetCapacityForItem(itemID, capacityAttributeID)
   1190             if actualCapacity is None:
   1191                 attribs = [ x for x in dogma.data.get_type_attributes(typeID) if x.attributeID == capacityAttributeID ]
   1192                 if self.item.groupID == const.groupSilo and self.item.categoryID == const.categoryStarbase:
   1193                     actualCapacity = self.GetSiloCapacityByItemID(itemID)
   1194                 if actualCapacity is None and len(attribs):
   1195                     actualCapacity = attribs[0].value
   1196                 if actualCapacity is None:
   1197                     actualCapacity = dogma.data.get_attribute_default_value(capacityAttributeID)
   1198         else:
   1199             actualCapacity = None
   1200             if flag == const.flagCargo:
   1201                 actualCapacity = dogmaIM.GetCapacityForItem(itemID, const.attributeCapacity)
   1202             if actualCapacity is None:
   1203                 actualCapacity = evetypes.GetCapacity(typeID)
   1204         used = 0.0
   1205         isCustomsOffice = evetypes.GetGroupID(typeID) == const.groupPlanetaryCustomsOffices
   1206         for each in self.List(flag):
   1207             if isCustomsOffice and flag == const.flagHangar and each.ownerID != session.charid:
   1208                 continue
   1209             if each.flagID == flag:
   1210                 used = used + GetItemVolume(each)
   1211 
   1212         if typeID == const.typePlasticWrap:
   1213             actualCapacity = used
   1214         if self.item.groupID == const.groupNpcIndustrialCommand and self.item.ownerID in RW_CORPORATIONS:
   1215             haulerCapacity = sm.GetService('rwService').get_hauler_capacity(itemID)
   1216             if haulerCapacity is not None:
   1217                 actualCapacity = haulerCapacity
   1218         capacity = Row(['capacity', 'used'], [actualCapacity, used])
   1219         self.capacityByLocation = capacity
   1220         return capacity
   1221 
   1222     def GetFrigateEscapeBayCapacity(self, flag):
   1223         used = 0.0
   1224         for each in self.List(flag):
   1225             used = used + GetItemVolume(each)
   1226 
   1227         if used:
   1228             used = 1.0
   1229         capacity = Row(['capacity', 'used'], [1.0, used])
   1230         return capacity
   1231 
   1232     @Memoize(1)
   1233     def GetSiloCapacityByItemID(self, itemID):
   1234         return Moniker('posMgr', session.solarsystemid).GetSiloCapacityByItemID(itemID)
   1235 
   1236     def GetItem(self, force = False):
   1237         if force is True:
   1238             self._itemID = self._typeID = self._item = None
   1239         return self.item
   1240 
   1241     def OnItemChange(self, item, change, location):
   1242         self._ProcessItemChange(item, change)
   1243         sm.ScatterEvent('OnItemChangeProcessed', item, change)
   1244 
   1245     def _ProcessItemChange(self, item, change):
   1246         oldItem = blue.DBRow(item)
   1247         for k, v in change.iteritems():
   1248             if k in (const.ixStackSize, const.ixSingleton):
   1249                 k = const.ixQuantity
   1250             oldItem[k] = v
   1251 
   1252         location = self.key[0]
   1253         owner = self.key[1]
   1254         shouldCache = False
   1255         if item.flagID in self.listed or item.ownerID == session.charid:
   1256             shouldCache = True
   1257         elif item.locationID == session.shipid:
   1258             shouldCache = True
   1259         elif item.ownerID == session.corpid:
   1260             role = 0
   1261             if item.flagID in const.corpHangarQueryRolesByFlag:
   1262                 role = const.corpHangarQueryRolesByFlag[item.flagID]
   1263             elif self.item.groupID in const.containerGroupIDs:
   1264                 if not idCheckers.IsSystemOrNPC(item.locationID):
   1265                     try:
   1266                         containerItem = self.broker.GetInventoryFromId(item.locationID).GetItem()
   1267                         role = const.corpHangarQueryRolesByFlag[containerItem.flagID]
   1268                     except KeyError:
   1269                         pass
   1270 
   1271             if session.corprole & role:
   1272                 shouldCache = True
   1273         if location == const.containerHangar:
   1274             if owner is not None and owner not in [item.ownerID, oldItem.ownerID]:
   1275                 return
   1276             if not session.stationid:
   1277                 return
   1278             if location == const.containerHangar and owner is not None and item.ownerID == owner and const.ixOwnerID in change:
   1279                 self.cachedItems[item.itemID] = item
   1280                 return
   1281             if item.locationID != session.stationid or item.stacksize == 0 or item.ownerID != session.charid:
   1282                 if oldItem.flagID in self.capacityByFlag:
   1283                     del self.capacityByFlag[oldItem.flagID]
   1284                 elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1285                     self.capacityByLocation = None
   1286                 if item.itemID in self.cachedItems:
   1287                     del self.cachedItems[item.itemID]
   1288                 return
   1289             if item.ownerID == session.charid:
   1290                 if oldItem.flagID in self.capacityByFlag:
   1291                     del self.capacityByFlag[oldItem.flagID]
   1292                 elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1293                     self.capacityByLocation = None
   1294                 self.cachedItems[item.itemID] = item
   1295         elif location in (const.containerCorpMarket, const.containerCapsuleerDeliveries):
   1296             if owner is not None and owner not in [item.ownerID, oldItem.ownerID]:
   1297                 return
   1298             if not session.stationid:
   1299                 return
   1300             if item.locationID != session.stationid or item.stacksize == 0 or item.ownerID != session.corpid:
   1301                 if oldItem.flagID in self.capacityByFlag:
   1302                     del self.capacityByFlag[oldItem.flagID]
   1303                 elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1304                     self.capacityByLocation = None
   1305                 if item.itemID in self.cachedItems:
   1306                     del self.cachedItems[item.itemID]
   1307                 return
   1308             if item.ownerID == session.corpid:
   1309                 if oldItem.flagID in self.capacityByFlag:
   1310                     del self.capacityByFlag[oldItem.flagID]
   1311                 elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1312                     self.capacityByLocation = None
   1313                 if oldItem.locationID != session.stationid and item.locationID == session.stationid:
   1314                     if item.itemID in self.cachedItems:
   1315                         del self.cachedItems[item.itemID]
   1316                     self.cachedItems[item.itemID] = item
   1317                 else:
   1318                     if item.itemID in self.cachedItems:
   1319                         del self.cachedItems[item.itemID]
   1320                     self.cachedItems[item.itemID] = item
   1321         elif item.locationID != location or item.stacksize == 0:
   1322             if item.itemID in self.cachedItems:
   1323                 del self.cachedItems[item.itemID]
   1324             if oldItem.flagID in self.capacityByFlag:
   1325                 del self.capacityByFlag[oldItem.flagID]
   1326             elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1327                 self.capacityByLocation = None
   1328         elif oldItem.locationID != location and item.locationID == location:
   1329             if item.itemID in self.cachedItems:
   1330                 del self.cachedItems[item.itemID]
   1331             if shouldCache:
   1332                 self.cachedItems[item.itemID] = item
   1333             if item.flagID in self.capacityByFlag:
   1334                 del self.capacityByFlag[item.flagID]
   1335             elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1336                 self.capacityByLocation = None
   1337         else:
   1338             if item.itemID in self.cachedItems:
   1339                 del self.cachedItems[item.itemID]
   1340             if shouldCache:
   1341                 self.cachedItems[item.itemID] = item
   1342             if item.flagID in self.capacityByFlag:
   1343                 del self.capacityByFlag[item.flagID]
   1344             elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1345                 self.capacityByLocation = None
   1346             if const.ixFlag in change:
   1347                 if oldItem.flagID in self.capacityByFlag:
   1348                     del self.capacityByFlag[oldItem.flagID]
   1349                 elif self.item and self.IsFlagCapacityLocationWide(self.item):
   1350                     self.capacityByLocation = None
   1351 
   1352     def __getattr__(self, method):
   1353         if method in self.__dict__:
   1354             return self.__dict__[method]
   1355         if method.startswith('__'):
   1356             raise AttributeError(method)
   1357         if not hasattr(self.moniker, method):
   1358             raise AttributeError(method)
   1359         return FastInvCallWrapper(self.moniker, method)
   1360 
   1361 
   1362 class PasswordHandlerCallWrapper():
   1363 
   1364     def __init__(self, object, method):
   1365         self.__method__ = method
   1366         self.__callable__ = object
   1367 
   1368     def __call__(self, *args, **keywords):
   1369         try:
   1370             return getattr(self.__callable__, self.__method__)(*args, **keywords)
   1371         except UserError as what:
   1372             if what.args[0] == 'PasswordProtectedCacheInvalid':
   1373                 invCache = sm.GetService('invCache')
   1374                 invCache.InvalidateLocationCache(args[1])
   1375                 raise UserError('SCGeneralPasswordRequired')
   1376             else:
   1377                 if what.args[0] in ('SCGeneralPasswordRequired', 'SCConfigPasswordRequired') and self.__method__ != 'SetPassword':
   1378                     if what.args[0] == 'SCGeneralPasswordRequired':
   1379                         which = const.SCCPasswordTypeGeneral
   1380                         caption = localization.GetByLabel('UI/Inventory/ItemActions/AccessPasswordRequiredCaption')
   1381                         label = localization.GetByLabel('UI/Inventory/ItemActions/PleaseEnterAccessPassword')
   1382                     else:
   1383                         which = const.SCCPasswordTypeConfig
   1384                         caption = localization.GetByLabel('UI/Inventory/ItemActions/ConfigurationPasswordRequiredCaption')
   1385                         label = localization.GetByLabel('UI/Inventory/ItemActions/PleaseEnterConfigurationPassword')
   1386                     passw = utilWindows.NamePopup(caption=caption, label=label, setvalue='', maxLength=16, passwordChar='*')
   1387                     if passw is None:
   1388                         raise
   1389                     sys.exc_clear()
   1390                     self.__callable__.EnterPassword(which, passw)
   1391                     return self.__call__(*args, **keywords)
   1392                 raise
   1393         finally:
   1394             self.__dict__.clear()
   1395 
   1396 
   1397 class PasswordHandlerObjectWrapper():
   1398 
   1399     def __init__(self, object):
   1400         self.__object = object
   1401 
   1402     def __getattr__(self, attributeName):
   1403         if attributeName.startswith('__'):
   1404             raise AttributeError(attributeName)
   1405         if not hasattr(self.__object, attributeName):
   1406             raise AttributeError(attributeName)
   1407         return PasswordHandlerCallWrapper(self.__object, attributeName)
   1408 
   1409 
   1410 class FastInvCallWrapper():
   1411 
   1412     def __init__(self, object, method):
   1413         self.__method__ = method
   1414         self.__callable__ = object
   1415 
   1416     def __call__(self, *args, **keywords):
   1417         try:
   1418             return getattr(self.__callable__, self.__method__)(*args, **keywords)
   1419         finally:
   1420             self.__dict__.clear()