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)