Compare commits
	
		
			No commits in common. "e61ff12b4f5921f0bbde1e3b3caa578618b99226" and "162806811e937ff9d360280682058dad92e017a3" have entirely different histories.
		
	
	
		
			e61ff12b4f
			...
			162806811e
		
	
		
					 30 changed files with 361 additions and 514 deletions
				
			
		| 
						 | 
					@ -43,14 +43,6 @@ public interface PropertyHolder {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void setItemProperty(EntityProperty<?> key, ItemStack value);
 | 
					    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
 | 
					     * 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
 | 
					     * @return The number of lines in the hologram
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    int lineCount();
 | 
					    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 {
 | 
					runServer {
 | 
				
			||||||
    javaLauncher = javaToolchains.launcherFor {
 | 
					    javaLauncher = javaToolchains.launcherFor {
 | 
				
			||||||
        languageVersion = JavaLanguageVersion.of(21)
 | 
					        languageVersion = JavaLanguageVersion.of(17)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    minecraftVersion "1.20.6"
 | 
					    minecraftVersion "1.20.4"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
processResources {
 | 
					processResources {
 | 
				
			||||||
| 
						 | 
					@ -32,17 +32,18 @@ publishing {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    compileOnly "me.clip:placeholderapi:2.11.5" // Placeholder support
 | 
					    compileOnly "me.clip:placeholderapi:2.11.5" // Placeholder support
 | 
				
			||||||
    implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
 | 
					    compileOnly "com.google.code.gson:gson:2.10.1" // JSON parsing
 | 
				
			||||||
    implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
 | 
					    compileOnly "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
 | 
				
			||||||
    implementation "me.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
 | 
					    compileOnly "me.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
 | 
				
			||||||
    implementation "com.github.retrooper.packetevents:spigot:2.3.0" // Packets
 | 
					    compileOnly "com.github.retrooper.packetevents:spigot:2.3.0" // Packets
 | 
				
			||||||
    implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
 | 
					    compileOnly "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
 | 
				
			||||||
    implementation "lol.pyr:director-adventure:2.1.1" // Commands
 | 
					    compileOnly "lol.pyr:director-adventure:2.1.1" // Commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Fancy text library
 | 
					    // Fancy text library
 | 
				
			||||||
    implementation "net.kyori:adventure-platform-bukkit:4.3.2"
 | 
					    compileOnly "net.kyori:adventure-platform-bukkit:4.3.2"
 | 
				
			||||||
    implementation "net.kyori:adventure-text-minimessage:4.15.0"
 | 
					    compileOnly "net.kyori:adventure-text-minimessage:4.15.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    implementation "me.lucko:jar-relocator:1.7"
 | 
				
			||||||
    implementation project(":api")
 | 
					    implementation project(":api")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,7 +115,7 @@ public class ZNpcsPlus {
 | 
				
			||||||
        MojangSkinCache skinCache = new MojangSkinCache(configManager);
 | 
					        MojangSkinCache skinCache = new MojangSkinCache(configManager);
 | 
				
			||||||
        EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
 | 
					        EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
 | 
				
			||||||
        PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
 | 
					        PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
 | 
				
			||||||
        propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer);
 | 
					        propertyRegistry.registerTypes(packetFactory, textSerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ActionRegistry actionRegistry = new ActionRegistry();
 | 
					        ActionRegistry actionRegistry = new ActionRegistry();
 | 
				
			||||||
        NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
 | 
					        NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
 | 
				
			||||||
| 
						 | 
					@ -131,7 +131,7 @@ public class ZNpcsPlus {
 | 
				
			||||||
                scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
 | 
					                scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
 | 
				
			||||||
                propertyRegistry, skinCache, npcRegistry, bungeeConnector);
 | 
					                propertyRegistry, skinCache, npcRegistry, bungeeConnector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        log(ChatColor.WHITE + " * Registering components...");
 | 
					        log(ChatColor.WHITE + " * Registerring components...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bungeeConnector.registerChannel();
 | 
					        bungeeConnector.registerChannel();
 | 
				
			||||||
        shutdownTasks.add(bungeeConnector::unregisterChannel);
 | 
					        shutdownTasks.add(bungeeConnector::unregisterChannel);
 | 
				
			||||||
| 
						 | 
					@ -276,13 +276,10 @@ public class ZNpcsPlus {
 | 
				
			||||||
        registerEnumParser(manager, RabbitType.class, incorrectUsageMessage);
 | 
					        registerEnumParser(manager, RabbitType.class, incorrectUsageMessage);
 | 
				
			||||||
        registerEnumParser(manager, AttachDirection.class, incorrectUsageMessage);
 | 
					        registerEnumParser(manager, AttachDirection.class, incorrectUsageMessage);
 | 
				
			||||||
        registerEnumParser(manager, Sound.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"))
 | 
					        manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
 | 
				
			||||||
                .addSubcommand("center", new CenterCommand(npcRegistry))
 | 
					                .addSubcommand("center", new CenterCommand(npcRegistry))
 | 
				
			||||||
                .addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry))
 | 
					                .addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry))
 | 
				
			||||||
                .addSubcommand("clone", new CloneCommand(npcRegistry))
 | 
					 | 
				
			||||||
                .addSubcommand("reloadconfig", new ReloadConfigCommand(configManager))
 | 
					                .addSubcommand("reloadconfig", new ReloadConfigCommand(configManager))
 | 
				
			||||||
                .addSubcommand("toggle", new ToggleCommand(npcRegistry))
 | 
					                .addSubcommand("toggle", new ToggleCommand(npcRegistry))
 | 
				
			||||||
                .addSubcommand("skin", new SkinCommand(skinCache, npcRegistry, typeRegistry, propertyRegistry))
 | 
					                .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.adventure.command.CommandContext;
 | 
				
			||||||
import lol.pyr.director.common.message.Message;
 | 
					import lol.pyr.director.common.message.Message;
 | 
				
			||||||
 | 
					import lol.pyr.znpcsplus.libraries.LibraryLoader;
 | 
				
			||||||
import lol.pyr.znpcsplus.util.FileUtil;
 | 
					import lol.pyr.znpcsplus.util.FileUtil;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
 | 
					import net.kyori.adventure.text.minimessage.MiniMessage;
 | 
				
			||||||
| 
						 | 
					@ -31,6 +32,55 @@ public class ZNpcsPlusBootstrap extends JavaPlugin {
 | 
				
			||||||
            Bukkit.getPluginManager().disablePlugin(this);
 | 
					            Bukkit.getPluginManager().disablePlugin(this);
 | 
				
			||||||
            return;
 | 
					            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);
 | 
					        zNpcsPlus = new ZNpcsPlus(this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,4 +120,9 @@ public class ZNpcsPlusBootstrap extends JavaPlugin {
 | 
				
			||||||
    public boolean movedLegacy() {
 | 
					    public boolean movedLegacy() {
 | 
				
			||||||
        return legacy;
 | 
					        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.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MoveCommand implements CommandHandler {
 | 
					public class MoveCommand implements CommandHandler {
 | 
				
			||||||
    private final NpcRegistryImpl npcRegistry;
 | 
					    private final NpcRegistryImpl npcRegistry;
 | 
				
			||||||
| 
						 | 
					@ -28,7 +27,7 @@ public class MoveCommand implements CommandHandler {
 | 
				
			||||||
        Player player = context.ensureSenderIsPlayer();
 | 
					        Player player = context.ensureSenderIsPlayer();
 | 
				
			||||||
        NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
 | 
					        NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
 | 
				
			||||||
        npc.setLocation(new NpcLocation(player.getLocation()));
 | 
					        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));
 | 
					        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.Component;
 | 
				
			||||||
import net.kyori.adventure.text.event.ClickEvent;
 | 
					import net.kyori.adventure.text.event.ClickEvent;
 | 
				
			||||||
import net.kyori.adventure.text.format.NamedTextColor;
 | 
					import net.kyori.adventure.text.format.NamedTextColor;
 | 
				
			||||||
import org.bukkit.Location;
 | 
					 | 
				
			||||||
import org.bukkit.entity.Player;
 | 
					import org.bukkit.entity.Player;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class NearCommand implements CommandHandler {
 | 
					public class NearCommand implements CommandHandler {
 | 
				
			||||||
| 
						 | 
					@ -32,11 +30,8 @@ public class NearCommand implements CommandHandler {
 | 
				
			||||||
        double radius = Math.pow(raw, 2);
 | 
					        double radius = Math.pow(raw, 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<NpcEntryImpl> entries = npcRegistry.getAllModifiable().stream()
 | 
					        List<NpcEntryImpl> entries = npcRegistry.getAllModifiable().stream()
 | 
				
			||||||
                .filter(entry -> Objects.equals(entry.getNpc().getWorld(), player.getWorld()))
 | 
					                .filter(entry -> entry.getNpc().getWorld().equals(player.getWorld()))
 | 
				
			||||||
                .filter(entry -> {
 | 
					                .filter(entry -> entry.getNpc().getBukkitLocation().distanceSquared(player.getLocation()) < radius)
 | 
				
			||||||
                    Location loc = entry.getNpc().getBukkitLocation();
 | 
					 | 
				
			||||||
                    return loc != null && loc.distanceSquared(player.getLocation()) < radius;
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .collect(Collectors.toList());
 | 
					                .collect(Collectors.toList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (entries.isEmpty()) context.halt(Component.text("There are no npcs within " + raw + " blocks around you.", NamedTextColor.RED));
 | 
					        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 lol.pyr.znpcsplus.util.FoliaUtil;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import net.kyori.adventure.text.format.NamedTextColor;
 | 
					import net.kyori.adventure.text.format.NamedTextColor;
 | 
				
			||||||
import org.bukkit.Location;
 | 
					 | 
				
			||||||
import org.bukkit.entity.Player;
 | 
					import org.bukkit.entity.Player;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
| 
						 | 
					@ -27,9 +26,7 @@ public class TeleportCommand implements CommandHandler {
 | 
				
			||||||
        context.setUsage(context.getLabel() + " teleport <id>");
 | 
					        context.setUsage(context.getLabel() + " teleport <id>");
 | 
				
			||||||
        Player player = context.ensureSenderIsPlayer();
 | 
					        Player player = context.ensureSenderIsPlayer();
 | 
				
			||||||
        NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
 | 
					        NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
 | 
				
			||||||
        Location location = npc.getBukkitLocation();
 | 
					        FoliaUtil.teleport(player, npc.getBukkitLocation());
 | 
				
			||||||
        if (location == null) context.halt("Unable to teleport to NPC, the world is not loaded!");
 | 
					 | 
				
			||||||
        FoliaUtil.teleport(player, location);
 | 
					 | 
				
			||||||
        context.send(Component.text("Teleported to NPC!", NamedTextColor.GREEN));
 | 
					        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.DataImporter;
 | 
				
			||||||
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTrait;
 | 
					import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTrait;
 | 
				
			||||||
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTraitsRegistry;
 | 
					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.entity.EntityPropertyRegistryImpl;
 | 
				
			||||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
 | 
					import lol.pyr.znpcsplus.npc.NpcEntryImpl;
 | 
				
			||||||
import lol.pyr.znpcsplus.npc.NpcImpl;
 | 
					import lol.pyr.znpcsplus.npc.NpcImpl;
 | 
				
			||||||
| 
						 | 
					@ -53,7 +52,7 @@ public class CitizensImporter implements DataImporter {
 | 
				
			||||||
        this.propertyRegistry = propertyRegistry;
 | 
					        this.propertyRegistry = propertyRegistry;
 | 
				
			||||||
        this.skinCache = skinCache;
 | 
					        this.skinCache = skinCache;
 | 
				
			||||||
        this.dataFile = dataFile;
 | 
					        this.dataFile = dataFile;
 | 
				
			||||||
        this.traitsRegistry = new CitizensTraitsRegistry(propertyRegistry, skinCache, taskScheduler, textSerializer);
 | 
					        this.traitsRegistry = new CitizensTraitsRegistry(typeRegistry, propertyRegistry, skinCache);
 | 
				
			||||||
        this.npcRegistry = npcRegistry;
 | 
					        this.npcRegistry = npcRegistry;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,12 +81,11 @@ public class CitizensImporter implements DataImporter {
 | 
				
			||||||
                world = Bukkit.getWorlds().get(0).getName();
 | 
					                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));
 | 
					            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");
 | 
					            ConfigurationSection traits = npcSection.getConfigurationSection("traits");
 | 
				
			||||||
            if (traits != null) {
 | 
					            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)) {
 | 
					                for (String traitName : traits.getKeys(false)) {
 | 
				
			||||||
                    Object trait = traits.get(traitName);
 | 
					                    Object trait = traits.get(traitName);
 | 
				
			||||||
                    CitizensTrait citizensTrait = traitsRegistry.getByName(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();
 | 
					            String id = key.toLowerCase();
 | 
				
			||||||
            while (npcRegistry.getById(id) != null) {
 | 
					            while (npcRegistry.getById(id) != null) {
 | 
				
			||||||
                id += "_"; // TODO: make a backup of the old npc instead
 | 
					                id += "_"; // TODO: make a backup of the old npc instead
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,28 +1,24 @@
 | 
				
			||||||
package lol.pyr.znpcsplus.conversion.citizens.model;
 | 
					package lol.pyr.znpcsplus.conversion.citizens.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
 | 
					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.conversion.citizens.model.traits.*;
 | 
				
			||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
 | 
					 | 
				
			||||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
 | 
					import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
 | 
				
			||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class CitizensTraitsRegistry {
 | 
					public class CitizensTraitsRegistry {
 | 
				
			||||||
    private final HashMap<String, CitizensTrait> traitMap = new HashMap<>();
 | 
					    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 LocationTrait());
 | 
				
			||||||
 | 
					        register(new TypeTrait(typeRegistry));
 | 
				
			||||||
        register(new ProfessionTrait(propertyRegistry));
 | 
					        register(new ProfessionTrait(propertyRegistry));
 | 
				
			||||||
        register(new VillagerTrait(propertyRegistry));
 | 
					        register(new VillagerTrait(propertyRegistry));
 | 
				
			||||||
        register(new SkinTrait(propertyRegistry));
 | 
					        register(new SkinTrait(propertyRegistry));
 | 
				
			||||||
        register(new MirrorTrait(propertyRegistry, skinCache));
 | 
					        register(new MirrorTrait(propertyRegistry, skinCache));
 | 
				
			||||||
        register(new SkinLayersTrait(propertyRegistry));
 | 
					        register(new SkinLayersTrait(propertyRegistry));
 | 
				
			||||||
        register(new LookTrait(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) {
 | 
					    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();
 | 
					            HologramImpl hologram = npc.getHologram();
 | 
				
			||||||
            hologram.setOffset(model.getHologramHeight());
 | 
					            hologram.setOffset(model.getHologramHeight());
 | 
				
			||||||
            Collections.reverse(model.getHologramLines());
 | 
					 | 
				
			||||||
            for (String raw : model.getHologramLines()) {
 | 
					            for (String raw : model.getHologramLines()) {
 | 
				
			||||||
                Component line = textSerializer.deserialize(raw);
 | 
					                Component line = textSerializer.deserialize(raw);
 | 
				
			||||||
                hologram.addTextLineComponent(line);
 | 
					                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.nbt.NBTString;
 | 
				
			||||||
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
 | 
					import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
 | 
				
			||||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
 | 
					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.EntityProperty;
 | 
				
			||||||
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
 | 
					import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
 | 
				
			||||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
 | 
					import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
 | 
				
			||||||
| 
						 | 
					@ -87,8 +86,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
 | 
				
			||||||
        registerEnumSerializer(RabbitType.class);
 | 
					        registerEnumSerializer(RabbitType.class);
 | 
				
			||||||
        registerEnumSerializer(AttachDirection.class);
 | 
					        registerEnumSerializer(AttachDirection.class);
 | 
				
			||||||
        registerEnumSerializer(Sound.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);
 | 
					        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();
 | 
					        ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
 | 
				
			||||||
        boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
 | 
					        boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
 | 
				
			||||||
        boolean legacyNames = 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 DummyProperty<>("permission_required", false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register(new ForceBodyRotationProperty(plugin));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        register(new DummyProperty<>("player_knockback", false));
 | 
					        register(new DummyProperty<>("player_knockback", false));
 | 
				
			||||||
        register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
 | 
					        register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
 | 
				
			||||||
        register(new DummyProperty<>("player_knockback_distance", 0.4));
 | 
					        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));
 | 
					            register(new EncodedByteProperty<>("wolf_collar", DyeColor.BLUE, wolfIndex++, DyeColor::getDyeData));
 | 
				
			||||||
        } else register(new EncodedIntegerProperty<>("wolf_collar", DyeColor.RED, wolfIndex++, Enum::ordinal));
 | 
					        } else register(new EncodedIntegerProperty<>("wolf_collar", DyeColor.RED, wolfIndex++, Enum::ordinal));
 | 
				
			||||||
        if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) {
 | 
					        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");
 | 
					            linkProperties("tamed", "sitting");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
| 
						 | 
					@ -639,9 +634,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
 | 
				
			||||||
        // Frog
 | 
					        // Frog
 | 
				
			||||||
        register(new EncodedIntegerProperty<>("frog_variant", FrogVariant.TEMPERATE, 17, Enum::ordinal, EntityDataTypes.FROG_VARIANT));
 | 
					        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;
 | 
					        if (!ver.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Camel
 | 
					        // Camel
 | 
				
			||||||
| 
						 | 
					@ -651,14 +643,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Sniffer
 | 
					        // Sniffer
 | 
				
			||||||
        register(new CustomTypeProperty<>("sniffer_state", 17, SnifferState.IDLING, EntityDataTypes.SNIFFER_STATE, state -> com.github.retrooper.packetevents.protocol.entity.sniffer.SnifferState.valueOf(state.name())));
 | 
					        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) {
 | 
					    private void registerSerializer(PropertySerializer<?> serializer) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,11 +107,6 @@ public class PacketEntity implements PropertyHolder {
 | 
				
			||||||
        properties.setItemProperty(key, value);
 | 
					        properties.setItemProperty(key, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public ItemStack getItemProperty(EntityProperty<?> key) {
 | 
					 | 
				
			||||||
        return properties.getItemProperty(key);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Set<EntityProperty<?>> getAppliedProperties() {
 | 
					    public Set<EntityProperty<?>> getAppliedProperties() {
 | 
				
			||||||
        return properties.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);
 | 
					        for (HologramLine<?> line : lines) line.hide(player);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public long getRefreshDelay() {
 | 
					    public long getRefreshDelay() {
 | 
				
			||||||
        return refreshDelay;
 | 
					        return refreshDelay;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void setRefreshDelay(long refreshDelay) {
 | 
					    public void setRefreshDelay(long refreshDelay) {
 | 
				
			||||||
        this.refreshDelay = refreshDelay;
 | 
					        this.refreshDelay = refreshDelay;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
package lol.pyr.znpcsplus.hologram;
 | 
					package lol.pyr.znpcsplus.hologram;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
 | 
					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.EntityProperty;
 | 
				
			||||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
 | 
					import lol.pyr.znpcsplus.api.entity.PropertyHolder;
 | 
				
			||||||
import lol.pyr.znpcsplus.entity.PacketEntity;
 | 
					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");
 | 
					        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
 | 
					    @Override
 | 
				
			||||||
    public Set<EntityProperty<?>> getAppliedProperties() {
 | 
					    public Set<EntityProperty<?>> getAppliedProperties() {
 | 
				
			||||||
        return properties;
 | 
					        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.World;
 | 
				
			||||||
import org.bukkit.entity.Player;
 | 
					import org.bukkit.entity.Player;
 | 
				
			||||||
import org.bukkit.inventory.ItemStack;
 | 
					import org.bukkit.inventory.ItemStack;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
| 
						 | 
					@ -76,10 +75,8 @@ public class NpcImpl extends Viewable implements Npc {
 | 
				
			||||||
        return location;
 | 
					        return location;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public @Nullable Location getBukkitLocation() {
 | 
					    public Location getBukkitLocation() {
 | 
				
			||||||
        World world = getWorld();
 | 
					        return location.toBukkitLocation(getWorld());
 | 
				
			||||||
        if (world == null) return null;
 | 
					 | 
				
			||||||
        return location.toBukkitLocation(world);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void setLocation(NpcLocation location) {
 | 
					    public void setLocation(NpcLocation location) {
 | 
				
			||||||
| 
						 | 
					@ -115,7 +112,7 @@ public class NpcImpl extends Viewable implements Npc {
 | 
				
			||||||
        return uuid;
 | 
					        return uuid;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public @Nullable World getWorld() {
 | 
					    public World getWorld() {
 | 
				
			||||||
        return Bukkit.getWorld(worldName);
 | 
					        return Bukkit.getWorld(worldName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,7 +133,6 @@ public class NpcImpl extends Viewable implements Npc {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
 | 
					    private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
 | 
				
			||||||
        if (!type.isAllowedProperty(property)) return;
 | 
					 | 
				
			||||||
        for (Player viewer : getViewers()) {
 | 
					        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);
 | 
					            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));
 | 
					        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) {
 | 
					    public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
 | 
				
			||||||
        if (key == null) return;
 | 
					        if (key == null) return;
 | 
				
			||||||
        if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
 | 
					        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);
 | 
					        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() {
 | 
					    public Set<EntityProperty<?>> getAllProperties() {
 | 
				
			||||||
        return Collections.unmodifiableSet(propertyMap.keySet());
 | 
					        return Collections.unmodifiableSet(propertyMap.keySet());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,12 @@
 | 
				
			||||||
package lol.pyr.znpcsplus.npc;
 | 
					package lol.pyr.znpcsplus.npc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lol.pyr.znpcsplus.ZNpcsPlus;
 | 
					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.NpcEntry;
 | 
				
			||||||
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
 | 
					import lol.pyr.znpcsplus.api.npc.NpcRegistry;
 | 
				
			||||||
import lol.pyr.znpcsplus.api.npc.NpcType;
 | 
					import lol.pyr.znpcsplus.api.npc.NpcType;
 | 
				
			||||||
import lol.pyr.znpcsplus.config.ConfigManager;
 | 
					import lol.pyr.znpcsplus.config.ConfigManager;
 | 
				
			||||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
 | 
					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.ActionRegistry;
 | 
				
			||||||
import lol.pyr.znpcsplus.interaction.InteractionActionImpl;
 | 
					 | 
				
			||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
 | 
					import lol.pyr.znpcsplus.packets.PacketFactory;
 | 
				
			||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
 | 
					import lol.pyr.znpcsplus.scheduling.TaskScheduler;
 | 
				
			||||||
import lol.pyr.znpcsplus.storage.NpcStorage;
 | 
					import lol.pyr.znpcsplus.storage.NpcStorage;
 | 
				
			||||||
| 
						 | 
					@ -158,35 +153,6 @@ public class NpcRegistryImpl implements NpcRegistry {
 | 
				
			||||||
        return entry;
 | 
					        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
 | 
					    @Override
 | 
				
			||||||
    public void delete(String id) {
 | 
					    public void delete(String id) {
 | 
				
			||||||
        NpcEntryImpl entry = npcIdLookupMap.get(id.toLowerCase());
 | 
					        NpcEntryImpl entry = npcIdLookupMap.get(id.toLowerCase());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,18 +164,12 @@ public class NpcTypeImpl implements NpcType {
 | 
				
			||||||
                    addProperties("panda_eating");
 | 
					                    addProperties("panda_eating");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_TAMEABLE_ANIMAL) &&
 | 
					            if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_TAMEABLE_ANIMAL)) {
 | 
				
			||||||
                    !(version.isNewerThanOrEquals(ServerVersion.V_1_14) && type.equals(EntityTypes.OCELOT))) {
 | 
					 | 
				
			||||||
                addProperties("tamed", "sitting");
 | 
					                addProperties("tamed", "sitting");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (EntityTypes.isTypeInstanceOf(type, EntityTypes.GUARDIAN)) {
 | 
					            if (EntityTypes.isTypeInstanceOf(type, EntityTypes.GUARDIAN)) {
 | 
				
			||||||
                addProperties("is_retracting_spikes");
 | 
					                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);
 | 
					            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)
 | 
					        register(builder(p, "player", EntityTypes.PLAYER)
 | 
				
			||||||
                .setHologramOffset(-0.15D)
 | 
					                .setHologramOffset(-0.15D)
 | 
				
			||||||
                .addEquipmentProperties()
 | 
					                .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_cape", true)
 | 
				
			||||||
                .addDefaultProperty("skin_jacket", true)
 | 
					                .addDefaultProperty("skin_jacket", true)
 | 
				
			||||||
                .addDefaultProperty("skin_left_sleeve", true)
 | 
					                .addDefaultProperty("skin_left_sleeve", true)
 | 
				
			||||||
| 
						 | 
					@ -357,8 +357,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
 | 
				
			||||||
                .setHologramOffset(-1.675));
 | 
					                .setHologramOffset(-1.675));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register(builder(p, "warden", EntityTypes.WARDEN)
 | 
					        register(builder(p, "warden", EntityTypes.WARDEN)
 | 
				
			||||||
                .setHologramOffset(0.925)
 | 
					                .setHologramOffset(0.925));
 | 
				
			||||||
                .addProperties("warden_anger"));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!version.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
 | 
					        if (!version.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,12 +368,6 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
 | 
				
			||||||
        register(builder(p, "camel", EntityTypes.CAMEL)
 | 
					        register(builder(p, "camel", EntityTypes.CAMEL)
 | 
				
			||||||
                .setHologramOffset(0.25)
 | 
					                .setHologramOffset(0.25)
 | 
				
			||||||
                .addProperties("bashing", "camel_sitting"));
 | 
					                .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() {
 | 
					    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.
 | 
					 * pre-1.17 had all of their classes "flattened" into one package.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ReflectionPackage {
 | 
					public class ReflectionPackage {
 | 
				
			||||||
    private static final String VERSION = generateVersion();
 | 
					    private static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
 | 
				
			||||||
    public static final String BUKKIT = "org.bukkit.craftbukkit" + VERSION;
 | 
					    public static final String BUKKIT = "org.bukkit.craftbukkit." + VERSION;
 | 
				
			||||||
    private static final boolean flattened = !PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17);
 | 
					    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
 | 
					     * 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.
 | 
					     * 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 final String ENTITY = flattened ? MINECRAFT : joinWithDot(MINECRAFT, "world.entity");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String joinWithDot(String... parts) {
 | 
					    public static String joinWithDot(String... parts) {
 | 
				
			||||||
        return Arrays.stream(parts)
 | 
					        return Arrays.stream(parts)
 | 
				
			||||||
                .filter(Objects::nonNull)
 | 
					                .filter(Objects::nonNull)
 | 
				
			||||||
                .filter(p -> !p.isEmpty())
 | 
					                .filter(p -> p.length() != 0)
 | 
				
			||||||
                .collect(Collectors.joining("."));
 | 
					                .collect(Collectors.joining("."));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static String generateVersion() {
 | 
					 | 
				
			||||||
        String[] parts = Bukkit.getServer().getClass().getPackage().getName().split("\\.");
 | 
					 | 
				
			||||||
        if (parts.length > 3) return "." + parts[3];
 | 
					 | 
				
			||||||
        return "";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue