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

shipSkinTradingSvc.py (15474B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\cosmetics\client\shipSkinTradingSvc.py
      2 import datetime
      3 import gametime
      4 import logging
      5 import uthread2
      6 import uuid
      7 from carbon.common.script.sys.serviceConst import ROLE_PROGRAMMER
      8 from cosmetics.client.messengers.cosmetics.ship.shipSkinTradingRequestMessenger import PublicShipSkinTradingRequestMessenger
      9 from cosmetics.client.shipCosmeticTransactionSvc import get_ship_cosmetic_transaction_svc
     10 from cosmetics.client.shipSkinDataSvc import get_ship_skin_data_svc
     11 from cosmetics.client.ships.feature_flag import are_skin_hub_offers_enabled
     12 from cosmetics.client.ships.link.validators.paste import ShipSkinListingLinkValidator
     13 from cosmetics.client.ships.ship_skin_signals import on_skin_listing_cache_invalidated, on_skin_hub_offers_availability_changed, on_skin_listing_expired
     14 from cosmetics.client.ships.skins.errors import GenericError, ListingError
     15 from cosmetics.client.ships.skins.live_data.skin_listing_buyer_fee import ListingBuyerFeeType, ListingBuyerFee
     16 from cosmetics.client.ships.skins.live_data.skin_listing_target import ListingTarget, ListedTo
     17 from cosmetics.client.ships.skins.live_data.skin_listing_target import ListingTargetType
     18 from cosmetics.client.skinListingsPaginationController import SkinListingsPaginationController
     19 from cosmetics.common.ships.skins.util import SkinListingOrder, Currency
     20 from eve.common.script.sys.idCheckers import IsCharacter, IsPlayerCorporation, IsAlliance, IsNPCCorporation
     21 from stackless_response_router.exceptions import TimeoutException
     22 logger = logging.getLogger(__name__)
     23 _instance = None
     24 _EXPIRY_WATCHDOG_FREQUENCY = 5
     25 
     26 def get_ship_skin_trading_svc():
     27     global _instance
     28     if _instance is None:
     29         _instance = _ShipSkinTradingSvc()
     30     return _instance
     31 
     32 
     33 class _ShipSkinTradingSvc(object):
     34     __startupdependencies__ = ['publicGatewaySvc']
     35     __notifyevents__ = ['OnSessionChanged', 'OnSkillsChanged']
     36 
     37     def __init__(self):
     38         self._skin_listing_by_id = {}
     39         self._cached_broker_fees_per_duration = {}
     40         self._cached_broker_fees_listing_target = None
     41         self._cached_tax_rate = None
     42         self._cached_tax_rate_listing_target = None
     43         self._cached_buyer_fees = {}
     44         self._cached_buyer_fee_definitions = None
     45         self._cached_buyer_fee_definitions_version = None
     46         self._listing_expiry_watcher_thread = None
     47         self._already_expired = []
     48         public_gateway = sm.GetService('publicGatewaySvc')
     49         self._request_messenger = PublicShipSkinTradingRequestMessenger(public_gateway)
     50         self._connect_signals()
     51         self._listing_expiry_watcher_thread = uthread2.start_tasklet(self._watch_for_expired_listings)
     52         sm.GetService('chat').add_paste_validator(ShipSkinListingLinkValidator())
     53 
     54     def __del__(self):
     55         self._disconnect_signals()
     56         if self._listing_expiry_watcher_thread:
     57             self._listing_expiry_watcher_thread.kill()
     58             self._listing_expiry_watcher_thread = None
     59 
     60     def _connect_signals(self):
     61         sm.RegisterNotify(self)
     62         on_skin_hub_offers_availability_changed.connect(self._on_skin_hub_offers_availability_changed)
     63 
     64     def _disconnect_signals(self):
     65         sm.UnregisterNotify(self)
     66         on_skin_hub_offers_availability_changed.disconnect(self._on_skin_hub_offers_availability_changed)
     67 
     68     def OnSessionChanged(self, _isRemote, _sess, change):
     69         if 'charid' in change or 'corpid' in change or 'allianceid' in change or 'corprole' in change:
     70             self._clear_cache()
     71 
     72     def OnSkillsChanged(self, *args):
     73         self._cached_tax_rate = None
     74         self._cached_tax_rate_listing_target = None
     75 
     76     def _on_skin_hub_offers_availability_changed(self, *args):
     77         logger.info('SKIN LICENSE TRADING - feature flag switched for SKIN trading availability, clearing cache')
     78         self._clear_cache()
     79 
     80     def _clear_cache(self):
     81         self._skin_listing_by_id = {}
     82         self._already_expired = []
     83         self._cached_broker_fees_listing_target = None
     84         self._cached_broker_fees_per_duration = {}
     85         self._cached_tax_rate = None
     86         self._cached_tax_rate_listing_target = None
     87         self._cached_buyer_fee_definitions = None
     88         self._cached_buyer_fee_definitions_version = None
     89         self._cached_buyer_fees = {}
     90         on_skin_listing_cache_invalidated()
     91 
     92     def _watch_for_expired_listings(self):
     93         while True:
     94             uthread2.Sleep(_EXPIRY_WATCHDOG_FREQUENCY)
     95             for listing_id, listing in self._skin_listing_by_id.iteritems():
     96                 if listing and listing.is_expired and listing_id not in self._already_expired:
     97                     logger.info('SKIN LICENSE TRADING - watchdog found expired listing %s (expired at %s)' % (listing_id, listing.valid_until))
     98                     self._already_expired.append(listing_id)
     99                     on_skin_listing_expired(listing_id)
    100 
    101     def get_broker_fees_per_duration(self, target_id = None):
    102         try:
    103             target_type = self.get_listing_target_type(target_id)
    104             target = ListingTarget(target_type, target_id)
    105             if len(self._cached_broker_fees_per_duration) > 0 and self._cached_tax_rate_listing_target and self._cached_broker_fees_listing_target == target:
    106                 return (self._cached_broker_fees_per_duration, None)
    107             self._cached_broker_fees_listing_target = target
    108             self._cached_broker_fees_per_duration = self._request_messenger.get_broker_fees_and_available_durations_request(self._cached_broker_fees_listing_target)
    109             return (self._cached_broker_fees_per_duration, None)
    110         except Exception as e:
    111             logger.exception('exception in get_broker_fees_per_duration: %s' % e)
    112             return ({}, ListingError.UNKNOWN)
    113 
    114     def get_tax_rate(self, target_id = None):
    115         try:
    116             target_type = self.get_listing_target_type(target_id)
    117             target = ListingTarget(target_type, target_id)
    118             if self._cached_tax_rate is not None and self._cached_tax_rate_listing_target and self._cached_tax_rate_listing_target == target:
    119                 return (self._cached_tax_rate, None)
    120             self._cached_tax_rate_listing_target = target
    121             self._cached_tax_rate = self._request_messenger.get_tax_rate_request(self._cached_tax_rate_listing_target)
    122             return (self._cached_tax_rate, None)
    123         except Exception as e:
    124             logger.exception('exception in get_tax_rate: %s' % e)
    125             return (None, ListingError.UNKNOWN)
    126 
    127     def get_predicted_buyer_fee(self, skin_hex, target_id = None):
    128         try:
    129             target_type = self.get_listing_target_type(target_id)
    130             target = ListingTarget(target_type, target_id)
    131             if skin_hex in self._cached_buyer_fees:
    132                 for cached_target, cached_buyer_fee in self._cached_buyer_fees[skin_hex].iteritems():
    133                     if cached_target == target:
    134                         return (cached_buyer_fee, None)
    135 
    136             else:
    137                 self._cached_buyer_fees[skin_hex] = {}
    138             self._cached_buyer_fees[skin_hex][target] = self._request_messenger.get_buyer_fee_request(skin_hex, target)
    139             return (self._cached_buyer_fees[skin_hex][target], None)
    140         except Exception as e:
    141             logger.exception('exception in get_predicted_buyer_fee: %s' % e)
    142             return (None, ListingError.UNKNOWN)
    143 
    144     def get_buyer_fee_for_listing(self, listing):
    145         if listing is None:
    146             return (None, ListingError.UNKNOWN)
    147         try:
    148             _, err = self._get_buyer_fees_definitions()
    149             if err is not None:
    150                 return (None, err)
    151             skin_data = get_ship_skin_data_svc().get_skin_data(listing.skin_hex)
    152             if skin_data and skin_data.tier_level in self._cached_buyer_fee_definitions:
    153                 if listing.branded:
    154                     return (self._cached_buyer_fee_definitions[skin_data.tier_level].get(ListingBuyerFeeType.BRANDED_LISTING_BUYER_FEE, None), None)
    155                 else:
    156                     return (self._cached_buyer_fee_definitions[skin_data.tier_level].get(ListingBuyerFeeType.REGULAR_LISTING_BUYER_FEE, None), None)
    157         except Exception as e:
    158             logger.exception('exception in get_buyer_fee_for_listing: %s' % e)
    159             return (None, ListingError.UNKNOWN)
    160 
    161     def get_available_listing_durations(self):
    162         self.get_broker_fees_per_duration()
    163         return sorted(self._cached_broker_fees_per_duration.keys())
    164 
    165     def get_listing_target_type(self, type_id):
    166         if type_id is None:
    167             return ListingTargetType.PUBLIC
    168         if IsCharacter(type_id):
    169             return ListingTargetType.CHARACTER
    170         if IsPlayerCorporation(type_id) or IsNPCCorporation(type_id):
    171             return ListingTargetType.CORPORATION
    172         if IsAlliance(type_id):
    173             return ListingTargetType.ALLIANCE
    174         return ListingTargetType.PUBLIC
    175 
    176     def get_pagination_controller(self):
    177         pagination_controller = SkinListingsPaginationController(get_page_method=self._fetch_page_from_server)
    178         return pagination_controller
    179 
    180     def get_owned_listings(self, listed_to = ListedTo.PUBLIC):
    181         if not are_skin_hub_offers_enabled():
    182             logger.info('SKIN LICENSE TRADING - Skip fetching owned SKIN listings, feature currently disabled')
    183             return []
    184         listings = self._request_messenger.get_all_owned_listings_request()
    185         if listed_to == ListedTo.MY_CORP:
    186             listings = [ l for l in listings if l.target.target_type == ListingTargetType.CORPORATION and l.target.target_id == session.corpid ]
    187         elif listed_to == ListedTo.MY_ALLIANCE:
    188             listings = [ l for l in listings if l.target.target_type == ListingTargetType.ALLIANCE and l.target.target_id == session.allianceid ]
    189         elif listed_to == ListedTo.SPECIFIC:
    190             listings = [ l for l in listings if l.target.target_type in (ListingTargetType.CHARACTER, ListingTargetType.CORPORATION, ListingTargetType.ALLIANCE) ]
    191         return listings
    192 
    193     def get_listing(self, listing_id):
    194         if not are_skin_hub_offers_enabled():
    195             logger.info('SKIN LICENSE TRADING - Skip fetching SKIN listing, feature currently disabled')
    196             return (None, ListingError.FEATURE_FLAG_OFF)
    197         listing, error = self._request_messenger.get_listing_request(listing_id)
    198         return (listing, error)
    199 
    200     def purchase_listing(self, listing_id, quantity = 1):
    201         if not are_skin_hub_offers_enabled():
    202             logger.info('SKIN LICENSE TRADING - Forbid SKIN listing purchase, feature currently disabled')
    203             return ListingError.FEATURE_FLAG_OFF
    204         if self._cached_buyer_fee_definitions_version is None:
    205             _, err = self._get_buyer_fees_definitions(force_refresh=True)
    206             if err is not None:
    207                 return err
    208         try:
    209             transaction_id, error = self._request_messenger.initiate_purchase_listing_request(listing_id, quantity, self._cached_buyer_fee_definitions_version)
    210             if error is not None:
    211                 if error == ListingError.INVALID_VERSION:
    212                     self._cached_buyer_fee_definitions_version = None
    213                 return error
    214             try:
    215                 transaction_result = get_ship_cosmetic_transaction_svc().process_transaction(transaction_id)
    216                 if transaction_result:
    217                     return
    218                 return ListingError.UNKNOWN
    219             except TimeoutException as e:
    220                 logger.warning('transaction timed out for listing %s purchase' % listing_id)
    221                 return GenericError.TIMEOUT
    222 
    223         except TimeoutException as e:
    224             logger.warning('transaction timed out for initiating listing %s purchase' % listing_id)
    225             return GenericError.TIMEOUT
    226 
    227     def cancel_listing(self, listing_id):
    228         try:
    229             error = self._request_messenger.cancel_listing_request(listing_id)
    230             if error is not None:
    231                 return error
    232             self._clear_cache()
    233         except TimeoutException as e:
    234             logger.warning('transaction timed out for requesting listing {listing_id} cancellation'.format(listing_id=listing_id))
    235             return GenericError.TIMEOUT
    236 
    237     def create_listing(self, currency, price, skin_hex, duration_in_seconds, quantity = 1, target_id = None):
    238         if not are_skin_hub_offers_enabled():
    239             logger.info('SKIN LICENSE TRADING - Forbid SKIN listing creation, feature currently disabled')
    240             return ListingError.FEATURE_FLAG_OFF
    241         target_type = self.get_listing_target_type(target_id)
    242         target = ListingTarget(target_type, target_id)
    243         try:
    244             transaction_id, error = self._request_messenger.initiate_place_listing_request(session.charid, currency, price, skin_hex, duration_in_seconds, quantity, target)
    245             if error is not None:
    246                 return error
    247             try:
    248                 transaction_result = get_ship_cosmetic_transaction_svc().process_transaction(transaction_id)
    249                 if transaction_result:
    250                     return
    251                 return ListingError.UNKNOWN
    252             except TimeoutException as e:
    253                 logger.warning('transaction timed out for creating listing for skin_hex %s' % skin_hex)
    254                 return GenericError.TIMEOUT
    255 
    256         except TimeoutException as e:
    257             logger.warning('transaction timed out for initiating listing request for skin_hex %s' % skin_hex)
    258             return GenericError.TIMEOUT
    259 
    260     def admin_expire_listing(self, listing_id, minutes_to_expiry):
    261         if session and session.role & ROLE_PROGRAMMER:
    262             if minutes_to_expiry > 0:
    263                 timestamp = gametime.now() + datetime.timedelta(minutes=minutes_to_expiry)
    264                 self._request_messenger.set_expiration_request(listing_id, timestamp)
    265                 self._clear_cache()
    266 
    267     def _fetch_page_from_server(self, ship_types_filter, order_by, ascending, listing_target_type, seller_membership_type, page_token, num_per_page):
    268         self._skin_listing_by_id.clear()
    269         if not are_skin_hub_offers_enabled():
    270             logger.info('SKIN LICENSE TRADING - Skip fetching SKIN listings, feature currently disabled')
    271             return ([], None, None)
    272         try:
    273             listings, next_page_token, error = self._request_messenger.get_all_listings_request(ship_types_filter, order_by, ascending, listing_target_type, seller_membership_type, page_token, num_per_page)
    274         except Exception as e:
    275             logging.exception(e)
    276             return ([], None, None)
    277 
    278         if error:
    279             logging.exception(error)
    280             return ([], None, error)
    281         for listing in listings:
    282             self._skin_listing_by_id[listing.identifier] = listing
    283 
    284         return (listings, next_page_token, None)
    285 
    286     def _get_buyer_fees_definitions(self, force_refresh = False):
    287         try:
    288             if force_refresh or self._cached_buyer_fee_definitions is None:
    289                 self._cached_buyer_fee_definitions_version, self._cached_buyer_fee_definitions = self._request_messenger.get_buyer_fees_definition_request()
    290             return (self._cached_buyer_fee_definitions, None)
    291         except Exception as e:
    292             logger.exception('exception in _get_buyer_fees_definitions: %s' % e)
    293             return ({}, ListingError.UNKNOWN)