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

guests.py (15361B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\dockedUI\guests.py
      2 import eveicon
      3 import logging
      4 import blue
      5 import localization
      6 import signals
      7 import uthread2
      8 import utillib
      9 from bannedwords.client import bannedwords
     10 from carbon.common.script.sys.serviceManager import ServiceManager
     11 from carbonui import Density, uiconst
     12 from carbonui.control.basicDynamicScroll import BasicDynamicScroll
     13 from carbonui.control.contextMenu.menuData import MenuData
     14 from carbonui.primitives.container import Container
     15 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     16 from clientsettings.setting import UserSettingBool
     17 from carbonui.util.various_unsorted import SortListOfTuples
     18 from eve.client.script.ui.control.entries.util import GetFromClass
     19 from carbonui.button.menu import MenuButtonIcon
     20 from eve.client.script.ui.quickFilter import QuickFilterEdit
     21 from eve.client.script.ui.shared.userentry import User, UserSimple
     22 from eve.common.script.sys import idCheckers
     23 logger = logging.getLogger(__name__)
     24 guest_list_compact_mode_setting = UserSettingBool('guestCondensedUserList', False)
     25 
     26 class LoadState(object):
     27     UNLOADED = 1
     28     LOADING = 2
     29     UNLOAD_PENDING = 3
     30     LOADED = 4
     31     ERROR = 5
     32 
     33 
     34 class GuestsPanelController(object):
     35     __notifyevents__ = ('OnCharNoLongerInStation', 'OnCharNowInStation', 'OnCharacterEnteredStructure', 'OnCharacterLeftStructure', 'OnSessionChanged')
     36     _guest_list = None
     37     _location_id = None
     38 
     39     def __init__(self):
     40         self._load_state = LoadState.UNLOADED
     41         self._guests_pending_add = {}
     42         self._guests_pending_remove = set()
     43         self.on_guest_list_changed = signals.Signal('{}.on_guest_list_changed'.format(self.__class__.__name__))
     44         self.on_loading_changed = signals.Signal('{}.on_loading_changed'.format(self.__class__.__name__))
     45         self.on_guest_count_changed = signals.Signal('{}.on_guest_count_changed'.format(self.__class__.__name__))
     46 
     47     @property
     48     def guest_list(self):
     49         if self._load_state == LoadState.LOADED:
     50             return self._guest_list
     51 
     52     @property
     53     def guest_count(self):
     54         if self._load_state == LoadState.LOADED:
     55             return len(self._guest_list.guests)
     56         else:
     57             return 0
     58 
     59     @property
     60     def loading(self):
     61         return self._load_state in {LoadState.LOADING, LoadState.UNLOAD_PENDING}
     62 
     63     def load(self):
     64         if self._load_state == LoadState.UNLOADED:
     65             ServiceManager.Instance().RegisterNotify(self)
     66             self._load_guest_list(location_id=self._get_location_id_from_session(session))
     67 
     68     def unload(self):
     69         if self._load_state == LoadState.LOADING:
     70             self._set_load_state(LoadState.UNLOAD_PENDING)
     71         else:
     72             ServiceManager.Instance().UnregisterNotify(self)
     73             self._guests_pending_add.clear()
     74             self._guests_pending_remove.clear()
     75             self._guest_list = None
     76             self._set_load_state(LoadState.UNLOADED)
     77             self.on_guest_list_changed(self)
     78             self.on_guest_count_changed(self)
     79 
     80     def _set_load_state(self, value):
     81         if self._load_state != value:
     82             was_loading = self.loading
     83             self._load_state = value
     84             if self.loading != was_loading:
     85                 self.on_loading_changed(self)
     86 
     87     @staticmethod
     88     def _get_station_guests():
     89         guests_raw = ServiceManager.Instance().GetService('station').GetGuests()
     90         return [ Guest(character_id, corporation_id, alliance_id, war_faction_id) for character_id, (corporation_id, alliance_id, war_faction_id) in guests_raw.items() ]
     91 
     92     @staticmethod
     93     def _get_structure_guests():
     94         guests_raw = ServiceManager.Instance().GetService('structureGuests').GetGuests()
     95         return [ Guest(character_id, corporation_id, alliance_id, war_faction_id) for character_id, (corporation_id, alliance_id, war_faction_id) in guests_raw.items() ]
     96 
     97     def _load_guest_list(self, location_id):
     98         if self._location_id == location_id:
     99             return
    100         self._location_id = location_id
    101         self._guests_pending_add.clear()
    102         self._guests_pending_remove.clear()
    103         self._set_load_state(LoadState.LOADING)
    104         try:
    105             guest_list = None
    106             if idCheckers.IsStation(location_id):
    107                 guest_list = GuestList(location_id=session.stationid, guests=self._get_station_guests())
    108             elif location_id is not None:
    109                 guest_list = GuestList(location_id=session.structureid, guests=self._get_structure_guests())
    110             if self._location_id != location_id:
    111                 return
    112             if self._load_state == LoadState.UNLOAD_PENDING:
    113                 self.unload()
    114                 return
    115             if guest_list is not None:
    116                 for guest in self._guests_pending_add.values():
    117                     guest_list.add(guest)
    118 
    119                 for character_id in self._guests_pending_remove:
    120                     guest_list.discard(character_id)
    121 
    122             self._guests_pending_add.clear()
    123             self._guests_pending_remove.clear()
    124         except Exception:
    125             logger.exception('Unhandled exception while attempting to load a guest list')
    126             if self._location_id == location_id:
    127                 self._set_load_state(LoadState.ERROR)
    128                 self.unload()
    129         else:
    130             self._guest_list = guest_list
    131             if guest_list is not None:
    132                 self._set_load_state(LoadState.LOADED)
    133             else:
    134                 self._set_load_state(LoadState.UNLOADED)
    135             self.on_guest_list_changed(self)
    136             self.on_guest_count_changed(self)
    137 
    138     @staticmethod
    139     def _get_location_id_from_session(session):
    140         if session.stationid:
    141             return session.stationid
    142         elif session.structureid:
    143             return session.structureid
    144         else:
    145             return None
    146 
    147     def _add_guest(self, guest):
    148         if self.loading:
    149             self._guests_pending_remove.discard(guest.character_id)
    150             self._guests_pending_add[guest.character_id] = guest
    151         elif self._load_state == LoadState.LOADED:
    152             self._guest_list.add(guest)
    153             self.on_guest_count_changed(self)
    154 
    155     def _remove_guest(self, character_id):
    156         if self.loading:
    157             if character_id in self._guests_pending_add:
    158                 self._guests_pending_add.pop(character_id)
    159             self._guests_pending_remove.add(character_id)
    160         elif self._load_state == LoadState.LOADED:
    161             self._guest_list.discard(character_id)
    162             self.on_guest_count_changed(self)
    163 
    164     def OnCharNowInStation(self, rec):
    165         character_id, corporation_id, alliance_id, war_faction_id = rec
    166         self._add_guest(Guest(character_id, corporation_id, alliance_id, war_faction_id))
    167 
    168     def OnCharNoLongerInStation(self, rec):
    169         self._remove_guest(character_id=rec[0])
    170 
    171     def OnCharacterEnteredStructure(self, characterID, corporationID, allianceID, factionID):
    172         self._add_guest(Guest(character_id=characterID, corporation_id=corporationID, alliance_id=allianceID, war_faction_id=factionID))
    173 
    174     def OnCharacterLeftStructure(self, characterID):
    175         self._remove_guest(character_id=characterID)
    176 
    177     def OnSessionChanged(self, isRemote, session, change):
    178         if any((name in change for name in ('stationid', 'structureid'))):
    179             uthread2.start_tasklet(self._load_guest_list, location_id=self._get_location_id_from_session(session))
    180 
    181 
    182 class GuestsPanel(Container):
    183     _loaded = False
    184     _quick_filter = None
    185     _scroll = None
    186     _scroll_update_id = 0
    187 
    188     def __init__(self, controller = None, **kwargs):
    189         self._controller = controller or GuestsPanelController()
    190         super(GuestsPanel, self).__init__(**kwargs)
    191 
    192     def LoadPanel(self):
    193         if not self._loaded:
    194             self._loaded = True
    195             self._layout()
    196             self._subscribe()
    197             uthread2.start_tasklet(self._load)
    198 
    199     def _load(self):
    200         self._controller.load()
    201         self._update_scroll_content()
    202 
    203     def _layout(self):
    204         top_cont = ContainerAutoSize(parent=self, align=uiconst.TOTOP, alignMode=uiconst.TOPLEFT)
    205         self._quick_filter = QuickFilterEdit(name='quickFilterEdit', parent=top_cont, align=uiconst.TOPLEFT, isCharacterField=True, density=Density.COMPACT)
    206         self._quick_filter.ReloadFunction = self._update_scroll_filter
    207         self._scroll = BasicDynamicScroll(parent=self, padTop=8)
    208         self._update_scroll_loading()
    209         MenuButtonIcon(parent=top_cont, align=uiconst.CENTERRIGHT, get_menu_func=get_guest_list_settings, texturePath=eveicon.people, hint=localization.GetByLabel('UI/Chat/UserlistSettingButtonHint'), width=16, height=16, iconSize=16)
    210 
    211     def _update_scroll_filter(self):
    212         filter_query = self._quick_filter.GetValue()
    213         if len(filter_query):
    214             bannedwords.check_search_words_allowed(filter_query)
    215         self._update_scroll_content()
    216 
    217     def _update_scroll_loading(self):
    218         if self._scroll:
    219             if self._controller.loading:
    220                 self._scroll.ShowLoading()
    221 
    222     def _update_scroll_content(self):
    223         if not self._scroll or self._scroll.destroyed:
    224             return
    225         scroll_update_id = self._allocate_scroll_update_id()
    226         guest_list = self._controller.guest_list
    227         if guest_list is None:
    228             self._location_id = None
    229             self._scroll.Clear()
    230             self._scroll.HideLoading()
    231         else:
    232             self._location_id = guest_list.location_id
    233             guest_list.on_guest_added.connect(self._on_guest_list_guest_added)
    234             guest_list.on_guest_removed.connect(self._on_guest_list_guest_removed)
    235             guests = self._get_filtered_guests(guest_list.guests)
    236             entries = self._get_guest_entries(guests)
    237             if self._scroll_update_id == scroll_update_id:
    238                 if self._scroll.destroyed:
    239                     return
    240                 self._scroll.Clear()
    241                 self._scroll.AddNodes(0, entries)
    242                 self._scroll.HideLoading()
    243 
    244     def _allocate_scroll_update_id(self):
    245         self._scroll_update_id += 1
    246         return self._scroll_update_id
    247 
    248     def _on_guest_list_guest_added(self, guest):
    249         self._add_guest_to_scroll(guest)
    250 
    251     def _on_guest_list_guest_removed(self, guest):
    252         self._remove_guest_from_scroll(guest)
    253 
    254     def _add_guest_to_scroll(self, guest):
    255         if not self._scroll or self._scroll.destroyed:
    256             return
    257         nodes = self._scroll.GetNodes()
    258         index = bisect_left(nodes, guest.name.lower(), key=lambda node: node.info.name.lower())
    259         entry = self._get_guest_entry(guest)
    260         self._scroll.AddNodes(index, [entry])
    261 
    262     def _remove_guest_from_scroll(self, guest):
    263         if self._scroll and not self._scroll.destroyed:
    264             for node in self._scroll.GetNodes():
    265                 if node.charID == guest.character_id:
    266                     self._scroll.RemoveNodes([node])
    267 
    268     def _get_filtered_guests(self, guests):
    269         cfg.eveowners.Prime([ guest.character_id for guest in guests ])
    270         filter_query = self._quick_filter.GetValue()
    271         if len(filter_query):
    272             filtered_guests = []
    273             for guest in guests:
    274                 if self._quick_filter.QuickFilter(rec=utillib.KeyVal(name=guest.name.lower())):
    275                     filtered_guests.append(guest)
    276                 blue.pyos.BeNice()
    277 
    278             return filtered_guests
    279         else:
    280             return guests
    281 
    282     def _get_guest_entries(self, guests):
    283         owner_ids = [ guest.character_id for guest in guests ]
    284         cfg.eveowners.Prime(owner_ids)
    285         entries = []
    286         for guest in guests:
    287             entries.append((guest.name.lower(), self._get_guest_entry(guest)))
    288             blue.pyos.BeNice()
    289 
    290         return SortListOfTuples(entries)
    291 
    292     @staticmethod
    293     def _get_guest_entry(guest):
    294         return GetFromClass(entryType=get_guest_entry_class(), data={'charID': guest.character_id,
    295          'info': cfg.eveowners.Get(guest.character_id),
    296          'label': guest.name,
    297          'corpID': guest.corporation_id,
    298          'allianceID': guest.alliance_id,
    299          'warFactionID': guest.war_faction_id})
    300 
    301     def _subscribe(self):
    302         guest_list_compact_mode_setting.on_change.connect(self._on_guest_list_compact_mode_changed)
    303         self._controller.on_guest_list_changed.connect(self._on_guest_list_changed)
    304         self._controller.on_loading_changed.connect(self._on_loading_changed)
    305 
    306     def _on_loading_changed(self, controller):
    307         self._update_scroll_loading()
    308 
    309     def _on_guest_list_changed(self, controller):
    310         uthread2.start_tasklet(self._update_scroll_content)
    311 
    312     def _on_guest_list_compact_mode_changed(self, value):
    313         uthread2.start_tasklet(self._update_scroll_content)
    314 
    315 
    316 class GuestList(object):
    317 
    318     def __init__(self, location_id, guests = None):
    319         self._location_id = location_id
    320         self._guests = {}
    321         self.on_guest_added = signals.Signal('{}.on_guest_added'.format(self.__class__.__name__))
    322         self.on_guest_removed = signals.Signal('{}.on_guest_removed'.format(self.__class__.__name__))
    323         if guests:
    324             for guest in guests:
    325                 self._guests[guest.character_id] = guest
    326 
    327     @property
    328     def guests(self):
    329         return self._guests.values()
    330 
    331     @property
    332     def guests_by_character_id(self):
    333         return self._guests.copy()
    334 
    335     @property
    336     def location_id(self):
    337         return self._location_id
    338 
    339     def add(self, guest):
    340         self._guests[guest.character_id] = guest
    341         self.on_guest_added(guest)
    342 
    343     def remove(self, character_id):
    344         guest = self._guests.pop(character_id)
    345         self.on_guest_removed(guest)
    346         return guest
    347 
    348     def discard(self, character_id):
    349         if character_id in self._guests:
    350             return self.remove(character_id)
    351 
    352 
    353 class Guest(object):
    354 
    355     def __init__(self, character_id, corporation_id, alliance_id, war_faction_id):
    356         self.character_id = character_id
    357         self.corporation_id = corporation_id
    358         self.alliance_id = alliance_id
    359         self.war_faction_id = war_faction_id
    360 
    361     @property
    362     def name(self):
    363         return cfg.eveowners.Get(self.character_id).name
    364 
    365     @property
    366     def owner_info(self):
    367         return cfg.eveowners.Get(self.character_id)
    368 
    369     def __eq__(self, other):
    370         return isinstance(other, Guest) and self.character_id == other.character_id and self.corporation_id == other.corporation_id and self.alliance_id == other.alliance_id and self.war_faction_id == other.war_faction_id
    371 
    372     def __ne__(self, other):
    373         return not self.__eq__(other)
    374 
    375     def __hash__(self):
    376         return self.character_id
    377 
    378 
    379 def get_guest_list_settings():
    380     menu = MenuData()
    381     menu.AddCheckbox(text=localization.GetByLabel('UI/Chat/ShowCompactMemberList'), setting=guest_list_compact_mode_setting)
    382     return menu
    383 
    384 
    385 def get_guest_entry_class():
    386     if guest_list_compact_mode_setting.is_enabled():
    387         return UserSimple
    388     else:
    389         return User
    390 
    391 
    392 def bisect_left(a, x, key):
    393     lo = 0
    394     hi = len(a)
    395     while lo < hi:
    396         mid = (lo + hi) // 2
    397         if key(a[mid]) < x:
    398             lo = mid + 1
    399         else:
    400             hi = mid
    401 
    402     return lo