diff --git a/core/build.gradle b/core/build.gradle index db816ae64..90efd393b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -16,6 +16,7 @@ dependencies { shadow(project(path: ":nms:shared", configuration: "apiElements")) shadow(project(path: ":nms:latest")) { transitive = false } + shadow(project(path: ":nms:v1_21_10")) { transitive = false } implementation("com.github.retrooper:packetevents-spigot:2.10.1") implementation("net.dmulloy2:ProtocolLib:5.4.0") { transitive = false } diff --git a/nms/latest/build.gradle b/nms/latest/build.gradle index 2ce8c3dae..85f35d747 100644 --- a/nms/latest/build.gradle +++ b/nms/latest/build.gradle @@ -3,6 +3,6 @@ plugins { } dependencies { - paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.11-rc2-R0.1-SNAPSHOT") implementation project(":nms:shared") } diff --git a/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileCodeLatest.java b/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileCodeLatest.java index f0f5e14b6..7971af410 100644 --- a/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileCodeLatest.java +++ b/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileCodeLatest.java @@ -22,6 +22,8 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.jetbrains.annotations.NotNull; + import net.kyori.adventure.text.Component; import io.papermc.paper.adventure.PaperAdventure; @@ -35,27 +37,27 @@ import net.minecraft.core.BlockPos; import net.minecraft.advancements.*; import net.minecraft.world.phys.Vec3; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.EntityType; import net.minecraft.network.protocol.game.*; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.item.PrimedTnt; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.core.particles.ColorParticleOption; -import net.minecraft.advancements.critereon.ImpossibleTrigger; +import net.minecraft.advancements.criterion.ImpossibleTrigger; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; public class VolatileCodeLatest extends VolatileCodeHandle { - private final ResourceLocation TOAST_KEY = ResourceLocation.fromNamespaceAndPath("magicspells", "toast_effect"); + private final Identifier TOAST_KEY = Identifier.fromNamespaceAndPath("magicspells", "toast_effect"); - private final EntityDataAccessor> DATA_EFFECT_PARTICLES; - private final EntityDataAccessor DATA_EFFECT_AMBIENCE_ID; - private final EntityDataAccessor DATA_SHARED_FLAGS_ID; + private final EntityDataAccessor<@NotNull List> DATA_EFFECT_PARTICLES; + private final EntityDataAccessor<@NotNull Boolean> DATA_EFFECT_AMBIENCE_ID; + private final EntityDataAccessor<@NotNull Byte> DATA_SHARED_FLAGS_ID; private final Method UPDATE_EFFECT_PARTICLES; @SuppressWarnings("unchecked") @@ -64,17 +66,17 @@ public VolatileCodeLatest(VolatileCodeHelper helper) throws Exception { Field dataSharedFlagsIdField = net.minecraft.world.entity.Entity.class.getDeclaredField("DATA_SHARED_FLAGS_ID"); dataSharedFlagsIdField.setAccessible(true); - DATA_SHARED_FLAGS_ID = (EntityDataAccessor) dataSharedFlagsIdField.get(null); + DATA_SHARED_FLAGS_ID = (EntityDataAccessor<@NotNull Byte>) dataSharedFlagsIdField.get(null); Class nmsEntityClass = net.minecraft.world.entity.LivingEntity.class; Field dataEffectParticlesField = nmsEntityClass.getDeclaredField("DATA_EFFECT_PARTICLES"); dataEffectParticlesField.setAccessible(true); - DATA_EFFECT_PARTICLES = (EntityDataAccessor>) dataEffectParticlesField.get(null); + DATA_EFFECT_PARTICLES = (EntityDataAccessor<@NotNull List>) dataEffectParticlesField.get(null); Field dataEffectAmbienceIdField = nmsEntityClass.getDeclaredField("DATA_EFFECT_AMBIENCE_ID"); dataEffectAmbienceIdField.setAccessible(true); - DATA_EFFECT_AMBIENCE_ID = (EntityDataAccessor) dataEffectAmbienceIdField.get(null); + DATA_EFFECT_AMBIENCE_ID = (EntityDataAccessor<@NotNull Boolean>) dataEffectAmbienceIdField.get(null); UPDATE_EFFECT_PARTICLES = nmsEntityClass.getDeclaredMethod("updateSynchronizedMobEffectParticles"); UPDATE_EFFECT_PARTICLES.setAccessible(true); diff --git a/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileGlowManagerLatest.java b/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileGlowManagerLatest.java index b70df77e6..4a812b63a 100644 --- a/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileGlowManagerLatest.java +++ b/nms/latest/src/main/java/com/nisovin/magicspells/volatilecode/latest/VolatileGlowManagerLatest.java @@ -44,7 +44,7 @@ public class VolatileGlowManagerLatest extends PacketBasedGlowManager, ClientboundSetEntityDataPacket, ClientboundSetPlayerTeamPacket> { - private static final EntityDataAccessor DATA_SHARED_FLAGS_ID = new EntityDataAccessor<>(0, EntityDataSerializers.BYTE); + private static final EntityDataAccessor<@NotNull Byte> DATA_SHARED_FLAGS_ID = new EntityDataAccessor<>(0, EntityDataSerializers.BYTE); private final Set> handled = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); private final MethodHandle teamPacketHandle; diff --git a/nms/v1_21_10/build.gradle b/nms/v1_21_10/build.gradle new file mode 100644 index 000000000..2ce8c3dae --- /dev/null +++ b/nms/v1_21_10/build.gradle @@ -0,0 +1,8 @@ +plugins { + id "io.papermc.paperweight.userdev" +} + +dependencies { + paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") + implementation project(":nms:shared") +} diff --git a/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileCode_v1_21_10.java b/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileCode_v1_21_10.java new file mode 100644 index 000000000..f3cf3882c --- /dev/null +++ b/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileCode_v1_21_10.java @@ -0,0 +1,226 @@ +package com.nisovin.magicspells.volatilecode.v1_21_10; + +import java.util.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.bukkit.World; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.util.Vector; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.event.entity.ExplosionPrimeEvent; + +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.entity.CraftTNTPrimed; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; + +import net.kyori.adventure.text.Component; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.advancement.AdvancementDisplay; + +import com.nisovin.magicspells.util.glow.GlowManager; +import com.nisovin.magicspells.volatilecode.VolatileCodeHandle; +import com.nisovin.magicspells.volatilecode.VolatileCodeHelper; + +import net.minecraft.util.ARGB; +import net.minecraft.core.BlockPos; +import net.minecraft.advancements.*; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.entity.EntityType; +import net.minecraft.network.protocol.game.*; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.item.PrimedTnt; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.core.particles.ColorParticleOption; +import net.minecraft.advancements.critereon.ImpossibleTrigger; +import net.minecraft.world.entity.boss.enderdragon.EnderDragon; + +public class VolatileCode_v1_21_10 extends VolatileCodeHandle { + + private final ResourceLocation TOAST_KEY = ResourceLocation.fromNamespaceAndPath("magicspells", "toast_effect"); + + private final EntityDataAccessor> DATA_EFFECT_PARTICLES; + private final EntityDataAccessor DATA_EFFECT_AMBIENCE_ID; + private final EntityDataAccessor DATA_SHARED_FLAGS_ID; + private final Method UPDATE_EFFECT_PARTICLES; + + @SuppressWarnings("unchecked") + public VolatileCode_v1_21_10(VolatileCodeHelper helper) throws Exception { + super(helper); + + Field dataSharedFlagsIdField = net.minecraft.world.entity.Entity.class.getDeclaredField("DATA_SHARED_FLAGS_ID"); + dataSharedFlagsIdField.setAccessible(true); + DATA_SHARED_FLAGS_ID = (EntityDataAccessor) dataSharedFlagsIdField.get(null); + + Class nmsEntityClass = net.minecraft.world.entity.LivingEntity.class; + + Field dataEffectParticlesField = nmsEntityClass.getDeclaredField("DATA_EFFECT_PARTICLES"); + dataEffectParticlesField.setAccessible(true); + DATA_EFFECT_PARTICLES = (EntityDataAccessor>) dataEffectParticlesField.get(null); + + Field dataEffectAmbienceIdField = nmsEntityClass.getDeclaredField("DATA_EFFECT_AMBIENCE_ID"); + dataEffectAmbienceIdField.setAccessible(true); + DATA_EFFECT_AMBIENCE_ID = (EntityDataAccessor) dataEffectAmbienceIdField.get(null); + + UPDATE_EFFECT_PARTICLES = nmsEntityClass.getDeclaredMethod("updateSynchronizedMobEffectParticles"); + UPDATE_EFFECT_PARTICLES.setAccessible(true); + } + + @Override + public void addPotionGraphicalEffect(LivingEntity entity, int color, long duration) { + var nmsEntity = (((CraftLivingEntity) entity)).getHandle(); + SynchedEntityData entityData = nmsEntity.getEntityData(); + + entityData.set( + DATA_EFFECT_PARTICLES, + List.of(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, ARGB.opaque(color))) + ); + + entityData.set(DATA_EFFECT_AMBIENCE_ID, false); + + if (duration <= 0) return; + helper.scheduleDelayedTask(() -> { + try { + UPDATE_EFFECT_PARTICLES.invoke(nmsEntity); + } catch (Exception e) { + e.printStackTrace(); + } + }, duration); + } + + @Override + public void sendFakeSlotUpdate(Player player, int slot, ItemStack item) { + var nmsItem = CraftItemStack.asNMSCopy(item); + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(0, 0, slot + 36, nmsItem); + + ((CraftPlayer) player).getHandle().connection.send(packet); + } + + @Override + public boolean simulateTnt(Location target, LivingEntity source, float explosionSize, boolean fire) { + ServerLevel world = ((CraftWorld) target.getWorld()).getHandle(); + var igniter = ((CraftLivingEntity) source).getHandle(); + + PrimedTnt tnt = new PrimedTnt(world, target.x(), target.y(), target.z(), igniter); + CraftTNTPrimed craftTNT = new CraftTNTPrimed((CraftServer) Bukkit.getServer(), tnt); + + return !new ExplosionPrimeEvent(craftTNT, explosionSize, fire).callEvent(); + } + + @Override + public void playDragonDeathEffect(Location location) { + EnderDragon dragon = new EnderDragon(EntityType.ENDER_DRAGON, ((CraftWorld) location.getWorld()).getHandle()); + dragon.setPos(location.x(), location.y(), location.z()); + + BlockPos pos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + ClientboundAddEntityPacket addMobPacket = new ClientboundAddEntityPacket(dragon, 0, pos); + ClientboundEntityEventPacket entityEventPacket = new ClientboundEntityEventPacket(dragon, (byte) 3); + ClientboundRemoveEntitiesPacket removeEntityPacket = new ClientboundRemoveEntitiesPacket(dragon.getId()); + + List players = new ArrayList<>(); + for (Player player : location.getNearbyPlayers(64.0)) { + players.add(player); + ServerPlayer nmsPlayer = ((CraftPlayer) player).getHandle(); + nmsPlayer.connection.send(addMobPacket); + nmsPlayer.connection.send(entityEventPacket); + } + + helper.scheduleDelayedTask(() -> { + for (Player player : players) { + if (!player.isValid()) continue; + ((CraftPlayer) player).getHandle().connection.send(removeEntityPacket); + } + }, 200); + } + + @Override + public void setClientVelocity(Player player, Vector velocity) { + Vec3 pos = new Vec3(velocity.getX(), velocity.getY(), velocity.getZ()); + ClientboundSetEntityMotionPacket packet = new ClientboundSetEntityMotionPacket(player.getEntityId(), pos); + ((CraftPlayer) player).getHandle().connection.send(packet); + } + + @Override + public void playHurtSound(LivingEntity entity) { + var nmsEntity = ((CraftLivingEntity) entity).getHandle(); + var sound = nmsEntity.getHurtSound(nmsEntity.damageSources().generic()); + + if (sound == null || nmsEntity.isSilent()) return; + nmsEntity.level().playSound( + null, + nmsEntity.blockPosition(), + sound, + nmsEntity.getSoundSource(), + nmsEntity.getSoundVolume(), + nmsEntity.getVoicePitch() + ); + } + + @Override + public void sendToastEffect(Player receiver, ItemStack icon, AdvancementDisplay.Frame frameType, Component text) { + var iconNms = CraftItemStack.asNMSCopy(icon); + var textNms = PaperAdventure.asVanilla(text); + var description = PaperAdventure.asVanilla(Component.empty()); + AdvancementType frame; + try { + frame = AdvancementType.valueOf(frameType.name()); + } catch (IllegalArgumentException ignored) { + frame = AdvancementType.TASK; + } + + AdvancementHolder advancement = Advancement.Builder.advancement() + .display(iconNms, textNms, description, null, frame, true, false, true) + .addCriterion("impossible", new Criterion<>(new ImpossibleTrigger(), new ImpossibleTrigger.TriggerInstance())) + .build(TOAST_KEY); + AdvancementProgress progress = new AdvancementProgress(); + progress.update(new AdvancementRequirements(List.of(List.of("impossible")))); + progress.grantProgress("impossible"); + + ServerPlayer player = ((CraftPlayer) receiver).getHandle(); + player.connection.send(new ClientboundUpdateAdvancementsPacket( + false, + Collections.singleton(advancement), + Collections.emptySet(), + Collections.singletonMap(TOAST_KEY, progress), + true + )); + player.connection.send(new ClientboundUpdateAdvancementsPacket( + false, + Collections.emptySet(), + Collections.singleton(TOAST_KEY), + Collections.emptyMap(), + true + )); + } + + @Override + public byte getEntityMetadata(Entity entity) { + return ((CraftEntity) entity).getHandle().getEntityData().get(DATA_SHARED_FLAGS_ID); + } + + @Override + public Entity getEntityFromId(World world, int id) { + var entity = ((CraftWorld) world).getHandle().moonrise$getEntityLookup().get(id); + return entity == null ? null : entity.getBukkitEntity(); + } + + @Override + public GlowManager getGlowManager() { + return new VolatileGlowManager_v1_21_10(helper); + } + +} diff --git a/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileGlowManager_v1_21_10.java b/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileGlowManager_v1_21_10.java new file mode 100644 index 000000000..73546bc7f --- /dev/null +++ b/nms/v1_21_10/src/main/java/com/nisovin/magicspells/volatilecode/v1_21_10/VolatileGlowManager_v1_21_10.java @@ -0,0 +1,321 @@ +package com.nisovin.magicspells.volatilecode.v1_21_10; + +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.lang.invoke.MethodType; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import io.netty.channel.ChannelPromise; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.protocol.Packet; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.scores.Team.Visibility; +import net.minecraft.world.scores.Team.CollisionRule; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.scoreboard.Team; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.configuration.ConfigurationSection; + +import com.nisovin.magicspells.util.glow.LibsDisguiseHelper; +import com.nisovin.magicspells.volatilecode.VolatileCodeHelper; +import com.nisovin.magicspells.util.glow.PacketBasedGlowManager; + +public class VolatileGlowManager_v1_21_10 extends PacketBasedGlowManager, ClientboundSetEntityDataPacket, ClientboundSetPlayerTeamPacket> { + + private static final EntityDataAccessor DATA_SHARED_FLAGS_ID = new EntityDataAccessor<>(0, EntityDataSerializers.BYTE); + + private final Set> handled = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); + private final MethodHandle teamPacketHandle; + private final VolatileCodeHelper helper; + + public VolatileGlowManager_v1_21_10(VolatileCodeHelper helper) { + this.helper = helper; + + try { + teamPacketHandle = MethodHandles + .privateLookupIn(ClientboundSetPlayerTeamPacket.class, MethodHandles.lookup()) + .findConstructor( + ClientboundSetPlayerTeamPacket.class, + MethodType.methodType(void.class, String.class, int.class, Optional.class, Collection.class) + ); + } catch (Exception e) { + throw new RuntimeException("Encountered an error while initializing VolatileGlowManagerLatest", e); + } + + helper.registerEvents(this); + } + + @Override + public void load() { + super.load(); + + Bukkit.getOnlinePlayers().forEach(this::addGlowChannelHandler); + } + + @Override + public synchronized void unload() { + Bukkit.getOnlinePlayers().forEach(this::removeGlowChannelHandler); + + super.unload(); + handled.clear(); + } + + @Override + protected Collection createAddTeamPackets() { + List teamPackets = new ArrayList<>(); + + ConfigurationSection config = helper.getMainConfig(); + boolean seeFriendlyInvisibles = config.getBoolean("general.glow-spell-scoreboard-teams.see-friendly-invisibles", false); + CollisionRule collision = getStringOption("collision-rule", CollisionRule.ALWAYS, StringRepresentable.createNameLookup(CollisionRule.values()), config, helper::error); + Visibility visibility = getStringOption("name-tag-visibility", Visibility.ALWAYS, StringRepresentable.createNameLookup(Visibility.values()), config, helper::error); + + Scoreboard scoreboard = new Scoreboard(); + for (ChatFormatting formatting : ChatFormatting.values()) { + if (!formatting.isColor()) continue; + + PlayerTeam team = new PlayerTeam(scoreboard, "magicspells:" + formatting.getName()); + team.setSeeFriendlyInvisibles(seeFriendlyInvisibles); + team.setNameTagVisibility(visibility); + team.setCollisionRule(collision); + team.setColor(formatting); + + teamPackets.add(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true)); + } + + return teamPackets; + } + + @Override + protected Collection createRemoveTeamPackets() { + List packets = new ArrayList<>(); + + Scoreboard scoreboard = new Scoreboard(); + for (ChatFormatting formatting : ChatFormatting.values()) { + if (!formatting.isColor()) continue; + + PlayerTeam team = new PlayerTeam(scoreboard, "magicspells:" + formatting.getName()); + packets.add(ClientboundSetPlayerTeamPacket.createRemovePacket(team)); + } + + return packets; + } + + @Override + protected ClientboundSetEntityDataPacket createEntityDataPacket(@NotNull Entity entity, boolean forceGlow) { + byte metadata = ((CraftEntity) entity).getHandle().getEntityData().get(DATA_SHARED_FLAGS_ID); + if (forceGlow) metadata |= 0x40; + + return new ClientboundSetEntityDataPacket( + entity.getEntityId(), + list(new SynchedEntityData.DataValue<>( + 0, + EntityDataSerializers.BYTE, + metadata + )) + ); + } + + @Override + protected Collection createJoinTeamPacket(@NotNull GlowData data) { + return data.lastScoreboardEntry().map(entry -> { + try { + return (ClientboundSetPlayerTeamPacket) teamPacketHandle.invoke( + "magicspells:" + data.color(), + 3, + Optional.empty(), + list(entry) + ); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + @Override + protected Collection createResetTeamPacket(org.bukkit.scoreboard.@NotNull Scoreboard scoreboard, @NotNull GlowData data) { + return data.lastScoreboardEntry().map(entry -> { + try { + Team team = scoreboard.getEntryTeam(entry); + if (team != null) { + return (ClientboundSetPlayerTeamPacket) teamPacketHandle.invoke( + team.getName(), + 3, + Optional.empty(), + list(entry) + ); + } + + return (ClientboundSetPlayerTeamPacket) teamPacketHandle.invoke( + "magicspells:" + data.color(), + 4, + Optional.empty(), + list(entry) + ); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + @Override + protected void sendPacket(@NotNull Player player, @NotNull Packet packet) { + if (!(packet instanceof ClientboundSetPlayerTeamPacket teamPacket) || teamPacket.getTeamAction() == null) + handled.add(packet); + + ServerGamePacketListenerImpl connection = ((CraftPlayer) player).getHandle().connection; + connection.send(packet); + } + + @Override + protected void registerEvents(Listener listener) { + helper.registerEvents(listener); + } + + @Override + protected void cancelTask(int taskId) { + helper.cancelTask(taskId); + } + + @Override + public int scheduleDelayedTask(Runnable runnable, long delay) { + return helper.scheduleDelayedTask(runnable, delay); + } + + private void addGlowChannelHandler(Player player) { + ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().connection.connection.channel.pipeline(); + pipeline.addBefore("unbundler", "magicspells:glow_channel_handler", new GlowChannelHandler(player)); + } + + private void removeGlowChannelHandler(Player player) { + ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().connection.connection.channel.pipeline(); + + if (pipeline.get("magicspells:glow_channel_handler") != null) + pipeline.remove("magicspells:glow_channel_handler"); + } + + private List list(T element) { + List list = new ArrayList<>(1); + list.add(element); + + return list; + } + + @Override + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + super.onPlayerJoin(event); + addGlowChannelHandler(event.getPlayer()); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + removeGlowChannelHandler(event.getPlayer()); + } + + private class GlowChannelHandler extends ChannelOutboundHandlerAdapter { + + private final Player player; + + private GlowChannelHandler(Player player) { + this.player = player; + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (msg instanceof Packet p && handled.contains(p)) { + super.write(ctx, msg, promise); + return; + } + + synchronized (VolatileGlowManager_v1_21_10.this) { + if (glows.isEmpty() && perPlayerGlows.isEmpty()) { + super.write(ctx, msg, promise); + return; + } + } + + switch (msg) { + case ClientboundSetEntityDataPacket packet -> handleEntityData(packet); + case ClientboundSetPlayerTeamPacket packet -> { + msg = handleTeamPacket(packet); + if (msg == null) return; + } + default -> {} + } + + super.write(ctx, msg, promise); + } + + private void handleEntityData(ClientboundSetEntityDataPacket packet) { + List> packedItems = packet.packedItems(); + if (packedItems.isEmpty()) return; + + SynchedEntityData.DataValue item = packedItems.getFirst(); + if (item.id() != 0) return; + + byte flags = (byte) item.value(); + if ((flags & 0x40) > 0) return; + + UUID uuid; + if (!libsDisguisesLoaded || packet.id() != LibsDisguiseHelper.getSelfDisguiseId()) { + var entity = ((CraftPlayer) player).getHandle().level().moonrise$getEntityLookup().get(packet.id()); + if (entity == null) return; + + uuid = entity.getUUID(); + } else uuid = player.getUniqueId(); + + GlowData data = getGlowData(player.getUniqueId(), uuid); + if (data == null) return; + + flags |= 0x40; + packedItems.set(0, new SynchedEntityData.DataValue<>(0, EntityDataSerializers.BYTE, flags)); + } + + private ClientboundSetPlayerTeamPacket handleTeamPacket(ClientboundSetPlayerTeamPacket packet) { + ClientboundSetPlayerTeamPacket.Action playerAction = packet.getPlayerAction(); + if (playerAction == null || packet.getTeamAction() != null) return packet; + + Collection entries = packet.getPlayers(); + if (entries.isEmpty()) return packet; + + Collection filtered = filterTeamEntries(player, entries); + if (filtered == null) return packet; + if (filtered.isEmpty()) return null; + + try { + return (ClientboundSetPlayerTeamPacket) teamPacketHandle.invoke( + packet.getName(), + playerAction == ClientboundSetPlayerTeamPacket.Action.ADD ? 3 : 4, + packet.getParameters(), + filtered + ); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7d86ea860..be5c6c4d9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,6 +9,7 @@ include("towny") include(":nms:shared") include(":nms:latest") +include(":nms:v1_21_10") pluginManagement { repositories {