From 844bed40a82f3bfa43817d26c640649787327985 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 00:13:01 +0700 Subject: [PATCH 01/70] Remove old geyser logic --- .../common/network/GeyserEmotePacket.java | 44 --------- .../emotes/server/config/Serializer.java | 5 -- .../emotes/server/geyser/EmoteMappings.java | 29 ------ .../network/AbstractServerEmotePlay.java | 90 +------------------ .../network/IServerNetworkInstance.java | 2 - .../server/serializer/BiMapSerializer.java | 32 ------- .../network/CommonServerNetworkHandler.java | 11 +-- .../arch/network/EmotePacketPayload.java | 5 -- .../arch/network/ModdedServerPlayNetwork.java | 7 -- .../arch/network/NetworkPlatformTools.java | 6 -- .../fabric/network/PayloadTypeRegistator.java | 1 - .../fabric/network/ServerNetworkStuff.java | 3 - .../emotes/neoforge/network/ForgeNetwork.java | 6 -- .../kosmx/emotes/bukkit/BukkitWrapper.java | 2 - .../bukkit/network/BukkitNetworkInstance.java | 6 -- .../bukkit/network/ServerSideEmotePlay.java | 22 +---- 16 files changed, 5 insertions(+), 266 deletions(-) delete mode 100644 emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/GeyserEmotePacket.java delete mode 100644 emotesServer/src/main/java/io/github/kosmx/emotes/server/geyser/EmoteMappings.java delete mode 100644 emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/BiMapSerializer.java diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/GeyserEmotePacket.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/GeyserEmotePacket.java deleted file mode 100644 index a0357da23..000000000 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/GeyserEmotePacket.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.kosmx.emotes.common.network; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.UUID; - -public class GeyserEmotePacket { - private long runtimeEntityID; - private UUID emoteID; - - public void read(byte[] bytes) throws IOException { - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); - runtimeEntityID = 0; - byte[] str = new byte[byteBuffer.get()]; - byteBuffer.get(str); - emoteID = UUID.fromString(new String(str, StandardCharsets.UTF_8)); - } - - public byte[] write() throws IOException { - byte[] bytes = emoteID.toString().getBytes(StandardCharsets.UTF_8); - ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length + 1 + 8); - byteBuffer.put((byte) bytes.length); - byteBuffer.put(bytes); - byteBuffer.putLong(runtimeEntityID); - return byteBuffer.array(); - } - - public long getRuntimeEntityID() { - return runtimeEntityID; - } - - public void setEmoteID(UUID emoteID) { - this.emoteID = emoteID; - } - - public UUID getEmoteID() { - return emoteID; - } - - public void setRuntimeEntityID(long runtimeEntityID) { - this.runtimeEntityID = runtimeEntityID; - } -} diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/config/Serializer.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/config/Serializer.java index 7110c681b..27004d823 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/config/Serializer.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/config/Serializer.java @@ -5,11 +5,8 @@ import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.TypeToken; import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.common.SerializableConfig; -import io.github.kosmx.emotes.common.tools.BiMap; -import io.github.kosmx.emotes.server.serializer.BiMapSerializer; import io.github.kosmx.emotes.server.services.InstanceService; import java.io.BufferedReader; @@ -17,7 +14,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.UUID; import java.util.logging.Level; /** @@ -37,7 +33,6 @@ public Serializer() { public void initializeSerializer(GsonBuilder builder) { builder.registerTypeAdapter(SerializableConfig.class, new ConfigSerializer()); - builder.registerTypeAdapter(new TypeToken>(){}.getType(), new BiMapSerializer()); } public void saveConfig() { diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/geyser/EmoteMappings.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/geyser/EmoteMappings.java deleted file mode 100644 index 0cc0e8983..000000000 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/geyser/EmoteMappings.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.kosmx.emotes.server.geyser; - -import io.github.kosmx.emotes.common.tools.BiMap; - -import org.jetbrains.annotations.Nullable; -import java.util.UUID; - -public class EmoteMappings { - //Bedrock - Java - final BiMap map; - - public EmoteMappings(BiMap map){ - if (map != null) { - this.map = map; - } else { - this.map = new BiMap<>(); - } - } - - @Nullable - public UUID getBeEmote(UUID javaEmote){ - return map.getL(javaEmote); - } - - @Nullable - public UUID getJavaEmote(UUID beEmote){ - return map.getR(beEmote); - } -} diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java index cf6a65e50..0e790725b 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java @@ -1,7 +1,5 @@ package io.github.kosmx.emotes.server.network; -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; import dev.kosmx.playerAnim.core.data.KeyframeAnimation; import dev.kosmx.playerAnim.core.impl.event.EventResult; import dev.kosmx.playerAnim.core.util.Pair; @@ -11,22 +9,13 @@ import io.github.kosmx.emotes.api.proxy.INetworkInstance; import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.network.GeyserEmotePacket; -import io.github.kosmx.emotes.common.network.PacketTask; import io.github.kosmx.emotes.common.network.objects.NetData; -import io.github.kosmx.emotes.common.tools.BiMap; import io.github.kosmx.emotes.server.config.Serializer; -import io.github.kosmx.emotes.server.geyser.EmoteMappings; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; -import io.github.kosmx.emotes.server.services.InstanceService; import org.jetbrains.annotations.Nullable; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.UUID; import java.util.logging.Level; @@ -36,39 +25,8 @@ */ @SuppressWarnings({"ConstantConditions", "rawtypes", "unused"}) public abstract class AbstractServerEmotePlay

extends ServerEmoteAPI { - protected EmoteMappings bedrockEmoteMap = new EmoteMappings(new BiMap<>()); - - //private AbstractServerEmotePlay instance; - - public AbstractServerEmotePlay(){ ServerEmoteAPI.INSTANCE = this; - - try { - initMappings(InstanceService.INSTANCE.getConfigPath()); - }catch (IOException e){ - LoggerService.INSTANCE.log(Level.WARNING, "Failed to load bedrock mappings!", e); - } - } - - public void initMappings(Path configPath) throws IOException{ - Path filePath = configPath.resolveSibling("emotecraft_emote_map.json"); - if(filePath.toFile().isFile()){ - BufferedReader reader = Files.newBufferedReader(filePath); - try { - this.bedrockEmoteMap = new EmoteMappings(Serializer.getSerializer().fromJson(reader, new TypeToken>() {}.getType())); - }catch (JsonParseException e){ - LoggerService.INSTANCE.log(Level.WARNING, "Failed to parse bedrock mappings!", e); - } - reader.close(); - } - else { - BiMap example = new BiMap<>(); - example.put(new UUID(0x0011223344556677L, 0x8899aabbccddeeffL), new UUID(0xffeeddccbbaa9988L, 0x7766554433221100L)); - BufferedWriter writer = Files.newBufferedWriter(filePath); - Serializer.getSerializer().toJson(example, new TypeToken>() {}.getType(), writer); - writer.close(); - } } protected boolean doValidate(){ @@ -110,22 +68,6 @@ public void receiveMessage(NetData data, P player, INetworkInstance instance) th } } - /** - * Receive emote from GeyserMC - * @param player player - * @param emotePacket BE emote uuid - */ - public void receiveBEEmote(P player, GeyserEmotePacket emotePacket) throws IOException { - UUID javaEmote = bedrockEmoteMap.getJavaEmote(emotePacket.getEmoteID()); - if(javaEmote != null && UniversalEmoteSerializer.getEmote(javaEmote) != null){ - NetData data = new NetData(); - data.emoteData = UniversalEmoteSerializer.getEmote(javaEmote); - data.purpose = PacketTask.STREAM; - handleStreamEmote(data, player, null); - } - else sendForEveryoneElse(emotePacket, player); - } - /** * Handle received stream message * @param data received data @@ -164,14 +106,7 @@ protected void streamEmote(NetData data, P player, boolean isForced, boolean isF data.isForced = isForced; data.player = getUUIDFromPlayer(player); data.strictSizeLimit = false; - UUID bedrockEmoteID = bedrockEmoteMap.getBeEmote(data.emoteData.getUuid()); - GeyserEmotePacket geyserEmotePacket = null; - if(bedrockEmoteID != null){ - geyserEmotePacket = new GeyserEmotePacket(); - geyserEmotePacket.setEmoteID(bedrockEmoteID); - geyserEmotePacket.setRuntimeEntityID(getRuntimePlayerID(player)); - } - sendForEveryoneElse(data, geyserEmotePacket, player); + sendForEveryoneElse(data, player); if (!isFromPlayer) { sendForPlayer(data, player, this.getUUIDFromPlayer(player)); } @@ -184,7 +119,7 @@ protected void stopEmote(P player, @Nullable NetData originalMessage) { ServerEmoteEvents.EMOTE_STOP_BY_USER.invoker().onStopEmote(emote.getLeft().getUuid(), getUUIDFromPlayer(player)); NetData data = new EmotePacket.Builder().configureToSendStop(emote.getLeft().getUuid(), getUUIDFromPlayer(player)).build().data; - sendForEveryoneElse(data, null, player); + sendForEveryoneElse(data, player); if (originalMessage == null) { //If the stop is not from the player, server needs to notify the player too data.isForced = true; sendForPlayer(data, player, getUUIDFromPlayer(player)); @@ -192,17 +127,6 @@ protected void stopEmote(P player, @Nullable NetData originalMessage) { } } - public void receiveGeyserMessage(P player, byte[] data){ - try { - GeyserEmotePacket packet = new GeyserEmotePacket(); - packet.read(data); - packet.setRuntimeEntityID(getRuntimePlayerID(player)); - receiveBEEmote(player, packet); - }catch (Throwable t){ - LoggerService.INSTANCE.log(Level.WARNING, "Failed to receive geyser packet!", t); - } - } - public void playerStartTracking(P tracked, P tracker) { if (tracked == null || tracker == null) return; Pair playedEmote = getPlayerNetworkInstance(tracked).getEmoteTracker().getPlayedEmote(); @@ -261,20 +185,12 @@ public EmotePacket.Builder getS2CConfigPacket(boolean trackPlayState) { return new EmotePacket.Builder(configData); } - /** - * Send message to everyone, except for the player. Only geyser packet - * @param packet Geyser packet - * @param player send around this player - */ - protected abstract void sendForEveryoneElse(GeyserEmotePacket packet, P player); - /** * Send the message to everyone, except for the player * @param data message - * @param emotePacket GeyserMC emote packet for Geyser users ;D * @param player send around this player */ - protected abstract void sendForEveryoneElse(NetData data, @Nullable GeyserEmotePacket emotePacket, P player); + protected abstract void sendForEveryoneElse(NetData data, P player); /** * Send message to target. If target see player the message will be sent diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/IServerNetworkInstance.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/IServerNetworkInstance.java index 448540ed9..3d01b4d95 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/IServerNetworkInstance.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/IServerNetworkInstance.java @@ -18,7 +18,5 @@ default boolean trackPlayState() { EmotePlayTracker getEmoteTracker(); - void sendGeyserPacket(ByteBuffer buffer); - void disconnect(String literal); } diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/BiMapSerializer.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/BiMapSerializer.java deleted file mode 100644 index dd1d4568f..000000000 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/serializer/BiMapSerializer.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.kosmx.emotes.server.serializer; - -import com.google.gson.*; -import dev.kosmx.playerAnim.core.util.Pair; -import io.github.kosmx.emotes.common.tools.BiMap; - -import java.lang.reflect.Type; -import java.util.UUID; - -public class BiMapSerializer implements JsonSerializer>, JsonDeserializer> { - @Override - public JsonElement serialize(BiMap src, Type typeOfSrc, JsonSerializationContext context) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("bedrock-emote", "java-emote"); //just for a little help. - for(Pair pair:src){ - jsonObject.addProperty(pair.getLeft().toString(), pair.getRight().toString()); - } - return jsonObject; - } - - @Override - public BiMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - final BiMap map = new BiMap<>(); - json.getAsJsonObject().entrySet().forEach(entry -> { - if(entry.getKey().equals("bedrock-emote")) return; - try { - map.put(UUID.fromString(entry.getKey()), UUID.fromString(entry.getValue().getAsString())); - }catch (IllegalArgumentException ignore){} - }); - return map; - } -} diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java index 194a8aa68..cf270e47e 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java @@ -3,7 +3,6 @@ import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.arch.mixin.ServerChunkCacheAccessor; import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.network.GeyserEmotePacket; import io.github.kosmx.emotes.common.network.objects.NetData; import io.github.kosmx.emotes.server.network.AbstractServerEmotePlay; import io.github.kosmx.emotes.server.network.IServerNetworkInstance; @@ -92,21 +91,13 @@ protected IServerNetworkInstance getPlayerNetworkInstance(Player sourcePlayer) { } @Override - protected void sendForEveryoneElse(GeyserEmotePacket packet, Player player) { - sendForEveryoneElse(null, packet, player); // don't make things complicated - } - - @Override - protected void sendForEveryoneElse(@Nullable NetData data, @Nullable GeyserEmotePacket geyserPacket, Player player) { + protected void sendForEveryoneElse(@Nullable NetData data, Player player) { getTrackedPlayers(player).forEach(target -> { if (target != player) { try { if (data != null && NetworkPlatformTools.canSendPlay(target, NetworkPlatformTools.EMOTE_CHANNEL_ID.id())) { IServerNetworkInstance playerNetwork = getPlayerNetworkInstance(target); playerNetwork.sendMessage(new EmotePacket.Builder(data), null); - } else if (geyserPacket != null && NetworkPlatformTools.canSendPlay(target, NetworkPlatformTools.GEYSER_CHANNEL_ID.id())) { - IServerNetworkInstance playerNetwork = getPlayerNetworkInstance(target); - playerNetwork.sendGeyserPacket(ByteBuffer.wrap(geyserPacket.write())); } } catch (IOException e) { LoggerService.INSTANCE.log(Level.WARNING, "Failed to send packet!", e); diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/EmotePacketPayload.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/EmotePacketPayload.java index 5d8dda80d..a52db82a9 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/EmotePacketPayload.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/EmotePacketPayload.java @@ -26,10 +26,6 @@ public byte[] unwrapBytes() { return new EmotePacketPayload(NetworkPlatformTools.STREAM_CHANNEL_ID, bytes); } - public static @NotNull CustomPacketPayload geyserPacket(@NotNull ByteBuffer bytes) { - return new EmotePacketPayload(NetworkPlatformTools.GEYSER_CHANNEL_ID, bytes); - } - @NotNull public static StreamCodec reader(@NotNull CustomPacketPayload.Type channel) { return CustomPacketPayload.codec((payload, buf) -> buf.writeBytes(payload.unwrapBytes()), buf -> { @@ -42,5 +38,4 @@ public static StreamCodec reader(@NotNull C public static final StreamCodec EMOTE_CHANNEL_READER = reader(NetworkPlatformTools.EMOTE_CHANNEL_ID); public static final StreamCodec STREAM_CHANNEL_READER = reader(NetworkPlatformTools.STREAM_CHANNEL_ID); - public static final StreamCodec GEYSER_CHANNEL_READER = reader(NetworkPlatformTools.GEYSER_CHANNEL_ID); } diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/ModdedServerPlayNetwork.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/ModdedServerPlayNetwork.java index 86ca401db..50cd5ecc6 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/ModdedServerPlayNetwork.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/ModdedServerPlayNetwork.java @@ -6,7 +6,6 @@ import io.github.kosmx.emotes.server.network.EmotePlayTracker; import io.github.kosmx.emotes.server.network.IServerNetworkInstance; import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.server.network.ServerGamePacketListenerImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,12 +41,6 @@ void sendEmotePacket(ByteBuffer buffer) { sendPlayMessage(buffer); } - - @Override - public void sendGeyserPacket(ByteBuffer buffer) { - serverGamePacketListener.send(NetworkPlatformTools.geyserPacket(buffer)); - } - @Override public void disconnect(String literal) { serverGamePacketListener.disconnect(Component.literal(literal)); diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/NetworkPlatformTools.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/NetworkPlatformTools.java index e4e57a7ad..325b597b4 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/NetworkPlatformTools.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/NetworkPlatformTools.java @@ -17,8 +17,6 @@ public final class NetworkPlatformTools { public static final CustomPacketPayload.Type EMOTE_CHANNEL_ID = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(CommonData.MOD_ID, CommonData.playEmoteID)); public static final CustomPacketPayload.Type STREAM_CHANNEL_ID = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(CommonData.MOD_ID, CommonData.emoteStreamID)); - public static final CustomPacketPayload.Type GEYSER_CHANNEL_ID = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath("geyser", "emote")); - @ExpectPlatform @Contract // contract to fix flow analysis. @@ -50,8 +48,4 @@ public static MinecraftServer getServer() { public static @NotNull Packet streamPacket(@NotNull ByteBuffer buf) { return createClientboundPacket(STREAM_CHANNEL_ID, buf); } - - public static @NotNull Packet geyserPacket(@NotNull ByteBuffer buf) { - return createClientboundPacket(GEYSER_CHANNEL_ID, buf); - } } diff --git a/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/PayloadTypeRegistator.java b/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/PayloadTypeRegistator.java index 95313b3f8..cdca46cf9 100644 --- a/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/PayloadTypeRegistator.java +++ b/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/PayloadTypeRegistator.java @@ -11,7 +11,6 @@ public class PayloadTypeRegistator { public static void init() { register(NetworkPlatformTools.EMOTE_CHANNEL_ID, EmotePacketPayload.EMOTE_CHANNEL_READER); register(NetworkPlatformTools.STREAM_CHANNEL_ID, EmotePacketPayload.STREAM_CHANNEL_READER); - register(NetworkPlatformTools.GEYSER_CHANNEL_ID, EmotePacketPayload.GEYSER_CHANNEL_READER); } private static void register(CustomPacketPayload.Type type, StreamCodec codec) { diff --git a/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/ServerNetworkStuff.java b/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/ServerNetworkStuff.java index 8295b2e1b..38f5678a9 100644 --- a/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/ServerNetworkStuff.java +++ b/minecraft/fabric/src/main/java/io/github/kosmx/emotes/fabric/network/ServerNetworkStuff.java @@ -74,8 +74,5 @@ protected void sendStreamChunk(ByteBuffer buffer) { ServerPlayNetworking.registerGlobalReceiver(NetworkPlatformTools.STREAM_CHANNEL_ID, (buf, context) -> CommonServerNetworkHandler.instance.receiveStreamMessage(buf.unwrapBytes(), context.player()) ); - ServerPlayNetworking.registerGlobalReceiver(NetworkPlatformTools.GEYSER_CHANNEL_ID, (buf, context) -> - CommonServerNetworkHandler.instance.receiveGeyserMessage(context.player(), buf.unwrapBytes()) - ); } } diff --git a/minecraft/neoforge/src/main/java/io/github/kosmx/emotes/neoforge/network/ForgeNetwork.java b/minecraft/neoforge/src/main/java/io/github/kosmx/emotes/neoforge/network/ForgeNetwork.java index 45f83181c..ec4389976 100644 --- a/minecraft/neoforge/src/main/java/io/github/kosmx/emotes/neoforge/network/ForgeNetwork.java +++ b/minecraft/neoforge/src/main/java/io/github/kosmx/emotes/neoforge/network/ForgeNetwork.java @@ -90,12 +90,6 @@ protected void sendStreamChunk(ByteBuffer buffer) { LoggerService.INSTANCE.log(Level.WARNING, e.getMessage(), e); } }); - - event.registrar("geyser") - .optional() - .playToServer(NetworkPlatformTools.GEYSER_CHANNEL_ID, EmotePacketPayload.GEYSER_CHANNEL_READER, - (arg, playPayloadContext) -> CommonServerNetworkHandler.instance.receiveGeyserMessage(playPayloadContext.player(), arg.unwrapBytes()) - ); } @SubscribeEvent diff --git a/paper/src/main/java/io/github/kosmx/emotes/bukkit/BukkitWrapper.java b/paper/src/main/java/io/github/kosmx/emotes/bukkit/BukkitWrapper.java index 342ebed4d..27acc1e20 100644 --- a/paper/src/main/java/io/github/kosmx/emotes/bukkit/BukkitWrapper.java +++ b/paper/src/main/java/io/github/kosmx/emotes/bukkit/BukkitWrapper.java @@ -18,9 +18,7 @@ import java.util.logging.Level; public class BukkitWrapper extends JavaPlugin { - public final static String EmotePacket = CommonData.getIDAsString(CommonData.playEmoteID); - public final static String GeyserPacket = "geyser:emote"; ServerSideEmotePlay networkPlay = null; @Override diff --git a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/BukkitNetworkInstance.java b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/BukkitNetworkInstance.java index 9264bf659..52fec5ede 100644 --- a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/BukkitNetworkInstance.java +++ b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/BukkitNetworkInstance.java @@ -8,7 +8,6 @@ import org.bukkit.entity.Player; import javax.annotation.Nullable; -import java.nio.ByteBuffer; import java.util.HashMap; import java.util.UUID; @@ -24,11 +23,6 @@ public EmotePlayTracker getEmoteTracker() { return this.emotePlayTracker; } - @Override - public void sendGeyserPacket(ByteBuffer buffer) { - player.sendPluginMessage(bukkitPlugin, "geyser:emote", buffer.array()); - } - @Override public void disconnect(String literal) { player.kickPlayer(literal); diff --git a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java index 13857c649..e607e7435 100644 --- a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java +++ b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java @@ -3,7 +3,6 @@ import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.bukkit.BukkitWrapper; import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.network.GeyserEmotePacket; import io.github.kosmx.emotes.common.network.objects.NetData; import io.github.kosmx.emotes.server.network.AbstractServerEmotePlay; import io.github.kosmx.emotes.server.network.IServerNetworkInstance; @@ -28,9 +27,7 @@ public class ServerSideEmotePlay extends AbstractServerEmotePlay impleme public ServerSideEmotePlay(BukkitWrapper plugin){ this.plugin = plugin; Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, BukkitWrapper.EmotePacket); - Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, BukkitWrapper.GeyserPacket); Bukkit.getMessenger().registerIncomingPluginChannel(plugin, BukkitWrapper.EmotePacket, this::receivePluginMessage); - Bukkit.getMessenger().registerIncomingPluginChannel(plugin, BukkitWrapper.GeyserPacket, this::receivePluginMessage); } private void receivePluginMessage(String channel, Player player, byte[] message) { @@ -48,9 +45,6 @@ private void receivePluginMessage(String channel, Player player, byte[] message) LoggerService.INSTANCE.log(Level.WARNING, "Player: " + player.getName() + " is not registered"); } } - else { - receiveGeyserMessage(player, message); - } } @Override @@ -85,20 +79,7 @@ protected IServerNetworkInstance getPlayerNetworkInstance(UUID player) { } @Override - protected void sendForEveryoneElse(GeyserEmotePacket packet, Player player) { - for(Player player1 : plugin.getServer().getOnlinePlayers()){ - if (player1 != player && player1.canSee(player)) { - try { - player1.sendPluginMessage(plugin, BukkitWrapper.GeyserPacket, packet.write()); - }catch (Exception e){ - LoggerService.INSTANCE.log(Level.WARNING, e.getMessage(), e); - } - } - } - } - - @Override - protected void sendForEveryoneElse(NetData data, GeyserEmotePacket emotePacket, Player player) { + protected void sendForEveryoneElse(NetData data, Player player) { for(Player player1 : plugin.getServer().getOnlinePlayers()){ if (player1 != player && player1.canSee(player)) { try { @@ -109,7 +90,6 @@ protected void sendForEveryoneElse(NetData data, GeyserEmotePacket emotePacket, packetBuilder.setVersion(getPlayerNetworkInstance(player1).getRemoteVersions()); player1.sendPluginMessage(plugin, BukkitWrapper.EmotePacket, packetBuilder.build().write().array()); } - else if(emotePacket != null) player1.sendPluginMessage(plugin, BukkitWrapper.GeyserPacket, emotePacket.write()); }catch (Exception e){ LoggerService.INSTANCE.log(Level.WARNING, e.getMessage(), e); } From fc8a96e883139e14a4ab97fc094b15cb6820cca9 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 01:57:47 +0700 Subject: [PATCH 02/70] Update gradle --- gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 zcmV;H4{7l5(*nQL0Kr1kzC=_KMxQY0|W5(lc#i zH*M1^P4B}|{x<+fkObwl)u#`$GxKKV&3pg*-y6R6txw)0qU|Clf9Uds3x{_-**c=7 z&*)~RHPM>Rw#Hi1R({;bX|7?J@w}DMF>dQQU2}9yj%iLjJ*KD6IEB2^n#gK7M~}6R zkH+)bc--JU^pV~7W=3{E*4|ZFpDpBa7;wh4_%;?XM-5ZgZNnVJ=vm!%a2CdQb?oTa z70>8rTb~M$5Tp!Se+4_OKWOB1LF+7gv~$$fGC95ToUM(I>vrd$>9|@h=O?eARj0MH zT4zo(M>`LWoYvE>pXvqG=d96D-4?VySz~=tPVNyD$XMshoTX(1ZLB5OU!I2OI{kb) zS8$B8Qm>wLT6diNnyJZC?yp{Kn67S{TCOt-!OonOK7$K)e-13U9GlnQXPAb&SJ0#3 z+vs~+4Qovv(%i8g$I#FCpCG^C4DdyQw3phJ(f#y*pvNDQCRZ~MvW<}fUs~PL=4??j zmhPyg<*I4RbTz|NHFE-DC7lf2=}-sGkE5e!RM%3ohM7_I^IF=?O{m*uUPH(V?gqyc(Rp?-Qu(3bBIL4Fz(v?=_Sh?LbK{nqZMD>#9D_hNhaV$0ef3@9V90|0u#|PUNTO>$F=qRhg1duaE z0`v~X3G{8RVT@kOa-pU+z8{JWyP6GF*u2e8eKr7a2t1fuqQy)@d|Qn(%YLZ62TWtoX@$nL}9?atE#Yw`rd(>cr0gY;dT9~^oL;u)zgHUvxc2I*b&ZkGM-iq=&(?kyO(3}=P! zRp=rErEyMT5UE9GjPHZ#T<`cnD)jyIL!8P{H@IU#`e8cAG5jMK zVyKw7--dAC;?-qEu*rMr$5@y535qZ6p(R#+fLA_)G~!wnT~~)|s`}&fA(s6xXN`9j zP#Fd3GBa#HeS{5&8p?%DKUyN^X9cYUc6vq}D_3xJ&d@=6j(6BZKPl?!k1?!`f3z&a zR4ZF60Mx7oBxLSxGuzA*Dy5n-d2K=+)6VMZh_0KetK|{e;E{8NJJ!)=_E~1uu=A=r zrn&gh)h*SFhsQJo!f+wKMIE;-EOaMSMB@aXRU(UcnJhZW^B^mgs|M9@5WF@s6B0p& zm#CTz)yiQCgURE{%hjxHcJ6G&>G9i`7MyftL!QQd5 z@RflRs?7)99?X`kHNt>W3l7YqscBpi*R2+fsgABor>KVOu(i(`03aytf2UA!&SC9v z!E}whj#^9~=XHMinFZ;6UOJjo=mmNaWkv~nC=qH9$s-8roGeyaW-E~SzZ3Gg>j zZ8}<320rg4=$`M0nxN!w(PtHUjeeU?MvYgWKZ6kkzABK;vMN0|U;X9abJleJA(xy<}5h5P(5 z{RzAFPvMnX2m0yH0Jn2Uo-p`daE|(O`YQiC#jB8;6bVIUf?SY(k$#C0`d6qT`>Xe0+0}Oj0=F&*D;PVe=Z<=0AGI<6$gYLwa#r` zm449x*fU;_+J>Mz!wa;T-wldoBB%&OEMJgtm#oaI60TSYCy7;+$5?q!zi5K`u66Wq zvg)Fx$s`V3Em{=OEY{3lmh_7|08ykS&U9w!kp@Ctuzqe1JFOGz6%i5}Kmm9>^=gih z?kRxqLA<3@e=}G4R_?phW{4DVr?`tPfyZSN@R=^;P;?!2bh~F1I|fB7P=V=9a6XU5 z<#0f>RS0O&rhc&nTRFOW7&QhevP0#>j0eq<1@D5yAlgMl5n&O9X|Vq}%RX}iNyRFF z7sX&u#6?E~bm~N|z&YikXC=I0E*8Z$v7PtWfjy)$e_Ez25fnR1Q=q1`;U!~U>|&YS zaOS8y!^ORmr2L4ik!IYR8@Dcx8MTC=(b4P6iE5CnrbI~7j7DmM8em$!da&D!6Xu)!vKPdLG z9f#)se|6=5yOCe)N6xDhPI!m81*dNe7u985zi%IVfOfJh69+#ag4ELzGne?o`eA`42K4T)h3S+s)5IT97%O>du- z0U54L8m4}rkRQ?QBfJ%DLssy^+a7Ajw;0&`NOTY4o;0-ivm9 zBz1C%nr_hQ)X)^QM6T1?=yeLkuG9Lf50(eH}`tFye;01&(p?8i+6h};VV-2B~qdxeC#=X z(JLlzy&fHkyi9Ksbcs~&r^%lh^2COldLz^H@X!s~mr9Dr6z!j+4?zkD@Ls7F8(t(f z9`U?P$Lmn*Y{K}aR4N&1N=?xtQ1%jqf1~pJyQ4SgBrEtR`j4lQuh7cqP49Em5cO=I zB(He2`iPN5M=Y0}h(IU$37ANTGx&|b-u1BYA*#dE(L-lptoOpo&th~E)_)y-`6kSH z3vvyVrcBwW^_XYReJ=JYd9OBQrzv;f2AQdZH#$Y{Y+Oa33M70XFI((fs;mB4e`<<{ ze4dv2B0V_?Ytsi>>g%qs*}oDGd5d(RNZ*6?7qNbdp7wP4T72=F&r?Ud#kZr8Ze5tB z_oNb7{G+(o2ajL$!69FW@jjPQ2a5C)m!MKKRirC$_VYIuVQCpf9rIms0GRDf)8AH${I`q^~5rjot@#3$2#zT2f`(N^P7Z;6(@EK$q*Jgif00I6*^ZGV+XB5uw*1R-@23yTw&WKD{s1;HTL;dO)%5i#`dc6b7;5@^{KU%N|A-$zsYw4)7LA{3`Zp>1 z-?K9_IE&z)dayUM)wd8K^29m-l$lFhi$zj0l!u~4;VGR6Y!?MAfBC^?QD53hy6VdD z@eUZIui}~L%#SmajaRq1J|#> z4m=o$vZ*34=ZWK2!QMNEcp2Lbc5N1q!lEDq(bz0b;WI9;e>l=CG9^n#ro`w>_0F$Q zfZ={2QyTkfByC&gy;x!r*NyXXbk=a%~~(#K?< zTke0HuF5{Q+~?@!KDXR|g+43$+;ab`^flS%miup_0OUTm=nIc%d5nLP)i308PIjl_YMF6cpQ__6&$n6it8K- z8PIjl_YMF6cpQ_!r)L8IivW`WdK8mBs6PXdjR2DYdK8nCs73=4j{uVadK8oNjwX|E wpAeHLsTu^*Y>Trk?aBtSQ(D-o$(D8Px^?ZI-PUB? z*1fv!{YdHme3Fc8%cR@*@zc5A_nq&2=R47Hp@$-JF4Fz*;SLw5}K^y>s-s;V!}b2i=5=M- zComP?ju>8Fe@=H@rlwe1l`J*6BTTo`9b$zjQ@HxrAhp0D#u?M~TxGC_!?ccCHCjt| zF*PgJf@kJB`|Ml}cmsyrAjO#Kjr^E5p29w+#>$C`Q|54BoDv$fQ9D?3n32P9LPMIzu?LjNqggOH=1@T{9bMn*u8(GI z!;MLTtFPHal^S>VcJdiYqX0VU|Rn@A}C1xOlxCribxes0~+n2 z6qDaIA2$?e`opx3_KW!rAgbpzU)gFdjAKXh|5w``#F0R|c)Y)Du0_Ihhz^S?k^pk% zP>9|pIDx)xHH^_~+aA=^$M!<8K~Hy(71nJGf6`HnjtS=4X4=Hk^O71oNia2V{HUCC zoN3RSBS?mZCLw;l4W4a+D8qc)XJS`pUJ5X-f^1ytxwr`@si$lAE?{4G|o; zO0l>`rr?;~c;{ZEFJ!!3=7=FdGJ?Q^xfNQh4A?i;IJ4}B+A?4olTK(fN++3CRBP97 ze~lG9h%oegkn)lpW-4F8o2`*WW0mZHwHez`ko@>U1_;EC_6ig|Drn@=DMV9YEUSCa zIf$kHei3(u#zm9I!Jf(4t`Vm1lltJ&lVHy(eIXE8sy9sUpmz%I_gA#8x^Zv8%w?r2 z{GdkX1SkzRIr>prRK@rqn9j2wG|rUvf6PJbbin=yy-TAXrguvzN8jL$hUrIXzr^s5 zVM?H4;eM-QeRFr06@ifV(ocvk?_)~N@1c2ien56UjWXid6W%6ievIh)>dk|rIs##^kY67ib8Kw%#-oVFaXG7$ERyA9(NSJUvWiOA5H(!{uOpcW zg&-?iqPhds%3%tFspHDqqr;A!e@B#iPQjHd=c>N1LoOEGRehVoPOdxJ>b6>yc#o#+ zl8s8!(|NMeqjsy@0x{8^j0d00SqRZjp{Kj)&4UHYGxG+z9b-)72I*&J70?+8e?p_@ z=>-(>l6z5vYlP~<2%DU02b!mA{7mS)NS_eLe=t)sm&+Pmk?asOEKlkPQ)EUvvfC=;4M&*|I!w}(@V_)eUKLA_t^%`o z0PM9LV|UKTLnk|?M3u!|f2S0?UqZsEIH9*NJS-8lzu;A6-rr-ot=dg9SASoluZUkFH$7X; zP=?kYX!K?JL-b~<#7wU;b;eS)O;@?h%sPPk{4xEBxb{!sm0AY|f9cNvx6>$3F!*0c z75H=dy8JvTyO8}g1w{$9T$p~5en}AeSLoCF>_RT9YPMpChUjl310o*$QocjbH& zbnwg#gssR#jDVN{uEi3n(PZ%PFZ|6J2 z5_rBf0-u>e4sFe0*Km49ATi7>Kn0f9!uc|rRMR1Dtt6m1LW8^>qFlo}h$@br=Rmpi z;mI&>OF64Be{dVeHI8utrh)v^wsZ0jii%x8UgZ8TC%K~@I(4E};GFW&(;WVov}3%H zH;IhRkfD^(vt^DjZz(MyHLZxv8}qzPc(%itBkBwf_fC~sDBgh<3XAv5cxxfF3<2U! z03Xe&z`is!JDHbe;mNmfkH+_LFE*I2^mdL@7(@9DfAcP6O04V-ko;Rpgp<%Cj5r8Z zd0`sXoIjV$j)--;jA6Zy^D5&5v$o^>e%>Q?9GLm{i~p^lAn!%ZtF$I~>39XVZxk0b zROh^Bk9cE0AJBLozZIEmy7xG(yHWGztvfnr0(2ro1%>zsGMS^EMu+S$r=_;9 zWwZkgf7Q7`H9sLf2Go^Xy6&h~a&%s2_T@_Csf19MntF$aVFiFkvE3_hUg(B@&Xw@YJ zpL$wNYf78=0c@!QU6_a$>CPiXT7QAGDM}7Z(0z#_ZA=fmLUj{2z7@Ypo71UDy8GHr z-&TLKf6a5WCf@Adle3VglBt4>Z>;xF}}-S~B7<(%B;Y z0QR55{z-buw>8ilNM3u6I+D$S%?)(p>=eBx-HpvZj{7c*_?K=d()*7q?93us}1dq%FAFYLsW8ZTQ_XZLh`P2*6(NgS}qGcfGXVWpwsp#Rs}IuKbk*`2}&) zI^Vsk6S&Q4@oYS?dJ`NwMVBs6f57+RxdqVub#PvMu?$=^OJy5xEl0<5SLsSRy%%a0 zi}Y#1-F3m;Ieh#Y12UgW?-R)|eX>ZuF-2cc!1>~NS|XSF-6In>zBoZg+ml!6%fk7U zw0LHcz8VQk(jOJ+Yu)|^|15ufl$KQd_1eUZZzj`aC%umU6F1&D5XVWce_wAe(qCSZ zpX-QF4e{EmEVN9~6%bR5U*UT{eMHfcUo`jw*u?4r2s_$`}U{?NjvEm(u&<>B|%mq$Q3weshxk z76<``8vh{+nX`@9CB6IE&z)I%IFjR^LH{s1p|eppv=x za(g_jLU|xjWMAn-V7th$f({|LG8zzIE0g?cyW;%Dmtv%C+0@xVxPE^ zyZzi9P%JAD6ynwHptuzP`Kox7*9h7XSMonCalv;Md0i9Vb-c*!f0ubfk?&T&T}AHh z4m8Bz{JllKcdNg?D^%a5MFQ;#1z|*}H^qHLzW)L}wp?2tY7RejtSh8<;Zw)QGJYUm z|MbTxyj*McKlStlT9I5XlSWtQGN&-LTr2XyNU+`490rg?LYLMRnz-@oKqT1hpCGqP zyRXt4=_Woj$%n5ee<3zhLF>5>`?m9a#xQH+Jk_+|RM8Vi;2*XbK- zEL6sCpaGPzP>k8f4Kh|##_imt#zJMB;ir|JrMPGW`rityK1vHXMLy18%qmMQAm4WZ zP)i30KR&5vs15)C+8dM66&$k~i|ZT;KR&5vs15)C+8dJ(sAmGPijyIz6_bsqKLSFH zlOd=TljEpH0>h4zA*dCTK&emy#FCRCs1=i^sZ9bFmXjf<6_X39E(XY)00000#N437 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c820..e18bc253b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From c32ce1ddad5f188dec3c11696a72cb54f5d087f1 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 03:17:00 +0700 Subject: [PATCH 03/70] Base geyser ext --- build.gradle.kts | 12 ++- geyser/build.gradle.kts | 81 +++++++++++++++++++ .../mods/emotecraft/geyser/EmotecraftExt.java | 25 ++++++ .../geyser/services/GeyserLoggerService.java | 29 +++++++ ...ub.kosmx.emotes.api.services.LoggerService | 1 + geyser/src/main/resources/extension.yml | 6 ++ gradle.properties | 2 +- settings.gradle.kts | 3 + 8 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 geyser/build.gradle.kts create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java create mode 100644 geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService create mode 100644 geyser/src/main/resources/extension.yml diff --git a/build.gradle.kts b/build.gradle.kts index f703595ec..3791b7d90 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ import me.modmuss50.mpp.ReleaseType plugins { + id("xyz.wagyourtail.jvmdowngrader") version("1.2.2") apply false id("dev.architectury.loom") version "1.9-SNAPSHOT" apply false id("architectury-plugin") version "3.4-SNAPSHOT" apply true id("com.gradleup.shadow") version "8.3.6" apply false @@ -24,17 +25,14 @@ subprojects { maven("https://repo.redlance.org/public") maven("https://libraries.minecraft.net") maven("https://maven.neoforged.net/releases") + maven("https://repo.opencollab.dev/main/") { + name = "Geyser" + } } tasks.withType(JavaCompile::class).configureEach { - val targetVersion = properties["java_version"] as String - sourceCompatibility = targetVersion - targetCompatibility = targetVersion - + options.release = 21 options.encoding = "UTF-8" - - //options.compilerArgs << "-Xlint:unchecked" - //options.deprecation = true //deprecated warning on compile } repositories { diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts new file mode 100644 index 000000000..83574da99 --- /dev/null +++ b/geyser/build.gradle.kts @@ -0,0 +1,81 @@ +plugins { + java + `maven-publish` + id("com.gradleup.shadow") + id("xyz.wagyourtail.jvmdowngrader") +} + +base.archivesName = "${archives_base_name}-${name}-for-MC${minecraft_version}" +version = mod_version + +val compileApi = configurations.register("compileApi").get() +configurations.api.configure { extendsFrom(compileApi) } + +dependencies { + compileOnly("org.geysermc.geyser:core:${properties["geyser_version"] as String}") + runtimeOnly("org.geysermc.geyser:standalone:${properties["geyser_version"] as String}") { + exclude(module = "netty-incubator-transport-native-io_uring") + } + + compileApi(project(":emotesAssets")) + compileApi(project(":emotesAPI")) { + exclude(group = "org.jetbrains", module = "annotations") + exclude(module = "gson") + } +} + +tasks { + processResources { + inputs.property("version", version) + inputs.property("description", mod_description) + + filesMatching("extension.yml") { + expand("version" to version, "description" to mod_description) + } + } + + shadowJar { + configurations = listOf(compileApi) + archiveClassifier.set("shaded") + mergeServiceFiles() + } + + downgradeJar { + dependsOn(shadowJar) + + downgradeTo = JavaVersion.VERSION_17 + inputFile = shadowJar.get().archiveFile + archiveClassifier.set("") + } + + jar { + archiveClassifier.set("dev") + } + + assemble { + dependsOn(downgradeJar) + } +} + +java { + withSourcesJar() +} + +publishing { + publications { + register("mavenJava") { + artifactId = "emotesGeyser" + from(components["java"]) + withCustomPom("emotesGeyser", "Minecraft Emotecraft Geyser extension") + } + } + + repositories { + if (shouldPublishMaven) { + kosmxRepo(project) + } else { + mavenLocal() + } + } +} + diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java new file mode 100644 index 000000000..58c0769b4 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -0,0 +1,25 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser; + +import io.github.kosmx.emotes.api.services.LoggerService; +import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.logging.Level; + +public class EmotecraftExt implements Extension { + private static EmotecraftExt instance; + + public EmotecraftExt() { + EmotecraftExt.instance = this; + } + + @Subscribe + public void onPreInitialize(GeyserPreInitializeEvent event) { + LoggerService.INSTANCE.log(Level.INFO, "Loading emotecraft on geyser..."); + } + + public static EmotecraftExt getInstance() { + return EmotecraftExt.instance; + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java new file mode 100644 index 000000000..a314352d9 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java @@ -0,0 +1,29 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.services; + +import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.tools.ServiceLoaderUtil; + +import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import java.util.logging.Level; + +public class GeyserLoggerService implements LoggerService { + @Override + public void log(Level level, String msg, Throwable throwable) { + EmotecraftExt.getInstance().logger().severe(msg, throwable); + } + + @Override + public void log(Level level, String msg) { + EmotecraftExt.getInstance().logger().info(msg); + } + + @Override + public boolean isActive() { + return EmotecraftExt.getInstance() != null; + } + + @Override + public int getPriority() { + return ServiceLoaderUtil.DEFAULT_PRIORITY / 2; + } +} diff --git a/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService new file mode 100644 index 000000000..bafe78b4a --- /dev/null +++ b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService @@ -0,0 +1 @@ +org.redlance.dima_dencep.mods.emotecraft.geyser.services.GeyserLoggerService \ No newline at end of file diff --git a/geyser/src/main/resources/extension.yml b/geyser/src/main/resources/extension.yml new file mode 100644 index 000000000..0a3fe8b3e --- /dev/null +++ b/geyser/src/main/resources/extension.yml @@ -0,0 +1,6 @@ +id: emotecraft +name: EmotecraftExt +api: 2.6.1 +main: org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt +version: ${version} +authors: [dima_dencep, KosmX] diff --git a/gradle.properties b/gradle.properties index 094a5df7e..8e48fc8de 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,13 +21,13 @@ org.gradle.parallel=true archives_base_name = emotecraft # Dependencies - java_version = 21 velocity_version = 3.4.0-SNAPSHOT player_animator_version = 2.0.1.1+1.21.4 modmenu_version = 13.0.1 fabric_api_version = 0.115.0+1.21.4 searchables_version = 1.0.3 fabric_permissions_api = 0.3.3 + geyser_version = 2.6.1-SNAPSHOT systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl systemProp.javax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl diff --git a/settings.gradle.kts b/settings.gradle.kts index eea44be47..eeb53de05 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,5 +23,8 @@ include("minecraft:neoforge") // Paper plugin include("paper") +// Geyser ext +include("geyser") + //Velocity plugin stuff // include "velocity" From 9f99d3f0f84e9f557f2cedf6911b12cb5eea65ba Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 23:32:47 +0700 Subject: [PATCH 04/70] Remove --- .../io/github/kosmx/emotes/common/network/EmotePacket.java | 5 ++++- .../kosmx/emotes/server/network/AbstractServerEmotePlay.java | 2 -- .../emotes/arch/network/CommonServerNetworkHandler.java | 5 ----- .../kosmx/emotes/bukkit/network/ServerSideEmotePlay.java | 5 ----- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java index 024ceb97c..79fffda5b 100644 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java +++ b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java @@ -271,6 +271,9 @@ public Builder strictSizeLimit(boolean strict) { data.strictSizeLimit = strict; return this; } - } + public NetData getData() { + return this.data; + } + } } diff --git a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java index 0e790725b..7f0368786 100644 --- a/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java +++ b/emotesServer/src/main/java/io/github/kosmx/emotes/server/network/AbstractServerEmotePlay.java @@ -37,8 +37,6 @@ protected boolean doValidate(){ protected abstract P getPlayerFromUUID(UUID player); - protected abstract long getRuntimePlayerID(P player); - protected abstract IServerNetworkInstance getPlayerNetworkInstance(P player); protected IServerNetworkInstance getPlayerNetworkInstance(UUID player) { //For potential optimization diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java index cf270e47e..0c4c58667 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/network/CommonServerNetworkHandler.java @@ -76,11 +76,6 @@ protected Player getPlayerFromUUID(UUID player) { return NetworkPlatformTools.getServer().getPlayerList().getPlayer(player); } - @Override - protected long getRuntimePlayerID(Player player) { - return player.getId(); - } - @Override protected IServerNetworkInstance getPlayerNetworkInstance(Player sourcePlayer) { if (!(sourcePlayer instanceof ServerPlayer player)) { diff --git a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java index e607e7435..088e8e7e5 100644 --- a/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java +++ b/paper/src/main/java/io/github/kosmx/emotes/bukkit/network/ServerSideEmotePlay.java @@ -57,11 +57,6 @@ public Player getPlayerFromUUID(UUID player) { return plugin.getServer().getPlayer(player); } - @Override - protected long getRuntimePlayerID(Player player) { - return player.getEntityId(); - } - @Override protected IServerNetworkInstance getPlayerNetworkInstance(Player player) { UUID playerUuid = getUUIDFromPlayer(player); From e181e14417ce07b916f79bc1b1611e2e5e37207c Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 23:33:11 +0700 Subject: [PATCH 05/70] More work --- .../mods/emotecraft/geyser/EmotecraftExt.java | 103 +++++++++++++ .../fuckery/ChainedPacketTranslator.java | 10 ++ .../geyser/fuckery/GayserHacks.java | 47 ++++++ .../geyser/handler/GeyserNetworkInstance.java | 135 ++++++++++++++++++ .../geyser/utils/DinnerboneProtocolUtils.java | 77 ++++++++++ 5 files changed, 372 insertions(+) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 58c0769b4..fdd669ffa 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -1,13 +1,42 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.CommonData; +import io.github.kosmx.emotes.common.network.EmotePacket; +import io.github.kosmx.emotes.common.network.objects.NetData; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.kyori.adventure.key.Key; +import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; +import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; +import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.util.MinecraftKey; +import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket; +import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; +import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; public class EmotecraftExt implements Extension { + private static final Map INSTANCES = new ConcurrentHashMap<>(); + + public static final Key MIECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); + public static final Key EMOTECAFT_EMOTE_TYPE = Key.key(CommonData.MOD_ID, CommonData.playEmoteID); + private static EmotecraftExt instance; public EmotecraftExt() { @@ -17,6 +46,80 @@ public EmotecraftExt() { @Subscribe public void onPreInitialize(GeyserPreInitializeEvent event) { LoggerService.INSTANCE.log(Level.INFO, "Loading emotecraft on geyser..."); + + GayserHacks.addCustomJavaTranslator(ClientboundCustomPayloadPacket.class, (session, packet) -> { + Key type = packet.getChannel(); + if (CommonData.MOD_ID.equals(type.namespace())) { // Any emotecraft payload + onEmotecraftPayload(session, packet.getChannel(), packet.getData()); + return false; // Discard + + } else if (MIECRAFT_REGISTER_TYPE.equals(type)) { + onMinecraftRegisterPayload(session, packet.getChannel(), packet.getData()); + } + return true; // Pass + }); + GayserHacks.addCustomBedrockTranslator(EmoteListPacket.class, (session, packet) -> { + LoggerService.INSTANCE.log(Level.INFO, "Player emotes " + packet.getPieceIds()); + return true; + }); + } + + private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] bytes) { + Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); + LoggerService.INSTANCE.log(Level.INFO, "Server listening channels: " + channels); + if (channels.contains(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)) { + LoggerService.INSTANCE.log(Level.INFO, "Has emotecraft!"); + + ByteBuf byteBuf = Unpooled.buffer(); + DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); + session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + } else { + // Online-emotes integration? + } + } + + private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) throws IOException { + NetData data = new EmotePacket.Builder().build().read(ByteBuffer.wrap(bytes)); + if (data == null) { + throw new IOException("no valid data"); + } + + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); + if (!networkInstance.trustReceivedPlayer()) { + data.player = null; + } + if (data.player == null && data.purpose.playerBound) { + throw new IOException("Didn't received any player information"); + } + + LoggerService.INSTANCE.log(Level.FINE, "[emotes client] Received message: " + data); + + if (data.purpose == null) { + LoggerService.INSTANCE.log(Level.INFO, "Packet execution is not possible without a purpose"); + return; + } + + networkInstance.sendMessage(new EmotePacket.Builder(data.copy()) + .setVersion(networkInstance.getRemoteVersions()), null + ); + } + + @Subscribe + public void onSessionInitialize(SessionInitializeEvent event) { + GeyserSession session = (GeyserSession) event.connection(); + EmotecraftExt.INSTANCES.put(session, new GeyserNetworkInstance(session)); + } + + @Subscribe + public void onSessionDisconnect(SessionDisconnectEvent event) { + EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); + } + + @Subscribe + public void onEmote(ClientEmoteEvent event) { + LoggerService.INSTANCE.log(Level.INFO, "On emote " + event.emoteId()); + GeyserSession session = (GeyserSession) event.connection(); + // TODO translate } public static EmotecraftExt getInstance() { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java new file mode 100644 index 000000000..be1a0fce7 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java @@ -0,0 +1,10 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import org.geysermc.geyser.session.GeyserSession; + +import java.io.IOException; + +@FunctionalInterface +public interface ChainedPacketTranslator { + boolean translate(GeyserSession session, T packet) throws IOException; +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java new file mode 100644 index 000000000..0055e0dab --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java @@ -0,0 +1,47 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.geysermc.geyser.registry.PacketTranslatorRegistry; +import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.mcprotocollib.network.packet.Packet; + +import java.io.IOException; + +@SuppressWarnings({"unchecked","rawtypes"}) +public class GayserHacks { + public static void addCustomBedrockTranslator(Class packet, ChainedPacketTranslator chained) { + GayserHacks.addCustomTranslator(Registries.BEDROCK_PACKET_TRANSLATORS, packet, chained); + } + + public static void addCustomJavaTranslator(Class packet, ChainedPacketTranslator chained) { + GayserHacks.addCustomTranslator(Registries.JAVA_PACKET_TRANSLATORS, packet, chained); + } + + public static void addCustomTranslator(PacketTranslatorRegistry registry, Class packet, ChainedPacketTranslator chained) { + PacketTranslator translator = (PacketTranslator) registry.get(packet); + registry.register(packet, new WrappedTranslator<>(translator, chained)); + } + + private static class WrappedTranslator extends PacketTranslator { + private final PacketTranslator original; + private final ChainedPacketTranslator chained; + + private WrappedTranslator(PacketTranslator original, ChainedPacketTranslator chained) { + this.original = original; + this.chained = chained; + } + + @Override + public void translate(GeyserSession session, T packet) { + try { + if (this.chained.translate(session, packet) && this.original != null) { + this.original.translate(session, packet); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java new file mode 100644 index 000000000..9ebd37a23 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -0,0 +1,135 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.handler; + +import dev.kosmx.playerAnim.core.impl.event.EventResult; +import io.github.kosmx.emotes.api.events.client.ClientEmoteEvents; +import io.github.kosmx.emotes.api.proxy.INetworkInstance; +import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.CommonData; +import io.github.kosmx.emotes.common.network.EmotePacket; +import io.github.kosmx.emotes.common.network.PacketConfig; +import io.github.kosmx.emotes.common.network.objects.NetData; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; + +public class GeyserNetworkInstance implements INetworkInstance { + private final HashMap versions = new HashMap<>(); + private final Map queue = new ConcurrentHashMap<>(); + private final GeyserSession session; + + public GeyserNetworkInstance(GeyserSession session) { + this.session = session; + } + + @Override + public HashMap getRemoteVersions() { + return this.versions; + } + + @Override + public void setVersions(HashMap map) { + this.versions.clear(); + this.versions.putAll(map); + } + + @Override + public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) throws IOException { + NetData data = builder.getData(); + + switch (Objects.requireNonNull(data.purpose)) { + case STREAM: + assert data.emoteData != null; + PlayerEntity playerEntity = getPlayerFromUUID(data.player); + + EventResult result = ClientEmoteEvents.EMOTE_VERIFICATION.invoker().verify(data.emoteData, data.player); + if (result == EventResult.FAIL) break; + + if (playerEntity != null) { + ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); + + //playerEntity.emotecraft$playEmote(data.emoteData, data.tick, data.isForced); + this.session.showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + } else { + // this.queue.put(data.player, new QueueEntry(data.emoteData, data.tick, ClientMethods.getCurrentTick())); + } + break; + + case STOP: + assert data.stopEmoteID != null; + PlayerEntity player = getPlayerFromUUID(data.player); + + if (player != null) { + ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(data.stopEmoteID, player.getUuid()); + + this.session.showEmote(player, "idk"); + + if (isMainPlayer(player) && !data.isForced) { + sendChatMessage("emotecraft.blockedEmote"); + } + } else { + this.queue.remove(data.player); + } + break; + case CONFIG: + setVersions(Objects.requireNonNull(data.versions)); + break; + + case FILE: + // TODO add bedrock form + break; + + case UNKNOWN: + LoggerService.INSTANCE.log(Level.WARNING, "Packet execution is not possible unknown purpose"); + break; + } + } + + public void sendChatMessage(String key) { + this.session.sendChat(MinecraftLocale.getLocaleString(key, this.session.locale())); + } + + public PlayerEntity getPlayerFromUUID(UUID uuid) { + if (this.session.javaUuid().equals(uuid)) { + return this.session.getPlayerEntity(); + } + return this.session.getEntityCache().getPlayerEntity(uuid); + } + + public boolean isMainPlayer(PlayerEntity player) { + return player != null && this.session.javaUuid().equals(player.getUuid()); + } + + @Override + public boolean isActive() { + return this.session != null; + } + + @Override + public int getRemoteVersion() { + return CommonData.networkingVersion; + } + + @Override + public boolean isServerTrackingPlayState() { + return this.versions.get(PacketConfig.SERVER_TRACK_EMOTE_PLAY) != 0; + } + + @Override + public int maxDataSize() { + return CommonData.MAX_PACKET_SIZE; + } + + @Override + public boolean sendPlayerID() { + return !isServerTrackingPlayState(); + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java new file mode 100644 index 000000000..15bd5e383 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import io.github.kosmx.emotes.api.services.LoggerService; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.key.Key; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; + +/** + * Protocol utilities for communicating over Dinnerbone's protocol. + */ +public class DinnerboneProtocolUtils { + + /** + * Reads a set of channels from the buffer. + * Each channel is a null-terminated string. + * If a string is not a valid channel, it is ignored. + * + * @param buf the buffer + * @return the channels + */ + public static Set readChannels(ByteBuf buf) { + final StringBuilder builder = new StringBuilder(); + final Set channels = new HashSet<>(); + + while (buf.isReadable()) { + final char c = (char) buf.readByte(); + if (c == '\0') { + parseAndAddChannel(builder, channels); + } else { + builder.append(c); + } + } + + parseAndAddChannel(builder, channels); + + return channels; + } + + public static void parseAndAddChannel(StringBuilder builder, Set channels) { + if (builder.isEmpty()) { + return; + } + + final String channel = builder.toString(); + try { + channels.add(Key.key(channel)); + } catch (Exception e) { + LoggerService.INSTANCE.log(Level.SEVERE, "Invalid channel: '" + channel + "'!", e); + } finally { + builder.setLength(0); + } + } + + /** + * Writes a set of channels to the buffer. + * Each channel is a null-terminated string. + * + * @param buf the buffer + * @param channels the channels + */ + public static void writeChannels(ByteBuf buf, Set channels) { + for (Key channel : channels) { + for (char c : channel.asString().toCharArray()) { + buf.writeByte(c); + } + buf.writeByte('\0'); + } + } +} From 61bc63508f44fd3a4ee9d9255874764f07c1239e Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Feb 2025 23:56:27 +0700 Subject: [PATCH 06/70] More work --- .../mods/emotecraft/geyser/EmotecraftExt.java | 48 ++++++-------- .../fuckery/ChainedPacketTranslator.java | 4 +- .../geyser/fuckery/GayserHacks.java | 10 +-- .../geyser/handler/GeyserNetworkInstance.java | 62 ++++++++++++++----- 4 files changed, 69 insertions(+), 55 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index fdd669ffa..4868aa8d4 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -3,11 +3,11 @@ import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.network.objects.NetData; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.adventure.key.Key; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; +import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; @@ -23,7 +23,6 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.Collections; import java.util.Map; @@ -31,6 +30,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; +/** + * Some cool stuff: + * - MarketplaceEmoteList + */ public class EmotecraftExt implements Extension { private static final Map INSTANCES = new ConcurrentHashMap<>(); @@ -78,30 +81,8 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] } } - private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) throws IOException { - NetData data = new EmotePacket.Builder().build().read(ByteBuffer.wrap(bytes)); - if (data == null) { - throw new IOException("no valid data"); - } - - GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); - if (!networkInstance.trustReceivedPlayer()) { - data.player = null; - } - if (data.player == null && data.purpose.playerBound) { - throw new IOException("Didn't received any player information"); - } - - LoggerService.INSTANCE.log(Level.FINE, "[emotes client] Received message: " + data); - - if (data.purpose == null) { - LoggerService.INSTANCE.log(Level.INFO, "Packet execution is not possible without a purpose"); - return; - } - - networkInstance.sendMessage(new EmotePacket.Builder(data.copy()) - .setVersion(networkInstance.getRemoteVersions()), null - ); + private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { + EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new).receiveMessage(bytes); } @Subscribe @@ -115,11 +96,18 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); } - @Subscribe + @Subscribe(postOrder = PostOrder.FIRST) public void onEmote(ClientEmoteEvent event) { - LoggerService.INSTANCE.log(Level.INFO, "On emote " + event.emoteId()); - GeyserSession session = (GeyserSession) event.connection(); - // TODO translate + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); + if (networkInstance != null) { + try { + EmotePacket.Builder packet = new EmotePacket.Builder().configureToStreamEmote(null); // TODO translate + networkInstance.sendMessage(packet, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + event.setCancelled(true); + } } public static EmotecraftExt getInstance() { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java index be1a0fce7..7f8a22076 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ChainedPacketTranslator.java @@ -2,9 +2,7 @@ import org.geysermc.geyser.session.GeyserSession; -import java.io.IOException; - @FunctionalInterface public interface ChainedPacketTranslator { - boolean translate(GeyserSession session, T packet) throws IOException; + boolean translate(GeyserSession session, T packet); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java index 0055e0dab..63bbb8441 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserHacks.java @@ -7,8 +7,6 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.mcprotocollib.network.packet.Packet; -import java.io.IOException; - @SuppressWarnings({"unchecked","rawtypes"}) public class GayserHacks { public static void addCustomBedrockTranslator(Class packet, ChainedPacketTranslator chained) { @@ -35,12 +33,8 @@ private WrappedTranslator(PacketTranslator original, ChainedPacketTranslator< @Override public void translate(GeyserSession session, T packet) { - try { - if (this.chained.translate(session, packet) && this.original != null) { - this.original.translate(session, packet); - } - } catch (IOException e) { - throw new RuntimeException(e); + if (this.chained.translate(session, packet) && this.original != null) { + this.original.translate(session, packet); } } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 9ebd37a23..157868dd8 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -2,18 +2,20 @@ import dev.kosmx.playerAnim.core.impl.event.EventResult; import io.github.kosmx.emotes.api.events.client.ClientEmoteEvents; -import io.github.kosmx.emotes.api.proxy.INetworkInstance; +import io.github.kosmx.emotes.api.proxy.AbstractNetworkInstance; import io.github.kosmx.emotes.api.services.LoggerService; -import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.network.EmotePacket; import io.github.kosmx.emotes.common.network.PacketConfig; import io.github.kosmx.emotes.common.network.objects.NetData; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; +import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.jetbrains.annotations.Nullable; +import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -21,7 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; -public class GeyserNetworkInstance implements INetworkInstance { +public class GeyserNetworkInstance extends AbstractNetworkInstance { private final HashMap versions = new HashMap<>(); private final Map queue = new ConcurrentHashMap<>(); private final GeyserSession session; @@ -43,8 +45,43 @@ public void setVersions(HashMap map) { @Override public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) throws IOException { - NetData data = builder.getData(); + super.sendMessage(builder.setVersion(getRemoteVersions()), target); + } + + @Override + protected void sendMessage(byte[] bytes, @Nullable UUID target) { + this.session.sendDownstreamPacket(new ServerboundCustomPayloadPacket( + EmotecraftExt.EMOTECAFT_EMOTE_TYPE, bytes + )); + } + + @Override + public void receiveMessage(ByteBuffer byteBuffer, UUID player) { + try { + NetData data = new EmotePacket.Builder().build().read(byteBuffer); + if (data == null) { + throw new IOException("no valid data"); + } + if (!trustReceivedPlayer()) { + data.player = null; + } + if (data.player == null && data.purpose.playerBound) { + throw new IOException("Didn't received any player information"); + } + + LoggerService.INSTANCE.log(Level.FINE, "[emotes client] Received message: " + data); + if (data.purpose == null) { + LoggerService.INSTANCE.log(Level.INFO, "Packet execution is not possible without a purpose"); + return; + } + + handleNetData(data); + } catch (Throwable th) { + throw new RuntimeException(th); + } + } + private void handleNetData(NetData data) { switch (Objects.requireNonNull(data.purpose)) { case STREAM: assert data.emoteData != null; @@ -81,6 +118,13 @@ public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) thro break; case CONFIG: setVersions(Objects.requireNonNull(data.versions)); + sendC2SConfig(payload -> { + try { + sendMessage(payload, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); break; case FILE: @@ -113,21 +157,11 @@ public boolean isActive() { return this.session != null; } - @Override - public int getRemoteVersion() { - return CommonData.networkingVersion; - } - @Override public boolean isServerTrackingPlayState() { return this.versions.get(PacketConfig.SERVER_TRACK_EMOTE_PLAY) != 0; } - @Override - public int maxDataSize() { - return CommonData.MAX_PACKET_SIZE; - } - @Override public boolean sendPlayerID() { return !isServerTrackingPlayState(); From 8b2c1beb1fe8288d64ea61bf007cd5b8665558c2 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 23 Feb 2025 00:47:46 +0700 Subject: [PATCH 07/70] Working --- .../mods/emotecraft/geyser/EmotecraftExt.java | 6 ++++-- .../geyser/services/GeyserLoggerService.java | 2 +- .../emotes/arch/mixin/ServerPlayTrackerMixin.java | 12 +++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 4868aa8d4..b05136937 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -12,7 +12,7 @@ import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; -import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.MinecraftKey; @@ -47,8 +47,10 @@ public EmotecraftExt() { } @Subscribe - public void onPreInitialize(GeyserPreInitializeEvent event) { + public void onPostInitialize(GeyserPostInitializeEvent event) { LoggerService.INSTANCE.log(Level.INFO, "Loading emotecraft on geyser..."); + LoggerService.INSTANCE.log(Level.WARNING, "Note that this extension does some horrible hacks on geyser."); + LoggerService.INSTANCE.log(Level.WARNING, "Until custom packet event is added, workarounds cannot be avoided."); GayserHacks.addCustomJavaTranslator(ClientboundCustomPayloadPacket.class, (session, packet) -> { Key type = packet.getChannel(); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java index a314352d9..f3216e4ad 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java @@ -14,7 +14,7 @@ public void log(Level level, String msg, Throwable throwable) { @Override public void log(Level level, String msg) { - EmotecraftExt.getInstance().logger().info(msg); + EmotecraftExt.getInstance().logger().warning(msg); } @Override diff --git a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/mixin/ServerPlayTrackerMixin.java b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/mixin/ServerPlayTrackerMixin.java index 1915f4195..790cd02dc 100644 --- a/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/mixin/ServerPlayTrackerMixin.java +++ b/minecraft/archCommon/src/main/java/io/github/kosmx/emotes/arch/mixin/ServerPlayTrackerMixin.java @@ -1,6 +1,6 @@ package io.github.kosmx.emotes.arch.mixin; -import io.github.kosmx.emotes.server.network.AbstractServerEmotePlay; +import io.github.kosmx.emotes.arch.network.CommonServerNetworkHandler; import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -14,12 +14,14 @@ @Mixin(ServerEntity.class) public class ServerPlayTrackerMixin { - @Shadow @Final private Entity entity; + @Shadow + @Final + private Entity entity; @Inject(method = "addPairing", at = @At(value = "TAIL")) - @SuppressWarnings("unchecked") private void startTrackingCallback(ServerPlayer serverPlayer, CallbackInfo ci) { - if (this.entity instanceof Player) AbstractServerEmotePlay.getInstance().playerStartTracking(this.entity, serverPlayer); //Do not do this in your code + if (this.entity instanceof Player player) { + CommonServerNetworkHandler.instance.playerStartTracking(player, serverPlayer); //Do not do this in your code + } } - } From bae623bb6fad30d3779c20e63d4e1b9f59ae7101 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 23 Feb 2025 00:59:47 +0700 Subject: [PATCH 08/70] Fix config --- .../mods/emotecraft/geyser/EmotecraftExt.java | 22 +++++++++++---- .../geyser/handler/GeyserNetworkInstance.java | 27 ++++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index b05136937..711ff9658 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -70,14 +70,26 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { } private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] bytes) { + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); - LoggerService.INSTANCE.log(Level.INFO, "Server listening channels: " + channels); + + LoggerService.INSTANCE.log(Level.FINE, "Server listening channels: " + channels); if (channels.contains(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)) { - LoggerService.INSTANCE.log(Level.INFO, "Has emotecraft!"); + LoggerService.INSTANCE.log(Level.FINE, "Has emotecraft!"); + + if (networkInstance.isHandShaked()) { + LoggerService.INSTANCE.log(Level.FINE, "Sending config...."); + networkInstance.sendC2SConfig(); + + } else { + LoggerService.INSTANCE.log(Level.FINE, "Sending register...."); - ByteBuf byteBuf = Unpooled.buffer(); - DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); - session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + ByteBuf byteBuf = Unpooled.buffer(); + DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); + session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + + networkInstance.setHandShaked(true); + } } else { // Online-emotes integration? } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 157868dd8..5400b82f2 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -28,6 +28,8 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { private final Map queue = new ConcurrentHashMap<>(); private final GeyserSession session; + private boolean isHandShaked; + public GeyserNetworkInstance(GeyserSession session) { this.session = session; } @@ -118,13 +120,6 @@ private void handleNetData(NetData data) { break; case CONFIG: setVersions(Objects.requireNonNull(data.versions)); - sendC2SConfig(payload -> { - try { - sendMessage(payload, null); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); break; case FILE: @@ -166,4 +161,22 @@ public boolean isServerTrackingPlayState() { public boolean sendPlayerID() { return !isServerTrackingPlayState(); } + + public void sendC2SConfig() { + sendC2SConfig(payload -> { + try { + sendMessage(payload, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + public void setHandShaked(boolean is) { + this.isHandShaked = is; + } + + public boolean isHandShaked() { + return this.isHandShaked; + } } From c1451b2973d4970a5a54ce5add2680731f1581ee Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 25 Feb 2025 21:54:01 +0700 Subject: [PATCH 09/70] Fully work bedrock2java --- .../mods/emotecraft/geyser/EmotecraftExt.java | 66 ++++++++----- .../geyser/handler/GeyserNetworkInstance.java | 44 ++++++++- .../geyser/utils/BedrockEmoteLoader.java | 95 +++++++++++++++++++ 3 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 711ff9658..b928d3595 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -1,12 +1,13 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; +import dev.kosmx.playerAnim.core.data.KeyframeAnimation; import io.github.kosmx.emotes.api.services.LoggerService; import io.github.kosmx.emotes.common.CommonData; -import io.github.kosmx.emotes.common.network.EmotePacket; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.adventure.key.Key; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; @@ -20,13 +21,14 @@ import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; -import java.io.IOException; import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -46,7 +48,7 @@ public EmotecraftExt() { EmotecraftExt.instance = this; } - @Subscribe + @Subscribe(postOrder = PostOrder.LAST) public void onPostInitialize(GeyserPostInitializeEvent event) { LoggerService.INSTANCE.log(Level.INFO, "Loading emotecraft on geyser..."); LoggerService.INSTANCE.log(Level.WARNING, "Note that this extension does some horrible hacks on geyser."); @@ -63,40 +65,43 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { } return true; // Pass }); + GayserHacks.addCustomBedrockTranslator(PlayerAuthInputPacket.class, (session, packet) -> { + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); + if (networkInstance != null && networkInstance.isPlaying() && session.isSneaking()) { + LoggerService.INSTANCE.log(Level.FINE, "Stopping animation " + session.name()); + networkInstance.stopEmote(session.getPlayerEntity()); + } + return true; + }); GayserHacks.addCustomBedrockTranslator(EmoteListPacket.class, (session, packet) -> { - LoggerService.INSTANCE.log(Level.INFO, "Player emotes " + packet.getPieceIds()); + BedrockEmoteLoader.preloadEmotes(packet.getPieceIds()); // Preload emotes return true; }); } private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] bytes) { - GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); LoggerService.INSTANCE.log(Level.FINE, "Server listening channels: " + channels); if (channels.contains(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)) { LoggerService.INSTANCE.log(Level.FINE, "Has emotecraft!"); - if (networkInstance.isHandShaked()) { - LoggerService.INSTANCE.log(Level.FINE, "Sending config...."); - networkInstance.sendC2SConfig(); - - } else { - LoggerService.INSTANCE.log(Level.FINE, "Sending register...."); - - ByteBuf byteBuf = Unpooled.buffer(); - DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); - session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); - - networkInstance.setHandShaked(true); - } + ByteBuf byteBuf = Unpooled.buffer(); + DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); + session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); } else { // Online-emotes integration? } } private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { - EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new).receiveMessage(bytes); + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); + if (!networkInstance.isHandShaked()) { + LoggerService.INSTANCE.log(Level.FINE, "Configuring emotecraft..."); + networkInstance.sendC2SConfig(); // If we are in the config state, the server is the first to send a packet and we reply to it + networkInstance.setHandShaked(true); + } + networkInstance.receiveMessage(bytes); } @Subscribe @@ -110,15 +115,26 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); } - @Subscribe(postOrder = PostOrder.FIRST) + @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); if (networkInstance != null) { - try { - EmotePacket.Builder packet = new EmotePacket.Builder().configureToStreamEmote(null); // TODO translate - networkInstance.sendMessage(packet, null); - } catch (IOException e) { - throw new RuntimeException(e); + CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); + + if (animation.isDone() && !animation.isCompletedExceptionally()) { + networkInstance.playEmote(animation.join(), false); + + } else { + networkInstance.stopEmote(); + + if (animation.isCompletedExceptionally()) { + networkInstance.sendChatMessage("emotecraft.cantconvert"); + + } else { + animation.thenAccept(emote -> networkInstance.playEmote( + emote, true + )); + } } event.setCancelled(true); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 5400b82f2..d04133ce3 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -1,5 +1,6 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.handler; +import dev.kosmx.playerAnim.core.data.KeyframeAnimation; import dev.kosmx.playerAnim.core.impl.event.EventResult; import io.github.kosmx.emotes.api.events.client.ClientEmoteEvents; import io.github.kosmx.emotes.api.proxy.AbstractNetworkInstance; @@ -28,6 +29,7 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { private final Map queue = new ConcurrentHashMap<>(); private final GeyserSession session; + private UUID currentEmote; private boolean isHandShaked; public GeyserNetworkInstance(GeyserSession session) { @@ -97,6 +99,10 @@ private void handleNetData(NetData data) { //playerEntity.emotecraft$playEmote(data.emoteData, data.tick, data.isForced); this.session.showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + + if (isMainPlayer(playerEntity)) { + this.currentEmote = data.emoteData.getUuid(); + } } else { // this.queue.put(data.player, new QueueEntry(data.emoteData, data.tick, ClientMethods.getCurrentTick())); } @@ -108,8 +114,7 @@ private void handleNetData(NetData data) { if (player != null) { ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(data.stopEmoteID, player.getUuid()); - - this.session.showEmote(player, "idk"); + stopEmote(player); if (isMainPlayer(player) && !data.isForced) { sendChatMessage("emotecraft.blockedEmote"); @@ -132,10 +137,41 @@ private void handleNetData(NetData data) { } } + public void stopEmote() { + stopEmote(this.session.getPlayerEntity()); + } + + public void stopEmote(PlayerEntity player) { + this.session.showEmote(player, "idk"); + + if (isMainPlayer(player) && this.currentEmote != null) { + try { + sendMessage(new EmotePacket.Builder().configureToSendStop(this.currentEmote), null); + } catch (IOException e) { + LoggerService.INSTANCE.log(Level.WARNING, "Failed to stop animation!", e); + } + } + + this.currentEmote = null; + } + public void sendChatMessage(String key) { this.session.sendChat(MinecraftLocale.getLocaleString(key, this.session.locale())); } + public void playEmote(KeyframeAnimation animation, boolean local) { + ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(animation, 0, this.session.javaUuid()); + try { + sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); + if (local) { + this.session.showEmote(this.session.getPlayerEntity(), animation.getUuid().toString()); + } + this.currentEmote = animation.getUuid(); + } catch (Throwable th) { + throw new RuntimeException(th); + } + } + public PlayerEntity getPlayerFromUUID(UUID uuid) { if (this.session.javaUuid().equals(uuid)) { return this.session.getPlayerEntity(); @@ -179,4 +215,8 @@ public void setHandShaked(boolean is) { public boolean isHandShaked() { return this.isHandShaked; } + + public boolean isPlaying() { + return this.currentEmote != null; + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java new file mode 100644 index 000000000..21168fce0 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -0,0 +1,95 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.gson.JsonObject; +import dev.kosmx.playerAnim.core.data.KeyframeAnimation; +import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.network.EmotePacket; +import io.github.kosmx.emotes.common.network.PacketTask; +import io.github.kosmx.emotes.common.network.objects.NetData; +import net.raphimc.minecraftauth.util.JsonUtil; +import org.jetbrains.annotations.NotNull; +import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +public class BedrockEmoteLoader { + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + private static final LoadingCache> BEDROCK_KEYFRAMES = CacheBuilder.newBuilder() + .maximumSize(128) + .expireAfterAccess(5, TimeUnit.HOURS) + .build(new CacheLoader<>() { + @Override + public CompletableFuture load(@NotNull String emoteId) { + HttpRequest request = HttpRequest.newBuilder() + .build(); + + return BedrockEmoteLoader.HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .thenCompose(this::parseAnimation) + .exceptionally(throwable -> { + BedrockEmoteLoader.BEDROCK_KEYFRAMES.invalidate(emoteId); + LoggerService.INSTANCE.log(Level.WARNING, "Failed to load emote!", throwable); + return null; + }); + } + + private CompletableFuture parseAnimation(HttpResponse response) { + try (Reader reader = new InputStreamReader(response.body())) { + JsonObject obj = JsonUtil.GSON.fromJson(reader, JsonObject.class); + + if (!JsonUtil.getBooleanOr(obj, "present", false)) { + return CompletableFuture.failedFuture(new NullPointerException()); + } + + NetData data = new EmotePacket.Builder() + .setSizeLimit(Integer.MAX_VALUE, false) + .build() + .read(ByteBuffer.wrap( + JsonUtil.GSON.fromJson(obj.get("bytes"), byte[].class) + )); + + if (data == null || data.purpose != PacketTask.STREAM) { + return CompletableFuture.failedFuture(new IllegalStateException("Binary emote is invalid!")); + } + + return CompletableFuture.completedFuture(data.emoteData); + } catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } + }); + + public static void preloadEmotes(List emotes) { + for (UUID emoteId : emotes) { + LoggerService.INSTANCE.log(Level.FINE, "Preloading emote " + emoteId + "..."); + try { + BedrockEmoteLoader.BEDROCK_KEYFRAMES.get(emoteId.toString()); + } catch (Throwable th) { + LoggerService.INSTANCE.log(Level.WARNING, "Failed to preload emote: " + emoteId, th); + } + } + } + + public static CompletableFuture loadEmote(String emoteId) { + return BedrockEmoteLoader.BEDROCK_KEYFRAMES.getUnchecked(emoteId); + } +} From 2b5d06700becb502d8f7acd965360e5e0158c496 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 25 Feb 2025 21:56:39 +0700 Subject: [PATCH 10/70] Check --- .../dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index b928d3595..a482c0579 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -118,7 +118,7 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); - if (networkInstance != null) { + if (networkInstance != null && networkInstance.isHandShaked()) { CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); if (animation.isDone() && !animation.isCompletedExceptionally()) { From fb5effceddaf6d14e48ff1782278626fc88892b1 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Wed, 23 Jul 2025 21:01:21 +0700 Subject: [PATCH 11/70] Rebase issues --- build.gradle.kts | 2 +- .../mods/emotecraft/geyser/EmotecraftExt.java | 23 +++++++-------- .../geyser/handler/GeyserNetworkInstance.java | 26 +++++++---------- .../geyser/services/GeyserLoggerService.java | 29 ------------------- .../geyser/utils/BedrockEmoteLoader.java | 23 +++++++-------- .../geyser/utils/DinnerboneProtocolUtils.java | 5 ++-- ...ub.kosmx.emotes.api.services.LoggerService | 1 - settings.gradle.kts | 3 ++ 8 files changed, 38 insertions(+), 74 deletions(-) delete mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java delete mode 100644 geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService diff --git a/build.gradle.kts b/build.gradle.kts index 677240035..22878a229 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,7 +40,7 @@ subprojects { } tasks.withType(JavaCompile::class).configureEach { - options.release = 21 + options.release = (properties["java_version"] as String).toInt() options.encoding = "UTF-8" } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index a482c0579..29532219c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -1,7 +1,6 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; -import dev.kosmx.playerAnim.core.data.KeyframeAnimation; -import io.github.kosmx.emotes.api.services.LoggerService; +import com.zigythebird.playeranimcore.animation.Animation; import io.github.kosmx.emotes.common.CommonData; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -30,12 +29,12 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; /** * Some cool stuff: * - MarketplaceEmoteList */ +@SuppressWarnings("unused") public class EmotecraftExt implements Extension { private static final Map INSTANCES = new ConcurrentHashMap<>(); @@ -50,9 +49,9 @@ public EmotecraftExt() { @Subscribe(postOrder = PostOrder.LAST) public void onPostInitialize(GeyserPostInitializeEvent event) { - LoggerService.INSTANCE.log(Level.INFO, "Loading emotecraft on geyser..."); - LoggerService.INSTANCE.log(Level.WARNING, "Note that this extension does some horrible hacks on geyser."); - LoggerService.INSTANCE.log(Level.WARNING, "Until custom packet event is added, workarounds cannot be avoided."); + CommonData.LOGGER.info("Loading emotecraft on geyser..."); + CommonData.LOGGER.warn("Note that this extension does some horrible hacks on geyser."); + CommonData.LOGGER.warn("Until custom packet event is added, workarounds cannot be avoided."); GayserHacks.addCustomJavaTranslator(ClientboundCustomPayloadPacket.class, (session, packet) -> { Key type = packet.getChannel(); @@ -68,7 +67,7 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { GayserHacks.addCustomBedrockTranslator(PlayerAuthInputPacket.class, (session, packet) -> { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); if (networkInstance != null && networkInstance.isPlaying() && session.isSneaking()) { - LoggerService.INSTANCE.log(Level.FINE, "Stopping animation " + session.name()); + CommonData.LOGGER.debug("Stopping animation {}", session.name()); networkInstance.stopEmote(session.getPlayerEntity()); } return true; @@ -82,9 +81,9 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] bytes) { Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); - LoggerService.INSTANCE.log(Level.FINE, "Server listening channels: " + channels); + CommonData.LOGGER.debug("Server listening channels: {}", channels); if (channels.contains(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)) { - LoggerService.INSTANCE.log(Level.FINE, "Has emotecraft!"); + CommonData.LOGGER.debug("Has emotecraft!"); ByteBuf byteBuf = Unpooled.buffer(); DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); @@ -97,8 +96,8 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); if (!networkInstance.isHandShaked()) { - LoggerService.INSTANCE.log(Level.FINE, "Configuring emotecraft..."); - networkInstance.sendC2SConfig(); // If we are in the config state, the server is the first to send a packet and we reply to it + CommonData.LOGGER.debug("Configuring emotecraft..."); + networkInstance.sendC2SConfig(); // If we are in the config state, the server is the first to send a packet, and we reply to it networkInstance.setHandShaked(true); } networkInstance.receiveMessage(bytes); @@ -119,7 +118,7 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { public void onEmote(ClientEmoteEvent event) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); if (networkInstance != null && networkInstance.isHandShaked()) { - CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); + CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); if (animation.isDone() && !animation.isCompletedExceptionally()) { networkInstance.playEmote(animation.join(), false); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index d04133ce3..81b1d6c97 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -1,10 +1,10 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.handler; -import dev.kosmx.playerAnim.core.data.KeyframeAnimation; -import dev.kosmx.playerAnim.core.impl.event.EventResult; +import com.zigythebird.playeranimcore.animation.Animation; +import com.zigythebird.playeranimcore.event.EventResult; import io.github.kosmx.emotes.api.events.client.ClientEmoteEvents; import io.github.kosmx.emotes.api.proxy.AbstractNetworkInstance; -import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.network.EmotePacket; import io.github.kosmx.emotes.common.network.PacketConfig; import io.github.kosmx.emotes.common.network.objects.NetData; @@ -22,7 +22,6 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; public class GeyserNetworkInstance extends AbstractNetworkInstance { private final HashMap versions = new HashMap<>(); @@ -63,9 +62,6 @@ protected void sendMessage(byte[] bytes, @Nullable UUID target) { public void receiveMessage(ByteBuffer byteBuffer, UUID player) { try { NetData data = new EmotePacket.Builder().build().read(byteBuffer); - if (data == null) { - throw new IOException("no valid data"); - } if (!trustReceivedPlayer()) { data.player = null; } @@ -73,9 +69,9 @@ public void receiveMessage(ByteBuffer byteBuffer, UUID player) { throw new IOException("Didn't received any player information"); } - LoggerService.INSTANCE.log(Level.FINE, "[emotes client] Received message: " + data); + CommonData.LOGGER.debug("[emotes client] Received message: {}", data); if (data.purpose == null) { - LoggerService.INSTANCE.log(Level.INFO, "Packet execution is not possible without a purpose"); + CommonData.LOGGER.warn("Packet execution is not possible without a purpose"); return; } @@ -101,7 +97,7 @@ private void handleNetData(NetData data) { this.session.showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate if (isMainPlayer(playerEntity)) { - this.currentEmote = data.emoteData.getUuid(); + this.currentEmote = data.emoteData.get(); } } else { // this.queue.put(data.player, new QueueEntry(data.emoteData, data.tick, ClientMethods.getCurrentTick())); @@ -132,7 +128,7 @@ private void handleNetData(NetData data) { break; case UNKNOWN: - LoggerService.INSTANCE.log(Level.WARNING, "Packet execution is not possible unknown purpose"); + CommonData.LOGGER.warn("Packet execution is not possible unknown purpose"); break; } } @@ -148,7 +144,7 @@ public void stopEmote(PlayerEntity player) { try { sendMessage(new EmotePacket.Builder().configureToSendStop(this.currentEmote), null); } catch (IOException e) { - LoggerService.INSTANCE.log(Level.WARNING, "Failed to stop animation!", e); + CommonData.LOGGER.warn("Failed to stop animation!", e); } } @@ -159,14 +155,14 @@ public void sendChatMessage(String key) { this.session.sendChat(MinecraftLocale.getLocaleString(key, this.session.locale())); } - public void playEmote(KeyframeAnimation animation, boolean local) { + public void playEmote(Animation animation, boolean local) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(animation, 0, this.session.javaUuid()); try { sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); if (local) { - this.session.showEmote(this.session.getPlayerEntity(), animation.getUuid().toString()); + this.session.showEmote(this.session.getPlayerEntity(), animation.get().toString()); } - this.currentEmote = animation.getUuid(); + this.currentEmote = animation.get(); } catch (Throwable th) { throw new RuntimeException(th); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java deleted file mode 100644 index f3216e4ad..000000000 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/services/GeyserLoggerService.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.services; - -import io.github.kosmx.emotes.api.services.LoggerService; -import io.github.kosmx.emotes.common.tools.ServiceLoaderUtil; - -import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; -import java.util.logging.Level; - -public class GeyserLoggerService implements LoggerService { - @Override - public void log(Level level, String msg, Throwable throwable) { - EmotecraftExt.getInstance().logger().severe(msg, throwable); - } - - @Override - public void log(Level level, String msg) { - EmotecraftExt.getInstance().logger().warning(msg); - } - - @Override - public boolean isActive() { - return EmotecraftExt.getInstance() != null; - } - - @Override - public int getPriority() { - return ServiceLoaderUtil.DEFAULT_PRIORITY / 2; - } -} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index 21168fce0..7f4d4e75c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -4,20 +4,18 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.gson.JsonObject; -import dev.kosmx.playerAnim.core.data.KeyframeAnimation; -import io.github.kosmx.emotes.api.services.LoggerService; +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.network.PacketTask; import io.github.kosmx.emotes.common.network.objects.NetData; import net.raphimc.minecraftauth.util.JsonUtil; import org.jetbrains.annotations.NotNull; -import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -26,7 +24,6 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; public class BedrockEmoteLoader { private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() @@ -34,12 +31,12 @@ public class BedrockEmoteLoader { .followRedirects(HttpClient.Redirect.ALWAYS) .build(); - private static final LoadingCache> BEDROCK_KEYFRAMES = CacheBuilder.newBuilder() + private static final LoadingCache> BEDROCK_KEYFRAMES = CacheBuilder.newBuilder() .maximumSize(128) .expireAfterAccess(5, TimeUnit.HOURS) .build(new CacheLoader<>() { @Override - public CompletableFuture load(@NotNull String emoteId) { + public @NotNull CompletableFuture load(@NotNull String emoteId) { HttpRequest request = HttpRequest.newBuilder() .build(); @@ -47,12 +44,12 @@ public CompletableFuture load(@NotNull String emoteId) { .thenCompose(this::parseAnimation) .exceptionally(throwable -> { BedrockEmoteLoader.BEDROCK_KEYFRAMES.invalidate(emoteId); - LoggerService.INSTANCE.log(Level.WARNING, "Failed to load emote!", throwable); + CommonData.LOGGER.error("Failed to load emote!", throwable); return null; }); } - private CompletableFuture parseAnimation(HttpResponse response) { + private CompletableFuture parseAnimation(HttpResponse response) { try (Reader reader = new InputStreamReader(response.body())) { JsonObject obj = JsonUtil.GSON.fromJson(reader, JsonObject.class); @@ -67,7 +64,7 @@ private CompletableFuture parseAnimation(HttpResponse parseAnimation(HttpResponse emotes) { for (UUID emoteId : emotes) { - LoggerService.INSTANCE.log(Level.FINE, "Preloading emote " + emoteId + "..."); + CommonData.LOGGER.debug("Preloading emote {}...", emoteId); try { BedrockEmoteLoader.BEDROCK_KEYFRAMES.get(emoteId.toString()); } catch (Throwable th) { - LoggerService.INSTANCE.log(Level.WARNING, "Failed to preload emote: " + emoteId, th); + CommonData.LOGGER.error("Failed to preload emote: {}", emoteId, th); } } } - public static CompletableFuture loadEmote(String emoteId) { + public static CompletableFuture loadEmote(String emoteId) { return BedrockEmoteLoader.BEDROCK_KEYFRAMES.getUnchecked(emoteId); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java index 15bd5e383..72379b604 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/DinnerboneProtocolUtils.java @@ -5,13 +5,12 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; -import io.github.kosmx.emotes.api.services.LoggerService; +import io.github.kosmx.emotes.common.CommonData; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; import java.util.HashSet; import java.util.Set; -import java.util.logging.Level; /** * Protocol utilities for communicating over Dinnerbone's protocol. @@ -53,7 +52,7 @@ public static void parseAndAddChannel(StringBuilder builder, Set channels) try { channels.add(Key.key(channel)); } catch (Exception e) { - LoggerService.INSTANCE.log(Level.SEVERE, "Invalid channel: '" + channel + "'!", e); + CommonData.LOGGER.error("Invalid channel: '{}'!", channel, e); } finally { builder.setLength(0); } diff --git a/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService deleted file mode 100644 index bafe78b4a..000000000 --- a/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.api.services.LoggerService +++ /dev/null @@ -1 +0,0 @@ -org.redlance.dima_dencep.mods.emotecraft.geyser.services.GeyserLoggerService \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5f07facb6..44e89fa99 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,3 +22,6 @@ include("minecraft:neoforge") // Paper plugin include("paper") + +// Geyser ext +include("geyser") From 3646c359a866a82a22446e3562e223ca14e55df4 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Thu, 24 Jul 2025 01:23:46 +0700 Subject: [PATCH 12/70] Update --- build.gradle.kts | 1 + .../mods/emotecraft/geyser/EmotecraftExt.java | 4 +- .../geyser/utils/BedrockEmoteLoader.java | 79 ++++++++----------- gradle.properties | 2 +- 4 files changed, 39 insertions(+), 47 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 22878a229..b13b069f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ subprojects { maven("https://repo.opencollab.dev/main/") { name = "Geyser" } + mavenLocal() } tasks.withType(JavaCompile::class).configureEach { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 29532219c..c3328b889 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -38,7 +38,7 @@ public class EmotecraftExt implements Extension { private static final Map INSTANCES = new ConcurrentHashMap<>(); - public static final Key MIECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); + public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); public static final Key EMOTECAFT_EMOTE_TYPE = Key.key(CommonData.MOD_ID, CommonData.playEmoteID); private static EmotecraftExt instance; @@ -59,7 +59,7 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { onEmotecraftPayload(session, packet.getChannel(), packet.getData()); return false; // Discard - } else if (MIECRAFT_REGISTER_TYPE.equals(type)) { + } else if (MINECRAFT_REGISTER_TYPE.equals(type)) { onMinecraftRegisterPayload(session, packet.getChannel(), packet.getData()); } return true; // Pass diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index 7f4d4e75c..fcc93efa4 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -5,75 +5,66 @@ import com.google.common.cache.LoadingCache; import com.google.gson.JsonObject; import com.zigythebird.playeranimcore.animation.Animation; +import com.zigythebird.playeranimcore.loading.UniversalAnimLoader; import io.github.kosmx.emotes.common.CommonData; -import io.github.kosmx.emotes.common.network.EmotePacket; -import io.github.kosmx.emotes.common.network.PacketTask; -import io.github.kosmx.emotes.common.network.objects.NetData; import net.raphimc.minecraftauth.util.JsonUtil; import org.jetbrains.annotations.NotNull; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.nio.ByteBuffer; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class BedrockEmoteLoader { +public class BedrockEmoteLoader extends CacheLoader> { private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) + .version(HttpClient.Version.HTTP_2) .followRedirects(HttpClient.Redirect.ALWAYS) .build(); private static final LoadingCache> BEDROCK_KEYFRAMES = CacheBuilder.newBuilder() .maximumSize(128) .expireAfterAccess(5, TimeUnit.HOURS) - .build(new CacheLoader<>() { - @Override - public @NotNull CompletableFuture load(@NotNull String emoteId) { - HttpRequest request = HttpRequest.newBuilder() - .build(); + .build(new BedrockEmoteLoader()); - return BedrockEmoteLoader.HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) - .thenCompose(this::parseAnimation) - .exceptionally(throwable -> { - BedrockEmoteLoader.BEDROCK_KEYFRAMES.invalidate(emoteId); - CommonData.LOGGER.error("Failed to load emote!", throwable); - return null; - }); - } + @Override + public @NotNull CompletableFuture load(@NotNull String emoteId) { + HttpRequest request = HttpRequest.newBuilder() + .build(); - private CompletableFuture parseAnimation(HttpResponse response) { - try (Reader reader = new InputStreamReader(response.body())) { - JsonObject obj = JsonUtil.GSON.fromJson(reader, JsonObject.class); + return BedrockEmoteLoader.HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .thenApply(this::parseAnimation) + .exceptionally(throwable -> { + BedrockEmoteLoader.BEDROCK_KEYFRAMES.invalidate(emoteId); + CommonData.LOGGER.error("Failed to load emote!", throwable); + return null; + }); + } + + private Animation parseAnimation(HttpResponse response) { + try (Reader reader = new InputStreamReader(response.body())) { + JsonObject obj = JsonUtil.GSON.fromJson(reader, JsonObject.class); - if (!JsonUtil.getBooleanOr(obj, "present", false)) { - return CompletableFuture.failedFuture(new NullPointerException()); - } + if (!JsonUtil.getBooleanOr(obj, "present", false)) { + throw new NullPointerException(JsonUtil.getStringOr(obj, "message", "Animation is not present!")); - NetData data = new EmotePacket.Builder() - .setSizeLimit(Integer.MAX_VALUE, false) - .build() - .read(ByteBuffer.wrap( - JsonUtil.GSON.fromJson(obj.get("bytes"), byte[].class) - )); + } else if (obj.has("message")) { + CommonData.LOGGER.warn(obj.get("message").getAsString()); + } - if (data.purpose != PacketTask.STREAM) { - return CompletableFuture.failedFuture(new IllegalStateException("Binary emote is invalid!")); - } + for (Map.Entry animation : UniversalAnimLoader.loadAnimations(obj.getAsJsonObject("emotes")).entrySet()) { + return animation.getValue(); + } - return CompletableFuture.completedFuture(data.emoteData); - } catch (IOException e) { - return CompletableFuture.failedFuture(e); - } - } - }); + throw new NullPointerException(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } public static void preloadEmotes(List emotes) { for (UUID emoteId : emotes) { diff --git a/gradle.properties b/gradle.properties index f0af26abe..e58190165 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,7 +24,7 @@ loom.ignoreDependencyLoomVersionValidation=true # Dependencies java_version = 21 - playeranimlib_version = 1.0.4+mc1.21.7 + playeranimlib_version = 1.0.4+dev+mc1.21.7 bendablecuboids_version = 1.0.2+mc1.21.7 modmenu_version = 15.0.0-beta.3 fabric_api_version = 0.129.0+1.21.7 From f65c939737a9100d46f9b87bb791ff748567859f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Thu, 24 Jul 2025 03:30:38 +0700 Subject: [PATCH 13/70] Run emotecraftext in dev --- geyser/build.gradle.kts | 5 +- .../emotecraft/geyser/GeyserBootstrap.java | 157 ++++++++++++++++++ gradle.properties | 2 +- 3 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 83574da99..65ca9fe13 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -13,9 +13,8 @@ configurations.api.configure { extendsFrom(compileApi) } dependencies { compileOnly("org.geysermc.geyser:core:${properties["geyser_version"] as String}") - runtimeOnly("org.geysermc.geyser:standalone:${properties["geyser_version"] as String}") { - exclude(module = "netty-incubator-transport-native-io_uring") - } + implementation("org.geysermc.geyser:standalone:${properties["geyser_version"] as String}") + implementation("org.ow2.asm:asm:9.8") compileApi(project(":emotesAssets")) compileApi(project(":emotesAPI")) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java new file mode 100644 index 000000000..c590870d6 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -0,0 +1,157 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser; + +import io.github.kosmx.emotes.common.CommonData; +import org.objectweb.asm.*; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; + +import static org.objectweb.asm.Opcodes.*; + +public class GeyserBootstrap extends ClassLoader { + static { + System.setProperty("java.awt.headless", "true"); + } + + public GeyserBootstrap(ClassLoader parent) { + super(parent); + } + + public static void main(String[] args) throws ReflectiveOperationException { + GeyserBootstrap loader = new GeyserBootstrap(GeyserBootstrap.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(loader); + + Class bootstrapClass = loader.loadClass("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap"); + Method main = bootstrapClass.getMethod("main", String[].class); + main.invoke(null, (Object) args); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.") || name.startsWith("jdk.") || name.startsWith("org.apache.logging.") || name.startsWith("com.sun.") || name.startsWith("org.slf4j.")) { + return super.loadClass(name, resolve); + } + + synchronized (getClassLoadingLock(name)) { + Class c = findLoadedClass(name); + if (c != null) return c; + + try { + byte[] classBytes = loadClassBytes(name); + + if (name.equals("org.geysermc.geyser.extension.GeyserExtensionLoader")) { + CommonData.LOGGER.info("Transforming {}", name); + ClassReader reader = new ClassReader(classBytes); + ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + ClassVisitor transformer = new GeyserExtensionLoaderClassVisitor(writer); + reader.accept(transformer, ClassReader.EXPAND_FRAMES); + classBytes = writer.toByteArray(); + } + + c = defineClass(name, classBytes, 0, classBytes.length); + + if (resolve) resolveClass(c); + return c; + } catch (ClassNotFoundException | IOException e) { + return super.loadClass(name, resolve); + } catch (Exception e) { + throw new ClassNotFoundException("Failed to load or transform " + name, e); + } + } + } + + private byte[] loadClassBytes(String name) throws IOException, ClassNotFoundException { + String resourceName = name.replace('.', '/') + ".class"; + try (InputStream is = getParent().getResourceAsStream(resourceName)) { + if (is == null) { + throw new ClassNotFoundException("Resource not found by bootstrap loader: " + resourceName); + } + return is.readAllBytes(); + } + } + + private static class GeyserExtensionLoaderClassVisitor extends ClassVisitor { + public GeyserExtensionLoaderClassVisitor(ClassVisitor classVisitor) { + super(ASM9, classVisitor); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (mv != null && "loadAllExtensions".equals(name) && "(Lorg/geysermc/geyser/api/extension/ExtensionManager;)V".equals(descriptor)) { + CommonData.LOGGER.info("Found method 'loadAllExtensions', applying transformation..."); + return new LoadAllExtensionsMethodVisitor(mv); + } + return mv; + } + } + + private static class LoadAllExtensionsMethodVisitor extends MethodVisitor { + public LoadAllExtensionsMethodVisitor(MethodVisitor methodVisitor) { + super(ASM9, methodVisitor); + } + + @Override + public void visitInsn(int opcode) { + if (opcode == RETURN) { + CommonData.LOGGER.info("Injecting emotecraft code..."); + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchHandler = new Label(); + Label exit = new Label(); + super.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); + super.visitLabel(tryStart); + super.visitTypeInsn(NEW, "org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt"); + super.visitInsn(DUP); + super.visitMethodInsn(INVOKESPECIAL, "org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt", "", "()V", false); + super.visitVarInsn(ASTORE, 2); + super.visitTypeInsn(NEW, "java/io/InputStreamReader"); + super.visitInsn(DUP); + super.visitLdcInsn(Type.getType("Lorg/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt;")); + super.visitLdcInsn("/extension.yml"); + super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false); + super.visitMethodInsn(INVOKESTATIC, "java/util/Objects", "requireNonNull", "(Ljava/lang/Object;)Ljava/lang/Object;", false); + super.visitTypeInsn(CHECKCAST, "java/io/InputStream"); + super.visitMethodInsn(INVOKESPECIAL, "java/io/InputStreamReader", "", "(Ljava/io/InputStream;)V", false); + super.visitMethodInsn(INVOKESTATIC, "org/geysermc/geyser/extension/GeyserExtensionDescription", "fromYaml", "(Ljava/io/Reader;)Lorg/geysermc/geyser/extension/GeyserExtensionDescription;", false); + super.visitVarInsn(ASTORE, 3); + super.visitVarInsn(ALOAD, 0); + super.visitVarInsn(ALOAD, 2); + super.visitVarInsn(ALOAD, 3); + super.visitLdcInsn("."); + super.visitInsn(ICONST_0); + super.visitTypeInsn(ANEWARRAY, "java/lang/String"); + super.visitMethodInsn(INVOKESTATIC, "java/nio/file/Path", "of", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;", true); + super.visitTypeInsn(NEW, "org/geysermc/geyser/extension/event/GeyserExtensionEventBus"); + super.visitInsn(DUP); + super.visitMethodInsn(INVOKESTATIC, "org/geysermc/geyser/GeyserImpl", "getInstance", "()Lorg/geysermc/geyser/GeyserImpl;", false); + super.visitMethodInsn(INVOKEVIRTUAL, "org/geysermc/geyser/GeyserImpl", "eventBus", "()Lorg/geysermc/geyser/api/event/EventBus;", false); + super.visitVarInsn(ALOAD, 2); + super.visitMethodInsn(INVOKESPECIAL, "org/geysermc/geyser/extension/event/GeyserExtensionEventBus", "", "(Lorg/geysermc/geyser/api/event/EventBus;Lorg/geysermc/geyser/api/extension/Extension;)V", false); + super.visitMethodInsn(INVOKESPECIAL, "org/geysermc/geyser/extension/GeyserExtensionLoader", "setup", "(Lorg/geysermc/geyser/api/extension/Extension;Lorg/geysermc/geyser/extension/GeyserExtensionDescription;Ljava/nio/file/Path;Lorg/geysermc/geyser/api/event/ExtensionEventBus;)Lorg/geysermc/geyser/extension/GeyserExtensionContainer;", false); + super.visitVarInsn(ASTORE, 4); + super.visitVarInsn(ALOAD, 0); + super.visitFieldInsn(GETFIELD, "org/geysermc/geyser/extension/GeyserExtensionLoader", "extensionContainers", "Ljava/util/Map;"); + super.visitVarInsn(ALOAD, 2); + super.visitVarInsn(ALOAD, 4); + super.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true); + super.visitInsn(POP); + super.visitVarInsn(ALOAD, 0); + super.visitVarInsn(ALOAD, 2); + super.visitVarInsn(ALOAD, 1); + super.visitMethodInsn(INVOKEVIRTUAL, "org/geysermc/geyser/extension/GeyserExtensionLoader", "register", "(Lorg/geysermc/geyser/api/extension/Extension;Lorg/geysermc/geyser/api/extension/ExtensionManager;)V", false); + super.visitLabel(tryEnd); + super.visitJumpInsn(GOTO, exit); + super.visitLabel(catchHandler); + super.visitTypeInsn(NEW, "java/lang/RuntimeException"); + super.visitInsn(DUP_X1); + super.visitInsn(SWAP); + super.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "", "(Ljava/lang/Throwable;)V", false); + super.visitInsn(ATHROW); + super.visitLabel(exit); + } + super.visitInsn(opcode); + } + } +} diff --git a/gradle.properties b/gradle.properties index e58190165..a47472aae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,4 +31,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.1 fabric_permissions_api = 0.4.0 noteblocklib_version = 3.1.0-SNAPSHOT - geyser_version = 2.6.1-SNAPSHOT + geyser_version = 2.8.2-SNAPSHOT From 5ce7dd7237d98656f166a07058cb0475a03623d4 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 25 Jul 2025 05:08:15 +0700 Subject: [PATCH 14/70] Fix networking --- .../mods/emotecraft/geyser/EmotecraftExt.java | 27 ++++++++--- .../geyser/fuckery/GayserSessionUtils.java | 47 +++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index c3328b889..a5e08cb02 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -16,17 +16,16 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.MinecraftKey; +import org.geysermc.mcprotocollib.protocol.data.ProtocolState; import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserSessionUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; -import java.util.Collections; - -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -39,7 +38,10 @@ public class EmotecraftExt implements Extension { private static final Map INSTANCES = new ConcurrentHashMap<>(); public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); + public static final Key EMOTECAFT_EMOTE_TYPE = Key.key(CommonData.MOD_ID, CommonData.playEmoteID); + public static final Key EMOTECAFT_STREAM_TYPE = Key.key(CommonData.MOD_ID, CommonData.emoteStreamID); + private static final Set EMOTECRAFT_CHANNELS = Set.of(EMOTECAFT_EMOTE_TYPE, EMOTECAFT_STREAM_TYPE); private static EmotecraftExt instance; @@ -86,8 +88,17 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] CommonData.LOGGER.debug("Has emotecraft!"); ByteBuf byteBuf = Unpooled.buffer(); - DinnerboneProtocolUtils.writeChannels(byteBuf, Collections.singleton(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)); + DinnerboneProtocolUtils.writeChannels(byteBuf, EMOTECRAFT_CHANNELS); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + + if (GayserSessionUtils.getProtocol(session).getOutboundState() == ProtocolState.GAME) { + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); + + if (!networkInstance.isHandShaked()) { + CommonData.LOGGER.warn("The server failed to configure the client, attempting to configure..."); + networkInstance.sendC2SConfig(); + } + } } else { // Online-emotes integration? } @@ -96,8 +107,10 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); if (!networkInstance.isHandShaked()) { - CommonData.LOGGER.debug("Configuring emotecraft..."); - networkInstance.sendC2SConfig(); // If we are in the config state, the server is the first to send a packet, and we reply to it + if (GayserSessionUtils.getProtocol(session).getOutboundState() == ProtocolState.CONFIGURATION) { + CommonData.LOGGER.debug("Configuring emotecraft..."); + networkInstance.sendC2SConfig(); + } networkInstance.setHandShaked(true); } networkInstance.receiveMessage(bytes); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java new file mode 100644 index 000000000..4f1ca637c --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java @@ -0,0 +1,47 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; + +@SuppressWarnings("deprecation") +public class GayserSessionUtils { + private static final MethodHandles.Lookup TRUSTED_LOOKUP; + + static { + try { + Field hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + TRUSTED_LOOKUP = (MethodHandles.Lookup) UnsafeAccess.UNSAFE.getObject( + UnsafeAccess.UNSAFE.staticFieldBase(hackfield), + UnsafeAccess.UNSAFE.staticFieldOffset(hackfield) + ); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } + + private static final VarHandle PROTOCOL = uncheck(() -> + TRUSTED_LOOKUP.findVarHandle(GeyserSession.class, "protocol", MinecraftProtocol.class) + ); + + public static MinecraftProtocol getProtocol(GeyserSession session) { + return (MinecraftProtocol) PROTOCOL.get(session); + } + + public static R uncheck(Supplier_WithExceptions supplier) { + try { + return supplier.get(); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + @FunctionalInterface + public interface Supplier_WithExceptions { + T get() throws E; + } +} From 019929f03062b701f887e3aebd98c9aaf65db715 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 13:39:16 +0700 Subject: [PATCH 15/70] Remove custom classloader --- .../emotecraft/geyser/GeyserBootstrap.java | 76 ++++++------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index c590870d6..25f23fd3f 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -1,73 +1,41 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; import io.github.kosmx.emotes.common.CommonData; +import org.geysermc.geyser.extension.GeyserExtensionContainer; +import org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap; import org.objectweb.asm.*; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Method; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.UnaryOperator; import static org.objectweb.asm.Opcodes.*; -public class GeyserBootstrap extends ClassLoader { +public class GeyserBootstrap { static { System.setProperty("java.awt.headless", "true"); } - public GeyserBootstrap(ClassLoader parent) { - super(parent); + public static void main(String[] args) throws ReflectiveOperationException, IOException { + patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", bytes -> { + ClassReader reader = new ClassReader(bytes); + ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + ClassVisitor transformer = new GeyserExtensionLoaderClassVisitor(writer); + reader.accept(transformer, ClassReader.EXPAND_FRAMES); + return writer.toByteArray(); + }); + GeyserStandaloneBootstrap.main(args); } - public static void main(String[] args) throws ReflectiveOperationException { - GeyserBootstrap loader = new GeyserBootstrap(GeyserBootstrap.class.getClassLoader()); - Thread.currentThread().setContextClassLoader(loader); - - Class bootstrapClass = loader.loadClass("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap"); - Method main = bootstrapClass.getMethod("main", String[].class); - main.invoke(null, (Object) args); - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.") || name.startsWith("jdk.") || name.startsWith("org.apache.logging.") || name.startsWith("com.sun.") || name.startsWith("org.slf4j.")) { - return super.loadClass(name, resolve); - } - - synchronized (getClassLoadingLock(name)) { - Class c = findLoadedClass(name); - if (c != null) return c; - - try { - byte[] classBytes = loadClassBytes(name); - - if (name.equals("org.geysermc.geyser.extension.GeyserExtensionLoader")) { - CommonData.LOGGER.info("Transforming {}", name); - ClassReader reader = new ClassReader(classBytes); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - ClassVisitor transformer = new GeyserExtensionLoaderClassVisitor(writer); - reader.accept(transformer, ClassReader.EXPAND_FRAMES); - classBytes = writer.toByteArray(); - } - - c = defineClass(name, classBytes, 0, classBytes.length); - - if (resolve) resolveClass(c); - return c; - } catch (ClassNotFoundException | IOException e) { - return super.loadClass(name, resolve); - } catch (Exception e) { - throw new ClassNotFoundException("Failed to load or transform " + name, e); - } - } - } - - private byte[] loadClassBytes(String name) throws IOException, ClassNotFoundException { - String resourceName = name.replace('.', '/') + ".class"; - try (InputStream is = getParent().getResourceAsStream(resourceName)) { - if (is == null) { - throw new ClassNotFoundException("Resource not found by bootstrap loader: " + resourceName); - } - return is.readAllBytes(); + private static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { + try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { + byte[] bytecode = patcher.apply(is.readAllBytes()); + Files.write(Path.of(name.replace("/", "")), bytecode); + MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); } } From 38c3eae9b326c34d12fca6872611691ad62db7c6 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 13:54:07 +0700 Subject: [PATCH 16/70] Fix build --- build.gradle.kts | 2 +- buildSrc/build.gradle.kts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 057a766c9..85eb8d37b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ import me.modmuss50.mpp.ReleaseType plugins { - id("xyz.wagyourtail.jvmdowngrader") version("1.2.2") apply false + id("xyz.wagyourtail.jvmdowngrader") version("1.3.3") apply false id("dev.architectury.loom") version "1.10-SNAPSHOT" apply false id("architectury-plugin") version "3.4-SNAPSHOT" apply true id("com.gradleup.shadow") version "9.0.2" apply false diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 296b9d0d1..2dc180e44 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -13,4 +13,8 @@ dependencies { implementation("me.modmuss50.mod-publish-plugin:me.modmuss50.mod-publish-plugin.gradle.plugin:0.8.4") compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") compileOnly("com.squareup.okhttp3:okhttp:5.1.0") + + constraints { + implementation("commons-io:commons-io:2.20.0") + } } From cb55b11364b73cfc2d92bd68283583b3869c84e9 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 14:13:20 +0700 Subject: [PATCH 17/70] Remove debug code --- .../dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index 25f23fd3f..58889dfa0 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -8,13 +8,14 @@ import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Objects; import java.util.function.UnaryOperator; import static org.objectweb.asm.Opcodes.*; +/** + * Used to run Emotecraft in a dev environment. + */ public class GeyserBootstrap { static { System.setProperty("java.awt.headless", "true"); @@ -34,7 +35,6 @@ public static void main(String[] args) throws ReflectiveOperationException, IOEx private static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { byte[] bytecode = patcher.apply(is.readAllBytes()); - Files.write(Path.of(name.replace("/", "")), bytecode); MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); } } From 34eaf6342a0bd384591088edad82f325e66fa5d8 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 14:50:43 +0700 Subject: [PATCH 18/70] Add publishing --- build.gradle.kts | 1 + buildSrc/src/main/java/CustomDiscordSettings.kt | 12 ++++++------ buildSrc/src/main/java/CustomPublishResult.kt | 7 +++++-- geyser/build.gradle.kts | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 85eb8d37b..70e56ec64 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -122,6 +122,7 @@ val ds = publishDiscord { val hangarLink = "https://hangar.papermc.io/$hangarProjectName/versions/$ver" nextRow() custom("Hangar (Paper)", hangarLink, HANGAR_EMOJI) + from(":geyser", "github", GEYSER_EMOJI, "Geyser Ext") } } diff --git a/buildSrc/src/main/java/CustomDiscordSettings.kt b/buildSrc/src/main/java/CustomDiscordSettings.kt index c4c8cdb3f..5a861b887 100644 --- a/buildSrc/src/main/java/CustomDiscordSettings.kt +++ b/buildSrc/src/main/java/CustomDiscordSettings.kt @@ -20,23 +20,23 @@ class DownloadLinks { currentRow.add(result) } - fun RegularFileProperty.emoji(emoji: Emoji?): LatePublishResult { + fun RegularFileProperty.toLate(emoji: Emoji?, title: String?): LatePublishResult { val file = this.get().asFile - return LatePublishResult(file, emoji) + return LatePublishResult(file, emoji, title) } /** * Adds publish result for [platform] from mod-publish-plugin in [project] */ - fun Project.from(project: String, platform: String, emoji: Emoji? = null) { - from(project(project), platform, emoji) + fun Project.from(project: String, platform: String, emoji: Emoji? = null, title: String? = null) { + from(project(project), platform, emoji, title) } /** * Adds publish result for [platform] from mod-publish-plugin in [project] */ - fun from(project: Project, platform: String, emoji: Emoji? = null) { - add(project.publishResult(platform).emoji(emoji)) + fun from(project: Project, platform: String, emoji: Emoji? = null, title: String? = null) { + add(project.publishResult(platform).toLate(emoji, title)) } fun custom(title: String, link: String, emoji: Emoji?) { diff --git a/buildSrc/src/main/java/CustomPublishResult.kt b/buildSrc/src/main/java/CustomPublishResult.kt index b0bed7993..59c92d079 100644 --- a/buildSrc/src/main/java/CustomPublishResult.kt +++ b/buildSrc/src/main/java/CustomPublishResult.kt @@ -5,12 +5,15 @@ import java.io.FileNotFoundException val CURSEFORGE_EMOJI = Emoji("curseforge", "1136405235187847198") val MODRINTH_EMOJI = Emoji("modrinth", "1136404935374798878") val HANGAR_EMOJI = Emoji("hangar", "1407387843931672647") +val GITHUB_EMOJI = Emoji("github", "1136406913542795364") +val GEYSER_EMOJI = Emoji("geyser", "1409806511508815922") fun Emoji.Companion.fromPlatform(platform: String): Emoji? { return when (platform) { "modrinth" -> MODRINTH_EMOJI "curseforge" -> CURSEFORGE_EMOJI "hangar" -> HANGAR_EMOJI + "github" -> GITHUB_EMOJI else -> null } } @@ -31,7 +34,7 @@ class CustomPublishResult(override val title: String, } } -class LatePublishResult(val file: File, private val emojiOverride: Emoji? = null) : ICustomPublishResult { +class LatePublishResult(val file: File, private val emojiOverride: Emoji? = null, private val titleOverride: String? = null) : ICustomPublishResult { val loaded by lazy { val p = try { PublishResult.fromJson(file.readText()) @@ -42,7 +45,7 @@ class LatePublishResult(val file: File, private val emojiOverride: Emoji? = null CustomPublishResult.from(p) } override val title: String - get() = loaded.title + get() = titleOverride ?: loaded.title override val link: String get() = loaded.link override val emoji: Emoji? diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 65ca9fe13..2731b62fc 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -1,8 +1,11 @@ +import me.modmuss50.mpp.ReleaseType + plugins { java `maven-publish` id("com.gradleup.shadow") id("xyz.wagyourtail.jvmdowngrader") + id("me.modmuss50.mod-publish-plugin") } base.archivesName = "${archives_base_name}-${name}-for-MC${minecraft_version}" @@ -78,3 +81,16 @@ publishing { } } +publishMods { + file.set(tasks.downgradeJar.get().archiveFile) // Java 17 + // additionalFiles.from(tasks.shadowJar.get().archiveFile) // Java 21 + + type = ReleaseType.of(releaseType) + changelog = changes + dryRun = gradle.startParameter.isDryRun + + github { + accessToken = providers.environmentVariable("GH_TOKEN") + parent(rootProject.tasks.named("publishGithub")) + } +} From d3c802c6c383688d0a3392dd4901df48586b8873 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 14:57:04 +0700 Subject: [PATCH 19/70] Update build.gradle.kts --- geyser/build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 2731b62fc..351d7ccc9 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -22,7 +22,15 @@ dependencies { compileApi(project(":emotesAssets")) compileApi(project(":emotesAPI")) { exclude(group = "org.jetbrains", module = "annotations") + exclude(module = "gson") + exclude(module = "slf4j-api") + exclude(module = "fastutil") + exclude(module = "netty-buffer") + exclude(module = "jspecify") + exclude(module = "guava") + exclude(module = "error_prone_annotations") + exclude(module = "netty-buffer") } } From d7e359b1d16f775552245ac2843f09a1e1032a65 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 17:10:23 +0700 Subject: [PATCH 20/70] Update build.gradle.kts --- geyser/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 351d7ccc9..f8bb3db44 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -48,6 +48,9 @@ tasks { configurations = listOf(compileApi) archiveClassifier.set("shaded") mergeServiceFiles() + + relocate("team.unnamed.mocha", "com.zigythebird.playeranim.lib.mochafloats") + relocate("javassist", "com.zigythebird.playeranim.lib.javassist") } downgradeJar { From 9c3588ecb895935e456f10adf6b61d05a7ee1326 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 19:44:54 +0700 Subject: [PATCH 21/70] Remove reflection --- .../mods/emotecraft/geyser/EmotecraftExt.java | 17 +++++- .../emotecraft/geyser/GeyserBootstrap.java | 14 +---- .../geyser/fuckery/GayserSessionUtils.java | 47 --------------- .../geyser/fuckery/GeyserSessionPatch.java | 59 +++++++++++++++++++ .../geyser/fuckery/ProtocolProvider.java | 12 ++++ 5 files changed, 87 insertions(+), 62 deletions(-) delete mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index a5e08cb02..c701943e1 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -15,16 +15,19 @@ import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.ProtocolState; import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; -import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserSessionUtils; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; +import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -35,6 +38,14 @@ */ @SuppressWarnings("unused") public class EmotecraftExt implements Extension { + static { + try { + GeyserSessionPatch.patchClass(PendingMicrosoftAuthentication.class, "org/geysermc/geyser/session/GeyserSession.class", GeyserSessionPatch::patch); + } catch (ReflectiveOperationException | IOException e) { + throw new RuntimeException(e); + } + } + private static final Map INSTANCES = new ConcurrentHashMap<>(); public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); @@ -91,7 +102,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] DinnerboneProtocolUtils.writeChannels(byteBuf, EMOTECRAFT_CHANNELS); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); - if (GayserSessionUtils.getProtocol(session).getOutboundState() == ProtocolState.GAME) { + if (((ProtocolProvider) session).ec$state() == ProtocolState.GAME) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); if (!networkInstance.isHandShaked()) { @@ -107,7 +118,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); if (!networkInstance.isHandShaked()) { - if (GayserSessionUtils.getProtocol(session).getOutboundState() == ProtocolState.CONFIGURATION) { + if (((ProtocolProvider) session).ec$state() == ProtocolState.CONFIGURATION) { CommonData.LOGGER.debug("Configuring emotecraft..."); networkInstance.sendC2SConfig(); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index 58889dfa0..510113838 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -4,12 +4,9 @@ import org.geysermc.geyser.extension.GeyserExtensionContainer; import org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap; import org.objectweb.asm.*; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; import java.io.IOException; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.util.Objects; -import java.util.function.UnaryOperator; import static org.objectweb.asm.Opcodes.*; @@ -22,7 +19,7 @@ public class GeyserBootstrap { } public static void main(String[] args) throws ReflectiveOperationException, IOException { - patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", bytes -> { + GeyserSessionPatch.patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", bytes -> { ClassReader reader = new ClassReader(bytes); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); ClassVisitor transformer = new GeyserExtensionLoaderClassVisitor(writer); @@ -32,13 +29,6 @@ public static void main(String[] args) throws ReflectiveOperationException, IOEx GeyserStandaloneBootstrap.main(args); } - private static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { - try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { - byte[] bytecode = patcher.apply(is.readAllBytes()); - MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); - } - } - private static class GeyserExtensionLoaderClassVisitor extends ClassVisitor { public GeyserExtensionLoaderClassVisitor(ClassVisitor classVisitor) { super(ASM9, classVisitor); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java deleted file mode 100644 index 4f1ca637c..000000000 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GayserSessionUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; - -import io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Field; - -@SuppressWarnings("deprecation") -public class GayserSessionUtils { - private static final MethodHandles.Lookup TRUSTED_LOOKUP; - - static { - try { - Field hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); - TRUSTED_LOOKUP = (MethodHandles.Lookup) UnsafeAccess.UNSAFE.getObject( - UnsafeAccess.UNSAFE.staticFieldBase(hackfield), - UnsafeAccess.UNSAFE.staticFieldOffset(hackfield) - ); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException(ex); - } - } - - private static final VarHandle PROTOCOL = uncheck(() -> - TRUSTED_LOOKUP.findVarHandle(GeyserSession.class, "protocol", MinecraftProtocol.class) - ); - - public static MinecraftProtocol getProtocol(GeyserSession session) { - return (MinecraftProtocol) PROTOCOL.get(session); - } - - public static R uncheck(Supplier_WithExceptions supplier) { - try { - return supplier.get(); - } catch (Exception exception) { - throw new RuntimeException(exception); - } - } - - @FunctionalInterface - public interface Supplier_WithExceptions { - T get() throws E; - } -} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java new file mode 100644 index 000000000..94ef76be3 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java @@ -0,0 +1,59 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import javassist.*; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; +import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.util.Objects; +import java.util.function.UnaryOperator; + +public class GeyserSessionPatch { + public static final String CLASS_NAME = "org.geysermc.geyser.session.GeyserSession"; + + @SuppressWarnings("unused") + public static void hook(StartGamePacket packet) { + System.out.println(packet); + } + + public static byte[] patch(byte[] bytes) { + ClassPool pool = ClassPool.getDefault(); + pool.appendClassPath(new ByteArrayClassPath(CLASS_NAME, bytes)); + + try { + CtClass cc = pool.get(CLASS_NAME); + + CtClass protocolProviderInterface = pool.get("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider"); + cc.addInterface(protocolProviderInterface); + + String methodSrc = """ + public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$getProtocol() { + return this.protocol; + }"""; + cc.addMethod(CtNewMethod.make(methodSrc, cc)); + + CtMethod startGameMethod = cc.getDeclaredMethod("startGame"); + startGameMethod.instrument(new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getClassName().equals("org.geysermc.geyser.session.UpstreamSession") && m.getMethodName().equals("sendPacket")) { + m.replace("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch.hook(startGamePacket); $_ = $proceed($$);"); + } + } + }); + + return cc.toBytecode(); + } catch (Exception ex) { + throw new RuntimeException("Failed to patch!", ex); + } + } + + public static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { + try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { + byte[] bytecode = patcher.apply(is.readAllBytes()); + MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); + } + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java new file mode 100644 index 000000000..a86962bba --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java @@ -0,0 +1,12 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; +import org.geysermc.mcprotocollib.protocol.data.ProtocolState; + +public interface ProtocolProvider { + MinecraftProtocol ec$protocol(); + + default ProtocolState ec$state() { + return ec$protocol().getOutboundState(); + } +} From f84d5b7988967bdad56bb8aa2b85ef0af64aa27c Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 19:50:06 +0700 Subject: [PATCH 22/70] Add @Override --- .../geyser/fuckery/GeyserSessionPatch.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java index 94ef76be3..c88274268 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java @@ -1,6 +1,9 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; import javassist.*; +import javassist.bytecode.AnnotationsAttribute; +import javassist.bytecode.ConstPool; +import javassist.bytecode.MethodInfo; import javassist.expr.ExprEditor; import javassist.expr.MethodCall; import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; @@ -29,11 +32,19 @@ public static byte[] patch(byte[] bytes) { CtClass protocolProviderInterface = pool.get("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider"); cc.addInterface(protocolProviderInterface); - String methodSrc = """ + CtMethod ctMethod = CtNewMethod.make(""" public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$getProtocol() { return this.protocol; - }"""; - cc.addMethod(CtNewMethod.make(methodSrc, cc)); + }""", cc + ); + + MethodInfo methodInfo = ctMethod.getMethodInfo(); + ConstPool cp = methodInfo.getConstPool(); + AnnotationsAttribute attr = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); + attr.addAnnotation(new javassist.bytecode.annotation.Annotation(Override.class.getName(), cp)); + methodInfo.addAttribute(attr); + + cc.addMethod(ctMethod); CtMethod startGameMethod = cc.getDeclaredMethod("startGame"); startGameMethod.instrument(new ExprEditor() { From b9feb8362b48dc4f624b92b3c56592fc28d58e91 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 19:59:22 +0700 Subject: [PATCH 23/70] Update geyser --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 081a05795..9096091b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,4 +32,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.1 fabric_permissions_api = 0.4.0 noteblocklib_version = 3.1.0 - geyser_version = 2.8.2-SNAPSHOT + geyser_version = 2.8.3-SNAPSHOT From 8ab99ca09dcccef3984f672ef6eaf6e3b240340b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 20:00:51 +0700 Subject: [PATCH 24/70] Oops --- .../mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java index c88274268..cdfe245ae 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java @@ -33,7 +33,7 @@ public static byte[] patch(byte[] bytes) { cc.addInterface(protocolProviderInterface); CtMethod ctMethod = CtNewMethod.make(""" - public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$getProtocol() { + public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$protocol() { return this.protocol; }""", cc ); From 5135ae69a4f13765715b412fabec72dd81c03bdf Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Tue, 26 Aug 2025 20:16:21 +0700 Subject: [PATCH 25/70] Remove ASM dep --- geyser/build.gradle.kts | 1 - .../emotecraft/geyser/GeyserBootstrap.java | 125 +++++------------- 2 files changed, 33 insertions(+), 93 deletions(-) diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index f8bb3db44..40bd6e13b 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -17,7 +17,6 @@ configurations.api.configure { extendsFrom(compileApi) } dependencies { compileOnly("org.geysermc.geyser:core:${properties["geyser_version"] as String}") implementation("org.geysermc.geyser:standalone:${properties["geyser_version"] as String}") - implementation("org.ow2.asm:asm:9.8") compileApi(project(":emotesAssets")) compileApi(project(":emotesAPI")) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index 510113838..a084a2a24 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -1,15 +1,15 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; -import io.github.kosmx.emotes.common.CommonData; +import javassist.ByteArrayClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; import org.geysermc.geyser.extension.GeyserExtensionContainer; import org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap; -import org.objectweb.asm.*; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; import java.io.IOException; -import static org.objectweb.asm.Opcodes.*; - /** * Used to run Emotecraft in a dev environment. */ @@ -19,97 +19,38 @@ public class GeyserBootstrap { } public static void main(String[] args) throws ReflectiveOperationException, IOException { - GeyserSessionPatch.patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", bytes -> { - ClassReader reader = new ClassReader(bytes); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - ClassVisitor transformer = new GeyserExtensionLoaderClassVisitor(writer); - reader.accept(transformer, ClassReader.EXPAND_FRAMES); - return writer.toByteArray(); - }); + GeyserSessionPatch.patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", GeyserBootstrap::patch); GeyserStandaloneBootstrap.main(args); } - private static class GeyserExtensionLoaderClassVisitor extends ClassVisitor { - public GeyserExtensionLoaderClassVisitor(ClassVisitor classVisitor) { - super(ASM9, classVisitor); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - if (mv != null && "loadAllExtensions".equals(name) && "(Lorg/geysermc/geyser/api/extension/ExtensionManager;)V".equals(descriptor)) { - CommonData.LOGGER.info("Found method 'loadAllExtensions', applying transformation..."); - return new LoadAllExtensionsMethodVisitor(mv); - } - return mv; - } - } - - private static class LoadAllExtensionsMethodVisitor extends MethodVisitor { - public LoadAllExtensionsMethodVisitor(MethodVisitor methodVisitor) { - super(ASM9, methodVisitor); - } - - @Override - public void visitInsn(int opcode) { - if (opcode == RETURN) { - CommonData.LOGGER.info("Injecting emotecraft code..."); - Label tryStart = new Label(); - Label tryEnd = new Label(); - Label catchHandler = new Label(); - Label exit = new Label(); - super.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); - super.visitLabel(tryStart); - super.visitTypeInsn(NEW, "org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt"); - super.visitInsn(DUP); - super.visitMethodInsn(INVOKESPECIAL, "org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt", "", "()V", false); - super.visitVarInsn(ASTORE, 2); - super.visitTypeInsn(NEW, "java/io/InputStreamReader"); - super.visitInsn(DUP); - super.visitLdcInsn(Type.getType("Lorg/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt;")); - super.visitLdcInsn("/extension.yml"); - super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false); - super.visitMethodInsn(INVOKESTATIC, "java/util/Objects", "requireNonNull", "(Ljava/lang/Object;)Ljava/lang/Object;", false); - super.visitTypeInsn(CHECKCAST, "java/io/InputStream"); - super.visitMethodInsn(INVOKESPECIAL, "java/io/InputStreamReader", "", "(Ljava/io/InputStream;)V", false); - super.visitMethodInsn(INVOKESTATIC, "org/geysermc/geyser/extension/GeyserExtensionDescription", "fromYaml", "(Ljava/io/Reader;)Lorg/geysermc/geyser/extension/GeyserExtensionDescription;", false); - super.visitVarInsn(ASTORE, 3); - super.visitVarInsn(ALOAD, 0); - super.visitVarInsn(ALOAD, 2); - super.visitVarInsn(ALOAD, 3); - super.visitLdcInsn("."); - super.visitInsn(ICONST_0); - super.visitTypeInsn(ANEWARRAY, "java/lang/String"); - super.visitMethodInsn(INVOKESTATIC, "java/nio/file/Path", "of", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;", true); - super.visitTypeInsn(NEW, "org/geysermc/geyser/extension/event/GeyserExtensionEventBus"); - super.visitInsn(DUP); - super.visitMethodInsn(INVOKESTATIC, "org/geysermc/geyser/GeyserImpl", "getInstance", "()Lorg/geysermc/geyser/GeyserImpl;", false); - super.visitMethodInsn(INVOKEVIRTUAL, "org/geysermc/geyser/GeyserImpl", "eventBus", "()Lorg/geysermc/geyser/api/event/EventBus;", false); - super.visitVarInsn(ALOAD, 2); - super.visitMethodInsn(INVOKESPECIAL, "org/geysermc/geyser/extension/event/GeyserExtensionEventBus", "", "(Lorg/geysermc/geyser/api/event/EventBus;Lorg/geysermc/geyser/api/extension/Extension;)V", false); - super.visitMethodInsn(INVOKESPECIAL, "org/geysermc/geyser/extension/GeyserExtensionLoader", "setup", "(Lorg/geysermc/geyser/api/extension/Extension;Lorg/geysermc/geyser/extension/GeyserExtensionDescription;Ljava/nio/file/Path;Lorg/geysermc/geyser/api/event/ExtensionEventBus;)Lorg/geysermc/geyser/extension/GeyserExtensionContainer;", false); - super.visitVarInsn(ASTORE, 4); - super.visitVarInsn(ALOAD, 0); - super.visitFieldInsn(GETFIELD, "org/geysermc/geyser/extension/GeyserExtensionLoader", "extensionContainers", "Ljava/util/Map;"); - super.visitVarInsn(ALOAD, 2); - super.visitVarInsn(ALOAD, 4); - super.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true); - super.visitInsn(POP); - super.visitVarInsn(ALOAD, 0); - super.visitVarInsn(ALOAD, 2); - super.visitVarInsn(ALOAD, 1); - super.visitMethodInsn(INVOKEVIRTUAL, "org/geysermc/geyser/extension/GeyserExtensionLoader", "register", "(Lorg/geysermc/geyser/api/extension/Extension;Lorg/geysermc/geyser/api/extension/ExtensionManager;)V", false); - super.visitLabel(tryEnd); - super.visitJumpInsn(GOTO, exit); - super.visitLabel(catchHandler); - super.visitTypeInsn(NEW, "java/lang/RuntimeException"); - super.visitInsn(DUP_X1); - super.visitInsn(SWAP); - super.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "", "(Ljava/lang/Throwable;)V", false); - super.visitInsn(ATHROW); - super.visitLabel(exit); - } - super.visitInsn(opcode); + private static byte[] patch(byte[] bytes) { + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ByteArrayClassPath("org.geysermc.geyser.extension.GeyserExtensionLoader", bytes)); + + try { + CtClass cc = pool.get("org.geysermc.geyser.extension.GeyserExtensionLoader"); + CtMethod method = cc.getDeclaredMethod("loadAllExtensions"); + String src = """ + org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt extension = new org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt(); + try { + java.io.InputStreamReader reader = new java.io.InputStreamReader(org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt.class.getResourceAsStream("/extension.yml")); + org.geysermc.geyser.extension.GeyserExtensionDescription description = org.geysermc.geyser.extension.GeyserExtensionDescription.fromYaml(reader); + reader.close(); + + java.nio.file.Path path = java.nio.file.Path.of(".", new String[0]); + org.geysermc.geyser.api.event.EventBus eventBus = org.geysermc.geyser.GeyserImpl.getInstance().eventBus(); + org.geysermc.geyser.extension.event.GeyserExtensionEventBus extensionEventBus = new org.geysermc.geyser.extension.event.GeyserExtensionEventBus(eventBus, extension); + org.geysermc.geyser.extension.GeyserExtensionContainer container = this.setup(extension, description, path, extensionEventBus); + + this.extensionContainers.put(extension, container); + this.register(extension, $1); + } catch (Throwable t) { + throw new RuntimeException(t); + }"""; + method.insertAfter(src, false); + return cc.toBytecode(); + } catch (Exception e) { + throw new RuntimeException(e); } } } From b6554aa215ff073cbca39a9a620cdc618626039b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Wed, 27 Aug 2025 13:57:18 +0700 Subject: [PATCH 26/70] Form and more --- .../emotes/common/network/EmotePacket.java | 4 -- geyser/build.gradle.kts | 2 +- .../mods/emotecraft/geyser/EmotecraftExt.java | 41 +++++++++-- .../emotecraft/geyser/EmotecraftService.java | 22 ++++++ .../emotecraft/geyser/GeyserBootstrap.java | 2 +- .../geyser/handler/ConnectionType.java | 16 +++++ .../geyser/handler/GeyserNetworkInstance.java | 68 ++++++++++++++----- .../geyser/utils/EmotecraftLocale.java | 58 ++++++++++++++++ .../emotecraft/geyser/utils/FormUtils.java | 57 ++++++++++++++++ ...smx.emotes.server.services.InstanceService | 1 + 10 files changed, 242 insertions(+), 29 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/ConnectionType.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java create mode 100644 geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService diff --git a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java index 7b861ca64..d149196fa 100644 --- a/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java +++ b/emotesAPI/src/main/java/io/github/kosmx/emotes/common/network/EmotePacket.java @@ -272,9 +272,5 @@ public Builder strictSizeLimit(boolean strict) { data.strictSizeLimit = strict; return this; } - - public NetData getData() { - return this.data; - } } } diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 40bd6e13b..6a8f6b6ab 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { implementation("org.geysermc.geyser:standalone:${properties["geyser_version"] as String}") compileApi(project(":emotesAssets")) - compileApi(project(":emotesAPI")) { + compileApi(project(":emotesServer")) { exclude(group = "org.jetbrains", module = "annotations") exclude(module = "gson") diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index c701943e1..9344029a1 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -2,6 +2,10 @@ import com.zigythebird.playeranimcore.animation.Animation; import io.github.kosmx.emotes.common.CommonData; +import io.github.kosmx.emotes.common.SerializableConfig; +import io.github.kosmx.emotes.server.config.ConfigSerializer; +import io.github.kosmx.emotes.server.config.Serializer; +import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.adventure.key.Key; @@ -9,9 +13,11 @@ import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.session.GeyserSession; @@ -23,6 +29,7 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider; +import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.ConnectionType; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; @@ -66,6 +73,9 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { CommonData.LOGGER.warn("Note that this extension does some horrible hacks on geyser."); CommonData.LOGGER.warn("Until custom packet event is added, workarounds cannot be avoided."); + Serializer.INSTANCE = new Serializer<>(new ConfigSerializer<>(SerializableConfig::new), SerializableConfig.class); + UniversalEmoteSerializer.loadEmotes(); + GayserHacks.addCustomJavaTranslator(ClientboundCustomPayloadPacket.class, (session, packet) -> { Key type = packet.getChannel(); if (CommonData.MOD_ID.equals(type.namespace())) { // Any emotecraft payload @@ -105,24 +115,24 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] if (((ProtocolProvider) session).ec$state() == ProtocolState.GAME) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); - if (!networkInstance.isHandShaked()) { + if (networkInstance.getConnectionType() == ConnectionType.NONE) { CommonData.LOGGER.warn("The server failed to configure the client, attempting to configure..."); networkInstance.sendC2SConfig(); } } - } else { + } /*else { // Online-emotes integration? - } + }*/ } private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); - if (!networkInstance.isHandShaked()) { + if (networkInstance.getConnectionType() == ConnectionType.NONE) { if (((ProtocolProvider) session).ec$state() == ProtocolState.CONFIGURATION) { CommonData.LOGGER.debug("Configuring emotecraft..."); networkInstance.sendC2SConfig(); } - networkInstance.setHandShaked(true); + networkInstance.setConnectionType(ConnectionType.BACKEND); } networkInstance.receiveMessage(bytes); } @@ -135,13 +145,30 @@ public void onSessionInitialize(SessionInitializeEvent event) { @Subscribe public void onSessionDisconnect(SessionDisconnectEvent event) { - EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); + GeyserNetworkInstance instance = EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); + if (instance != null) instance.disconnect(); + } + + @Subscribe + public void onDefineCommands(GeyserDefineCommandsEvent event) { + event.register(Command.builder(this) + .name(rootCommand()) + .bedrockOnly(true) + .source(GeyserSession.class) + .aliases(List.of("emotes", "form")) + .description("Emotecraft command") + .playerOnly(true) + .executor((source, cmd, args) -> + EmotecraftExt.INSTANCES.get((GeyserSession) source).showForm() + ) + .build() + ); } @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); - if (networkInstance != null && networkInstance.isHandShaked()) { + if (networkInstance != null && networkInstance.getConnectionType() != ConnectionType.NONE) { CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); if (animation.isDone() && !animation.isCompletedExceptionally()) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java new file mode 100644 index 000000000..985c053c5 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java @@ -0,0 +1,22 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser; + +import io.github.kosmx.emotes.server.services.InstanceService; + +import java.nio.file.Path; + +public class EmotecraftService implements InstanceService { + @Override + public Path getGameDirectory() { + return EmotecraftExt.getInstance().dataFolder(); + } + + @Override + public Path getConfigPath() { + return getGameDirectory().resolve("emotecraft.json"); + } + + @Override + public boolean isActive() { + return EmotecraftExt.getInstance() != null && EmotecraftExt.getInstance().isEnabled(); + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index a084a2a24..7d8103cde 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -37,7 +37,7 @@ private static byte[] patch(byte[] bytes) { org.geysermc.geyser.extension.GeyserExtensionDescription description = org.geysermc.geyser.extension.GeyserExtensionDescription.fromYaml(reader); reader.close(); - java.nio.file.Path path = java.nio.file.Path.of(".", new String[0]); + java.nio.file.Path path = this.extensionsDirectory.resolve(description.id()); org.geysermc.geyser.api.event.EventBus eventBus = org.geysermc.geyser.GeyserImpl.getInstance().eventBus(); org.geysermc.geyser.extension.event.GeyserExtensionEventBus extensionEventBus = new org.geysermc.geyser.extension.event.GeyserExtensionEventBus(eventBus, extension); org.geysermc.geyser.extension.GeyserExtensionContainer container = this.setup(extension, description, path, extensionEventBus); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/ConnectionType.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/ConnectionType.java new file mode 100644 index 000000000..89d8e080c --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/ConnectionType.java @@ -0,0 +1,16 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.handler; + +import org.jetbrains.annotations.Nullable; + +public enum ConnectionType { + NONE("emotecraft.no_server"), + BACKEND(null)/*, + PROXY("emotecraft.only_proxy")*/; + + @Nullable + public final String translation; + + ConnectionType(@Nullable String translation) { + this.translation = translation; + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 81b1d6c97..deeb06060 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -8,28 +8,31 @@ import io.github.kosmx.emotes.common.network.EmotePacket; import io.github.kosmx.emotes.common.network.PacketConfig; import io.github.kosmx.emotes.common.network.objects.NetData; +import io.github.kosmx.emotes.common.tools.UUIDMap; +import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; +import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; public class GeyserNetworkInstance extends AbstractNetworkInstance { private final HashMap versions = new HashMap<>(); - private final Map queue = new ConcurrentHashMap<>(); + // private final Map queue = new ConcurrentHashMap<>(); + private final UUIDMap animations = new UUIDMap<>(); private final GeyserSession session; private UUID currentEmote; - private boolean isHandShaked; + private ConnectionType connectionType = ConnectionType.NONE; public GeyserNetworkInstance(GeyserSession session) { this.session = session; @@ -99,9 +102,9 @@ private void handleNetData(NetData data) { if (isMainPlayer(playerEntity)) { this.currentEmote = data.emoteData.get(); } - } else { + } /*else { // this.queue.put(data.player, new QueueEntry(data.emoteData, data.tick, ClientMethods.getCurrentTick())); - } + }*/ break; case STOP: @@ -116,7 +119,8 @@ private void handleNetData(NetData data) { sendChatMessage("emotecraft.blockedEmote"); } } else { - this.queue.remove(data.player); + // this.queue.remove(data.player); + CommonData.LOGGER.warn("Queue is not supported!"); } break; case CONFIG: @@ -124,7 +128,7 @@ private void handleNetData(NetData data) { break; case FILE: - // TODO add bedrock form + this.animations.add(data.emoteData); break; case UNKNOWN: @@ -133,12 +137,33 @@ private void handleNetData(NetData data) { } } + public void showForm() { + SimpleForm.Builder builder = SimpleForm.builder() + .translator(EmotecraftLocale::getLocaleString, this.session.locale()) + .title(CommonData.MOD_NAME); + if (this.connectionType.translation != null) builder.content(this.connectionType.translation); + + for (Animation animation : UniversalEmoteSerializer.getLoadedEmotes().values()) { + builder.button(FormUtils.createButtonComponent(animation, this.session.locale())); + } + for (Animation animation : this.animations.values()) { + builder.button(FormUtils.createButtonComponent(animation, this.session.locale())); + } + + SimpleForm simpleForm = builder.validResultHandler((form, response) -> { + UUID emoteId = FormUtils.extractAnimationFromButton(response.clickedButton()); + Animation animation = this.animations.getOrDefault(emoteId, UniversalEmoteSerializer.getEmote(emoteId)); + if (animation != null) playEmote(animation, true); + }).build(); + this.session.sendForm(simpleForm); + } + public void stopEmote() { stopEmote(this.session.getPlayerEntity()); } public void stopEmote(PlayerEntity player) { - this.session.showEmote(player, "idk"); + this.session.showEmote(player, ""); if (isMainPlayer(player) && this.currentEmote != null) { try { @@ -152,7 +177,7 @@ public void stopEmote(PlayerEntity player) { } public void sendChatMessage(String key) { - this.session.sendChat(MinecraftLocale.getLocaleString(key, this.session.locale())); + this.session.sendMessage(EmotecraftLocale.getLocaleString(key, this.session.locale())); } public void playEmote(Animation animation, boolean local) { @@ -160,7 +185,7 @@ public void playEmote(Animation animation, boolean local) { try { sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); if (local) { - this.session.showEmote(this.session.getPlayerEntity(), animation.get().toString()); + this.session.showEmote(this.session.getPlayerEntity(), "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate } this.currentEmote = animation.get(); } catch (Throwable th) { @@ -204,15 +229,26 @@ public void sendC2SConfig() { }); } - public void setHandShaked(boolean is) { - this.isHandShaked = is; + public void setConnectionType(ConnectionType type) { + this.connectionType = type; } - public boolean isHandShaked() { - return this.isHandShaked; + public ConnectionType getConnectionType() { + return this.connectionType; } public boolean isPlaying() { return this.currentEmote != null; } + + @Override + public void disconnect() { + if (this.currentEmote != null) { + stopEmote(); + this.currentEmote = null; + } + this.connectionType = ConnectionType.NONE; + this.animations.clear(); + this.versions.clear(); + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java new file mode 100644 index 000000000..b4d5aa533 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java @@ -0,0 +1,58 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import io.github.kosmx.emotes.common.CommonData; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.text.GeyserLocale; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +public class EmotecraftLocale { + private static final Map> LOCALE_MAPPINGS = new HashMap<>(); + + static { + loadLocale("en_us"); + loadLocale(GeyserLocale.getDefaultLocale().toLowerCase(Locale.ROOT)); + } + + public static void loadLocale(String locale) { + if (LOCALE_MAPPINGS.containsKey(locale)) return; + + try (InputStream localeStream = EmotecraftLocale.class.getResourceAsStream("/assets/emotecraft/lang/" + locale + ".json")) { + JsonNode localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream); + Iterator> localeIterator = localeObj.fields(); + Map langMap = new HashMap<>(); + + while (localeIterator.hasNext()) { + Map.Entry entry = localeIterator.next(); + langMap.put(entry.getKey(), entry.getValue().asText()); + } + + LOCALE_MAPPINGS.put(locale, langMap); + } catch (FileNotFoundException e) { + throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage())); + } catch (Exception e) { + throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.json", locale), e); + } + } + + public static String getLocaleString(String messageText, String locale) { + loadLocale(locale.toLowerCase(Locale.ROOT)); + + Map localeStrings = LOCALE_MAPPINGS.get(locale.toLowerCase(Locale.ROOT)); + if (localeStrings == null) { + localeStrings = LOCALE_MAPPINGS.get(GeyserLocale.getDefaultLocale().toLowerCase(Locale.ROOT)); + + if (localeStrings == null) { + CommonData.LOGGER.warn("MISSING DEFAULT LOCALE: {}", GeyserLocale.getDefaultLocale()); + return messageText; + } + } + return localeStrings.getOrDefault(messageText, messageText); + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java new file mode 100644 index 000000000..64962c48a --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java @@ -0,0 +1,57 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import com.zigythebird.playeranimcore.animation.Animation; +import net.kyori.adventure.text.Component; +import org.geysermc.cumulus.component.ButtonComponent; +import org.geysermc.cumulus.util.FormImage; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; +import java.util.UUID; + +public class FormUtils { + private static final Component AUTHOR = Component.translatable("emotecraft.emote.author"); + + public static ButtonComponent createButtonComponent(Animation animation, String locale) { + return ButtonComponent.of(formatAnimationName(animation, locale), FormImage.of(FormImage.Type.URL, + String.format("https://bot.redlance.org/api/emotes/icon/%s.png#%s", animation.boneAnimations().hashCode(), animation.uuid()) + )); + } + + private static String formatAnimationName(Animation animation, String locale) { + if (animation.data().getRaw("name") instanceof String rawName) { + String name = EmotecraftLocale.getLocaleString(MessageTranslator.convertMessageLenient(rawName, locale), locale); // TODO fallbacks + + /*if (animation.data().getRaw("description") instanceof String description) { // Doesn't fit + name = String.format("%s\n%s", name, EmotecraftLocale.getLocaleString( + MessageTranslator.convertMessageLenient(description, locale), locale + )); + }*/ + + if (animation.data().getRaw("author") instanceof String rawAuthor) { + String author = EmotecraftLocale.getLocaleString(MessageTranslator.convertMessage(AUTHOR, locale), locale); + name = String.format("%s\n(%s %s)", name, author, MessageTranslator.convertMessageLenient(rawAuthor, locale)); + } + + return name; + } + return String.format("INVALID: %s", animation.uuid()); + } + + @Nullable + public static UUID extractAnimationFromButton(@NotNull ButtonComponent button) { + FormImage image = button.image(); + if (image == null) return null; + return extractAnimationFromImage(image); + } + + @Nullable + public static UUID extractAnimationFromImage(@NotNull FormImage button) { + return switch (button.type()) { + case URL -> UUID.fromString(URI.create(button.data()).getFragment()); + case PATH -> null; // TODO + }; + } +} diff --git a/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService new file mode 100644 index 000000000..1102bbde7 --- /dev/null +++ b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService @@ -0,0 +1 @@ +org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftService \ No newline at end of file From 942e43792af2916147d1119ecdcb8672e8b08bec Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Wed, 27 Aug 2025 14:06:49 +0700 Subject: [PATCH 27/70] Remove space --- .../dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java index 64962c48a..47922f4cc 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/FormUtils.java @@ -32,7 +32,7 @@ private static String formatAnimationName(Animation animation, String locale) { if (animation.data().getRaw("author") instanceof String rawAuthor) { String author = EmotecraftLocale.getLocaleString(MessageTranslator.convertMessage(AUTHOR, locale), locale); - name = String.format("%s\n(%s %s)", name, author, MessageTranslator.convertMessageLenient(rawAuthor, locale)); + name = String.format("%s\n(%s%s)", name, author, MessageTranslator.convertMessageLenient(rawAuthor, locale)); } return name; From 47647208618aa38b7d982575f4e978fe99929882 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 31 Oct 2025 14:42:52 +0700 Subject: [PATCH 28/70] Update --- .../mods/emotecraft/geyser/EmotecraftExt.java | 8 +++--- .../geyser/fuckery/GeyserSessionPatch.java | 27 ++----------------- .../geyser/handler/GeyserNetworkInstance.java | 2 +- gradle.properties | 2 +- 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 9344029a1..ed520165f 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -57,9 +57,9 @@ public class EmotecraftExt implements Extension { public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); - public static final Key EMOTECAFT_EMOTE_TYPE = Key.key(CommonData.MOD_ID, CommonData.playEmoteID); - public static final Key EMOTECAFT_STREAM_TYPE = Key.key(CommonData.MOD_ID, CommonData.emoteStreamID); - private static final Set EMOTECRAFT_CHANNELS = Set.of(EMOTECAFT_EMOTE_TYPE, EMOTECAFT_STREAM_TYPE); + public static final Key EMOTECRAFT_EMOTE_TYPE = Key.key(CommonData.MOD_ID, CommonData.playEmoteID); + public static final Key EMOTECRAFT_STREAM_TYPE = Key.key(CommonData.MOD_ID, CommonData.emoteStreamID); + private static final Set EMOTECRAFT_CHANNELS = Set.of(EMOTECRAFT_EMOTE_TYPE, EMOTECRAFT_STREAM_TYPE); private static EmotecraftExt instance; @@ -105,7 +105,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); CommonData.LOGGER.debug("Server listening channels: {}", channels); - if (channels.contains(EmotecraftExt.EMOTECAFT_EMOTE_TYPE)) { + if (channels.contains(EmotecraftExt.EMOTECRAFT_EMOTE_TYPE)) { CommonData.LOGGER.debug("Has emotecraft!"); ByteBuf byteBuf = Unpooled.buffer(); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java index cdfe245ae..531637386 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java @@ -17,11 +17,6 @@ public class GeyserSessionPatch { public static final String CLASS_NAME = "org.geysermc.geyser.session.GeyserSession"; - @SuppressWarnings("unused") - public static void hook(StartGamePacket packet) { - System.out.println(packet); - } - public static byte[] patch(byte[] bytes) { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(new ByteArrayClassPath(CLASS_NAME, bytes)); @@ -31,29 +26,11 @@ public static byte[] patch(byte[] bytes) { CtClass protocolProviderInterface = pool.get("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider"); cc.addInterface(protocolProviderInterface); - - CtMethod ctMethod = CtNewMethod.make(""" + cc.addMethod(CtNewMethod.make(""" public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$protocol() { return this.protocol; }""", cc - ); - - MethodInfo methodInfo = ctMethod.getMethodInfo(); - ConstPool cp = methodInfo.getConstPool(); - AnnotationsAttribute attr = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); - attr.addAnnotation(new javassist.bytecode.annotation.Annotation(Override.class.getName(), cp)); - methodInfo.addAttribute(attr); - - cc.addMethod(ctMethod); - - CtMethod startGameMethod = cc.getDeclaredMethod("startGame"); - startGameMethod.instrument(new ExprEditor() { - public void edit(MethodCall m) throws CannotCompileException { - if (m.getClassName().equals("org.geysermc.geyser.session.UpstreamSession") && m.getMethodName().equals("sendPacket")) { - m.replace("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch.hook(startGamePacket); $_ = $proceed($$);"); - } - } - }); + )); return cc.toBytecode(); } catch (Exception ex) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index deeb06060..cdd9781af 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -57,7 +57,7 @@ public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) thro @Override protected void sendMessage(byte[] bytes, @Nullable UUID target) { this.session.sendDownstreamPacket(new ServerboundCustomPayloadPacket( - EmotecraftExt.EMOTECAFT_EMOTE_TYPE, bytes + EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, bytes )); } diff --git a/gradle.properties b/gradle.properties index 75fd51358..e633820dc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,4 +32,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.2 fabric_permissions_api = 0.4.0 noteblocklib_version = 3.1.1 - geyser_version = 2.8.3-SNAPSHOT + geyser_version = 2.9.0-SNAPSHOT From 36b7f56a2d87c40d7c02380171eb7c43f631f96c Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 31 Oct 2025 14:53:35 +0700 Subject: [PATCH 29/70] Less internals --- .../mods/emotecraft/geyser/EmotecraftExt.java | 15 ++++++----- .../geyser/handler/GeyserNetworkInstance.java | 26 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index ed520165f..3e725824b 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -14,6 +14,7 @@ import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; @@ -53,7 +54,7 @@ public class EmotecraftExt implements Extension { } } - private static final Map INSTANCES = new ConcurrentHashMap<>(); + private static final Map INSTANCES = new ConcurrentHashMap<>(); public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); @@ -125,7 +126,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] }*/ } - private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] bytes) { + private void onEmotecraftPayload(GeyserConnection session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); if (networkInstance.getConnectionType() == ConnectionType.NONE) { if (((ProtocolProvider) session).ec$state() == ProtocolState.CONFIGURATION) { @@ -139,13 +140,13 @@ private void onEmotecraftPayload(GeyserSession session, Key channel, byte[] byte @Subscribe public void onSessionInitialize(SessionInitializeEvent event) { - GeyserSession session = (GeyserSession) event.connection(); + GeyserConnection session = event.connection(); EmotecraftExt.INSTANCES.put(session, new GeyserNetworkInstance(session)); } @Subscribe public void onSessionDisconnect(SessionDisconnectEvent event) { - GeyserNetworkInstance instance = EmotecraftExt.INSTANCES.remove((GeyserSession) event.connection()); + GeyserNetworkInstance instance = EmotecraftExt.INSTANCES.remove(event.connection()); if (instance != null) instance.disconnect(); } @@ -154,12 +155,12 @@ public void onDefineCommands(GeyserDefineCommandsEvent event) { event.register(Command.builder(this) .name(rootCommand()) .bedrockOnly(true) - .source(GeyserSession.class) + .source(GeyserConnection.class) .aliases(List.of("emotes", "form")) .description("Emotecraft command") .playerOnly(true) .executor((source, cmd, args) -> - EmotecraftExt.INSTANCES.get((GeyserSession) source).showForm() + EmotecraftExt.INSTANCES.get((GeyserConnection) source).showForm() ) .build() ); @@ -167,7 +168,7 @@ public void onDefineCommands(GeyserDefineCommandsEvent event) { @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { - GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get((GeyserSession) event.connection()); + GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(event.connection()); if (networkInstance != null && networkInstance.getConnectionType() != ConnectionType.NONE) { CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index cdd9781af..382b6fc1c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -11,6 +11,8 @@ import io.github.kosmx.emotes.common.tools.UUIDMap; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; import org.geysermc.cumulus.form.SimpleForm; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; @@ -29,12 +31,12 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { private final HashMap versions = new HashMap<>(); // private final Map queue = new ConcurrentHashMap<>(); private final UUIDMap animations = new UUIDMap<>(); - private final GeyserSession session; + private final GeyserConnection session; private UUID currentEmote; private ConnectionType connectionType = ConnectionType.NONE; - public GeyserNetworkInstance(GeyserSession session) { + public GeyserNetworkInstance(GeyserConnection session) { this.session = session; } @@ -56,7 +58,7 @@ public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) thro @Override protected void sendMessage(byte[] bytes, @Nullable UUID target) { - this.session.sendDownstreamPacket(new ServerboundCustomPayloadPacket( + ((GeyserSession) this.session).sendDownstreamPacket(new ServerboundCustomPayloadPacket( EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, bytes )); } @@ -97,7 +99,7 @@ private void handleNetData(NetData data) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); //playerEntity.emotecraft$playEmote(data.emoteData, data.tick, data.isForced); - this.session.showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + this.session.entities().showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate if (isMainPlayer(playerEntity)) { this.currentEmote = data.emoteData.get(); @@ -159,11 +161,11 @@ public void showForm() { } public void stopEmote() { - stopEmote(this.session.getPlayerEntity()); + stopEmote(this.session.entities().playerEntity()); } - public void stopEmote(PlayerEntity player) { - this.session.showEmote(player, ""); + public void stopEmote(GeyserPlayerEntity player) { + this.session.entities().showEmote(player, ""); if (isMainPlayer(player) && this.currentEmote != null) { try { @@ -185,7 +187,7 @@ public void playEmote(Animation animation, boolean local) { try { sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); if (local) { - this.session.showEmote(this.session.getPlayerEntity(), "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + this.session.entities().showEmote(this.session.entities().playerEntity(), "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate } this.currentEmote = animation.get(); } catch (Throwable th) { @@ -195,13 +197,13 @@ public void playEmote(Animation animation, boolean local) { public PlayerEntity getPlayerFromUUID(UUID uuid) { if (this.session.javaUuid().equals(uuid)) { - return this.session.getPlayerEntity(); + return (PlayerEntity) this.session.entities().playerEntity(); } - return this.session.getEntityCache().getPlayerEntity(uuid); + return ((GeyserSession) this.session).getEntityCache().getPlayerEntity(uuid); } - public boolean isMainPlayer(PlayerEntity player) { - return player != null && this.session.javaUuid().equals(player.getUuid()); + public boolean isMainPlayer(GeyserPlayerEntity geyserPlayer) { + return geyserPlayer instanceof PlayerEntity player && this.session.javaUuid().equals(player.getUuid()); } @Override From 14991f799d59d5b7c8fb32d3f88bddb32b0e33b6 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 1 Nov 2025 21:23:31 +0700 Subject: [PATCH 30/70] Java2Bedrock WIP Closes #684 --- build.gradle.kts | 2 +- .../mods/emotecraft/geyser/EmotecraftExt.java | 16 ++ .../animator/GeyserAnimationController.java | 167 ++++++++++++ .../geyser/handler/GeyserNetworkInstance.java | 29 ++- .../geyser/utils/EmoteResourcePack.java | 238 ++++++++++++++++++ .../utils/SeekableInMemoryByteChannel.java | 208 +++++++++++++++ .../ResourcePackVersionSerializer.java | 19 ++ 7 files changed, 672 insertions(+), 7 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java diff --git a/build.gradle.kts b/build.gradle.kts index 741ee4e57..d2fea2116 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ subprojects { group = "io.github.kosmx.emotes" repositories { + mavenLocal() maven("https://api.modrinth.com/maven") { name = "Modrinth" content { @@ -37,7 +38,6 @@ subprojects { maven("https://repo.opencollab.dev/main/") { name = "Geyser" } - mavenLocal() } tasks.withType(JavaCompile::class).configureEach { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 3e725824b..3caee9ed6 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -20,7 +20,9 @@ import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.pack.GeyserResourcePackManifest; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.util.MinecraftKey; @@ -34,6 +36,7 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmoteResourcePack; import java.io.IOException; import java.util.*; @@ -62,12 +65,21 @@ public class EmotecraftExt implements Extension { public static final Key EMOTECRAFT_STREAM_TYPE = Key.key(CommonData.MOD_ID, CommonData.emoteStreamID); private static final Set EMOTECRAFT_CHANNELS = Set.of(EMOTECRAFT_EMOTE_TYPE, EMOTECRAFT_STREAM_TYPE); + private final EmoteResourcePack resourcePack = new EmoteResourcePack( + new GeyserResourcePackManifest.Version(1, 0, 0), CommonData.MOD_NAME, CommonData.MOD_NAME + ); + private static EmotecraftExt instance; public EmotecraftExt() { EmotecraftExt.instance = this; } + @Subscribe + public void onPreInitialize(GeyserPreInitializeEvent event) { + eventBus().register(this.resourcePack); + } + @Subscribe(postOrder = PostOrder.LAST) public void onPostInitialize(GeyserPostInitializeEvent event) { CommonData.LOGGER.info("Loading emotecraft on geyser..."); @@ -191,6 +203,10 @@ public void onEmote(ClientEmoteEvent event) { } } + public EmoteResourcePack getResourcePack() { + return this.resourcePack; + } + public static EmotecraftExt getInstance() { return EmotecraftExt.instance; } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java new file mode 100644 index 000000000..b68d3ec03 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -0,0 +1,167 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; + +import com.zigythebird.playeranimcore.animation.AnimationController; +import com.zigythebird.playeranimcore.animation.AnimationData; +import com.zigythebird.playeranimcore.bones.PlayerAnimBone; +import com.zigythebird.playeranimcore.enums.Axis; +import com.zigythebird.playeranimcore.enums.PlayState; +import com.zigythebird.playeranimcore.enums.TransformType; +import com.zigythebird.playeranimcore.math.Vec3f; +import com.zigythebird.playeranimcore.molang.MolangLoader; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityProperty; +import org.cloudburstmc.protocol.bedrock.packet.AnimateEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.geyser.api.entity.property.GeyserEntityProperty; +import org.geysermc.geyser.api.entity.property.type.GeyserIntEntityProperty; +import org.geysermc.geyser.api.util.Identifier; +import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager; +import org.geysermc.geyser.entity.properties.type.PropertyType; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmoteResourcePack; + +import java.time.Duration; +import java.util.*; + +/** + * Bends in the bedrock are not supported, so this feature is not implemented here. + */ +public class GeyserAnimationController extends AnimationController implements Runnable { + // Bone pivot point positions used to apply custom pivot point translations. + public static final Map BONE_POSITIONS = Map.of( + "right_arm", new Vec3f(5, 22, 0), + "left_arm", new Vec3f(-5, 22, 0), + "left_leg", new Vec3f(-2f, 12, 0f), + "right_leg", new Vec3f(2f, 12, 0f), + "torso", new Vec3f(0, 24, 0), + "head", new Vec3f(0, 24, 0), + "body", new Vec3f(0, 12, 0), + "cape", new Vec3f(0, 24, 2), + "elytra", new Vec3f(0, 24, 2) + ); + + private final Set lastUsedProperties = new HashSet<>(1); + private final PlayerEntity playerEntity; + + public GeyserAnimationController(PlayerEntity playerEntity) { + super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); + this.playerEntity = playerEntity; + } + + @Override + public void registerBones() { + this.registerPlayerAnimBone("body"); + this.registerPlayerAnimBone("right_arm"); + this.registerPlayerAnimBone("left_arm"); + this.registerPlayerAnimBone("right_leg"); + this.registerPlayerAnimBone("left_leg"); + this.registerPlayerAnimBone("head"); + this.registerPlayerAnimBone("torso"); + this.registerPlayerAnimBone("right_item"); + this.registerPlayerAnimBone("left_item"); + this.registerPlayerAnimBone("cape"); + this.registerPlayerAnimBone("elytra"); + } + + @Override + public void run() { + // Check propertyManager + GeyserEntityPropertyManager propertyManager = this.playerEntity.getPropertyManager(); + if (propertyManager == null) return; + + // Start animation + AnimateEntityPacket animatePacket = new AnimateEntityPacket(); + animatePacket.setAnimation(EmoteResourcePack.ANIMATION_NAME); + animatePacket.setNextState("default"); + animatePacket.setBlendOutTime(0.0f); + animatePacket.setStopExpression("query.any_animation_finished"); + animatePacket.setController("__runtime_controller"); + animatePacket.getRuntimeEntityIds().add(this.playerEntity.getGeyserId()); + this.playerEntity.getSession().sendUpstreamPacket(animatePacket); + + AnimationData data = new AnimationData(0, 0.0F); + setupAnim(data); + tick(data); + + // Animate via properties + for (String partKey : BONE_POSITIONS.keySet()) { + PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); + + updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); + updateAxis(propertyManager, partKey, TransformType.ROTATION, bone.getRotX(), bone.getRotY(), bone.getRotZ()); + } + + // Flush + flushPropertiesImmediately(); + } + + private void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { + Map ids = EmotecraftExt.getInstance().getResourcePack().getAxisIds(partKey, type); + int packedX = pack(ids.get(Axis.X), x); + int packedY = pack(ids.get(Axis.Y), y); + int packedZ = pack(ids.get(Axis.Z), z); + + System.out.printf("%s %s: X(id=%d, val=%.2f, packed=%d), Y(id=%d, val=%.2f, packed=%d), Z(id=%d, val=%.2f, packed=%d)%n", + partKey, type, ids.get(Axis.X), x, packedX, ids.get(Axis.Y), y, packedY, ids.get(Axis.Z), z, packedZ + ); + + updateProperty(propertyManager, getAvailableProperty(), packedX); + updateProperty(propertyManager, getAvailableProperty(), packedY); + updateProperty(propertyManager, getAvailableProperty(), packedZ); + } + + private GeyserIntEntityProperty getAvailableProperty() { + for (GeyserIntEntityProperty property : EmotecraftExt.getInstance().getResourcePack().getRegisteredProperties()) { + if (this.lastUsedProperties.contains(property.identifier())) continue; + this.lastUsedProperties.add(property.identifier()); + return property; + } + + // Try flush + flushPropertiesImmediately(); + return getAvailableProperty(); + } + + public static void updateProperty(GeyserEntityPropertyManager propertyManager, @NonNull GeyserEntityProperty property, @Nullable T value) { + Objects.requireNonNull(property, "property must not be null!"); + if (!(property instanceof PropertyType propertyType)) { + throw new IllegalArgumentException("Invalid property implementation! Got: " + property.getClass().getSimpleName()); + } + propertyType.apply(propertyManager, value); + } + + private void flushPropertiesImmediately() { + GeyserEntityPropertyManager propertyManager = this.playerEntity.getPropertyManager(); + if (propertyManager == null || !propertyManager.hasProperties()) return; + + SetEntityDataPacket packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(this.playerEntity.getGeyserId()); + propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); + propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); + this.playerEntity.getSession().sendUpstreamPacketImmediately(packet); + + try { + Thread.sleep(Duration.ofMillis(10)); // IDK + } catch (InterruptedException ignored) {} + this.lastUsedProperties.clear(); + } + + @Override + public Vec3f getBonePosition(String name) { + if (BONE_POSITIONS.containsKey(name)) return BONE_POSITIONS.get(name); + if (pivotBones.containsKey(name)) return pivotBones.get(name).getPivot(); + return Vec3f.ZERO; + } + + public static int pack(int id, float value) { + id = Math.max(0, Math.min(99, id)); + value = Math.max(-9999.99f, Math.min(9999.99f, value)); + + int intValue = Math.round(value * 100f); + intValue = intValue + 1000000; + + return id * 10000000 + intValue; + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 382b6fc1c..cd1e681cd 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -18,26 +18,35 @@ import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Objects; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.*; public class GeyserNetworkInstance extends AbstractNetworkInstance { + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); + private final HashMap versions = new HashMap<>(); // private final Map queue = new ConcurrentHashMap<>(); private final UUIDMap animations = new UUIDMap<>(); private final GeyserConnection session; + private final Future ticker; + + private final Map controllers = new WeakHashMap<>(); private UUID currentEmote; private ConnectionType connectionType = ConnectionType.NONE; public GeyserNetworkInstance(GeyserConnection session) { this.session = session; + + this.ticker = EXECUTOR.scheduleAtFixedRate(() -> this.controllers.values() + .forEach(GeyserAnimationController::run), 0L, 50L, TimeUnit.MILLISECONDS + ); } @Override @@ -99,7 +108,10 @@ private void handleNetData(NetData data) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); //playerEntity.emotecraft$playEmote(data.emoteData, data.tick, data.isForced); - this.session.entities().showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + // this.session.entities().showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + + this.controllers.computeIfAbsent(playerEntity, GeyserAnimationController::new) + .triggerAnimation(data.emoteData, data.tick); if (isMainPlayer(playerEntity)) { this.currentEmote = data.emoteData.get(); @@ -165,6 +177,10 @@ public void stopEmote() { } public void stopEmote(GeyserPlayerEntity player) { + if (player instanceof PlayerEntity entity && this.controllers.containsKey(entity)) { + this.controllers.get(entity).stop(); + } + this.session.entities().showEmote(player, ""); if (isMainPlayer(player) && this.currentEmote != null) { @@ -173,9 +189,9 @@ public void stopEmote(GeyserPlayerEntity player) { } catch (IOException e) { CommonData.LOGGER.warn("Failed to stop animation!", e); } - } - this.currentEmote = null; + this.currentEmote = null; + } } public void sendChatMessage(String key) { @@ -252,5 +268,6 @@ public void disconnect() { this.connectionType = ConnectionType.NONE; this.animations.clear(); this.versions.clear(); + this.ticker.cancel(true); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java new file mode 100644 index 000000000..eceba1012 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java @@ -0,0 +1,238 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.zigythebird.playeranimcore.enums.Axis; +import com.zigythebird.playeranimcore.enums.TransformType; +import com.zigythebird.playeranimcore.loading.UniversalAnimLoader; +import io.github.kosmx.emotes.common.CommonData; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.PostOrder; +import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.entity.property.type.GeyserIntEntityProperty; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; +import org.geysermc.geyser.api.pack.PackCodec; +import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.ResourcePackManifest; +import org.geysermc.geyser.api.util.Identifier; +import org.geysermc.geyser.pack.GeyserResourcePack; +import org.geysermc.geyser.pack.GeyserResourcePackManifest; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.ResourcePackVersionSerializer; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@SuppressWarnings("unused") +public final class EmoteResourcePack extends PackCodec implements EventRegistrar { + private static final Identifier PLAYER_IDENTIFIER = Identifier.of(Identifier.DEFAULT_NAMESPACE, "player"); + public static final String ANIMATION_NAME = String.format("animation.%s", CommonData.MOD_ID); + + public static final ObjectMapper JSON_MAPPER = new ObjectMapper() + .registerModule(new SimpleModule() + .addSerializer(GeyserResourcePackManifest.Version.class, new ResourcePackVersionSerializer()) + ) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + + private final Map>> identifiers = new HashMap<>(); + private final Set registeredProperties = new HashSet<>(1); + private final ResourcePackManifest manifest; + + private String molangScript; + private byte[] packData; + private byte[] sha256; + + public EmoteResourcePack(GeyserResourcePackManifest.Version version, String name, String description) { + this(new GeyserResourcePackManifest(2, new GeyserResourcePackManifest.Header( + UUID.randomUUID(), version, name, description, new GeyserResourcePackManifest.Version(1, 16, 0)), + + Collections.singleton(new GeyserResourcePackManifest.Module( + UUID.randomUUID(), version, "resources", description + )), null, null, null + )); + } + + private EmoteResourcePack(ResourcePackManifest manifest) { + this.manifest = manifest; + } + + private JsonObject generateAnimation() { + JsonObject bones = new JsonObject(); + + int id = 0; + + this.identifiers.clear(); + for (String boneName : GeyserAnimationController.BONE_POSITIONS.keySet()) { + JsonObject bone = new JsonObject(); + + EnumMap> transformType = this.identifiers.computeIfAbsent(boneName, + k -> new EnumMap<>(TransformType.class) + ); + + bone.add("rotation", generateBone(id++, id++, id++, 0, transformType.computeIfAbsent( + TransformType.ROTATION, k -> new EnumMap<>(Axis.class) + ))); + bone.add("position", generateBone(id++, id++, id++, 0, transformType.computeIfAbsent( + TransformType.POSITION, k -> new EnumMap<>(Axis.class) + ))); + bone.add("scale", generateBone(id++, id++, id++, 1, transformType.computeIfAbsent( + TransformType.SCALE, k -> new EnumMap<>(Axis.class) + ))); + + String bedrockBone = UniversalAnimLoader.restorePlayerBoneName(boneName); + if ("body".equals(bedrockBone)) bedrockBone = "root"; + if ("torso".equals(bedrockBone)) bedrockBone = "body"; + bones.add(bedrockBone, bone); + } + + JsonObject animation = new JsonObject(); + animation.addProperty("animation_length", 1.0F); + animation.addProperty("loop", true); + animation.add("bones", bones); + + JsonObject container = new JsonObject(); + container.add(ANIMATION_NAME, animation); + + JsonObject parent = new JsonObject(); + parent.add("animations", container); + parent.addProperty("format_version", "1.8.0"); + + return parent; + } + + private byte[] generatePackData() { + if (this.packData != null) return this.packData; + + try ( + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos) + ) { + String manifestJson = EmoteResourcePack.JSON_MAPPER.writeValueAsString(this.manifest); + System.out.println(manifestJson); + zos.putNextEntry(new ZipEntry("manifest.json")); + zos.write(manifestJson.getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + + try (InputStream is = EmoteResourcePack.class.getClassLoader().getResourceAsStream("emotecraft_mod_logo.png")) { + zos.putNextEntry(new ZipEntry("pack_icon.png")); + zos.write(Objects.requireNonNull(is).readAllBytes()); + zos.closeEntry(); + } catch (Throwable th) { + CommonData.LOGGER.warn("Failed to put icon!", th); + } + + String animationJson = generateAnimation().toString(); + zos.putNextEntry(new ZipEntry("animations/player.animation.json")); + zos.write(animationJson.getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + + zos.finish(); + return this.packData = baos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("Failed to generate resource pack", e); + } + } + + private JsonArray generateBone(int x, int y, int z, float defaultValue, EnumMap axisMap) { + axisMap.put(Axis.X, x); + axisMap.put(Axis.Y, y); + axisMap.put(Axis.Z, z); + + JsonArray axis = new JsonArray(); + axis.add(this.molangScript + .replace("{BONE_ID}", String.valueOf(x)) + .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) + ); + axis.add(this.molangScript + .replace("{BONE_ID}", String.valueOf(y)) + .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) + ); + axis.add(this.molangScript + .replace("{BONE_ID}", String.valueOf(z)) + .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) + ); + return axis; + } + + @Override + public byte @NonNull [] sha256() { + if (this.sha256 != null) { + return this.sha256; + } + + try { + return this.sha256 = MessageDigest.getInstance("SHA-256").digest(generatePackData()); + } catch (Exception e) { + throw new RuntimeException("Could not calculate pack hash", e); + } + } + + @Override + public long size() { + return generatePackData().length; + } + + @Override + public @NonNull SeekableByteChannel serialize() { + return new SeekableInMemoryByteChannel(generatePackData()); + } + + @Override + protected @NonNull ResourcePack create() { + return new GeyserResourcePack(this, this.manifest, ""); + } + + @Override + protected ResourcePack.@NonNull Builder createBuilder() { + throw new UnsupportedOperationException(); + } + + @Subscribe(postOrder = PostOrder.LAST) + public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { + int reserved = /*Math.max(1, (32 - event.properties(PLAYER_IDENTIFIER).size()) / 3)*/32; + CommonData.LOGGER.info("{} properties will be reserved by emotecraft! Please ignore the warnings below...", reserved); + + StringBuilder molangScript = new StringBuilder("variable.bone_{BONE_ID} = variable.bone_{BONE_ID} ?? {DEFAULT_VALUE};"); + + this.registeredProperties.clear(); + for (int i = 0; i < reserved; i++) { + Identifier identifier = Identifier.of(CommonData.MOD_ID, String.format("property_%s", i)); + this.registeredProperties.add(event.registerIntegerProperty(PLAYER_IDENTIFIER, identifier, Integer.MIN_VALUE, Integer.MAX_VALUE)); + + String prop = "variable." + identifier.path(); + + molangScript.append(prop).append(" = q.property('").append(identifier).append("');"); + molangScript.append("variable.bone_{BONE_ID} = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID}) ? "); + molangScript.append("((").append(prop).append(" - {BONE_ID} * 10000000 - 1000000) / 100) : variable.bone_{BONE_ID};"); + } + molangScript.append("return variable.bone_{BONE_ID};"); + + CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); + this.molangScript = molangScript.toString(); + } + + public Set getRegisteredProperties() { + return Collections.unmodifiableSet(this.registeredProperties); + } + + public Map getAxisIds(String part, TransformType type) { + return Collections.unmodifiableMap(this.identifiers.get(part).get(type)); + } + + @Subscribe + public void onDefineResourcePacks(GeyserDefineResourcePacksEvent event) { + event.register(create()); + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java new file mode 100644 index 000000000..4d2a4d9f8 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SeekableByteChannel; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A {@link SeekableByteChannel} implementation that wraps a byte[]. + *

+ * When this channel is used for writing an internal buffer grows to accommodate incoming data. The natural size limit is the value of {@link Integer#MAX_VALUE} + * and it is not possible to {@link #position(long) set the position} or {@link #truncate truncate} to a value bigger than that. Internal buffer can be accessed + * via {@link SeekableInMemoryByteChannel#array()}. + *

+ * + * @since 1.13 + * @NotThreadSafe + */ +public class SeekableInMemoryByteChannel implements SeekableByteChannel { + + private static final int NAIVE_RESIZE_LIMIT = Integer.MAX_VALUE >> 1; + + private byte[] data; + private final AtomicBoolean closed = new AtomicBoolean(); + private int position, size; + + /** + * Constructs a new instance from a byte array. + *

+ * This constructor is intended to be used with pre-allocated buffer or when reading from a given byte array. + *

+ * + * @param data input data or pre-allocated array. + */ + public SeekableInMemoryByteChannel(final byte[] data) { + this.data = data; + this.size = data.length; + } + + /** + * Constructs a new instance from a size of storage to be allocated. + *

+ * Creates a channel and allocates internal storage of a given size. + *

+ * + * @param size size of internal buffer to allocate, in bytes. + */ + public SeekableInMemoryByteChannel(final int size) { + this(new byte[size]); + } + + /** + * Obtains the array backing this channel. + *

+ * NOTE: The returned buffer is not aligned with containing data, use {@link #size()} to obtain the size of data stored in the buffer. + *

+ * + * @return internal byte array. + */ + public byte[] array() { + return data; + } + + @Override + public void close() { + closed.set(true); + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + } + + @Override + public boolean isOpen() { + return !closed.get(); + } + + /** + * Returns this channel's position. + *

+ * This method violates the contract of {@link SeekableByteChannel#position()} as it will not throw any exception when invoked on a closed channel. Instead + * it will return the position the channel had when close has been called. + *

+ */ + @Override + public long position() { + return position; + } + + @Override + public SeekableByteChannel position(final long newPosition) throws IOException { + ensureOpen(); + if (newPosition < 0L || newPosition > Integer.MAX_VALUE) { + throw new IOException("Position has to be in range 0.. " + Integer.MAX_VALUE); + } + position = (int) newPosition; + return this; + } + + @Override + public int read(final ByteBuffer buf) throws IOException { + ensureOpen(); + int wanted = buf.remaining(); + final int possible = size - position; + if (possible <= 0) { + return -1; + } + if (wanted > possible) { + wanted = possible; + } + buf.put(data, position, wanted); + position += wanted; + return wanted; + } + + private void resize(final int newLength) { + int len = data.length; + if (len <= 0) { + len = 1; + } + if (newLength < NAIVE_RESIZE_LIMIT) { + while (len < newLength) { + len <<= 1; + } + } else { // avoid overflow + len = newLength; + } + data = Arrays.copyOf(data, len); + } + + /** + * Returns the current size of entity to which this channel is connected. + *

+ * This method violates the contract of {@link SeekableByteChannel#size} as it will not throw any exception when invoked on a closed channel. Instead it + * will return the size the channel had when close has been called. + *

+ */ + @Override + public long size() { + return size; + } + + /** + * Truncates the entity, to which this channel is connected, to the given size. + *

+ * This method violates the contract of {@link SeekableByteChannel#truncate} as it will not throw any exception when invoked on a closed channel. + *

+ * + * @throws IllegalArgumentException if size is negative or bigger than the maximum of a Java integer + */ + @Override + public SeekableByteChannel truncate(final long newSize) { + if (newSize < 0L || newSize > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Size has to be in range 0.. " + Integer.MAX_VALUE); + } + if (size > newSize) { + size = (int) newSize; + } + if (position > newSize) { + position = (int) newSize; + } + return this; + } + + @Override + public int write(final ByteBuffer b) throws IOException { + ensureOpen(); + int wanted = b.remaining(); + final int possibleWithoutResize = size - position; + if (wanted > possibleWithoutResize) { + final int newSize = position + wanted; + if (newSize < 0) { // overflow + resize(Integer.MAX_VALUE); + wanted = Integer.MAX_VALUE - position; + } else { + resize(newSize); + } + } + b.get(data, position, wanted); + position += wanted; + if (size < position) { + size = position; + } + return wanted; + } + +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java new file mode 100644 index 000000000..8532d9c27 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java @@ -0,0 +1,19 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.geysermc.geyser.pack.GeyserResourcePackManifest; + +import java.io.IOException; + +public class ResourcePackVersionSerializer extends JsonSerializer { + @Override + public void serialize(GeyserResourcePackManifest.Version version, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeStartArray(); + generator.writeNumber(version.major()); + generator.writeNumber(version.minor()); + generator.writeNumber(version.patch()); + generator.writeEndArray(); + } +} From 248c8ca8a160a2dd3151de48eb1a4aad90d5119f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 1 Nov 2025 23:06:06 +0700 Subject: [PATCH 31/70] Fix rotation --- .../emotecraft/geyser/animator/GeyserAnimationController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index b68d3ec03..c4d01c11d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -90,7 +90,9 @@ public void run() { PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); - updateAxis(propertyManager, partKey, TransformType.ROTATION, bone.getRotX(), bone.getRotY(), bone.getRotZ()); + updateAxis(propertyManager, partKey, TransformType.ROTATION, + (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) + ); } // Flush From 49496055815469f876542418b2bfeab646ebf1c6 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 1 Nov 2025 23:51:13 +0700 Subject: [PATCH 32/70] Update only used bones --- .../geyser/animator/GeyserAnimationController.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index c4d01c11d..9ca343e96 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -8,6 +8,7 @@ import com.zigythebird.playeranimcore.enums.TransformType; import com.zigythebird.playeranimcore.math.Vec3f; import com.zigythebird.playeranimcore.molang.MolangLoader; +import io.github.kosmx.emotes.common.CommonData; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.entity.EntityProperty; @@ -86,7 +87,12 @@ public void run() { tick(data); // Animate via properties - for (String partKey : BONE_POSITIONS.keySet()) { + for (String partKey : this.activeBones.keySet()) { + if (!BONE_POSITIONS.containsKey(partKey)) { + CommonData.LOGGER.warn("Unsupported bone: {}!", partKey); + continue; + } + PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); From 62df432f0e6204d437969d20e2d4ae9c17547896 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 2 Nov 2025 01:03:10 +0700 Subject: [PATCH 33/70] Interpolation --- .../mods/emotecraft/geyser/utils/EmoteResourcePack.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java index eceba1012..ae1313053 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java @@ -205,6 +205,7 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { CommonData.LOGGER.info("{} properties will be reserved by emotecraft! Please ignore the warnings below...", reserved); StringBuilder molangScript = new StringBuilder("variable.bone_{BONE_ID} = variable.bone_{BONE_ID} ?? {DEFAULT_VALUE};"); + molangScript.append("variable.bone_{BONE_ID}_target = variable.bone_{BONE_ID}_target ?? {DEFAULT_VALUE};"); this.registeredProperties.clear(); for (int i = 0; i < reserved; i++) { @@ -214,9 +215,11 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { String prop = "variable." + identifier.path(); molangScript.append(prop).append(" = q.property('").append(identifier).append("');"); - molangScript.append("variable.bone_{BONE_ID} = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID}) ? "); - molangScript.append("((").append(prop).append(" - {BONE_ID} * 10000000 - 1000000) / 100) : variable.bone_{BONE_ID};"); + molangScript.append("variable.bone_{BONE_ID}_target = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID}) ? "); + molangScript.append("((").append(prop).append(" - {BONE_ID} * 10000000 - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); } + + molangScript.append("variable.bone_{BONE_ID} = math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.3);"); molangScript.append("return variable.bone_{BONE_ID};"); CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); From 808e79063faff45830c97f49d19214592c62e33a Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 2 Nov 2025 01:24:43 +0700 Subject: [PATCH 34/70] Bone fixes --- .../geyser/animator/GeyserAnimationController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 9ca343e96..3f875c6a5 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -95,6 +95,13 @@ public void run() { PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); + if ("left_arm".equals(partKey) || "right_arm".equals(partKey) || "head".equals(partKey)) { + bone.applyOtherBone(get3DTransform(new PlayerAnimBone("torso")).scale(-1)); + + } else if ("cape".equals(partKey)) { + bone.rotX *= -1; + } + updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); updateAxis(propertyManager, partKey, TransformType.ROTATION, (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) From 395d29aac7bfe067886456dc9fd230fbc447a73f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 2 Nov 2025 02:58:31 +0700 Subject: [PATCH 35/70] Fix stopping --- .../animator/GeyserAnimationController.java | 78 +++++++++++-------- .../geyser/utils/BedrockPacketsUtils.java | 27 +++++++ .../geyser/utils/EmoteResourcePack.java | 1 + 3 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 3f875c6a5..782fc2d49 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -8,11 +8,9 @@ import com.zigythebird.playeranimcore.enums.TransformType; import com.zigythebird.playeranimcore.math.Vec3f; import com.zigythebird.playeranimcore.molang.MolangLoader; -import io.github.kosmx.emotes.common.CommonData; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.entity.EntityProperty; -import org.cloudburstmc.protocol.bedrock.packet.AnimateEntityPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.geyser.api.entity.property.GeyserEntityProperty; import org.geysermc.geyser.api.entity.property.type.GeyserIntEntityProperty; @@ -21,6 +19,7 @@ import org.geysermc.geyser.entity.properties.type.PropertyType; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmoteResourcePack; import java.time.Duration; @@ -44,7 +43,9 @@ public class GeyserAnimationController extends AnimationController implements Ru ); private final Set lastUsedProperties = new HashSet<>(1); - private final PlayerEntity playerEntity; + protected final PlayerEntity playerEntity; + + private final Set dirtyBones = new HashSet<>(); public GeyserAnimationController(PlayerEntity playerEntity) { super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); @@ -66,30 +67,32 @@ public void registerBones() { this.registerPlayerAnimBone("elytra"); } + @Override + protected void setupNewAnimation() { + super.setupNewAnimation(); + BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, this.playerEntity); + for (String partKey : this.dirtyBones) { + updateBone(this.playerEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); + } + this.dirtyBones.clear(); + } + @Override public void run() { // Check propertyManager GeyserEntityPropertyManager propertyManager = this.playerEntity.getPropertyManager(); if (propertyManager == null) return; - // Start animation - AnimateEntityPacket animatePacket = new AnimateEntityPacket(); - animatePacket.setAnimation(EmoteResourcePack.ANIMATION_NAME); - animatePacket.setNextState("default"); - animatePacket.setBlendOutTime(0.0f); - animatePacket.setStopExpression("query.any_animation_finished"); - animatePacket.setController("__runtime_controller"); - animatePacket.getRuntimeEntityIds().add(this.playerEntity.getGeyserId()); - this.playerEntity.getSession().sendUpstreamPacket(animatePacket); - AnimationData data = new AnimationData(0, 0.0F); - setupAnim(data); tick(data); + if (!isActive()) return; + setupAnim(data); + // Animate via properties for (String partKey : this.activeBones.keySet()) { if (!BONE_POSITIONS.containsKey(partKey)) { - CommonData.LOGGER.warn("Unsupported bone: {}!", partKey); + // CommonData.LOGGER.debug("Unsupported bone: {}!", partKey); continue; } @@ -101,30 +104,27 @@ public void run() { } else if ("cape".equals(partKey)) { bone.rotX *= -1; } - - updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); - updateAxis(propertyManager, partKey, TransformType.ROTATION, - (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) - ); + updateBone(propertyManager, partKey, bone); } // Flush flushPropertiesImmediately(); + if (this.dirtyBones.isEmpty()) this.dirtyBones.addAll(this.activeBones.keySet()); } - private void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { - Map ids = EmotecraftExt.getInstance().getResourcePack().getAxisIds(partKey, type); - int packedX = pack(ids.get(Axis.X), x); - int packedY = pack(ids.get(Axis.Y), y); - int packedZ = pack(ids.get(Axis.Z), z); - - System.out.printf("%s %s: X(id=%d, val=%.2f, packed=%d), Y(id=%d, val=%.2f, packed=%d), Z(id=%d, val=%.2f, packed=%d)%n", - partKey, type, ids.get(Axis.X), x, packedX, ids.get(Axis.Y), y, packedY, ids.get(Axis.Z), z, packedZ + protected void updateBone(GeyserEntityPropertyManager propertyManager, String partKey, PlayerAnimBone bone) { + if (!BONE_POSITIONS.containsKey(partKey)) return; + updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); + updateAxis(propertyManager, partKey, TransformType.ROTATION, + (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) ); + } - updateProperty(propertyManager, getAvailableProperty(), packedX); - updateProperty(propertyManager, getAvailableProperty(), packedY); - updateProperty(propertyManager, getAvailableProperty(), packedZ); + protected void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { + Map ids = EmotecraftExt.getInstance().getResourcePack().getAxisIds(partKey, type); + updateProperty(propertyManager, getAvailableProperty(), pack(ids.get(Axis.X), x)); + updateProperty(propertyManager, getAvailableProperty(), pack(ids.get(Axis.Y), y)); + updateProperty(propertyManager, getAvailableProperty(), pack(ids.get(Axis.Z), z)); } private GeyserIntEntityProperty getAvailableProperty() { @@ -170,6 +170,22 @@ public Vec3f getBonePosition(String name) { return Vec3f.ZERO; } + @Override + public void process(AnimationData state) { + super.process(state); + if (!this.animationState.isActive()) internalStop(); + } + + @Override + public void stop() { + super.stop(); + internalStop(); + } + + protected void internalStop() { + BedrockPacketsUtils.sendBobAnimation(this.playerEntity); + } + public static int pack(int id, float value) { id = Math.max(0, Math.min(99, id)); value = Math.max(-9999.99f, Math.min(9999.99f, value)); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java new file mode 100644 index 000000000..e8448d6b1 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java @@ -0,0 +1,27 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import org.cloudburstmc.protocol.bedrock.packet.AnimateEntityPacket; +import org.geysermc.geyser.entity.type.player.PlayerEntity; + +public class BedrockPacketsUtils { + public static final String BOB = "animation.player.bob"; + + public static void sendInstantAnimation(String animation, PlayerEntity playerEntity) { + sendAnimation(animation, "0", playerEntity); + } + + public static void sendBobAnimation(PlayerEntity playerEntity) { + sendAnimation(BOB, "1", playerEntity); + } + + public static void sendAnimation(String animation, String stopExpression, PlayerEntity playerEntity) { + AnimateEntityPacket animatePacket = new AnimateEntityPacket(); + animatePacket.setAnimation(animation); + animatePacket.setNextState("default"); + animatePacket.setBlendOutTime(0.0f); + animatePacket.setStopExpression(stopExpression); + animatePacket.setController("__runtime_controller"); + animatePacket.getRuntimeEntityIds().add(playerEntity.getGeyserId()); + playerEntity.getSession().sendUpstreamPacketImmediately(animatePacket); + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java index ae1313053..e91bb3fed 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java @@ -100,6 +100,7 @@ private JsonObject generateAnimation() { JsonObject animation = new JsonObject(); animation.addProperty("animation_length", 1.0F); animation.addProperty("loop", true); + animation.addProperty("override_previous_animation", true); animation.add("bones", bones); JsonObject container = new JsonObject(); From e25a4331ac320525bad2348f56c6981318216392 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 2 Nov 2025 20:14:11 +0700 Subject: [PATCH 36/70] Update GeyserAnimationController.java --- .../animator/GeyserAnimationController.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 782fc2d49..fd7ab2e56 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -96,15 +96,7 @@ public void run() { continue; } - PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); - - if ("left_arm".equals(partKey) || "right_arm".equals(partKey) || "head".equals(partKey)) { - bone.applyOtherBone(get3DTransform(new PlayerAnimBone("torso")).scale(-1)); - - } else if ("cape".equals(partKey)) { - bone.rotX *= -1; - } - updateBone(propertyManager, partKey, bone); + updateBone(propertyManager, partKey, get3DTransform(new PlayerAnimBone(partKey))); } // Flush @@ -112,12 +104,27 @@ public void run() { if (this.dirtyBones.isEmpty()) this.dirtyBones.addAll(this.activeBones.keySet()); } + @Override + public PlayerAnimBone get3DTransform(@NonNull PlayerAnimBone bone) { + bone = super.get3DTransform(bone); + + String boneName = bone.getName(); + if ("left_arm".equals(boneName) || "right_arm".equals(boneName) || "head".equals(boneName)) { + bone.applyOtherBone(get3DTransform(new PlayerAnimBone("torso")).scale(-1)); + + } else if ("cape".equals(boneName)) { + bone.rotX *= -1; + } + return bone; + } + protected void updateBone(GeyserEntityPropertyManager propertyManager, String partKey, PlayerAnimBone bone) { if (!BONE_POSITIONS.containsKey(partKey)) return; updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); updateAxis(propertyManager, partKey, TransformType.ROTATION, (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) ); + updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); } protected void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { @@ -158,7 +165,7 @@ private void flushPropertiesImmediately() { this.playerEntity.getSession().sendUpstreamPacketImmediately(packet); try { - Thread.sleep(Duration.ofMillis(10)); // IDK + Thread.sleep(Duration.ofMillis(10 + this.playerEntity.getSession().ping())); // IDK } catch (InterruptedException ignored) {} this.lastUsedProperties.clear(); } From 4a20c126e2042d78a62b6c7b14bf5ba961e59de1 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 2 Nov 2025 21:28:49 +0700 Subject: [PATCH 37/70] Update build.gradle.kts --- geyser/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 6a8f6b6ab..4ea4c0b45 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { exclude(module = "gson") exclude(module = "slf4j-api") - exclude(module = "fastutil") + // exclude(module = "fastutil") exclude(module = "netty-buffer") exclude(module = "jspecify") exclude(module = "guava") From ee6d1764731215938256eecd53aa09815c92bd24 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Mon, 3 Nov 2025 18:45:37 +0700 Subject: [PATCH 38/70] Move --- .../dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java | 2 +- .../emotecraft/geyser/animator/GeyserAnimationController.java | 2 +- .../geyser/utils/{ => resourcepack}/EmoteResourcePack.java | 3 +-- .../utils/{ => resourcepack}/SeekableInMemoryByteChannel.java | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) rename geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/{ => resourcepack}/EmoteResourcePack.java (98%) rename geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/{ => resourcepack}/SeekableInMemoryByteChannel.java (98%) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 3caee9ed6..3d7b2d1ba 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -36,7 +36,7 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmoteResourcePack; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.io.IOException; import java.util.*; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index fd7ab2e56..c6b94c5a9 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -20,7 +20,7 @@ import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmoteResourcePack; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.time.Duration; import java.util.*; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java similarity index 98% rename from geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java rename to geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index e91bb3fed..29cd5d5ff 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -1,4 +1,4 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; @@ -23,7 +23,6 @@ import org.geysermc.geyser.pack.GeyserResourcePack; import org.geysermc.geyser.pack.GeyserResourcePackManifest; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.ResourcePackVersionSerializer; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/SeekableInMemoryByteChannel.java similarity index 98% rename from geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java rename to geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/SeekableInMemoryByteChannel.java index 4d2a4d9f8..d046a7347 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/SeekableInMemoryByteChannel.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/SeekableInMemoryByteChannel.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; import java.io.IOException; import java.nio.ByteBuffer; From db7c60e88cc4f5808a4cbf4dff6c7a12dfe2df7b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 7 Nov 2025 20:21:31 +0700 Subject: [PATCH 39/70] Create GeyserRun.run.xml --- .run/GeyserRun.run.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .run/GeyserRun.run.xml diff --git a/.run/GeyserRun.run.xml b/.run/GeyserRun.run.xml new file mode 100644 index 000000000..25e788b0b --- /dev/null +++ b/.run/GeyserRun.run.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file From 82d7abd74d2b62f2db2db0dcf9a38d8efadcfb79 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 7 Nov 2025 20:27:25 +0700 Subject: [PATCH 40/70] Fix patch --- .../emotecraft/geyser/fuckery/GeyserSessionPatch.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java index 531637386..a9c8161cc 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java @@ -1,12 +1,6 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; import javassist.*; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ConstPool; -import javassist.bytecode.MethodInfo; -import javassist.expr.ExprEditor; -import javassist.expr.MethodCall; -import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; import java.io.IOException; import java.io.InputStream; @@ -18,7 +12,8 @@ public class GeyserSessionPatch { public static final String CLASS_NAME = "org.geysermc.geyser.session.GeyserSession"; public static byte[] patch(byte[] bytes) { - ClassPool pool = ClassPool.getDefault(); + ClassPool pool = new ClassPool(null); + pool.insertClassPath(new LoaderClassPath(GeyserSessionPatch.class.getClassLoader())); pool.appendClassPath(new ByteArrayClassPath(CLASS_NAME, bytes)); try { From 77a028ed9252b762eff475e674e76e9d48b00c34 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 9 Nov 2025 05:42:58 +0700 Subject: [PATCH 41/70] Publish geyser ext on modrinth --- build.gradle.kts | 2 +- buildSrc/build.gradle.kts | 4 +--- buildSrc/src/main/java/CustomPublishResult.kt | 1 - geyser/build.gradle.kts | 11 +++++++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 05ac6b309..401a3a10f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -107,6 +107,7 @@ val ds = publishDiscord { links { from(":minecraft:neoforge", "modrinth") from(":minecraft:fabric", "modrinth") + from(":geyser", "modrinth") val paper = project(":paper") from(paper, "modrinth") @@ -121,7 +122,6 @@ val ds = publishDiscord { val hangarLink = "https://hangar.papermc.io/$hangarProjectName/versions/$ver" nextRow() custom("Hangar (Paper)", hangarLink, HANGAR_EMOJI) - from(":geyser", "github", GEYSER_EMOJI, "Geyser Ext") } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 83577d66f..8a8cd8133 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -14,7 +14,5 @@ dependencies { compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") compileOnly("com.squareup.okhttp3:okhttp:5.3.0") - constraints { - implementation("commons-io:commons-io:2.20.0") - } + implementation("commons-io:commons-io:2.20.0") } diff --git a/buildSrc/src/main/java/CustomPublishResult.kt b/buildSrc/src/main/java/CustomPublishResult.kt index 59c92d079..9684bfe82 100644 --- a/buildSrc/src/main/java/CustomPublishResult.kt +++ b/buildSrc/src/main/java/CustomPublishResult.kt @@ -6,7 +6,6 @@ val CURSEFORGE_EMOJI = Emoji("curseforge", "1136405235187847198") val MODRINTH_EMOJI = Emoji("modrinth", "1136404935374798878") val HANGAR_EMOJI = Emoji("hangar", "1407387843931672647") val GITHUB_EMOJI = Emoji("github", "1136406913542795364") -val GEYSER_EMOJI = Emoji("geyser", "1409806511508815922") fun Emoji.Companion.fromPlatform(platform: String): Emoji? { return when (platform) { diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts index 4ea4c0b45..56bdeaf5c 100644 --- a/geyser/build.gradle.kts +++ b/geyser/build.gradle.kts @@ -92,6 +92,8 @@ publishing { } publishMods { + modLoaders.add("geyser") + file.set(tasks.downgradeJar.get().archiveFile) // Java 17 // additionalFiles.from(tasks.shadowJar.get().archiveFile) // Java 21 @@ -103,4 +105,13 @@ publishMods { accessToken = providers.environmentVariable("GH_TOKEN") parent(rootProject.tasks.named("publishGithub")) } + + modrinth { + announcementTitle = "Modrinth (Geyser)" + accessToken = providers.environmentVariable("MODRINTH_TOKEN") + projectId = providers.gradleProperty("modrinth_id") + minecraftVersions.addAll(release_minecraft_versions) + displayName = mod_version + version = "${mod_version}+${removePreRc(minecraft_version)}-geyser" + } } From 38f2e0e1004b6160a32250d9a3c817b0c5fd25fa Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 22 Nov 2025 17:41:37 +0700 Subject: [PATCH 42/70] Update geyser --- .../mods/emotecraft/geyser/EmotecraftExt.java | 10 +++++++-- .../geyser/handler/GeyserNetworkInstance.java | 16 +++++++++----- .../geyser/utils/BedrockEmoteLoader.java | 11 +++++----- .../geyser/utils/EmotecraftLocale.java | 17 ++++++-------- .../utils/resourcepack/CollectionAdapter.java | 22 +++++++++++++++++++ .../utils/resourcepack/EmoteResourcePack.java | 18 +++++++-------- .../ResourcePackVersionSerializer.java | 20 ++++++++--------- gradle.properties | 2 +- 8 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/CollectionAdapter.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 3d7b2d1ba..59ea0d655 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -3,6 +3,7 @@ import com.zigythebird.playeranimcore.animation.Animation; import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.SerializableConfig; +import io.github.kosmx.emotes.common.network.EmotePacket; import io.github.kosmx.emotes.server.config.ConfigSerializer; import io.github.kosmx.emotes.server.config.Serializer; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; @@ -115,7 +116,9 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { } private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] bytes) { - Set channels = DinnerboneProtocolUtils.readChannels(Unpooled.wrappedBuffer(bytes)); + ByteBuf inputByteBuf = Unpooled.wrappedBuffer(bytes); + Set channels = DinnerboneProtocolUtils.readChannels(inputByteBuf); + inputByteBuf.release(); CommonData.LOGGER.debug("Server listening channels: {}", channels); if (channels.contains(EmotecraftExt.EMOTECRAFT_EMOTE_TYPE)) { @@ -124,6 +127,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] ByteBuf byteBuf = Unpooled.buffer(); DinnerboneProtocolUtils.writeChannels(byteBuf, EMOTECRAFT_CHANNELS); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + byteBuf.release(); if (((ProtocolProvider) session).ec$state() == ProtocolState.GAME) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); @@ -147,7 +151,9 @@ private void onEmotecraftPayload(GeyserConnection session, Key channel, byte[] b } networkInstance.setConnectionType(ConnectionType.BACKEND); } - networkInstance.receiveMessage(bytes); + ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); + networkInstance.receiveMessage(new EmotePacket(byteBuf)); + byteBuf.release(); } @Subscribe diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index cd1e681cd..9dc02b302 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -10,6 +10,8 @@ import io.github.kosmx.emotes.common.network.objects.NetData; import io.github.kosmx.emotes.common.tools.UUIDMap; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; @@ -23,7 +25,6 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.*; @@ -55,7 +56,7 @@ public HashMap getRemoteVersions() { } @Override - public void setVersions(HashMap map) { + public void setVersions(Map map) { this.versions.clear(); this.versions.putAll(map); } @@ -66,16 +67,19 @@ public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) thro } @Override - protected void sendMessage(byte[] bytes, @Nullable UUID target) { + public void sendMessage(EmotePacket packet, @Nullable UUID target) { + ByteBuf buf = Unpooled.buffer(); + packet.write(buf); ((GeyserSession) this.session).sendDownstreamPacket(new ServerboundCustomPayloadPacket( - EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, bytes + EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, buf.array() )); + buf.release(); } @Override - public void receiveMessage(ByteBuffer byteBuffer, UUID player) { + public void receiveMessage(EmotePacket packet, UUID player) { try { - NetData data = new EmotePacket.Builder().build().read(byteBuffer); + NetData data = packet.data; if (!trustReceivedPlayer()) { data.player = null; } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index fcc93efa4..e6b9c1943 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -6,9 +6,10 @@ import com.google.gson.JsonObject; import com.zigythebird.playeranimcore.animation.Animation; import com.zigythebird.playeranimcore.loading.UniversalAnimLoader; +import com.zigythebird.playeranimcore.util.JsonUtil; import io.github.kosmx.emotes.common.CommonData; -import net.raphimc.minecraftauth.util.JsonUtil; import org.jetbrains.annotations.NotNull; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.io.*; import java.net.http.HttpClient; @@ -22,7 +23,7 @@ public class BedrockEmoteLoader extends CacheLoader> { private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) + // .version(HttpClient.Version.HTTP_2) .followRedirects(HttpClient.Redirect.ALWAYS) .build(); @@ -47,10 +48,10 @@ public class BedrockEmoteLoader extends CacheLoader response) { try (Reader reader = new InputStreamReader(response.body())) { - JsonObject obj = JsonUtil.GSON.fromJson(reader, JsonObject.class); + JsonObject obj = EmoteResourcePack.GSON.fromJson(reader, JsonObject.class); - if (!JsonUtil.getBooleanOr(obj, "present", false)) { - throw new NullPointerException(JsonUtil.getStringOr(obj, "message", "Animation is not present!")); + if (!obj.has("present") || !obj.get("present").getAsBoolean()) { + throw new NullPointerException(JsonUtil.getAsString(obj, "message", "Animation is not present!")); } else if (obj.has("message")) { CommonData.LOGGER.warn(obj.get("message").getAsString()); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java index b4d5aa533..98d50b1d4 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/EmotecraftLocale.java @@ -1,16 +1,17 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.github.kosmx.emotes.common.CommonData; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.JsonUtils; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.HashMap; -import java.util.Iterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; public class EmotecraftLocale { private static final Map> LOCALE_MAPPINGS = new HashMap<>(); @@ -24,15 +25,11 @@ public static void loadLocale(String locale) { if (LOCALE_MAPPINGS.containsKey(locale)) return; try (InputStream localeStream = EmotecraftLocale.class.getResourceAsStream("/assets/emotecraft/lang/" + locale + ".json")) { - JsonNode localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream); - Iterator> localeIterator = localeObj.fields(); + JsonObject localeObj = JsonUtils.fromJson(Objects.requireNonNull(localeStream)); Map langMap = new HashMap<>(); - - while (localeIterator.hasNext()) { - Map.Entry entry = localeIterator.next(); - langMap.put(entry.getKey(), entry.getValue().asText()); + for (Map.Entry entry : localeObj.entrySet()) { + langMap.put(entry.getKey(), entry.getValue().getAsString()); } - LOCALE_MAPPINGS.put(locale, langMap); } catch (FileNotFoundException e) { throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage())); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/CollectionAdapter.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/CollectionAdapter.java new file mode 100644 index 000000000..f8964b8f9 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/CollectionAdapter.java @@ -0,0 +1,22 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; +import java.util.Collection; + +public final class CollectionAdapter implements JsonSerializer> { + @Override + public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext ctx) { + if (src == null || src.isEmpty()) return null; + JsonArray array = new JsonArray(); + for (Object child : src) { + array.add(ctx.serialize(child)); + } + return array; + } +} + diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 29cd5d5ff..f60821a38 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -1,8 +1,7 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.zigythebird.playeranimcore.enums.Axis; @@ -39,11 +38,11 @@ public final class EmoteResourcePack extends PackCodec implements EventRegistrar private static final Identifier PLAYER_IDENTIFIER = Identifier.of(Identifier.DEFAULT_NAMESPACE, "player"); public static final String ANIMATION_NAME = String.format("animation.%s", CommonData.MOD_ID); - public static final ObjectMapper JSON_MAPPER = new ObjectMapper() - .registerModule(new SimpleModule() - .addSerializer(GeyserResourcePackManifest.Version.class, new ResourcePackVersionSerializer()) - ) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + public static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(GeyserResourcePackManifest.Version.class, new ResourcePackVersionSerializer()) + .registerTypeHierarchyAdapter(Collection.class, new CollectionAdapter()) + .disableHtmlEscaping() + .create(); private final Map>> identifiers = new HashMap<>(); private final Set registeredProperties = new HashSet<>(1); @@ -119,8 +118,7 @@ private byte[] generatePackData() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(baos) ) { - String manifestJson = EmoteResourcePack.JSON_MAPPER.writeValueAsString(this.manifest); - System.out.println(manifestJson); + String manifestJson = EmoteResourcePack.GSON.toJson(this.manifest); zos.putNextEntry(new ZipEntry("manifest.json")); zos.write(manifestJson.getBytes(StandardCharsets.UTF_8)); zos.closeEntry(); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java index 8532d9c27..577bc1ec2 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/ResourcePackVersionSerializer.java @@ -1,19 +1,17 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; +import com.google.gson.*; import org.geysermc.geyser.pack.GeyserResourcePackManifest; -import java.io.IOException; +import java.lang.reflect.Type; -public class ResourcePackVersionSerializer extends JsonSerializer { +public class ResourcePackVersionSerializer extends GeyserResourcePackManifest.Version.VersionDeserializer implements JsonDeserializer, JsonSerializer { @Override - public void serialize(GeyserResourcePackManifest.Version version, JsonGenerator generator, SerializerProvider provider) throws IOException { - generator.writeStartArray(); - generator.writeNumber(version.major()); - generator.writeNumber(version.minor()); - generator.writeNumber(version.patch()); - generator.writeEndArray(); + public JsonElement serialize(GeyserResourcePackManifest.Version version, Type typeOfSrc, JsonSerializationContext ctx) { + JsonArray array = new JsonArray(3); + array.add(version.major()); + array.add(version.minor()); + array.add(version.patch()); + return array; } } diff --git a/gradle.properties b/gradle.properties index 22900b290..071161b39 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,4 +32,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.2 fabric_permissions_api = 0.4.0 noteblocklib_version = 3.1.1 - geyser_version = 2.9.0-SNAPSHOT + geyser_version = 2.9.1-SNAPSHOT From feb4575dcab447ab3473d612be0cc1245af53b73 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 19 Dec 2025 02:11:13 +0700 Subject: [PATCH 43/70] Update --- .../emotecraft/geyser/EmotecraftService.java | 5 ++ .../animator/GeyserAnimationController.java | 58 +++++-------------- .../utils/resourcepack/EmoteResourcePack.java | 2 +- gradle.properties | 2 +- 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java index 985c053c5..a9ce022f1 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftService.java @@ -19,4 +19,9 @@ public Path getConfigPath() { public boolean isActive() { return EmotecraftExt.getInstance() != null && EmotecraftExt.getInstance().isEnabled(); } + + @Override + public int getPriority() { + return InstanceService.super.getPriority() - 1; + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index c6b94c5a9..bb48adb51 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -1,13 +1,13 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; -import com.zigythebird.playeranimcore.animation.AnimationController; import com.zigythebird.playeranimcore.animation.AnimationData; +import com.zigythebird.playeranimcore.animation.HumanoidAnimationController; import com.zigythebird.playeranimcore.bones.PlayerAnimBone; import com.zigythebird.playeranimcore.enums.Axis; import com.zigythebird.playeranimcore.enums.PlayState; import com.zigythebird.playeranimcore.enums.TransformType; -import com.zigythebird.playeranimcore.math.Vec3f; import com.zigythebird.playeranimcore.molang.MolangLoader; +import io.github.kosmx.emotes.common.CommonData; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.entity.EntityProperty; @@ -25,23 +25,7 @@ import java.time.Duration; import java.util.*; -/** - * Bends in the bedrock are not supported, so this feature is not implemented here. - */ -public class GeyserAnimationController extends AnimationController implements Runnable { - // Bone pivot point positions used to apply custom pivot point translations. - public static final Map BONE_POSITIONS = Map.of( - "right_arm", new Vec3f(5, 22, 0), - "left_arm", new Vec3f(-5, 22, 0), - "left_leg", new Vec3f(-2f, 12, 0f), - "right_leg", new Vec3f(2f, 12, 0f), - "torso", new Vec3f(0, 24, 0), - "head", new Vec3f(0, 24, 0), - "body", new Vec3f(0, 12, 0), - "cape", new Vec3f(0, 24, 2), - "elytra", new Vec3f(0, 24, 2) - ); - +public class GeyserAnimationController extends HumanoidAnimationController implements Runnable { private final Set lastUsedProperties = new HashSet<>(1); protected final PlayerEntity playerEntity; @@ -52,21 +36,6 @@ public GeyserAnimationController(PlayerEntity playerEntity) { this.playerEntity = playerEntity; } - @Override - public void registerBones() { - this.registerPlayerAnimBone("body"); - this.registerPlayerAnimBone("right_arm"); - this.registerPlayerAnimBone("left_arm"); - this.registerPlayerAnimBone("right_leg"); - this.registerPlayerAnimBone("left_leg"); - this.registerPlayerAnimBone("head"); - this.registerPlayerAnimBone("torso"); - this.registerPlayerAnimBone("right_item"); - this.registerPlayerAnimBone("left_item"); - this.registerPlayerAnimBone("cape"); - this.registerPlayerAnimBone("elytra"); - } - @Override protected void setupNewAnimation() { super.setupNewAnimation(); @@ -91,8 +60,8 @@ public void run() { // Animate via properties for (String partKey : this.activeBones.keySet()) { - if (!BONE_POSITIONS.containsKey(partKey)) { - // CommonData.LOGGER.debug("Unsupported bone: {}!", partKey); + if (!this.bones.containsKey(partKey)) { + CommonData.LOGGER.debug("Unsupported bone: {}!", partKey); continue; } @@ -119,7 +88,7 @@ public PlayerAnimBone get3DTransform(@NonNull PlayerAnimBone bone) { } protected void updateBone(GeyserEntityPropertyManager propertyManager, String partKey, PlayerAnimBone bone) { - if (!BONE_POSITIONS.containsKey(partKey)) return; + if (!this.bones.containsKey(partKey)) return; updateAxis(propertyManager, partKey, TransformType.POSITION, bone.getPosX(), bone.getPosY(), bone.getPosZ()); updateAxis(propertyManager, partKey, TransformType.ROTATION, (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) @@ -170,13 +139,6 @@ private void flushPropertiesImmediately() { this.lastUsedProperties.clear(); } - @Override - public Vec3f getBonePosition(String name) { - if (BONE_POSITIONS.containsKey(name)) return BONE_POSITIONS.get(name); - if (pivotBones.containsKey(name)) return pivotBones.get(name).getPivot(); - return Vec3f.ZERO; - } - @Override public void process(AnimationData state) { super.process(state); @@ -202,4 +164,12 @@ public static int pack(int id, float value) { return id * 10000000 + intValue; } + + /** + * A small hack that allows us to get all registered bones. + */ + public static Collection getRegisteredBones() { + GeyserAnimationController controller = new GeyserAnimationController(null); + return Collections.unmodifiableCollection(controller.bones.keySet()); + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index f60821a38..d01da00fc 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -72,7 +72,7 @@ private JsonObject generateAnimation() { int id = 0; this.identifiers.clear(); - for (String boneName : GeyserAnimationController.BONE_POSITIONS.keySet()) { + for (String boneName : GeyserAnimationController.getRegisteredBones()) { JsonObject bone = new JsonObject(); EnumMap> transformType = this.identifiers.computeIfAbsent(boneName, diff --git a/gradle.properties b/gradle.properties index 452537864..8550b8c74 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,4 +32,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.2 fabric_permissions_api = 0.6.0 noteblocklib_version = 3.1.1 - geyser_version = 2.9.1-SNAPSHOT + geyser_version = 2.9.2-SNAPSHOT From 8d919646e9cdbfe6018325a764952e48351a8dad Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 19 Dec 2025 02:21:10 +0700 Subject: [PATCH 44/70] avatar --- .../animator/GeyserAnimationController.java | 26 +++++++++---------- .../geyser/handler/GeyserNetworkInstance.java | 5 ++-- .../geyser/utils/BedrockPacketsUtils.java | 8 +++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index bb48adb51..48dd12d0c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -17,7 +17,7 @@ import org.geysermc.geyser.api.util.Identifier; import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager; import org.geysermc.geyser.entity.properties.type.PropertyType; -import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.type.player.AvatarEntity; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; @@ -27,21 +27,21 @@ public class GeyserAnimationController extends HumanoidAnimationController implements Runnable { private final Set lastUsedProperties = new HashSet<>(1); - protected final PlayerEntity playerEntity; + protected final AvatarEntity avatarEntity; private final Set dirtyBones = new HashSet<>(); - public GeyserAnimationController(PlayerEntity playerEntity) { + public GeyserAnimationController(AvatarEntity avatarEntity) { super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); - this.playerEntity = playerEntity; + this.avatarEntity = avatarEntity; } @Override protected void setupNewAnimation() { super.setupNewAnimation(); - BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, this.playerEntity); + BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, this.avatarEntity); for (String partKey : this.dirtyBones) { - updateBone(this.playerEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); + updateBone(this.avatarEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); } this.dirtyBones.clear(); } @@ -49,10 +49,10 @@ protected void setupNewAnimation() { @Override public void run() { // Check propertyManager - GeyserEntityPropertyManager propertyManager = this.playerEntity.getPropertyManager(); + GeyserEntityPropertyManager propertyManager = this.avatarEntity.getPropertyManager(); if (propertyManager == null) return; - AnimationData data = new AnimationData(0, 0.0F); + AnimationData data = new AnimationData(0, 1.0F); tick(data); if (!isActive()) return; @@ -124,17 +124,17 @@ public static void updateProperty(GeyserEntityPropertyManager propertyManage } private void flushPropertiesImmediately() { - GeyserEntityPropertyManager propertyManager = this.playerEntity.getPropertyManager(); + GeyserEntityPropertyManager propertyManager = this.avatarEntity.getPropertyManager(); if (propertyManager == null || !propertyManager.hasProperties()) return; SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.setRuntimeEntityId(this.playerEntity.getGeyserId()); + packet.setRuntimeEntityId(this.avatarEntity.getGeyserId()); propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); - this.playerEntity.getSession().sendUpstreamPacketImmediately(packet); + this.avatarEntity.getSession().sendUpstreamPacketImmediately(packet); try { - Thread.sleep(Duration.ofMillis(10 + this.playerEntity.getSession().ping())); // IDK + Thread.sleep(Duration.ofMillis(10 + this.avatarEntity.getSession().ping())); // IDK } catch (InterruptedException ignored) {} this.lastUsedProperties.clear(); } @@ -152,7 +152,7 @@ public void stop() { } protected void internalStop() { - BedrockPacketsUtils.sendBobAnimation(this.playerEntity); + BedrockPacketsUtils.sendBobAnimation(this.avatarEntity); } public static int pack(int id, float value) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 9dc02b302..a9f777a2c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -15,6 +15,7 @@ import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; +import org.geysermc.geyser.entity.type.player.AvatarEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; @@ -37,7 +38,7 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { private final GeyserConnection session; private final Future ticker; - private final Map controllers = new WeakHashMap<>(); + private final Map controllers = new WeakHashMap<>(); private UUID currentEmote; private ConnectionType connectionType = ConnectionType.NONE; @@ -181,7 +182,7 @@ public void stopEmote() { } public void stopEmote(GeyserPlayerEntity player) { - if (player instanceof PlayerEntity entity && this.controllers.containsKey(entity)) { + if (player instanceof AvatarEntity entity && this.controllers.containsKey(entity)) { this.controllers.get(entity).stop(); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java index e8448d6b1..0251ff202 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockPacketsUtils.java @@ -1,20 +1,20 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; import org.cloudburstmc.protocol.bedrock.packet.AnimateEntityPacket; -import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.type.player.AvatarEntity; public class BedrockPacketsUtils { public static final String BOB = "animation.player.bob"; - public static void sendInstantAnimation(String animation, PlayerEntity playerEntity) { + public static void sendInstantAnimation(String animation, AvatarEntity playerEntity) { sendAnimation(animation, "0", playerEntity); } - public static void sendBobAnimation(PlayerEntity playerEntity) { + public static void sendBobAnimation(AvatarEntity playerEntity) { sendAnimation(BOB, "1", playerEntity); } - public static void sendAnimation(String animation, String stopExpression, PlayerEntity playerEntity) { + public static void sendAnimation(String animation, String stopExpression, AvatarEntity playerEntity) { AnimateEntityPacket animatePacket = new AnimateEntityPacket(); animatePacket.setAnimation(animation); animatePacket.setNextState("default"); From f577f7904c1121d772a51b8cde1e0e746f1992c0 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 19 Dec 2025 03:18:45 +0700 Subject: [PATCH 45/70] Fix network --- .../io/github/kosmx/emotes/common/tools/MathHelper.java | 8 ++++++++ .../dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java | 3 ++- .../emotecraft/geyser/handler/GeyserNetworkInstance.java | 3 ++- 3 files changed, 12 insertions(+), 2 deletions(-) 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 f42cf1639..b21b7a6fe 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 @@ -1,5 +1,7 @@ package io.github.kosmx.emotes.common.tools; +import io.netty.buffer.ByteBuf; + import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; @@ -23,4 +25,10 @@ public static byte[] safeGetBytesFromBuffer(ByteBuffer byteBuffer) { } else return byteBuffer.array(); } + + public static byte[] readBytes(ByteBuf buf) { + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + return bytes; + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 59ea0d655..b2ffe0ca5 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -4,6 +4,7 @@ import io.github.kosmx.emotes.common.CommonData; import io.github.kosmx.emotes.common.SerializableConfig; import io.github.kosmx.emotes.common.network.EmotePacket; +import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.server.config.ConfigSerializer; import io.github.kosmx.emotes.server.config.Serializer; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; @@ -126,7 +127,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] ByteBuf byteBuf = Unpooled.buffer(); DinnerboneProtocolUtils.writeChannels(byteBuf, EMOTECRAFT_CHANNELS); - session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, byteBuf.array())); + session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, MathHelper.readBytes(byteBuf))); byteBuf.release(); if (((ProtocolProvider) session).ec$state() == ProtocolState.GAME) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index a9f777a2c..36c0579b0 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -8,6 +8,7 @@ import io.github.kosmx.emotes.common.network.EmotePacket; import io.github.kosmx.emotes.common.network.PacketConfig; import io.github.kosmx.emotes.common.network.objects.NetData; +import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; import io.netty.buffer.ByteBuf; @@ -72,7 +73,7 @@ public void sendMessage(EmotePacket packet, @Nullable UUID target) { ByteBuf buf = Unpooled.buffer(); packet.write(buf); ((GeyserSession) this.session).sendDownstreamPacket(new ServerboundCustomPayloadPacket( - EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, buf.array() + EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, MathHelper.readBytes(buf) )); buf.release(); } From b110800bac39e24012c4d535d28354fb687a98fb Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 19 Dec 2025 03:38:54 +0700 Subject: [PATCH 46/70] Update GeyserRun.run.xml --- .run/GeyserRun.run.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.run/GeyserRun.run.xml b/.run/GeyserRun.run.xml index 25e788b0b..3f0a78544 100644 --- a/.run/GeyserRun.run.xml +++ b/.run/GeyserRun.run.xml @@ -2,7 +2,7 @@ From f21cacfc54334a1b07200df33570e0accc42f65f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 01:38:11 +0700 Subject: [PATCH 47/70] MethodHandles --- .../mods/emotecraft/geyser/EmotecraftExt.java | 17 ++------ .../emotecraft/geyser/GeyserBootstrap.java | 14 ++++++- .../geyser/fuckery/GeyserSessionPatch.java | 42 ------------------- .../geyser/fuckery/ProtocolProvider.java | 12 ------ .../geyser/fuckery/ReflectHacks.java | 39 +++++++++++++++++ 5 files changed, 54 insertions(+), 70 deletions(-) delete mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java delete mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index b2ffe0ca5..ba7e70cad 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -26,21 +26,18 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.pack.GeyserResourcePackManifest; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.ProtocolState; import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; -import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; -import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ReflectHacks; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.ConnectionType; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.GeyserNetworkInstance; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockEmoteLoader; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.DinnerboneProtocolUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; -import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -51,14 +48,6 @@ */ @SuppressWarnings("unused") public class EmotecraftExt implements Extension { - static { - try { - GeyserSessionPatch.patchClass(PendingMicrosoftAuthentication.class, "org/geysermc/geyser/session/GeyserSession.class", GeyserSessionPatch::patch); - } catch (ReflectiveOperationException | IOException e) { - throw new RuntimeException(e); - } - } - private static final Map INSTANCES = new ConcurrentHashMap<>(); public static final Key MINECRAFT_REGISTER_TYPE = MinecraftKey.key("register"); @@ -130,7 +119,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(type, MathHelper.readBytes(byteBuf))); byteBuf.release(); - if (((ProtocolProvider) session).ec$state() == ProtocolState.GAME) { + if (ReflectHacks.getProtocol(session).getOutboundState() == ProtocolState.GAME) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); if (networkInstance.getConnectionType() == ConnectionType.NONE) { @@ -146,7 +135,7 @@ private void onMinecraftRegisterPayload(GeyserSession session, Key type, byte[] private void onEmotecraftPayload(GeyserConnection session, Key channel, byte[] bytes) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.computeIfAbsent(session, GeyserNetworkInstance::new); if (networkInstance.getConnectionType() == ConnectionType.NONE) { - if (((ProtocolProvider) session).ec$state() == ProtocolState.CONFIGURATION) { + if (ReflectHacks.getProtocol((GeyserSession) session).getOutboundState() == ProtocolState.CONFIGURATION) { CommonData.LOGGER.debug("Configuring emotecraft..."); networkInstance.sendC2SConfig(); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index 7d8103cde..a1a2a19c7 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -6,9 +6,12 @@ import javassist.CtMethod; import org.geysermc.geyser.extension.GeyserExtensionContainer; import org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap; -import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GeyserSessionPatch; import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.util.Objects; +import java.util.function.UnaryOperator; /** * Used to run Emotecraft in a dev environment. @@ -19,7 +22,7 @@ public class GeyserBootstrap { } public static void main(String[] args) throws ReflectiveOperationException, IOException { - GeyserSessionPatch.patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", GeyserBootstrap::patch); + GeyserBootstrap.patchClass(GeyserExtensionContainer.class, "org/geysermc/geyser/extension/GeyserExtensionLoader.class", GeyserBootstrap::patch); GeyserStandaloneBootstrap.main(args); } @@ -53,4 +56,11 @@ private static byte[] patch(byte[] bytes) { throw new RuntimeException(e); } } + + public static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { + try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { + byte[] bytecode = patcher.apply(is.readAllBytes()); + MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); + } + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java deleted file mode 100644 index a9c8161cc..000000000 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/GeyserSessionPatch.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; - -import javassist.*; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.util.Objects; -import java.util.function.UnaryOperator; - -public class GeyserSessionPatch { - public static final String CLASS_NAME = "org.geysermc.geyser.session.GeyserSession"; - - public static byte[] patch(byte[] bytes) { - ClassPool pool = new ClassPool(null); - pool.insertClassPath(new LoaderClassPath(GeyserSessionPatch.class.getClassLoader())); - pool.appendClassPath(new ByteArrayClassPath(CLASS_NAME, bytes)); - - try { - CtClass cc = pool.get(CLASS_NAME); - - CtClass protocolProviderInterface = pool.get("org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ProtocolProvider"); - cc.addInterface(protocolProviderInterface); - cc.addMethod(CtNewMethod.make(""" - public org.geysermc.mcprotocollib.protocol.MinecraftProtocol ec$protocol() { - return this.protocol; - }""", cc - )); - - return cc.toBytecode(); - } catch (Exception ex) { - throw new RuntimeException("Failed to patch!", ex); - } - } - - public static void patchClass(Class nearClass, String name, UnaryOperator patcher) throws ReflectiveOperationException, IOException { - try (InputStream is = Objects.requireNonNull(nearClass.getClassLoader().getResourceAsStream(name))) { - byte[] bytecode = patcher.apply(is.readAllBytes()); - MethodHandles.privateLookupIn(nearClass, MethodHandles.lookup()).defineClass(bytecode); - } - } -} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java deleted file mode 100644 index a86962bba..000000000 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ProtocolProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; - -import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; -import org.geysermc.mcprotocollib.protocol.data.ProtocolState; - -public interface ProtocolProvider { - MinecraftProtocol ec$protocol(); - - default ProtocolState ec$state() { - return ec$protocol().getOutboundState(); - } -} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java new file mode 100644 index 000000000..5311b846d --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java @@ -0,0 +1,39 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery; + +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; + +import static io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess.UNSAFE; + +public class ReflectHacks { + @SuppressWarnings("removal") + protected static final MethodHandles.Lookup TRUSTED_LOOKUP = uncheck(() -> { + Field hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + return (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(hackfield), UNSAFE.staticFieldOffset(hackfield)); + }); + + private static final VarHandle GEYSER_SESSION_PROTOCOL = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findVarHandle( + GeyserSession.class, "protocol", MinecraftProtocol.class + )); + + public static MinecraftProtocol getProtocol(GeyserSession session) { + return (MinecraftProtocol) GEYSER_SESSION_PROTOCOL.get(session); + } + + @FunctionalInterface + public interface Supplier_WithExceptions { + T get() throws E; + } + + public static R uncheck(Supplier_WithExceptions supplier) { + try { + return supplier.get(); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } +} From 6f5d0ff928496084a9ce8bf75038095e8b696de4 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 02:26:10 +0700 Subject: [PATCH 48/70] waist --- .../geyser/animator/GeyserAnimationController.java | 5 ++++- .../geyser/utils/resourcepack/EmoteResourcePack.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 48dd12d0c..54d3c200d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -81,7 +81,10 @@ public PlayerAnimBone get3DTransform(@NonNull PlayerAnimBone bone) { if ("left_arm".equals(boneName) || "right_arm".equals(boneName) || "head".equals(boneName)) { bone.applyOtherBone(get3DTransform(new PlayerAnimBone("torso")).scale(-1)); - } else if ("cape".equals(boneName)) { + } else if ("left_leg".equals(boneName) || "right_leg".equals(boneName)) { + bone.applyOtherBone(get3DTransform(new PlayerAnimBone("body"))); + + } if ("cape".equals(boneName)) { bone.rotX *= -1; } return bone; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index d01da00fc..302aa3610 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -90,7 +90,7 @@ private JsonObject generateAnimation() { ))); String bedrockBone = UniversalAnimLoader.restorePlayerBoneName(boneName); - if ("body".equals(bedrockBone)) bedrockBone = "root"; + if ("body".equals(bedrockBone)) bedrockBone = "waist"; if ("torso".equals(bedrockBone)) bedrockBone = "body"; bones.add(bedrockBone, bone); } From 37b0ebc1a293c0aaf521de7884162f8ac0e46f3f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 02:45:12 +0700 Subject: [PATCH 49/70] Fixes --- .../geyser/animator/GeyserAnimationController.java | 2 +- .../emotecraft/geyser/handler/GeyserNetworkInstance.java | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 54d3c200d..20f40b87c 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -96,7 +96,7 @@ protected void updateBone(GeyserEntityPropertyManager propertyManager, String pa updateAxis(propertyManager, partKey, TransformType.ROTATION, (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) ); - updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + // updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); } protected void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 36c0579b0..8d0cc3355 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -48,7 +48,13 @@ public GeyserNetworkInstance(GeyserConnection session) { this.session = session; this.ticker = EXECUTOR.scheduleAtFixedRate(() -> this.controllers.values() - .forEach(GeyserAnimationController::run), 0L, 50L, TimeUnit.MILLISECONDS + .forEach(controller -> { + try { + controller.run(); + } catch (Throwable th) { + th.printStackTrace(); + } + }), 0L, 50L, TimeUnit.MILLISECONDS ); } From 7aef428929850cbd268c92fa43b31877cfc6f12e Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 22:05:15 +0700 Subject: [PATCH 50/70] More ids --- .../animator/GeyserAnimationController.java | 2 +- .../utils/resourcepack/EmoteResourcePack.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 20f40b87c..231a819ab 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -159,7 +159,7 @@ protected void internalStop() { } public static int pack(int id, float value) { - id = Math.max(0, Math.min(99, id)); + id = Math.max(-99, Math.min(99, id)); value = Math.max(-9999.99f, Math.min(9999.99f, value)); int intValue = Math.round(value * 100f); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 302aa3610..f7036676d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -69,7 +69,7 @@ private EmoteResourcePack(ResourcePackManifest manifest) { private JsonObject generateAnimation() { JsonObject bones = new JsonObject(); - int id = 0; + int id = -98; this.identifiers.clear(); for (String boneName : GeyserAnimationController.getRegisteredBones()) { @@ -150,15 +150,18 @@ private JsonArray generateBone(int x, int y, int z, float defaultValue, EnumMap< JsonArray axis = new JsonArray(); axis.add(this.molangScript - .replace("{BONE_ID}", String.valueOf(x)) + .replace("{BONE_ID_RAW}", String.valueOf(x)) + .replace("{BONE_ID}", String.valueOf(x).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript - .replace("{BONE_ID}", String.valueOf(y)) + .replace("{BONE_ID_RAW}", String.valueOf(y)) + .replace("{BONE_ID}", String.valueOf(y).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript - .replace("{BONE_ID}", String.valueOf(z)) + .replace("{BONE_ID_RAW}", String.valueOf(z)) + .replace("{BONE_ID}", String.valueOf(z).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); return axis; @@ -213,8 +216,8 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { String prop = "variable." + identifier.path(); molangScript.append(prop).append(" = q.property('").append(identifier).append("');"); - molangScript.append("variable.bone_{BONE_ID}_target = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID}) ? "); - molangScript.append("((").append(prop).append(" - {BONE_ID} * 10000000 - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); + molangScript.append("variable.bone_{BONE_ID}_target = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID_RAW}) ? "); + molangScript.append("((").append(prop).append(" - ({BONE_ID_RAW} * 10000000) - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); } molangScript.append("variable.bone_{BONE_ID} = math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.3);"); From 426a7af01146625c780173348c07dafbc55ade5f Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 23:26:39 +0700 Subject: [PATCH 51/70] bends --- .../geyser/animator/BendingGeometry.java | 124 ++++++++++++++++++ .../animator/GeyserAnimationController.java | 29 +++- .../geyser/fuckery/ReflectHacks.java | 2 +- .../utils/resourcepack/EmoteResourcePack.java | 9 +- 4 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java new file mode 100644 index 000000000..a38add4d6 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java @@ -0,0 +1,124 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.zigythebird.playeranimcore.PlayerAnimLib; +import com.zigythebird.playeranimcore.loading.UniversalAnimLoader; +import io.github.kosmx.emotes.common.CommonData; +import org.geysermc.geyser.api.skin.SkinGeometry; +import org.geysermc.geyser.skin.SkinManager; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ReflectHacks; + +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.Set; + +public class BendingGeometry { + private static final VarHandle SKIN_MANAGER_GEOMETRY = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findStaticVarHandle( + SkinManager.class, "GEOMETRY", String.class + )); + + public static final Set BENDABLE_BONES = Set.of( + "right_arm", "left_arm", + "body", + "right_leg", "left_leg" + ); + public static final String BEND_SUFFIX = "_bend"; + + public static SkinGeometry addBoneBends(SkinGeometry geometry) { + JsonObject geometryObj = PlayerAnimLib.GSON.fromJson( + geometry.geometryData().isBlank() ? (String) SKIN_MANAGER_GEOMETRY.get() : geometry.geometryData(), JsonObject.class + ); + for (JsonElement element : geometryObj.getAsJsonArray("minecraft:geometry")) { + addBoneBends(element.getAsJsonObject()); + } + return new SkinGeometry(geometry.geometryName(), PlayerAnimLib.GSON.toJson(geometryObj)); + } + + private static void addBoneBends(JsonObject geometry) { + String identifier = geometry.getAsJsonObject("description").get("identifier").getAsString(); + CommonData.LOGGER.info("Patching '{}' for bends...", identifier); + + JsonArray bones = geometry.getAsJsonArray("bones"); + for (JsonElement element : new ArrayList<>(bones.asList())) { + JsonObject boneObj = element.getAsJsonObject(); + + if (!boneObj.has("cubes")) continue; // Skip bones without cubes + + String boneName = UniversalAnimLoader.getCorrectPlayerBoneName(boneObj.get("name").getAsString()); + if (BendingGeometry.BENDABLE_BONES.contains(boneName)) addBoneBendsToBone(bones, boneObj); + } + geometry.add("bones", bones); + } + + private static void addBoneBendsToBone(JsonArray bones, JsonObject bone) { + int boneSize = bone.getAsJsonArray("cubes").get(0).getAsJsonObject() + .getAsJsonArray("size").get(1).getAsInt(); + + JsonObject secondBone = makeCubeBendable(bone); + String name = bone.get("name").getAsString(); + for (JsonElement element : new ArrayList<>(bones.asList())) { // Fix hierarchy + JsonObject boneObj = element.getAsJsonObject(); + + if (boneObj.has("parent") && name.equals(boneObj.get("parent").getAsString())) { + JsonObject firstCube = boneObj.has("cubes") ? boneObj.getAsJsonArray("cubes").get(0).getAsJsonObject() : new JsonObject(); + + if (firstCube.has("inflate") && boneSize == firstCube.getAsJsonArray("size").get(1).getAsInt()) { + CommonData.LOGGER.info("Second layer detected! {}", boneObj); + + JsonObject secondBoneSecondLayer = makeCubeBendable(boneObj); + boneObj.add("parent", bone.get("name")); + secondBoneSecondLayer.add("parent", secondBone.get("name")); + bones.add(secondBoneSecondLayer); + } else { + boneObj.add("parent", secondBone.get("name")); + } + } + } + bones.add(secondBone); + } + + /** + * Patches the bone and adds a second one + * @param bone Mutable bone + * @return Second bending bone + */ + private static JsonObject makeCubeBendable(JsonObject bone) { + JsonObject bendableCube = bone.getAsJsonArray("cubes").get(0).getAsJsonObject(); + { // Patch size + JsonArray size = bendableCube.getAsJsonArray("size"); + size.set(1, new JsonPrimitive(size.get(1).getAsFloat() / 2F)); + } + + JsonObject secondBendableCube = bendableCube.deepCopy(); + float secondBendableCubeSizeY = secondBendableCube.getAsJsonArray("size").get(1).getAsFloat(); + { // Patch second cube uv + pivot + JsonArray uv = secondBendableCube.getAsJsonArray("uv"); + uv.set(1, new JsonPrimitive(uv.get(1).getAsFloat() + secondBendableCubeSizeY)); + } + + JsonArray secondCubes = new JsonArray(); + secondCubes.add(secondBendableCube); + + { // Patch first cube origin + float sizeY = bendableCube.getAsJsonArray("size").get(1).getAsFloat(); + JsonArray origin = bendableCube.getAsJsonArray("origin"); + origin.set(1, new JsonPrimitive(origin.get(1).getAsFloat() + sizeY)); + } + + JsonObject secondBone = new JsonObject(); + secondBone.add("parent", bone.get("name")); + secondBone.addProperty("name", UniversalAnimLoader.restorePlayerBoneName(bone.get("name").getAsString() + BEND_SUFFIX)); + secondBone.add("cubes", secondCubes); + + JsonArray pivot = new JsonArray(); + pivot.add(bone.get("pivot").getAsJsonArray().get(0)); + pivot.add(bone.get("pivot").getAsJsonArray().get(1).getAsFloat() - secondBendableCubeSizeY); + pivot.add(bone.get("pivot").getAsJsonArray().get(2)); + secondBone.add("pivot", pivot); + + return secondBone; + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 231a819ab..91ddb6af6 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -97,6 +97,10 @@ protected void updateBone(GeyserEntityPropertyManager propertyManager, String pa (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) ); // updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + + if (BendingGeometry.BENDABLE_BONES.contains(partKey)) { + updateBend(propertyManager, partKey + BendingGeometry.BEND_SUFFIX, bone.bend); + } } protected void updateAxis(GeyserEntityPropertyManager propertyManager, String partKey, TransformType type, float x, float y, float z) { @@ -106,6 +110,27 @@ protected void updateAxis(GeyserEntityPropertyManager propertyManager, String pa updateProperty(propertyManager, getAvailableProperty(), pack(ids.get(Axis.Z), z)); } + protected void updateBend(GeyserEntityPropertyManager propertyManager, String partKey, float bend) { + EmoteResourcePack resourcePack = EmotecraftExt.getInstance().getResourcePack(); + + updateProperty(propertyManager, getAvailableProperty(), pack( + resourcePack.getAxisIds(partKey, TransformType.ROTATION).get(Axis.X), + (float) Math.toDegrees(bend) + )); + + float radius = 2.0f; + float angle = Math.abs(bend); + + updateProperty(propertyManager, getAvailableProperty(), pack( + resourcePack.getAxisIds(partKey, TransformType.POSITION).get(Axis.Y), + (float) (radius * (1 - Math.cos(angle))) + )); + updateProperty(propertyManager, getAvailableProperty(), pack( + resourcePack.getAxisIds(partKey, TransformType.POSITION).get(Axis.Z), + (float) -(radius * Math.sin(angle)) + )); + } + private GeyserIntEntityProperty getAvailableProperty() { for (GeyserIntEntityProperty property : EmotecraftExt.getInstance().getResourcePack().getRegisteredProperties()) { if (this.lastUsedProperties.contains(property.identifier())) continue; @@ -173,6 +198,8 @@ public static int pack(int id, float value) { */ public static Collection getRegisteredBones() { GeyserAnimationController controller = new GeyserAnimationController(null); - return Collections.unmodifiableCollection(controller.bones.keySet()); + Set bones = new HashSet<>(controller.bones.keySet()); + for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); + return Collections.unmodifiableCollection(bones); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java index 5311b846d..fdb1e134a 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/fuckery/ReflectHacks.java @@ -11,7 +11,7 @@ public class ReflectHacks { @SuppressWarnings("removal") - protected static final MethodHandles.Lookup TRUSTED_LOOKUP = uncheck(() -> { + public static final MethodHandles.Lookup TRUSTED_LOOKUP = uncheck(() -> { Field hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); return (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(hackfield), UNSAFE.staticFieldOffset(hackfield)); }); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index f7036676d..dae69c498 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -21,6 +21,7 @@ import org.geysermc.geyser.api.util.Identifier; import org.geysermc.geyser.pack.GeyserResourcePack; import org.geysermc.geyser.pack.GeyserResourcePackManifest; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.BendingGeometry; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; import java.io.ByteArrayOutputStream; @@ -85,9 +86,11 @@ private JsonObject generateAnimation() { bone.add("position", generateBone(id++, id++, id++, 0, transformType.computeIfAbsent( TransformType.POSITION, k -> new EnumMap<>(Axis.class) ))); - bone.add("scale", generateBone(id++, id++, id++, 1, transformType.computeIfAbsent( - TransformType.SCALE, k -> new EnumMap<>(Axis.class) - ))); + if (!boneName.endsWith(BendingGeometry.BEND_SUFFIX)) { + bone.add("scale", generateBone(id++, id++, id++, 1, transformType.computeIfAbsent( + TransformType.SCALE, k -> new EnumMap<>(Axis.class) + ))); + } String bedrockBone = UniversalAnimLoader.restorePlayerBoneName(boneName); if ("body".equals(bedrockBone)) bedrockBone = "waist"; From fe597b871790b17bb121a7be5f8bf7cbbb8f4927 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 20 Dec 2025 23:50:35 +0700 Subject: [PATCH 52/70] Fix uv --- .../geyser/animator/BendingGeometry.java | 73 +++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java index a38add4d6..e4e0b8b58 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java @@ -57,7 +57,7 @@ private static void addBoneBendsToBone(JsonArray bones, JsonObject bone) { int boneSize = bone.getAsJsonArray("cubes").get(0).getAsJsonObject() .getAsJsonArray("size").get(1).getAsInt(); - JsonObject secondBone = makeCubeBendable(bone); + JsonObject secondBone = makeCubeBendable(bone, true); String name = bone.get("name").getAsString(); for (JsonElement element : new ArrayList<>(bones.asList())) { // Fix hierarchy JsonObject boneObj = element.getAsJsonObject(); @@ -68,7 +68,7 @@ private static void addBoneBendsToBone(JsonArray bones, JsonObject bone) { if (firstCube.has("inflate") && boneSize == firstCube.getAsJsonArray("size").get(1).getAsInt()) { CommonData.LOGGER.info("Second layer detected! {}", boneObj); - JsonObject secondBoneSecondLayer = makeCubeBendable(boneObj); + JsonObject secondBoneSecondLayer = makeCubeBendable(boneObj, false); boneObj.add("parent", bone.get("name")); secondBoneSecondLayer.add("parent", secondBone.get("name")); bones.add(secondBoneSecondLayer); @@ -85,8 +85,10 @@ private static void addBoneBendsToBone(JsonArray bones, JsonObject bone) { * @param bone Mutable bone * @return Second bending bone */ - private static JsonObject makeCubeBendable(JsonObject bone) { + private static JsonObject makeCubeBendable(JsonObject bone, boolean drawCrossSection) { JsonObject bendableCube = bone.getAsJsonArray("cubes").get(0).getAsJsonObject(); + if (bendableCube.get("uv").isJsonArray()) bendableCube.add("uv", expandCubeUV(bendableCube)); + { // Patch size JsonArray size = bendableCube.getAsJsonArray("size"); size.set(1, new JsonPrimitive(size.get(1).getAsFloat() / 2F)); @@ -95,8 +97,37 @@ private static JsonObject makeCubeBendable(JsonObject bone) { JsonObject secondBendableCube = bendableCube.deepCopy(); float secondBendableCubeSizeY = secondBendableCube.getAsJsonArray("size").get(1).getAsFloat(); { // Patch second cube uv + pivot - JsonArray uv = secondBendableCube.getAsJsonArray("uv"); - uv.set(1, new JsonPrimitive(uv.get(1).getAsFloat() + secondBendableCubeSizeY)); + JsonObject uv = secondBendableCube.getAsJsonObject("uv"); + String[] sides = {"north", "south", "east", "west"}; + + for (String side : sides) { + JsonObject face = uv.getAsJsonObject(side); + + JsonArray uvCoords = face.getAsJsonArray("uv"); + uvCoords.set(1, new JsonPrimitive(uvCoords.get(1).getAsFloat() + secondBendableCubeSizeY)); + + JsonArray uvSize = face.getAsJsonArray("uv_size"); + uvSize.set(1, new JsonPrimitive(secondBendableCubeSizeY)); + } + + if (!drawCrossSection) uv.remove("up"); + } + + { // Patch first cube uv + JsonObject uv = bendableCube.getAsJsonObject("uv"); + String[] sides = {"north", "south", "east", "west"}; + + for (String side : sides) { + JsonObject face = uv.getAsJsonObject(side); + JsonArray uvSize = face.getAsJsonArray("uv_size"); + uvSize.set(1, new JsonPrimitive(secondBendableCubeSizeY)); + } + + if (drawCrossSection) { + uv.add("down", uv.getAsJsonObject("up").deepCopy()); + } else { + uv.remove("down"); + } } JsonArray secondCubes = new JsonArray(); @@ -121,4 +152,36 @@ private static JsonObject makeCubeBendable(JsonObject bone) { return secondBone; } + + public static JsonObject expandCubeUV(JsonObject cube) { + JsonArray uvArray = cube.getAsJsonArray("uv"); + float u = uvArray.get(0).getAsFloat(); + float v = uvArray.get(1).getAsFloat(); + + JsonArray sizeArray = cube.getAsJsonArray("size"); + float w = sizeArray.get(0).getAsFloat(); + float h = sizeArray.get(1).getAsFloat(); + float d = sizeArray.get(2).getAsFloat(); + + JsonObject root = new JsonObject(); + root.add("east", createFace(u, v + d, d, h)); + root.add("north", createFace(u + d, v + d, w, h)); + root.add("west", createFace(u + d + w, v + d, d, h)); + root.add("south", createFace(u + d + w + d, v + d, w, h)); + root.add("up", createFace(u + d, v, w, d)); + root.add("down", createFace(u + d + w, v, w, d)); + + return root; + } + + private static JsonObject createFace(float u, float v, float w, float h) { + JsonObject face = new JsonObject(); + JsonArray uv = new JsonArray(); + uv.add(u); uv.add(v); + face.add("uv", uv); + JsonArray size = new JsonArray(); + size.add(w); size.add(h); + face.add("uv_size", size); + return face; + } } From 40c2cd6c3daf8dfa89998d36b1fa037d8fba5272 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 21 Dec 2025 00:25:04 +0700 Subject: [PATCH 53/70] Refactor executor --- .../animator/GeyserAnimationController.java | 33 ++++++++++++++++--- .../geyser/handler/GeyserNetworkInstance.java | 22 +++++-------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 91ddb6af6..bdca4f97a 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -22,18 +22,33 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; +import java.io.Closeable; +import java.io.IOException; import java.time.Duration; import java.util.*; +import java.util.concurrent.*; + +public class GeyserAnimationController extends HumanoidAnimationController implements Runnable, Closeable { + private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(100, Thread.ofVirtual() + .name("emotecraft-animating-") + .uncaughtExceptionHandler((t, e) -> CommonData.LOGGER.warn("Failed to animate!", e)) + .factory() + ); -public class GeyserAnimationController extends HumanoidAnimationController implements Runnable { private final Set lastUsedProperties = new HashSet<>(1); protected final AvatarEntity avatarEntity; + protected final Future ticker; private final Set dirtyBones = new HashSet<>(); public GeyserAnimationController(AvatarEntity avatarEntity) { + this(avatarEntity, EXECUTOR); + } + + public GeyserAnimationController(AvatarEntity avatarEntity, ScheduledExecutorService executor) { super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); this.avatarEntity = avatarEntity; + this.ticker = executor.scheduleAtFixedRate(this, 0L, 50L, TimeUnit.MILLISECONDS); } @Override @@ -193,13 +208,21 @@ public static int pack(int id, float value) { return id * 10000000 + intValue; } + @Override + public void close() throws IOException { + this.ticker.cancel(true); + } + /** * A small hack that allows us to get all registered bones. */ public static Collection getRegisteredBones() { - GeyserAnimationController controller = new GeyserAnimationController(null); - Set bones = new HashSet<>(controller.bones.keySet()); - for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); - return Collections.unmodifiableCollection(bones); + try (GeyserAnimationController controller = new GeyserAnimationController(null)) { + Set bones = new HashSet<>(controller.bones.keySet()); + for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); + return Collections.unmodifiableCollection(bones); + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 8d0cc3355..0a5814630 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -31,13 +31,10 @@ import java.util.concurrent.*; public class GeyserNetworkInstance extends AbstractNetworkInstance { - private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); - private final HashMap versions = new HashMap<>(); // private final Map queue = new ConcurrentHashMap<>(); private final UUIDMap animations = new UUIDMap<>(); private final GeyserConnection session; - private final Future ticker; private final Map controllers = new WeakHashMap<>(); @@ -46,16 +43,6 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { public GeyserNetworkInstance(GeyserConnection session) { this.session = session; - - this.ticker = EXECUTOR.scheduleAtFixedRate(() -> this.controllers.values() - .forEach(controller -> { - try { - controller.run(); - } catch (Throwable th) { - th.printStackTrace(); - } - }), 0L, 50L, TimeUnit.MILLISECONDS - ); } @Override @@ -280,6 +267,13 @@ public void disconnect() { this.connectionType = ConnectionType.NONE; this.animations.clear(); this.versions.clear(); - this.ticker.cancel(true); + for (GeyserAnimationController controller : this.controllers.values()) { + try { + controller.close(); + } catch (IOException e) { + CommonData.LOGGER.warn("Failed to close controller!", e); + } + } + this.controllers.clear(); } } From 7fe18b9d6aff54b8c2931c4546a1f820be920a10 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 21 Dec 2025 00:28:14 +0700 Subject: [PATCH 54/70] smooth --- .../emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index dae69c498..45629ab60 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -223,7 +223,7 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { molangScript.append("((").append(prop).append(" - ({BONE_ID_RAW} * 10000000) - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); } - molangScript.append("variable.bone_{BONE_ID} = math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.3);"); + molangScript.append("variable.bone_{BONE_ID} = math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); molangScript.append("return variable.bone_{BONE_ID};"); CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); From 9da9e7d1b44e8a36dfaeb7918ffacc96492f9510 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 21 Dec 2025 00:45:00 +0700 Subject: [PATCH 55/70] Fix lerp --- .../utils/resourcepack/EmoteResourcePack.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 45629ab60..1a3e9c4fd 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -80,14 +80,14 @@ private JsonObject generateAnimation() { k -> new EnumMap<>(TransformType.class) ); - bone.add("rotation", generateBone(id++, id++, id++, 0, transformType.computeIfAbsent( + bone.add("rotation", generateBone(true, id++, id++, id++, 0, transformType.computeIfAbsent( TransformType.ROTATION, k -> new EnumMap<>(Axis.class) ))); - bone.add("position", generateBone(id++, id++, id++, 0, transformType.computeIfAbsent( + bone.add("position", generateBone(false, id++, id++, id++, 0, transformType.computeIfAbsent( TransformType.POSITION, k -> new EnumMap<>(Axis.class) ))); if (!boneName.endsWith(BendingGeometry.BEND_SUFFIX)) { - bone.add("scale", generateBone(id++, id++, id++, 1, transformType.computeIfAbsent( + bone.add("scale", generateBone(false, id++, id++, id++, 1, transformType.computeIfAbsent( TransformType.SCALE, k -> new EnumMap<>(Axis.class) ))); } @@ -146,23 +146,26 @@ private byte[] generatePackData() { } } - private JsonArray generateBone(int x, int y, int z, float defaultValue, EnumMap axisMap) { + private JsonArray generateBone(boolean rotate, int x, int y, int z, float defaultValue, EnumMap axisMap) { axisMap.put(Axis.X, x); axisMap.put(Axis.Y, y); axisMap.put(Axis.Z, z); JsonArray axis = new JsonArray(); axis.add(this.molangScript + .replace("{{ROTATE}}", rotate ? "rotate" : "") .replace("{BONE_ID_RAW}", String.valueOf(x)) .replace("{BONE_ID}", String.valueOf(x).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript + .replace("{{ROTATE}}", rotate ? "rotate" : "") .replace("{BONE_ID_RAW}", String.valueOf(y)) .replace("{BONE_ID}", String.valueOf(y).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript + .replace("{{ROTATE}}", rotate ? "rotate" : "") .replace("{BONE_ID_RAW}", String.valueOf(z)) .replace("{BONE_ID}", String.valueOf(z).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) @@ -223,7 +226,7 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { molangScript.append("((").append(prop).append(" - ({BONE_ID_RAW} * 10000000) - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); } - molangScript.append("variable.bone_{BONE_ID} = math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); + molangScript.append("variable.bone_{BONE_ID} = math.lerp{{ROTATE}}(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); molangScript.append("return variable.bone_{BONE_ID};"); CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); From b4eb6d0d11220514d0c3322ab0c896789802184b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 21 Dec 2025 05:08:55 +0700 Subject: [PATCH 56/70] Update PAL --- .../animator/GeyserAnimationController.java | 29 ++++++++++--------- .../geyser/handler/GeyserNetworkInstance.java | 7 +---- gradle.properties | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index bdca4f97a..762a34732 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -6,7 +6,9 @@ import com.zigythebird.playeranimcore.enums.Axis; import com.zigythebird.playeranimcore.enums.PlayState; import com.zigythebird.playeranimcore.enums.TransformType; +import com.zigythebird.playeranimcore.math.Vec3f; import com.zigythebird.playeranimcore.molang.MolangLoader; +import com.zigythebird.playeranimcore.util.MatrixUtil; import io.github.kosmx.emotes.common.CommonData; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -23,7 +25,6 @@ import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.io.Closeable; -import java.io.IOException; import java.time.Duration; import java.util.*; import java.util.concurrent.*; @@ -67,7 +68,7 @@ public void run() { GeyserEntityPropertyManager propertyManager = this.avatarEntity.getPropertyManager(); if (propertyManager == null) return; - AnimationData data = new AnimationData(0, 1.0F); + AnimationData data = new AnimationData(0, 1.0F, false); tick(data); if (!isActive()) return; @@ -94,12 +95,19 @@ public PlayerAnimBone get3DTransform(@NonNull PlayerAnimBone bone) { String boneName = bone.getName(); if ("left_arm".equals(boneName) || "right_arm".equals(boneName) || "head".equals(boneName)) { - bone.applyOtherBone(get3DTransform(new PlayerAnimBone("torso")).scale(-1)); - + PlayerAnimBone torsoBone = get3DTransform(new PlayerAnimBone("torso")); + torsoBone.mulPos(-1); + torsoBone.mulRot(-1); + torsoBone.setScaleX(1 / bone.getScaleX()); + torsoBone.setScaleY(1 / bone.getScaleY()); + torsoBone.setScaleZ(1 / bone.getScaleZ()); + + MatrixUtil.applyParentsToChild(bone, Collections.singleton(torsoBone), this::getBonePosition); } else if ("left_leg".equals(boneName) || "right_leg".equals(boneName)) { - bone.applyOtherBone(get3DTransform(new PlayerAnimBone("body"))); + PlayerAnimBone body = get3DTransform(new PlayerAnimBone("body")); + MatrixUtil.applyParentsToChild(bone, Collections.singleton(body), this::getBonePosition); - } if ("cape".equals(boneName)) { + } else if ("cape".equals(boneName)) { bone.rotX *= -1; } return bone; @@ -201,15 +209,12 @@ protected void internalStop() { public static int pack(int id, float value) { id = Math.max(-99, Math.min(99, id)); value = Math.max(-9999.99f, Math.min(9999.99f, value)); - int intValue = Math.round(value * 100f); - intValue = intValue + 1000000; - - return id * 10000000 + intValue; + return id * 10000000 + intValue + 1000000; } @Override - public void close() throws IOException { + public void close() { this.ticker.cancel(true); } @@ -221,8 +226,6 @@ public static Collection getRegisteredBones() { Set bones = new HashSet<>(controller.bones.keySet()); for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); return Collections.unmodifiableCollection(bones); - } catch (IOException e) { - throw new RuntimeException(e); } } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 0a5814630..195da7bb3 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.util.*; -import java.util.concurrent.*; public class GeyserNetworkInstance extends AbstractNetworkInstance { private final HashMap versions = new HashMap<>(); @@ -268,11 +267,7 @@ public void disconnect() { this.animations.clear(); this.versions.clear(); for (GeyserAnimationController controller : this.controllers.values()) { - try { - controller.close(); - } catch (IOException e) { - CommonData.LOGGER.warn("Failed to close controller!", e); - } + controller.close(); } this.controllers.clear(); } diff --git a/gradle.properties b/gradle.properties index 8550b8c74..759113438 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,7 +25,7 @@ loom.ignoreDependencyLoomVersionValidation=true # Dependencies java_version = 21 translationfallbacks_version = 1.1.0+mc1.21.8 - playeranimlib_version = 1.1.3+alpha.1+mc.1.21.11 + playeranimlib_version = 1.1.4+mc.1.21.11 bendablecuboids_version = 1.0.6+mc.1.21.9 modmenu_version = 17.0.0-alpha.1 fabric_api_version = 0.139.4+1.21.11 From 08a53b61319b10271c953036ea119d49582bc61e Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Mon, 22 Dec 2025 14:44:24 +0700 Subject: [PATCH 57/70] Fix lerp --- .../geyser/utils/resourcepack/EmoteResourcePack.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 1a3e9c4fd..a86086c70 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -226,7 +226,10 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { molangScript.append("((").append(prop).append(" - ({BONE_ID_RAW} * 10000000) - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); } - molangScript.append("variable.bone_{BONE_ID} = math.lerp{{ROTATE}}(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); + molangScript.append("variable.bone_{BONE_ID}_delta = math.abs(variable.bone_{BONE_ID} - variable.bone_{BONE_ID}_target);"); + molangScript.append("variable.bone_{BONE_ID} = (variable.bone_{BONE_ID}_delta < 0.001) ? "); + molangScript.append("variable.bone_{BONE_ID}_target : "); + molangScript.append("math.lerp{{ROTATE}}(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); molangScript.append("return variable.bone_{BONE_ID};"); CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); From 9820f2ec9feb25a2d95b1d02808605ee8427db27 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Fri, 26 Dec 2025 11:47:59 +0700 Subject: [PATCH 58/70] Fix scale --- .../animator/GeyserAnimationController.java | 12 ++--- .../geyser/animator/PackedProperty.java | 47 +++++++++++++++++++ .../utils/resourcepack/EmoteResourcePack.java | 9 ++-- 3 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/PackedProperty.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 762a34732..4e5f5a55f 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -6,7 +6,6 @@ import com.zigythebird.playeranimcore.enums.Axis; import com.zigythebird.playeranimcore.enums.PlayState; import com.zigythebird.playeranimcore.enums.TransformType; -import com.zigythebird.playeranimcore.math.Vec3f; import com.zigythebird.playeranimcore.molang.MolangLoader; import com.zigythebird.playeranimcore.util.MatrixUtil; import io.github.kosmx.emotes.common.CommonData; @@ -29,6 +28,8 @@ import java.util.*; import java.util.concurrent.*; +import static org.redlance.dima_dencep.mods.emotecraft.geyser.animator.PackedProperty.pack; + public class GeyserAnimationController extends HumanoidAnimationController implements Runnable, Closeable { private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(100, Thread.ofVirtual() .name("emotecraft-animating-") @@ -119,7 +120,7 @@ protected void updateBone(GeyserEntityPropertyManager propertyManager, String pa updateAxis(propertyManager, partKey, TransformType.ROTATION, (float) Math.toDegrees(bone.getRotX()), (float) Math.toDegrees(bone.getRotY()), (float) Math.toDegrees(bone.getRotZ()) ); - // updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); + updateAxis(propertyManager, partKey, TransformType.SCALE, bone.getScaleX(), bone.getScaleY(), bone.getScaleZ()); if (BendingGeometry.BENDABLE_BONES.contains(partKey)) { updateBend(propertyManager, partKey + BendingGeometry.BEND_SUFFIX, bone.bend); @@ -206,13 +207,6 @@ protected void internalStop() { BedrockPacketsUtils.sendBobAnimation(this.avatarEntity); } - public static int pack(int id, float value) { - id = Math.max(-99, Math.min(99, id)); - value = Math.max(-9999.99f, Math.min(9999.99f, value)); - int intValue = Math.round(value * 100f); - return id * 10000000 + intValue + 1000000; - } - @Override public void close() { this.ticker.cancel(true); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/PackedProperty.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/PackedProperty.java new file mode 100644 index 000000000..5c1e7cff1 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/PackedProperty.java @@ -0,0 +1,47 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; + +@SuppressWarnings("unused") // api +public final class PackedProperty { + public static final int ID_MIN = -99; + public static final int ID_MAX = 99; + public static final int ID_COUNT = ID_MAX - ID_MIN + 1; // 199 + + public static final int PROP_MIN = -1_000_000; + public static final int PROP_RANGE = 2_000_001; // [-1_000_000 .. +1_000_000] + public static final int SLOT = PROP_RANGE / ID_COUNT; // 10050 + public static final int CENTER = SLOT / 2; // 5025 + + // 10 => 0.1, 100 => 0.01, 1 => 1.0 + public static final int SCALE = 10; + + public static final float VALUE_MIN = -CENTER / (float) SCALE; + public static final float VALUE_MAX = (SLOT - 1 - CENTER) / (float) SCALE; + + public static final int PROP_MAX_USED = PROP_MIN + (SLOT * ID_COUNT) - 1; + + public static int pack(int id, float value) { + id = Math.max(ID_MIN, Math.min(ID_MAX, id)); + value = Math.max(VALUE_MIN, Math.min(VALUE_MAX, value)); + int idx = id - ID_MIN; // 0..198 + int quant = Math.round(value * SCALE); + int valueIndex = quant + CENTER; + if (valueIndex < 0) valueIndex = 0; + if (valueIndex > SLOT - 1) valueIndex = SLOT - 1; + int raw = idx * SLOT + valueIndex; + return PROP_MIN + raw; + } + + public static int unpackId(int packed) { + int raw = packed - PROP_MIN; + int idx = raw / SLOT; + return idx + ID_MIN; + } + + public static float unpackValue(int packed) { + int raw = packed - PROP_MIN; + int valueIndex = raw % SLOT; + int quant = valueIndex - CENTER; + return quant / (float) SCALE; + } +} + diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index a86086c70..67122ea54 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -23,6 +23,7 @@ import org.geysermc.geyser.pack.GeyserResourcePackManifest; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.BendingGeometry; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.PackedProperty; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -217,13 +218,15 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { this.registeredProperties.clear(); for (int i = 0; i < reserved; i++) { Identifier identifier = Identifier.of(CommonData.MOD_ID, String.format("property_%s", i)); - this.registeredProperties.add(event.registerIntegerProperty(PLAYER_IDENTIFIER, identifier, Integer.MIN_VALUE, Integer.MAX_VALUE)); + this.registeredProperties.add(event.registerIntegerProperty(PLAYER_IDENTIFIER, identifier, PackedProperty.PROP_MIN, PackedProperty.PROP_MAX_USED)); String prop = "variable." + identifier.path(); molangScript.append(prop).append(" = q.property('").append(identifier).append("');"); - molangScript.append("variable.bone_{BONE_ID}_target = (math.floor(").append(prop).append(" / 10000000) == {BONE_ID_RAW}) ? "); - molangScript.append("((").append(prop).append(" - ({BONE_ID_RAW} * 10000000) - 1000000) / 100) : variable.bone_{BONE_ID}_target;"); + molangScript.append("variable._raw = ").append(prop).append(" + 1000000;"); + molangScript.append("variable._id = math.floor(variable._raw / ").append(PackedProperty.SLOT).append(") - 99;"); + molangScript.append("variable._val = (math.mod(variable._raw, ").append(PackedProperty.SLOT).append(") - ").append(PackedProperty.CENTER).append(") / ").append(PackedProperty.SCALE).append(";"); + molangScript.append("variable.bone_{BONE_ID}_target = (variable._id == {BONE_ID_RAW}) ? ").append("variable._val : variable.bone_{BONE_ID}_target;"); } molangScript.append("variable.bone_{BONE_ID}_delta = math.abs(variable.bone_{BONE_ID} - variable.bone_{BONE_ID}_target);"); From ff3de7cefa07e01f88afe1b9e3e218ceb4a78492 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 27 Dec 2025 00:55:04 +0700 Subject: [PATCH 59/70] Disable lerp for rotate --- .../utils/resourcepack/EmoteResourcePack.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 67122ea54..483076c8d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -154,19 +154,19 @@ private JsonArray generateBone(boolean rotate, int x, int y, int z, float defaul JsonArray axis = new JsonArray(); axis.add(this.molangScript - .replace("{{ROTATE}}", rotate ? "rotate" : "") + .replace("{LERP_VALUE}", rotate ? "0" : "1") .replace("{BONE_ID_RAW}", String.valueOf(x)) .replace("{BONE_ID}", String.valueOf(x).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript - .replace("{{ROTATE}}", rotate ? "rotate" : "") + .replace("{LERP_VALUE}", rotate ? "0" : "1") .replace("{BONE_ID_RAW}", String.valueOf(y)) .replace("{BONE_ID}", String.valueOf(y).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) ); axis.add(this.molangScript - .replace("{{ROTATE}}", rotate ? "rotate" : "") + .replace("{LERP_VALUE}", rotate ? "0" : "1") .replace("{BONE_ID_RAW}", String.valueOf(z)) .replace("{BONE_ID}", String.valueOf(z).replace("-", "_")) .replace("{DEFAULT_VALUE}", String.valueOf(defaultValue)) @@ -229,10 +229,9 @@ public void onDefineEntityProperties(GeyserDefineEntityPropertiesEvent event) { molangScript.append("variable.bone_{BONE_ID}_target = (variable._id == {BONE_ID_RAW}) ? ").append("variable._val : variable.bone_{BONE_ID}_target;"); } - molangScript.append("variable.bone_{BONE_ID}_delta = math.abs(variable.bone_{BONE_ID} - variable.bone_{BONE_ID}_target);"); - molangScript.append("variable.bone_{BONE_ID} = (variable.bone_{BONE_ID}_delta < 0.001) ? "); - molangScript.append("variable.bone_{BONE_ID}_target : "); - molangScript.append("math.lerp{{ROTATE}}(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2);"); + molangScript.append("variable.bone_{BONE_ID} = ({LERP_VALUE} == 1) ? ("); + molangScript.append("(math.abs(variable.bone_{BONE_ID} - variable.bone_{BONE_ID}_target) < 0.001) ? variable.bone_{BONE_ID}_target : math.lerp(variable.bone_{BONE_ID}, variable.bone_{BONE_ID}_target, 0.2)"); + molangScript.append(") : variable.bone_{BONE_ID}_target;"); molangScript.append("return variable.bone_{BONE_ID};"); CommonData.LOGGER.debug("Registered {} properties!", this.registeredProperties.size()); From 94b13a58589d81b183c239e9d724b733396eb2c9 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 27 Dec 2025 09:39:02 +0700 Subject: [PATCH 60/70] Update GeyserAnimationController.java --- .../emotecraft/geyser/animator/GeyserAnimationController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 4e5f5a55f..9ad4c00ef 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -186,7 +186,7 @@ private void flushPropertiesImmediately() { this.avatarEntity.getSession().sendUpstreamPacketImmediately(packet); try { - Thread.sleep(Duration.ofMillis(10 + this.avatarEntity.getSession().ping())); // IDK + Thread.sleep(Duration.ofMillis(10)); } catch (InterruptedException ignored) {} this.lastUsedProperties.clear(); } From de3ea5871b5b1e55341fb9745c9417377d65e924 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sat, 27 Dec 2025 10:31:33 +0700 Subject: [PATCH 61/70] Optimization --- .../mods/emotecraft/geyser/EmotecraftExt.java | 6 +- .../geyser/animator/ControllerHolder.java | 17 ++++ .../animator/GeyserAnimationController.java | 96 +++++++++++-------- .../geyser/handler/GeyserNetworkInstance.java | 67 +++++-------- .../geyser/utils/GeyserEntityUtils.java | 38 ++++++++ 5 files changed, 137 insertions(+), 87 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index ba7e70cad..66d4dd0a6 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -15,12 +15,10 @@ import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; -import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; -import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.extension.Extension; @@ -158,7 +156,7 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { if (instance != null) instance.disconnect(); } - @Subscribe + /*@Subscribe public void onDefineCommands(GeyserDefineCommandsEvent event) { event.register(Command.builder(this) .name(rootCommand()) @@ -172,7 +170,7 @@ public void onDefineCommands(GeyserDefineCommandsEvent event) { ) .build() ); - } + }*/ @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java new file mode 100644 index 000000000..7a42825c6 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java @@ -0,0 +1,17 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.geyser.entity.type.player.AvatarEntity; + +import java.util.Map; +import java.util.UUID; + +public class ControllerHolder { + private static final Map CONTROLLERS = new Object2ObjectOpenHashMap<>(); + + public static GeyserAnimationController get(AvatarEntity entity) { + GeyserAnimationController controller = CONTROLLERS.computeIfAbsent(entity.getUuid(), GeyserAnimationController::new); + controller.subscribe(entity); + return controller; + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 9ad4c00ef..1c863d033 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -38,56 +38,70 @@ public class GeyserAnimationController extends HumanoidAnimationController imple ); private final Set lastUsedProperties = new HashSet<>(1); - protected final AvatarEntity avatarEntity; + private final Set listeners = new HashSet<>(); + + protected final UUID avatarId; protected final Future ticker; private final Set dirtyBones = new HashSet<>(); - public GeyserAnimationController(AvatarEntity avatarEntity) { - this(avatarEntity, EXECUTOR); + protected GeyserAnimationController(UUID avatarId) { + this(avatarId, EXECUTOR); } - public GeyserAnimationController(AvatarEntity avatarEntity, ScheduledExecutorService executor) { + protected GeyserAnimationController(UUID avatarId, ScheduledExecutorService executor) { super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); - this.avatarEntity = avatarEntity; + this.avatarId = avatarId; this.ticker = executor.scheduleAtFixedRate(this, 0L, 50L, TimeUnit.MILLISECONDS); } @Override protected void setupNewAnimation() { super.setupNewAnimation(); - BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, this.avatarEntity); + for (AvatarEntity avatar : this.listeners) subscribe(avatar); + this.dirtyBones.clear(); + } + + public void subscribe(AvatarEntity avatarEntity) { + this.listeners.add(avatarEntity); + BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, avatarEntity); for (String partKey : this.dirtyBones) { - updateBone(this.avatarEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); + updateBone(avatarEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); } - this.dirtyBones.clear(); } @Override public void run() { - // Check propertyManager - GeyserEntityPropertyManager propertyManager = this.avatarEntity.getPropertyManager(); - if (propertyManager == null) return; - - AnimationData data = new AnimationData(0, 1.0F, false); - tick(data); - - if (!isActive()) return; - setupAnim(data); - - // Animate via properties - for (String partKey : this.activeBones.keySet()) { - if (!this.bones.containsKey(partKey)) { - CommonData.LOGGER.debug("Unsupported bone: {}!", partKey); - continue; + try { + AnimationData data = new AnimationData(0, 1.0F, false); + tick(data); + + if (!isActive()) return; + setupAnim(data); + + // Animate via properties + for (String partKey : this.activeBones.keySet()) { + if (!this.bones.containsKey(partKey)) { + CommonData.LOGGER.debug("Unsupported bone: {}!", partKey); + continue; + } + + PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); + + for (AvatarEntity avatarEntity : this.listeners) { + // Check propertyManager + GeyserEntityPropertyManager propertyManager = avatarEntity.getPropertyManager(); + if (propertyManager == null) continue; + updateBone(propertyManager, partKey, bone); + } } - updateBone(propertyManager, partKey, get3DTransform(new PlayerAnimBone(partKey))); + // Flush + flushPropertiesImmediately(); + if (this.dirtyBones.isEmpty()) this.dirtyBones.addAll(this.activeBones.keySet()); + } catch (Throwable th) { + CommonData.LOGGER.warn("Failed to animate {}!", this.avatarId, th); } - - // Flush - flushPropertiesImmediately(); - if (this.dirtyBones.isEmpty()) this.dirtyBones.addAll(this.activeBones.keySet()); } @Override @@ -176,14 +190,16 @@ public static void updateProperty(GeyserEntityPropertyManager propertyManage } private void flushPropertiesImmediately() { - GeyserEntityPropertyManager propertyManager = this.avatarEntity.getPropertyManager(); - if (propertyManager == null || !propertyManager.hasProperties()) return; - - SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.setRuntimeEntityId(this.avatarEntity.getGeyserId()); - propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); - propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); - this.avatarEntity.getSession().sendUpstreamPacketImmediately(packet); + for (AvatarEntity avatarEntity : this.listeners) { + GeyserEntityPropertyManager propertyManager = avatarEntity.getPropertyManager(); + if (propertyManager == null || !propertyManager.hasProperties()) continue; + + SetEntityDataPacket packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(avatarEntity.getGeyserId()); + propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); + propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); + avatarEntity.getSession().sendUpstreamPacketImmediately(packet); + } try { Thread.sleep(Duration.ofMillis(10)); @@ -204,7 +220,11 @@ public void stop() { } protected void internalStop() { - BedrockPacketsUtils.sendBobAnimation(this.avatarEntity); + for (AvatarEntity avatarEntity : this.listeners) BedrockPacketsUtils.sendBobAnimation(avatarEntity); + } + + public void unsubscribe(AvatarEntity avatarEntity) { + if (this.listeners.remove(avatarEntity)) BedrockPacketsUtils.sendBobAnimation(avatarEntity); } @Override @@ -216,7 +236,7 @@ public void close() { * A small hack that allows us to get all registered bones. */ public static Collection getRegisteredBones() { - try (GeyserAnimationController controller = new GeyserAnimationController(null)) { + try (GeyserAnimationController controller = new GeyserAnimationController(UUID.randomUUID())) { Set bones = new HashSet<>(controller.bones.keySet()); for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); return Collections.unmodifiableCollection(bones); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 195da7bb3..ac534ae73 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -10,21 +10,17 @@ import io.github.kosmx.emotes.common.network.objects.NetData; import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; -import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.entity.type.player.AvatarEntity; -import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; -import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.ControllerHolder; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; import java.io.IOException; import java.util.*; @@ -35,8 +31,6 @@ public class GeyserNetworkInstance extends AbstractNetworkInstance { private final UUIDMap animations = new UUIDMap<>(); private final GeyserConnection session; - private final Map controllers = new WeakHashMap<>(); - private UUID currentEmote; private ConnectionType connectionType = ConnectionType.NONE; @@ -97,21 +91,15 @@ private void handleNetData(NetData data) { switch (Objects.requireNonNull(data.purpose)) { case STREAM: assert data.emoteData != null; - PlayerEntity playerEntity = getPlayerFromUUID(data.player); + AvatarEntity avatarEntity = getAvatarFromUUID(data.player); EventResult result = ClientEmoteEvents.EMOTE_VERIFICATION.invoker().verify(data.emoteData, data.player); if (result == EventResult.FAIL) break; - if (playerEntity != null) { + if (avatarEntity != null) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); - - //playerEntity.emotecraft$playEmote(data.emoteData, data.tick, data.isForced); - // this.session.entities().showEmote(playerEntity, "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate - - this.controllers.computeIfAbsent(playerEntity, GeyserAnimationController::new) - .triggerAnimation(data.emoteData, data.tick); - - if (isMainPlayer(playerEntity)) { + ControllerHolder.get(avatarEntity).triggerAnimation(data.emoteData, data.tick); + if (isMainAvatar(avatarEntity)) { this.currentEmote = data.emoteData.get(); } } /*else { @@ -121,13 +109,12 @@ private void handleNetData(NetData data) { case STOP: assert data.stopEmoteID != null; - PlayerEntity player = getPlayerFromUUID(data.player); - - if (player != null) { - ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(data.stopEmoteID, player.getUuid()); - stopEmote(player); + AvatarEntity avatar = getAvatarFromUUID(data.player); - if (isMainPlayer(player) && !data.isForced) { + if (avatar != null) { + ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(data.stopEmoteID, avatar.getUuid()); + stopEmote(avatar); + if (isMainAvatar(avatar) && !data.isForced) { sendChatMessage("emotecraft.blockedEmote"); } } else { @@ -149,7 +136,7 @@ private void handleNetData(NetData data) { } } - public void showForm() { + /*public void showForm() { SimpleForm.Builder builder = SimpleForm.builder() .translator(EmotecraftLocale::getLocaleString, this.session.locale()) .title(CommonData.MOD_NAME); @@ -168,20 +155,17 @@ public void showForm() { if (animation != null) playEmote(animation, true); }).build(); this.session.sendForm(simpleForm); - } + }*/ public void stopEmote() { - stopEmote(this.session.entities().playerEntity()); + stopEmote((AvatarEntity) this.session.entities().playerEntity()); } - public void stopEmote(GeyserPlayerEntity player) { - if (player instanceof AvatarEntity entity && this.controllers.containsKey(entity)) { - this.controllers.get(entity).stop(); - } - - this.session.entities().showEmote(player, ""); + public void stopEmote(AvatarEntity avatarEntity) { + ControllerHolder.get(avatarEntity).stop(); + GeyserEntityUtils.showEmote(avatarEntity, ""); - if (isMainPlayer(player) && this.currentEmote != null) { + if (isMainAvatar(avatarEntity) && this.currentEmote != null) { try { sendMessage(new EmotePacket.Builder().configureToSendStop(this.currentEmote), null); } catch (IOException e) { @@ -209,15 +193,12 @@ public void playEmote(Animation animation, boolean local) { } } - public PlayerEntity getPlayerFromUUID(UUID uuid) { - if (this.session.javaUuid().equals(uuid)) { - return (PlayerEntity) this.session.entities().playerEntity(); - } - return ((GeyserSession) this.session).getEntityCache().getPlayerEntity(uuid); + public AvatarEntity getAvatarFromUUID(UUID uuid) { + return GeyserEntityUtils.getAvatarByUUID((GeyserSession) this.session, uuid); } - public boolean isMainPlayer(GeyserPlayerEntity geyserPlayer) { - return geyserPlayer instanceof PlayerEntity player && this.session.javaUuid().equals(player.getUuid()); + public boolean isMainAvatar(AvatarEntity avatarEntity) { + return ((AvatarEntity) this.session.entities().playerEntity()).getUuid().equals(avatarEntity.getUuid()); } @Override @@ -266,9 +247,5 @@ public void disconnect() { this.connectionType = ConnectionType.NONE; this.animations.clear(); this.versions.clear(); - for (GeyserAnimationController controller : this.controllers.values()) { - controller.close(); - } - this.controllers.clear(); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java new file mode 100644 index 000000000..a3a9126f8 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java @@ -0,0 +1,38 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; + +import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.player.AvatarEntity; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.UUID; + +public class GeyserEntityUtils { + public static @Nullable AvatarEntity getAvatarByUUID(GeyserSession session, UUID uuid) { + if (session.entities().playerEntity() instanceof AvatarEntity player && player.getUuid().equals(uuid)) { + return player; + } + + PlayerEntity player = session.getEntityCache().getPlayerEntity(uuid); + if (player != null) return player; // Fast + + for (Entity entity : session.getEntityCache().getEntities().values()) { + if (entity instanceof AvatarEntity avatar && avatar.getUuid().equals(uuid)) { + return avatar; + } + } + return null; + } + + public static void showEmote(@NonNull AvatarEntity emoter, @NonNull String emoteId) { + EmotePacket packet = new EmotePacket(); + packet.setRuntimeEntityId(emoter.getGeyserId()); + packet.setXuid(""); + packet.setPlatformId(""); // BDS sends empty + packet.setEmoteId(emoteId); + emoter.getSession().sendUpstreamPacket(packet); + } +} From b749d022371dfe54755542295c0bba24066a2288 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 05:19:44 +0700 Subject: [PATCH 62/70] Fix ticking --- .../geyser/animator/ControllerHolder.java | 44 +++++++++++++-- .../animator/GeyserAnimationController.java | 54 ++++++++----------- .../geyser/handler/GeyserNetworkInstance.java | 4 +- .../geyser/utils/GeyserEntityUtils.java | 4 ++ 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java index 7a42825c6..7f9e56500 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java @@ -1,16 +1,50 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import io.github.kosmx.emotes.common.CommonData; import org.geysermc.geyser.entity.type.player.AvatarEntity; import java.util.Map; import java.util.UUID; +import java.util.concurrent.*; -public class ControllerHolder { - private static final Map CONTROLLERS = new Object2ObjectOpenHashMap<>(); +public final class ControllerHolder { + private static final ThreadFactory THREAD_FACTORY = Thread.ofVirtual() + .name("emotecraft-animating-") + .uncaughtExceptionHandler((t, e) -> CommonData.LOGGER.warn("Failed to animate!", e)) + .factory(); - public static GeyserAnimationController get(AvatarEntity entity) { - GeyserAnimationController controller = CONTROLLERS.computeIfAbsent(entity.getUuid(), GeyserAnimationController::new); + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(THREAD_FACTORY); + private static final ExecutorService CHILD_EXECUTOR = Executors.newCachedThreadPool(THREAD_FACTORY); + + public static final ControllerHolder INSTANCE = new ControllerHolder(); + + private final Map controllers = new ConcurrentHashMap<>(); + private final Map> inFlight = new ConcurrentHashMap<>(); + + private ControllerHolder() { + EXECUTOR.scheduleAtFixedRate(this::run, 0L, 50L, TimeUnit.MILLISECONDS); + } + + private void run() { + if (this.controllers.isEmpty()) return; + for (Map.Entry e : this.controllers.entrySet()) { + UUID uuid = e.getKey(); + + CompletableFuture current = this.inFlight.get(uuid); + if (current != null && !current.isDone()) continue; + + CompletableFuture cf = CompletableFuture.supplyAsync(e.getValue()::handleFrame, CHILD_EXECUTOR); + inFlight.put(uuid, cf); + + cf.whenComplete((remove, ex) -> { + inFlight.remove(uuid, cf); + if (remove) controllers.remove(uuid); + }); + } + } + + public GeyserAnimationController get(AvatarEntity entity) { + GeyserAnimationController controller = this.controllers.computeIfAbsent(entity.getUuid(), GeyserAnimationController::new); controller.subscribe(entity); return controller; } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 1c863d033..d5a859c1b 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -1,5 +1,6 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; +import com.google.common.collect.Sets; import com.zigythebird.playeranimcore.animation.AnimationData; import com.zigythebird.playeranimcore.animation.HumanoidAnimationController; import com.zigythebird.playeranimcore.bones.PlayerAnimBone; @@ -21,38 +22,25 @@ import org.geysermc.geyser.entity.type.player.AvatarEntity; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; -import java.io.Closeable; import java.time.Duration; import java.util.*; import java.util.concurrent.*; import static org.redlance.dima_dencep.mods.emotecraft.geyser.animator.PackedProperty.pack; -public class GeyserAnimationController extends HumanoidAnimationController implements Runnable, Closeable { - private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(100, Thread.ofVirtual() - .name("emotecraft-animating-") - .uncaughtExceptionHandler((t, e) -> CommonData.LOGGER.warn("Failed to animate!", e)) - .factory() - ); - +public class GeyserAnimationController extends HumanoidAnimationController { private final Set lastUsedProperties = new HashSet<>(1); - private final Set listeners = new HashSet<>(); + private final Set listeners = Sets.newConcurrentHashSet(); + private final Set dirtyBones = new HashSet<>(); protected final UUID avatarId; - protected final Future ticker; - - private final Set dirtyBones = new HashSet<>(); protected GeyserAnimationController(UUID avatarId) { - this(avatarId, EXECUTOR); - } - - protected GeyserAnimationController(UUID avatarId, ScheduledExecutorService executor) { super((controller, state, animationSetter) -> PlayState.STOP, MolangLoader::createNewEngine); this.avatarId = avatarId; - this.ticker = executor.scheduleAtFixedRate(this, 0L, 50L, TimeUnit.MILLISECONDS); } @Override @@ -70,13 +58,12 @@ public void subscribe(AvatarEntity avatarEntity) { } } - @Override - public void run() { + public boolean handleFrame() { try { AnimationData data = new AnimationData(0, 1.0F, false); tick(data); - if (!isActive()) return; + if (!isActive()) return true; setupAnim(data); // Animate via properties @@ -89,6 +76,11 @@ public void run() { PlayerAnimBone bone = get3DTransform(new PlayerAnimBone(partKey)); for (AvatarEntity avatarEntity : this.listeners) { + if (GeyserEntityUtils.unsubscribedFromEntity(avatarEntity)) { + unsubscribe(avatarEntity); + continue; + } + // Check propertyManager GeyserEntityPropertyManager propertyManager = avatarEntity.getPropertyManager(); if (propertyManager == null) continue; @@ -102,6 +94,9 @@ public void run() { } catch (Throwable th) { CommonData.LOGGER.warn("Failed to animate {}!", this.avatarId, th); } + + this.listeners.removeIf(GeyserEntityUtils::unsubscribedFromEntity); + return this.listeners.isEmpty(); } @Override @@ -203,7 +198,10 @@ private void flushPropertiesImmediately() { try { Thread.sleep(Duration.ofMillis(10)); - } catch (InterruptedException ignored) {} + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return; + } this.lastUsedProperties.clear(); } @@ -227,19 +225,13 @@ public void unsubscribe(AvatarEntity avatarEntity) { if (this.listeners.remove(avatarEntity)) BedrockPacketsUtils.sendBobAnimation(avatarEntity); } - @Override - public void close() { - this.ticker.cancel(true); - } - /** * A small hack that allows us to get all registered bones. */ public static Collection getRegisteredBones() { - try (GeyserAnimationController controller = new GeyserAnimationController(UUID.randomUUID())) { - Set bones = new HashSet<>(controller.bones.keySet()); - for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); - return Collections.unmodifiableCollection(bones); - } + GeyserAnimationController controller = new GeyserAnimationController(UUID.randomUUID()); + Set bones = new HashSet<>(controller.bones.keySet()); + for (String bendable : BendingGeometry.BENDABLE_BONES) bones.add(bendable + BendingGeometry.BEND_SUFFIX); + return Collections.unmodifiableCollection(bones); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index ac534ae73..72d08b001 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -98,7 +98,7 @@ private void handleNetData(NetData data) { if (avatarEntity != null) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); - ControllerHolder.get(avatarEntity).triggerAnimation(data.emoteData, data.tick); + ControllerHolder.INSTANCE.get(avatarEntity).triggerAnimation(data.emoteData, data.tick); if (isMainAvatar(avatarEntity)) { this.currentEmote = data.emoteData.get(); } @@ -162,7 +162,7 @@ public void stopEmote() { } public void stopEmote(AvatarEntity avatarEntity) { - ControllerHolder.get(avatarEntity).stop(); + ControllerHolder.INSTANCE.get(avatarEntity).stop(); GeyserEntityUtils.showEmote(avatarEntity, ""); if (isMainAvatar(avatarEntity) && this.currentEmote != null) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java index a3a9126f8..8f6c2ce37 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java @@ -35,4 +35,8 @@ public static void showEmote(@NonNull AvatarEntity emoter, @NonNull String emote packet.setEmoteId(emoteId); emoter.getSession().sendUpstreamPacket(packet); } + + public static boolean unsubscribedFromEntity(AvatarEntity entity) { + return entity.getSession().getEntityCache().getEntityByGeyserId(entity.getGeyserId()) == null; + } } From 65b339b83a966d5488c24f3f78397dfb9f7816ce Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 05:58:17 +0700 Subject: [PATCH 63/70] fixes --- .../mods/emotecraft/geyser/EmotecraftExt.java | 28 ++++++++++------ .../geyser/handler/GeyserNetworkInstance.java | 32 ++++++++++++------- .../geyser/utils/BedrockEmoteLoader.java | 7 +++- .../geyser/utils/GeyserEntityUtils.java | 8 ++++- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 66d4dd0a6..a758b9486 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -11,14 +11,17 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.extension.Extension; @@ -156,21 +159,26 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { if (instance != null) instance.disconnect(); } - /*@Subscribe + @Override + public @NonNull String rootCommand() { + return "emotes-geyser"; + } + + @Subscribe public void onDefineCommands(GeyserDefineCommandsEvent event) { event.register(Command.builder(this) - .name(rootCommand()) + .name("list") .bedrockOnly(true) .source(GeyserConnection.class) - .aliases(List.of("emotes", "form")) - .description("Emotecraft command") + .aliases(Collections.singletonList("emotes")) + .description("List of emotes") .playerOnly(true) .executor((source, cmd, args) -> - EmotecraftExt.INSTANCES.get((GeyserConnection) source).showForm() + EmotecraftExt.INSTANCES.get((GeyserConnection) source).showEmoteList() ) .build() ); - }*/ + } @Subscribe(postOrder = PostOrder.FIRST, ignoreCancelled = true) public void onEmote(ClientEmoteEvent event) { @@ -179,17 +187,17 @@ public void onEmote(ClientEmoteEvent event) { CompletableFuture animation = BedrockEmoteLoader.loadEmote(event.emoteId()); if (animation.isDone() && !animation.isCompletedExceptionally()) { - networkInstance.playEmote(animation.join(), false); - + networkInstance.playEmote(animation.join(), null); } else { networkInstance.stopEmote(); if (animation.isCompletedExceptionally()) { - networkInstance.sendChatMessage("emotecraft.cantconvert"); + networkInstance.sendChatMessage("emotecraft.blockedEmote"); + CommonData.LOGGER.warn("Failed to translate emote!", animation.exceptionNow()); } else { animation.thenAccept(emote -> networkInstance.playEmote( - emote, true + emote, event.emoteId() )); } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 72d08b001..24a85c0ff 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -10,16 +10,20 @@ import io.github.kosmx.emotes.common.network.objects.NetData; import io.github.kosmx.emotes.common.tools.MathHelper; import io.github.kosmx.emotes.common.tools.UUIDMap; +import io.github.kosmx.emotes.server.serializer.UniversalEmoteSerializer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.entity.type.player.AvatarEntity; +import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.ControllerHolder; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; import java.io.IOException; @@ -57,11 +61,14 @@ public void sendMessage(EmotePacket.Builder builder, @Nullable UUID target) thro @Override public void sendMessage(EmotePacket packet, @Nullable UUID target) { ByteBuf buf = Unpooled.buffer(); - packet.write(buf); - ((GeyserSession) this.session).sendDownstreamPacket(new ServerboundCustomPayloadPacket( - EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, MathHelper.readBytes(buf) - )); - buf.release(); + try { + packet.write(buf); + ((GeyserSession) this.session).sendDownstreamPacket(new ServerboundCustomPayloadPacket( + EmotecraftExt.EMOTECRAFT_EMOTE_TYPE, MathHelper.readBytes(buf) + )); + } finally { + buf.release(); + } } @Override @@ -136,7 +143,7 @@ private void handleNetData(NetData data) { } } - /*public void showForm() { + public void showEmoteList() { SimpleForm.Builder builder = SimpleForm.builder() .translator(EmotecraftLocale::getLocaleString, this.session.locale()) .title(CommonData.MOD_NAME); @@ -152,10 +159,10 @@ private void handleNetData(NetData data) { SimpleForm simpleForm = builder.validResultHandler((form, response) -> { UUID emoteId = FormUtils.extractAnimationFromButton(response.clickedButton()); Animation animation = this.animations.getOrDefault(emoteId, UniversalEmoteSerializer.getEmote(emoteId)); - if (animation != null) playEmote(animation, true); + if (animation != null) playEmote(animation, null); }).build(); this.session.sendForm(simpleForm); - }*/ + } public void stopEmote() { stopEmote((AvatarEntity) this.session.entities().playerEntity()); @@ -180,12 +187,15 @@ public void sendChatMessage(String key) { this.session.sendMessage(EmotecraftLocale.getLocaleString(key, this.session.locale())); } - public void playEmote(Animation animation, boolean local) { + public void playEmote(Animation animation, String bedrockId) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(animation, 0, this.session.javaUuid()); try { sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); - if (local) { - this.session.entities().showEmote(this.session.entities().playerEntity(), "4c8ae710-df2e-47cd-814d-cc7bf21a3d67"); // TODO translate + PlayerEntity playerEntity = (PlayerEntity) this.session.entities().playerEntity(); + if (bedrockId != null) { + this.session.entities().showEmote(playerEntity, bedrockId); + } else { + ControllerHolder.INSTANCE.get(playerEntity).triggerAnimation(animation, 0); } this.currentEmote = animation.get(); } catch (Throwable th) { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index e6b9c1943..c501a17e4 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class BedrockEmoteLoader extends CacheLoader> { @@ -79,6 +80,10 @@ public static void preloadEmotes(List emotes) { } public static CompletableFuture loadEmote(String emoteId) { - return BedrockEmoteLoader.BEDROCK_KEYFRAMES.getUnchecked(emoteId); + try { + return BedrockEmoteLoader.BEDROCK_KEYFRAMES.get(emoteId); + } catch (Throwable th) { + return CompletableFuture.failedFuture(th); + } } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java index 8f6c2ce37..7573f491d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java @@ -37,6 +37,12 @@ public static void showEmote(@NonNull AvatarEntity emoter, @NonNull String emote } public static boolean unsubscribedFromEntity(AvatarEntity entity) { - return entity.getSession().getEntityCache().getEntityByGeyserId(entity.getGeyserId()) == null; + GeyserSession session = entity.getSession(); + + if (session.entities().playerEntity() == entity) { + return session.isClosed(); + } else { + return session.getEntityCache().getEntityByGeyserId(entity.getGeyserId()) == null; + } } } From b9c3ca45ece51dc5111e0f38b1cd49a6679fe6ca Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 06:04:07 +0700 Subject: [PATCH 64/70] Fix stop --- .../geyser/handler/GeyserNetworkInstance.java | 3 ++- .../emotecraft/geyser/utils/BedrockEmoteLoader.java | 3 ++- .../emotecraft/geyser/utils/GeyserEntityUtils.java | 11 ----------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index 24a85c0ff..abd8968bc 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.ControllerHolder; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; @@ -170,7 +171,7 @@ public void stopEmote() { public void stopEmote(AvatarEntity avatarEntity) { ControllerHolder.INSTANCE.get(avatarEntity).stop(); - GeyserEntityUtils.showEmote(avatarEntity, ""); + BedrockPacketsUtils.sendBobAnimation(avatarEntity); if (isMainAvatar(avatarEntity) && this.currentEmote != null) { try { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index c501a17e4..d8a75c879 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -9,9 +9,11 @@ import com.zigythebird.playeranimcore.util.JsonUtil; import io.github.kosmx.emotes.common.CommonData; import org.jetbrains.annotations.NotNull; +import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.io.*; +import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -19,7 +21,6 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class BedrockEmoteLoader extends CacheLoader> { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java index 7573f491d..b0aed604e 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java @@ -1,11 +1,9 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser.utils; -import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.AvatarEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.UUID; @@ -27,15 +25,6 @@ public class GeyserEntityUtils { return null; } - public static void showEmote(@NonNull AvatarEntity emoter, @NonNull String emoteId) { - EmotePacket packet = new EmotePacket(); - packet.setRuntimeEntityId(emoter.getGeyserId()); - packet.setXuid(""); - packet.setPlatformId(""); // BDS sends empty - packet.setEmoteId(emoteId); - emoter.getSession().sendUpstreamPacket(packet); - } - public static boolean unsubscribedFromEntity(AvatarEntity entity) { GeyserSession session = entity.getSession(); From 850566923ebefd117ba4c2fe35f05bc9b4d50852 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 06:51:44 +0700 Subject: [PATCH 65/70] Debug --- .../emotecraft/geyser/GeyserBootstrap.java | 18 ++++++++++++++++++ .../{ => geometry}/BendingGeometry.java | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) rename geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/{ => geometry}/BendingGeometry.java (98%) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java index a1a2a19c7..5e46f37c9 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/GeyserBootstrap.java @@ -1,15 +1,19 @@ package org.redlance.dima_dencep.mods.emotecraft.geyser; +import io.github.kosmx.emotes.common.CommonData; import javassist.ByteArrayClassPath; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import org.geysermc.geyser.extension.GeyserExtensionContainer; import org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ReflectHacks; import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.Objects; import java.util.function.UnaryOperator; @@ -17,8 +21,22 @@ * Used to run Emotecraft in a dev environment. */ public class GeyserBootstrap { + private static final Class LEVEL_CLASS = ReflectHacks.uncheck(() -> Class.forName("org.apache.logging.log4j.Level")); + private static final MethodHandle SET_LEVEL = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findStatic( + Class.forName("org.apache.logging.log4j.core.config.Configurator"), + "setLevel", MethodType.methodType(void.class, String.class, LEVEL_CLASS) + )); + private static final MethodHandle DEBUG_LEVEL = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findStaticGetter( + LEVEL_CLASS, "DEBUG", LEVEL_CLASS + )); + static { System.setProperty("java.awt.headless", "true"); + try { + SET_LEVEL.invoke(CommonData.LOGGER.getName(), DEBUG_LEVEL.invoke()); + } catch (Throwable e) { + throw new RuntimeException(e); + } } public static void main(String[] args) throws ReflectiveOperationException, IOException { diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java similarity index 98% rename from geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java rename to geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java index e4e0b8b58..f39f98f99 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/BendingGeometry.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java @@ -39,7 +39,7 @@ public static SkinGeometry addBoneBends(SkinGeometry geometry) { private static void addBoneBends(JsonObject geometry) { String identifier = geometry.getAsJsonObject("description").get("identifier").getAsString(); - CommonData.LOGGER.info("Patching '{}' for bends...", identifier); + CommonData.LOGGER.debug("Patching '{}' for bends...", identifier); JsonArray bones = geometry.getAsJsonArray("bones"); for (JsonElement element : new ArrayList<>(bones.asList())) { @@ -66,7 +66,7 @@ private static void addBoneBendsToBone(JsonArray bones, JsonObject bone) { JsonObject firstCube = boneObj.has("cubes") ? boneObj.getAsJsonArray("cubes").get(0).getAsJsonObject() : new JsonObject(); if (firstCube.has("inflate") && boneSize == firstCube.getAsJsonArray("size").get(1).getAsInt()) { - CommonData.LOGGER.info("Second layer detected! {}", boneObj); + CommonData.LOGGER.debug("Second layer detected! {}", boneObj); JsonObject secondBoneSecondLayer = makeCubeBendable(boneObj, false); boneObj.add("parent", bone.get("name")); From c03cb8c8fc1e09a009b6f6aa782834f95ce4db1e Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 06:51:57 +0700 Subject: [PATCH 66/70] Fix --- .../mods/emotecraft/geyser/utils/GeyserEntityUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java index b0aed604e..ac9e87775 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/GeyserEntityUtils.java @@ -27,9 +27,10 @@ public class GeyserEntityUtils { public static boolean unsubscribedFromEntity(AvatarEntity entity) { GeyserSession session = entity.getSession(); + if (session.isClosed()) return true; if (session.entities().playerEntity() == entity) { - return session.isClosed(); + return false; } else { return session.getEntityCache().getEntityByGeyserId(entity.getGeyserId()) == null; } From efb6c17e84cb662fe9dbafb53c102d5975a7ba9b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 06:52:09 +0700 Subject: [PATCH 67/70] Bends --- .../animator/GeyserAnimationController.java | 19 ++++- .../animator/geometry/BendingGeometry.java | 2 +- .../animator/geometry/GeometryChanger.java | 85 +++++++++++++++++++ .../utils/resourcepack/EmoteResourcePack.java | 2 +- 4 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/GeometryChanger.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index d5a859c1b..22ede9551 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -21,13 +21,14 @@ import org.geysermc.geyser.entity.properties.type.PropertyType; import org.geysermc.geyser.entity.type.player.AvatarEntity; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.geometry.BendingGeometry; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.geometry.GeometryChanger; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.resourcepack.EmoteResourcePack; import java.time.Duration; import java.util.*; -import java.util.concurrent.*; import static org.redlance.dima_dencep.mods.emotecraft.geyser.animator.PackedProperty.pack; @@ -51,19 +52,26 @@ protected void setupNewAnimation() { } public void subscribe(AvatarEntity avatarEntity) { - this.listeners.add(avatarEntity); + if (this.listeners.add(avatarEntity)) { + GeometryChanger.changeGeometryToBending(avatarEntity).join(); + try { + Thread.sleep(Duration.ofMillis(10)); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } BedrockPacketsUtils.sendInstantAnimation(EmoteResourcePack.ANIMATION_NAME, avatarEntity); for (String partKey : this.dirtyBones) { updateBone(avatarEntity.getPropertyManager(), partKey, new PlayerAnimBone(partKey)); } } - public boolean handleFrame() { + protected void handleFrameInternal() { try { AnimationData data = new AnimationData(0, 1.0F, false); tick(data); - if (!isActive()) return true; + if (!isActive()) return; setupAnim(data); // Animate via properties @@ -94,7 +102,10 @@ public boolean handleFrame() { } catch (Throwable th) { CommonData.LOGGER.warn("Failed to animate {}!", this.avatarId, th); } + } + protected boolean handleFrame() { + handleFrameInternal(); this.listeners.removeIf(GeyserEntityUtils::unsubscribedFromEntity); return this.listeners.isEmpty(); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java index f39f98f99..6c479587d 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/BendingGeometry.java @@ -1,4 +1,4 @@ -package org.redlance.dima_dencep.mods.emotecraft.geyser.animator; +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator.geometry; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/GeometryChanger.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/GeometryChanger.java new file mode 100644 index 000000000..9f910bdf6 --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/geometry/GeometryChanger.java @@ -0,0 +1,85 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.animator.geometry; + +import org.cloudburstmc.protocol.bedrock.data.skin.SerializedSkin; +import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerSkinPacket; +import org.geysermc.geyser.api.skin.Cape; +import org.geysermc.geyser.api.skin.Skin; +import org.geysermc.geyser.api.skin.SkinData; +import org.geysermc.geyser.api.skin.SkinGeometry; +import org.geysermc.geyser.entity.type.player.AvatarEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.skin.SkinManager; +import org.geysermc.geyser.skin.SkinProvider; +import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ReflectHacks; + +import java.awt.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.concurrent.CompletableFuture; + +public class GeometryChanger { + private static final MethodHandle REQUEST_SKIN_DATA = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findStatic( + SkinProvider.class, "requestSkinData", MethodType.methodType(CompletableFuture.class, AvatarEntity.class, GeyserSession.class) + )); + private static final MethodHandle GET_SKIN = ReflectHacks.uncheck(() -> ReflectHacks.TRUSTED_LOOKUP.findStatic( + SkinManager.class, "getSkin", MethodType.methodType(SerializedSkin.class, GeyserSession.class, String.class, Skin.class, Cape.class, SkinGeometry.class) + )); + + public static CompletableFuture changeGeometryToBending(AvatarEntity entity) { + return requestSkinData(entity, entity.getSession()) + .thenApply(skinData -> { + SkinData bendable = new SkinData(skinData.skin(), skinData.cape(), + BendingGeometry.addBoneBends(skinData.geometry()) + ); + sendSkinPacket(entity.getSession(), entity, bendable); + return bendable; + }); + } + + public static void sendSkinPacket(GeyserSession session, AvatarEntity entity, SkinData skinData) { + Skin skin = skinData.skin(); + Cape cape = skinData.cape(); + SkinGeometry geometry = skinData.geometry(); + Color color = session.getWaypointCache().getWaypointColor(entity.getUuid()).orElse(Color.WHITE); + + if (entity.getUuid().equals(session.getPlayerEntity().getUuid())) { + PlayerListPacket.Entry updatedEntry = SkinManager.buildEntryManually( + session, + entity.getUuid(), + entity.getUsername(), + entity.getGeyserId(), + skin, + cape, + geometry, + color + ); + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setAction(PlayerListPacket.Action.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.sendUpstreamPacketImmediately(playerAddPacket); + } else { + PlayerSkinPacket packet = new PlayerSkinPacket(); + packet.setUuid(entity.getUuid()); + packet.setOldSkinName(""); + packet.setNewSkinName(skin.textureUrl()); + try { + packet.setSkin((SerializedSkin) GET_SKIN.invoke(session, skin.textureUrl(), skin, cape, geometry)); + } catch (Throwable e) { + throw new RuntimeException(e); + } + packet.setTrustedSkin(true); + session.sendUpstreamPacketImmediately(packet); + } + } + + @SuppressWarnings("unchecked") + public static CompletableFuture requestSkinData(AvatarEntity entity, GeyserSession session) { + try { + return (CompletableFuture) REQUEST_SKIN_DATA.invoke(entity, session); + } catch (Throwable th) { + return CompletableFuture.failedFuture(th); + } + } +} diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java index 483076c8d..56246bf8a 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/resourcepack/EmoteResourcePack.java @@ -21,7 +21,7 @@ import org.geysermc.geyser.api.util.Identifier; import org.geysermc.geyser.pack.GeyserResourcePack; import org.geysermc.geyser.pack.GeyserResourcePackManifest; -import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.BendingGeometry; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.geometry.BendingGeometry; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.GeyserAnimationController; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.PackedProperty; From e1baaafe0a7b5cef0da8757e04a56d017abe0d3b Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 07:16:43 +0700 Subject: [PATCH 68/70] Fix events --- .../mods/emotecraft/geyser/EmotecraftExt.java | 8 ++- .../geyser/handler/GeyserNetworkInstance.java | 55 +++++++++++-------- .../geyser/utils/BedrockEmoteLoader.java | 1 + 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index a758b9486..6a55f5edc 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -96,7 +96,7 @@ public void onPostInitialize(GeyserPostInitializeEvent event) { GeyserNetworkInstance networkInstance = EmotecraftExt.INSTANCES.get(session); if (networkInstance != null && networkInstance.isPlaying() && session.isSneaking()) { CommonData.LOGGER.debug("Stopping animation {}", session.name()); - networkInstance.stopEmote(session.getPlayerEntity()); + networkInstance.stopEmote(session.getPlayerEntity(), null); } return true; }); @@ -189,7 +189,11 @@ public void onEmote(ClientEmoteEvent event) { if (animation.isDone() && !animation.isCompletedExceptionally()) { networkInstance.playEmote(animation.join(), null); } else { - networkInstance.stopEmote(); + try { + networkInstance.stopEmote(UUID.fromString(event.emoteId())); + } catch (IllegalArgumentException ex) { // Not uuid + networkInstance.stopEmote(null); + } if (animation.isCompletedExceptionally()) { networkInstance.sendChatMessage("emotecraft.blockedEmote"); diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java index abd8968bc..71c6c3d26 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/handler/GeyserNetworkInstance.java @@ -22,10 +22,7 @@ import org.jetbrains.annotations.Nullable; import org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt; import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.ControllerHolder; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.BedrockPacketsUtils; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.EmotecraftLocale; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.FormUtils; -import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.GeyserEntityUtils; +import org.redlance.dima_dencep.mods.emotecraft.geyser.utils.*; import java.io.IOException; import java.util.*; @@ -105,11 +102,7 @@ private void handleNetData(NetData data) { if (result == EventResult.FAIL) break; if (avatarEntity != null) { - ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(data.emoteData, data.tick, data.player); - ControllerHolder.INSTANCE.get(avatarEntity).triggerAnimation(data.emoteData, data.tick); - if (isMainAvatar(avatarEntity)) { - this.currentEmote = data.emoteData.get(); - } + playEmote(avatarEntity, data.emoteData, null); } /*else { // this.queue.put(data.player, new QueueEntry(data.emoteData, data.tick, ClientMethods.getCurrentTick())); }*/ @@ -120,8 +113,7 @@ private void handleNetData(NetData data) { AvatarEntity avatar = getAvatarFromUUID(data.player); if (avatar != null) { - ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(data.stopEmoteID, avatar.getUuid()); - stopEmote(avatar); + stopEmote(avatar, data.stopEmoteID); if (isMainAvatar(avatar) && !data.isForced) { sendChatMessage("emotecraft.blockedEmote"); } @@ -165,15 +157,23 @@ public void showEmoteList() { this.session.sendForm(simpleForm); } - public void stopEmote() { - stopEmote((AvatarEntity) this.session.entities().playerEntity()); + public void stopEmote(@Nullable UUID stopEmoteID) { + stopEmote((AvatarEntity) this.session.entities().playerEntity(), stopEmoteID); } - public void stopEmote(AvatarEntity avatarEntity) { + public void stopEmote(AvatarEntity avatarEntity, @Nullable UUID stopEmoteID) { + if (stopEmoteID != null) { // TODO check + ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(stopEmoteID, avatarEntity.getUuid()); + } + ControllerHolder.INSTANCE.get(avatarEntity).stop(); BedrockPacketsUtils.sendBobAnimation(avatarEntity); if (isMainAvatar(avatarEntity) && this.currentEmote != null) { + if (stopEmoteID == null) { // TODO check + ClientEmoteEvents.EMOTE_STOP.invoker().onEmoteStop(this.currentEmote, avatarEntity.getUuid()); + } + try { sendMessage(new EmotePacket.Builder().configureToSendStop(this.currentEmote), null); } catch (IOException e) { @@ -189,18 +189,25 @@ public void sendChatMessage(String key) { } public void playEmote(Animation animation, String bedrockId) { + playEmote((AvatarEntity) this.session.entities().playerEntity(), animation, bedrockId); + } + + public void playEmote(AvatarEntity avatarEntity, Animation animation, String bedrockId) { ClientEmoteEvents.EMOTE_PLAY.invoker().onEmotePlay(animation, 0, this.session.javaUuid()); - try { - sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); - PlayerEntity playerEntity = (PlayerEntity) this.session.entities().playerEntity(); - if (bedrockId != null) { - this.session.entities().showEmote(playerEntity, bedrockId); - } else { - ControllerHolder.INSTANCE.get(playerEntity).triggerAnimation(animation, 0); + + if (isMainAvatar(avatarEntity)) { + try { + sendMessage(new EmotePacket.Builder().configureToStreamEmote(animation), null); + } catch (IOException e) { + throw new RuntimeException(e); } this.currentEmote = animation.get(); - } catch (Throwable th) { - throw new RuntimeException(th); + } + + if (avatarEntity instanceof PlayerEntity player && bedrockId != null) { + this.session.entities().showEmote(player, bedrockId); + } else { + ControllerHolder.INSTANCE.get(avatarEntity).triggerAnimation(animation, 0); } } @@ -252,7 +259,7 @@ public boolean isPlaying() { @Override public void disconnect() { if (this.currentEmote != null) { - stopEmote(); + stopEmote(this.currentEmote); this.currentEmote = null; } this.connectionType = ConnectionType.NONE; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index d8a75c879..fb6bd34b8 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -39,6 +39,7 @@ public class BedrockEmoteLoader extends CacheLoader { From a0e2a7b024a7ec9d8f528c38a862f774a81ebcb2 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 07:24:30 +0700 Subject: [PATCH 69/70] add bend fix command --- .../mods/emotecraft/geyser/EmotecraftExt.java | 17 +++++++++++++---- .../geyser/animator/ControllerHolder.java | 10 ++++++++++ .../animator/GeyserAnimationController.java | 2 +- .../geyser/commands/FixGeometryCommand.java | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/commands/FixGeometryCommand.java diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java index 6a55f5edc..32d2b5f28 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/EmotecraftExt.java @@ -31,6 +31,7 @@ import org.geysermc.mcprotocollib.protocol.data.ProtocolState; import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; +import org.redlance.dima_dencep.mods.emotecraft.geyser.commands.FixGeometryCommand; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.GayserHacks; import org.redlance.dima_dencep.mods.emotecraft.geyser.fuckery.ReflectHacks; import org.redlance.dima_dencep.mods.emotecraft.geyser.handler.ConnectionType; @@ -166,16 +167,24 @@ public void onSessionDisconnect(SessionDisconnectEvent event) { @Subscribe public void onDefineCommands(GeyserDefineCommandsEvent event) { - event.register(Command.builder(this) + event.register(Command.builder(this) .name("list") .bedrockOnly(true) .source(GeyserConnection.class) .aliases(Collections.singletonList("emotes")) .description("List of emotes") .playerOnly(true) - .executor((source, cmd, args) -> - EmotecraftExt.INSTANCES.get((GeyserConnection) source).showEmoteList() - ) + .executor((source, cmd, args) -> EmotecraftExt.INSTANCES.get(source).showEmoteList()) + .build() + ); + event.register(Command.builder(this) + .name("fix-geometry") + .bedrockOnly(true) + .source(GeyserConnection.class) + .aliases(Collections.singletonList("fix-bends")) + .description("Fix geometry") + .playerOnly(true) + .executor(new FixGeometryCommand()) .build() ); } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java index 7f9e56500..ee14ab971 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/ControllerHolder.java @@ -48,4 +48,14 @@ public GeyserAnimationController get(AvatarEntity entity) { controller.subscribe(entity); return controller; } + + public void resubscribe(AvatarEntity entity) { + for (GeyserAnimationController controller : this.controllers.values()) { + if (controller.listeners.contains(entity)) { + controller.unsubscribe(entity); + controller.subscribe(entity); + controller.subscribe(entity); + } + } + } } diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java index 22ede9551..f88a8e1c3 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/animator/GeyserAnimationController.java @@ -34,7 +34,7 @@ public class GeyserAnimationController extends HumanoidAnimationController { private final Set lastUsedProperties = new HashSet<>(1); - private final Set listeners = Sets.newConcurrentHashSet(); + protected final Set listeners = Sets.newConcurrentHashSet(); private final Set dirtyBones = new HashSet<>(); protected final UUID avatarId; diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/commands/FixGeometryCommand.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/commands/FixGeometryCommand.java new file mode 100644 index 000000000..757b3ac1f --- /dev/null +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/commands/FixGeometryCommand.java @@ -0,0 +1,19 @@ +package org.redlance.dima_dencep.mods.emotecraft.geyser.commands; + +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandExecutor; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.player.AvatarEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.jspecify.annotations.NonNull; +import org.redlance.dima_dencep.mods.emotecraft.geyser.animator.ControllerHolder; + +public class FixGeometryCommand implements CommandExecutor { + @Override + public void execute(@NonNull GeyserConnection source, @NonNull Command command, @NonNull String[] args) { + for (Entity entity : ((GeyserSession) source).getEntityCache().getEntities().values()) { + if (entity instanceof AvatarEntity avatar) ControllerHolder.INSTANCE.resubscribe(avatar); + } + } +} From d6f4b65f34ed8e8a2956568496ea67a16b91a0e8 Mon Sep 17 00:00:00 2001 From: dima_dencep Date: Sun, 28 Dec 2025 08:16:17 +0700 Subject: [PATCH 70/70] api.redlance.org --- .../mods/emotecraft/geyser/utils/BedrockEmoteLoader.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java index fb6bd34b8..69e219c09 100644 --- a/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java +++ b/geyser/src/main/java/org/redlance/dima_dencep/mods/emotecraft/geyser/utils/BedrockEmoteLoader.java @@ -37,6 +37,9 @@ public class BedrockEmoteLoader extends CacheLoader load(@NotNull String emoteId) { HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://api.redlance.org/bugrock/v1/marketplace/get-emote-by-uuid")) + .header("user-agent", EmotecraftExt.getInstance().description().toString()) + .POST(HttpRequest.BodyPublishers.ofString(emoteId)) .build(); CommonData.LOGGER.debug("Sending request: {}", request);