diff --git a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/RunicAltarBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/RunicAltarBlockEntity.java index 737b286019..d582891ea1 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/RunicAltarBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/RunicAltarBlockEntity.java @@ -424,8 +424,8 @@ public static void render(RunicAltarBlockEntity altar, GuiGraphics gui, Minecraf ms.popPose(); } - RenderHelper.renderProgressPie(gui, xc + radius + 32, yc - 8, progress, - recipe.assemble(altar.getItemHandler(), altar.getLevel().registryAccess())); + ItemStack output = recipe.assemble(altar.getItemHandler(), altar.getLevel().registryAccess()); + RenderHelper.renderProgressPie(gui, xc + radius + 32, yc - 8, progress, output); if (progress == 1F) { gui.drawString(mc.font, "+", xc + radius + 14, yc + 12, 0xFFFFFF, false); @@ -445,9 +445,13 @@ public static void render(RunicAltarBlockEntity altar, GuiGraphics gui, Minecraf } if (altar.recipeKeepTicks > 0 && altar.canAddLastRecipe()) { String s = I18n.get("botaniamisc.altarRefill0"); - gui.drawString(mc.font, s, xc - mc.font.width(s) / 2, yc + 10, 0xFFFFFF); + if (s != null && !s.isEmpty()) { + gui.drawString(mc.font, s, xc - mc.font.width(s) / 2, yc + 10, 0xFFFFFF); + } s = I18n.get("botaniamisc.altarRefill1"); - gui.drawString(mc.font, s, xc - mc.font.width(s) / 2, yc + 20, 0xFFFFFF); + if (s != null && !s.isEmpty()) { + gui.drawString(mc.font, s, xc - mc.font.width(s) / 2, yc + 20, 0xFFFFFF); + } } } } diff --git a/Xplat/src/main/java/vazkii/botania/common/crafting/recipe/HeadRecipe.java b/Xplat/src/main/java/vazkii/botania/common/crafting/recipe/HeadRecipe.java index cb2b4853e8..b5e69216a7 100644 --- a/Xplat/src/main/java/vazkii/botania/common/crafting/recipe/HeadRecipe.java +++ b/Xplat/src/main/java/vazkii/botania/common/crafting/recipe/HeadRecipe.java @@ -30,9 +30,11 @@ import vazkii.botania.common.crafting.BotaniaRecipeTypes; import vazkii.botania.common.crafting.RunicAltarRecipe; import vazkii.botania.common.helper.ItemNBTHelper; +import vazkii.botania.data.UuidNameProvider; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; public class HeadRecipe extends RunicAltarRecipe { @@ -70,7 +72,15 @@ public ItemStack assemble(@NotNull Container inv, @NotNull RegistryAccess regist for (int i = 0; i < inv.getContainerSize(); i++) { ItemStack ingr = inv.getItem(i); if (ingr.is(Items.NAME_TAG)) { - ItemNBTHelper.setString(stack, "SkullOwner", ingr.getHoverName().getString()); + String nameTagText = ingr.getHoverName().getString(); + if (isUUID(nameTagText)) { + // UUID + String PlayerName = UuidNameProvider.getPlayerNameFromUUID(nameTagText.replace("-", "")); + ItemNBTHelper.setString(stack, "SkullOwner", PlayerName); + } else { + // Player Name + ItemNBTHelper.setString(stack, "SkullOwner", ingr.getHoverName().getString()); + } break; } } @@ -109,4 +119,10 @@ public void toNetwork(@NotNull FriendlyByteBuf buf, @NotNull HeadRecipe recipe) } } + private boolean isUUID(String input) { + String uuidRegex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$|^[0-9a-fA-F]{32}$"; + Pattern pattern = Pattern.compile(uuidRegex); + return pattern.matcher(input).matches(); + } + } diff --git a/Xplat/src/main/java/vazkii/botania/data/UuidNameProvider.java b/Xplat/src/main/java/vazkii/botania/data/UuidNameProvider.java new file mode 100644 index 0000000000..9621c72bf1 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/data/UuidNameProvider.java @@ -0,0 +1,99 @@ +package vazkii.botania.data; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class UuidNameProvider { + + private static final Map cache = new ConcurrentHashMap<>(); + private static final Map failureTimes = new ConcurrentHashMap<>(); + private static final Duration CACHE_DURATION = Duration.ofMinutes(15); + private static final int MAX_FAILURES = 5; + + public static String getPlayerNameFromUUID(String uuid) { + CacheEntry entry = cache.get(uuid); + boolean isCacheExpired = entry == null || entry.isExpired(); + + if (!isCacheExpired) { + return entry.name; + } + + try { + URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + + JsonObject jsonObject = JsonParser.parseString(content.toString()).getAsJsonObject(); + if (jsonObject.has("name")) { + String name = jsonObject.get("name").getAsString(); + + cache.put(uuid, new CacheEntry(name)); + failureTimes.remove(uuid); // Reset failure count on success + + return name; + } else { + return SaveFailureData(uuid); + } + } catch (Exception e) { + e.printStackTrace(); + return SaveFailureData(uuid); + } + } + + @Nullable + private static String SaveFailureData(String uuid) { + recordFailureTime(uuid); + FailureEntry failureEntry = failureTimes.getOrDefault(uuid, new FailureEntry()); + if (failureEntry.failures >= MAX_FAILURES) { + cache.put(uuid, new CacheEntry(uuid)); // Store UUID as Name + failureTimes.remove(uuid); // Reset failure count after storing UUID as Name + return uuid; + } + return null; + } + + private static void recordFailureTime(String uuid) { + FailureEntry failureEntry = failureTimes.computeIfAbsent(uuid, k -> new FailureEntry()); + failureEntry.failures++; + failureEntry.lastFailureTime = Instant.now(); + } + + private static class CacheEntry { + final String name; + final Instant creationTime; + + CacheEntry(String name) { + this.name = name; + this.creationTime = Instant.now(); + } + + boolean isExpired() { + return Instant.now().isAfter(creationTime.plus(CACHE_DURATION)); + } + } + + private static class FailureEntry { + int failures = 0; + Instant lastFailureTime = Instant.now(); + } +}