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

goalsController.py (15216B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dailygoals\client\goalsController.py
      2 import uthread2
      3 import threadutils
      4 import logging
      5 from carbon.common.script.sys.serviceConst import ROLE_QA
      6 from dailygoals.client.featureFlag import are_daily_goals_enabled
      7 from dailygoals.client.goalMessenger import DailyGoalMessenger
      8 from dailygoals.client.mockMessenger import MockDailyGoalMessenger
      9 import dailygoals.client.goalMessengerSignals as messenger_signals
     10 from dailygoals.client.const import DailyGoalCategory, BATCH_SIZE
     11 import dailygoals.client.goalSignals as dailyGoalSignals
     12 from dailygoals.client.goal import DailyGoal
     13 from eve.common.script.util import notificationconst
     14 from dailygoals.client.errors import GoalNotRedeemableToCurrentLocation, GoalMessengerError
     15 from assetholding.client.assetholdingSignals import on_entitlement_redeemed
     16 from assetholding.client.assetholdingController import AssetHoldingController
     17 import dailygoals.client.qa_settings as daily_goals_qa_settings
     18 from omega.client.clone_grade_svc import get_clone_grade_svc
     19 logger = logging.getLogger('dailygoals')
     20 
     21 class DailyGoalsController(object):
     22     __notifyevents__ = ['OnCharacterSelected']
     23     _instance = None
     24 
     25     def __init__(self):
     26         if daily_goals_qa_settings.daily_goals_use_mock.get():
     27             self._daily_goal_messenger = MockDailyGoalMessenger.get_instance(sm.GetService('publicGatewaySvc'))
     28         else:
     29             self._daily_goal_messenger = DailyGoalMessenger.get_instance(sm.GetService('publicGatewaySvc'))
     30         self._fetching_active = False
     31         self._active_goals = {}
     32         self._is_active_goals_dirty = True
     33         self._fetching_unclaimed = False
     34         self._unclaimed_goals = {}
     35         self._is_unclaimed_dirty = True
     36         self._unclaimed_goal_ids = set()
     37         self._has_tried_fetching_any_unclaimed_ids = False
     38         self._abort_fetching = False
     39         self._connect_to_messenger_signals()
     40         AssetHoldingController.get_instance()
     41         sm.RegisterNotify(self)
     42 
     43     def __del__(self):
     44         self._disconnect_from_messenger_signals()
     45 
     46     @classmethod
     47     def get_instance(cls):
     48         if not cls._instance:
     49             cls._instance = DailyGoalsController()
     50         return cls._instance
     51 
     52     def _connect_to_messenger_signals(self):
     53         messenger_signals.on_current_goals_received_internal.connect(self._on_current_goals_received)
     54         messenger_signals.on_goal_completed_internal.connect(self._on_goal_completed)
     55         messenger_signals.on_goal_progressed_internal.connect(self._on_goal_progressed)
     56         messenger_signals.on_goal_redeemed_internal.connect(self._on_goal_redeemed)
     57         dailyGoalSignals.on_availability_changed.connect(self._on_availability_changed)
     58         on_entitlement_redeemed.connect(self._on_entitlement_redeemed)
     59 
     60     def _disconnect_from_messenger_signals(self):
     61         messenger_signals.on_current_goals_received_internal.disconnect(self._on_current_goals_received)
     62         messenger_signals.on_goal_completed_internal.disconnect(self._on_goal_completed)
     63         messenger_signals.on_goal_progressed_internal.disconnect(self._on_goal_progressed)
     64         messenger_signals.on_goal_redeemed_internal.disconnect(self._on_goal_redeemed)
     65         dailyGoalSignals.on_availability_changed.disconnect(self._on_availability_changed)
     66         on_entitlement_redeemed.disconnect(self._on_entitlement_redeemed)
     67 
     68     def flush_cache(self):
     69         if self._fetching_active or self._fetching_unclaimed:
     70             self._abort_fetching = True
     71         self._fetching_active = False
     72         self._active_goals = {}
     73         self._is_active_goals_dirty = True
     74         self._unclaimed_goals = {}
     75         self._is_unclaimed_dirty = True
     76         self._fetching_unclaimed = False
     77         self._unclaimed_goal_ids.clear()
     78         self._has_tried_fetching_any_unclaimed_ids = False
     79         dailyGoalSignals.on_cache_invalidated()
     80 
     81     def get_all_goals(self):
     82         all_goals = self._get_active_goals()
     83         if self._is_unclaimed_dirty:
     84             uthread2.start_tasklet(self._fetch_unclaimed_history_ids)
     85         all_goals.extend([ goal for goal in self._unclaimed_goals.values() if goal.has_data() ])
     86         return all_goals
     87 
     88     def get_daily_goal_ids_of_the_day(self):
     89         result = []
     90         for goal in self._active_goals.itervalues():
     91             if goal.get_category() == DailyGoalCategory.CATEGORY_DAILY:
     92                 result.append(goal.get_id())
     93 
     94         return result
     95 
     96     def get_daily_bonus_goal_id_of_the_day(self):
     97         for goal in self._active_goals.itervalues():
     98             if goal.get_category() == DailyGoalCategory.CATEGORY_DAILY_BONUS:
     99                 return goal.get_id()
    100 
    101     def get_monthly_milestone_goal_ids(self):
    102         result = []
    103         for goal in self._active_goals.itervalues():
    104             if goal.get_category() == DailyGoalCategory.CATEGORY_MONTHLY_BONUS:
    105                 result.append(goal.get_id())
    106 
    107         return result
    108 
    109     def has_unclaimed_rewards(self):
    110         if not bool(self._unclaimed_goal_ids) and not self._has_tried_fetching_any_unclaimed_ids and self._is_unclaimed_dirty and not self._fetching_unclaimed and are_daily_goals_enabled():
    111             self._has_tried_fetching_any_unclaimed_ids = True
    112             uthread2.start_tasklet(self._fetch_unclaimed_page, None)
    113         return bool(self._unclaimed_goal_ids)
    114 
    115     def redeem_reward(self, goal_id, redeem_to_current_location = False):
    116         try:
    117             self._daily_goal_messenger.redeem_reward(goal_id, redeem_to_current_location)
    118         except GoalNotRedeemableToCurrentLocation:
    119             try:
    120                 self._daily_goal_messenger.redeem_reward(goal_id, do_redeem_current_location=False)
    121             except Exception as e:
    122                 logger.exception('Daily Goals: Unable to redeem on retry for goal %s' % goal_id)
    123                 dailyGoalSignals.on_goal_payment_failed(goal_id)
    124 
    125         except Exception as e:
    126             logger.exception('Daily Goals: Failed to redeem goal with id %s' % goal_id)
    127             dailyGoalSignals.on_goal_payment_failed(goal_id)
    128 
    129     def pay_for_completion(self, goal_id):
    130         try:
    131             self._daily_goal_messenger.pay_for_completion(goal_id)
    132             self._get_goal(goal_id).set_paid_completion(True)
    133             dailyGoalSignals.on_pay_for_completion_successful(goal_id)
    134         except GoalMessengerError as gme:
    135             logger.exception('Daily Goals: Redeeming the goal returned status code %s' % gme.status_code)
    136             dailyGoalSignals.on_pay_for_completion_failed(goal_id, gme.status_code)
    137         except Exception as e:
    138             logger.exception('Daily Goals: Failed to redeem goal with id %s' % goal_id)
    139             dailyGoalSignals.on_pay_for_completion_failed(goal_id, None)
    140 
    141     def get_completion_convenience_counter(self):
    142         ret = 0
    143         for goal in self._get_active_goals():
    144             if goal.paid_completion():
    145                 ret += 1
    146 
    147         return ret
    148 
    149     @threadutils.threaded
    150     def fetch_unclaimed_goals_data(self):
    151         if not are_daily_goals_enabled():
    152             return
    153         if self._is_unclaimed_dirty:
    154             self._fetch_unclaimed_history_ids()
    155         while self._fetching_unclaimed:
    156             uthread2.Yield()
    157 
    158         for goal in self._unclaimed_goals.values():
    159             self._fetch_missing_goal_data(goal)
    160 
    161     def _get_cached_goal(self, goal_id):
    162         if goal_id in self._active_goals:
    163             return self._active_goals[goal_id]
    164         if goal_id in self._unclaimed_goals:
    165             return self._unclaimed_goals[goal_id]
    166 
    167     def _get_goal(self, goal_id):
    168         goal = self._get_cached_goal(goal_id)
    169         if goal:
    170             return goal
    171         data = self._fetch_goal(goal_id)
    172         if not data:
    173             return None
    174         goal = DailyGoal(goal_id)
    175         goal.set_data(data)
    176         if goal.is_expired:
    177             self._unclaimed_goals[goal_id] = goal
    178         else:
    179             self._active_goals[goal_id] = goal
    180         return goal
    181 
    182     def _get_active_goals(self):
    183         self._fetch_active_goals()
    184         return self._active_goals.values()
    185 
    186     def _fetch_active_goals(self):
    187         if self._fetching_active or not self._is_active_goals_dirty or not are_daily_goals_enabled():
    188             return
    189         self._fetching_active = True
    190         try:
    191             data = self._daily_goal_messenger.get_current_goals()
    192         except Exception as e:
    193             self._fetching_active = False
    194             self._abort_fetching = False
    195             raise e
    196 
    197         for goal in data:
    198             goal_id = goal.get('goal_id', None)
    199             if goal and goal_id:
    200                 daily_goal = DailyGoal(goal_id)
    201                 daily_goal.set_data(goal)
    202                 self._active_goals[goal_id] = daily_goal
    203                 if goal_id in self._unclaimed_goals:
    204                     del self._unclaimed_goals[goal_id]
    205                 dailyGoalSignals.on_goal_data_fetched(daily_goal)
    206                 if goal_id not in self._unclaimed_goal_ids and daily_goal.has_unclaimed_rewards():
    207                     self._unclaimed_goal_ids.add(goal_id)
    208                     dailyGoalSignals.on_unclaimed_goal_ids_changed()
    209 
    210         self._fetching_active = False
    211         self._is_active_goals_dirty = False
    212         self._abort_fetching = False
    213 
    214     def _fetch_unclaimed_history_ids(self):
    215         if self._fetching_unclaimed or not self._is_unclaimed_dirty or not are_daily_goals_enabled():
    216             return
    217         self._fetching_unclaimed = True
    218         next_token = None
    219         has_exception = False
    220         while next_token != '':
    221             try:
    222                 next_token = self._fetch_unclaimed_page(token=next_token)
    223             except Exception as e:
    224                 logger.exception('Daily Goals: Unable to fetch goal ids with token %s, %s' % (next_token, str(e)))
    225                 has_exception = True
    226                 break
    227 
    228             if self._abort_fetching:
    229                 break
    230 
    231         self._fetching_unclaimed = False
    232         self._is_unclaimed_dirty = has_exception
    233         self._abort_fetching = False
    234         dailyGoalSignals.on_unclaimed_goals_fetched()
    235 
    236     def _fetch_unclaimed_page(self, token):
    237         if token is None:
    238             token = ''
    239         self._has_tried_fetching_any_unclaimed_ids = True
    240         next_token, goal_ids = self._daily_goal_messenger.get_all_with_rewards_page(token=token, size=BATCH_SIZE)
    241         if self._abort_fetching:
    242             self._abort_fetching = False
    243             self._fetching_unclaimed = False
    244             return
    245         for goal_id in goal_ids:
    246             if goal_id in self._active_goals:
    247                 continue
    248             if goal_id not in self._unclaimed_goals:
    249                 self._unclaimed_goals[goal_id] = DailyGoal(goal_id)
    250 
    251         if bool(goal_ids):
    252             self._unclaimed_goal_ids.update(goal_ids)
    253             dailyGoalSignals.on_unclaimed_goal_ids_changed()
    254         return next_token
    255 
    256     @threadutils.threaded
    257     def _fetch_missing_goal_data(self, goal):
    258         if not goal or goal.fetching or goal.has_data():
    259             return
    260         goal.fetching = True
    261         data = self._fetch_goal(goal.get_id())
    262         goal.fetching = False
    263         if data is not None:
    264             goal.set_data(data)
    265             dailyGoalSignals.on_goal_data_fetched(goal)
    266 
    267     def _error_cleanup(self, log_text, exception):
    268         logger.exception('%s: %s' % log_text, str(exception))
    269         self._fetching_unclaimed = False
    270         self._abort_fetching = False
    271 
    272     def _fetch_goal(self, goal_id):
    273         try:
    274             goal_data = self._daily_goal_messenger.get_goal(goal_id)
    275         except Exception as e:
    276             logger.exception('Unable to fetch goal: %s' % str(e))
    277             return None
    278 
    279         return goal_data
    280 
    281     def _on_current_goals_received(self):
    282         self.flush_cache()
    283 
    284     @threadutils.threaded
    285     def _on_goal_completed(self, goal_id):
    286         goal = self._get_goal(goal_id)
    287         if not goal:
    288             return
    289         self._unclaimed_goal_ids.add(goal_id)
    290         desired_progress = goal.get_target_progress()
    291         goal.set_current_progress(desired_progress)
    292         dailyGoalSignals.on_completed(goal_id)
    293         if goal.get_category() != DailyGoalCategory.CATEGORY_MONTHLY_BONUS:
    294             self._dispatch_goal_completed_notification(goal_id, goal.get_name())
    295         elif not goal.is_last_milestone:
    296             self._dispatch_milestone_completed_notification()
    297         elif not get_clone_grade_svc().is_omega_with_grace_period():
    298             self._dispatch_track_completed_notification()
    299 
    300     def _on_goal_progressed(self, goal_id, progress):
    301         if goal_id not in self._active_goals:
    302             return
    303         previous_progress = self._active_goals[goal_id].get_current_progress()
    304         self._active_goals[goal_id].set_current_progress(progress)
    305         dailyGoalSignals.on_progress_changed(goal_id, progress, previous_progress)
    306 
    307     def _is_cached_active_or_unclaimed(self, goal_id):
    308         return goal_id in self._active_goals or goal_id in self._unclaimed_goals
    309 
    310     def _on_goal_redeemed(self, goal_id):
    311         if not self._is_cached_active_or_unclaimed(goal_id):
    312             return
    313         goal = self._get_goal(goal_id)
    314         if not goal:
    315             return
    316         goal.mark_as_redeemed()
    317         dailyGoalSignals.on_goal_payment_redeemed(goal_id)
    318         dailyGoalSignals.on_goal_payment_complete(goal_id)
    319         self._unclaimed_goal_ids.discard(goal_id)
    320         dailyGoalSignals.on_unclaimed_goal_ids_changed()
    321 
    322     def _on_entitlement_redeemed(self, goal_id, _):
    323         self._on_goal_redeemed(goal_id)
    324 
    325     def _on_availability_changed(self, *args, **kwargs):
    326         self.flush_cache()
    327 
    328     def _dispatch_goal_completed_notification(self, goal_id, goal_name):
    329         sm.GetService('notificationSvc').MakeAndScatterNotification(notificationconst.notificationTypeDailyGoalCompleted, data={'goalName': goal_name,
    330          'goalID': goal_id})
    331 
    332     def _dispatch_milestone_completed_notification(self):
    333         sm.GetService('notificationSvc').MakeAndScatterNotification(notificationconst.notificationTypeDailyMilestoneCompleted, data={'isOmega': get_clone_grade_svc().is_omega_with_grace_period()})
    334 
    335     def _dispatch_track_completed_notification(self):
    336         sm.GetService('notificationSvc').MakeAndScatterNotification(notificationconst.notificationTypeDailyRewardTrackCompleted, data={})
    337 
    338     def admin_set_progress(self, goal_id, progress_to_set):
    339         if session.role & ROLE_QA:
    340             goal = self._get_goal(goal_id)
    341             if goal:
    342                 progress_to_add = progress_to_set - goal.get_current_progress()
    343                 progress_to_add = max(0, progress_to_add)
    344                 progress_to_add = min(progress_to_add, goal.get_target_progress() - goal.get_current_progress())
    345                 if progress_to_add > 0:
    346                     self._daily_goal_messenger.set_goal_progress(goal_id, session.charid, progress_to_add)
    347 
    348     def OnCharacterSelected(self, *args, **kwargs):
    349         self.flush_cache()
    350 
    351 
    352 def __SakeReloadHook():
    353     try:
    354         DailyGoalsController.get_instance().flush_cache()
    355     except Exception:
    356         import log
    357         log.LogException()