Начинающий
- Статус
- Оффлайн
- Регистрация
- 3 Май 2025
- Сообщения
- 17
- Реакции
- 0
- Выберите загрузчик игры
- Fabric
сливаю гптшный островок, первая тема не судите строго
dynamic island:
package fun.rich.display.hud;
import com.google.common.base.Suppliers;
import fun.rich.utils.client.managers.api.draggable.AbstractDraggable;
import fun.rich.utils.display.atlasfont.msdf.MsdfFont;
import fun.rich.utils.display.color.ColorAssist;
import fun.rich.utils.display.shape.ShapeProperties;
import fun.rich.utils.display.systemrender.builders.Builder;
import fun.rich.utils.media.MediaUtils;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import org.joml.Matrix4f;
import java.awt.*;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.function.Supplier;
public class DynamicIsland extends AbstractDraggable {
private static final long SHOW_MS = 2200L;
public enum NotificationType {
SUCCESS(new Color(0x4AE54A)),
ERROR (new Color(0xFF4545)),
WARNING(new Color(0xFFB830)),
INFO (new Color(0x4AADFF)),
DEFAULT(new Color(0xA855F7));
public final Color color;
NotificationType(Color c) { this.color = c; }
}
private record Notif(String text, Color color) {}
private static volatile Notif active = null;
private static volatile long activeEnd = 0L;
private static final Queue<Notif> queue = new ArrayDeque<>();
public static void addNotification(String text, NotificationType type) {
Notif n = new Notif(text, type.color);
if (active == null || System.currentTimeMillis() >= activeEnd) {
active = n;
activeEnd = System.currentTimeMillis() + SHOW_MS;
} else {
queue.offer(n);
}
}
public static void notifyModuleToggle(String name, boolean enabled) {
addNotification(name + (enabled ? " включен" : " выключен"),
enabled ? NotificationType.SUCCESS : NotificationType.ERROR);
}
public static void strengthNotification() {
if (mc.player == null) return;
EquipmentSlot[] slots = {
EquipmentSlot.HEAD, EquipmentSlot.CHEST,
EquipmentSlot.LEGS, EquipmentSlot.FEET
};
String[] names = {"Шлем", "Нагрудник", "Поножи", "Ботинки"};
for (int i = 0; i < slots.length; i++) {
ItemStack s = mc.player.getEquippedStack(slots[i]);
if (s.isEmpty() || !s.isDamageable()) continue;
float pct = (float)(s.getMaxDamage() - s.getDamage()) / s.getMaxDamage();
if (pct <= 0.1f && pct > 0f)
addNotification(names[i] + " имеет низкую прочность!", NotificationType.WARNING);
}
}
private static final Supplier<MsdfFont> F_MEDIUM =
Suppliers.memoize(() -> MsdfFont.builder().atlas("medium").data("medium").build());
private static final Supplier<MsdfFont> F_REGULAR =
Suppliers.memoize(() -> MsdfFont.builder().atlas("regular").data("regular").build());
private static final Supplier<MsdfFont> F_ICON =
Suppliers.memoize(() -> MsdfFont.builder().atlas("clienticon1").data("clienticon1").build());
private static final Color BG = new Color(18, 18, 22, 220);
private static final Color BG_DARK = new Color(12, 12, 16, 220);
private float pillW = 80f;
private static final float PILL_H = 18f;
private final float[] waveH = {3f, 7f, 4f, 8f};
private final float[] waveDir = {1f, -1f, 1f, -1f};
private final float[] waveSpd = {0.18f, 0.22f, 0.15f, 0.20f};
public DynamicIsland() {
super("Watermark", 10, 10, 80, 18, false);
}
@Override
public void tick() {
if (active != null && System.currentTimeMillis() >= activeEnd) {
Notif next = queue.poll();
active = next;
activeEnd = next != null ? System.currentTimeMillis() + SHOW_MS : 0L;
}
for (int i = 0; i < 4; i++) {
waveH[i] += waveDir[i] * waveSpd[i];
if (waveH[i] >= 9f) { waveH[i] = 9f; waveDir[i] = -1f; waveSpd[i] = 0.12f + (float)(Math.random() * 0.18f); }
if (waveH[i] <= 2f) { waveH[i] = 2f; waveDir[i] = 1f; waveSpd[i] = 0.12f + (float)(Math.random() * 0.18f); }
}
}
@Override
public void drawDraggable(DrawContext context) {
MatrixStack ms = context.getMatrices();
Matrix4f mat = ms.peek().getPositionMatrix();
int sw = window.getScaledWidth();
boolean hasNotif = active != null && System.currentTimeMillis() < activeEnd;
MediaUtils.MediaInfo media = MediaUtils.getCurrentMedia();
boolean hasMedia = !hasNotif && media != null && !media.title.isEmpty();
String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
float timeW = F_MEDIUM.get().getWidth(time, 8.5f);
float targetW;
if (hasNotif) {
float tw = F_REGULAR.get().getWidth(active.text(), 8.5f);
targetW = 8f + 7f + 5f + tw + 8f;
} else if (hasMedia) {
String t = trimTitle(media.title);
float tw = F_REGULAR.get().getWidth(t, 8.5f);
targetW = 8f + tw + 6f + waveGroupW() + 8f;
} else {
float nameW = F_REGULAR.get().getWidth(getDisplayName(), 8.5f);
targetW = 8f + 10f + 1f + nameW + 8f;
}
pillW += (targetW - pillW) * 0.18f;
float pingGroupW = 4 * 3.3f + 3f;
float totalW = timeW + 6f + pillW + 6f + pingGroupW;
float startX = sw / 2f - totalW / 2f;
float pillX = startX + timeW + 6f;
float pillY = 5f;
float midY = pillY + PILL_H / 2f;
float ty = midY - 8.5f / 2f - 0.5f;
Builder.text().font(F_MEDIUM.get()).text(time).size(8.5f)
.thickness(0.04f).color(ColorAssist.getText()).build()
.render(mat, startX, ty);
blur.render(ShapeProperties.create(ms, pillX, pillY, pillW, PILL_H)
.round(PILL_H / 2f).quality(14).color(BG.getRGB()).build());
rectangle.render(ShapeProperties.create(ms, pillX, pillY, pillW, PILL_H)
.round(PILL_H / 2f).thickness(0.15f)
.outlineColor(new Color(255, 255, 255, 18).getRGB())
.color(BG.getRGB(), BG_DARK.getRGB(), BG_DARK.getRGB(), BG.getRGB()).build());
float cx = pillX + 8f;
if (hasNotif) {
float dotR = 3.5f;
rectangle.render(ShapeProperties.create(ms, cx, midY - dotR, dotR * 2f, dotR * 2f)
.round(dotR).color(active.color().getRGB()).build());
cx += dotR * 2f + 5f;
Builder.text().font(F_REGULAR.get()).text(active.text()).size(8.5f)
.thickness(0.04f).color(ColorAssist.getText()).build()
.render(mat, cx, ty);
} else if (hasMedia) {
String t = trimTitle(media.title);
Builder.text().font(F_REGULAR.get()).text(t).size(8.5f)
.thickness(0.04f).color(ColorAssist.getText()).build()
.render(mat, cx, ty);
cx += F_REGULAR.get().getWidth(t, 8.5f) + 6f;
renderWaveBars(ms, cx, midY);
} else {
renderPixelHeart(ms, cx, midY - 4.5f, ColorAssist.getClientColor());
cx += 11f;
Builder.text().font(F_REGULAR.get()).text(getDisplayName()).size(8.5f)
.thickness(0.04f).color(ColorAssist.getText()).build()
.render(mat, cx, ty - 1f);
}
float barsX = pillX + pillW + 6f;
float barsY = midY - 10f / 2f;
renderPing(ms, barsX, barsY);
setX((int) pillX); setY((int) pillY);
setWidth((int) pillW); setHeight((int) PILL_H);
}
private void renderWaveBars(MatrixStack ms, float x, float midY) {
int color = ColorAssist.getText(0.9f);
for (int i = 0; i < 4; i++) {
float bh = waveH[i];
rectangle.render(ShapeProperties.create(ms, x + i * 4f, midY - bh / 2f, 2.5f, bh)
.round(1f).color(color).build());
}
}
private float waveGroupW() { return 4 * 2.5f + 3 * 1.5f; }
private void renderPing(MatrixStack ms, float x, float y) {
int ping = getPlayerPing();
int lit = ping <= 50 ? 4 : ping <= 80 ? 3 : ping <= 120 ? 2 : ping <= 180 ? 1 : 0;
int[] maxH = {4, 6, 8, 10};
long t = System.currentTimeMillis();
for (int i = 0; i < 4; i++) {
float breath = (float)(Math.sin(t / 600.0 + i * 0.7) * 0.5 + 0.5);
float bh = i < lit ? maxH[i] * (0.6f + 0.4f * breath) : maxH[i] * (0.25f + 0.1f * breath);
int col = i < lit ? ColorAssist.getText(0.85f) : ColorAssist.getText(0.18f);
rectangle.render(ShapeProperties.create(ms, x + i * 3.3f, y + (10 - bh), 2.8f, bh)
.round(1f).color(col).build());
}
}
private static final int[][] HEART = {
{0,1,1,0,0,0,1,1,0},
{1,1,1,1,0,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{0,1,1,1,1,1,1,1,0},
{0,0,1,1,1,1,1,0,0},
{0,0,0,1,1,1,0,0,0},
{0,0,0,0,1,0,0,0,0},
};
private void renderPixelHeart(MatrixStack ms, float x, float y, int color) {
float px = 1.1f;
for (int row = 0; row < HEART.length; row++)
for (int col = 0; col < HEART[row].length; col++)
if (HEART[row][col] == 1)
rectangle.render(ShapeProperties.create(ms, x + col * px, y + row * px, px, px)
.round(0f).color(color).build());
}
private String trimTitle(String s) { return s.length() > 22 ? s.substring(0, 20) + "…" : s; }
private String getDisplayName() { return "Paradox"; }
private int getPlayerPing() {
if (mc.player == null || mc.getNetworkHandler() == null) return 0;
PlayerListEntry e = mc.getNetworkHandler().getPlayerListEntry(mc.player.getUuid());
return e != null ? e.getLatency() : 0;
}
}
mediautils:
package fun.rich.utils.media;
import by.bonenaut7.mediatransport4j.api.MediaSession;
import by.bonenaut7.mediatransport4j.api.MediaTransport;
import com.google.common.io.BaseEncoding;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.Map;
public class MediaUtils {
private static boolean initialized = false;
private static final ScheduledExecutorService EXECUTOR =
Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "MediaUtils");
t.setDaemon(true);
return t;
});
private static volatile MediaInfo mediaInfo = null;
private static volatile MediaSession activeSession = null;
public static volatile Color lastAlbumColor = Color.WHITE;
private static final Map<String, Identifier> textureCache = new ConcurrentHashMap<>();
private static String previousHash = "";
public static class MediaInfo {
public final String title;
public final String artist;
public final String textureHash;
public final boolean isPlaying;
public final long positionMs;
public final long durationMs;
public final long pollTimeMs;
public MediaInfo(String title, String artist, String textureHash,
boolean isPlaying, long positionMs, long durationMs, long pollTimeMs) {
this.title = title;
this.artist = artist;
this.textureHash = textureHash;
this.isPlaying = isPlaying;
this.positionMs = positionMs;
this.durationMs = durationMs;
this.pollTimeMs = pollTimeMs;
}
public long estimatedPositionMs() {
if (!isPlaying) return positionMs;
long elapsed = System.currentTimeMillis() - pollTimeMs;
return Math.min(positionMs + elapsed, durationMs > 0 ? durationMs : Long.MAX_VALUE);
}
public Identifier getTexture() {
return textureCache.get(textureHash);
}
}
public static void init() {
if (initialized) return;
MediaTransport.init();
initialized = true;
EXECUTOR.scheduleAtFixedRate(() -> {
try {
List<MediaSession> sessions = MediaTransport.getMediaSessions();
if (sessions != null && !sessions.isEmpty()) {
MediaSession session = sessions.get(0);
activeSession = session;
String hash = "";
if (session.hasThumbnail()) {
ByteBuffer buf = session.getThumbnail();
hash = hashBuffer(buf);
if (!hash.equals(previousHash)) {
Identifier old = textureCache.remove(previousHash);
if (old != null) {
MinecraftClient.getInstance().execute(() ->
MinecraftClient.getInstance().getTextureManager().destroyTexture(old));
}
final String finalHash = hash;
Identifier id = registerTexture(buf, finalHash);
if (id != null) textureCache.put(hash, id);
previousHash = hash;
}
}
boolean playing = session.isPlaying();
long pos = session.getPosition();
long dur = session.getDuration();
mediaInfo = new MediaInfo(
session.getTitle(), session.getArtist(), hash,
playing, pos, dur, System.currentTimeMillis()
);
} else {
activeSession = null;
clearCache();
mediaInfo = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
public static MediaInfo getCurrentMedia() {
if (!initialized) init();
return mediaInfo;
}
public static void playPause() {
MediaSession s = activeSession;
if (s != null) EXECUTOR.submit(() -> { try { s.togglePlay(); } catch (Exception ignored) {} });
}
public static void next() {
MediaSession s = activeSession;
if (s != null) EXECUTOR.submit(() -> { try { s.switchToNext(); } catch (Exception ignored) {} });
}
public static void previous() {
MediaSession s = activeSession;
if (s != null) EXECUTOR.submit(() -> { try { s.switchToPrevious(); } catch (Exception ignored) {} });
}
private static Identifier registerTexture(ByteBuffer buffer, String hash) {
try {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(buffer.array()));
if (img == null) return null;
lastAlbumColor = averageColor(img);
BufferedImage argb = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = argb.createGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(argb, "png", baos);
byte[] pngBytes = baos.toByteArray();
Identifier id = Identifier.of("rich", "media/" + hash);
MinecraftClient.getInstance().execute(() -> {
try {
NativeImage ni = NativeImage.read(new ByteArrayInputStream(pngBytes));
MinecraftClient.getInstance().getTextureManager()
.registerTexture(id, new NativeImageBackedTexture(ni));
} catch (Exception e) {
e.printStackTrace();
}
});
return id;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static void clearCache() {
textureCache.forEach((k, id) ->
MinecraftClient.getInstance().execute(() ->
MinecraftClient.getInstance().getTextureManager().destroyTexture(id)));
textureCache.clear();
previousHash = "";
}
private static String hashBuffer(ByteBuffer buffer) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(buffer.array());
return BaseEncoding.base16().lowerCase().encode(md.digest());
} catch (Exception e) {
return "";
}
}
private static Color averageColor(BufferedImage img) {
long r = 0, g = 0, b = 0;
int count = 0, step = 5;
for (int x = 0; x < img.getWidth(); x += step)
for (int y = 0; y < img.getHeight(); y += step) {
int rgb = img.getRGB(x, y);
r += (rgb >> 16) & 0xFF;
g += (rgb >> 8) & 0xFF;
b += rgb & 0xFF;
count++;
}
return count == 0 ? Color.WHITE
: new Color((int)(r/count), (int)(g/count), (int)(b/count));
}
}
Пожалуйста, авторизуйтесь для просмотра ссылки.
еще либку mediatransport4j-1.0.3 скачаете