package net.minecraft.client.gui.screens.social; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.client.multiplayer.chat.ChatLog; import net.minecraft.client.multiplayer.chat.LoggedChatMessage.Player; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class SocialInteractionsPlayerList extends ContainerObjectSelectionList { private final SocialInteractionsScreen socialInteractionsScreen; private final List players = Lists.newArrayList(); @Nullable private String filter; public SocialInteractionsPlayerList(SocialInteractionsScreen socialInteractionsScreen, Minecraft minecraft, int width, int height, int y, int itemHeight) { super(minecraft, width, height, y, itemHeight); this.socialInteractionsScreen = socialInteractionsScreen; } @Override protected void renderListBackground(GuiGraphics guiGraphics) { } @Override protected void renderListSeparators(GuiGraphics guiGraphics) { } @Override protected void enableScissor(GuiGraphics guiGraphics) { guiGraphics.enableScissor(this.getX(), this.getY() + 4, this.getRight(), this.getBottom()); } public void updatePlayerList(Collection ids, double scrollAmount, boolean addChatLogPlayers) { Map map = new HashMap(); this.addOnlinePlayers(ids, map); this.updatePlayersFromChatLog(map, addChatLogPlayers); this.updateFiltersAndScroll(map.values(), scrollAmount); } private void addOnlinePlayers(Collection ids, Map playerMap) { ClientPacketListener clientPacketListener = this.minecraft.player.connection; for (UUID uUID : ids) { PlayerInfo playerInfo = clientPacketListener.getPlayerInfo(uUID); if (playerInfo != null) { boolean bl = playerInfo.hasVerifiableChat(); playerMap.put(uUID, new PlayerEntry(this.minecraft, this.socialInteractionsScreen, uUID, playerInfo.getProfile().getName(), playerInfo::getSkin, bl)); } } } private void updatePlayersFromChatLog(Map playerMap, boolean addPlayers) { for (GameProfile gameProfile : collectProfilesFromChatLog(this.minecraft.getReportingContext().chatLog())) { PlayerEntry playerEntry; if (addPlayers) { playerEntry = (PlayerEntry)playerMap.computeIfAbsent( gameProfile.getId(), uUID -> { PlayerEntry playerEntryx = new PlayerEntry( this.minecraft, this.socialInteractionsScreen, gameProfile.getId(), gameProfile.getName(), this.minecraft.getSkinManager().lookupInsecure(gameProfile), true ); playerEntryx.setRemoved(true); return playerEntryx; } ); } else { playerEntry = (PlayerEntry)playerMap.get(gameProfile.getId()); if (playerEntry == null) { continue; } } playerEntry.setHasRecentMessages(true); } } private static Collection collectProfilesFromChatLog(ChatLog chatLog) { Set set = new ObjectLinkedOpenHashSet<>(); for (int i = chatLog.end(); i >= chatLog.start(); i--) { if (chatLog.lookup(i) instanceof Player player && player.message().hasSignature()) { set.add(player.profile()); } } return set; } private void sortPlayerEntries() { this.players.sort(Comparator.comparing(playerEntry -> { if (this.minecraft.isLocalPlayer(playerEntry.getPlayerId())) { return 0; } else if (this.minecraft.getReportingContext().hasDraftReportFor(playerEntry.getPlayerId())) { return 1; } else if (playerEntry.getPlayerId().version() == 2) { return 4; } else { return playerEntry.hasRecentMessages() ? 2 : 3; } }).thenComparing(playerEntry -> { if (!playerEntry.getPlayerName().isBlank()) { int i = playerEntry.getPlayerName().codePointAt(0); if (i == 95 || i >= 97 && i <= 122 || i >= 65 && i <= 90 || i >= 48 && i <= 57) { return 0; } } return 1; }).thenComparing(PlayerEntry::getPlayerName, String::compareToIgnoreCase)); } private void updateFiltersAndScroll(Collection players, double scrollAmount) { this.players.clear(); this.players.addAll(players); this.sortPlayerEntries(); this.updateFilteredPlayers(); this.replaceEntries(this.players); this.setScrollAmount(scrollAmount); } private void updateFilteredPlayers() { if (this.filter != null) { this.players.removeIf(playerEntry -> !playerEntry.getPlayerName().toLowerCase(Locale.ROOT).contains(this.filter)); this.replaceEntries(this.players); } } public void setFilter(String filter) { this.filter = filter; } public boolean isEmpty() { return this.players.isEmpty(); } public void addPlayer(PlayerInfo playerInfo, SocialInteractionsScreen.Page page) { UUID uUID = playerInfo.getProfile().getId(); for (PlayerEntry playerEntry : this.players) { if (playerEntry.getPlayerId().equals(uUID)) { playerEntry.setRemoved(false); return; } } if ((page == SocialInteractionsScreen.Page.ALL || this.minecraft.getPlayerSocialManager().shouldHideMessageFrom(uUID)) && (Strings.isNullOrEmpty(this.filter) || playerInfo.getProfile().getName().toLowerCase(Locale.ROOT).contains(this.filter))) { boolean bl = playerInfo.hasVerifiableChat(); PlayerEntry playerEntryx = new PlayerEntry( this.minecraft, this.socialInteractionsScreen, playerInfo.getProfile().getId(), playerInfo.getProfile().getName(), playerInfo::getSkin, bl ); this.addEntry(playerEntryx); this.players.add(playerEntryx); } } public void removePlayer(UUID id) { for (PlayerEntry playerEntry : this.players) { if (playerEntry.getPlayerId().equals(id)) { playerEntry.setRemoved(true); return; } } } }