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

shipSkinComponentSvc.py (16835B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\cosmetics\client\shipSkinComponentSvc.py
      2 import logging
      3 import random
      4 import evetypes
      5 import uthread
      6 import uthread2
      7 from carbon.common.script.sys.serviceConst import ROLE_PROGRAMMER
      8 from cosmetics.client.messengers.cosmetics.ship.shipSkinComponentLicenseNoticeMessenger import PublicShipComponentLicenseNoticeMessenger
      9 from cosmetics.client.messengers.cosmetics.ship.shipSkinComponentLicenseRequestMessenger import PublicShipComponentLicenseRequestMessenger
     10 from cosmetics.client.ships import ship_skin_signals, ship_skin_svc_signals
     11 from cosmetics.client.ships.skins.errors import ConsumeComponentItemConflict
     12 from cosmetics.client.ships.skins.live_data.component_license import ComponentLicense
     13 from cosmetics.common.ships.skins.live_data.component_license_type import ComponentLicenseType
     14 from cosmetics.common.ships.skins.static_data.component import ComponentsDataLoader
     15 from cosmetics.common.ships.skins.static_data.slot import SlotsDataLoader
     16 from inventorycommon.const import groupShipSkinDesignComponents
     17 from localization import GetByLabel
     18 from stackless_response_router.exceptions import TimeoutException
     19 _instance = None
     20 logger = logging.getLogger(__name__)
     21 _POPULATE_CACHE_TIMEOUT_IN_SECONDS = 5
     22 _POPULATE_CACHE_INCREMENT_IN_SECONDS = 0.1
     23 _MAX_CONCURRENT_COMPONENT_ITEM_ACTIVATIONS = 10
     24 
     25 def get_ship_skin_component_svc():
     26     global _instance
     27     if _instance is None:
     28         _instance = _ShipSkinComponentSvc()
     29     return _instance
     30 
     31 
     32 class _ShipSkinComponentSvc(object):
     33     __startupdependencies__ = ['publicGatewaySvc']
     34     __notifyevents__ = ['OnSessionChanged']
     35 
     36     def __init__(self):
     37         self._licenses_by_element_type = {}
     38         self._owned_licenses_already_fetched = False
     39         self._owned_licenses_is_fetching = False
     40         self._items_being_consumed = set()
     41         self._components_flagged_as_new = set()
     42         public_gateway = sm.GetService('publicGatewaySvc')
     43         self._request_messenger = PublicShipComponentLicenseRequestMessenger(public_gateway)
     44         self._notice_messenger = PublicShipComponentLicenseNoticeMessenger(public_gateway)
     45         sm.RegisterNotify(self)
     46         ship_skin_svc_signals.on_component_license_granted_internal.connect(self._on_component_license_granted_internal)
     47 
     48     def __del__(self):
     49         ship_skin_svc_signals.on_component_license_granted_internal.disconnect(self._on_component_license_granted_internal)
     50 
     51     def OnSessionChanged(self, _isRemote, _sess, change):
     52         if 'charid' in change:
     53             self.clear_cache()
     54             self._items_being_consumed = set()
     55 
     56     def _check_populate_cache(self, force_refresh = False):
     57         if not self._owned_licenses_already_fetched or force_refresh:
     58             self._populate_cache()
     59 
     60     def clear_cache(self, licenses_to_flush = None):
     61         if licenses_to_flush is None:
     62             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - flushing all component licenses cache')
     63             self._licenses_by_element_type = {}
     64             self._components_flagged_as_new.clear()
     65         elif len(licenses_to_flush) > 0:
     66             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - flushing cache for component licenses for components %s' % licenses_to_flush)
     67             for component_id in licenses_to_flush:
     68                 if component_id in self._licenses_by_element_type:
     69                     self._licenses_by_element_type.pop(component_id)
     70 
     71         else:
     72             return
     73         self._owned_licenses_already_fetched = False
     74         ship_skin_signals.on_component_license_cache_invalidated(licenses_to_flush or [])
     75 
     76     def _populate_cache(self):
     77         if self._owned_licenses_is_fetching:
     78             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - cache is already being populated, waiting for results')
     79             timeout = 0
     80             while self._owned_licenses_is_fetching:
     81                 if timeout >= _POPULATE_CACHE_TIMEOUT_IN_SECONDS:
     82                     raise TimeoutException
     83                 timeout += _POPULATE_CACHE_INCREMENT_IN_SECONDS
     84                 uthread2.Sleep(_POPULATE_CACHE_INCREMENT_IN_SECONDS)
     85 
     86             return
     87         try:
     88             self._owned_licenses_is_fetching = True
     89             self._licenses_by_element_type = {}
     90             component_ids_per_license_type = self._request_messenger.get_owned_licenses_request()
     91             self._get_licenses_parallel(component_ids_per_license_type)
     92             self._owned_licenses_already_fetched = True
     93         finally:
     94             self._owned_licenses_is_fetching = False
     95 
     96     def _get_licenses_parallel(self, component_ids_per_license_type):
     97         ComponentsDataLoader.load_components_data()
     98         parallel_calls = []
     99         for license_type, component_ids in component_ids_per_license_type.iteritems():
    100             for component_id in component_ids:
    101                 component_data = ComponentsDataLoader.get_component_data(component_id)
    102                 if component_data:
    103                     parallel_calls.append((self.get_license, (component_id, component_data.category, license_type)))
    104                 else:
    105                     logger.warning('SKIN COMPONENT LICENSES MANAGEMENT - could not find component data for component %s' % component_id)
    106 
    107         uthread.parallel(parallel_calls) if len(parallel_calls) > 0 else []
    108 
    109     def _cache_license(self, component_id, license_type, license):
    110         if license_type == ComponentLicenseType.LIMITED and license is not None and license.remaining_license_uses == 0:
    111             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - %s %s not cached, bound license with 0 remaining uses' % (license_type, component_id))
    112             return
    113         if component_id not in self._licenses_by_element_type:
    114             self._licenses_by_element_type[component_id] = {}
    115         self._licenses_by_element_type[component_id][license_type] = license
    116         logger.info('SKIN COMPONENT LICENSES MANAGEMENT - cached %s %s' % (license_type, component_id))
    117 
    118     def get_license(self, component_id, component_type, license_type, force_refresh = False):
    119         if license_type == ComponentLicenseType.LIMITED:
    120             return self.get_bound_license(component_id, component_type, force_refresh)
    121         elif license_type == ComponentLicenseType.UNLIMITED:
    122             return self.get_unbound_license(component_id, component_type, force_refresh)
    123         else:
    124             return None
    125 
    126     def get_all_owned_licenses(self):
    127         self._check_populate_cache()
    128         licenses = []
    129         for component_id, license_types in self._licenses_by_element_type.iteritems():
    130             for license_type, license in license_types.iteritems():
    131                 if license:
    132                     licenses.append(license)
    133 
    134         return licenses
    135 
    136     def get_all_owned_licenses_in_category(self, category):
    137         licenses = self.get_all_owned_licenses()
    138         licenses = [ x for x in licenses if x is not None and x.get_component_data().category == category ]
    139         return licenses
    140 
    141     def get_owned_licenses_by_type(self, component_id):
    142         self._check_populate_cache()
    143         return self._licenses_by_element_type.get(component_id, {})
    144 
    145     def get_bound_license(self, component_id, component_type, force_refresh = False):
    146         if not force_refresh and component_id in self._licenses_by_element_type and ComponentLicenseType.LIMITED in self._licenses_by_element_type[component_id]:
    147             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - returning cached license for %s %s' % (ComponentLicenseType.LIMITED, component_id))
    148             return self._licenses_by_element_type[component_id][ComponentLicenseType.LIMITED]
    149         license = self._request_messenger.get_finite_license_request(session.charid, component_id, component_type)
    150         self._cache_license(component_id, ComponentLicenseType.LIMITED, license)
    151         return self._licenses_by_element_type.get(component_id, {}).get(ComponentLicenseType.LIMITED, None)
    152 
    153     def get_unbound_license(self, component_id, component_type, force_refresh = False):
    154         if not force_refresh and component_id in self._licenses_by_element_type and ComponentLicenseType.UNLIMITED in self._licenses_by_element_type[component_id]:
    155             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - returning cached license for %s %s' % (ComponentLicenseType.UNLIMITED, component_id))
    156             return self._licenses_by_element_type[component_id][ComponentLicenseType.UNLIMITED]
    157         license = self._request_messenger.get_infinite_license_request(session.charid, component_id, component_type)
    158         self._cache_license(component_id, ComponentLicenseType.UNLIMITED, license)
    159         return self._licenses_by_element_type.get(component_id, {}).get(ComponentLicenseType.UNLIMITED, None)
    160 
    161     def get_default_license_to_use(self, component_id, component_type):
    162         license = self.get_unbound_license(component_id, component_type)
    163         if license:
    164             return license
    165         return self.get_bound_license(component_id, component_type)
    166 
    167     def consume_component_items(self, items):
    168         if len(self._items_being_consumed) > 0:
    169             return GetByLabel('UI/Personalization/ShipSkins/DesignComponents/UnableToActivateComponentsTooFast')
    170         if len(items) > _MAX_CONCURRENT_COMPONENT_ITEM_ACTIVATIONS:
    171             return GetByLabel('UI/Personalization/ShipSkins/DesignComponents/UnableToActivateTooManyComponents', max_designs=_MAX_CONCURRENT_COMPONENT_ITEM_ACTIVATIONS)
    172         error_msg = None
    173         item_ids_to_consume = set([ item_id for item_id, _, _ in items ])
    174         item_ids_to_consume = item_ids_to_consume.difference(self._items_being_consumed)
    175         self._items_being_consumed.update(item_ids_to_consume)
    176         items_to_consume = [ (item_id, type_id, qty) for item_id, type_id, qty in items if item_id in item_ids_to_consume ]
    177         try:
    178             first_exception = None
    179             for item in items_to_consume:
    180                 try:
    181                     itemID, typeID, quantity = item
    182                     self._consume_component_item(itemID, typeID, quantity)
    183                 except Exception as e:
    184                     if first_exception is None:
    185                         first_exception = e
    186 
    187             if first_exception:
    188                 if isinstance(first_exception, ConsumeComponentItemConflict):
    189                     error_msg = GetByLabel('UI/Personalization/ShipSkins/DesignComponents/FailedToConsumeSKINDesignComponentItemAlreadyOwned')
    190                 else:
    191                     error_msg = GetByLabel('UI/Personalization/ShipSkins/DesignComponents/FailedToConsumeSKINDesignComponentItem')
    192         finally:
    193             for item_id in item_ids_to_consume:
    194                 self._items_being_consumed.remove(item_id)
    195 
    196             return error_msg
    197 
    198     def get_random_component_license(self, slot_id):
    199         component_data = self.get_random_component_data(slot_id)
    200         if not component_data:
    201             return None
    202         bound_license = self.get_bound_license(component_id=component_data.component_id, component_type=component_data.category)
    203         unbound_license = self.get_unbound_license(component_id=component_data.component_id, component_type=component_data.category)
    204         if unbound_license:
    205             return unbound_license
    206         if bound_license:
    207             return bound_license
    208 
    209     def get_random_component_data(self, slot_id, only_owned = True):
    210         allowed_components = SlotsDataLoader.get_allowed_component(slot_id).values()
    211         if only_owned:
    212             owned_licenses = self.get_all_owned_licenses()
    213             owned_ids = {l.component_id for l in owned_licenses}
    214             components = [ c for c in allowed_components if c.component_id in owned_ids and c.published == True ]
    215         else:
    216             components = [ c for c in allowed_components if c and c.published == True ]
    217         components = sorted(components, key=lambda component: component.color_shade_sort_index, reverse=False)
    218         if len(components) == 0:
    219             return None
    220         component_data = random.choice(components)
    221         return component_data
    222 
    223     def _consume_component_item(self, item_id, type_id, quantity):
    224         group_id = evetypes.GetGroupID(type_id)
    225         if group_id == groupShipSkinDesignComponents:
    226             if self._is_license_type(type_id, ComponentLicenseType.UNLIMITED):
    227                 quantity = 1
    228             self._request_messenger.consume_request(item_id, quantity)
    229             if self._is_license_type(type_id, ComponentLicenseType.LIMITED):
    230                 component_id = self._get_component_id_by_type(type_id)
    231                 if component_id:
    232                     self.clear_cache([component_id])
    233         else:
    234             raise Exception('item %s must be of group %s to be consumed for skin component licenses' % (item_id, group_id))
    235 
    236     def _is_license_type(self, type_id, license_type):
    237         component_data = ComponentsDataLoader.get_component_for_component_type(type_id)
    238         if component_data:
    239             return component_data.get_license_type(type_id) == license_type
    240         return False
    241 
    242     def _get_component_id_by_type(self, type_id):
    243         component_data = ComponentsDataLoader.get_component_for_component_type(type_id)
    244         if component_data:
    245             return component_data.component_id
    246 
    247     def admin_grant_license(self, component_id, license_type, component_type, license_count = None):
    248         if session and session.role & ROLE_PROGRAMMER:
    249             if license_type == ComponentLicenseType.LIMITED:
    250                 self._request_messenger.admin_grant_finite_request(component_id, component_type, license_count)
    251             elif license_type == ComponentLicenseType.UNLIMITED:
    252                 self._request_messenger.admin_grant_infinite_request(component_id, component_type)
    253             self.clear_cache([component_id])
    254 
    255     def admin_revoke_license(self, component_id, license_type, component_type):
    256         if session and session.role & ROLE_PROGRAMMER:
    257             if license_type == ComponentLicenseType.LIMITED:
    258                 self._request_messenger.admin_revoke_finite_request(session.charid, component_id, component_type)
    259             elif license_type == ComponentLicenseType.UNLIMITED:
    260                 self._request_messenger.admin_revoke_infinite_request(session.charid, component_id, component_type)
    261             self.clear_cache([component_id])
    262 
    263     def set_component_is_new_flag(self, component_id, is_new):
    264         if is_new:
    265             self._components_flagged_as_new.add(component_id)
    266         else:
    267             self._components_flagged_as_new.discard(component_id)
    268 
    269     def is_component_new(self, component_id):
    270         return component_id in self._components_flagged_as_new
    271 
    272     def _on_component_license_granted_internal(self, component_id, quantity):
    273         component_data = ComponentsDataLoader.get_component_data(component_id)
    274         if component_data:
    275             license_type = ComponentLicenseType.LIMITED if quantity is not None else ComponentLicenseType.UNLIMITED
    276             license = self.get_license(component_id, component_data.category, license_type, force_refresh=True)
    277             self._set_component_is_new_flag(component_data, license, quantity)
    278             logger.info('SKIN COMPONENT LICENSES MANAGEMENT - license %s granted' % license)
    279             ship_skin_signals.on_component_license_granted(component_id, license_type, quantity)
    280         else:
    281             raise Exception('could not find component data for component %s' % component_id)
    282 
    283     def _set_component_is_new_flag(self, component_data, license, quantity):
    284         if license.license_type == ComponentLicenseType.UNLIMITED:
    285             existing_limited_license = self.get_license(component_data.component_id, component_data.category, ComponentLicenseType.LIMITED)
    286             if not existing_limited_license:
    287                 self.set_component_is_new_flag(component_data.component_id, is_new=True)
    288         elif license.license_type == ComponentLicenseType.LIMITED:
    289             existing_unlimited_license = self.get_license(component_data.component_id, component_data.category, ComponentLicenseType.UNLIMITED)
    290             if not existing_unlimited_license:
    291                 is_first_grant = license.remaining_license_uses - quantity == 0
    292                 if is_first_grant:
    293                     self.set_component_is_new_flag(component_data.component_id, is_new=is_first_grant)
    294 
    295     def has_existing_license(self, component_id):
    296         component_data = ComponentsDataLoader.get_component_data(component_id)
    297         bound_license = self.get_bound_license(component_data.component_id, component_data.category)
    298         unbound_license = self.get_unbound_license(component_data.component_id, component_data.category)
    299         return bound_license is not None or unbound_license is not None