chore: bump PacketEvents to 2.8.0 fix: baby property and a mem leak add: zombie related properties

This commit is contained in:
D3v1s0m 2025-05-15 23:33:06 +05:30
parent fe70e7ca9c
commit 95432d438e
No known key found for this signature in database
GPG key ID: FA1F770C7B1D40C1
42 changed files with 243 additions and 97 deletions

View file

@ -2,6 +2,8 @@ package lol.pyr.znpcsplus.api;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.ServicePriority;
/**
* Provider for the registered entity property registry instance
@ -30,10 +32,12 @@ public class NpcPropertyRegistryProvider {
* Internal method used to register the main instance of the plugin as the entity property registry provider
* You probably shouldn't call this method under any circumstances
*
* @param plugin Instance of the ZNPCsPlus plugin
* @param api Instance of the ZNPCsPlus entity property registry
*/
public static void register(EntityPropertyRegistry api) {
public static void register(Plugin plugin, EntityPropertyRegistry api) {
NpcPropertyRegistryProvider.registry = api;
Bukkit.getServicesManager().register(EntityPropertyRegistry.class, registry, plugin, ServicePriority.Normal);
}
/**

View file

@ -31,11 +31,25 @@ public interface EntityPropertyRegistry {
*/
<T> EntityProperty<T> getByName(String name, Class<T> type);
/**
* Register a dummy property that can be used to store unique information per npc<br>
* Note: Properties registered this way will be player-modifiable by default
*
* @param name The name of the new property
* @param type The type of the new property
* @deprecated Use {@link #registerDummy(String, Class, boolean)} instead
*/
@Deprecated
default void registerDummy(String name, Class<?> type) {
registerDummy(name, type, true);
}
/**
* Register a dummy property that can be used to store unique information per npc
*
* @param name The name of the new property
* @param type The type of the new property
* @param playerModifiable Whether this property can be modified by players using commands
*/
void registerDummy(String name, Class<?> type);
void registerDummy(String name, Class<?> type, boolean playerModifiable);
}

View file

@ -0,0 +1,11 @@
package lol.pyr.znpcsplus.util;
public enum ZombieType {
ZOMBIE,
FARMER,
LIBRARIAN,
PRIEST,
BLACKSMITH,
BUTCHER,
HUSK
}

View file

@ -19,7 +19,7 @@ dependencies {
compileOnly "me.clip:placeholderapi:2.11.6" // Placeholder support
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
implementation "com.github.retrooper:packetevents-spigot:2.7.0" // Packets
implementation "com.github.retrooper:packetevents-spigot:2.8.0" // Packets
implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
implementation "lol.pyr:director-adventure:2.1.2" // Commands

View file

@ -97,7 +97,7 @@ public class ZNpcsPlus {
skinCache = new MojangSkinCache(configManager, new File(getDataFolder(), "skins"));
propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
NpcPropertyRegistryProvider.register(propertyRegistry);
NpcPropertyRegistryProvider.register(bootstrap, propertyRegistry);
shutdownTasks.add(NpcPropertyRegistryProvider::unregister);
}
@ -130,7 +130,7 @@ public class ZNpcsPlus {
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer, scheduler);
propertyRegistry.registerTypes(packetFactory, textSerializer, scheduler);
BungeeConnector bungeeConnector = new BungeeConnector(bootstrap);
ActionRegistryImpl actionRegistry = new ActionRegistryImpl();

View file

@ -45,6 +45,7 @@ public class PropertySetCommand implements CommandHandler {
// TODO: find a way to do this better & rewrite this mess
if (!npc.getType().getAllowedProperties().contains(property)) context.halt(Component.text("Property " + property.getName() + " not allowed for npc type " + npc.getType().getName(), NamedTextColor.RED));
if (!property.isPlayerModifiable()) context.halt(Component.text("This property is not modifiable by players", NamedTextColor.RED));
Class<?> type = property.getType();
Object value;
String valueName;

View file

@ -47,16 +47,16 @@ public abstract class EntityPropertyImpl<T> implements EntityProperty<T> {
dependencies.add(property);
}
protected static <V> EntityData newEntityData(int index, EntityDataType<V> type, V value) {
return new EntityData(index, type, value);
protected static <V> EntityData<V> newEntityData(int index, EntityDataType<V> type, V value) {
return new EntityData<>(index, type, value);
}
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
Map<Integer, EntityData> map = new HashMap<>();
public List<EntityData<?>> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
Map<Integer, EntityData<?>> map = new HashMap<>();
apply(player, packetEntity, isSpawned, map);
for (EntityPropertyImpl<?> property : dependencies) property.apply(player, packetEntity, isSpawned, map);
return new ArrayList<>(map.values());
}
abstract public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties);
abstract public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties);
}

View file

@ -10,7 +10,6 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import lol.pyr.znpcsplus.ZNpcsPlusBootstrap;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
@ -109,7 +108,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
*/
}
public void registerTypes(ZNpcsPlusBootstrap plugin, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, TaskScheduler taskScheduler) {
public void registerTypes(PacketFactory packetFactory, LegacyComponentSerializer textSerializer, TaskScheduler taskScheduler) {
ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9);
@ -133,7 +132,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new DummyProperty<>("permission_required", false));
register(new ForceBodyRotationProperty(plugin, taskScheduler));
register(new ForceBodyRotationProperty(taskScheduler));
register(new DummyProperty<>("player_knockback", false));
register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
@ -192,7 +191,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) babyIndex = 11;
else babyIndex = 12;
if (ver.isOlderThan(ServerVersion.V_1_9)) {
register(new EncodedByteProperty<>("baby", false, babyIndex, obj -> (byte) (obj ? -1 : 0)));
register(new LegacyBabyProperty(babyIndex));
} else {
register(new BooleanProperty("baby", babyIndex, false, legacyBooleans));
}
@ -286,7 +285,9 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
else horseIndex = 16;
int horseEating = ver.isNewerThanOrEquals(ServerVersion.V_1_12) ? 0x10 : 0x20;
register(new BitsetProperty("is_tame", horseIndex, 0x02, false, legacyBooleans));
if (ver.isOlderThan(ServerVersion.V_1_21)) {
register(new BitsetProperty("is_saddled", horseIndex, 0x04, false, legacyBooleans));
}
register(new BitsetProperty("is_eating", horseIndex, horseEating, false, legacyBooleans));
register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans));
register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans));
@ -354,10 +355,14 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
// Chested Horse
if (ver.isOlderThan(ServerVersion.V_1_11)) {
register(new BitsetProperty("has_chest", horseIndex, 0x08, false, legacyBooleans));
linkProperties("is_saddled", "has_chest", "is_eating", "is_rearing", "has_mouth_open");
linkProperties("is_tame", "is_saddled", "has_chest", "is_eating", "is_rearing", "has_mouth_open");
} else {
register(new BooleanProperty("has_chest", horseVariantIndex, false, legacyBooleans));
linkProperties("is_saddled", "is_eating", "is_rearing", "has_mouth_open");
if (ver.isOlderThan(ServerVersion.V_1_21)){
linkProperties("is_tame", "is_saddled", "is_eating", "is_rearing", "has_mouth_open");
} else {
linkProperties("is_tame", "is_eating", "is_rearing", "has_mouth_open");
}
}
// Slime, Magma Cube and Phantom
@ -459,6 +464,33 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
else register(new EncodedIntegerProperty<>("skeleton_type", SkeletonType.NORMAL, ver.isOlderThan(ServerVersion.V_1_10) ? 11 : 12, Enum::ordinal));
}
// Zombie
int zombieIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) zombieIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) zombieIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) zombieIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) zombieIndex = 12;
else zombieIndex = 13;
if (ver.isOlderThan(ServerVersion.V_1_9)) {
register(new EncodedByteProperty<>("zombie_is_villager", false, zombieIndex++, b -> (byte) (b ? 1 : 0)));
} else if (ver.isOlderThan(ServerVersion.V_1_11)) {
register(new EncodedIntegerProperty<>("zombie_type", ZombieType.ZOMBIE, zombieIndex++, Enum::ordinal));
} else {
zombieIndex++; // Not a mistake, this is field unused in 1.11+
}
if (ver.isOlderThan(ServerVersion.V_1_9)) {
register(new EncodedByteProperty<>("is_converting", false, zombieIndex++, b -> (byte) (b ? 1 : 0)));
} else if (ver.isOlderThan(ServerVersion.V_1_11)) {
register(new BooleanProperty("is_converting", zombieIndex++, false, legacyBooleans));
}
if (ver.isNewerThanOrEquals(ServerVersion.V_1_9) && ver.isOlderThan(ServerVersion.V_1_14)) {
register(new BooleanProperty("zombie_hands_held_up", zombieIndex++, false, legacyBooleans));
}
if (ver.isNewerThanOrEquals(ServerVersion.V_1_13)) {
register(new BooleanProperty("zombie_becoming_drowned", zombieIndex++, false, legacyBooleans));
}
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
// Shulker
int shulkerIndex;
@ -758,8 +790,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
}
@Override
public void registerDummy(String name, Class<?> type) {
register(new DummyProperty<>(name, type));
public void registerDummy(String name, Class<?> type, boolean playerModifiable) {
register(new DummyProperty<>(name, type, playerModifiable));
}
public EntityPropertyImpl<?> getByName(String name) {

View file

@ -31,13 +31,22 @@ public class BitsetProperty extends EntityPropertyImpl<Boolean> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
EntityData<?> oldData = properties.get(index);
boolean enabled = entity.getProperty(this);
if (inverted) enabled = !enabled;
properties.put(index,
integer ? newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : (int) oldData.getValue()) | (enabled ? bitmask : 0)) :
newEntityData(index, EntityDataTypes.BYTE, (byte) ((oldData == null ? 0 : (byte) oldData.getValue()) | (enabled ? bitmask : 0))));
if (integer) {
int oldValue = 0;
if (oldData != null && oldData.getValue() instanceof Number) {
oldValue = ((Number) oldData.getValue()).intValue();
}
properties.put(index, newEntityData(index, EntityDataTypes.INT, oldValue | (enabled ? bitmask : 0)));
} else {
byte oldValue = 0;
if (oldData != null && oldData.getValue() instanceof Number) {
oldValue = ((Number) oldData.getValue()).byteValue();
}
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (oldValue | (enabled ? bitmask : 0))));
}
}
}

View file

@ -25,7 +25,7 @@ public class BooleanProperty extends EntityPropertyImpl<Boolean> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
boolean enabled = entity.getProperty(this);
if (inverted) enabled = !enabled;
if (legacy) properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (enabled ? 1 : 0)));

View file

@ -20,7 +20,7 @@ public class CamelSittingProperty extends EntityPropertyImpl<Boolean> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
boolean value = entity.getProperty(this);
if (value) {
properties.put(poseIndex, newEntityData(poseIndex, EntityDataTypes.ENTITY_POSE, EntityPose.SITTING));

View file

@ -22,7 +22,7 @@ public class CustomTypeProperty<T, U> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
properties.put(index, newEntityData(index, type, decoder.decode(entity.getProperty(this))));
}

View file

@ -1,7 +1,6 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
@ -16,20 +15,21 @@ import java.util.Optional;
public class DinnerboneProperty extends EntityPropertyImpl<Boolean> {
private final boolean optional;
private final Object serialized;
private final EntityDataType<?> type;
public DinnerboneProperty(boolean legacy, boolean optional) {
super("dinnerbone", false, Boolean.class);
this.optional = optional;
Component name = Component.text("Dinnerbone");
Object serialized = legacy ? AdventureSerializer.getLegacyGsonSerializer().serialize(name) :
this.serialized = legacy ? AdventureSerializer.serializer().legacy().serialize(name) :
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
this.serialized = optional ? Optional.of(serialized) : serialized;
this.type = optional ? EntityDataTypes.OPTIONAL_ADV_COMPONENT : EntityDataTypes.STRING;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(2, new EntityData(2, type, entity.getProperty(this) ? serialized : optional ? null : ""));
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
if (optional) {
properties.put(2, new EntityData<>(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, entity.getProperty(this) ? Optional.of((Component) serialized) : Optional.empty()));
} else {
properties.put(2, new EntityData<>(2, EntityDataTypes.STRING, entity.getProperty(this) ? (String) serialized : ""));
}
}
}

View file

@ -28,6 +28,6 @@ public class DummyProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
}
}

View file

@ -36,7 +36,7 @@ public class EncodedByteProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));

View file

@ -36,7 +36,7 @@ public class EncodedIntegerProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));

View file

@ -36,7 +36,7 @@ public class EncodedStringProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));

View file

@ -22,7 +22,7 @@ public class EntitySittingProperty extends EntityPropertyImpl<Boolean> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
boolean sitting = entity.getProperty(this);
if (sitting) {
if (entity.getVehicle() == null) {

View file

@ -22,7 +22,7 @@ public class EquipmentProperty extends EntityPropertyImpl<ItemStack> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
packetFactory.sendEquipment(player, entity, new Equipment(slot, entity.getProperty(this)));
}
}

View file

@ -1,7 +1,6 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.ZNpcsPlusBootstrap;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import org.bukkit.entity.Player;
@ -9,17 +8,15 @@ import org.bukkit.entity.Player;
import java.util.Map;
public class ForceBodyRotationProperty extends DummyProperty<Boolean> {
private final ZNpcsPlusBootstrap plugin;
private final TaskScheduler scheduler;
public ForceBodyRotationProperty(ZNpcsPlusBootstrap plugin, TaskScheduler scheduler) {
public ForceBodyRotationProperty(TaskScheduler scheduler) {
super("force_body_rotation", false);
this.plugin = plugin;
this.scheduler = scheduler;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
if (entity.getProperty(this)) {
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 2L);
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 6L);

View file

@ -19,10 +19,14 @@ public class GlowProperty extends EntityPropertyImpl<NamedColor> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
NamedColor value = entity.getProperty(this);
EntityData oldData = properties.get(0);
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
EntityData<?> oldData = properties.get(0);
// byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
byte oldValue = 0;
if (oldData != null && oldData.getValue() instanceof Number) {
oldValue = ((Number) oldData.getValue()).byteValue();
}
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (value == null ? 0 : 0x40))));
// the team is already created with the right glow color in the packet factory if the npc isnt spawned yet
if (isSpawned) {

View file

@ -18,10 +18,10 @@ public class HealthProperty extends EntityPropertyImpl<Float> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
float health = entity.getProperty(this);
health = (float) Attributes.MAX_HEALTH.sanitizeValue(health);
properties.put(index, new EntityData(index, EntityDataTypes.FLOAT, health));
properties.put(index, new EntityData<>(index, EntityDataTypes.FLOAT, health));
}
}

View file

@ -17,7 +17,7 @@ public class HologramItemProperty extends EntityPropertyImpl<ItemStack> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
properties.put(8, newEntityData(8, EntityDataTypes.ITEMSTACK, entity.getProperty(this)));
properties.put(5, newEntityData(5, EntityDataTypes.BOOLEAN, true));
}

View file

@ -18,9 +18,13 @@ public class HorseColorProperty extends EntityPropertyImpl<HorseColor> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
EntityData<?> oldData = properties.get(index);
HorseColor value = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.INT, value.ordinal() | (oldData == null ? 0 : ((int) oldData.getValue() & 0xFF00))));
int oldValue = (oldData != null && oldData.getValue() instanceof Integer) ? (Integer) oldData.getValue() : 0;
int newValue = value.ordinal() | (oldValue & 0xFF00);
properties.put(index, newEntityData(index, EntityDataTypes.INT, newValue));
}
}

View file

@ -18,9 +18,13 @@ public class HorseStyleProperty extends EntityPropertyImpl<HorseStyle> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
EntityData<?> oldData = properties.get(index);
HorseStyle value = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : ((int) oldData.getValue() & 0x00FF)) | (value.ordinal() << 8)));
int oldValue = (oldData != null && oldData.getValue() instanceof Integer) ? (Integer) oldData.getValue() : 0;
int newValue = (oldValue & 0x00FF) | (value.ordinal() << 8);
properties.put(index, newEntityData(index, EntityDataTypes.INT, newValue));
}
}

View file

@ -23,7 +23,7 @@ public class IntegerProperty extends EntityPropertyImpl<Integer> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
properties.put(index, legacy ?
newEntityData(index, EntityDataTypes.BYTE, (byte) entity.getProperty(this).intValue()) :
newEntityData(index, EntityDataTypes.INT, entity.getProperty(this)));

View file

@ -0,0 +1,29 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class LegacyBabyProperty extends EntityPropertyImpl<Boolean> {
private final int index;
public LegacyBabyProperty(int index) {
super("baby", false, Boolean.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
boolean isBaby = entity.getProperty(this);
if (entity.getType().equals(EntityTypes.ZOMBIE)) {
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (isBaby ? 1 : 0)));
} else {
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (isBaby ? -1 : 0)));
}
}
}

View file

@ -48,7 +48,7 @@ public class NBTProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
T value = entity.getProperty(this);
if (value == null && !allowNull) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));

View file

@ -27,14 +27,17 @@ public class NameProperty extends EntityPropertyImpl<Component> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
Component value = entity.getProperty(this);
if (value != null) {
value = PapiUtil.set(legacySerializer, player, value);
Object serialized = legacySerialization ? AdventureSerializer.getLegacyGsonSerializer().serialize(value) :
optional ? value : LegacyComponentSerializer.legacySection().serialize(value);
if (optional) properties.put(2, new EntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of(serialized)));
else properties.put(2, new EntityData(2, EntityDataTypes.STRING, serialized));
if (legacySerialization) {
properties.put(2, newEntityData(2, EntityDataTypes.STRING, AdventureSerializer.serializer().asJson(value)));
} else if (optional) {
properties.put(2, newEntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of(value)));
} else {
properties.put(2, newEntityData(2, EntityDataTypes.STRING, LegacyComponentSerializer.legacySection().serialize(value)));
}
}
if (legacySerialization) properties.put(3, newEntityData(3, EntityDataTypes.BYTE, (byte) (value != null ? 1 : 0)));

View file

@ -19,10 +19,10 @@ public class OptionalBlockPosProperty extends EntityPropertyImpl<Vector3i> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
Vector3i value = entity.getProperty(this);
if (value == null) properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty()));
else properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION,
if (value == null) properties.put(index, new EntityData<>(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty()));
else properties.put(index, new EntityData<>(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION,
Optional.of(new com.github.retrooper.packetevents.util.Vector3i(value.getX(), value.getY(), value.getZ()))));
}
}

View file

@ -1,7 +1,6 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
@ -17,31 +16,38 @@ import java.util.Optional;
public class RabbitTypeProperty extends EntityPropertyImpl<RabbitType> {
private final int index;
private final boolean legacyBooleans;
private final boolean optional;
private final Object serialized;
private final EntityDataType<?> type;
public RabbitTypeProperty(int index, boolean legacyBooleans, boolean legacyNames, boolean optional) {
super("rabbit_type", RabbitType.BROWN, RabbitType.class);
this.index = index;
this.legacyBooleans = legacyBooleans;
this.optional = optional;
Component name = Component.text("Toast");
Object serialized = legacyNames ? AdventureSerializer.getLegacyGsonSerializer().serialize(name) :
this.serialized = legacyNames ? AdventureSerializer.serializer().legacy().serialize(name) :
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
this.serialized = optional ? Optional.of(serialized) : serialized;
this.type = optional ? EntityDataTypes.OPTIONAL_ADV_COMPONENT : EntityDataTypes.STRING;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
RabbitType rabbitType = entity.getProperty(this);
if (rabbitType == null) return;
if (!rabbitType.equals(RabbitType.TOAST)) {
properties.put(index, legacyBooleans ?
newEntityData(index, EntityDataTypes.BYTE, (byte) rabbitType.getId()) :
newEntityData(index, EntityDataTypes.INT, rabbitType.getId()));
properties.put(2, new EntityData(2, type, null));
if (optional) {
properties.put(2, new EntityData<>(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.empty()));
} else {
properties.put(2, new EntityData(2, type, serialized));
properties.put(2, new EntityData<>(2, EntityDataTypes.STRING, ""));
}
} else {
if (optional) {
properties.put(2, newEntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of((Component) serialized)));
} else {
properties.put(2, newEntityData(2, EntityDataTypes.STRING, (String) serialized));
}
}
}
}

View file

@ -18,7 +18,7 @@ public class RotationProperty extends EntityPropertyImpl<Vector3f> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
Vector3f vec = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.ROTATION, new com.github.retrooper.packetevents.util.Vector3f(vec.getX(), vec.getY(), vec.getZ())));
}

View file

@ -18,7 +18,7 @@ public class TargetNpcProperty extends EntityPropertyImpl<NpcEntryImpl> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
NpcEntryImpl value = entity.getProperty(this);
if (value == null) return;
if (value.getNpc().getEntity().getEntityId() == entity.getEntityId()) return;

View file

@ -25,15 +25,14 @@ public class TropicalFishVariantProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
T value = entity.getProperty(this);
if (value == null) {
return;
}
EntityData oldData = properties.get(index);
if (value == null) return;
EntityData<?> oldData = properties.get(index);
TropicalFishVariant.Builder builder;
if (oldData != null && oldData.getType() == EntityDataTypes.INT && oldData.getValue() != null) {
int oldVal = (int) oldData.getValue();
if (oldData != null && oldData.getType() == EntityDataTypes.INT && oldData.getValue() instanceof Integer) {
int oldVal = (Integer) oldData.getValue();
builder = TropicalFishVariant.Builder.fromInt(oldVal);
} else {
builder = new TropicalFishVariant.Builder();

View file

@ -35,13 +35,13 @@ public class AttributeProperty extends EntityPropertyImpl<Double> {
}
@Override
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
public List<EntityData<?>> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
apply(player, packetEntity, isSpawned, Collections.emptyList());
return Collections.emptyList();
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
}
public void apply(Player player, PacketEntity entity, boolean isSpawned, List<WrapperPlayServerUpdateAttributes.Property> properties) {

View file

@ -21,8 +21,8 @@ public abstract class VillagerDataProperty<T> extends EntityPropertyImpl<T> {
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
EntityData<?> oldData = properties.get(index);
VillagerData old = oldData == null ? new VillagerData(VillagerTypes.PLAINS, VillagerProfessions.NONE, 1) : (VillagerData) oldData.getValue();
properties.put(index, newEntityData(index, EntityDataTypes.VILLAGER_DATA, apply(old, entity.getProperty(this))));
}

View file

@ -159,7 +159,7 @@ public class NpcImpl extends Viewable implements Npc {
private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
if (!type.isAllowedProperty(property)) return;
for (Player viewer : getViewers()) {
List<EntityData> data = property.applyStandalone(viewer, entity, true);
List<EntityData<?>> data = property.applyStandalone(viewer, entity, true);
if (!data.isEmpty()) packetFactory.sendMetadata(viewer, entity, data);
}
}
@ -232,7 +232,8 @@ public class NpcImpl extends Viewable implements Npc {
}
@Override
public void addAction(InteractionAction action) {
public void addAction(InteractionAction action) throws IllegalArgumentException {
if (action == null) throw new IllegalArgumentException("action can not be null");
actions.add(action);
}
@ -242,7 +243,8 @@ public class NpcImpl extends Viewable implements Npc {
}
@Override
public void editAction(int index, InteractionAction action) {
public void editAction(int index, InteractionAction action) throws IllegalArgumentException {
if (action == null) throw new IllegalArgumentException("action can not be null");
actions.set(index, action);
}

View file

@ -180,6 +180,11 @@ public class NpcTypeImpl implements NpcType {
if (version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.WOLF)) {
addProperties("wolf_variant");
if (version.isNewerThanOrEquals(ServerVersion.V_1_21)) {
addProperties("body");
} else {
addProperties("chestplate");
}
}
}
if (version.isNewerThanOrEquals(ServerVersion.V_1_21_4)) {
@ -187,6 +192,25 @@ public class NpcTypeImpl implements NpcType {
addProperties("creaking_crumbling");
}
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ZOMBIE)) {
if (version.isOlderThan(ServerVersion.V_1_9)) {
addProperties("zombie_is_villager");
} else if (version.isOlderThan(ServerVersion.V_1_11)) {
addProperties("zombie_type");
}
if (version.isOlderThan(ServerVersion.V_1_11)) {
addProperties("is_converting");
}
if (version.isNewerThanOrEquals(ServerVersion.V_1_9) && version.isOlderThan(ServerVersion.V_1_14)) {
addProperties("zombie_hands_held_up");
}
if (version.isNewerThanOrEquals(ServerVersion.V_1_13)) {
addProperties("zombie_becoming_drowned");
}
}
return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties), defaultProperties);
}
}

View file

@ -306,7 +306,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "trader_llama", EntityTypes.TRADER_LLAMA)
.setHologramOffset(-0.105)
.addProperties("llama_variant"));
.addProperties("carpet_color", "llama_variant", "body"));
register(builder(p, "wandering_trader", EntityTypes.WANDERING_TRADER)
.setHologramOffset(-0.025)

View file

@ -22,7 +22,7 @@ public interface PacketFactory {
void removeTeam(Player player, PacketEntity entity);
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendEquipment(Player player, PacketEntity entity, Equipment equipment);
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
void sendMetadata(Player player, PacketEntity entity, List<EntityData<?>> data);
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
void sendHandSwing(Player player, PacketEntity entity, boolean offHand);
void setPassengers(Player player, int vehicle, int... passengers);

View file

@ -135,13 +135,13 @@ public class V1_8PacketFactory implements PacketFactory {
@Override
public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> datas = new HashMap<>();
Map<Integer, EntityData<?>> datas = new HashMap<>();
for (EntityProperty<?> property : properties.getAppliedProperties()) ((EntityPropertyImpl<?>) property).apply(player, entity, false, datas);
sendMetadata(player, entity, new ArrayList<>(datas.values()));
}
@Override
public void sendMetadata(Player player, PacketEntity entity, List<EntityData> data) {
public void sendMetadata(Player player, PacketEntity entity, List<EntityData<?>> data) {
sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data));
}
@ -180,7 +180,7 @@ public class V1_8PacketFactory implements PacketFactory {
return future;
}
protected void add(Map<Integer, EntityData> map, EntityData data) {
protected void add(Map<Integer, EntityData<?>> map, EntityData<?> data) {
map.put(data.getIndex(), data);
}

View file

@ -52,6 +52,9 @@ public abstract class Viewable {
queueVisibilityTask(() -> {
UNSAFE_hideAll();
viewers.clear();
synchronized (all) {
all.removeIf(reference -> reference.get() == null || reference.get() == this);
}
});
}