svc_invtools.py (23451B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\svc_invtools.py 2 from __future__ import print_function 3 import random 4 import types 5 import time 6 import blue 7 import evetypes 8 from carbon.common.script.sys.service import Service 9 from carbon.common.script.sys.serviceConst import ROLE_GML, ROLE_WORLDMOD, SERVICE_RUNNING, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_STOPPED 10 from carbonui import uiconst 11 from carbonui.control.combo import Combo 12 from carbonui.control.singlelineedits.singleLineEditFloat import SingleLineEditFloat 13 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger 14 from carbonui.primitives.container import Container 15 from carbonui.primitives.frame import Frame 16 from carbonui.primitives.line import Line 17 from eve.client.script.ui.control import eveLabel 18 from carbonui.button.group import ButtonGroup 19 from carbonui.control.checkbox import Checkbox 20 from carbonui.control.radioButton import RadioButton 21 from carbonui.control.window import Window 22 from eve.client.script.ui.util import uix 23 from eve.common.lib import appConst as const 24 from eve.common.script.sys import eveCfg 25 from player_messaging.client.ui_message import prompt_player_fsd_dialog 26 MAX_VOLUME = 1000000000 27 Progress = lambda title, text, current, total: sm.GetService('loading').ProgressWnd(title, text, current, total) 28 29 class InvTools(Service): 30 __module__ = __name__ 31 __exportedcalls__ = {} 32 __notifyevents__ = [] 33 __dependencies__ = [] 34 __servicename__ = 'invtools' 35 __displayname__ = 'invtools' 36 __guid__ = 'svc.invtools' 37 38 def __init__(self): 39 Service.__init__(self) 40 self.inv = sm.GetService('invCache').GetInventory 41 42 def Run(self, memStream = None): 43 self.state = SERVICE_START_PENDING 44 Service.Run(self, memStream) 45 self.wnd = None 46 self.state = SERVICE_RUNNING 47 48 def Stop(self, memStream = None): 49 self.state = SERVICE_STOP_PENDING 50 Service.Stop(self, memStream) 51 self.state = SERVICE_STOPPED 52 53 def Startup(self, *args): 54 pass 55 56 def GetShip(self, *args): 57 if not hasattr(session, 'shipid'): 58 return 59 return self.inv(session.shipid) 60 61 def GetSelf(self, *args): 62 if not hasattr(session, 'charid'): 63 return 64 return self.inv(session.charid) 65 66 def GetSpecial(self, loc = None, *args): 67 if loc is None: 68 return 69 return self.inv(loc) 70 71 def IsValid(self, typeID, onlyPublished, onlyUnpublished, volume): 72 if evetypes.GetVolume(typeID) > volume: 73 return False 74 if onlyPublished and not evetypes.IsPublished(typeID): 75 return False 76 if onlyUnpublished and evetypes.IsPublished(typeID): 77 return False 78 if evetypes.IsDynamicType(typeID): 79 return False 80 return True 81 82 def SpawnRandom(self, loc = None, flag = None, minQty = None, maxQty = None, iterations = None, categories = True, groups = False, want = None, onlyPublished = True, onlyUnpublished = False, useMultiMove = True, volume = None, *args): 83 if loc is None: 84 return 85 if loc == eveCfg.GetActiveShip() and sm.GetService('clientDogmaIM').GetDogmaLocation().GetItem(loc).groupID == const.groupCapsule: 86 prompt_player_fsd_dialog('CustomNotify', {'notify': 'You cannot spawn items into capsule.'}) 87 return 88 if minQty is None: 89 minQty = 1 90 if maxQty is None: 91 maxQty = 10 92 if iterations is None: 93 iterations = 10 94 if minQty > maxQty: 95 prompt_player_fsd_dialog('CustomNotify', {'notify': 'Min stack size is greater than max stack size.'}) 96 return 97 if minQty > iterations: 98 prompt_player_fsd_dialog('CustomNotify', {'notify': 'Min stack size is greater than number of items.'}) 99 return 100 if want is None: 101 categories = True 102 want = [const.categoryCharge, 103 const.categoryCommodity, 104 const.categoryModule, 105 const.categoryStructureModule] 106 if type(want) is types.IntType: 107 want = [want] 108 if type(want) is not types.ListType: 109 return 110 selectedTypesAndQty = [] 111 mustMove = [] 112 possibleTypes = [] 113 if categories: 114 for typeID in evetypes.GetTypeIDsByCategories(want): 115 if self.IsValid(typeID, onlyPublished, onlyUnpublished, volume): 116 possibleTypes.append(typeID) 117 118 else: 119 for typeID in evetypes.GetTypeIDsByGroups(want): 120 if self.IsValid(typeID, onlyPublished, onlyUnpublished, volume): 121 possibleTypes.append(typeID) 122 123 if len(possibleTypes) == 0: 124 return 125 for i in xrange(1, iterations + 1): 126 selectedTypesAndQty.append((random.choice(possibleTypes), random.choice(xrange(minQty, maxQty + 1)))) 127 128 title = 'Spawning...' 129 idx = int() 130 qty = len(selectedTypesAndQty) 131 Progress(title, 'Processing', 0, qty) 132 for t, q in selectedTypesAndQty: 133 idx += 1 134 msg = 'Processing %d of %d' % (idx, qty) 135 Progress(title, msg, idx, qty) 136 if q == 0: 137 q = 1 138 try: 139 if session.role & ROLE_WORLDMOD: 140 itemID = sm.RemoteSvc('slash').SlashCmd('/createitem %d %d' % (t, q)) 141 elif session.role & ROLE_GML: 142 itemID = sm.RemoteSvc('slash').SlashCmd('/load me %d %d' % (t, q))[0] 143 else: 144 return 145 mustMove.append(itemID) 146 except Exception: 147 self.LogException('Failed to spawn item with typeID %s' % t) 148 149 Progress(title, 'Complete', 1, 1) 150 if session.stationid is not None and loc != const.containerHangar: 151 self.MoveItems(items=mustMove, source=session.locationid, destination=loc, flag=flag, multi=useMultiMove, suppress=True) 152 153 def GetFlags(self, *args): 154 flagList = [] 155 for key in const.__dict__: 156 if key.lower().startswith('flag'): 157 flagList.append(key) 158 159 flagList.sort() 160 return flagList 161 162 def GetItemFromContainer(self, itemID = None, container = None, *args): 163 if itemID is None: 164 return 165 if container is None: 166 return 167 inv = [ item for item in self.inv(container).List() ] 168 for item in inv: 169 if item.itemID == itemID: 170 return item 171 172 def MoveItems(self, items = None, source = None, destination = None, flag = None, multi = True, suppress = False, **kwargs): 173 if None in (items, source, destination): 174 return 175 if not suppress: 176 destinationCargo = {} 177 postDestinationCargo = {} 178 sourceCargo = {} 179 postSourceCargo = {} 180 cargoDict = {} 181 for rec in self.inv(destination).List(): 182 destinationCargo[rec.itemID] = rec.stacksize 183 184 for rec in self.inv(source).List(): 185 sourceCargo[rec.itemID] = rec.stacksize 186 187 for each in items: 188 cargoDict[each] = self.GetItemFromContainer(each, source).stacksize 189 190 title = 'Moving...' 191 Progress(title, 'Telling Scotty the docking manager what to do...', 0, 1) 192 if multi: 193 if flag is not None: 194 self.inv(destination).MultiAdd(items, source, flag=flag, **kwargs) 195 else: 196 self.inv(destination).MultiAdd(items, source, **kwargs) 197 else: 198 idx = int() 199 all = len(items) 200 for itemID in items: 201 idx += 1 202 msg = 'Processing %d of %d' % (idx, all) 203 Progress(title, msg, idx, all) 204 self.inv(destination).Add(itemID, source, **kwargs) 205 206 Progress(title, "All done, don't forget to pay the man!", 1, 1) 207 if not suppress: 208 blue.pyos.synchro.SleepWallclock(5000) 209 for rec in self.inv(destination).List(): 210 postDestinationCargo[rec.itemID] = rec.stacksize 211 212 for rec in self.inv(source).List(): 213 postSourceCargo[rec.itemID] = rec.stacksize 214 215 for k in cargoDict.iterkeys(): 216 if k in postDestinationCargo: 217 qtyCargo = cargoDict[k] 218 qtyDest = postDestinationCargo[k] 219 if qtyCargo == qtyDest: 220 pass 221 else: 222 print(k, ' is in destination and qty is different!') 223 else: 224 print('\t%s is not in destination', k) 225 print('\tsrc inv rec', self.GetItemFromContainer(k, source)) 226 print('\tdest inv rec:', self.GetItemFromContainer(k, destination)) 227 228 postSourceCopy = postSourceCargo.copy() 229 postSourceCopy.update(cargoDict) 230 print('\tsrc ok?\t', ['No', 'Yes'][postSourceCopy == sourceCargo]) 231 destinationCopy = destinationCargo.copy() 232 destinationCopy.update(cargoDict) 233 print('\tdest ok?\t', ['No', 'Yes'][destinationCopy == postDestinationCargo]) 234 235 def InvMoveLoop(self, loop = None, noSpawn = True, *args): 236 if loop is None: 237 loop = 10 238 _loop = loop 239 if not noSpawn: 240 self.SpawnRandom(session.shipid, iterations=10) 241 items = self.GetShip().ListCargo() 242 if len(items) == 0: 243 return 244 itemIDs = [ rec.itemID for rec in items ] 245 moveCommands = [(session.shipid, 246 const.containerHangar, 247 const.flagHangar, 248 False), 249 (const.containerHangar, 250 session.shipid, 251 const.flagCargo, 252 False), 253 (session.shipid, 254 const.containerHangar, 255 const.flagHangar, 256 True), 257 (const.containerHangar, 258 session.shipid, 259 const.flagCargo, 260 True)] 261 lookup = {eveCfg.GetActiveShip(): 'Ship', 262 const.containerHangar: 'Hangar Floor', 263 const.flagCargo: 'Cargo Bay', 264 const.flagCorpSAG1: 'Corp Hangar, Division 1', 265 const.flagCorpSAG2: 'Corp Hangar, Division 2', 266 const.flagCorpSAG3: 'Corp Hangar, Division 3', 267 const.flagCorpSAG4: 'Corp Hangar, Division 4', 268 const.flagCorpSAG5: 'Corp Hangar, Division 5', 269 const.flagCorpSAG6: 'Corp Hangar, Division 6', 270 const.flagCorpSAG7: 'Corp Hangar, Division 7', 271 const.flagCorpGoalDeliveries: 'Corp Hangar, Goal Deliveries'} 272 usecase = {True: 'multimove', 273 False: 'single move'} 274 office = sm.StartService('officeManager').GetCorpOfficeAtLocation() 275 if office is not None: 276 office = office.officeID 277 moveCommands.extend([(session.shipid, 278 office, 279 const.flagCorpSAG1, 280 False), 281 (office, 282 office, 283 const.flagCorpSAG2, 284 False), 285 (office, 286 office, 287 const.flagCorpSAG3, 288 False), 289 (office, 290 office, 291 const.flagCorpSAG4, 292 False), 293 (office, 294 office, 295 const.flagCorpSAG5, 296 False), 297 (office, 298 office, 299 const.flagCorpSAG6, 300 False), 301 (office, 302 office, 303 const.flagCorpSAG7, 304 False), 305 (office, 306 office, 307 const.flagCorpGoalDeliveries, 308 False), 309 (office, 310 session.shipid, 311 const.flagCargo, 312 False), 313 (session.shipid, 314 office, 315 const.flagCorpSAG1, 316 True), 317 (office, 318 office, 319 const.flagCorpSAG2, 320 True), 321 (office, 322 office, 323 const.flagCorpSAG3, 324 True), 325 (office, 326 office, 327 const.flagCorpSAG4, 328 True), 329 (office, 330 office, 331 const.flagCorpSAG5, 332 True), 333 (office, 334 office, 335 const.flagCorpSAG6, 336 True), 337 (office, 338 office, 339 const.flagCorpSAG7, 340 True), 341 (office, 342 office, 343 const.flagCorpGoalDeliveries, 344 True), 345 (office, 346 session.shipid, 347 const.flagCargo, 348 True)]) 349 lookup[office] = 'Office' 350 start = time.time() 351 while loop: 352 for src, dest, f, m in moveCommands: 353 if f is not None: 354 print("Moving from %s to %s's %s using %s:" % (lookup[src], 355 lookup[dest], 356 lookup[f], 357 usecase[m])) 358 else: 359 print('Moving from %s to %s using %s:' % (lookup[src], lookup[dest], usecase[m])) 360 t = time.time() 361 self.MoveItems(itemIDs, src, dest, f, m) 362 duration = time.time() - t 363 print('Move took %d:%.2d to move %s items\n' % (duration / 60, duration % 60, len(items))) 364 365 loop -= 1 366 367 finish = time.time() - start 368 print('Move test complete, total time: %d:%.2d for %s iteration(s)' % (finish / 60, finish % 60, _loop)) 369 print('%s items were moved %s times' % (len(items), len(moveCommands) * _loop)) 370 371 372 class InvToolsWnd(Window): 373 __guid__ = 'form.invTools' 374 __notifyevents__ = ['OnSessionChanged'] 375 RANDOM_COMBO = ('Random', 'random') 376 PUBLISHED_LIST = ['<color=0xffff0000>Not Published<color=0xffffffff>', '<color=0xff00ff00>Published<color=0xffffffff>'] 377 default_windowID = 'InvTools' 378 379 def ApplyAttributes(self, attributes): 380 Window.ApplyAttributes(self, attributes) 381 self.SetCaption('Inventory Tools') 382 self.MakeUnResizeable() 383 self.cats = None 384 self.grps = None 385 self.locs = None 386 self.DoSetup() 387 self.Begin() 388 389 def OnSessionChanged(self, isRemote, session, change): 390 self.location.entries = self.locs = self.GetLocations() 391 self.location.SetValue('ship') 392 393 def DoSetup(self): 394 self.cats = [ ('%s [%s]' % (evetypes.GetCategoryNameByCategory(categoryID), self.PUBLISHED_LIST[evetypes.IsCategoryPublishedByCategory(categoryID)]), categoryID) for categoryID in evetypes.IterateCategories() if categoryID is not 0 ] 395 self.cats.sort() 396 self.cats.insert(0, self.RANDOM_COMBO) 397 self.grps = [self.RANDOM_COMBO] 398 self.default = {'categoryID': 'random', 399 'groupID': 'random', 400 'published': 1, 401 'multimove': 1, 402 'minQty': 1, 403 'maxQty': 10, 404 'number': 10, 405 'volume': MAX_VOLUME, 406 'location': 'ship'} 407 self.config = self.default.copy() 408 self.locs = self.GetLocations() 409 self.spawnlocation = (eveCfg.GetActiveShip(), const.flagCargo) 410 self.prepCorpHangar = False 411 412 def Begin(self): 413 margin = const.defaultPadding 414 self.main = main = Container(name='main', parent=self.sr.main, padding=margin) 415 Frame(parent=main, color=(1.0, 1.0, 1.0, 0.2)) 416 uix.GetContainerHeader('Random Item Spawn', main, bothlines=0) 417 COMBOPADDING = 120 418 self.categories = Combo(parent=main, options=self.cats, select=self.config['categoryID'], name='category', align=uiconst.TOTOP, callback=self.DoCategoryChange, padding=(COMBOPADDING, 419 margin, 420 margin, 421 0)) 422 txt = eveLabel.Label(text='Category', parent=self.categories, align=uiconst.CENTERLEFT, left=-COMBOPADDING + margin, fontsize=10) 423 self.groups = Combo(parent=main, options=self.grps, select=self.config['groupID'], name='group', align=uiconst.TOTOP, callback=self.DoGroupChange, padding=(COMBOPADDING, 424 margin, 425 margin, 426 0)) 427 txt = eveLabel.Label(text='Group', parent=self.groups, align=uiconst.CENTERLEFT, left=-COMBOPADDING + margin, fontsize=10) 428 self.pub = RadioButton(text='use only published items', parent=main, settingsKey='pub', retval=0, checked=bool(self.config['published'] == 1), groupname='published', align=uiconst.TOTOP, callback=self.DoPublishChange, padding=(margin, 429 0, 430 margin, 431 0)) 432 Line(parent=main, align=uiconst.TOTOP) 433 self.unpub = RadioButton(text='use only unpublished items', parent=main, settingsKey='unpub', retval=0, checked=bool(self.config['published'] == 0), groupname='published', align=uiconst.TOTOP, callback=self.DoPublishChange, padding=(margin, 434 0, 435 margin, 436 0)) 437 self.all = RadioButton(text='use all items', parent=main, settingsKey='all', retval=0, checked=bool(self.config['published'] == -1), groupname='published', align=uiconst.TOTOP, callback=self.DoPublishChange, padding=(margin, 438 0, 439 margin, 440 0)) 441 Line(parent=main, align=uiconst.TOTOP) 442 self.multimove = Checkbox(text='use multimove', parent=main, settingsKey='multimove', checked=self.config['multimove'], groupname=None, align=uiconst.TOTOP, callback=self.DoMoveChange, padding=(margin, 443 0, 444 margin, 445 0)) 446 Line(parent=main, align=uiconst.TOTOP) 447 for checkbox in [self.pub, 448 self.unpub, 449 self.all, 450 self.multimove]: 451 checkbox.hint = 'Changing this is <color=0xffff0000>not advised<color=0xffffffff>.<br>Proceed with caution!' 452 453 INPUTPADDING = 140 454 self.min = SingleLineEditInteger(name='minQty', setvalue=str(self.config['minQty']), minValue=1, maxValue=100, parent=main, align=uiconst.TOTOP, padding=(INPUTPADDING, 455 margin, 456 margin, 457 0)) 458 txt = eveLabel.Label(text='Minimum stack size', parent=self.min, align=uiconst.CENTERLEFT, left=-INPUTPADDING + margin, fontsize=10) 459 self.max = SingleLineEditInteger(name='maxQty', setvalue=str(self.config['maxQty']), minValue=1, maxValue=100, parent=main, align=uiconst.TOTOP, padding=(INPUTPADDING, 460 margin, 461 margin, 462 0)) 463 txt = eveLabel.Label(text='Maximum stack size', parent=self.max, align=uiconst.CENTERLEFT, left=-INPUTPADDING + margin, fontsize=10) 464 self.number = SingleLineEditInteger(name='number', setvalue=str(self.config['number']), minValue=1, maxValue=1000, parent=main, align=uiconst.TOTOP, padding=(INPUTPADDING, 465 margin, 466 margin, 467 0)) 468 txt = eveLabel.Label(text='Number of Items', parent=self.number, align=uiconst.CENTERLEFT, left=-INPUTPADDING + margin, fontsize=10) 469 self.volume = SingleLineEditFloat(name='volume', setvalue=str(self.config['volume']), maxValue=MAX_VOLUME, parent=main, align=uiconst.TOTOP, padding=(INPUTPADDING, 470 margin, 471 margin, 472 0)) 473 txt = eveLabel.Label(text='Maximum item volume', parent=self.volume, align=uiconst.CENTERLEFT, left=-INPUTPADDING + margin, fontsize=10) 474 self.location = Combo(parent=main, options=self.locs, select=self.config['location'], name='location', align=uiconst.TOTOP, callback=self.DoLocationChange, padding=(COMBOPADDING, 475 margin, 476 margin, 477 0)) 478 txt = eveLabel.Label(text='Location', parent=self.location, align=uiconst.CENTERLEFT, left=-COMBOPADDING + margin, fontsize=10) 479 buttons = [['Spawn', 480 self.DoFormSubmit, 481 None, 482 81]] 483 btns = ButtonGroup(btns=buttons, line=1, parent=main, padTop=margin) 484 btn = btns.GetChild('Spawn_Btn') 485 if session.role & ROLE_WORLDMOD: 486 btn.hint = 'Spawn using /createitem...' 487 elif session.role & ROLE_GML: 488 btn.hint = 'Spawn using /load...' 489 else: 490 btn.hint = 'No <b>WORLDMOD</b> or <b>GML</b> role present.' 491 content_height = sum((each.height + each.padTop + each.padBottom + each.top for each in main.children)) + 16 492 _, window_height = self.GetWindowSizeForContentSize(height=content_height) 493 self.height = window_height 494 495 def DoCategoryChange(self, combo, text, value): 496 self.config['categoryID'] = value 497 if value == 'random': 498 self.grps = [self.RANDOM_COMBO] 499 else: 500 self.grps = [ ('%s [%s]' % (evetypes.GetGroupNameByGroup(groupID), self.PUBLISHED_LIST[evetypes.IsGroupPublishedByGroup(groupID)]), groupID) for groupID in evetypes.IterateGroups() if evetypes.GetCategoryIDByGroup(groupID) == value ] 501 self.grps.sort() 502 self.grps.insert(0, self.RANDOM_COMBO) 503 self.groups.SetValue('random') 504 self.config['groupID'] = 'random' 505 self.groups.entries = self.grps 506 507 def DoGroupChange(self, combo, text, value): 508 self.config['groupID'] = value 509 510 def DoLocationChange(self, combo, text, value): 511 self.prepCorpHangar = False 512 self.config['location'] = value 513 if value == 'ship': 514 self.spawnlocation = (eveCfg.GetActiveShip(), const.flagCargo) 515 elif value == 'hangar': 516 self.spawnlocation = const.containerHangar 517 518 def DoPublishChange(self, cb): 519 lookup = {'pub': 1, 520 'unpub': 0, 521 'all': -1} 522 self.config['published'] = lookup[cb.name] 523 524 def DoMoveChange(self, cb): 525 self.config['multimove'] = cb.GetValue() 526 527 def DoFormSubmit(self, *args): 528 for formField in [self.min, 529 self.max, 530 self.number, 531 self.volume]: 532 configFieldName = formField.name 533 self.config[configFieldName] = formField.GetValue() 534 535 loc = self.spawnlocation 536 if isinstance(loc, types.TupleType): 537 loc, flag = loc 538 else: 539 flag = None 540 if self.config['categoryID'] == 'random': 541 ids = set([ evetypes.GetCategoryIDByGroup(groupID) for groupID in evetypes.IterateGroups() ]) 542 want = list(ids) 543 categories = True 544 groups = False 545 elif self.config['groupID'] == 'random': 546 ids = evetypes.GetGroupIDsByCategory(self.config['categoryID']) 547 want = list(ids) 548 categories = False 549 groups = True 550 else: 551 want = [self.config['groupID']] 552 categories = False 553 groups = True 554 publishedFlags = {0: (False, True), 555 1: (True, False), 556 -1: (False, False)} 557 onlyPublished, onlyUnpublished = publishedFlags[self.config['published']] 558 if self.prepCorpHangar is True: 559 sm.GetService('window').OpenCorpHangar(None, None, 1) 560 sm.StartService('invtools').SpawnRandom(loc=loc, flag=flag, minQty=self.config['minQty'], maxQty=self.config['maxQty'], iterations=self.config['number'], categories=categories, groups=groups, want=want, onlyPublished=onlyPublished, onlyUnpublished=onlyUnpublished, useMultiMove=self.config['multimove'], volume=self.config['volume']) 561 562 def GetLocations(self): 563 ret = [('Current Ship', 'ship')] 564 if session.stationid or session.structureid: 565 ret.append(('Hangar Floor', 'hangar')) 566 return ret