Compare commits

..

No commits in common. "e61ff12b4f5921f0bbde1e3b3caa578618b99226" and "162806811e937ff9d360280682058dad92e017a3" have entirely different histories.

30 changed files with 361 additions and 514 deletions

@ -43,14 +43,6 @@ public interface PropertyHolder {
*/
void setItemProperty(EntityProperty<?> key, ItemStack value);
/**
* Weird fix which is sadly required in order to not decrease performance
* when using item properties, read https://github.com/Pyrbu/ZNPCsPlus/pull/129#issuecomment-1948777764
*
* @param key Unique key representing a property
*/
ItemStack getItemProperty(EntityProperty<?> key);
/**
* Method used to get a set of all of the property keys that this holder has a value for
*

@ -41,16 +41,4 @@ public interface Hologram {
* @return The number of lines in the hologram
*/
int lineCount();
/**
* Gets the refresh delay of the hologram
* @return The refresh delay of the hologram
*/
long getRefreshDelay();
/**
* Sets the refresh delay of the hologram
* @param delay The delay to set
*/
void setRefreshDelay(long delay);
}

@ -1,8 +0,0 @@
package lol.pyr.znpcsplus.util;
public enum ArmadilloState {
IDLE,
ROLLING,
SCARED,
UNROLLING
}

@ -1,23 +0,0 @@
package lol.pyr.znpcsplus.util;
public enum WoldVariant {
PALE(3),
SPOTTED(6),
SNOWY(5),
BLACK(1),
ASHEN(0),
RUSTY(4),
WOODS(8),
CHESTNUT(2),
STRIPED(7);
private final int id;
WoldVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
}

@ -6,9 +6,9 @@ plugins {
runServer {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
minecraftVersion "1.20.6"
minecraftVersion "1.20.4"
}
processResources {
@ -32,17 +32,18 @@ publishing {
dependencies {
compileOnly "me.clip:placeholderapi:2.11.5" // Placeholder support
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
implementation "me.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
implementation "com.github.retrooper.packetevents:spigot:2.3.0" // Packets
implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
implementation "lol.pyr:director-adventure:2.1.1" // Commands
compileOnly "com.google.code.gson:gson:2.10.1" // JSON parsing
compileOnly "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
compileOnly "me.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
compileOnly "com.github.retrooper.packetevents:spigot:2.3.0" // Packets
compileOnly "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
compileOnly "lol.pyr:director-adventure:2.1.1" // Commands
// Fancy text library
implementation "net.kyori:adventure-platform-bukkit:4.3.2"
implementation "net.kyori:adventure-text-minimessage:4.15.0"
compileOnly "net.kyori:adventure-platform-bukkit:4.3.2"
compileOnly "net.kyori:adventure-text-minimessage:4.15.0"
implementation "me.lucko:jar-relocator:1.7"
implementation project(":api")
}

@ -115,7 +115,7 @@ public class ZNpcsPlus {
MojangSkinCache skinCache = new MojangSkinCache(configManager);
EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer);
propertyRegistry.registerTypes(packetFactory, textSerializer);
ActionRegistry actionRegistry = new ActionRegistry();
NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
@ -131,7 +131,7 @@ public class ZNpcsPlus {
scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
propertyRegistry, skinCache, npcRegistry, bungeeConnector);
log(ChatColor.WHITE + " * Registering components...");
log(ChatColor.WHITE + " * Registerring components...");
bungeeConnector.registerChannel();
shutdownTasks.add(bungeeConnector::unregisterChannel);
@ -276,13 +276,10 @@ public class ZNpcsPlus {
registerEnumParser(manager, RabbitType.class, incorrectUsageMessage);
registerEnumParser(manager, AttachDirection.class, incorrectUsageMessage);
registerEnumParser(manager, Sound.class, incorrectUsageMessage);
registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage);
registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage);
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
.addSubcommand("center", new CenterCommand(npcRegistry))
.addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry))
.addSubcommand("clone", new CloneCommand(npcRegistry))
.addSubcommand("reloadconfig", new ReloadConfigCommand(configManager))
.addSubcommand("toggle", new ToggleCommand(npcRegistry))
.addSubcommand("skin", new SkinCommand(skinCache, npcRegistry, typeRegistry, propertyRegistry))

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.common.message.Message;
import lol.pyr.znpcsplus.libraries.LibraryLoader;
import lol.pyr.znpcsplus.util.FileUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
@ -31,6 +32,55 @@ public class ZNpcsPlusBootstrap extends JavaPlugin {
Bukkit.getPluginManager().disablePlugin(this);
return;
}
getLogger().info("Downloading and loading libraries, this might take a while if this is the first time you're launching the plugin");
LibraryLoader loader = new LibraryLoader(this, new File(getDataFolder(), "libraries"));
loader.addRelocation(decrypt("org..bstats"), "lol.pyr.znpcsplus.libraries.bstats");
loader.addRelocation(decrypt("me..robertlit..spigotresources"), "lol.pyr.znpcsplus.libraries.spigotresources");
loader.addRelocation(decrypt("net..kyori"), "lol.pyr.znpcsplus.libraries.kyori");
loader.addRelocation(decrypt("org..checkerframework"), "lol.pyr.znpcsplus.libraries.checkerframework");
loader.addRelocation(decrypt("com..google"), "lol.pyr.znpcsplus.libraries.google");
loader.addRelocation(decrypt("com..github..retrooper..packetevents"), "lol.pyr.znpcsplus.libraries.packetevents.api");
loader.addRelocation(decrypt("io..github..retrooper..packetevents"), "lol.pyr.znpcsplus.libraries.packetevents.impl");
loader.addRelocation(decrypt("org..yaml..snakeyaml"), "lol.pyr.znpcsplus.libraries.snakeyaml");
loader.addRelocation(decrypt("space..arim..dazzleconf"), "lol.pyr.znpcsplus.libraries.dazzleconf");
loader.addRelocation(decrypt("lol..pyr..director"), "lol.pyr.znpcsplus.libraries.command");
loader.loadLibrary(decrypt("com..google..guava"), "guava", "18.0");
loader.loadLibrary(decrypt("com..google..code..gson"), "gson", "2.10.1");
loader.loadLibrary(decrypt("org..bstats"), "bstats-base", "3.0.2");
loader.loadLibrary(decrypt("org..bstats"), "bstats-bukkit", "3.0.2");
loader.loadLibrary("me.robertlit", "SpigotResourcesAPI", "2.0", "https://repo.pyr.lol/releases");
loader.loadLibrary(decrypt("com..github..retrooper..packetevents"), "api", "2.3.0", "https://repo.codemc.io/repository/maven-releases/");
loader.loadLibrary(decrypt("com..github..retrooper..packetevents"), "spigot", "2.3.0", "https://repo.codemc.io/repository/maven-releases/");
loader.loadLibrary(decrypt("space..arim..dazzleconf"), "dazzleconf-core", "1.2.1");
loader.loadLibrary(decrypt("space..arim..dazzleconf"), "dazzleconf-ext-snakeyaml", "1.2.1");
loader.loadLibrary("org.yaml", "snakeyaml", "1.33");
loader.loadLibrary("lol.pyr", "director-adventure", "2.1.1", "https://repo.pyr.lol/releases");
loader.loadLibrary(decrypt("net..kyori"), "adventure-api", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-key", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-nbt", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-facet", "4.3.2");
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-api", "4.3.2");
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-bukkit", "4.3.2");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-minimessage", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-bungeecord", "4.3.2");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-gson", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-gson-legacy-impl", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-json", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-json-legacy-impl", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-legacy", "4.15.0");
loader.loadLibrary(decrypt("net..kyori"), "examination-api", "1.3.0");
loader.loadLibrary(decrypt("net..kyori"), "examination-string", "1.3.0");
loader.deleteUnloadedLibraries();
getLogger().info("Loaded " + loader.loadedLibraryCount() + " libraries!");
zNpcsPlus = new ZNpcsPlus(this);
}
@ -70,4 +120,9 @@ public class ZNpcsPlusBootstrap extends JavaPlugin {
public boolean movedLegacy() {
return legacy;
}
// Ugly hack because of https://github.com/johnrengelman/shadow/issues/232
private static String decrypt(String packageName) {
return packageName.replace("..", ".");
}
}

@ -1,44 +0,0 @@
package lol.pyr.znpcsplus.commands;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
public class CloneCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public CloneCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " clone <id> <new id>");
Player player = context.ensureSenderIsPlayer();
String id = context.popString();
if (npcRegistry.getById(id) == null) context.halt(Component.text("NPC with ID " + id + " does not exist.", NamedTextColor.RED));
String newId = context.popString();
if (npcRegistry.getById(newId) != null) context.halt(Component.text("NPC with ID " + newId + " already exists.", NamedTextColor.RED));
npcRegistry.clone(id, newId, player.getWorld(), new NpcLocation(player.getLocation()));
context.send(Component.text("Cloned NPC with ID " + id + " to ID " + newId + ".", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
return Collections.emptyList();
}
}

@ -13,7 +13,6 @@ import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class MoveCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
@ -28,7 +27,7 @@ public class MoveCommand implements CommandHandler {
Player player = context.ensureSenderIsPlayer();
NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
npc.setLocation(new NpcLocation(player.getLocation()));
if (!Objects.equals(npc.getWorld(), player.getWorld())) npc.setWorld(player.getWorld());
if (!npc.getWorld().equals(player.getWorld())) npc.setWorld(player.getWorld());
context.send(Component.text("NPC moved to your current location.", NamedTextColor.GREEN));
}

@ -10,11 +10,9 @@ import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class NearCommand implements CommandHandler {
@ -32,11 +30,8 @@ public class NearCommand implements CommandHandler {
double radius = Math.pow(raw, 2);
List<NpcEntryImpl> entries = npcRegistry.getAllModifiable().stream()
.filter(entry -> Objects.equals(entry.getNpc().getWorld(), player.getWorld()))
.filter(entry -> {
Location loc = entry.getNpc().getBukkitLocation();
return loc != null && loc.distanceSquared(player.getLocation()) < radius;
})
.filter(entry -> entry.getNpc().getWorld().equals(player.getWorld()))
.filter(entry -> entry.getNpc().getBukkitLocation().distanceSquared(player.getLocation()) < radius)
.collect(Collectors.toList());
if (entries.isEmpty()) context.halt(Component.text("There are no npcs within " + raw + " blocks around you.", NamedTextColor.RED));

@ -9,7 +9,6 @@ import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.util.FoliaUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.Collections;
@ -27,9 +26,7 @@ public class TeleportCommand implements CommandHandler {
context.setUsage(context.getLabel() + " teleport <id>");
Player player = context.ensureSenderIsPlayer();
NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
Location location = npc.getBukkitLocation();
if (location == null) context.halt("Unable to teleport to NPC, the world is not loaded!");
FoliaUtil.teleport(player, location);
FoliaUtil.teleport(player, npc.getBukkitLocation());
context.send(Component.text("Teleported to NPC!", NamedTextColor.GREEN));
}

@ -4,7 +4,6 @@ import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.conversion.DataImporter;
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTrait;
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTraitsRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.traits.TypeTrait;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
@ -53,7 +52,7 @@ public class CitizensImporter implements DataImporter {
this.propertyRegistry = propertyRegistry;
this.skinCache = skinCache;
this.dataFile = dataFile;
this.traitsRegistry = new CitizensTraitsRegistry(propertyRegistry, skinCache, taskScheduler, textSerializer);
this.traitsRegistry = new CitizensTraitsRegistry(typeRegistry, propertyRegistry, skinCache);
this.npcRegistry = npcRegistry;
}
@ -82,12 +81,11 @@ public class CitizensImporter implements DataImporter {
world = Bukkit.getWorlds().get(0).getName();
}
NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world, typeRegistry.getByName("armor_stand"), new NpcLocation(0, 0, 0, 0, 0));
npc.getType().applyDefaultProperties(npc);
npc.getHologram().addTextLineComponent(textSerializer.deserialize(name));
ConfigurationSection traits = npcSection.getConfigurationSection("traits");
if (traits != null) {
TypeTrait typeTrait = new TypeTrait(typeRegistry);
npc = typeTrait.apply(npc, traits.getString("type"));
npc.getType().applyDefaultProperties(npc);
for (String traitName : traits.getKeys(false)) {
Object trait = traits.get(traitName);
CitizensTrait citizensTrait = traitsRegistry.getByName(traitName);
@ -96,10 +94,6 @@ public class CitizensImporter implements DataImporter {
}
}
}
boolean nameVisible = Boolean.parseBoolean(npcSection.getString("metadata.name-visible", "true"));
if (nameVisible) {
npc.getHologram().addTextLineComponent(textSerializer.deserialize(name));
}
String id = key.toLowerCase();
while (npcRegistry.getById(id) != null) {
id += "_"; // TODO: make a backup of the old npc instead

@ -1,28 +1,24 @@
package lol.pyr.znpcsplus.conversion.citizens.model;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.traits.*;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.HashMap;
public class CitizensTraitsRegistry {
private final HashMap<String, CitizensTrait> traitMap = new HashMap<>();
public CitizensTraitsRegistry(EntityPropertyRegistry propertyRegistry, MojangSkinCache skinCache, TaskScheduler taskScheduler, LegacyComponentSerializer textSerializer) {
public CitizensTraitsRegistry(NpcTypeRegistry typeRegistry, EntityPropertyRegistry propertyRegistry, MojangSkinCache skinCache) {
register(new LocationTrait());
register(new TypeTrait(typeRegistry));
register(new ProfessionTrait(propertyRegistry));
register(new VillagerTrait(propertyRegistry));
register(new SkinTrait(propertyRegistry));
register(new MirrorTrait(propertyRegistry, skinCache));
register(new SkinLayersTrait(propertyRegistry));
register(new LookTrait(propertyRegistry));
register(new CommandTrait(taskScheduler));
register(new HologramTrait(textSerializer));
register(new EquipmentTrait(propertyRegistry));
register(new SpawnedTrait());
}
public CitizensTrait getByName(String name) {

@ -1,69 +0,0 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.interaction.InteractionType;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.interaction.InteractionActionImpl;
import lol.pyr.znpcsplus.interaction.consolecommand.ConsoleCommandAction;
import lol.pyr.znpcsplus.interaction.playercommand.PlayerCommandAction;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public class CommandTrait extends SectionCitizensTrait {
private final TaskScheduler scheduler;
public CommandTrait(TaskScheduler scheduler) {
super("commandtrait");
this.scheduler = scheduler;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
ConfigurationSection commands = section.getConfigurationSection("commands");
if (commands != null) {
Set<String> keys = commands.getKeys(false);
if (keys != null) {
for (String key : keys) {
ConfigurationSection commandSection = commands.getConfigurationSection(key);
String command = commandSection.getString("command");
String hand = commandSection.getString("hand", "BOTH");
InteractionType clickType = wrapClickType(hand);
boolean isPlayerCommand = commandSection.getBoolean("player", true);
int cooldown = commandSection.getInt("cooldown", 0);
int delay = commandSection.getInt("delay", 0);
if (command != null) {
InteractionActionImpl action;
if (isPlayerCommand) {
action = new PlayerCommandAction(scheduler, command, clickType, cooldown, delay);
} else {
action = new ConsoleCommandAction(scheduler, command, clickType, cooldown, delay);
}
npc.addAction(action);
}
}
}
}
return npc;
}
private InteractionType wrapClickType(String hand) {
if (hand == null) {
return InteractionType.ANY_CLICK;
}
switch (hand) {
case "RIGHT":
case "SHIFT_RIGHT":
return InteractionType.RIGHT_CLICK;
case "LEFT":
case "SHIFT_LEFT":
return InteractionType.LEFT_CLICK;
case "BOTH":
return InteractionType.ANY_CLICK;
}
throw new IllegalStateException();
}
}

@ -1,115 +0,0 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import com.google.common.io.BaseEncoding;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class EquipmentTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry propertyRegistry;
private final HashMap<String, EquipmentSlot> EQUIPMENT_SLOT_MAP = new HashMap<>();
public EquipmentTrait(EntityPropertyRegistry propertyRegistry) {
super("equipment");
this.propertyRegistry = propertyRegistry;
EQUIPMENT_SLOT_MAP.put("hand", EquipmentSlot.MAIN_HAND);
EQUIPMENT_SLOT_MAP.put("offhand", EquipmentSlot.OFF_HAND);
EQUIPMENT_SLOT_MAP.put("helmet", EquipmentSlot.HELMET);
EQUIPMENT_SLOT_MAP.put("chestplate", EquipmentSlot.CHEST_PLATE);
EQUIPMENT_SLOT_MAP.put("leggings", EquipmentSlot.LEGGINGS);
EQUIPMENT_SLOT_MAP.put("boots", EquipmentSlot.BOOTS);
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
for (String key : section.getKeys(false)) {
EquipmentSlot slot = EQUIPMENT_SLOT_MAP.get(key);
if (slot == null) {
continue;
}
ItemStack itemStack = parseItemStack(section.getConfigurationSection(key));
if (itemStack == null) {
continue;
}
EntityProperty<ItemStack> property = propertyRegistry.getByName(key, ItemStack.class);
npc.setProperty(property, itemStack);
}
return npc;
}
private ItemStack parseItemStack(ConfigurationSection section) {
Material material = null;
if (section.isString("type_key")) {
material = Material.getMaterial(section.getString("type_key").toUpperCase());
} else if (section.isString("type")) {
material = Material.matchMaterial(section.getString("type").toUpperCase());
} else if (section.isString("id")) {
material = Material.matchMaterial(section.getString("id").toUpperCase());
}
if (material == null || material == Material.AIR) {
return null;
}
org.bukkit.inventory.ItemStack itemStack = new org.bukkit.inventory.ItemStack(material, section.getInt("amount", 1),
(short) section.getInt("durability", section.getInt("data", 0)));
if (section.isInt("mdata")) {
//noinspection deprecation
itemStack.getData().setData((byte) section.getInt("mdata"));
}
if (section.isConfigurationSection("enchantments")) {
ConfigurationSection enchantments = section.getConfigurationSection("enchantments");
itemStack.addUnsafeEnchantments(deserializeEnchantments(enchantments));
}
if (section.isConfigurationSection("meta")) {
ItemMeta itemMeta = deserializeMeta(section.getConfigurationSection("meta"));
if (itemMeta != null) {
itemStack.setItemMeta(itemMeta);
}
}
return SpigotConversionUtil.fromBukkitItemStack(itemStack);
}
private Map<Enchantment, Integer> deserializeEnchantments(ConfigurationSection section) {
Map<Enchantment, Integer> enchantments = new HashMap<>();
for (String key : section.getKeys(false)) {
Enchantment enchantment = Enchantment.getByName(key);
if (enchantment == null) {
continue;
}
enchantments.put(enchantment, section.getInt(key));
}
return enchantments;
}
private ItemMeta deserializeMeta(ConfigurationSection section) {
if (section.isString("encoded-meta")) {
byte[] raw = BaseEncoding.base64().decode(section.getString("encoded-meta"));
try {
BukkitObjectInputStream inp = new BukkitObjectInputStream(new ByteArrayInputStream(raw));
ItemMeta meta = (ItemMeta) inp.readObject();
inp.close();
return meta;
} catch (IOException | ClassNotFoundException e1) {
e1.printStackTrace();
}
}
return null;
}
}

@ -1,36 +0,0 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class HologramTrait extends SectionCitizensTrait {
private final LegacyComponentSerializer textSerializer;
public HologramTrait(LegacyComponentSerializer textSerializer) {
super("hologramtrait");
this.textSerializer = textSerializer;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
ConfigurationSection linesSection = section.getConfigurationSection("lines");
if (linesSection != null) {
List<String> keys = new ArrayList<>(linesSection.getKeys(false));
for (int i = keys.size() - 1; i >= 0; i--) {
String line = linesSection.getConfigurationSection(keys.get(i)).getString("text");
if (line != null) {
Component component = textSerializer.deserialize(line);
npc.getHologram().addTextLineComponent(component);
}
}
}
return npc;
}
}

@ -1,20 +0,0 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.jetbrains.annotations.NotNull;
public class SpawnedTrait extends CitizensTrait {
public SpawnedTrait() {
super("spawned");
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, Object value) {
if (value != null) {
npc.setEnabled((boolean) value);
}
return npc;
}
}

@ -157,7 +157,6 @@ public class ZNpcImporter implements DataImporter {
HologramImpl hologram = npc.getHologram();
hologram.setOffset(model.getHologramHeight());
Collections.reverse(model.getHologramLines());
for (String raw : model.getHologramLines()) {
Component line = textSerializer.deserialize(raw);
hologram.addTextLineComponent(line);

@ -9,7 +9,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;
@ -87,8 +86,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerEnumSerializer(RabbitType.class);
registerEnumSerializer(AttachDirection.class);
registerEnumSerializer(Sound.class);
registerEnumSerializer(ArmadilloState.class);
registerEnumSerializer(WoldVariant.class);
registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.class);
@ -104,7 +101,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
*/
}
public void registerTypes(ZNpcsPlusBootstrap plugin, PacketFactory packetFactory, LegacyComponentSerializer textSerializer) {
public void registerTypes(PacketFactory packetFactory, LegacyComponentSerializer textSerializer) {
ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9);
@ -127,8 +124,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new DummyProperty<>("permission_required", false));
register(new ForceBodyRotationProperty(plugin));
register(new DummyProperty<>("player_knockback", false));
register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
register(new DummyProperty<>("player_knockback_distance", 0.4));
@ -416,7 +411,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new EncodedByteProperty<>("wolf_collar", DyeColor.BLUE, wolfIndex++, DyeColor::getDyeData));
} else register(new EncodedIntegerProperty<>("wolf_collar", DyeColor.RED, wolfIndex++, Enum::ordinal));
if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) {
register(new EncodedIntegerProperty<>("wolf_angry", false, wolfIndex++, b -> b ? 1 : 0));
register(new EncodedIntegerProperty<>("wolf_angry", false, wolfIndex, b -> b ? 1 : 0));
linkProperties("tamed", "sitting");
}
else {
@ -639,9 +634,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
// Frog
register(new EncodedIntegerProperty<>("frog_variant", FrogVariant.TEMPERATE, 17, Enum::ordinal, EntityDataTypes.FROG_VARIANT));
// Warden
register(new EncodedIntegerProperty<>("warden_anger", 0, 16, b -> Math.min(150, Math.max(0, b))));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
// Camel
@ -651,14 +643,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
// Sniffer
register(new CustomTypeProperty<>("sniffer_state", 17, SnifferState.IDLING, EntityDataTypes.SNIFFER_STATE, state -> com.github.retrooper.packetevents.protocol.entity.sniffer.SnifferState.valueOf(state.name())));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return;
// Armadillo
register(new CustomTypeProperty<>("armadillo_state", 17, ArmadilloState.IDLE, EntityDataTypes.ARMADILLO_STATE, state ->
com.github.retrooper.packetevents.protocol.entity.armadillo.ArmadilloState.valueOf(state.name())));
// Wolf
register(new EncodedIntegerProperty<>("wolf_variant", WoldVariant.PALE, wolfIndex, WoldVariant::getId, EntityDataTypes.WOLF_VARIANT));
}
private void registerSerializer(PropertySerializer<?> serializer) {

@ -107,11 +107,6 @@ public class PacketEntity implements PropertyHolder {
properties.setItemProperty(key, value);
}
@Override
public ItemStack getItemProperty(EntityProperty<?> key) {
return properties.getItemProperty(key);
}
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return properties.getAppliedProperties();

@ -1,24 +0,0 @@
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 org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Map;
public class ForceBodyRotationProperty extends DummyProperty<Boolean> {
private final ZNpcsPlusBootstrap plugin;
public ForceBodyRotationProperty(ZNpcsPlusBootstrap plugin) {
super("force_body_rotation", false);
this.plugin = plugin;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
Bukkit.getScheduler().runTaskLater(plugin, () -> entity.swingHand(player, false), 2L);
Bukkit.getScheduler().runTaskLater(plugin, () -> entity.swingHand(player, false), 6L);
}
}

@ -147,12 +147,10 @@ public class HologramImpl extends Viewable implements Hologram {
for (HologramLine<?> line : lines) line.hide(player);
}
@Override
public long getRefreshDelay() {
return refreshDelay;
}
@Override
public void setRefreshDelay(long refreshDelay) {
this.refreshDelay = refreshDelay;
}

@ -1,7 +1,6 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
@ -77,12 +76,6 @@ public class HologramLine<M> implements PropertyHolder {
throw new UnsupportedOperationException("Can't set properties on a hologram line");
}
@SuppressWarnings("unchecked")
@Override
public ItemStack getItemProperty(EntityProperty<?> key) {
return SpigotConversionUtil.toBukkitItemStack(((EntityProperty<com.github.retrooper.packetevents.protocol.item.ItemStack>) key).getDefaultValue());
}
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return properties;

@ -0,0 +1,119 @@
package lol.pyr.znpcsplus.libraries;
import me.lucko.jarrelocator.JarRelocator;
import me.lucko.jarrelocator.Relocation;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
public class LibraryLoader {
private final static Logger logger = Logger.getLogger("ZNPCsPlus Library Loader");
private final UrlClassLoaderAccess loaderAccess;
private final File librariesFolder;
private final Set<File> loadedLibraries = new HashSet<>();
private final List<Relocation> relocationRules = new ArrayList<>();
public LibraryLoader(Plugin plugin, File librariesFolder) {
loaderAccess = UrlClassLoaderAccess.create((URLClassLoader) plugin.getClass().getClassLoader());
this.librariesFolder = librariesFolder;
if (!librariesFolder.exists()) librariesFolder.mkdirs();
}
public void deleteUnloadedLibraries() {
File[] files = librariesFolder.listFiles();
if (files == null) return;
for (File file : files) if (!loadedLibraries.contains(file)) file.delete();
}
public void addRelocation(String pre, String post) {
relocationRules.add(new Relocation(pre, post));
}
public void loadSnapshotLibrary(String groupId, String artifactId, String version, String snapshotVersion, String repoUrl) {
try {
loadLibrary(groupId + ":" + artifactId + ":" + version,
getDependencyFile(groupId, artifactId, version),
getSnapshotDependencyUrl(groupId, artifactId, version, snapshotVersion, repoUrl));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public int loadedLibraryCount() {
return loadedLibraries.size();
}
public void loadLibrary(String groupId, String artifactId, String version) {
loadLibrary(groupId, artifactId, version, "https://repo1.maven.org/maven2");
}
public void loadLibrary(String groupId, String artifactId, String version, String repoUrl) {
try {
loadLibrary(groupId + ":" + artifactId + ":" + version,
getDependencyFile(groupId, artifactId, version),
getDependencyUrl(groupId, artifactId, version, repoUrl));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private void loadLibrary(String name, File file, URL url) {
if (!file.exists()) {
try (InputStream in = url.openStream()) {
File temp = new File(file.getParentFile(), file.getName() + ".temp");
Files.copy(in, temp.toPath());
new JarRelocator(temp, file, relocationRules).run();
temp.delete();
// logger.info("Downloaded library " + name);
} catch (IOException e) {
logger.severe("Failed to download library " + name);
e.printStackTrace();
}
}
try {
loaderAccess.addURL(file.toURI().toURL());
loadedLibraries.add(file);
// logger.info("Loaded library " + name);
} catch (Exception e) {
logger.severe("Failed to load library, plugin may not work correctly (" + name + ")");
e.printStackTrace();
}
}
private File getDependencyFile(String groupId, String artifactId, String version) {
return new File(librariesFolder, groupId.replace(".", "-") + "-"
+ artifactId.replace(".", "-") + "-"
+ version.replace(".", "-") + ".jar");
}
private static URL getDependencyUrl(String groupId, String artifactId, String version, String repoUrl) throws MalformedURLException {
String url = repoUrl.endsWith("/") ? repoUrl : repoUrl + "/";
url += groupId.replace(".", "/") + "/";
url += artifactId + "/";
url += version + "/";
url += artifactId + "-" + version + ".jar";
return new URL(url);
}
private static URL getSnapshotDependencyUrl(String groupId, String artifactId, String version, String snapshotVersion, String repoUrl) throws MalformedURLException {
String url = repoUrl.endsWith("/") ? repoUrl : repoUrl + "/";
url += groupId.replace(".", "/") + "/";
url += artifactId + "/";
url += version + "/";
url += artifactId + "-" + snapshotVersion + ".jar";
return new URL(url);
}
}

@ -0,0 +1,152 @@
package lol.pyr.znpcsplus.libraries;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
/**
* Provides access to {@link URLClassLoader}#addURL.
* From https://github.com/lucko/helper/blob/master/helper/src/main/java/me/lucko/helper/maven/URLClassLoaderAccess.java
*/
public abstract class UrlClassLoaderAccess {
/**
* Creates a {@link UrlClassLoaderAccess} for the given class loader.
*
* @param classLoader the class loader
* @return the access object
*/
static UrlClassLoaderAccess create(URLClassLoader classLoader) {
if (Reflection.isSupported()) {
return new Reflection(classLoader);
} else if (Unsafe.isSupported()) {
return new Unsafe(classLoader);
} else {
return Noop.INSTANCE;
}
}
private final URLClassLoader classLoader;
protected UrlClassLoaderAccess(URLClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Adds the given URL to the class loader.
*
* @param url the URL to add
*/
public abstract void addURL(@Nonnull URL url);
/**
* Accesses using reflection, not supported on Java 9+.
*/
private static class Reflection extends UrlClassLoaderAccess {
private static final Method ADD_URL_METHOD;
static {
Method addUrlMethod;
try {
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
} catch (Exception e) {
addUrlMethod = null;
}
ADD_URL_METHOD = addUrlMethod;
}
private static boolean isSupported() {
return ADD_URL_METHOD != null;
}
Reflection(URLClassLoader classLoader) {
super(classLoader);
}
@Override
public void addURL(@Nonnull URL url) {
try {
ADD_URL_METHOD.invoke(super.classLoader, url);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
*
* @author Vaishnav Anil (https://github.com/slimjar/slimjar)
*/
private static class Unsafe extends UrlClassLoaderAccess {
private static final sun.misc.Unsafe UNSAFE;
static {
sun.misc.Unsafe unsafe;
try {
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
} catch (Throwable t) {
unsafe = null;
}
UNSAFE = unsafe;
}
private static boolean isSupported() {
return UNSAFE != null;
}
private final Collection<URL> unopenedURLs;
private final Collection<URL> pathURLs;
@SuppressWarnings("unchecked")
Unsafe(URLClassLoader classLoader) {
super(classLoader);
Collection<URL> unopenedURLs;
Collection<URL> pathURLs;
try {
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
} catch (Throwable e) {
unopenedURLs = null;
pathURLs = null;
}
this.unopenedURLs = unopenedURLs;
this.pathURLs = pathURLs;
}
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
Field field = clazz.getDeclaredField(name);
long offset = UNSAFE.objectFieldOffset(field);
return UNSAFE.getObject(object, offset);
}
@Override
public void addURL(@Nonnull URL url) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
}
}
private static class Noop extends UrlClassLoaderAccess {
private static final Noop INSTANCE = new Noop();
private Noop() {
super(null);
}
@Override
public void addURL(@Nonnull URL url) {
throw new UnsupportedOperationException();
}
}
}

@ -20,7 +20,6 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
@ -76,10 +75,8 @@ public class NpcImpl extends Viewable implements Npc {
return location;
}
public @Nullable Location getBukkitLocation() {
World world = getWorld();
if (world == null) return null;
return location.toBukkitLocation(world);
public Location getBukkitLocation() {
return location.toBukkitLocation(getWorld());
}
public void setLocation(NpcLocation location) {
@ -115,7 +112,7 @@ public class NpcImpl extends Viewable implements Npc {
return uuid;
}
public @Nullable World getWorld() {
public World getWorld() {
return Bukkit.getWorld(worldName);
}
@ -136,7 +133,6 @@ 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);
if (!data.isEmpty()) packetFactory.sendMetadata(viewer, entity, data);
@ -168,12 +164,6 @@ public class NpcImpl extends Viewable implements Npc {
setProperty((EntityPropertyImpl<com.github.retrooper.packetevents.protocol.item.ItemStack>) key, SpigotConversionUtil.fromBukkitItemStack(value));
}
@SuppressWarnings("unchecked")
@Override
public ItemStack getItemProperty(EntityProperty<?> key) {
return SpigotConversionUtil.toBukkitItemStack(getProperty((EntityProperty<com.github.retrooper.packetevents.protocol.item.ItemStack>) key));
}
public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
if (key == null) return;
if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
@ -186,11 +176,6 @@ public class NpcImpl extends Viewable implements Npc {
setProperty((EntityPropertyImpl<T>) property, (T) value);
}
@SuppressWarnings("unchecked")
public <T> void UNSAFE_setProperty(EntityProperty<?> property, Object value) {
setProperty((EntityPropertyImpl<T>) property, (T) value);
}
public Set<EntityProperty<?>> getAllProperties() {
return Collections.unmodifiableSet(propertyMap.keySet());
}

@ -1,17 +1,12 @@
package lol.pyr.znpcsplus.npc;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.npc.NpcEntry;
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
import lol.pyr.znpcsplus.api.npc.NpcType;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.hologram.HologramItem;
import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.hologram.HologramText;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.interaction.InteractionActionImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.storage.NpcStorage;
@ -158,35 +153,6 @@ public class NpcRegistryImpl implements NpcRegistry {
return entry;
}
public NpcEntryImpl clone(String id, String newId, World newWorld, NpcLocation newLocation) {
NpcEntryImpl oldNpc = getById(id);
if (oldNpc == null) return null;
NpcEntryImpl newNpc = create(newId, newWorld, oldNpc.getNpc().getType(), newLocation);
newNpc.enableEverything();
for (EntityProperty<?> property : oldNpc.getNpc().getAllProperties()) {
newNpc.getNpc().UNSAFE_setProperty(property, oldNpc.getNpc().getProperty(property));
}
for (InteractionActionImpl action : oldNpc.getNpc().getActions()) {
newNpc.getNpc().addAction(action);
}
for (HologramLine<?> line : oldNpc.getNpc().getHologram().getLines()) {
if (line instanceof HologramText) {
HologramText text = (HologramText) line;
newNpc.getNpc().getHologram().addTextLineComponent(text.getValue());
}
else if (line instanceof HologramItem) {
HologramItem item = (HologramItem) line;
newNpc.getNpc().getHologram().addItemLinePEStack(item.getValue());
}
else throw new IllegalArgumentException("Unknown hologram line type during clone");
}
return newNpc;
}
@Override
public void delete(String id) {
NpcEntryImpl entry = npcIdLookupMap.get(id.toLowerCase());

@ -164,18 +164,12 @@ public class NpcTypeImpl implements NpcType {
addProperties("panda_eating");
}
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_TAMEABLE_ANIMAL) &&
!(version.isNewerThanOrEquals(ServerVersion.V_1_14) && type.equals(EntityTypes.OCELOT))) {
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_TAMEABLE_ANIMAL)) {
addProperties("tamed", "sitting");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.GUARDIAN)) {
addProperties("is_retracting_spikes");
}
if (version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.WOLF)) {
addProperties("wolf_variant");
}
}
return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties), defaultProperties);
}
}

@ -36,7 +36,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "player", EntityTypes.PLAYER)
.setHologramOffset(-0.15D)
.addEquipmentProperties()
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right", "force_body_rotation")
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right")
.addDefaultProperty("skin_cape", true)
.addDefaultProperty("skin_jacket", true)
.addDefaultProperty("skin_left_sleeve", true)
@ -357,8 +357,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(-1.675));
register(builder(p, "warden", EntityTypes.WARDEN)
.setHologramOffset(0.925)
.addProperties("warden_anger"));
.setHologramOffset(0.925));
if (!version.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
@ -369,12 +368,6 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "camel", EntityTypes.CAMEL)
.setHologramOffset(0.25)
.addProperties("bashing", "camel_sitting"));
if (!version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return;
register(builder(p, "armadillo", EntityTypes.ARMADILLO)
.setHologramOffset(-1.475)
.addProperties("armadillo_state"));
}
public Collection<NpcType> getAll() {

@ -14,27 +14,21 @@ import java.util.stream.Collectors;
* pre-1.17 had all of their classes "flattened" into one package.
*/
public class ReflectionPackage {
private static final String VERSION = generateVersion();
public static final String BUKKIT = "org.bukkit.craftbukkit" + VERSION;
private static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
public static final String BUKKIT = "org.bukkit.craftbukkit." + VERSION;
private static final boolean flattened = !PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17);
/**
* Check if the classes are flattened, if so we need to add the version string into the
* package string which is another quirk of the old server jars.
*/
public static final String MINECRAFT = joinWithDot("net.minecraft", flattened ? "server" + VERSION : "");
public static final String MINECRAFT = joinWithDot("net.minecraft", flattened ? "server." + VERSION : "");
public static final String ENTITY = flattened ? MINECRAFT : joinWithDot(MINECRAFT, "world.entity");
public static String joinWithDot(String... parts) {
return Arrays.stream(parts)
.filter(Objects::nonNull)
.filter(p -> !p.isEmpty())
.filter(p -> p.length() != 0)
.collect(Collectors.joining("."));
}
private static String generateVersion() {
String[] parts = Bukkit.getServer().getClass().getPackage().getName().split("\\.");
if (parts.length > 3) return "." + parts[3];
return "";
}
}