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

inventorysvc.py (20176B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\inventorysvc.py
      2 import logging
      3 import sys
      4 import weakref
      5 import blue
      6 from locks import TempLock
      7 from eve.common.script.net import eveMoniker
      8 import inventorycommon.const as invConst
      9 import localization
     10 import telemetry
     11 from carbon.common.script.sys.service import Service
     12 from eve.client.script.environment.invControllers import ShipCargo, Structure
     13 from eve.client.script.ui.control.treeData import TreeData
     14 from eve.client.script.ui.shared.inventory.invCommon import SortData
     15 from eve.client.script.ui.shared.inventory.invConst import ContainerType
     16 from eve.client.script.ui.shared.inventory.invWindow import Inventory
     17 from eve.client.script.ui.shared.inventory.treeData import TreeDataShip, TreeDataInv, TreeDataStationCorp, TreeDataStructureCorp, TreeDataCelestialParent, TreeDataPOSCorp, TreeDataInvFolder, GetTreeDataClassByInvName, TreeDataShipHangar, TreeDataItemHangar, TreeDataAssetSafety, TreeDataStructure, TreeDataPlexVault
     18 from eve.common.script.sys import eveCfg
     19 from expiringdict import ExpiringDict
     20 from hackingcommon import hackingConstants
     21 from spacecomponents.common.componentConst import CARGO_BAY
     22 from spacecomponents.common.data import get_space_component_for_type
     23 from spacecomponents.common.helper import HasCargoBayComponent, HasAutoMoonMinerComponent
     24 from eve.common.script.sys.idCheckers import IsDockableStructure
     25 from eve.common.lib import appConst as const
     26 log = logging.getLogger(__name__)
     27 
     28 def GetInventorySvc():
     29     return sm.GetService('inv')
     30 
     31 
     32 class InventorySvc(Service):
     33     __guid__ = 'svc.inv'
     34     __exportedcalls__ = {'Register': [],
     35      'Unregister': []}
     36     __notifyevents__ = ['OnSlimItemChange']
     37 
     38     def Run(self, *etc):
     39         Service.Run(self, *etc)
     40         self.regs = {}
     41         self.itemClipboard = []
     42         self.itemClipboardCopy = False
     43         self.tempInvLocations = set()
     44         self.treeUpdatingEnabled = True
     45         self.crystalDamageTracker = CrystalDamageTracker()
     46 
     47     def _ShouldCloseContainer(self, item, change):
     48         if not item.singleton:
     49             return False
     50         if item.groupID not in (const.groupWreck,
     51          const.groupCargoContainer,
     52          const.groupSecureCargoContainer,
     53          const.groupAuditLogSecureContainer,
     54          const.groupFreightContainer):
     55             return False
     56         if const.ixLocationID in change or const.ixOwnerID in change or const.ixFlag in change:
     57             return True
     58         return False
     59 
     60     @telemetry.ZONE_METHOD
     61     def OnItemChange(self, item, change, location):
     62         self.LogInfo('OnItemChange', change, item)
     63         self.ClearItemClipboard()
     64         fancyChange = {}
     65         for k, v in change.iteritems():
     66             fancyChange[item.__columns__[k]] = (v, '->', item[k])
     67 
     68         self.LogInfo('OnItemChange (fancy)', fancyChange, item)
     69         old = blue.DBRow(item)
     70         for k, v in change.iteritems():
     71             if k == const.ixSingleton and v == 0:
     72                 v = 1
     73             if k in (const.ixStackSize, const.ixSingleton):
     74                 k = const.ixQuantity
     75             old[k] = v
     76 
     77         containerName = ''
     78         containerCookie = None
     79         closeContainer = self._ShouldCloseContainer(item, change)
     80         if closeContainer:
     81             containerName = 'loot_%s' % item.itemID
     82         for cookie, wr in self.regs.items():
     83             ob = wr()
     84             if not ob or ob.destroyed:
     85                 self.Unregister(cookie)
     86                 continue
     87             if closeContainer == 1:
     88                 if getattr(ob, 'id', 0) == item.itemID:
     89                     containerCookie = cookie
     90                     continue
     91                 if getattr(ob, 'name', '') == containerName:
     92                     containerCookie = cookie
     93                     continue
     94             if hasattr(ob, 'invController'):
     95                 if ob.invController is None:
     96                     continue
     97                 wasHere = old.stacksize != 0 and ob.invController.IsItemHere(old)
     98                 isHere = item.stacksize != 0 and ob.invController.IsItemHere(item)
     99             else:
    100                 wasHere = old.stacksize != 0 and ob.IsItemHere(old)
    101                 isHere = item.stacksize != 0 and ob.IsItemHere(item)
    102             try:
    103                 if getattr(ob, 'OnInvChangeAny', None):
    104                     ob.OnInvChangeAny(item, change)
    105                 if not wasHere and not isHere:
    106                     continue
    107                 if wasHere and isHere and getattr(ob, 'UpdateItem', None):
    108                     ob.UpdateItem(item, change)
    109                 elif wasHere and not isHere and getattr(ob, 'RemoveItem', None):
    110                     ob.RemoveItem(item)
    111                 elif not wasHere and isHere and getattr(ob, 'AddItem', None):
    112                     ob.AddItem(item)
    113                 if getattr(ob, 'OnInvChange', None):
    114                     ob.OnInvChange(item, change)
    115             except:
    116                 self.Unregister(cookie)
    117                 log.exception('svc.inv')
    118                 sys.exc_clear()
    119 
    120         if closeContainer == 1:
    121             if containerCookie is not None:
    122                 self.Unregister(containerCookie)
    123         if const.ixFlag in change:
    124             if change[const.ixFlag] in invConst.fittingFlags and item.flagID not in invConst.fittingFlags:
    125                 sm.ScatterEvent('OnModuleUnfitted', item.typeID)
    126             if {item.flagID, change[const.ixFlag]}.intersection(set(invConst.hiSlotFlags)):
    127                 sm.ScatterEvent('OnHighSlotFittingChanged')
    128 
    129     def OnSlimItemChange(self, oldSlim, newSlim):
    130         if oldSlim.hackingSecurityState is not None:
    131             if oldSlim.hackingSecurityState != newSlim.hackingSecurityState:
    132                 sm.GetService('invCache').InvalidateLocationCache(oldSlim.itemID)
    133                 if newSlim.hackingSecurityState != hackingConstants.hackingStateHacked:
    134                     invID = ('ItemFloatingCargo', oldSlim.itemID)
    135                     sm.GetService('inv').RemoveTemporaryInvLocation(invID)
    136 
    137     def Register(self, callbackObj):
    138         objectID = id(callbackObj)
    139         if objectID in self.regs:
    140             return
    141         self.LogInfo('Registering', objectID, callbackObj)
    142         self.regs[objectID] = weakref.ref(callbackObj)
    143         return objectID
    144 
    145     def Unregister(self, cookie):
    146         if cookie in self.regs:
    147             del self.regs[cookie]
    148             self.LogInfo('Unregistered', cookie)
    149         else:
    150             log.warning('inv.Unregister: Unknown cookie %s', cookie)
    151 
    152     def SetItemClipboard(self, nodes, copy = False):
    153         newNodes = []
    154         for node in nodes:
    155             if not self.IsOnClipboard(node.item.itemID):
    156                 newNodes.append(node)
    157 
    158         self.itemClipboard = newNodes
    159         self.itemClipboardCopy = copy
    160         sm.ScatterEvent('OnInvClipboardChanged')
    161 
    162     @telemetry.ZONE_METHOD
    163     def PopItemClipboard(self):
    164         ret = self.itemClipboard
    165         if not self.itemClipboardCopy:
    166             self.itemClipboard = []
    167         sm.ScatterEvent('OnInvClipboardChanged')
    168         return (ret, self.itemClipboardCopy)
    169 
    170     @telemetry.ZONE_METHOD
    171     def ClearItemClipboard(self):
    172         if self.itemClipboard:
    173             self.itemClipboard = []
    174             sm.ScatterEvent('OnInvClipboardChanged')
    175 
    176     def IsOnClipboard(self, itemID):
    177         if self.itemClipboardCopy:
    178             return
    179         itemIDs = [ node.item.itemID for node in self.itemClipboard ]
    180         return itemID in itemIDs
    181 
    182     def GetTemporaryInvLocations(self):
    183         bp = sm.GetService('michelle').GetBallpark()
    184         if bp is None or not session.solarsystemid or session.structureid:
    185             self.tempInvLocations = set()
    186         else:
    187             toRemove = []
    188             for invName, itemID in self.tempInvLocations:
    189                 if itemID not in bp.slimItems:
    190                     toRemove.append((invName, itemID))
    191 
    192             for invID in toRemove:
    193                 self.tempInvLocations.remove(invID)
    194 
    195         return self.tempInvLocations
    196 
    197     def AddTemporaryInvLocation(self, invID):
    198         if invID not in self.tempInvLocations:
    199             self.tempInvLocations.add(invID)
    200             sm.ChainEvent('ProcessTempInvLocationAdded', invID)
    201 
    202     def RemoveTemporaryInvLocation(self, invID, byUser = False):
    203         if invID in self.tempInvLocations:
    204             self.tempInvLocations.remove(invID)
    205             sm.ChainEvent('ProcessTempInvLocationRemoved', invID, byUser)
    206 
    207     def OnBreadcrumbTextClicked(self, linkNum, windowID, windowInstanceID = None):
    208         wnd = Inventory.GetIfOpen(windowID=windowID, windowInstanceID=windowInstanceID)
    209         if wnd:
    210             wnd.OnBreadcrumbLinkClicked(linkNum)
    211 
    212     @telemetry.ZONE_METHOD
    213     def GetInvLocationTreeData(self, rootInvID = None):
    214         data = []
    215         if session.shipid:
    216             if session.shipid == session.structureid:
    217                 data.append(TreeDataStructure(invType=ContainerType.STRUCTURE, itemID=session.structureid, typeID=Structure(session.shipid).GetTypeID()))
    218             else:
    219                 active_ship_tree_data = self.get_active_ship_tree_data()
    220                 data.append(active_ship_tree_data)
    221         if session.stationid:
    222             data.append(TreeDataShipHangar(invType=ContainerType.STATION_SHIPS, itemID=session.stationid, cmdName='OpenShipHangar'))
    223             data.append(TreeDataItemHangar(invType=ContainerType.STATION_ITEMS, itemID=session.stationid, cmdName='OpenHangarFloor'))
    224             if self._HangarHasAssetSafetyWrap():
    225                 data.append(TreeDataAssetSafety(invType=ContainerType.ASSET_SAFETY_DELIVERIES, itemID=session.stationid))
    226             if sm.GetService('officeManager').GetCorpOfficeAtLocation() is not None:
    227                 forceCollapsedMembers = not (rootInvID and rootInvID[0] in ('StationCorpMember', 'StationCorpMembers'))
    228                 forceCollapsed = not (rootInvID and rootInvID[0] in ('StationCorpHangar', 'StationCorpHangars'))
    229                 data.append(TreeDataStationCorp(forceCollapsed=forceCollapsed, forceCollapsedMembers=forceCollapsedMembers))
    230             if HasCorpDeliveryRoles():
    231                 data.append(TreeDataInv(invType=ContainerType.STATION_CORP_DELIVERIES, itemID=session.stationid, cmdName='OpenCorpDeliveries'))
    232             if HasCorpProjectManagerRoles():
    233                 data.append(TreeDataInv(invType=ContainerType.CORPORATION_ITEM_DELIVERIES, itemID=session.stationid, cmdName='OpenCorporationItemDeliveries'))
    234         elif session.structureid and IsDockableStructure(Structure(session.structureid).GetTypeID()):
    235             data.append(TreeDataShipHangar(invType=ContainerType.STRUCTURE_SHIP_HANGAR, itemID=session.structureid, cmdName='OpenShipHangar'))
    236             data.append(TreeDataItemHangar(invType=ContainerType.STRUCTURE_ITEM_HANGAR, itemID=session.structureid, cmdName='OpenHangarFloor'))
    237             data.append(TreeDataItemHangar(invType=ContainerType.STRUCTURE_DELIVERIES_HANGAR, itemID=session.structureid))
    238             if self._HangarHasAssetSafetyWrap():
    239                 data.append(TreeDataAssetSafety(invType=ContainerType.ASSET_SAFETY_DELIVERIES, itemID=session.structureid))
    240             corpOffice = sm.GetService('officeManager').GetCorpOfficeAtLocation()
    241             if corpOffice:
    242                 data.append(TreeDataStructureCorp(itemID=corpOffice.officeID))
    243             if HasCorpDeliveryRoles():
    244                 data.append(TreeDataInv(invType=ContainerType.STATION_CORP_DELIVERIES, itemID=session.structureid, cmdName='OpenCorpDeliveries'))
    245             if HasCorpProjectManagerRoles():
    246                 data.append(TreeDataInv(invType=ContainerType.CORPORATION_ITEM_DELIVERIES, itemID=session.structureid, cmdName='OpenCorporationItemDeliveries'))
    247         elif session.solarsystemid:
    248             starbaseData = []
    249             defensesData = []
    250             industryData = []
    251             hangarData = []
    252             infrastrcutureData = []
    253             bp = sm.GetService('michelle').GetBallpark()
    254             if bp:
    255                 for slimItem in bp.slimItems.values():
    256                     itemID = slimItem.itemID
    257                     groupID = slimItem.groupID
    258                     categoryID = slimItem.categoryID
    259                     if HasCargoBayComponent(slimItem.typeID):
    260                         if slimItem.ownerID == session.charid or get_space_component_for_type(slimItem.typeID, CARGO_BAY).allowFreeForAll:
    261                             data.append(TreeDataInv(invType=ContainerType.SPACE_COMPONENT_INVENTORY, itemID=itemID))
    262                     if HasAutoMoonMinerComponent(slimItem.typeID) and not session.structureid:
    263                         data.append(TreeDataInv(invType=ContainerType.STRUCTURE_MOON_MATERIAL_BAY, itemID=itemID, isRemovable=True))
    264                     haveAccess = bool(slimItem) and (slimItem.ownerID == session.charid or slimItem.ownerID == session.corpid or session.allianceid and slimItem.allianceID == session.allianceid)
    265                     isAnchored = not bp.balls[itemID].isFree
    266                     if not haveAccess or not isAnchored:
    267                         continue
    268                     if groupID == const.groupControlTower:
    269                         towerData = [TreeDataInv(invType=ContainerType.POS_STRONTIUM_BAY, itemID=itemID), TreeDataInv(invType=ContainerType.POS_FUEL_BAY, itemID=itemID)]
    270                         starbaseData.append(TreeDataCelestialParent(invType=ContainerType.BASE_CELESTIAL_CONTAINER, itemID=itemID, children=towerData, iconName='res:/UI/Texture/windowIcons/corporation.png'))
    271                     elif groupID == const.groupCorporateHangarArray:
    272                         hangarData.append(TreeDataPOSCorp(slimItem=slimItem))
    273                     elif groupID == const.groupAssemblyArray:
    274                         industryData.append(TreeDataPOSCorp(slimItem=slimItem))
    275                     elif groupID == const.groupMobileLaboratory:
    276                         industryData.append(TreeDataPOSCorp(slimItem=slimItem))
    277                     elif groupID in (const.groupMobileMissileSentry, const.groupMobileProjectileSentry, const.groupMobileHybridSentry):
    278                         defensesData.append(TreeDataInv(invType=ContainerType.POS_STRUCTURE_CHARGES, itemID=itemID))
    279                     elif groupID == const.groupMobileLaserSentry:
    280                         sentryData = [TreeDataInv(invType=ContainerType.POS_STRUCTURE_CHARGE_CRYSTAL, itemID=itemID), TreeDataInv(invType=ContainerType.POS_STRUCTURE_CHARGES_STORAGE, itemID=itemID)]
    281                         defensesData.append(TreeDataCelestialParent(invType=ContainerType.BASE_CELESTIAL_CONTAINER, itemID=itemID, children=sentryData, iconName='ui_13_64_9'))
    282                     elif groupID == const.groupShipMaintenanceArray:
    283                         hangarData.append(TreeDataInv(invType=ContainerType.POS_SHIP_MAINTENANCE_ARRAY, itemID=itemID))
    284                     elif groupID == const.groupSilo:
    285                         industryData.append(TreeDataInv(invType=ContainerType.POS_SILO, itemID=itemID))
    286                     elif groupID == const.groupMobileReactor:
    287                         industryData.append(TreeDataInv(invType=ContainerType.POS_MOBILE_REACTOR, itemID=itemID))
    288                     elif groupID == const.groupReprocessingArray:
    289                         industryData.append(TreeDataInv(invType=ContainerType.POS_REFINERY, itemID=itemID))
    290                     elif groupID == const.groupCompressionArray:
    291                         industryData.append(TreeDataInv(invType=ContainerType.POS_COMPRESSION, itemID=itemID))
    292                     elif groupID == const.groupPersonalHangar:
    293                         hangarData.append(TreeDataInv(invType=ContainerType.POS_PERSONAL_HANGAR, itemID=itemID))
    294 
    295             if industryData:
    296                 SortData(industryData)
    297                 starbaseData.append(TreeDataInvFolder(label=localization.GetByLabel('UI/Inventory/POSGroupIndustry'), children=industryData, icon='res:/UI/Texture/WindowIcons/industry.png'))
    298             if hangarData:
    299                 SortData(hangarData)
    300                 starbaseData.append(TreeDataInvFolder(label=localization.GetByLabel('UI/Inventory/POSGroupStorage'), children=hangarData, icon='res:/ui/Texture/WindowIcons/cargo.png'))
    301             if infrastrcutureData:
    302                 SortData(infrastrcutureData)
    303                 starbaseData.append(TreeDataInvFolder(label=localization.GetByLabel('UI/Inventory/POSGroupInfrastructure'), children=infrastrcutureData, icon='res:/ui/Texture/WindowIcons/sovereignty.png'))
    304             if defensesData:
    305                 SortData(defensesData)
    306                 starbaseData.append(TreeDataInvFolder(label=localization.GetByLabel('UI/Inventory/POSGroupDefenses'), children=defensesData, icon='ui_5_64_13'))
    307             if starbaseData:
    308                 data.append(TreeDataInvFolder(label=localization.GetByLabel('UI/Inventory/StarbaseStructures'), children=starbaseData, icon='res:/ui/Texture/WindowIcons/station.png'))
    309         data.append(TreeDataPlexVault())
    310         return TreeData(children=data)
    311 
    312     def get_active_ship_tree_data(self):
    313         return TreeDataShip(invType=ContainerType.SHIP_CARGO, itemID=session.shipid, typeID=ShipCargo(session.shipid).GetTypeID(), cmdName='OpenCargoHoldOfActiveShip')
    314 
    315     def _HangarHasAssetSafetyWrap(self):
    316         inv = sm.GetService('invCache').GetInventory(const.containerHangar)
    317         if len(inv.List(flag=const.flagAssetSafety)) > 0:
    318             return True
    319         return False
    320 
    321     def GetInvLocationTreeDataTemp(self, rootInvID = None):
    322         data = []
    323         tmpLocations = sm.GetService('inv').GetTemporaryInvLocations().copy()
    324         for invName, itemID in tmpLocations:
    325             if rootInvID in tmpLocations and rootInvID != (invName, itemID):
    326                 continue
    327             if itemID == eveCfg.GetActiveShip():
    328                 sm.GetService('inv').RemoveTemporaryInvLocation((invName, itemID))
    329                 continue
    330             else:
    331                 cls = GetTreeDataClassByInvName(invName)
    332                 data.append(cls(invName, itemID=itemID, isRemovable=True))
    333 
    334         return TreeData(children=data)
    335 
    336     def ChangeTreeUpdatingState(self, isUpdateEnabled):
    337         changed = self.treeUpdatingEnabled != isUpdateEnabled
    338         self.treeUpdatingEnabled = isUpdateEnabled
    339         if changed:
    340             sm.ScatterEvent('OnInvTreeUpdatingChanged', isUpdateEnabled)
    341 
    342     def IsTreeUpdatingEnabled(self):
    343         return self.treeUpdatingEnabled
    344 
    345 
    346 def HasCorpDeliveryRoles():
    347     deliveryRoles = const.corpRoleAccountant | const.corpRoleJuniorAccountant | const.corpRoleTrader
    348     hasCorpDeliveryRoles = session.corprole & deliveryRoles > 0
    349     return hasCorpDeliveryRoles
    350 
    351 
    352 def HasCorpProjectManagerRoles():
    353     deliveryRoles = const.corpRoleDirector | const.corpRoleProjectManager
    354     hasCorpDeliveryRoles = session.corprole & deliveryRoles > 0
    355     return hasCorpDeliveryRoles
    356 
    357 
    358 class CrystalDamageTracker(object):
    359     __notifyevents__ = ['OnItemChange', 'OnCharacterSessionChanged']
    360     NOT_CACHED = -1
    361 
    362     def __init__(self):
    363         self._damageByItemID = ExpiringDict(max_len=5000, max_age_seconds=300)
    364         sm.RegisterNotify(self)
    365 
    366     def PrimeCrystals(self, crystalIDs):
    367         with TempLock('CrystalDamageTracker_PrimeCrystals'):
    368             crystalIDsToPrime = {x for x in crystalIDs if x not in self._damageByItemID}
    369             if not crystalIDsToPrime:
    370                 return
    371             inventoryMgr = eveMoniker.GetInventoryMgr()
    372             dmgByItemID = inventoryMgr.GetDamageForCrystals(crystalIDsToPrime)
    373             self._damageByItemID.update(dmgByItemID)
    374 
    375     def GetCachedCrystalDamage(self, itemID):
    376         return self._damageByItemID.get(itemID, self.NOT_CACHED)
    377 
    378     def GetAllCachedCrystalDamage(self):
    379         return self._damageByItemID
    380 
    381     def OnItemChange(self, item, change, location):
    382         self._damageByItemID.pop(item.itemID, None)
    383 
    384     def OnCharacterSessionChanged(self, oldCharacterID, newCharacterID):
    385         self._damageByItemID.clear()
    386 
    387     def ClearDamageTrackingCache(self):
    388         self._damageByItemID.clear()
    389 
    390     def RemoveDamageTrackingForItemID(self, itemID):
    391         self._damageByItemID.pop(itemID, None)