repository.py (10394B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\cosmetics\client\character\title\repository\repository.py 2 import logging 3 from datetime import datetime, timedelta 4 from carbon.common.script.sys.serviceConst import ROLE_QA 5 from character.client.identifier import CharacterIdentifier 6 from cosmetics.client.character.title.api import signal as api_signal 7 from cosmetics.client.character.title.const import LOGGER_NAME 8 from cosmetics.client.character.title.model.definition.identifier import TitleIdentifier 9 from cosmetics.client.character.title.model.state.owned import CachedOwnedTitles 10 from cosmetics.client.character.title.model.state.equipped import CachedEquippedTitleAttributes, EquippedTitleStateAttributes 11 from cosmetics.client.character.title.repository.flatbuffer.repository import TitleLocalizedDefinitionsRepository 12 from cosmetics.client.character.title.repository.message_bus.request_messenger import TitleRequestMessenger 13 from cosmetics.client.character.title.repository.message_bus.notice_listener import TitleNoticeListener 14 from cosmetics.client.character.title.repository.message_bus import notice_listener_signal 15 from featuremanagement.client.flag import flag_bool 16 from localization.util import GetLanguageID 17 from locks import singleFlight 18 from locks.singleFlight import single_flight 19 from publicGateway.gateway import PublicGateway 20 from signals import Signal 21 logger = logging.getLogger(LOGGER_NAME) 22 _OWNED_TITLES_CACHE_TTL = 600 23 _EQUIP_STATE_CACHE_TTL = 30 24 if False: 25 from typing import Dict, List 26 from publicGateway.grpc.test.mock_publicGatewaySvc import MockPublicGateway 27 from carbon.common.script.sys.serviceManager import ServiceManager 28 from cosmetics.client.character.title.repository.test.setup import MockServiceManager 29 30 class CharacterTitlesRepository(object): 31 __startupdependencies__ = ['publicGatewaySvc'] 32 __notifyevents__ = ['OnSessionChanged', 'OnLanguageChanged'] 33 34 def __init__(self, public_gateway, service_manager): 35 logger.info('initializing repository...') 36 self._service_manager = service_manager 37 self._public_gateway = public_gateway 38 self._request_messenger = TitleRequestMessenger(self._public_gateway) 39 self._notice_messenger = TitleNoticeListener(self._public_gateway) 40 self._definitions_repository = None 41 self._owned_titles = None 42 self._equipped_titles = {} 43 notice_listener_signal.on_title_granted_notice.connect(self._on_title_granted_notice) 44 notice_listener_signal.on_title_revoked_notice.connect(self._on_title_revoked_notice) 45 notice_listener_signal.on_set_notice.connect(self._on_set_notice) 46 notice_listener_signal.on_check_version_notice.connect(self._on_check_version) 47 self._service_manager.RegisterNotify(self) 48 self._startup_definitions_repository() 49 50 def __del__(self): 51 notice_listener_signal.on_title_granted_notice.disconnect(self._on_title_granted_notice) 52 notice_listener_signal.on_title_revoked_notice.disconnect(self._on_title_revoked_notice) 53 notice_listener_signal.on_set_notice.disconnect(self._on_set_notice) 54 notice_listener_signal.on_check_version_notice.disconnect(self._on_check_version) 55 self._service_manager.UnregisterNotify(self) 56 if self._definitions_repository is not None: 57 self._definitions_repository.on_content_updated.disconnect(self._on_definitions_updated) 58 self._definitions_repository = None 59 60 @property 61 def definitions(self): 62 return self._definitions_repository 63 64 @single_flight 65 def get_owned_titles(self): 66 self._cache_owned_titles() 67 if self._owned_titles: 68 return self._owned_titles.cache 69 return [] 70 71 @single_flight 72 def get_equipped_title(self, character_identifier): 73 self._cache_equipped_title(character_identifier) 74 if character_identifier in self._equipped_titles: 75 return self._equipped_titles[character_identifier].equipped_state 76 77 @single_flight 78 def equip_title(self, title_identifier): 79 char_identifier = CharacterIdentifier(session.charid) 80 try: 81 if title_identifier is not None: 82 self._request_messenger.equip_title_request(title_identifier) 83 else: 84 self._request_messenger.unequip_title_request() 85 if char_identifier in self._equipped_titles: 86 self._equipped_titles.pop(char_identifier) 87 equipped_state = EquippedTitleStateAttributes(equipped_title=title_identifier) 88 self._add_equipped_title_to_cache(char_identifier, equipped_state) 89 api_signal.on_title_equipped_changed(char_identifier, equipped_state) 90 except Exception as e: 91 logger.error('unable to equip title %s for character %s' % (title_identifier, char_identifier), exc_info=e) 92 93 @single_flight 94 def admin_grant_title(self, title_identifier): 95 if not session.role & ROLE_QA: 96 return 97 try: 98 self._request_messenger.admin_grant_request(title_identifier) 99 except Exception as e: 100 logger.error('unable to admin grant title %s' % title_identifier, exc_info=e) 101 102 @single_flight 103 def admin_revoke_title(self, title_identifier): 104 if not session.role & ROLE_QA: 105 return 106 try: 107 self._request_messenger.admin_revoke_request(title_identifier) 108 except Exception as e: 109 logger.error('unable to admin revoke title %s' % title_identifier, exc_info=e) 110 111 def admin_clear_disk_cache(self): 112 self.definitions.admin_clear_disk_cache() 113 114 def _startup_definitions_repository(self, check_for_updates = False): 115 if self._definitions_repository is not None: 116 self._definitions_repository.on_content_updated.disconnect(self._on_definitions_updated) 117 self._definitions_repository.on_no_content_updated.disconnect(self._on_definitions_updated_noop) 118 self._definitions_repository = None 119 api_signal.on_definitions_invalidated() 120 language = GetLanguageID() 121 logger.info('creating definitions repository for language {l}...'.format(l=language)) 122 self._definitions_repository = TitleLocalizedDefinitionsRepository(self._public_gateway, language) 123 self._definitions_repository.on_content_updated.connect(self._on_definitions_updated) 124 self._definitions_repository.on_no_content_updated.connect(self._on_definitions_updated_noop) 125 if check_for_updates: 126 self._definitions_repository.check_version_and_prime(None) 127 128 def _cache_owned_titles(self): 129 if self._owned_titles is None or self._owned_titles.is_expired: 130 self._make_owned_titles_request() 131 132 def _make_owned_titles_request(self): 133 try: 134 page = '' 135 owned_titles = [] 136 while page is not None: 137 owned_titles_page, page = self._request_messenger.get_owned_titles_request(page) 138 owned_titles.extend(owned_titles_page) 139 140 self._owned_titles = CachedOwnedTitles(owned_titles=owned_titles, expiration=datetime.now() + timedelta(seconds=_OWNED_TITLES_CACHE_TTL)) 141 except Exception as e: 142 logger.error('unable to cache owned titles', exc_info=e) 143 144 def _cache_equipped_title(self, character_identifier): 145 if character_identifier not in self._equipped_titles or self._equipped_titles[character_identifier].is_expired: 146 self._make_get_equipped_title_request(character_identifier) 147 148 def _make_get_equipped_title_request(self, character_identifier): 149 try: 150 equipped_state = self._request_messenger.get_equipped_title_request(character_identifier) 151 self._add_equipped_title_to_cache(character_identifier, equipped_state) 152 except Exception as e: 153 logger.error('unable to cache equipped title for character %s' % character_identifier, exc_info=e) 154 155 def _add_equipped_title_to_cache(self, character_identifier, equipped_state): 156 self._equipped_titles[character_identifier] = CachedEquippedTitleAttributes(equipped_state=equipped_state, expiration=datetime.now() + timedelta(seconds=_EQUIP_STATE_CACHE_TTL)) 157 158 def _clear_cache(self): 159 self._equipped_titles = {} 160 self._clear_character_session_cache() 161 162 def _clear_character_session_cache(self): 163 self._owned_titles = None 164 165 def OnSessionChanged(self, _is_remote, _session, change): 166 if 'charid' in change: 167 self._clear_character_session_cache() 168 if self._definitions_repository.get_current_version_in_memory() is None: 169 logger.info("definitions aren't loaded yet as character enters game, manually fetching") 170 self._definitions_repository.check_version_and_prime(None) 171 172 def OnLanguageChanged(self, _language_id): 173 logger.info('language changed, reloading definitions') 174 self._startup_definitions_repository(True) 175 176 def _on_definitions_updated(self, *args): 177 logger.info('definitions loaded') 178 api_signal.on_definitions_loaded() 179 180 def _on_definitions_updated_noop(self, *args): 181 logger.info('definitions loaded - everything was already up-to-date') 182 183 def _on_check_version(self, version): 184 logger.info('check version notice received with version %s', version) 185 self.definitions.check_version_and_prime(version) 186 187 def _on_title_granted_notice(self, title_identifier): 188 if self._owned_titles is not None and not self._owned_titles.is_expired: 189 self._owned_titles.cache.append(title_identifier) 190 else: 191 self._cache_owned_titles() 192 api_signal.on_title_granted(title_identifier) 193 194 def _on_title_revoked_notice(self, title_identifier): 195 if self._owned_titles is not None and not self._owned_titles.is_expired: 196 if title_identifier in self._owned_titles.cache: 197 self._owned_titles.cache.remove(title_identifier) 198 else: 199 self._cache_owned_titles() 200 api_signal.on_title_revoked(title_identifier) 201 202 def _on_set_notice(self, character_identifier, equipped_state): 203 self._add_equipped_title_to_cache(character_identifier, equipped_state) 204 api_signal.on_title_equipped_changed(character_identifier, equipped_state)