Начинающий
- Статус
- Онлайн
- Регистрация
- 29 Июл 2025
- Сообщения
- 460
- Реакции
- 16
- Выберите загрузчик игры
- Fabric
Пожалуйста, авторизуйтесь для просмотра ссылки.
DynamicIsland:
package ru.flow.client.screens.widgets;
import lombok.Getter;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.BossBarHud;
import net.minecraft.client.gui.hud.ClientBossBar;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.client.texture.AbstractTexture;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import ru.flow.api.client.widget.AbstractWidget;
import ru.flow.api.media.MediaUtils;
import ru.flow.api.system.animation.Direction;
import ru.flow.api.system.animation.Easing;
import ru.flow.api.system.animation.implement.WaveAnimation;
import ru.flow.base.GlobalRefs;
import ru.flow.base.utils.font.Font;
import ru.flow.base.utils.font.FontsContext;
import ru.flow.base.utils.shape.states.ColorState;
import ru.flow.base.utils.shape.states.RadiusState;
import ru.flow.client.modules.render.Interface;
import java.awt.*;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
public class DynamicIsland extends AbstractWidget implements GlobalRefs {
private static final long DISPLAY_MS = 2000L;
private static final Color COLOR_GREEN = new Color(0xFF46FF00, true);
private static final Color COLOR_RED = new Color(0xFFFF4242, true);
private static final Color COLOR_DEFAULT = new Color(0xFFA442FF, true);
private static volatile String currentLabel = "Mytheria";
private static volatile Color currentStatusColor = COLOR_DEFAULT;
private static volatile NotificationType currentNotificationType = NotificationType.DEFAULT;
private static volatile long resetAt = 0L;
private final WaveAnimation[] waveAnimations = new WaveAnimation[4];
private final SimpleAnimation widthAnimation, heightAnimation;
public DynamicIsland() {
super("DynamicIsland", 10, 10, 128, 15, true);
widthAnimation = new SimpleAnimation(0f, 30);
heightAnimation = new SimpleAnimation(0f, 30);
for (int i = 0; i < 4; i++) {
waveAnimations[i] = new WaveAnimation(450 + (int)(Math.random() * 250), 10, Easing.BOTH_SINE);
}
}
public static void addNotification(String text, NotificationType type) {
currentLabel = text;
currentNotificationType = type;
currentStatusColor = type.color;
resetAt = System.currentTimeMillis() + DISPLAY_MS;
}
public static void notifyModuleToggle(String moduleName, boolean enabled) {
addNotification(moduleName + (enabled ? " включен" : " выключен"),
enabled ? NotificationType.SUCCESS : NotificationType.ERROR);
}
public static void strengthNotification() {
if (mc.player == null) return;
float criticalThreshold = 0.1f;
ItemStack[] armorSlots = {
mc.player.getEquippedStack(EquipmentSlot.FEET),
mc.player.getEquippedStack(EquipmentSlot.LEGS),
mc.player.getEquippedStack(EquipmentSlot.CHEST),
mc.player.getEquippedStack(EquipmentSlot.HEAD)
};
String[] slotNames = {"Ботинки", "Поножи", "Нагрудник", "Шлем"};
for (int i = 0; i < armorSlots.length; i++) {
ItemStack armor = armorSlots[i];
if (armor.isEmpty() || !armor.isDamageable()) continue;
int maxDurability = armor.getMaxDamage();
int currentDamage = armor.getDamage();
int durabilityLeft = maxDurability - currentDamage;
float durabilityPercent = (float) durabilityLeft / maxDurability;
if (durabilityPercent <= criticalThreshold && durabilityPercent > 0) {
addNotification(slotNames[i] + " имеет низкую прочность!", NotificationType.WARNING);
return;
}
}
}
@Override
public void drawWidget(DrawContext context) {
renderWatermark(context.getMatrices(), context.getScaledWindowWidth());
}
private void renderWatermark(MatrixStack ms, int screenWidth) {
if (resetAt != 0L && System.currentTimeMillis() >= resetAt) {
currentLabel = "Mytheria";
currentStatusColor = COLOR_DEFAULT;
currentNotificationType = NotificationType.DEFAULT;
resetAt = 0L;
}
RenderContext ctx = new RenderContext(screenWidth);
WatermarkContent content = determineContent();
animateDimensions(content);
float w = widthAnimation.getOutput();
float h = heightAnimation.getOutput();
float x = screenWidth / 2f - (w + ctx.timeWidth + 6f) / 2f + ctx.timeWidth - 6f;
float y = 7f;
renderTime(ms, ctx, x, y, h);
renderBackground(ms, x, y, w, h);
renderContent(ms, content, x, y, w, h);
renderConnectionIndicator(ms, ctx, x, y, w, h);
}
private WatermarkContent determineContent() {
if (System.currentTimeMillis() < resetAt) {
return createNotificationContent();
}
ClientBossBar bossBar = getPrimaryBossBar();
if (bossBar != null) return createBossBarContent(bossBar);
MediaUtils.MediaInfo media = MediaUtils.getCurrentMedia();
if (media != null && media.getTexture() != null) return createMediaContent(media);
return createDefaultContent();
}
private WatermarkContent createNotificationContent() {
Font font = FontsContext.REGULAR.getFont(9f);
float labelW = font.getFont().getWidth(currentLabel, 9f);
return new WatermarkContent(ContentType.NOTIFICATION, 11f + 3f + labelW + 7.5f,
Math.max(11f, 11f), currentLabel, false);
}
private WatermarkContent createBossBarContent(ClientBossBar bar) {
float[] size = calculateBossBarSize(bar);
return new WatermarkContent(ContentType.BOSS_BAR, size[0], size[1], bar, true);
}
private WatermarkContent createMediaContent(MediaUtils.MediaInfo media) {
Font font = FontsContext.REGULAR.getFont(9f);
float trackW = font.getFont().getWidth(media.title, 9f);
float waveW = 4 * 2.5f + 3 * 1f + 2f;
return new WatermarkContent(ContentType.MEDIA, 11f + 4f + trackW + 3f + waveW,
Math.max(11f, 11f), media, false);
}
private WatermarkContent createDefaultContent() {
Font font = FontsContext.REGULAR.getFont(9f);
float labelW = font.getFont().getWidth(currentLabel, 9f);
return new WatermarkContent(ContentType.DEFAULT, 11f + 4f + labelW + 2,
Math.max(11f, 11f), currentLabel, false);
}
private void renderContent(MatrixStack ms, WatermarkContent content, float x, float y, float w, float h) {
float cx = x + 6f;
float cy = y + (h - content.height) / 2f;
switch (content.type) {
case NOTIFICATION:
renderStatusBox(ms, cx, cy, content.height, currentStatusColor);
renderLabel(ms, currentLabel, cx + 13f, cy, content.height);
break;
case BOSS_BAR:
renderBossBar(ms, (ClientBossBar) content.data, cx + 2f, cy + 15f, content.width, 2f);
break;
case MEDIA:
renderMediaInfo(ms, (MediaUtils.MediaInfo) content.data, cx, cy, content.height);
break;
case DEFAULT:
renderStatusBox(ms, cx, cy, content.height, currentStatusColor);
renderLabel(ms, currentLabel, cx + 14f, cy, content.height);
break;
}
}
private void renderMediaInfo(MatrixStack ms, MediaUtils.MediaInfo media, float x, float y, float h) {
float size = 14f;
ru.flow.base.utils.render.RenderContext.drawTexture(ms, x - 3.5f, y + (h - size) / 2f - 1f,
size, size, 5.5f, media.getTexture(), Color.WHITE);
renderLabel(ms, media.title, x + 14f, y, h);
renderWaveAnimation(ms, x, y, h, media);
}
private void renderWaveAnimation(MatrixStack ms, float baseX, float baseY, float h, MediaUtils.MediaInfo media) {
Font font = FontsContext.REGULAR.getFont(9f);
float trackW = font.getFont().getWidth(media.title, 9f);
float textEndX = baseX + 11f + 4f + trackW + 4f;
float groupW = 4 * (2.5f + 1f) - 1f;
float centerX = textEndX + groupW / 2f;
float waveY = baseY + h + 5f;
for (WaveAnimation anim : waveAnimations) {
if (anim.isFinished(anim.getDirection())) {
if (anim.getDirection() == Direction.FORWARDS) {
anim.setDirection(Direction.BACKWARDS);
} else {
anim.setValue(4 + Math.random() * 2);
anim.setMs(350 + (int)(Math.random() * 400));
anim.setDirection(Direction.FORWARDS);
}
}
}
Color waveColor = desaturate(MediaUtils.lastAlbumColor, 0.5f);
for (int i = 0; i < 4; i++) {
double half = waveAnimations[i].getOutput();
float barX = centerX + (i - 2f) * 3.5f;
ru.flow.base.utils.render.RenderContext.drawRound(barX + 3, waveY - (float)half - 11,
2.3f, (float)(half * 2), 0.7f, waveColor, ms);
}
}
private void renderTime(MatrixStack ms, RenderContext ctx, float x, float y, float h) {
float timeX = x - ctx.timeWidth - 6f;
float timeY = y + (h - 9f) / 2f + 1f;
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.MEDIUM.getFont(9f),
ctx.timeString, timeX - 3, timeY, ctx.timeColor.getRGB(), ms.peek().getPositionMatrix());
}
private void renderBackground(MatrixStack ms, float x, float y, float w, float h) {
MediaUtils.MediaInfo media = MediaUtils.getCurrentMedia();
Color bg = media != null && media.getTexture() != null
? desaturate(MediaUtils.lastAlbumColor, 0.5f)
: new Color(0xFF464646, true);
float blur = media != null && media.getTexture() != null ? 55f : 25f;
ru.flow.base.utils.render.RenderContext.drawBlur(ms, x, y, w - 8, h - 2,
new ColorState(bg), new RadiusState(8.5), 1f, blur);
}
private void renderStatusBox(MatrixStack ms, float x, float y, float h, Color color) {
ru.flow.base.utils.render.RenderContext.drawRound(x - 1.5f, y + (h - 11f) / 2f - 1,
11f, 11f, 4.5f, color, ms);
}
private void renderLabel(MatrixStack ms, String text, float x, float y, float h) {
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.MEDIUM.getFont(9f),
text, x, y - 1f + (h - 9f) / 2f + 1.25f, Color.WHITE.getRGB(), ms.peek().getPositionMatrix());
}
private void renderBossBar(MatrixStack ms, ClientBossBar bar, float x, float y, float w, float h) {
BossBarInfo info = extractBossBarInfo(bar);
float timeBoxW = info.timeWidth + 10f;
float totalW = timeBoxW + info.labelWidth;
float cx = x + (w - totalW) / 2f;
float ty = y - 9f - 4f;
ru.flow.base.utils.render.RenderContext.drawRound(cx -5.5f, ty - 2f, timeBoxW, 11.5f, 3, COLOR_RED, ms);
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.MEDIUM.getFont(9f),
info.timeString, cx - 1, ty, Color.WHITE.getRGB(), ms.peek().getPositionMatrix());
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.MEDIUM.getFont(9f),
info.labelText, cx + timeBoxW - 3, ty, Color.WHITE.getRGB(), ms.peek().getPositionMatrix());
}
private void renderConnectionIndicator(MatrixStack ms, RenderContext ctx, float x, float y, float w, float h) {
float ix = x + w;
float iy = y + (h - 9f) / 2f - 3f;
if (Interface.getInstance().conIcon.isSelected("Обычная")) {
String symbol = mc.isInSingleplayer() ? "A" : "B";
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.ICF.getFont(12f),
symbol, ix, iy + 1.5f, ctx.timeColor.getRGB(), ms.peek().getPositionMatrix());
} else if (Interface.getInstance().conIcon.isSelected("Продвинутая")) {
if (mc.isInSingleplayer()) {
ru.flow.base.utils.render.RenderContext.drawText(FontsContext.ICF.getFont(12f),
"A", ix, iy + 1.5f, ctx.timeColor.getRGB(), ms.peek().getPositionMatrix());
} else {
renderConnectionQuality(ms, ix, iy + 2f);
}
}
}
private void renderConnectionQuality(MatrixStack ms, float x, float y) {
int ping = getPlayerPing();
int white = ping <= 60 ? 4 : ping <= 80 ? 3 : ping <= 100 ? 2 : ping <= 120 ? 1 : 0;
int[] heights = {4, 6, 8, 10};
for (int i = 0; i < 4; i++) {
Color c = i < white ? new Color(0xDDFFFFFF, true) : new Color(0x3DFFFFFF, true);
float bh = heights[i];
ru.flow.base.utils.render.RenderContext.drawRound(x + i * 3.3f, y + (10 - bh),
3f, bh, 0.2f, c, ms);
}
}
private void animateDimensions(WatermarkContent content) {
float w = content.width + 20f;
float h = Math.max(20f, content.height + 8f);
if (content.needsExtraHeight) h -= 1f;
if (Math.abs(widthAnimation.getOutput() - w) > 0.5f) widthAnimation.animateTo(w);
if (Math.abs(heightAnimation.getOutput() - h) > 0.5f) heightAnimation.animateTo(h);
}
private @Nullable ClientBossBar getPrimaryBossBar() {
InGameHud hud = mc.inGameHud;
BossBarHud bossBarHud = hud.getBossBarHud();
Map<UUID, ClientBossBar> bars = bossBarHud.bossBars;
return bars.isEmpty() ? null : bars.values().iterator().next();
}
private int getPlayerPing() {
if (mc.player == null || mc.getNetworkHandler() == null) return 0;
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(mc.player.getUuid());
return entry != null ? entry.getLatency() : 0;
}
private BossBarInfo extractBossBarInfo(ClientBossBar bar) {
String raw = bar.getName().getString();
Font font = FontsContext.REGULAR.getFont(9f);
Pattern pattern = Pattern.compile("(\\d+)(?!.*\\d)");
Matcher matcher = pattern.matcher(raw);
String time = matcher.find() ? matcher.group(1) + "s" : "??s";
String label = raw.replaceAll(",?\\s*до конца.*$", "")
.replaceAll("Телепортация.*", "Телепортация").trim();
return new BossBarInfo(time, label,
font.getFont().getWidth(time, 9f),
font.getFont().getWidth(label, 9f));
}
private float[] calculateBossBarSize(ClientBossBar bar) {
BossBarInfo info = extractBossBarInfo(bar);
return new float[] { info.timeWidth + 16f + info.labelWidth, 13f };
}
private Color desaturate(Color c, float factor) {
float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
hsb[1] *= factor;
return new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]));
}
public enum NotificationType {
SUCCESS(new Color(0xFF46FF00, true)),
ERROR(new Color(0xFFFF4242, true)),
WARNING(new Color(0xFFFFAA00, true)),
INFO(new Color(0xFF42A5FF, true)),
DEFAULT(new Color(0xFFA442FF, true));
public final Color color;
NotificationType(Color color) { this.color = color; }
}
private enum ContentType { NOTIFICATION, BOSS_BAR, MEDIA, DEFAULT }
private static class WatermarkContent {
final ContentType type;
final float width, height;
final Object data;
final boolean needsExtraHeight;
WatermarkContent(ContentType type, float width, float height, Object data, boolean needsExtraHeight) {
this.type = type;
this.width = width;
this.height = height;
this.data = data;
this.needsExtraHeight = needsExtraHeight;
}
}
private static class RenderContext {
final String timeString;
final float timeWidth;
final Color timeColor;
RenderContext(int screenWidth) {
Font font = FontsContext.REGULAR.getFont(9f);
timeString = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
timeWidth = font.getFont().getWidth(timeString, 9f);
timeColor = Color.WHITE;
}
}
private static class BossBarInfo {
final String timeString, labelText;
final float timeWidth, labelWidth;
BossBarInfo(String timeString, String labelText, float timeWidth, float labelWidth) {
this.timeString = timeString;
this.labelText = labelText;
this.timeWidth = timeWidth;
this.labelWidth = labelWidth;
}
}
private static class SimpleAnimation {
private float current, target;
private final long duration;
private long start;
private boolean running;
SimpleAnimation(float val, long duration) {
this.current = this.target = val;
this.duration = duration;
}
void animateTo(float newTarget) {
target = newTarget;
start = System.currentTimeMillis();
running = true;
}
float getOutput() {
if (!running) return current;
long elapsed = System.currentTimeMillis() - start;
float progress = Math.min(1f, (float) elapsed / duration);
current = current + progress * (target - current);
if (progress >= 1f) {
running = false;
current = target;
}
return current;
}
}
}
MediaUtils:
package ru.flow.api.media;
import by.bonenaut7.mediatransport4j.api.MediaSession;
import by.bonenaut7.mediatransport4j.api.MediaTransport;
import com.google.common.io.BaseEncoding;
import net.minecraft.client.texture.AbstractTexture;
import ru.flow.base.utils.render.Render2DUtil;
import ru.flow.base.utils.render.RenderContext;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class MediaUtils {
private static boolean initialized = false;
private static final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
private static volatile MediaInfo mediaInfo = null;
public static volatile Color lastAlbumColor = Color.WHITE;
private static final Map<String, AbstractTexture> textureCache = new ConcurrentHashMap<>();
private static String previousHash = "";
public static class MediaInfo {
public final String title;
public final String artist;
public final String textureHash;
public MediaInfo(String title, String artist, String textureHash) {
this.title = title;
this.artist = artist;
this.textureHash = textureHash;
}
public AbstractTexture getTexture() {
return textureCache.get(textureHash);
}
}
public static MediaInfo getCurrentMedia() {
if (!initialized) {
boolean bb = MediaTransport.init();
initialized = true;
s.scheduleAtFixedRate(() -> {
try {
List<MediaSession> sessions = MediaTransport.getMediaSessions();
if (sessions != null && !sessions.isEmpty()) {
final MediaSession mediaSession = sessions.get(0);
String hash = "";
AbstractTexture texture = null;
if (mediaSession.hasThumbnail()) {
ByteBuffer buf = mediaSession.getThumbnail();
hash = hashBuffer(buf);
if (!hash.equals(previousHash)) {
AbstractTexture old = textureCache.remove(previousHash);
if (old != null) old.close();
texture = convertTexture(buf);
if (texture != null) {
textureCache.put(hash, texture);
}
previousHash = hash;
}
}
mediaInfo = new MediaInfo(mediaSession.getTitle(), mediaSession.getArtist(), hash);
} else {
clearCache();
mediaInfo = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 50, TimeUnit.MILLISECONDS);
}
return mediaInfo;
}
public static Color getAverageColor(BufferedImage img) {
long r = 0, g = 0, b = 0;
int count = 0;
int 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++;
}
}
if (count == 0) return new Color(255, 255, 255);
return new Color((int)(r / count), (int)(g / count), (int)(b / count));
}
private static AbstractTexture convertTexture(ByteBuffer buffer) {
try {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(buffer.array()));
if (img != null) {
lastAlbumColor = getAverageColor(img);
return RenderContext.convert(img);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
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) {
e.printStackTrace();
return "";
}
}
private static void clearCache() {
textureCache.values().forEach(AbstractTexture::close);
textureCache.clear();
previousHash = "";
}
}