From a9f0dd8ff435fd4f3d4d9b482a78fa24d840548f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 31 Mar 2026 04:55:12 +0700 Subject: [PATCH 1/2] Update to use `platformtools.webp` --- .../network/objects/EmoteIconPacket.java | 23 ++++++++-------- .../kosmx/emotes/common/tools/MathHelper.java | 27 +++---------------- .../server/serializer/EmoteSerializer.java | 7 +++-- .../emotes/server/serializer/EmoteWriter.java | 7 +++-- .../serializer/UniversalEmoteSerializer.java | 6 ++--- gradle.properties | 2 +- .../github/kosmx/emotes/main/EmoteHolder.java | 26 +++++++++--------- 7 files changed, 40 insertions(+), 58 deletions(-) diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java index e266e5593..bc217fc4d 100644 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java +++ b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java @@ -2,9 +2,11 @@ import io.github.kosmx.emotes.common.network.PacketConfig; import io.github.kosmx.emotes.common.network.PacketTask; +import io.github.kosmx.emotes.common.tools.MathHelper; import io.netty.buffer.ByteBuf; +import org.redlance.platformtools.webp.decoder.DecodedImage; -import java.nio.ByteBuffer; +import java.io.IOException; public class EmoteIconPacket extends AbstractNetworkPacket{ @Override @@ -18,28 +20,27 @@ public byte getVer() { } @Override - public void read(ByteBuf byteBuf, NetData config, byte version) { + public void read(ByteBuf byteBuf, NetData config, byte version) throws IOException { int size = byteBuf.readInt(); if (size <= 0) return; - ByteBuffer buffer = ByteBuffer.allocate(size); - byteBuf.readBytes(buffer); - buffer.flip(); - config.extraData.put("iconData", buffer.asReadOnlyBuffer()); + byte[] iconData = MathHelper.readBytes(byteBuf, size); + config.extraData.put("iconData", DecodedImage.fromPng(iconData)); } @Override - public void write(ByteBuf byteBuf, NetData config, byte version) { + public void write(ByteBuf byteBuf, NetData config, byte version) throws IOException { assert config.emoteData != null; - ByteBuffer iconData = config.emoteData.data().getBinary("iconData"); - if (iconData == null || !iconData.hasRemaining()) { + DecodedImage iconData = config.emoteData.data().getImage("iconData"); + if (iconData == null) { byteBuf.writeInt(0); return; } - byteBuf.writeInt(iconData.remaining()); - byteBuf.writeBytes(iconData.duplicate()); + byte[] pngData = iconData.toPng(); + byteBuf.writeInt(pngData.length); + byteBuf.writeBytes(pngData); } @Override diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/tools/MathHelper.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/tools/MathHelper.java index b21b7a6fe..0a15d5278 100644 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/tools/MathHelper.java +++ b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/tools/MathHelper.java @@ -2,32 +2,13 @@ import io.netty.buffer.ByteBuf; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - public class MathHelper { - public static ByteBuffer readFromIStream(InputStream stream) throws IOException { - return ByteBuffer.wrap(stream.readAllBytes()); - } - - /** - * If {@link ByteBuffer} is wrapped, it is safe to get the array - * but if is direct manual read is required. - * @param byteBuffer get the bytes from - * @return the byte array - */ - public static byte[] safeGetBytesFromBuffer(ByteBuffer byteBuffer) { - if (byteBuffer.isDirect() || byteBuffer.isReadOnly()) { - byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - return bytes; - } - else return byteBuffer.array(); + public static byte[] readBytes(ByteBuf buf) { + return readBytes(buf, buf.readableBytes()); } - public static byte[] readBytes(ByteBuf buf) { - byte[] bytes = new byte[buf.readableBytes()]; + public static byte[] readBytes(ByteBuf buf, int size) { + byte[] bytes = new byte[size]; buf.readBytes(bytes); return bytes; } diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteSerializer.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteSerializer.java index 558e0e487..8d8bb32aa 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteSerializer.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteSerializer.java @@ -3,15 +3,14 @@ import com.zigythebird.playeranimcore.animation.Animation; import com.zigythebird.playeranimcore.animation.ExtraAnimationData; import io.github.kosmx.emotes.common.CommonData; -import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; import net.raphimc.noteblocklib.NoteBlockLib; import net.raphimc.noteblocklib.format.SongFormat; import net.raphimc.noteblocklib.model.song.Song; +import org.redlance.platformtools.webp.decoder.DecodedImage; import java.io.File; import java.io.InputStream; -import java.nio.ByteBuffer; import java.nio.file.FileVisitOption; import java.nio.file.Files; import java.nio.file.Path; @@ -70,10 +69,10 @@ public static Map serializeExternalEmote(Path file, String fo Path icon = file.getParent().resolve(baseFileName + ".png"); if (Files.isRegularFile(icon)) { try (InputStream iconStream = Files.newInputStream(icon)) { - final ByteBuffer byteBuffer = MathHelper.readFromIStream(iconStream); + final DecodedImage image = DecodedImage.fromPng(iconStream); for (Animation emote : emotes.values()) { // Avoid lambda - emote.data().put("iconData", byteBuffer); + emote.data().put("iconData", image); } } catch (Throwable th) { CommonData.LOGGER.warn("Error while reading icon: {}", icon.getFileName(), th); diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteWriter.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteWriter.java index 6fcdd9e78..7a26c8f9a 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteWriter.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/EmoteWriter.java @@ -1,13 +1,12 @@ package io.github.kosmx.emotes.server.serializer; import com.zigythebird.playeranimcore.animation.Animation; -import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.server.serializer.type.IWriter; import net.raphimc.noteblocklib.NoteBlockLib; import net.raphimc.noteblocklib.model.song.Song; +import org.redlance.platformtools.webp.decoder.DecodedImage; import java.io.OutputStream; -import java.nio.ByteBuffer; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; @@ -30,12 +29,12 @@ public static void writeAnimationInFormat(Animation animation, Path exportDir, I if (format.onlyEmoteFile()) { String fileName = EmoteSerializer.getBaseName(file.getFileName().toString()); - if (animation.data().getBinary("iconData") instanceof ByteBuffer iconData) { + if (animation.data().getImage("iconData") instanceof DecodedImage iconData) { Path iconPath = exportDir.resolve(fileName + ".png"); if (Files.exists(iconPath)) throw new FileAlreadyExistsException(iconPath.toString()); try (OutputStream iconStream = Files.newOutputStream(iconPath)) { - iconStream.write(MathHelper.safeGetBytesFromBuffer(iconData)); + iconStream.write(iconData.toPng()); iconStream.flush(); } } diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/UniversalEmoteSerializer.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/UniversalEmoteSerializer.java index 62c6eaf76..25bb98d32 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/UniversalEmoteSerializer.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/UniversalEmoteSerializer.java @@ -3,7 +3,6 @@ import com.zigythebird.playeranimcore.animation.Animation; import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; import io.github.kosmx.emotes.server.config.Serializer; import io.github.kosmx.emotes.server.serializer.type.*; @@ -11,6 +10,7 @@ import io.github.kosmx.emotes.server.services.InstanceService; import org.jetbrains.annotations.Nullable; import org.redlance.common.services.ServiceUtils; +import org.redlance.platformtools.webp.decoder.DecodedImage; import java.io.IOException; import java.io.InputStream; @@ -137,8 +137,8 @@ private static void serializeInternalJson(String name){ emote.data().put(EmoteSerializer.BUILT_IN_KEY, true); InputStream iconStream = UniversalEmoteSerializer.class.getClassLoader().getResourceAsStream("assets/" + CommonData.MOD_ID + "/emotes/" + name + ".png"); - if(iconStream != null) { - emote.data().put("iconData", MathHelper.readFromIStream(iconStream)); + if (iconStream != null) { + emote.data().put("iconData", DecodedImage.fromPng(iconStream)); iconStream.close(); } } diff --git a/gradle.properties b/gradle.properties index c565b5ee3..6599a408a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ org.gradle.parallel=true # Dependencies java_version = 25 translationfallbacks_version = 1.2.0+mc.26.1-snapshot-10 - playeranimlib_version = 1.2.1+mc.26.1 + playeranimlib_version = 1.2.1+dev+mc.26.1 bendablecuboids_version = 2.0.1+mc.1.21.11 modmenu_version = 18.0.0-alpha.6 fabric_api_version = 0.144.0+26.1 diff --git a/minecraft/src/main/java/io/github/kosmx/emotes/main/EmoteHolder.java b/minecraft/src/main/java/io/github/kosmx/emotes/main/EmoteHolder.java index 3806004b4..598d63635 100644 --- a/minecraft/src/main/java/io/github/kosmx/emotes/main/EmoteHolder.java +++ b/minecraft/src/main/java/io/github/kosmx/emotes/main/EmoteHolder.java @@ -10,7 +10,6 @@ import io.github.kosmx.emotes.PlatformTools; import io.github.kosmx.emotes.api.proxy.INetworkInstance; import io.github.kosmx.emotes.common.CommonData; -import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; import io.github.kosmx.emotes.main.network.ClientEmotePlay; import io.github.kosmx.emotes.mc.McUtils; @@ -28,10 +27,11 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.redlance.platformtools.webp.decoder.DecodedImage; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -117,27 +117,29 @@ public static void clearEmotes(INetworkInstance networkInstance) { } public @Nullable Identifier getIconIdentifier() { - if (this.emote.data().getBinary("iconData") instanceof ByteBuffer buff && this.iconIdentifier == null) { - registerIcon(buff); + try { + if (this.emote.data().getImage("iconData") instanceof DecodedImage image && this.iconIdentifier == null) { + registerIcon(image.toPng()); + } + }catch (Throwable th) { + CommonData.LOGGER.warn("Can't open emote {} icon!", emote, th); + /*if (!PlatformTools.getConfig().neverRemoveBadIcon.get()) { + this.iconIdentifier = null; + this.emote.data().remove("iconData"); + }*/ } return this.iconIdentifier; } - private void registerIcon(ByteBuffer buffer) { + private void registerIcon(byte[] buf) throws IOException { RenderSystem.assertOnRenderThread(); - try (InputStream stream = new ByteArrayInputStream(MathHelper.safeGetBytesFromBuffer((buffer)))) { + try (InputStream stream = new ByteArrayInputStream(buf)) { this.iconIdentifier = McUtils.newIdentifier("icon" + hashCode()); Minecraft.getInstance().getTextureManager().register(this.iconIdentifier, new DynamicTexture(this.iconIdentifier::toString, NativeImage.read(stream)) ); - } catch (Throwable th) { - CommonData.LOGGER.warn("Can't open emote {} icon!", emote, th); - /*if (!PlatformTools.getConfig().neverRemoveBadIcon.get()) { - this.iconIdentifier = null; - this.emote.data().remove("iconData"); - }*/ } } From 13e73b8382d8bd7fc5ecdd2fea153efb888a7c24 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 31 Mar 2026 05:15:00 +0700 Subject: [PATCH 2/2] write/read as webp --- .../network/objects/EmoteIconPacket.java | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java index bc217fc4d..e35cfa4bb 100644 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java +++ b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/objects/EmoteIconPacket.java @@ -5,6 +5,8 @@ import io.github.kosmx.emotes.common.tools.MathHelper; import io.netty.buffer.ByteBuf; import org.redlance.platformtools.webp.decoder.DecodedImage; +import org.redlance.platformtools.webp.decoder.PlatformWebPDecoder; +import org.redlance.platformtools.webp.encoder.PlatformWebPEncoder; import java.io.IOException; @@ -16,16 +18,21 @@ public byte getID() { @Override public byte getVer() { - return 0x12; + return 0x13; } @Override public void read(ByteBuf byteBuf, NetData config, byte version) throws IOException { int size = byteBuf.readInt(); - if (size <= 0) return; + if (size == 0 || (size < 0 && version < 0x13)) return; - byte[] iconData = MathHelper.readBytes(byteBuf, size); - config.extraData.put("iconData", DecodedImage.fromPng(iconData)); + byte[] iconData = MathHelper.readBytes(byteBuf, Math.abs(size)); + + if (size < 0) { // Negative size = WebP, positive size = PNG + config.extraData.put("iconData", PlatformWebPDecoder.INSTANCE.decode(iconData)); + } else { + config.extraData.put("iconData", DecodedImage.fromPng(iconData)); + } } @Override @@ -38,9 +45,15 @@ public void write(ByteBuf byteBuf, NetData config, byte version) throws IOExcept return; } - byte[] pngData = iconData.toPng(); - byteBuf.writeInt(pngData.length); - byteBuf.writeBytes(pngData); + if (version >= 0x13 && PlatformWebPEncoder.INSTANCE.isAvailable()) { + byte[] webpData = PlatformWebPEncoder.INSTANCE.encodeLossless(iconData); + byteBuf.writeInt(-webpData.length); + byteBuf.writeBytes(webpData); + } else { + byte[] pngData = iconData.toPng(); + byteBuf.writeInt(pngData.length); + byteBuf.writeBytes(pngData); + } } @Override