class LocalRecords(AppConfig): game_dependencies = ['trackmania'] app_dependencies = ['core.maniaplanet', 'core.trackmania'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.lock = asyncio.Lock() self.current_records = [] self.widget = None self.setting_chat_announce = Setting( 'chat_announce', 'Minimum index for chat announce', Setting.CAT_BEHAVIOUR, type=int, description= 'Minimum record index needed for public new record/recordchange announcement (0 for disable).', default=50) self.setting_record_limit = Setting( 'record_limit', 'Local Records limit', Setting.CAT_BEHAVIOUR, type=int, description= 'Limit for the amount of Local Records displayed in-game (0 for disable).', default=100) async def on_start(self): # Register commands await self.instance.command_manager.register( Command(command='records', target=self.show_records_list)) # Register signals self.instance.signal_manager.listen(mp_signals.map.map_begin, self.map_begin) self.instance.signal_manager.listen(tm_signals.finish, self.player_finish) self.instance.signal_manager.listen(mp_signals.player.player_connect, self.player_connect) await self.context.setting.register(self.setting_chat_announce, self.setting_record_limit) # Load initial data. await self.refresh_locals() await self.chat_current_record() self.widget = LocalRecordsWidget(self) await self.widget.display() async def refresh_locals(self): record_list = await LocalRecord.objects.execute( LocalRecord.select(LocalRecord, Player).join(Player).where( LocalRecord.map_id == self.instance.map_manager.current_map.get_id()).order_by( LocalRecord.score.asc())) self.current_records = list(record_list) async def show_records_list(self, player, data=None, **kwargs): """ Show record list view to player. :param player: Player instance. :param data: - :param kwargs: - :type player: pyplanet.apps.core.maniaplanet.models.Player :return: view instance or nothing when there are no records. """ if not len(self.current_records): message = '$i$f00There are currently no records on this map!' await self.instance.chat(message, player) return view = LocalRecordsListView(self) await view.display(player=player.login) return view async def map_begin(self, map): await self.refresh_locals() await asyncio.gather(self.chat_current_record(), self.widget.display()) async def player_connect(self, player, is_spectator, source, signal): await self.widget.display(player=player) async def player_finish(self, player, race_time, lap_time, cps, flow, raw, **kwargs): record_limit = await self.setting_record_limit.get_value() chat_announce = await self.setting_chat_announce.get_value() async with self.lock: current_records = [ x for x in self.current_records if x.player.login == player.login ] score = lap_time previous_index = None previous_time = None if len(current_records) > 0: current_record = current_records[0] if score > current_record.score: # No improvement, ignore return # Temporary make index + time local for the messages. previous_index = self.current_records.index(current_record) + 1 previous_time = current_record.score # If equal, only show message. if score == current_record.score and ( record_limit == 0 or previous_index <= record_limit): message = '$fff{}$z$s$0f3 equalled the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3.'.format( player.nickname, previous_index, times.format_time(score)) if chat_announce >= previous_index: return await self.instance.chat(message) elif chat_announce != 0: return await self.instance.chat(message, player.login) else: current_record = LocalRecord( map=self.instance.map_manager.current_map, player=player, ) # Set details (score + cps times). current_record.score = score current_record.checkpoints = ','.join([str(cp) for cp in cps]) async with self.lock: # Add to list when it's a new record! if current_record.get_id() is None: self.current_records.append(current_record) # (Re)sort the record list. self.current_records.sort(key=lambda x: x.score) new_index = self.current_records.index(current_record) + 1 # Prepare messages. if previous_index is not None and (record_limit == 0 or previous_index <= record_limit): if new_index < previous_index: message = '$fff{}$z$s$0f3 gained the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3 ($fff{}.$0f3 $fff-{}$0f3).'.format( player.nickname, new_index, times.format_time(score), previous_index, times.format_time((previous_time - score))) else: message = '$fff{}$z$s$0f3 improved the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3 ($fff-{}$0f3).'.format( player.nickname, new_index, times.format_time(score), times.format_time((previous_time - score))) else: message = '$fff{}$z$s$0f3 drove the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3.'.format( player.nickname, new_index, times.format_time(score)) # Save to database (but don't wait for it). asyncio.ensure_future(current_record.save()) coros = [self.widget.display()] if record_limit == 0 or new_index <= record_limit: if chat_announce >= new_index: coros.append(self.instance.chat(message)) elif chat_announce != 0: coros.append(self.instance.chat(message, player)) await asyncio.gather(*coros) async def chat_current_record(self): record_limit = await self.setting_record_limit.get_value() if record_limit > 0: records_amount = len(self.current_records[:record_limit]) else: records_amount = len(self.current_records) if records_amount > 0: first_record = self.current_records[0] message = '$0f3Current Local Record: $fff\uf017 {}$z$s$0f3 by $fff{}$z$s$0f3 ($fff{}$0f3 records)'.format( times.format_time(first_record.score), first_record.player.nickname, records_amount) calls = list() calls.append(self.instance.chat(message)) for player in self.instance.player_manager.online: calls.append(self.chat_personal_record(player, record_limit)) await self.instance.gbx.multicall(*calls) else: message = '$0f3There is no Local Record on this map yet.' await self.instance.chat(message) def chat_personal_record(self, player, record_limit): if record_limit > 0: records = self.current_records[:record_limit] else: records = self.current_records record = [x for x in records if x.player_id == player.get_id()] if len(record) > 0: message = '$0f3You currently hold the $fff{}.$0f3 Local Record: $fff\uf017 {}'.format( self.current_records.index(record[0]) + 1, times.format_time(record[0].score)) return self.instance.chat(message, player) else: message = '$0f3You don\'t have a Local Record on this map yet.' return self.instance.chat(message, player)