Skip to content

Instantly share code, notes, and snippets.

@Densamisten
Created October 5, 2024 23:39
Show Gist options
  • Save Densamisten/2b1bef5b20a77ac5f4faed47052e67f1 to your computer and use it in GitHub Desktop.
Save Densamisten/2b1bef5b20a77ac5f4faed47052e67f1 to your computer and use it in GitHub Desktop.
package exonihility.client.util.playersearch.screen;
import com.mojang.authlib.GameProfile;
import exonihility.client.util.playersearch.util.GuiEntityRenderer;
import exonihility.client.util.playersearch.util.PlayerApi;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.EditBoxWidget;
import net.minecraft.client.util.Clipboard;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class PlayerScreen extends Screen {
protected final Screen parent;
protected GameProfile profile;
private boolean firstLoad = true;
private static final Clipboard clipboardManager = new Clipboard();
private EditBoxWidget editBox;
private final PlayerEntity playerEntity; // Store the PlayerEntity
private ButtonWidget copyUuidButton;
private String previousText;
private String playerName = Text.translatable("gui.allyship.unknown").getString();
private UUID playerUuid;
private List<Map<String, Object>> nameHistory; // Store name history
private Identifier skinIdentifier;
private Identifier capeIdentifier;
private String model;
private NameHistoryListWidget nameHistoryListWidget;
public PlayerScreen(Screen parent, @Nullable GameProfile profile, PlayerEntity playerEntity) {
super(Text.translatable("playerSearch.title"));
this.parent = parent;
this.profile = profile;
this.playerEntity = playerEntity;
}
@Override
protected void init() {
super.init();
// Initialize the name history list widget
this.nameHistoryListWidget = new NameHistoryListWidget(this.client, this.width, this.height, 32, 20);
this.addSelectableChild(this.nameHistoryListWidget);
this.previousText = "";
this.editBox = this.addDrawableChild(new EditBoxWidget(this.textRenderer, this.width / 84, this.height / 84, 300, 20, Text.translatable("gui.player_search.search_hint"), Text.literal("")));
this.addDrawableChild(ButtonWidget.builder(Text.translatable("gui.allyship.button"), button -> {
String text = this.editBox.getText();
if (text.equals(this.previousText)) return; // Fix string comparison
Map<String, Object> request;
if (!text.isEmpty()) {
this.previousText = text;
// UUID detection and profile retrieval
if (text.length() == 32 || text.length() == 36) {
request = PlayerApi.getProfileFromUUID(text);
} else {
request = PlayerApi.getProfileFromName(text);
}
// Handle the request result
if (request == null) {
this.skinIdentifier = null;
this.capeIdentifier = null;
this.playerName = Text.translatable("gui.allyship.unknown").getString();
this.playerUuid = null;
this.nameHistory = null;
} else {
this.playerUuid = UUID.fromString(parseUuid(request.get("id").toString()));
this.playerName = request.get("name").toString();
this.profile = new GameProfile(this.playerUuid, this.playerName);
loadSkin(this.profile);
fetchCapeData(this.playerName); // Fetch the cape data
}
}
}).dimensions(330, this.height / 84, textRenderer.getWidth("Search") + 10, 20).build());
if (this.firstLoad && this.profile != null) {
this.playerUuid = this.profile.getId();
this.playerName = this.profile.getName();
this.editBox.setText(this.playerName);
this.previousText = this.playerName;
CompletableFuture.runAsync(() -> {
// Fetch the name history
this.nameHistory = PlayerApi.getNameHistory(this.playerUuid.toString());
// Ensure we are updating the UI on the main thread
MinecraftClient.getInstance().execute(() -> {
if (this.nameHistoryListWidget != null) {
this.nameHistoryListWidget.clearEntries(); // Clear the widget list before adding new entries
// Add new entries from the name history
for (Map<String, Object> historyEntry : nameHistory) {
this.nameHistoryListWidget.addNameHistoryEntry(historyEntry);
}
}
});
});
loadSkin(this.profile);
fetchCapeData(this.playerName);
this.firstLoad = false;
}
// Button to download the player's skin
this.addDrawableChild(ButtonWidget.builder(Text.translatable("gui.player_search.download.button"), button -> {
if (this.playerUuid == null) return;
downloadFile("https://crafatar.com/skins/" + this.playerUuid, this.playerUuid + ".png", "Skin");
}).dimensions(8, 136, textRenderer.getWidth("Grab Skin") + 8, 20).build());
// Add "Download Cape" button
this.addDrawableChild(ButtonWidget.builder(Text.translatable("gui.allyship.cape"), button -> {
if (this.capeIdentifier != null) {
downloadFile(this.capeIdentifier.toString(), this.playerUuid + "_cape.png", "Cape");
} else {
System.out.println("[PlayerScreen] No cape available for this player.");
}
}).dimensions(8, 160, textRenderer.getWidth("Download Cape") + 8, 20).build());
}
private void loadSkin(GameProfile profile) {
// Load the player's skin and texture information using Minecraft's built-in skin provider
MinecraftClient.getInstance().getSkinProvider().fetchSkinTextures(profile).thenAccept(skinTextures -> {
this.skinIdentifier = skinTextures.texture(); // Skin texture (identifier)
this.model = skinTextures.model() == SkinTextures.Model.SLIM ? "slim" : "default";
this.capeIdentifier = skinTextures.capeTexture();
});
}
// Fetch cape data using the cape API
private void fetchCapeData(String playerName) {
try {
String capeUrl = "https://api.capes.dev/load/" + playerName;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(capeUrl))
.build();
// Asynchronously fetch the cape data
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(this::handleCapeResponse)
.exceptionally(ex -> {
ex.printStackTrace();
System.out.println("[PlayerScreen] Failed to fetch cape data.");
return null;
});
} catch (Exception e) {
e.printStackTrace();
}
}
// Handle cape response from the API
private void handleCapeResponse(String json) {
Map<String, Object> capeData = PlayerApi.parseJsonToMap(json);
Map<String, Object> optifineCape = (Map<String, Object>) capeData.get("optifine");
if (optifineCape != null && Boolean.TRUE.equals(optifineCape.get("exists"))) {
String capeUrl = (String) optifineCape.get("capeUrl");
this.capeIdentifier = Identifier.of(capeUrl); // Store cape URL for downloading
System.out.println("[PlayerScreen] Cape found at URL: " + capeUrl);
} else {
System.out.println("[PlayerScreen] No cape found for player.");
}
}
// Download file (skin or cape) from the provided URL
private void downloadFile(String url, String fileName, String fileType) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
// Send request to download the file
client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
.thenAccept(response -> {
try (InputStream in = response.body()) {
Path dir = Paths.get(MinecraftClient.getInstance().runDirectory.getPath(), "downloads");
if (!Files.exists(dir)) {
Files.createDirectories(dir);
}
Path filePath = dir.resolve(fileName);
Files.copy(in, filePath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("[PlayerScreen] " + fileType + " downloaded successfully to: " + filePath);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[PlayerScreen] Error saving the " + fileType + " file.");
}
}).exceptionally(ex -> {
ex.printStackTrace();
System.out.println("[PlayerScreen] Error downloading the " + fileType + ".");
return null;
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
int playerX = this.width / 12;
int playerY = this.height / 2;
int playerSize = this.height / 5;
// Draw background box around player render area
context.fill(playerX - this.width / 14, playerY - playerSize * 2, playerX + this.width / 14, playerY + 10, 0x80000000);
// Render player model
GuiEntityRenderer.drawEntity(context, playerX, playerY, playerSize, 0, playerX - mouseX, playerY - playerSize * 1.8 - mouseY, this.playerEntity);
// Add "Copy UUID" button in the top right
this.copyUuidButton = ButtonWidget.builder(Text.literal("Copy UUID"), button -> {
if (this.playerUuid != null) {
String uuid = playerUuid.toString();
clipboardManager.setClipboard(GLFW.glfwGetCurrentContext(), uuid);
System.out.println("Copied UUID: " + uuid);
} else {
System.out.println("[PlayerScreen] No entity selected.");
}
}).dimensions(this.width / 14, 10, 20, 20).build();
addDrawableChild(this.copyUuidButton);
this.nameHistoryListWidget.render(context, mouseX, mouseY, delta);
}
private static String parseUuid(String uuid) {
StringBuilder stringBuilder = new StringBuilder(uuid);
if (uuid.length() == 32) {
stringBuilder.insert(8, '-');
stringBuilder.insert(13, '-');
stringBuilder.insert(18, '-');
stringBuilder.insert(23, '-');
}
return stringBuilder.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment