Commit ada68a1c authored by Ash's avatar Ash
Browse files

Renamed project to MythicCosmetics

`+` Plugin is now a Mythic add-on and depends on MythicMobs

`+` Updated to support 1.21.3 and 1.21.4 (emotes currently broken, though)

`+` Dropped support for all versions before 1.21.3

`+` Updated to use ModelEngine 4

`+` Added support to load cosmetics from Mythic packs using folder structure `PACK/cosmetics/hats` etc

`+` Added support for Crucible implicit generation for hats and other equippabless
parent 048c3a5f
......@@ -12,20 +12,20 @@ import io.lumine.cosmetics.storage.sql.mappings.tables.records.ProfileEquippedRe
import java.util.Arrays;
import java.util.List;
import io.lumine.utils.lib.jooq.Field;
import io.lumine.utils.lib.jooq.ForeignKey;
import io.lumine.utils.lib.jooq.Index;
import io.lumine.utils.lib.jooq.Name;
import io.lumine.utils.lib.jooq.Record;
import io.lumine.utils.lib.jooq.Row4;
import io.lumine.utils.lib.jooq.Schema;
import io.lumine.utils.lib.jooq.Table;
import io.lumine.utils.lib.jooq.TableField;
import io.lumine.utils.lib.jooq.TableOptions;
import io.lumine.utils.lib.jooq.UniqueKey;
import io.lumine.utils.lib.jooq.impl.DSL;
import io.lumine.utils.lib.jooq.impl.SQLDataType;
import io.lumine.utils.lib.jooq.impl.TableImpl;
import io.lumine.mythic.bukkit.utils.lib.jooq.Field;
import io.lumine.mythic.bukkit.utils.lib.jooq.ForeignKey;
import io.lumine.mythic.bukkit.utils.lib.jooq.Index;
import io.lumine.mythic.bukkit.utils.lib.jooq.Name;
import io.lumine.mythic.bukkit.utils.lib.jooq.Record;
import io.lumine.mythic.bukkit.utils.lib.jooq.Row4;
import io.lumine.mythic.bukkit.utils.lib.jooq.Schema;
import io.lumine.mythic.bukkit.utils.lib.jooq.Table;
import io.lumine.mythic.bukkit.utils.lib.jooq.TableField;
import io.lumine.mythic.bukkit.utils.lib.jooq.TableOptions;
import io.lumine.mythic.bukkit.utils.lib.jooq.UniqueKey;
import io.lumine.mythic.bukkit.utils.lib.jooq.impl.DSL;
import io.lumine.mythic.bukkit.utils.lib.jooq.impl.SQLDataType;
import io.lumine.mythic.bukkit.utils.lib.jooq.impl.TableImpl;
/**
......
......@@ -6,11 +6,11 @@ package io.lumine.cosmetics.storage.sql.mappings.tables.records;
import io.lumine.cosmetics.storage.sql.mappings.tables.MccosmeticsProfileEquipped;
import io.lumine.utils.lib.jooq.Field;
import io.lumine.utils.lib.jooq.Record2;
import io.lumine.utils.lib.jooq.Record4;
import io.lumine.utils.lib.jooq.Row4;
import io.lumine.utils.lib.jooq.impl.UpdatableRecordImpl;
import io.lumine.mythic.bukkit.utils.lib.jooq.Field;
import io.lumine.mythic.bukkit.utils.lib.jooq.Record2;
import io.lumine.mythic.bukkit.utils.lib.jooq.Record4;
import io.lumine.mythic.bukkit.utils.lib.jooq.Row4;
import io.lumine.mythic.bukkit.utils.lib.jooq.impl.UpdatableRecordImpl;
/**
......
......@@ -6,11 +6,11 @@ package io.lumine.cosmetics.storage.sql.mappings.tables.records;
import io.lumine.cosmetics.storage.sql.mappings.tables.MccosmeticsProfile;
import io.lumine.utils.lib.jooq.Field;
import io.lumine.utils.lib.jooq.Record1;
import io.lumine.utils.lib.jooq.Record2;
import io.lumine.utils.lib.jooq.Row2;
import io.lumine.utils.lib.jooq.impl.UpdatableRecordImpl;
import io.lumine.mythic.bukkit.utils.lib.jooq.Field;
import io.lumine.mythic.bukkit.utils.lib.jooq.Record1;
import io.lumine.mythic.bukkit.utils.lib.jooq.Record2;
import io.lumine.mythic.bukkit.utils.lib.jooq.Row2;
import io.lumine.mythic.bukkit.utils.lib.jooq.impl.UpdatableRecordImpl;
/**
......
name: MCCosmetics
name: MythicCosmetics
author: Lumine & MCModels
main: io.lumine.cosmetics.MCCosmeticsPlugin
version: ${project.version}-${build.number}
api-version: 1.13
depend: []
depend:
- MythicMobs
softdepend:
- ModelEngine
- MythicMobs
- MythicCrucible
- Vault
- LibsDisguises
- PlaceholderAPI
......
#
# Configuration Options for Cosmetics
#
# Config options for MythicCosmetics. Learn more on the wiki here:
#
# Manual - https://git.mythiccraft.io/mythiccosmetics/MythicCosmetics/-/wikis/home
# Discord - https://www.discord.gg/MythicCraft
#
Configuration:
#================================================================================
# Information for where data is stored
#================================================================================
Storage:
Driver: JSON
#================================================================================
# General Options
#================================================================================
General:
EquipDelay: 5
EquipDelay: 5 # Delay before equipping when a player joins
GenerateDefaultPack: true # Whether to generate the default 'example' pack
#
# Plugin Assets refer to things like menu GUIs, icons, etc.
# These can be automatically implicitly generated by Crucible from a given pack
#
Assets:
CrucibleGeneration: false # Should plugin assets be auto-generated by Crucible
PackToGenerateFrom: PLUGIN_DIR # Mythic Pack to generate plugin assets from
Menus:
Close-On-Unequip: false
#================================================================================
# Options for Sprays
#================================================================================
Sprays:
Sound: 'entity.cat.hiss'
Volume: 1.0
Pitch: 2.0
ReplaceLast: true
PersistTime: 60
Menus:
Close-On-Unequip: false
Language:
Command-Header: "<yellow><strikethrough>------------<gold>=====</strikethrough> <bold><gradient:#dc0c1f:#ed858f>MCCosmetics</gradient></bold> <strikethrough><gold>=====<yellow>------------</strikethrough>"
Command-Footer: "<yellow><strikethrough>--------------------------------------</strikethrough>"
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.lumine</groupId>
<artifactId>MCCosmetics-Plugin</artifactId>
<artifactId>MythicCosmetics-Plugin</artifactId>
<version>${mccosmetics.version}</version>
<name>MCCosmetics</name>
<description>A plugin for creating insane cosmetics.</description>
<name>MythicCosmetics</name>
<description>A plugin for creating thicc cosmetics.</description>
<url>https://www.mythiccraft.io</url>
<packaging>pom</packaging>
<organization>
......@@ -13,10 +13,11 @@
</organization>
<properties>
<mccosmetics.version>0.11.0</mccosmetics.version>
<paperapi.version>1.20.1-R0.1-SNAPSHOT</paperapi.version>
<lumineutils.version>1.19-SNAPSHOT</lumineutils.version>
<lombok.version>1.18.22</lombok.version>
<mccosmetics.version>0.0.1-SNAPSHOT</mccosmetics.version>
<paperapi.version>1.21.3-R0.1-SNAPSHOT</paperapi.version>
<mythic.version>5.8.0-SNAPSHOT</mythic.version>
<lumineutils.version>1.20-SNAPSHOT</lumineutils.version>
<lombok.version>1.18.32</lombok.version>
<!--suppress UnresolvedMavenProperty -->
<build.number>${CI_COMMIT_SHORT_SHA}</build.number>
<downloadSources>false</downloadSources>
......@@ -31,10 +32,8 @@
<modules>
<module>api</module>
<module>plugin</module>
<module>v1_18_R2</module>
<module>v1_19_R2</module>
<module>v1_19_R3</module>
<module>v1_20_R1</module>
<module>v1_21_R2</module>
<module>v1_21_R3</module>
<module>dist</module>
<module>dist-premium</module>
</modules>
......@@ -58,9 +57,9 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<version>3.13.0</version>
<configuration>
<release>16</release>
<release>21</release>
</configuration>
</plugin>
</plugins>
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>MCCosmetics-v1_18_R2</artifactId>
<parent>
<groupId>io.lumine</groupId>
<artifactId>MCCosmetics-Plugin</artifactId>
<version>${mccosmetics.version}</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:1.18.2-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>org.spigotmc:spigot:1.18.2-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
<srgIn>org.spigotmc:minecraft-server:1.18.2-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:1.18.2-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.2</version>
<configuration>
<updatePomFile>true</updatePomFile>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>package</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>lumine</id>
<url>https://mvn.lumine.io/repository/maven/</url>
</repository>
<repository>
<id>paper-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>
<dependencies>
<!-- Modules -->
<dependency>
<groupId>io.lumine</groupId>
<artifactId>LumineUtils</artifactId>
<version>${lumineutils.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MCCosmetics</artifactId>
<version>${project.parent.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18.2-R0.1-SNAPSHOT</version>
<classifier>shaded</classifier>
<scope>provided</scope>
</dependency>
<!-- Spigot NMS version -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.18.2-R0.1-SNAPSHOT</version>
<classifier>remapped-mojang</classifier>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>3.11.50</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId>
<version>1.0.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta4</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package io.lumine.cosmetics.nms;
import com.google.common.collect.Maps;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.Cosmetic;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.api.players.wardrobe.WardrobeTracker;
import io.lumine.cosmetics.managers.back.BackAccessory;
import io.lumine.cosmetics.managers.gestures.Gesture;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.managers.offhand.Offhand;
import io.lumine.cosmetics.managers.sprays.Spray;
import io.lumine.cosmetics.nms.cosmetic.VolatileCosmeticHelper;
import io.lumine.cosmetics.nms.v1_18_R2.cosmetic.*;
import io.lumine.cosmetics.nms.v1_18_R2.network.VolatileChannelHandler;
import io.lumine.cosmetics.nms.v1_18_R2.wardrobe.MannequinEntity;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import lombok.Getter;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.Map;
public class VolatileCodeEnabled_v1_18_R2 implements VolatileCodeHandler {
@Getter private final MCCosmeticsPlugin plugin;
private final Map<Class<? extends Cosmetic>, VolatileCosmeticHelper> cosmeticHelpers = Maps.newConcurrentMap();
public VolatileCodeEnabled_v1_18_R2(MCCosmeticsPlugin plugin) {
this.plugin = plugin;
cosmeticHelpers.put(Hat.class, new VolatileHatImpl(plugin, this));
cosmeticHelpers.put(BackAccessory.class, new VolatileBackImpl(plugin, this));
cosmeticHelpers.put(Spray.class, new VolatileSprayImpl(plugin, this));
cosmeticHelpers.put(Offhand.class, new VolatileOffhandImpl(plugin, this));
cosmeticHelpers.put(Gesture.class, new VolatileGestureImpl(plugin, this));
}
@Override
public VolatileCosmeticHelper getCosmeticHelper(Class<? extends Cosmetic> tClass) {
return cosmeticHelpers.get(tClass);
}
@Override
public Collection<VolatileCosmeticHelper> getCosmeticHelpers() {
return cosmeticHelpers.values();
}
@Override
public void injectPlayer(Player player) {
ServerPlayer ply = ((CraftPlayer) player).getHandle();
VolatileChannelHandler cdh = new VolatileChannelHandler(player, this);
ChannelPipeline pipeline = ply.connection.getConnection().channel.pipeline();
for (String name : pipeline.toMap().keySet()) {
if (pipeline.get(name) instanceof Connection) {
pipeline.addBefore(name, "mc_cosmetics_packet_handler", cdh);
break;
}
}
}
@Override
public void removePlayer(Player player) {
Channel channel = ((CraftPlayer) player).getHandle().connection.getConnection().channel;
channel.eventLoop().submit(() -> {
channel.pipeline().remove("mc_cosmetics_packet_handler");
return null;
});
}
public void broadcast(Packet<?>... packets) {
for(Player player : Bukkit.getOnlinePlayers()) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
}
public void broadcast(Player player, Packet<?>... packets) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
public void broadcastAroundAndSelf(Player wearer, Packet<?>... packets) {
final var level = ((CraftWorld) wearer.getWorld()).getHandle();
final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
if(trackedEntity == null) {
broadcast(wearer.getWorld(), packets);
return;
}
for(Packet<?> packet : packets)
trackedEntity.broadcastAndSend(packet);
}
public void broadcastAround(Player wearer, Packet<?>... packets) {
final var level = ((CraftWorld) wearer.getWorld()).getHandle();
final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
if(trackedEntity == null) {
broadcast(wearer.getWorld(), packets);
return;
}
for(Packet<?> packet : packets)
trackedEntity.broadcast(packet);
}
public void broadcast(World world, Packet<?>... packets) {
for(Player player : world.getPlayers()) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
}
public Entity getEntity(World world, int id) {
ServerLevel level = ((CraftWorld) world).getHandle();
final var entityManager = level.entityManager;
final var entity = entityManager.getEntityGetter().get(id);
return entity == null ? null : entity.getBukkitEntity();
}
@Override
public Mannequin createMannequin(WardrobeTracker tracker, Player player, Location location) {
return new MannequinEntity(tracker,this,player,location);
}
@Override
public void removeFakeEntity(int id) {
ClientboundRemoveEntitiesPacket packet = new ClientboundRemoveEntitiesPacket(id);
broadcast(packet);
}
@Override
public void setBodyYaw(LivingEntity entity, double yaw) {
((CraftLivingEntity) entity).getHandle().yBodyRot = (float) yaw;
}
@Override
public float getBodyYaw(LivingEntity entity) {
return ((CraftLivingEntity) entity).getHandle().yBodyRot;
}
}
package io.lumine.cosmetics.nms.v1_18_R2.cosmetic;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.Cosmetic;
import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.logging.MCLogger;
import io.lumine.cosmetics.managers.back.BackAccessory;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class VolatileBackImpl implements VolatileEquipmentHelper {
@Getter
private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_18_R2 nmsHandler;
private final Map<Player, ArmorStand> activeProfile = Maps.newConcurrentMap();
private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
public VolatileBackImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_18_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
var maybeEquipped = profile.getEquipped(BackAccessory.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equipped = maybeEquipped.get();
var cosmetic = equipped.getCosmetic();
if (!(cosmetic instanceof ItemCosmetic back)) {
return;
}
var nmsPlayer = ((CraftPlayer) player).getHandle();
var nmsBack = CraftItemStack.asNMSCopy(back.getCosmetic(equipped));
ArmorStand stand = activeProfile.get(player);
if(stand == null) {
playerTracker.put(player.getEntityId(), player);
stand = new ArmorStand(EntityType.ARMOR_STAND, ((CraftWorld) player.getWorld()).getHandle());
stand.moveTo(nmsPlayer.getX(), nmsPlayer.getY() + nmsPlayer.getPassengersRidingOffset() + stand.getMyRidingOffset(), nmsPlayer.getZ(), nmsPlayer.getYRot(), 0);
stand.setMarker(true);
stand.setInvisible(true);
stand.setSilent(true);
activeProfile.put(player, stand);
ClientboundAddMobPacket mobPacket = new ClientboundAddMobPacket(stand);
ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData(), true);
ClientboundSetPassengersPacket passengersPacket = createPassengerPacket(player.getEntityId(), stand.getId());
nmsHandler.broadcastAroundAndSelf(player, mobPacket, dataPacket, passengersPacket);
}
stand.setItemSlot(EquipmentSlot.HEAD, nmsBack);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsBack)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
Player player = profile.getPlayer();
ArmorStand stand = activeProfile.remove(player);
if(stand == null)
return;
playerTracker.remove(player.getEntityId());
ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(stand.getId());
nmsHandler.broadcastAroundAndSelf(player, removePacket);
}
@Override
public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
if(!(cosmetic.getCosmetic() instanceof BackAccessory back)) {
return;
}
final var entityId = mannequin.getEntityId();
final var player = mannequin.getPlayer();
var nmsBack = CraftItemStack.asNMSCopy(back.getCosmetic(cosmetic));
var mannequinLocation = mannequin.getLocation();
mannequin.removeExtraEntity(BackAccessory.class);
var stand = new ArmorStand(EntityType.ARMOR_STAND, ((CraftWorld) player.getWorld()).getHandle());
stand.moveTo(mannequinLocation.getX(), mannequinLocation.getY() + stand.getMyRidingOffset(), mannequinLocation.getZ(), mannequinLocation.getYaw(), 0);
stand.setMarker(true);
stand.setInvisible(true);
stand.setSilent(true);
mannequin.addExtraEntity(BackAccessory.class, stand.getId());
var mobPacket = new ClientboundAddEntityPacket(stand);
var dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData(), true);
var passengersPacket = createPassengerPacket(entityId, stand.getId());
nmsHandler.broadcast(player, mobPacket, dataPacket, passengersPacket);
stand.setItemSlot(EquipmentSlot.HEAD, nmsBack);
var equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsBack)));
nmsHandler.broadcast(player, equipmentPacket);
}
@Override
public void unequipMannequin(Mannequin mannequin) {
mannequin.removeExtraEntity(BackAccessory.class);
}
@Override
public boolean read(Player sender, Object packet, boolean isCanceled) {
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(sender);
if(profile == null || profile.isHidden(BackAccessory.class))
return true;
if(packet instanceof ServerboundMovePlayerPacket) {
handleRotate(profile);
}else if(packet instanceof ServerboundAcceptTeleportationPacket) {
final var list = handleSpawn(profile);
if(list == null)
return true;
final var connection = ((CraftPlayer) sender).getHandle().connection;
for(Object obj : list) {
connection.send((Packet<?>) obj);
}
}
return true;
}
@Override
public List<Object> write(Player receiver, Object packet) {
if(packet instanceof ClientboundAddPlayerPacket playerPacket) {
int id = playerPacket.getEntityId();
if(playerTracker.containsKey(id)) {
final var spawnedPlayer = playerTracker.get(id);
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
if(profile == null || profile.isHidden(BackAccessory.class))
return null;
return handleSpawn(profile);
}
}else if(packet instanceof ClientboundRemoveEntitiesPacket removePacket) {
for(int id : removePacket.getEntityIds()) {
if(playerTracker.containsKey(id)) {
return handleDespawn(playerTracker.get(id));
}
}
}
/*
else if(packet instanceof ClientboundMoveEntityPacket moveEntityPacket) {
FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer());
moveEntityPacket.write(byteBuf);
int id = byteBuf.readVarInt();
if(playerTracker.containsKey(id)) {
final var spawnedPlayer = playerTracker.get(id);
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
if(profile == null || profile.isHidden(BackAccessory.class))
return null;
return handleMove(profile, moveEntityPacket);
}
}else if(packet instanceof ClientboundTeleportEntityPacket teleportEntityPacket) {
int id = teleportEntityPacket.getId();
if(playerTracker.containsKey(id)) {
final var spawnedPlayer = playerTracker.get(id);
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
if(profile == null || profile.isHidden(BackAccessory.class))
return null;
return handleTeleport(profile);
}
}*/
return null;
}
private void handleRotate(Profile profile) {
if(!hasBack(profile))
return;
final var wearer = profile.getPlayer();
final var nmsPlayer = ((CraftPlayer) wearer).getHandle();
final var stand = activeProfile.get(wearer);
ClientboundRotateHeadPacket packet = new ClientboundRotateHeadPacket(stand, VolatileEquipmentHelper.toByte(nmsPlayer.getYRot()));
nmsHandler.broadcastAroundAndSelf(wearer, packet);
}
private List<Object> handleSpawn(Profile profile) {
if(!hasBack(profile))
return null;
final var wearer = profile.getPlayer();
final var stand = activeProfile.get(wearer);
ClientboundAddMobPacket mobPacket = new ClientboundAddMobPacket(stand);
ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData(), true);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, stand.getItemBySlot(EquipmentSlot.HEAD))));
ClientboundSetPassengersPacket passengersPacket = createPassengerPacket(wearer.getEntityId(), stand.getId());
return List.of(mobPacket, dataPacket, equipmentPacket, passengersPacket);
}
private List<Object> handleDespawn(Player player) {
final var stand = activeProfile.get(player);
if(stand == null)
return null;
ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(stand.getId());
return List.of(removePacket);
}
private List<Object> handleMove(Profile profile, ClientboundMoveEntityPacket moveEntityPacket) {
if(!hasBack(profile))
return null;
final var wearer = profile.getPlayer();
final var stand = activeProfile.get(wearer);
ClientboundMoveEntityPacket move;
if(moveEntityPacket.hasPosition() && moveEntityPacket.hasRotation()) {
move = new ClientboundMoveEntityPacket.PosRot(
stand.getId(),
moveEntityPacket.getXa(),
moveEntityPacket.getYa(),
moveEntityPacket.getZa(),
moveEntityPacket.getyRot(),
moveEntityPacket.getxRot(),
false);
}else if(moveEntityPacket.hasPosition()) {
move = new ClientboundMoveEntityPacket.Pos(
stand.getId(),
moveEntityPacket.getXa(),
moveEntityPacket.getYa(),
moveEntityPacket.getZa(),
false);
}else {
move = new ClientboundMoveEntityPacket.Rot(
stand.getId(),
moveEntityPacket.getyRot(),
moveEntityPacket.getxRot(),
false);
}
return List.of(move);
}
private List<Object> handleTeleport(Profile profile) {
if(!hasBack(profile))
return null;
final var wearer = profile.getPlayer();
final var nmsPlayer = ((CraftPlayer) wearer).getHandle();
final var stand = activeProfile.get(wearer);
stand.moveTo(nmsPlayer.getX(), nmsPlayer.getY() + nmsPlayer.getPassengersRidingOffset() + stand.getMyRidingOffset(), nmsPlayer.getZ(), nmsPlayer.getYRot(), 0);
return List.of(new ClientboundTeleportEntityPacket(stand));
}
private boolean hasBack(Profile profile) {
if(profile == null)
return false;
var maybeBack = profile.getEquipped(BackAccessory.class);
return maybeBack.isPresent() && maybeBack.get().getCosmetic() instanceof ItemCosmetic && activeProfile.containsKey(profile.getPlayer());
}
private ClientboundSetPassengersPacket createPassengerPacket(int mount, int... driver) {
FriendlyByteBuf bb = new FriendlyByteBuf(Unpooled.buffer());
bb.writeVarInt(mount);
bb.writeVarIntArray(driver);
return new ClientboundSetPassengersPacket(bb);
}
}
\ No newline at end of file
package io.lumine.cosmetics.nms.v1_18_R2.cosmetic;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.manager.HideableCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.managers.gestures.Gesture;
import io.lumine.cosmetics.managers.gestures.GestureManager;
import io.lumine.cosmetics.managers.gestures.QuitMethod;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.animal.horse.Horse;
import net.minecraft.world.item.ItemStack;
import org.bukkit.attribute.Attribute;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.attribute.CraftAttributeMap;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class VolatileGestureImpl implements VolatileEquipmentHelper {
private static final List<Pair<EquipmentSlot, ItemStack>> empty = new ArrayList<>();
static {
for(final var slot : EquipmentSlot.values())
empty.add(Pair.of(slot, ItemStack.EMPTY));
}
@Getter
private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_18_R2 nmsHandler;
private final Set<Player> activeProfile = Sets.newConcurrentHashSet();
private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
private Horse horse;
public VolatileGestureImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_18_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
if(activeProfile.contains(player))
return;
final var maybeEquipped = profile.getEquipped(Gesture.class);
if(maybeEquipped.isEmpty()) {
return;
}
var opt = maybeEquipped.get().getCosmetic();
if(!(opt instanceof Gesture gesture))
return;
playerTracker.put(player.getEntityId(), player);
activeProfile.add(player);
if(!gesture.isCanMove())
getHorsed(player);
player.setInvisible(true);
for(final var value : ((Profile) profile).getEquipped().values()) {
final var manager = value.getCosmetic().getManager();
if(!(manager instanceof HideableCosmetic hide))
continue;
hide.hide(profile, gesture);
}
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), empty);
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
if(!activeProfile.contains(player))
return;
activeProfile.remove(player);
playerTracker.remove(player.getEntityId());
nmsHandler.broadcast(player, new ClientboundRemoveEntitiesPacket(horse.getId()));
final var nmsPlayer = ((CraftPlayer) player).getHandle();
List<Pair<EquipmentSlot, ItemStack>> equipment = new ArrayList<>();
for(EquipmentSlot slot : EquipmentSlot.values())
equipment.add(Pair.of(slot, nmsPlayer.getItemBySlot(slot)));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), equipment);
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
player.setInvisible(false);
for(final var value : ((Profile) profile).getEquipped().values()) {
final var manager = value.getCosmetic().getManager();
if(!(manager instanceof HideableCosmetic hide))
continue;
hide.show(profile);
}
}
private ClientboundSetPassengersPacket createPassengerPacket(int mount, int... driver) {
FriendlyByteBuf bb = new FriendlyByteBuf(Unpooled.buffer());
bb.writeVarInt(mount);
bb.writeVarIntArray(driver);
return new ClientboundSetPassengersPacket(bb);
}
private void getHorsed(Player player) {
final var nmsPlayer = ((CraftPlayer) player).getHandle();
if(horse == null) {
horse = new Horse(EntityType.HORSE, ((CraftWorld) player.getWorld()).getHandle());
horse.setInvisible(true);
horse.setHealth(0);
horse.getAttribute(CraftAttributeMap.toMinecraft(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(0);
}
horse.setPos(nmsPlayer.getX(), nmsPlayer.getY() - horse.getPassengersRidingOffset() - nmsPlayer.getMyRidingOffset(), nmsPlayer.getZ());
ClientboundAddMobPacket mobPacket = new ClientboundAddMobPacket(horse);
ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(horse.getId(), horse.getEntityData(), true);
ClientboundUpdateAttributesPacket attributesPacket = new ClientboundUpdateAttributesPacket(horse.getId(), horse.getAttributes().getSyncableAttributes());
ClientboundSetPassengersPacket passengersPacket = createPassengerPacket(horse.getId(), nmsPlayer.getId());
nmsHandler.broadcast(player, mobPacket, dataPacket, attributesPacket, passengersPacket);
}
@Override
public boolean read(Player sender, Object packet, boolean isCanceled) {
if(!plugin.getGestureManager().getTicking().containsKey(sender)) {
return true;
}
if(packet instanceof ServerboundPlayerInputPacket inputPacket) {
final var manager = (GestureManager) plugin.getGestureManager();
if(inputPacket.isShiftKeyDown())
manager.quit(sender, QuitMethod.SNEAK);
if(inputPacket.isJumping())
manager.quit(sender, QuitMethod.JUMP);
}else if(packet instanceof ServerboundSetCarriedItemPacket setSlotPacket) {
int oSlot = sender.getInventory().getHeldItemSlot();
if(oSlot != setSlotPacket.getSlot()) {
nmsHandler.broadcast(sender, new ClientboundSetCarriedItemPacket(oSlot));
return false;
}
}
return true;
}
@Override
public List<Object> write(Player receiver, Object packet) {
if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
int id = equipmentPacket.getEntity();
final var spawnedPlayer = playerTracker.get(id);
if(spawnedPlayer == null)
return null;
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
if(profile != null)
return List.of(new ClientboundSetEquipmentPacket(id, empty));
}
return null;
}
}
package io.lumine.cosmetics.nms.v1_18_R2.cosmetic;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.lumine.utils.reflection.Reflector;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.network.protocol.game.ServerboundAcceptTeleportationPacket;
import net.minecraft.world.entity.EquipmentSlot;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class VolatileHatImpl implements VolatileEquipmentHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_18_R2 nmsHandler;
private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
public VolatileHatImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_18_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var opt = maybeEquipped.get().getCosmetic();
if(!(opt instanceof Hat hat)) {
return;
}
var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(maybeEquipped.get()));
playerTracker.put(player.getEntityId(), player);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
if(!(cosmetic.getCosmetic() instanceof Hat hat)) {
return;
}
final var entityId = mannequin.getEntityId();
final var player = mannequin.getPlayer();
var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(cosmetic));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId, List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcast(player, equipmentPacket);
}
@Override
public void unequipMannequin(Mannequin mannequin) {
final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public boolean read(Player sender, Object packet, boolean isCanceled) {
if(packet instanceof ServerboundAcceptTeleportationPacket) {
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(sender);
if(profile == null || profile.isHidden(Hat.class))
return true;
handleSpawn(profile);
}
return true;
}
@Override
public List<Object> write(Player receiver, Object packet) {
/*
if(packet instanceof ClientboundAddPlayerPacket playerPacket) {
int id = playerPacket.getEntityId();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Hat.class)) {
handleSpawn(profile);
}
} else */
if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
int id = equipmentPacket.getEntity();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Hat.class)) {
modifyPacket(profile, equipmentPacket);
}
}
return null;
}
private Profile getProfile(Player receiver, int id) {
final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
if(!(entity instanceof Player player))
return null;
return plugin.getProfiles().getProfile(player);
}
public void handleSpawn(Profile profile) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var player = profile.getPlayer();
final var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
boolean foundHead = false;
for(var pair : slots) {
final EquipmentSlot slot = pair.getFirst();
if(slot == EquipmentSlot.HEAD) {
foundHead = true;
newSlots.add(Pair.of(pair.getFirst(), nmsItem));
} else {
newSlots.add(pair);
}
}
if(!foundHead) {
newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
}
refEq.set(packet, "c", newSlots);
}
}
package io.lumine.cosmetics.nms.v1_18_R2.cosmetic;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.managers.offhand.Offhand;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.lumine.utils.reflection.Reflector;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.world.entity.EquipmentSlot;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class VolatileOffhandImpl implements VolatileEquipmentHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_18_R2 nmsHandler;
private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
public VolatileOffhandImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_18_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
final var maybeEquipped = profile.getEquipped(Offhand.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic offhand))
return;
var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
playerTracker.put(player.getEntityId(), player);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
if(!(cosmetic.getCosmetic() instanceof Offhand offhand)) {
return;
}
final var entityId = mannequin.getEntityId();
final var player = mannequin.getPlayer();
var nmsHat = CraftItemStack.asNMSCopy(offhand.getCosmetic(cosmetic));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId, List.of(Pair.of(EquipmentSlot.OFFHAND, nmsHat)));
nmsHandler.broadcast(player, equipmentPacket);
}
@Override
public void unequipMannequin(Mannequin mannequin) {
final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public List<Object> write(Player receiver, Object packet) {
if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
int id = equipmentPacket.getEntity();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Offhand.class))
modifyPacket(profile, equipmentPacket);
}
return null;
}
private Profile getProfile(Player receiver, int id) {
final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
if(!(entity instanceof Player player))
return null;
return plugin.getProfiles().getProfile(player);
}
public void handleSpawn(Profile profile) {
final var maybeEquipped = profile.getEquipped(Offhand.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic offhand))
return;
final var player = profile.getPlayer();
final var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
boolean foundHead = false;
for(var pair : slots) {
final EquipmentSlot slot = pair.getFirst();
if(slot == EquipmentSlot.HEAD) {
foundHead = true;
newSlots.add(Pair.of(pair.getFirst(), nmsItem));
} else {
newSlots.add(pair);
}
}
if(!foundHead) {
newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
}
refEq.set(packet, "c", newSlots);
}
}
package io.lumine.cosmetics.nms.v1_18_R2.cosmetic;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.managers.sprays.Spray;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileSprayHelper;
import lombok.Getter;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
public class VolatileSprayImpl implements VolatileSprayHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_18_R2 nmsHandler;
public VolatileSprayImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_18_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
private static final ItemStack map = new ItemStack(Material.FILLED_MAP);
private static final MapMeta mapMeta = (MapMeta) map.getItemMeta();
@Override
public int drawSpray(Spray spray, Location location, BlockFace face, int rotation) {
var world = ((CraftWorld) location.getWorld()).getHandle();
var pos = new BlockPos(location.getX(), location.getY(), location.getZ());
var image = spray.getImage();
mapMeta.setMapId(image.getMapNumber());
map.setItemMeta(mapMeta);
var nmsMap = CraftItemStack.asNMSCopy(map);
final int dir = switch(face) {
case DOWN -> 0;
case UP -> 1;
case NORTH -> 2;
case SOUTH -> 3;
case WEST -> 4;
case EAST -> 5;
default -> 1;
};
var frame = new ItemFrame(world, pos, Direction.UP);
frame.setItem(nmsMap);
frame.setInvisible(true);
frame.setRotation(rotation);
var packetAdd = new ClientboundAddEntityPacket(frame, dir);
var packetData = new ClientboundSetEntityDataPacket(frame.getId(), frame.getEntityData(), true);
var packetMap = constructMapPacket(image.getMapNumber(), image.getPixels());
nmsHandler.broadcast(packetAdd, packetData, packetMap);
return frame.getId();
}
private static final int startX = 0;
private static final int startY = 0;
private static final int mapWidth = 128;
private static final int mapHeight = 128;
private static final byte mapScale = 1;
private static final boolean mapLocked = true;
private ClientboundMapItemDataPacket constructMapPacket(int id, byte[] pixels) {
var mapData = constructMapData(startX, startY, mapWidth, mapHeight, pixels);
var packet = new ClientboundMapItemDataPacket(id, mapScale, mapLocked, null, mapData);
return packet;
}
private MapItemSavedData.MapPatch constructMapData(int startX, int startY, int width, int height, byte[] pixels) {
return new MapItemSavedData.MapPatch(startX, startY, width, height, pixels);
}
}
package io.lumine.cosmetics.nms.v1_18_R2.wardrobe;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
import com.google.common.collect.Maps;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.api.cosmetics.Cosmetic;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.api.players.wardrobe.WardrobeTracker;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_18_R2;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundAnimatePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket;
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EquipmentSlot;
public class MannequinEntity implements Mannequin {
private final VolatileCodeEnabled_v1_18_R2 handler;
@Getter private final WardrobeTracker tracker;
@Getter private final UUID uniqueId;
@Getter private final Player player;
@Getter private final Location location;
@Getter private float rotation;
@Getter private ServerPlayer fakePlayer;
private Map<Class<? extends Cosmetic>,Integer> extraEntities = Maps.newConcurrentMap();
public MannequinEntity(WardrobeTracker tracker, VolatileCodeEnabled_v1_18_R2 handler, Player player, Location location) {
this.tracker = tracker;
this.handler = handler;
this.player = player;
this.location = location;
this.uniqueId = UUID.randomUUID();
var serverPlayer = ((CraftPlayer) player).getHandle();
var level = ((CraftWorld) location.getWorld()).getHandle();
this.rotation = serverPlayer.getYRot() + 180;
if(rotation > 180) {
rotation = rotation - 360;
}
var gameProfile = serverPlayer.getGameProfile();
gameProfile = new GameProfile(uniqueId, "Wardrobe");
gameProfile.getProperties().putAll(serverPlayer.getGameProfile().getProperties());
this.fakePlayer = new ServerPlayer(level.getServer(), level, gameProfile);
var spawn = new FriendlyByteBuf(Unpooled.buffer());
spawn.writeVarInt(fakePlayer.getId());
spawn.writeUUID(fakePlayer.getUUID());
spawn.writeDouble(location.getX());
spawn.writeDouble(location.getY());
spawn.writeDouble(location.getZ());
spawn.writeByte((byte)(int)(serverPlayer.getYRot() * 256.0F / 360.0F));
spawn.writeByte((byte)(int)(serverPlayer.getXRot() * 256.0F / 360.0F));
var packetPlayerInfo = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, fakePlayer);
var packetAddPlayer = new ClientboundAddPlayerPacket(spawn);
var equipmentPacket = new ClientboundSetEquipmentPacket(fakePlayer.getId(),
List.of(Pair.of(EquipmentSlot.HEAD, serverPlayer.getItemBySlot(EquipmentSlot.HEAD)),
Pair.of(EquipmentSlot.CHEST, serverPlayer.getItemBySlot(EquipmentSlot.CHEST)),
Pair.of(EquipmentSlot.LEGS, serverPlayer.getItemBySlot(EquipmentSlot.LEGS)),
Pair.of(EquipmentSlot.FEET, serverPlayer.getItemBySlot(EquipmentSlot.FEET)),
Pair.of(EquipmentSlot.MAINHAND, serverPlayer.getItemBySlot(EquipmentSlot.MAINHAND)),
Pair.of(EquipmentSlot.OFFHAND, serverPlayer.getItemBySlot(EquipmentSlot.OFFHAND))));
var head = (byte)(int)(rotation * 256.0F / 360.0F);
var rotatePacket = new ClientboundRotateHeadPacket(fakePlayer, head);
var swingPacket = new ClientboundAnimatePacket(fakePlayer, 0);
handler.broadcast(player, packetPlayerInfo, packetAddPlayer, equipmentPacket, rotatePacket, swingPacket);
for(var eq : handler.getPlugin().getProfiles().getProfile(player).getEquipped().values()) {
eq.getCosmetic().getManager().equipMannequin(this, eq);
}
}
public void despawn() {
var packetPlayerInfo = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER, fakePlayer);
var packetRemovePlayer = new ClientboundRemoveEntitiesPacket(fakePlayer.getId());
handler.broadcast(player, packetPlayerInfo, packetRemovePlayer);
for(var i : extraEntities.values()) {
var removePacket = new ClientboundRemoveEntitiesPacket(i);
handler.broadcast(player, removePacket);
}
}
public void addExtraEntity(Class<? extends Cosmetic> type, int id) {
extraEntities.put(type, id);
}
public void removeExtraEntity(Class<? extends Cosmetic> type) {
var id = extraEntities.remove(type);
if(id != null) {
var removePacket = new ClientboundRemoveEntitiesPacket(id);
handler.broadcast(player, removePacket);
}
}
public int getEntityId() {
return fakePlayer.getId();
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>MCCosmetics-v1_19_R2</artifactId>
<parent>
<groupId>io.lumine</groupId>
<artifactId>MCCosmetics-Plugin</artifactId>
<version>${mccosmetics.version}</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
<srgIn>org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.2</version>
<configuration>
<updatePomFile>true</updatePomFile>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>package</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>lumine</id>
<url>https://mvn.lumine.io/repository/maven/</url>
</repository>
<repository>
<id>paper-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>
<dependencies>
<!-- Modules -->
<dependency>
<groupId>io.lumine</groupId>
<artifactId>LumineUtils</artifactId>
<version>${lumineutils.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MCCosmetics</artifactId>
<version>${project.parent.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19.3-R0.1-SNAPSHOT</version>
<classifier>shaded</classifier>
<scope>provided</scope>
</dependency>
<!-- Spigot NMS version -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.19.3-R0.1-SNAPSHOT</version>
<classifier>remapped-mojang</classifier>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>3.11.50</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId>
<version>1.0.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta4</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package io.lumine.cosmetics.nms;
import com.google.common.collect.Maps;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.Cosmetic;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.api.players.wardrobe.WardrobeTracker;
import io.lumine.cosmetics.managers.back.BackAccessory;
import io.lumine.cosmetics.managers.gestures.Gesture;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.managers.offhand.Offhand;
import io.lumine.cosmetics.managers.sprays.Spray;
import io.lumine.cosmetics.nms.cosmetic.VolatileCosmeticHelper;
import io.lumine.cosmetics.nms.v1_19_R2.cosmetic.*;
import io.lumine.cosmetics.nms.v1_19_R2.network.VolatileChannelHandler;
import io.lumine.cosmetics.nms.v1_19_R2.wardrobe.MannequinEntity;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import lombok.Getter;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.entity.LevelEntityGetter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
public class VolatileCodeEnabled_v1_19_R2 implements VolatileCodeHandler {
@Getter private final MCCosmeticsPlugin plugin;
private final Map<Class<? extends Cosmetic>, VolatileCosmeticHelper> cosmeticHelpers = Maps.newConcurrentMap();
private Method entityGetter;
public VolatileCodeEnabled_v1_19_R2(MCCosmeticsPlugin plugin) {
this.plugin = plugin;
cosmeticHelpers.put(Hat.class, new VolatileHatImpl(plugin, this));
cosmeticHelpers.put(BackAccessory.class, new VolatileBackImpl(plugin, this));
cosmeticHelpers.put(Spray.class, new VolatileSprayImpl(plugin, this));
cosmeticHelpers.put(Offhand.class, new VolatileOffhandImpl(plugin, this));
cosmeticHelpers.put(Gesture.class, new VolatileGestureImpl(plugin, this));
for(var method : ServerLevel.class.getMethods()) {
if(LevelEntityGetter.class.isAssignableFrom(method.getReturnType()) && method.getReturnType() != LevelEntityGetter.class) {
entityGetter = method;
break;
}
}
}
@Override
public VolatileCosmeticHelper getCosmeticHelper(Class<? extends Cosmetic> tClass) {
return cosmeticHelpers.get(tClass);
}
@Override
public Collection<VolatileCosmeticHelper> getCosmeticHelpers() {
return cosmeticHelpers.values();
}
@Override
public void injectPlayer(Player player) {
ServerPlayer ply = ((CraftPlayer) player).getHandle();
VolatileChannelHandler cdh = new VolatileChannelHandler(player, this);
ChannelPipeline pipeline = ply.connection.getConnection().channel.pipeline();
for (String name : pipeline.toMap().keySet()) {
if (pipeline.get(name) instanceof Connection) {
pipeline.addBefore(name, "mc_cosmetics_packet_handler", cdh);
break;
}
}
}
@Override
public void removePlayer(Player player) {
Channel channel = ((CraftPlayer) player).getHandle().connection.getConnection().channel;
channel.eventLoop().submit(() -> {
channel.pipeline().remove("mc_cosmetics_packet_handler");
return null;
});
}
public void broadcast(Packet<?>... packets) {
for(Player player : Bukkit.getOnlinePlayers()) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
}
public void broadcast(Player player, Packet<?>... packets) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
public void broadcastAroundAndSelf(Player wearer, Packet<?>... packets) {
final var level = ((CraftWorld) wearer.getWorld()).getHandle();
final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
if(trackedEntity == null) {
broadcast(wearer.getWorld(), packets);
return;
}
for(Packet<?> packet : packets)
trackedEntity.broadcastAndSend(packet);
}
public void broadcastAround(Player wearer, Packet<?>... packets) {
final var level = ((CraftWorld) wearer.getWorld()).getHandle();
final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
if(trackedEntity == null) {
broadcast(wearer.getWorld(), packets);
return;
}
for(Packet<?> packet : packets)
trackedEntity.broadcast(packet);
}
public void broadcast(World world, Packet<?>... packets) {
for(Player player : world.getPlayers()) {
var connection = ((CraftPlayer) player).getHandle().connection;
for(Packet<?> packet : packets) {
connection.send(packet);
}
}
}
public Entity getEntity(World world, int id) {
ServerLevel level = ((CraftWorld) world).getHandle();
try {
var getter = entityGetter != null ?
(LevelEntityGetter<net.minecraft.world.entity.Entity>) entityGetter.invoke(level) :
level.entityManager.getEntityGetter();
final var entity = getter.get(id);
return entity == null ? null : entity.getBukkitEntity();
} catch(Exception | Error ex) {
ex.printStackTrace();
return null;
}
}
@Override
public Mannequin createMannequin(WardrobeTracker tracker, Player player, Location location) {
return new MannequinEntity(tracker,this,player,location);
}
@Override
public void removeFakeEntity(int id) {
ClientboundRemoveEntitiesPacket packet = new ClientboundRemoveEntitiesPacket(id);
broadcast(packet);
}
@Override
public void setBodyYaw(LivingEntity entity, double yaw) {
((CraftLivingEntity) entity).getHandle().yBodyRot = (float) yaw;
}
@Override
public float getBodyYaw(LivingEntity entity) {
return ((CraftLivingEntity) entity).getHandle().yBodyRot;
}
}
package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.lumine.utils.logging.Log;
import io.lumine.utils.reflection.Reflector;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.network.protocol.game.ServerboundAcceptTeleportationPacket;
import net.minecraft.world.entity.EquipmentSlot;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class VolatileHatImpl implements VolatileEquipmentHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
//private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
public VolatileHatImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var opt = maybeEquipped.get().getCosmetic();
if(!(opt instanceof Hat hat))
return;
var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(maybeEquipped.get()));
//playerTracker.put(player.getEntityId(), player);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
if(!(cosmetic.getCosmetic() instanceof Hat hat)) {
return;
}
final var entityId = mannequin.getEntityId();
final var player = mannequin.getPlayer();
var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(cosmetic));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId,
List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcast(player, equipmentPacket);
}
@Override
public void unequipMannequin(Mannequin mannequin) {
final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public boolean read(Player sender, Object packet, boolean isCanceled) {
if(packet instanceof ServerboundAcceptTeleportationPacket) {
final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(sender);
if(profile == null || profile.isHidden(Hat.class))
return true;
handleSpawn(profile);
}
return true;
}
@Override
public List<Object> write(Player receiver, Object packet) {
/*
if(packet instanceof ClientboundAddPlayerPacket playerPacket) {
int id = playerPacket.getEntityId();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Hat.class)) {
handleSpawn(profile);
}
} else */
if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
int id = equipmentPacket.getEntity();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Hat.class)) {
modifyPacket(profile, equipmentPacket);
}
}
return null;
}
private Profile getProfile(Player receiver, int id) {
final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
if(!(entity instanceof Player player))
return null;
return plugin.getProfiles().getProfile(player);
}
public void handleSpawn(Profile profile) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var player = profile.getPlayer();
final var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
boolean foundHead = false;
for(var pair : slots) {
final EquipmentSlot slot = pair.getFirst();
if(slot == EquipmentSlot.HEAD) {
foundHead = true;
newSlots.add(Pair.of(pair.getFirst(), nmsItem));
} else {
newSlots.add(pair);
}
}
if(!foundHead) {
newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
}
refEq.set(packet, "c", newSlots);
}
}
package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
import io.lumine.cosmetics.api.players.CosmeticProfile;
import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
import io.lumine.cosmetics.managers.hats.Hat;
import io.lumine.cosmetics.managers.offhand.Offhand;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
import io.lumine.cosmetics.players.Profile;
import io.lumine.utils.reflection.Reflector;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.world.entity.EquipmentSlot;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class VolatileOffhandImpl implements VolatileEquipmentHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
public VolatileOffhandImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
@Override
public void apply(CosmeticProfile profile) {
if (profile == null)
return;
Player player = profile.getPlayer();
final var maybeEquipped = profile.getEquipped(Offhand.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic offhand))
return;
var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
playerTracker.put(player.getEntityId(), player);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
@Override
public void unapply(CosmeticProfile profile) {
final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
if(!(cosmetic.getCosmetic() instanceof Offhand offhand)) {
return;
}
final var entityId = mannequin.getEntityId();
final var player = mannequin.getPlayer();
var nmsHat = CraftItemStack.asNMSCopy(offhand.getCosmetic(cosmetic));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId, List.of(Pair.of(EquipmentSlot.OFFHAND, nmsHat)));
nmsHandler.broadcast(player, equipmentPacket);
}
@Override
public void unequipMannequin(Mannequin mannequin) {
final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
}
@Override
public List<Object> write(Player receiver, Object packet) {
if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
int id = equipmentPacket.getEntity();
Profile profile = getProfile(receiver, id);
if(profile != null && !profile.isHidden(Offhand.class))
modifyPacket(profile, equipmentPacket);
}
return null;
}
private Profile getProfile(Player receiver, int id) {
final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
if(!(entity instanceof Player player))
return null;
return plugin.getProfiles().getProfile(player);
}
public void handleSpawn(Profile profile) {
final var maybeEquipped = profile.getEquipped(Offhand.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic offhand))
return;
final var player = profile.getPlayer();
final var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
}
private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
final var maybeEquipped = profile.getEquipped(Hat.class);
if(maybeEquipped.isEmpty()) {
return;
}
var equip = maybeEquipped.get();
var opt = equip.getCosmetic();
if(!(opt instanceof ItemCosmetic hat)) {
return;
}
final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
boolean foundHead = false;
for(var pair : slots) {
final EquipmentSlot slot = pair.getFirst();
if(slot == EquipmentSlot.HEAD) {
foundHead = true;
newSlots.add(Pair.of(pair.getFirst(), nmsItem));
} else {
newSlots.add(pair);
}
}
if(!foundHead) {
newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
}
refEq.set(packet, "c", newSlots);
}
}
package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
import io.lumine.cosmetics.MCCosmeticsPlugin;
import io.lumine.cosmetics.managers.sprays.Spray;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
import io.lumine.cosmetics.nms.cosmetic.VolatileSprayHelper;
import lombok.Getter;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
public class VolatileSprayImpl implements VolatileSprayHelper {
@Getter private final MCCosmeticsPlugin plugin;
private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
public VolatileSprayImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
this.plugin = plugin;
this.nmsHandler = nmsHandler;
}
private static final ItemStack map = new ItemStack(Material.FILLED_MAP);
private static final MapMeta mapMeta = (MapMeta) map.getItemMeta();
@Override
public int drawSpray(Spray spray, Location location, BlockFace face, int rotation) {
var world = ((CraftWorld) location.getWorld()).getHandle();
var pos = new BlockPos(location.getX(), location.getY(), location.getZ());
var image = spray.getImage();
mapMeta.setMapId(image.getMapNumber());
map.setItemMeta(mapMeta);
var nmsMap = CraftItemStack.asNMSCopy(map);
final int dir = switch(face) {
case DOWN -> 0;
case UP -> 1;
case NORTH -> 2;
case SOUTH -> 3;
case WEST -> 4;
case EAST -> 5;
default -> 1;
};
var frame = new ItemFrame(world, pos, Direction.UP);
frame.setItem(nmsMap);
frame.setInvisible(true);
frame.setRotation(rotation);
var packetAdd = new ClientboundAddEntityPacket(frame, dir);
var packetData = new ClientboundSetEntityDataPacket(frame.getId(), frame.getEntityData().getNonDefaultValues());
var packetMap = constructMapPacket(image.getMapNumber(), image.getPixels());
nmsHandler.broadcast(packetAdd, packetData, packetMap);
return frame.getId();
}
private static final int startX = 0;
private static final int startY = 0;
private static final int mapWidth = 128;
private static final int mapHeight = 128;
private static final byte mapScale = 1;
private static final boolean mapLocked = true;
private ClientboundMapItemDataPacket constructMapPacket(int id, byte[] pixels) {
var mapData = constructMapData(startX, startY, mapWidth, mapHeight, pixels);
var packet = new ClientboundMapItemDataPacket(id, mapScale, mapLocked, null, mapData);
return packet;
}
private MapItemSavedData.MapPatch constructMapData(int startX, int startY, int width, int height, byte[] pixels) {
return new MapItemSavedData.MapPatch(startX, startY, width, height, pixels);
}
}
package io.lumine.cosmetics.nms.v1_19_R2.network;
import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import lombok.Getter;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
public class VolatileChannelHandler extends ChannelDuplexHandler {
private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
@Getter private final Player player;
public VolatileChannelHandler(Player player, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
this.player = player;
this.nmsHandler = nmsHandler;
}
@Override
public void write(ChannelHandlerContext ctx, Object obj, ChannelPromise promise) {
try {
List<Object> packets = new ArrayList<>();
for(final var helper : nmsHandler.getCosmeticHelpers()) {
final var writes = helper.write(player, obj);
if(writes != null)
packets.addAll(writes);
}
if(!packets.contains(obj))
super.write(ctx, obj, promise);
packets.remove(obj);
for (var p : packets) {
super.write(ctx, p, promise.channel().newPromise());
}
}catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
boolean isCanceled = false;
for(final var helper : nmsHandler.getCosmeticHelpers()) {
isCanceled |= !helper.read(player, obj, isCanceled);
}
if(!isCanceled)
super.channelRead(ctx, obj);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment