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

industrySvc.py (16544B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\industry\industrySvc.py
      2 import weakref
      3 from collections import defaultdict
      4 import industry
      5 import uthread
      6 import telemetry
      7 import blue
      8 import gametime
      9 import evetypes
     10 import structures
     11 from carbon.common.script.sys.service import Service
     12 from carbon.common.script.sys.serviceConst import SERVICE_RUNNING
     13 from carbon.common.script.util.format import DateToBlue
     14 from eve.client.script.ui.shared.neocom.wallet import walletUtil
     15 from eve.common.lib import appConst
     16 from eve.common.script.util import industryCommon
     17 from eve.common.script.sys.idCheckers import IsStation, IsStructure, IsTriglavianSystem
     18 from eveaccounting import get_accounting_key_for_name
     19 from eveexceptions import UserError
     20 from evestations.standingsrestriction import get_station_standings_restriction
     21 from uthread2 import call_after_wallclocktime_delay
     22 
     23 class IndustryService(Service):
     24     __guid__ = 'svc.industrySvc'
     25     __servicename__ = 'Industry'
     26     __displayname__ = 'Industry Service'
     27     __dependencies__ = ['blueprintSvc', 'facilitySvc', 'clientPathfinderService']
     28     __notifyevents__ = ['OnIndustryMaterials',
     29      'OnIndustryJob',
     30      'OnSessionChanged',
     31      'OnAccountChange',
     32      'OnSkillsChanged',
     33      'OnSessionReset']
     34 
     35     def __init__(self):
     36         super(IndustryService, self).__init__()
     37         self.monitoring = weakref.ref(set())
     38         self.installed = weakref.WeakValueDictionary()
     39         self._jobs = {}
     40         self._ownerJobs = defaultdict(dict)
     41         self._ownerJobsFetchedCompleted = set()
     42         self.slots = None
     43         self.slotworker = None
     44 
     45     def Run(self, *args, **kwargs):
     46         uthread.new(self._PollJobCompletion)
     47         Service.Run(self, *args, **kwargs)
     48 
     49     def Initialize(self):
     50         self.monitoring = weakref.ref(set())
     51         self.installed = weakref.WeakValueDictionary()
     52         self.slots = None
     53         self.slotworker = None
     54 
     55     def GetJobByID(self, jobID, useCached = False):
     56         if useCached and jobID in self._jobs:
     57             return self._jobs[jobID]
     58         return self._JobInstance(sm.RemoteSvc('industryManager').GetJob(jobID), fetchBlueprint=True)
     59 
     60     @telemetry.ZONE_METHOD
     61     def GetOwnerJobs(self, ownerID, includeCompleted = False, useCached = False):
     62         if useCached and ownerID in self._ownerJobs:
     63             if not includeCompleted:
     64                 return [ job for job in self._ownerJobs[ownerID].values() if job.status < industry.STATUS_COMPLETED ]
     65             if ownerID in self._ownerJobsFetchedCompleted:
     66                 return self._ownerJobs[ownerID].values()
     67         jobs = []
     68         locations = set()
     69         self._ownerJobs[ownerID].clear()
     70         self._ownerJobsFetchedCompleted.discard(ownerID)
     71         for data in sm.RemoteSvc('industryManager').GetJobsByOwner(ownerID, includeCompleted):
     72             job = self._JobInstance(data, fetchBlueprint=False)
     73             self._ownerJobs[ownerID][job.jobID] = job
     74             jobs.append(job)
     75             if not job.completed:
     76                 locations.add(job.facilityID)
     77 
     78         if includeCompleted:
     79             self._ownerJobsFetchedCompleted.add(ownerID)
     80         cfg.evelocations.Prime(locations)
     81         return jobs
     82 
     83     def GetCharacterJobs(self, includeCompleted = False, useCached = False):
     84         return self.GetOwnerJobs(session.charid, includeCompleted, useCached)
     85 
     86     def GetCorporationJobs(self, includeCompleted = False, useCached = False):
     87         return self.GetOwnerJobs(session.corpid, includeCompleted, useCached)
     88 
     89     @telemetry.ZONE_METHOD
     90     def CreateJob(self, blueprint, activityID, facilityID, runs = 1):
     91         job = industry.Job(blueprint, activityID)
     92         job.runs = runs
     93         job.status = industry.STATUS_UNSUBMITTED
     94         job.extras = industryCommon.GetOptionalMaterials(job)
     95         job.prices = industryCommon.JobPrices(activityID)
     96         industryCommon.AttachSessionToJob(job, session)
     97         self._UpdateSkills(job)
     98         self._UpdateSlots()
     99         self._UpdateModifiers(job)
    100         self._UpdateDistance(job)
    101         self._UpdateAccounts(job)
    102         job.on_facility.connect(self.LoadLocations)
    103         job.on_delete.connect(self.DisconnectJob)
    104         job.on_input_location.connect(self.ConnectJob)
    105         job.on_validate.connect(self._ValidateJob)
    106         self._ApplyJobSettings(job)
    107         job.facility = self.facilitySvc.GetFreshFacility(facilityID)
    108         return job
    109 
    110     def RecreateJob(self, existing):
    111         try:
    112             blueprint = self.blueprintSvc.GetBlueprintItem(existing.blueprintID)
    113             if blueprint.ownerID != existing.ownerID:
    114                 raise UserError
    115         except UserError:
    116             blueprint = self.blueprintSvc.GetBlueprintType(existing.blueprintTypeID, not existing.blueprint.original)
    117 
    118         job = self.CreateJob(blueprint, existing.activityID, existing.facilityID)
    119         job.outputLocation = industryCommon.MatchLocation(job, existing.outputLocationID, existing.outputFlagID, checkCapacity=True)
    120         job.productTypeID = existing.productTypeID
    121         job.runs = existing.runs
    122         job.licensedRuns = existing.licensedRuns
    123         for material in job.optional_materials:
    124             material.select(None)
    125             if getattr(existing, 'optionalTypeID', None) in material.all_types():
    126                 material.select(existing.optionalTypeID)
    127             if getattr(existing, 'optionalTypeID2', None) in material.all_types():
    128                 material.select(existing.optionalTypeID2)
    129 
    130         return job
    131 
    132     def JobDataWithBlueprint(self, existing):
    133         job = self._JobInstance(existing.data, fetchBlueprint=True)
    134         job.extras = industryCommon.GetOptionalMaterials(job)
    135         return job
    136 
    137     def InstallJob(self, job):
    138         try:
    139             return sm.RemoteSvc('industryManager').InstallJob(job.dump())
    140         except UserError:
    141             self.facilitySvc.Reload(job.facilityID)
    142             raise
    143 
    144     @staticmethod
    145     def CompleteJob(jobID, solarSystemID):
    146         sm.RemoteSvc('industryManager').CompleteJob(int(jobID), solarSystemID)
    147 
    148     @staticmethod
    149     def CompleteJobs(jobs):
    150         sm.RemoteSvc('industryManager').CompleteManyJobs([ (jobID, solarSystemID) for jobID, solarSystemID in jobs ])
    151 
    152     @staticmethod
    153     def CancelJob(jobID, solarSystemID):
    154         sm.RemoteSvc('industryManager').CancelJob(int(jobID), solarSystemID)
    155 
    156     def _ValidateJob(self, job):
    157         if not job.facility:
    158             return
    159         if IsStation(job.facility.facilityID):
    160             standing_restrictions = get_station_standings_restriction(appConst.stationServiceFactory, job.facility.facilityID, job.ownerID)
    161             if standing_restrictions:
    162                 job.add_error(industry.Error.STANDINGS_RESTRICTION, standing_restrictions)
    163         if IsStructure(evetypes.GetCategoryID(job.facility.typeID)) and IsTriglavianSystem(job.facility.solarSystemID):
    164             if job.structure_service_id in (structures.SERVICE_MANUFACTURING_CAPITAL, structures.SERVICE_MANUFACTURING_SUPERCAPITAL):
    165                 job.add_error(industry.Error.FACILITY_TYPE, job.blueprint.blueprintTypeID)
    166 
    167     def _PollJobCompletion(self):
    168         while self.state == SERVICE_RUNNING:
    169             uthread.new(self._PollJobCompletionThreaded)
    170             blue.pyos.synchro.SleepWallclock(1000)
    171 
    172     def _PollJobCompletionThreaded(self):
    173         for job in self.installed.values():
    174             if job.status == industry.STATUS_INSTALLED and DateToBlue(job.endDate) < blue.os.GetWallclockTime():
    175                 job.status = industry.STATUS_READY
    176                 sm.ScatterEvent('OnIndustryJob', job.jobID, job.ownerID, job.blueprintID, job.installerID, job.status, None)
    177 
    178     def _JobInstance(self, data, fetchBlueprint = False):
    179         if fetchBlueprint:
    180             blueprint = self.blueprintSvc.GetBlueprint(data.blueprintID, data.blueprintTypeID)
    181         else:
    182             blueprint = self.blueprintSvc.GetBlueprintType(data.blueprintTypeID, data.blueprintCopy)
    183         job = industryCommon.JobData(data, blueprint)
    184         if data.facilityID:
    185             job.facility = self.facilitySvc.GetFreshFacility(data.facilityID)
    186         self._UpdateSkills(job)
    187         self._UpdateSlots()
    188         self._UpdateModifiers(job)
    189         self._UpdateDistance(job)
    190         if job.status == industry.STATUS_INSTALLED:
    191             self.installed[job.jobID] = job
    192         else:
    193             self.installed.pop(job.jobID, None)
    194         self._jobs[job.jobID] = job
    195         return job
    196 
    197     @telemetry.ZONE_METHOD
    198     def _UpdateModifiers(self, job):
    199         if job:
    200             job.modifiers = industryCommon.GetJobModifiers(job, session.charid)
    201 
    202     @telemetry.ZONE_METHOD
    203     def _UpdateDistance(self, job):
    204         if job and isinstance(job, industry.JobData):
    205             job._distance = self.clientPathfinderService.GetJumpCountFromCurrent(job.solarSystemID)
    206 
    207     @telemetry.ZONE_METHOD
    208     def _UpdateSkills(self, job):
    209         if job:
    210             skills = {}
    211             mySkills = sm.GetService('skills').GetSkill
    212             for typeID in [ skill.typeID for skill in job.all_skills ]:
    213                 skillInfo = mySkills(typeID)
    214                 skills[typeID] = skillInfo.effectiveSkillLevel if skillInfo else 0
    215 
    216             job.skills = skills
    217 
    218     def _UpdateSlots(self):
    219         callinterval = 3
    220         callthread = self.slotworker
    221         startTime = getattr(callthread.tasklet, 'startTime', gametime.GetWallclockTimeNow()) if callthread else gametime.GetWallclockTimeNow()
    222         delay = 1 if callthread is None else callinterval
    223         if callthread is None or not callthread.IsAlive() or gametime.GetSecondsSinceWallclockTime(startTime) > delay:
    224             self.slotworker = call_after_wallclocktime_delay(self._UpdateSlots_thread, delay)
    225 
    226     def _UpdateSlots_thread(self):
    227         self.slots = sm.RemoteSvc('industryManager').GetJobCounts(session.charid)
    228         self.slotworker = None
    229         sm.ScatterEvent('OnIndustrySlotsUpdated')
    230 
    231     @telemetry.ZONE_METHOD
    232     def _UpdateAccounts(self, job, ownerID = None, account = None, balance = None):
    233         if job:
    234             accounts = {(session.charid, appConst.accountingKeyCash): sm.GetService('wallet').GetWealth()}
    235             if session.corpAccountKey and walletUtil.HaveAccessToCorpWalletDivision(session.corpAccountKey):
    236                 if ownerID and account and balance and session.corpid == ownerID and session.corpAccountKey == account:
    237                     accounts[session.corpid, session.corpAccountKey] = balance
    238                 else:
    239                     accounts[session.corpid, session.corpAccountKey] = sm.GetService('wallet').GetCorpWealthCached1Min(session.corpAccountKey)
    240             job.accounts = accounts
    241             if job.account not in job.accounts:
    242                 for accountOwner, accountKey in job.accounts.keys():
    243                     if job.ownerID == accountOwner:
    244                         job.account = (accountOwner, accountKey)
    245                         return
    246 
    247                 job.account = job.accounts.keys()[0]
    248 
    249     def OnSessionChanged(self, isRemote, session, change):
    250         industryCommon.AttachSessionToJob(self.monitoring(), session)
    251         if 'corpAccountKey' in change:
    252             self._UpdateAccounts(self.monitoring())
    253         if 'corprole' in change:
    254             self._UpdateAccounts(self.monitoring())
    255             self.LoadLocations(self.monitoring())
    256 
    257     def OnSessionReset(self):
    258         self.Initialize()
    259 
    260     def OnAccountChange(self, accountKey, ownerID, balance):
    261         keyID = get_accounting_key_for_name(accountKey)
    262         self._UpdateAccounts(self.monitoring(), ownerID, keyID, balance)
    263 
    264     def OnSkillsChanged(self, *args):
    265         self._UpdateSkills(self.monitoring())
    266         self._UpdateModifiers(self.monitoring())
    267 
    268     @telemetry.ZONE_METHOD
    269     def OnIndustryJob(self, jobID, ownerID, blueprintID, installerID, status, successfulRuns):
    270         if installerID == session.charid:
    271             self._UpdateSlots()
    272         if jobID in self._jobs:
    273             self._jobs[jobID].status = status
    274             self._jobs[jobID].successfulRuns = successfulRuns
    275         if ownerID in self._ownerJobs and jobID not in self._ownerJobs[ownerID]:
    276             self._ownerJobs.pop(ownerID)
    277             self._ownerJobsFetchedCompleted.discard(ownerID)
    278         sm.ScatterEvent('OnIndustryJobClient', jobID=jobID, ownerID=ownerID, blueprintID=blueprintID, installerID=installerID, status=status, successfulRuns=successfulRuns)
    279 
    280     @telemetry.ZONE_METHOD
    281     def ConnectJob(self, job):
    282         self.monitoring = weakref.ref(job)
    283         job.monitorID, job.available = sm.RemoteSvc('industryMonitor').ConnectJob(job.dump())
    284 
    285     @telemetry.ZONE_METHOD
    286     def DisconnectJob(self, job):
    287         if job is None or self.monitoring() == job:
    288             sm.RemoteSvc('industryMonitor').DisconnectJob(job.monitorID if job else None)
    289 
    290     @telemetry.ZONE_METHOD
    291     def LoadLocations(self, job):
    292         if job:
    293             job.locations = self.facilitySvc.GetFacilityLocations(job.facilityID, job.ownerID)
    294             if len(job.locations):
    295                 self._ApplyJobSettings(job)
    296 
    297     def OnIndustryMaterials(self, jobID, materials):
    298         job = self.monitoring()
    299         if job and hasattr(job, 'monitorID') and job.monitorID == jobID:
    300             job.available = materials
    301 
    302     def _UpdateJobSettings(self, job):
    303         settings.char.ui.Set('industry_b:%s_a:%s_runs' % (job.blueprint.blueprintTypeID, job.activityID), job.runs)
    304         settings.char.ui.Set('industry_b:%s_a:%s_productTypeID' % (job.blueprint.blueprintTypeID, job.activityID), job.productTypeID)
    305         settings.char.ui.Set('industry_b:%s_a:%s_licensedRuns' % (job.blueprint.blueprintTypeID, job.activityID), job.licensedRuns)
    306         settings.char.ui.Set('industry_account:%s' % (job.ownerID,), job.account)
    307         if job.inputLocation is not None and job.facility is not None:
    308             settings.char.ui.Set('industry_b:%s_a:%s_f:%s_input' % (job.blueprint.blueprintTypeID, job.activityID, job.facility.facilityID), (job.inputLocation.itemID, job.inputLocation.flagID))
    309         if job.outputLocation is not None and job.facility is not None:
    310             settings.char.ui.Set('industry_b:%s_a:%s_f:%s_output' % (job.blueprint.blueprintTypeID, job.activityID, job.facility.facilityID), (job.outputLocation.itemID, job.outputLocation.flagID))
    311         if len(job.optional_materials):
    312             settings.char.ui.Set('industry_b:%s_a:%s_materials' % (job.blueprint.blueprintTypeID, job.activityID), list([ material.typeID for material in job.optional_materials if material.typeID ]))
    313 
    314     def _ApplyJobSettings(self, job):
    315         job.runs = settings.char.ui.Get('industry_b:%s_a:%s_runs' % (job.blueprint.blueprintTypeID, job.activityID), 1)
    316         job.productTypeID = settings.char.ui.Get('industry_b:%s_a:%s_productTypeID' % (job.blueprint.blueprintTypeID, job.activityID), None)
    317         job.licensedRuns = min(job.maxLicensedRuns, settings.char.ui.Get('industry_b:%s_a:%s_licensedRuns' % (job.blueprint.blueprintTypeID, job.activityID), job.maxLicensedRuns))
    318         account = settings.char.ui.Get('industry_account:%s' % (job.ownerID,), job.account)
    319         job.account = account if account in job.accounts else job.account
    320         if job.facility:
    321             inputLocation = industryCommon.MatchLocation(job, *settings.char.ui.Get('industry_b:%s_a:%s_f:%s_input' % (job.blueprint.blueprintTypeID, job.activityID, job.facility.facilityID), (job.blueprint.location.itemID, job.blueprint.location.flagID)))
    322             if inputLocation != job.inputLocation:
    323                 job.inputLocation = inputLocation
    324             outputLocation = industryCommon.MatchLocation(job, checkCapacity=True, *settings.char.ui.Get('industry_b:%s_a:%s_f:%s_output' % (job.blueprint.blueprintTypeID, job.activityID, job.facility.facilityID), (job.blueprint.location.itemID, job.blueprint.location.flagID)))
    325             if outputLocation != job.outputLocation:
    326                 job.outputLocation = outputLocation
    327         materials = settings.char.ui.Get('industry_b:%s_a:%s_materials' % (job.blueprint.blueprintTypeID, job.activityID), [])
    328         for material in job.materials:
    329             for option in material.options:
    330                 if option.typeID in materials:
    331                     material.select(option)
    332 
    333         job.on_updated.connect(self._UpdateJobSettings)
    334 
    335     def GetJobCountForActivity(self, activityID):
    336         if self.slots is None:
    337             self._UpdateSlots()
    338             return
    339         return self.slots.get(activityID, 0)