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

repository.py (24270B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\achievement_system\client\repository\repository.py
      2 import blue
      3 from datetime import datetime, timedelta
      4 from achievement_system.client.api.error import RewardAlreadyClaimedError, ServiceUnavailableError, GenericInternalServerError
      5 from achievement_system.client.model.definition.reward.identifier import RewardDefinitionIdentifier
      6 from achievement_system.client.repository.activity.repository import AchievementActivityRepository
      7 from carbon.common.script.sys.serviceConst import ROLE_QA
      8 import logging
      9 from achievement_system.client.api.signal import on_achievement_completed, on_achievement_progressed, on_achievement_milestone_reached, on_category_progressed, on_category_milestone_reached, on_definitions_loaded, on_admin_achievement_reset, on_admin_achievement_reset_all, on_achievement_milestone_reward_claimed, on_category_milestone_reward_claimed
     10 from achievement_system.client.model.definition.achievement.identifier import AchievementDefinitionIdentifier
     11 from achievement_system.client.model.definition.achievement.milestone.identifier import AchievementMilestoneDefinitionIdentifier
     12 from achievement_system.client.model.definition.category.identifier import CategoryDefinitionIdentifier
     13 from achievement_system.client.model.definition.category.milestone.identifier import CategoryMilestoneDefinitionIdentifier
     14 from achievement_system.client.model.state.achievement import CachedAchievementStateAttributes, AchievementStateAttributes
     15 from achievement_system.client.model.state.unclaimed_rewards import CachedUnclaimedRewards, UnclaimedAchievementRewards, UnclaimedCategoryRewards
     16 from achievement_system.client.model.state.achievement_score import CachedAchievementScore
     17 from achievement_system.client.model.state.category import CategoryStateAttributes, CachedCategoryStateAttributes
     18 from achievement_system.client.model.state.checklist import Checklist
     19 from achievement_system.client.repository.flatbuffer.repository import AchievementDefinitionsRepository
     20 from achievement_system.client.repository.message_bus.notice_listener import AchievementNoticeListener
     21 from achievement_system.client.repository.message_bus.request_messenger import AchievementRequestMessenger
     22 from achievement_system.client.repository.message_bus import notice_listener_signal
     23 from achievement_system.common.const import LOGGER_NAME
     24 from character.client.identifier import CharacterIdentifier
     25 from locks.singleFlight import single_flight
     26 from semantic_version import SemanticVersion
     27 from featuremanagement.client.flag import flag_bool
     28 from signals import Signal
     29 logger = logging.getLogger(LOGGER_NAME)
     30 if False:
     31     from typing import Dict
     32     from carbon.common.script.sys.serviceManager import ServiceManager
     33     from achievement_system.client.repository.test.setup import MockServiceManager
     34 _STATE_CACHE_TTL_IN_SECONDS = 600
     35 _DELAY_BEFORE_MILESTONE_REACHED_SIGNAL_IN_SECONDS = 1
     36 
     37 class AchievementRepository(object):
     38     __startupdependencies__ = ['publicGatewaySvc']
     39     __notifyevents__ = ['OnSessionChanged']
     40 
     41     def __init__(self, public_gateway, service_manager, are_unreleased_achievements_visible, on_unreleased_achievements_visible_changed, tracked_achievements_setting, pinned_achievements_setting, recently_progressed_achievements_setting, recently_completed_achievements_setting):
     42         logger.info('initializing repository...')
     43         self._service_manager = service_manager
     44         self._on_unreleased_achievements_visible_changed_signal = on_unreleased_achievements_visible_changed
     45         self._are_unreleased_achievements_visible = are_unreleased_achievements_visible
     46         self._definitions_repository = AchievementDefinitionsRepository(public_gateway, are_unreleased_achievements_visible)
     47         self._activity_repository = AchievementActivityRepository(self._service_manager, tracked_achievements_setting, pinned_achievements_setting, recently_progressed_achievements_setting, recently_completed_achievements_setting)
     48         self._character_request_messenger = AchievementRequestMessenger(public_gateway)
     49         self._notice_listener = AchievementNoticeListener(public_gateway)
     50         notice_listener_signal.on_achievement_completed_notice.connect(self._on_achievement_completed)
     51         notice_listener_signal.on_achievement_cumulative_progressed_notice.connect(self._on_achievement_cumulative_progressed)
     52         notice_listener_signal.on_achievement_checklist_progressed_notice.connect(self._on_achievement_checklist_progressed)
     53         notice_listener_signal.on_check_version_notice.connect(self._on_check_version)
     54         notice_listener_signal.on_achievement_milestone_reached_notice.connect(self._on_achievement_milestone_reached)
     55         notice_listener_signal.on_category_milestone_reached_notice.connect(self._on_category_milestone_reached)
     56         notice_listener_signal.on_category_progressed_notice.connect(self._on_category_progressed)
     57         self._on_unreleased_achievements_visible_changed_signal.connect(self._on_unreleased_achievements_visible_changed)
     58         self._definitions_repository.on_content_updated.connect(self._on_definitions_updated)
     59         self._definitions_repository.on_no_content_updated.connect(self._on_definitions_updated_noop)
     60         self._achievement_states = {}
     61         self._category_states = {}
     62         self._unclaimed_rewards = None
     63         self._character_scores = {}
     64         self._service_manager.RegisterNotify(self)
     65 
     66     def __del__(self):
     67         notice_listener_signal.on_achievement_completed_notice.disconnect(self._on_achievement_completed)
     68         notice_listener_signal.on_achievement_cumulative_progressed_notice.disconnect(self._on_achievement_cumulative_progressed)
     69         notice_listener_signal.on_achievement_checklist_progressed_notice.disconnect(self._on_achievement_checklist_progressed)
     70         notice_listener_signal.on_check_version_notice.disconnect(self._on_check_version)
     71         notice_listener_signal.on_achievement_milestone_reached_notice.disconnect(self._on_achievement_milestone_reached)
     72         notice_listener_signal.on_category_milestone_reached_notice.disconnect(self._on_category_milestone_reached)
     73         notice_listener_signal.on_category_progressed_notice.disconnect(self._on_category_progressed)
     74         self._on_unreleased_achievements_visible_changed_signal.disconnect(self._on_unreleased_achievements_visible_changed)
     75         self._definitions_repository.on_content_updated.disconnect(self._on_definitions_updated)
     76         self._definitions_repository.on_no_content_updated.disconnect(self._on_definitions_updated_noop)
     77         self._service_manager.UnregisterNotify(self)
     78 
     79     @property
     80     def definitions(self):
     81         return self._definitions_repository
     82 
     83     @property
     84     def activity(self):
     85         return self._activity_repository
     86 
     87     @single_flight
     88     def get_achievement_state(self, achievement_identifier):
     89         self._cache_achievement_state(achievement_identifier=achievement_identifier)
     90         if achievement_identifier in self._achievement_states:
     91             return self._achievement_states[achievement_identifier].achievement_state
     92 
     93     @single_flight
     94     def get_category_state(self, category_identifier):
     95         self._cache_category_state(category_identifier=category_identifier)
     96         if category_identifier in self._category_states:
     97             return self._category_states[category_identifier].category_state
     98 
     99     @single_flight
    100     def get_achievement_score(self, character_id):
    101         self._cache_achievement_score(character_id)
    102         if character_id in self._character_scores:
    103             return self._character_scores[character_id].score
    104         return 0
    105 
    106     @single_flight
    107     def get_unclaimed_rewards_for_achievement(self, achievement_id):
    108         self._cache_unclaimed_rewards()
    109         if self._unclaimed_rewards and achievement_id in self._unclaimed_rewards.achievements:
    110             return self._unclaimed_rewards.achievements[achievement_id]
    111 
    112     def is_reward_claimed_for_achievement_milestone(self, achievement_id, milestone_id, reward_id):
    113         achievement_state = self.get_achievement_state(achievement_id)
    114         if not achievement_state:
    115             return False
    116         if not achievement_state.is_milestone_reached(milestone_id):
    117             return False
    118         unclaimed_achievement_rewards = self.get_unclaimed_rewards_for_achievement(achievement_id)
    119         if not unclaimed_achievement_rewards:
    120             return True
    121         unclaimed_milestone_rewards = unclaimed_achievement_rewards.unclaimed_rewards_per_milestone.get(milestone_id, [])
    122         return reward_id not in unclaimed_milestone_rewards
    123 
    124     @single_flight
    125     def get_unclaimed_rewards_for_category(self, category_id):
    126         self._cache_unclaimed_rewards()
    127         if self._unclaimed_rewards and category_id in self._unclaimed_rewards.categories:
    128             return self._unclaimed_rewards.categories[category_id]
    129 
    130     @single_flight
    131     def get_all_unclaimed_rewards(self):
    132         self._cache_unclaimed_rewards()
    133         if self._unclaimed_rewards:
    134             return (self._unclaimed_rewards.achievements, self._unclaimed_rewards.categories)
    135 
    136     @single_flight
    137     def get_unclaimed_rewards_count(self):
    138         reward_count = 0
    139         achievement_rewards, category_rewards = self.get_all_unclaimed_rewards()
    140         for x in achievement_rewards.values():
    141             reward_count += x.get_unclaimed_rewards_count()
    142 
    143         for x in category_rewards.values():
    144             reward_count += x.get_unclaimed_rewards_count()
    145 
    146         return reward_count
    147 
    148     @single_flight
    149     def get_unclaimed_rewards_row_count(self):
    150         try:
    151             reward_count = 0
    152             achievement_rewards, category_rewards = self.get_all_unclaimed_rewards()
    153             for x in achievement_rewards.values():
    154                 reward_count += len(x.unclaimed_rewards_per_milestone)
    155 
    156             for x in category_rewards.values():
    157                 reward_count += len(x.unclaimed_rewards_per_milestone)
    158 
    159             return reward_count
    160         except (ServiceUnavailableError, GenericInternalServerError):
    161             return 0
    162 
    163     @single_flight
    164     def claim_rewards_for_achievement(self, achievement_id, milestone_id):
    165         try:
    166             self._character_request_messenger.claim_achievement_milestone_rewards_request(achievement_id, milestone_id)
    167             self._unclaimed_rewards = None
    168             on_achievement_milestone_reward_claimed(achievement_id=achievement_id, milestone_id=milestone_id)
    169         except RewardAlreadyClaimedError:
    170             logger.info('reward already claimed, ignoring - probably just clicking too fast')
    171         except Exception as e:
    172             logger.error('unable to claim achievement rewards', exc_info=e)
    173 
    174     @single_flight
    175     def claim_rewards_for_category(self, category_id, milestone_id):
    176         try:
    177             self._character_request_messenger.claim_category_milestone_rewards_request(category_id, milestone_id)
    178             on_category_milestone_reward_claimed(category_id=category_id, milestone_id=milestone_id)
    179         except RewardAlreadyClaimedError:
    180             logger.info('reward already claimed, ignoring - probably just clicking too fast')
    181         except Exception as e:
    182             logger.error('unable to claim category rewards', exc_info=e)
    183 
    184     def are_definitions_loaded(self):
    185         return self.definitions and self.definitions.loaded()
    186 
    187     def are_definitions_loading(self):
    188         return self.definitions.request_in_progress
    189 
    190     def reload_definitions(self, force_operation = False):
    191         if not self.are_definitions_loaded() or force_operation:
    192             logger.info('Reloading definitions...')
    193             self.definitions.admin_clear_memory_cache()
    194             self.definitions.check_version_and_prime(None)
    195 
    196     @single_flight
    197     def admin_reset_all(self):
    198         if not session.role & ROLE_QA:
    199             return
    200         self._character_request_messenger.admin_reset_all_request()
    201         self._clear_character_session_cache()
    202         on_admin_achievement_reset_all()
    203 
    204     @single_flight
    205     def admin_add_cumulative_progress_to_achievement(self, achievement_identifier, progress_delta):
    206         if not session.role & ROLE_QA:
    207             return
    208         self._character_request_messenger.admin_add_cumulative_progress_request(achievement_identifier, progress_delta)
    209 
    210     @single_flight
    211     def admin_add_checklist_progress_to_achievement(self, achievement_identifier, checklist_delta):
    212         if not session.role & ROLE_QA:
    213             return
    214         self._character_request_messenger.admin_add_checklist_progress_request(achievement_identifier, checklist_delta)
    215 
    216     @single_flight
    217     def admin_add_set_progress_to_achievement(self, achievement_identifier, set_delta):
    218         if not session.role & ROLE_QA:
    219             return
    220         self._character_request_messenger.admin_add_set_progress_request(achievement_identifier, set_delta)
    221 
    222     @single_flight
    223     def admin_complete_achievement(self, achievement_identifier):
    224         if not session.role & ROLE_QA:
    225             return
    226         self._character_request_messenger.admin_complete_achievement_request(achievement_identifier)
    227 
    228     @single_flight
    229     def admin_complete_achievements_in_category(self, category_id):
    230         if not session.role & ROLE_QA:
    231             return
    232         for achievement_entry in self.definitions.get_achievement_entries_recursive(category_id):
    233             try:
    234                 self._character_request_messenger.admin_complete_achievement_request(achievement_entry.identifier)
    235             except Exception as e:
    236                 logger.error('unable to complete achievement %s' % achievement_entry.identifier, exc_info=e)
    237 
    238         self._clear_character_session_cache()
    239 
    240     @single_flight
    241     def admin_reset_achievement(self, achievement_identifier):
    242         if not session.role & ROLE_QA:
    243             return
    244         self._character_request_messenger.admin_reset_achievement_request(achievement_identifier)
    245         self._achievement_states.pop(achievement_identifier, None)
    246         on_admin_achievement_reset(achievement_identifier)
    247 
    248     @single_flight
    249     def admin_reset_achievements_in_category(self, category_id):
    250         if not session.role & ROLE_QA:
    251             return
    252         for achievement_entry in self.definitions.get_achievement_entries_recursive(category_id):
    253             try:
    254                 self.admin_reset_achievement(achievement_entry.identifier)
    255             except Exception as e:
    256                 logger.error('unable to reset achievement %s' % achievement_entry.identifier, exc_info=e)
    257 
    258     def admin_clear_disk_cache(self):
    259         self.definitions.admin_clear_disk_cache()
    260 
    261     def _cache_achievement_state(self, achievement_identifier):
    262         if achievement_identifier not in self._achievement_states or self._achievement_states[achievement_identifier].is_expired:
    263             self._make_achievement_state_request(achievement_identifier)
    264 
    265     def _refresh_cached_achievement_state(self, achievement_identifier, completed_date, checklist_value, progress_value, reached_milestones):
    266         if achievement_identifier not in self._achievement_states:
    267             return
    268         old_achievement_state = self._achievement_states[achievement_identifier].achievement_state
    269         new_completion_date = completed_date if completed_date is not None else old_achievement_state.completed_date
    270         new_checklist_value = checklist_value if checklist_value is not None else old_achievement_state.checklist
    271         new_progress_value = progress_value if progress_value is not None else old_achievement_state.progress
    272         new_reached_milestones = reached_milestones if reached_milestones is not None else old_achievement_state.reached_milestones
    273         new_achievement_state = AchievementStateAttributes(new_completion_date, new_checklist_value, new_progress_value, new_reached_milestones)
    274         self._achievement_states[achievement_identifier] = CachedAchievementStateAttributes(achievement_state=new_achievement_state, expiration=datetime.now() + timedelta(seconds=_STATE_CACHE_TTL_IN_SECONDS))
    275         return new_achievement_state
    276 
    277     def _cache_category_state(self, category_identifier):
    278         if category_identifier not in self._category_states or self._category_states[category_identifier].is_expired:
    279             self._make_category_state_request(category_identifier)
    280 
    281     def _cache_achievement_score(self, character_id):
    282         if character_id not in self._character_scores or self._character_scores[character_id].is_expired:
    283             self._make_character_score_request(character_id)
    284 
    285     def _cache_unclaimed_rewards(self):
    286         if self._unclaimed_rewards is None or self._unclaimed_rewards.is_expired:
    287             self._make_unclaimed_rewards_request()
    288 
    289     def _make_achievement_state_request(self, achievement_identifier):
    290         try:
    291             achievement_state = self._character_request_messenger.get_achievement_request(achievement_id=achievement_identifier)
    292             self._achievement_states[achievement_identifier] = CachedAchievementStateAttributes(achievement_state=achievement_state, expiration=datetime.now() + timedelta(seconds=_STATE_CACHE_TTL_IN_SECONDS))
    293         except Exception as e:
    294             logger.error('unable to cache achievement state', exc_info=e)
    295 
    296     def _make_category_state_request(self, category_identifier):
    297         try:
    298             category_state = self._character_request_messenger.get_category_state_request(category_id=category_identifier)
    299             self._category_states[category_identifier] = CachedCategoryStateAttributes(category_state=category_state, expiration=datetime.now() + timedelta(seconds=_STATE_CACHE_TTL_IN_SECONDS))
    300         except Exception as e:
    301             logger.error('unable to cache category state', exc_info=e)
    302 
    303     def _make_character_score_request(self, character_id):
    304         try:
    305             character_score = self._character_request_messenger.get_achievement_score_request(character_id=character_id)
    306             self._character_scores[character_id] = CachedAchievementScore(score=character_score, expiration=datetime.now() + timedelta(seconds=_STATE_CACHE_TTL_IN_SECONDS))
    307         except Exception as e:
    308             logger.error('unable to cache achievement character score', exc_info=e)
    309 
    310     def _make_unclaimed_rewards_request(self):
    311         try:
    312             page = ''
    313             unclaimed_achievement_rewards = {}
    314             unclaimed_category_rewards = {}
    315             while page is not None:
    316                 unclaimed_achievement_rewards_page, unclaimed_category_rewards_page, page = self._character_request_messenger.get_unclaimed_rewards_request(page)
    317                 for entry in unclaimed_achievement_rewards_page:
    318                     if entry.achievement_id not in unclaimed_achievement_rewards:
    319                         unclaimed_achievement_rewards[entry.achievement_id] = UnclaimedAchievementRewards()
    320                     unclaimed_achievement_rewards[entry.achievement_id].add_unclaimed_rewards_for_milestone(milestone_id=entry.milestone_id, reward_ids=entry.unclaimed_reward_ids)
    321 
    322                 for entry in unclaimed_category_rewards_page:
    323                     if entry.category_id not in unclaimed_category_rewards:
    324                         unclaimed_category_rewards[entry.category_id] = UnclaimedCategoryRewards()
    325                     unclaimed_category_rewards[entry.category_id].add_unclaimed_rewards_for_milestone(milestone_id=entry.milestone_id, reward_ids=entry.unclaimed_reward_ids)
    326 
    327             self._unclaimed_rewards = CachedUnclaimedRewards(unclaimed_achievement_rewards=unclaimed_achievement_rewards, unclaimed_category_rewards=unclaimed_category_rewards, expiration=datetime.now() + timedelta(seconds=_STATE_CACHE_TTL_IN_SECONDS))
    328         except ServiceUnavailableError as e:
    329             logger.info('unable to cache unclaimed rewards - ServiceUnavailableError silenced', exc_info=e)
    330         except Exception as e:
    331             logger.error('unable to cache unclaimed rewards', exc_info=e)
    332 
    333     def _on_achievement_completed(self, achievement_id):
    334         self._achievement_states.pop(achievement_id, None)
    335         self.get_achievement_state(achievement_identifier=achievement_id)
    336         character_id = CharacterIdentifier(session.charid)
    337         self._character_scores.pop(character_id, None)
    338         self.get_achievement_score(character_id)
    339         on_achievement_completed(achievement_id)
    340 
    341     def _on_achievement_cumulative_progressed(self, achievement_id, value):
    342         if achievement_id in self._achievement_states:
    343             achievement_state = self._refresh_cached_achievement_state(achievement_id, completed_date=None, checklist_value=None, progress_value=value, reached_milestones=None)
    344         else:
    345             achievement_state = self.get_achievement_state(achievement_identifier=achievement_id)
    346         on_achievement_progressed(achievement_id, achievement_state)
    347 
    348     def _on_achievement_checklist_progressed(self, achievement_id, checklist):
    349         if achievement_id in self._achievement_states:
    350             achievement_state = self._refresh_cached_achievement_state(achievement_id, completed_date=None, checklist_value=checklist, progress_value=None, reached_milestones=None)
    351         else:
    352             achievement_state = self.get_achievement_state(achievement_identifier=achievement_id)
    353         on_achievement_progressed(achievement_id, achievement_state)
    354 
    355     def _on_category_progressed(self, category_id, _score):
    356         self._category_states.pop(category_id, None)
    357         self.get_category_state(category_identifier=category_id)
    358         on_category_progressed(category_id)
    359 
    360     def _on_achievement_milestone_reached(self, achievement_id, milestone_id):
    361         self._achievement_states.pop(achievement_id, None)
    362         self._character_scores.pop(CharacterIdentifier(session.charid), None)
    363         self.get_achievement_state(achievement_identifier=achievement_id)
    364         try:
    365             blue.pyos.synchro.SleepWallclock(_DELAY_BEFORE_MILESTONE_REACHED_SIGNAL_IN_SECONDS * 1000.0)
    366         except Exception:
    367             logger.warning('could not sleep in _on_achievement_milestone_reached')
    368         finally:
    369             self._unclaimed_rewards = None
    370             self.get_unclaimed_rewards_for_achievement(achievement_id)
    371             on_achievement_milestone_reached(achievement_id, milestone_id)
    372 
    373     def _on_category_milestone_reached(self, category_id, milestone_id):
    374         self._category_states.pop(category_id, None)
    375         self.get_category_state(category_identifier=category_id)
    376         try:
    377             blue.pyos.synchro.SleepWallclock(_DELAY_BEFORE_MILESTONE_REACHED_SIGNAL_IN_SECONDS * 1000.0)
    378         except Exception:
    379             logger.warning('could not sleep in _on_category_milestone_reached')
    380         finally:
    381             self._unclaimed_rewards = None
    382             self.get_unclaimed_rewards_for_category(category_id)
    383             on_category_milestone_reached(category_id, milestone_id)
    384 
    385     def _on_check_version(self, version):
    386         logger.info('check version notice received with version %s', version)
    387         self.definitions.check_version_and_prime(version)
    388 
    389     def _on_definitions_updated(self, *args):
    390         logger.info('definitions loaded')
    391         on_definitions_loaded()
    392 
    393     def _on_definitions_updated_noop(self, *args):
    394         logger.info('definitions loaded - everything was already up-to-date')
    395 
    396     def _on_unreleased_achievements_visible_changed(self, *args):
    397         logger.info('unreleased achievements visibility changed, reloading definitions')
    398         self.reload_definitions(True)
    399 
    400     def OnSessionChanged(self, _is_remote, _session, change):
    401         if 'charid' in change:
    402             self._clear_character_session_cache()
    403             if self._definitions_repository.get_current_version_in_memory() is None:
    404                 logger.info("definitions aren't loaded yet as character enters game, manually fetching")
    405                 self._definitions_repository.check_version_and_prime(None)
    406 
    407     def _clear_character_session_cache(self):
    408         self._achievement_states = {}
    409         self._category_states = {}
    410         self._unclaimed_rewards = None
    411 
    412     def _clear_caches(self):
    413         self._clear_character_session_cache()
    414         self._character_scores = {}