Гайд поможет реализовать показ текущий воспроизводимый трек в худе клиента. Это может включать название песни, исполнителя, обложку альбома, прогресс воспроизведения и элементы управления, такие как пауза, воспроизведение и переключение треков. Для реализации используется библиотека, позволяющая получать информацию о системных медиа сессиях.
Библиотека
Для получения информации о треках используется библиотека
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Она предоставляет список активных медиасессий и позволяет управлять воспроизведением: пауза, продолжение, переключение треков. (для корректной работы ещё потребуется либка
Пожалуйста, авторизуйтесь для просмотра ссылки.
)Создание модуля
Создаём модуль, который будет хранить текущий трек, медиасессию и текстуру обложки. Также понадобится отдельный поток для асинхронного обновления, чтобы не блокировать игровой поток.
поля и поток:
private volatile MediaInfo currentTrack;
private volatile IMediaSession currentSession;
private long lastMediaUpdate;
private final ExecutorService mediaExecutor =
Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "media-thread");
t.setDaemon(true);
return t;
});
Обновление информации о треке
Информацию о музыке удобно обновлять в евенттике. Делать это каждый тик не нужно - достаточно раз в 1-2 секунды.
обновление:
@EventHandler
public void onTick(TickEvent e) {
long now = System.currentTimeMillis();
if (now - lastMediaUpdate < 1500) return;
lastMediaUpdate = now;
updateMediaAsync();
}
Асинхронное получение медиасессии:
получение сессии:
private void updateMediaAsync() {
mediaExecutor.execute(() -> {
List<IMediaSession> sessions = MediaPlayerInfo.Instance.getMediaSessions();
if (sessions == null || sessions.isEmpty()) {
currentTrack = null;
currentSession = null;
return;
}
MediaInfo found = null;
IMediaSession foundSession = null;
for (IMediaSession session : sessions) {
if (session == null) continue;
MediaInfo media;
try {
media = session.getMedia();
} catch (Exception e) {
continue;
}
if (media == null) continue;
if (media.getPlaying()) {
found = media;
foundSession = session;
break;
}
if (found == null) {
found = media;
foundSession = session;
}
}
currentTrack = found;
currentSession = foundSession;
});
}
Загрузка обложки трека
Если плеер отдаёт обложку, её можно превратить в текстуру Minecraft.
отрисовка обложки трека:
private ResourceLocation artworkTexture;
private void applyArtwork(byte[] artBytes) {
try {
NativeImage img = NativeImage.read(new ByteArrayInputStream(artBytes));
DynamicTexture texture = new DynamicTexture(img);
artworkTexture = Minecraft.getInstance()
.getTextureManager()
.getDynamicTextureLocation("media_art", texture);
} catch (Exception ignored) {}
}
Отрисовка в HUD
Рендер худа у меня выполняется в событии HudRenderEvent, там и буду рисовать панельку. Здесь выводится обложка, название трека, исполнитель и прогресс. На примере всё будет прям в методе события, но желательно вынести в отдельный метод.
(методы и аргументация методов будет отличатся, база своя)
код отрисовки:
@EventHandler
public void onHudRender(HudRenderEvent e) {
if (currentTrack == null) return;
Minecraft mc = Minecraft.getInstance();
String title = currentTrack.getTitle().isEmpty()
? "Unknown"
: currentTrack.getTitle();
String artist = currentTrack.getArtist().isEmpty()
? "Unknown"
: currentTrack.getArtist();
long position = currentTrack.getPosition();
long duration = currentTrack.getDuration();
float progress = duration > 0 ? (float) position / duration : 0f;
float x = mc.getMainWindow().getScaledWidth() / 2f - 60;
float y = 20;
RenderUtils.drawRoundedRect(x, y, 120, 40, 6f, 0xAA000000, 0xAA000000);
if (artworkTexture != null) {
RenderUtils.drawImage(artworkTexture, x + 4, y + 4, 32, 32, 0xFFFFFFFF);
}
FontRenderer font = FontManager.getInstance().getDefaultFont();
font.drawString(title, x + 40, y + 8, 0xFFFFFFFF);
font.drawString(artist, x + 40, y + 20, 0xFFAAAAAA);
RenderUtils.drawRoundedRect(
x + 4,
y + 36,
112 * progress,
2,
1,
0xFFFFFFFF,
0xFFFFFFFF
);
}
Кнопки управления
Можно добавить управление воспроизведением: предыдущий трек, пауза/воспроизведение и следующий.
обработка клика:
private void handleMediaClick(float mouseX, float mouseY) {
if (currentSession == null || currentTrack == null) return;
if (isInside(mouseX, mouseY, prevX, prevY, prevWidth)) {
mediaExecutor.execute(currentSession::previous);
} else if (isInside(mouseX, mouseY, playX, playY, playWidth)) {
mediaExecutor.execute(() -> {
if (currentTrack.getPlaying()) {
currentSession.pause();
} else {
currentSession.play();
}
});
} else if (isInside(mouseX, mouseY, nextX, nextY, nextWidth)) {
mediaExecutor.execute(currentSession::next);
}
}
Форматирование времени трека
форматирование:
private String formatTime(long seconds) {
if (seconds <= 0) return "0:00";
long minutes = seconds / 60;
long secs = seconds % 60;
return String.format("%d:%02d", minutes, secs);
}