diff --git a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java index ba3be7d..89583fb 100644 --- a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java +++ b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java @@ -11,9 +11,15 @@ import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; import net.minecraft.client.KeyMapping; import net.minecraft.resources.Identifier; import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class CitrusDevClient implements ClientModInitializer { + public static final Logger logger = LoggerFactory.getLogger(CitrusDevClient.class); + private static final boolean debug = true; + public static final String API_URL = "https://citrus-api.winniepat.de"; private static KeyMapping openScreenKey; @@ -59,4 +65,9 @@ public class CitrusDevClient implements ClientModInitializer { }); } + public static void debugLog(String message) { + if (debug) { + logger.info("[Citrus] [DEBUG] {}", message); + } + } } \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/client/Verifier.java b/src/main/java/de/fullrisk/citrusDev/client/Verifier.java index 0e0bcc4..6442f78 100644 --- a/src/main/java/de/fullrisk/citrusDev/client/Verifier.java +++ b/src/main/java/de/fullrisk/citrusDev/client/Verifier.java @@ -32,8 +32,7 @@ public class Verifier { HTTP.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenAccept(response -> { - System.out.println("Status: " + response.statusCode()); - System.out.println("Body: " + response.body()); + CitrusDevClient.debugLog("Verification Response - Status: " + response.statusCode() + ", Body: " + response.body()); }) .exceptionally(error -> { error.printStackTrace(); diff --git a/src/main/java/de/fullrisk/citrusDev/helper/RankHelper.java b/src/main/java/de/fullrisk/citrusDev/helper/RankHelper.java new file mode 100644 index 0000000..cc9f24d --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/helper/RankHelper.java @@ -0,0 +1,89 @@ +package de.fullrisk.citrusDev.helper; + +import de.fullrisk.citrusDev.client.CitrusDevClient; +import net.minecraft.client.Minecraft; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +public class RankHelper { + public static final ConcurrentHashMap rankCache = new ConcurrentHashMap<>(); + private static final HttpClient client = HttpClient.newHttpClient(); + + public static String getRankIcon(UUID uuid) { + String rank = rankCache.get(uuid); + + if (rank == null) { + rankCache.put(uuid, "loading"); + // TODO fetch rank + return " "; + } + + if (rank.equals("loading")) return " "; + + // TODO Icons + return switch (rank) { + case "owner" -> "\uE001"; + case "admin" -> "\uE002"; + case "vip" -> "\uE003"; + case "dev" -> "\uE004"; + case "bughunter" -> "\uE005"; + case "default" -> "\uE099"; + default -> ""; + }; + } + + private static String RANK_URL = CitrusDevClient.API_URL + "/api/player/%s/rank"; + + private static void fetchRank(UUID uuid) { + CompletableFuture.runAsync(() -> { + try { + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(RANK_URL.formatted(uuid))) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + String rawRank = response.body().trim().toLowerCase(); + + if (rawRank.equals(rankCache.get(uuid))) { + return; // No change + } + + CitrusDevClient.debugLog("Received Rank Content: '" + rawRank + "'"); + + rankCache.put(uuid, rawRank); + + Minecraft.getInstance().execute(() -> { + var world = Minecraft.getInstance().level; + if (world != null) { + var player = world.getPlayerByUUID(uuid); + if (player != null) { + player.setCustomNameVisible(player.isCustomNameVisible()); + CitrusDevClient.debugLog("Refreshed nametag for: " + player.getName().getString()); + } + } + }); + } else { + rankCache.put(uuid, "none"); + } + } catch (Exception e) { + CitrusDevClient.debugLog("Failed to fetch rank: " + e.getMessage()); + rankCache.put(uuid, "none"); + } + }); + } + + public static void refreshRankIfChanged() { + Minecraft client = Minecraft.getInstance(); + if (client.getConnection() == null) return; + + client.getConnection().getOnlinePlayers().forEach(playerInfo -> fetchRank(playerInfo.getProfile().id())); + } +} diff --git a/src/main/java/de/fullrisk/citrusDev/mixin/PlayerNametagMixin.java b/src/main/java/de/fullrisk/citrusDev/mixin/PlayerNametagMixin.java new file mode 100644 index 0000000..af4e725 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/mixin/PlayerNametagMixin.java @@ -0,0 +1,43 @@ +package de.fullrisk.citrusDev.mixin; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import de.fullrisk.citrusDev.helper.RankHelper; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.UUID; + +@Mixin(EntityRenderer.class) +public abstract class PlayerNametagMixin { + + @ModifyReturnValue(method = "getNameTag", at = @At("RETURN")) + private Component citrusDev$addRankIcon(Component original, T entity) { + if (entity instanceof Player player) { + UUID uuid = player.getUUID(); + + // TODO Check if player is online with client + + String iconChar = RankHelper.getRankIcon(uuid); + if (!iconChar.trim().isEmpty()) { + MutableComponent icon = Component.literal(iconChar).setStyle(Style.EMPTY.withFont( + new FontDescription.Resource(Identifier.fromNamespaceAndPath("citrus-dev", "icon_font")) + )); + + MutableComponent nameWithSpace = Component.literal(" ") + .append(original) + .setStyle(Style.EMPTY.withFont(new FontDescription.Resource(Identifier.fromNamespaceAndPath("minecraft", "default")))); + + return icon.append(nameWithSpace); + } + } + return original; + } +} diff --git a/src/main/resources/assets/citrus-dev/font/icon_font.json b/src/main/resources/assets/citrus-dev/font/icon_font.json new file mode 100644 index 0000000..2c456d0 --- /dev/null +++ b/src/main/resources/assets/citrus-dev/font/icon_font.json @@ -0,0 +1,46 @@ +{ + "providers": [ + { + "type": "bitmap", + "file": "citrus-dev:ranks/owner.png", + "ascent": 7, + "height": 8, + "chars": ["\uE001"] + }, + { + "type": "bitmap", + "file": "citrus-dev:ranks/admin.png", + "ascent": 7, + "height": 8, + "chars": ["\uE002"] + }, + { + "type": "bitmap", + "file": "citrus-dev:ranks/vip.png", + "ascent": 7, + "height": 8, + "chars": ["\uE003"] + }, + { + "type": "bitmap", + "file": "citrus-dev:ranks/dev.png", + "ascent": 7, + "height": 8, + "chars": ["\uE004"] + }, + { + "type": "bitmap", + "file": "citrus-dev:ranks/bughunter.png", + "ascent": 7, + "height": 8, + "chars": ["\uE005"] + }, + { + "type": "bitmap", + "file": "citrus-dev:ranks/default.png", + "ascent": 7, + "height": 8, + "chars": ["\uE099"] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/admin.png b/src/main/resources/assets/citrus-dev/textures/ranks/admin.png new file mode 100644 index 0000000..42f4651 Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/admin.png differ diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/bughunter.png b/src/main/resources/assets/citrus-dev/textures/ranks/bughunter.png new file mode 100644 index 0000000..44819dd Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/bughunter.png differ diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/default.png b/src/main/resources/assets/citrus-dev/textures/ranks/default.png new file mode 100644 index 0000000..7b89a39 Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/default.png differ diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/dev.png b/src/main/resources/assets/citrus-dev/textures/ranks/dev.png new file mode 100644 index 0000000..9e181f5 Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/dev.png differ diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/owner.png b/src/main/resources/assets/citrus-dev/textures/ranks/owner.png new file mode 100644 index 0000000..c17da3e Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/owner.png differ diff --git a/src/main/resources/assets/citrus-dev/textures/ranks/vip.png b/src/main/resources/assets/citrus-dev/textures/ranks/vip.png new file mode 100644 index 0000000..fc7af67 Binary files /dev/null and b/src/main/resources/assets/citrus-dev/textures/ranks/vip.png differ diff --git a/src/main/resources/citrus-dev.mixins.json b/src/main/resources/citrus-dev.mixins.json index 3b2348d..c98d729 100644 --- a/src/main/resources/citrus-dev.mixins.json +++ b/src/main/resources/citrus-dev.mixins.json @@ -8,7 +8,8 @@ "client": [ "SliderComponentMixin", "MouseHandlerMixin", - "LightmapRenderStateExtractorMixin" + "LightmapRenderStateExtractorMixin", + "PlayerNametagMixin" ], "injectors": { "defaultRequire": 1