package net.seanomilk.tamablefoxes; import com.google.common.collect.Maps; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.types.Type; import net.seanomilk.tamablefoxes.command.CommandSpawnTamableFox; import net.seanomilk.tamablefoxes.io.FileManager; import net.minecraft.server.v1_15_R1.*; import net.wesjd.anvilgui.AnvilGUI; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.World; import org.bukkit.*; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftFox; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftItem; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Fox; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.player.*; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.java.JavaPlugin; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; public class TamableFoxes extends JavaPlugin implements Listener { public static final String ITEM_INSPECTOR_LORE = ChatColor.BLUE + "Tamable Fox Inspector"; public static final String TAG_TAME_FOX = "tameablefox"; private FileManager fileManager; private Map foxUUIDs; private EntityTypes customType; private boolean isOnLoad = true; private Map lookupCache; private FileManager.Config config, configFoxes; private Map waitingName; @Override public void onEnable() { String version = Bukkit.getServer().getClass().getPackage().getName(); if (!version.equals("org.bukkit.craftbukkit.v1_15_R1")) { Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.RED + "This plugin version only supports 1.15.1!"); getServer().getPluginManager().disablePlugin(this); return; } fileManager = new FileManager(this); this.config = fileManager.getConfig("config.yml"); this.config.copyDefaults(true).save(); this.configFoxes = fileManager.getConfig("foxes.yml"); this.configFoxes.copyDefaults(true).save(); this.getServer().getPluginManager().registerEvents(this, this); this.getCommand("spawntamablefox").setExecutor(new CommandSpawnTamableFox(this)); this.foxUUIDs = Maps.newHashMap(); this.lookupCache = Maps.newHashMap(); this.waitingName = Maps.newHashMap(); final Map> types = (Map>) DataConverterRegistry.a() .getSchema(DataFixUtils.makeKey(SharedConstants.getGameVersion().getWorldVersion())) .findChoiceType(DataConverterTypes.ENTITY).types(); types.put("minecraft:" + TAG_TAME_FOX, types.get("minecraft:fox")); EntityTypes.a a = EntityTypes.a.a((entityTypes, world) -> new EntityTamableFox(this, entityTypes, world), EnumCreatureType.AMBIENT); customType = IRegistry.a(IRegistry.ENTITY_TYPE, "tameablefox", a.a("tameablefox")); this.replaceFoxesOnLoad(); } @Override public void onDisable() { } private void replaceFoxesOnLoad() { int amountReplaced = 0; for (World world : Bukkit.getWorlds()) { Chunk[] loadedChunks = world.getLoadedChunks(); for (Chunk chunk : loadedChunks) { Entity[] entities = chunk.getEntities(); for (Entity entity : entities) { if (!(entity instanceof Fox)) continue; if (this.isTamableFox(entity)) continue; EntityTamableFox tamableFox = (EntityTamableFox) spawnTamableFox(entity.getLocation(), ((CraftFox) entity).getHandle().getFoxType()); final YamlConfiguration configuration = configFoxes.get(); // get living entity data if (configuration.isConfigurationSection("Foxes." + entity.getUniqueId())) { String owner = configuration.getString("Foxes." + entity.getUniqueId() + ".owner"); // make new data if (owner.equals("none")) { foxUUIDs.replace(tamableFox.getUniqueID(), null); configuration.set("Foxes." + tamableFox.getUniqueID() + ".owner", "none"); } else { foxUUIDs.replace(tamableFox.getUniqueID(), UUID.fromString(owner)); tamableFox.setTamed(true); configuration.set("Foxes." + tamableFox.getUniqueID() + ".owner", owner); } // set name if (configuration.isSet("Foxes." + entity.getUniqueId() + ".name")) { final String name = configuration.getString("Foxes." + entity.getUniqueId() + ".name"); configuration.set("Foxes." + tamableFox.getUniqueID() + ".name", name); tamableFox.setChosenName(name); } // delete old data configuration.set("Foxes." + entity.getUniqueId(), null); tamableFox.setSitting(((EntityFox) ((CraftEntity) entity).getHandle()).isSitting()); tamableFox.updateFox(); tamableFox.setAge(((CraftFox) entity).getAge()); ItemStack entityMouthItem = ((CraftFox) entity).getEquipment().getItemInMainHand(); if (entityMouthItem.getType() != Material.AIR) { tamableFox.setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(new ItemStack(entityMouthItem.getType(), 1))); } else { tamableFox.setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); } } else { configuration.set("Foxes." + tamableFox.getUniqueID() + ".owner", "none"); tamableFox.setAge(((CraftFox) entity).getAge()); ItemStack entityMouthItem = ((CraftFox) entity).getEquipment().getItemInMainHand(); if (entityMouthItem.getType() != Material.AIR) { tamableFox.setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(new ItemStack(entityMouthItem.getType(), 1))); } else { tamableFox.setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); } } entity.remove(); ++amountReplaced; } } } configFoxes.save(); this.isOnLoad = false; } public net.minecraft.server.v1_15_R1.Entity spawnTamableFox(Location location, net.minecraft.server.v1_15_R1.EntityFox.Type type) { WorldServer world = ((CraftWorld) location.getWorld()).getHandle(); net.minecraft.server.v1_15_R1.Entity spawnedEntity = customType.b(world, null, null, null, new BlockPosition(location.getX(), location.getY(), location.getZ()), null, false, false); world.addEntity(spawnedEntity); EntityFox fox = (EntityFox) spawnedEntity; fox.setFoxType(type); configFoxes.get().set("Foxes." + spawnedEntity.getUniqueID() + ".owner", "none"); fileManager.saveConfig("foxes.yml"); return fox; } @EventHandler public void onChunkLoad(ChunkLoadEvent event) { if (isOnLoad) return; Chunk chunk = event.getChunk(); Entity[] entities = chunk.getEntities(); for (Entity entity : entities) { if (entity instanceof Fox && !this.isTamableFox(entity)) { EntityTamableFox tamableFox = (EntityTamableFox) spawnTamableFox(entity.getLocation(), ((CraftFox) entity).getHandle().getFoxType()); final YamlConfiguration configuration = configFoxes.get(); // if has data if (configuration.isConfigurationSection("Foxes." + entity.getUniqueId())) { String owner = configuration.getString("Foxes." + entity.getUniqueId() + ".owner", "none"); // if has owner if (!owner.equals("none")) { foxUUIDs.replace(tamableFox.getUniqueID(), UUID.fromString(owner)); configuration.set("Foxes." + tamableFox.getUniqueID() + ".owner", owner); // set name if (configuration.isSet("Foxes." + entity.getUniqueId() + ".name")) { String name = configuration.getString("Foxes." + entity.getUniqueId() + ".name"); configuration.set("Foxes." + tamableFox.getUniqueID() + ".name", name); tamableFox.setChosenName(name); } // remove old data configuration.set("Foxes." + entity.getUniqueId(), null); tamableFox.setTamed(true); tamableFox.setSitting(((EntityFox) ((CraftEntity) entity).getHandle()).isSitting()); tamableFox.updateFox(); tamableFox.setAge(((CraftFox) entity).getAge()); ItemStack entityMouthItem = ((CraftFox) entity).getEquipment().getItemInMainHand(); if (entityMouthItem.getType() != Material.AIR) { tamableFox.setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(new ItemStack(entityMouthItem.getType(), 1))); } else { tamableFox.setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); } } } else { configuration.set("Foxes." + tamableFox.getUniqueID() + ".owner", "none"); tamableFox.setAge(((CraftFox) entity).getAge()); ItemStack entityMouthItem = ((CraftFox) entity).getEquipment().getItemInMainHand(); if (entityMouthItem.getType() != Material.AIR) { tamableFox.setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(new ItemStack(entityMouthItem.getType(), 1))); } else { tamableFox.setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); } } entity.remove(); } } } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); getFoxesOf(player).forEach(uuid -> { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) this.getEntityByUniqueId(uuid)).getHandle(); tamableFox.setOwner(((CraftPlayer) player).getHandle()); tamableFox.setTamed(true); foxUUIDs.replace(tamableFox.getUniqueID(), player.getUniqueId()); }); } @EventHandler public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent event) { Entity entity = event.getRightClicked(); Player player = event.getPlayer(); if (event.getHand() != EquipmentSlot.HAND) return; final ItemStack playerHand = player.getInventory().getItemInMainHand(); ItemMeta itemMeta = playerHand.getItemMeta(); if (itemMeta != null && playerHand.getType() == Material.REDSTONE_TORCH && itemMeta.hasLore() && itemMeta.getLore().contains(ITEM_INSPECTOR_LORE)) { List lore; if (!this.isTamableFox(entity)) { lore = Arrays.asList(ITEM_INSPECTOR_LORE, "UUID: " + entity.getUniqueId(), "Entity ID: " + entity.getEntityId()); } else { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) entity).getHandle(); if (tamableFox.getOwner() == null) { lore = Arrays.asList(ITEM_INSPECTOR_LORE, "UUID: " + entity.getUniqueId(), "Entity ID: " + entity.getEntityId(), "Tamable", "Owner: None"); } else { lore = Arrays.asList(ITEM_INSPECTOR_LORE, "UUID: " + entity.getUniqueId(), "Entity ID: " + entity.getEntityId(), "Tamable", "Owner: " + tamableFox.getOwner().getName()); } } // update item itemMeta.setLore(lore); playerHand.setItemMeta(itemMeta); player.getInventory().setItemInMainHand(playerHand); event.setCancelled(true); player.sendMessage(getPrefix() + "Inspected Entity."); return; } if (this.isTamableFox(entity)) { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) entity).getHandle(); if (tamableFox.isTamed() && tamableFox.getOwner() != null && playerHand.getType() != Material.SWEET_BERRIES) { // if this fox is theirs if (foxUUIDs.get(entity.getUniqueId()).equals(player.getUniqueId())) { if (player.isSneaking()) { net.minecraft.server.v1_15_R1.ItemStack wolfHolding = tamableFox.getEquipment(EnumItemSlot.MAINHAND); net.minecraft.server.v1_15_R1.ItemStack playerItemInHandNMS; if (wolfHolding.isEmpty()) { if (playerHand.getType() == Material.AIR) { return; } playerItemInHandNMS = CraftItemStack.asNMSCopy(playerHand); playerItemInHandNMS.setCount(1); tamableFox.setSlot(EnumItemSlot.MAINHAND, playerItemInHandNMS); playerHand.setAmount(playerHand.getAmount() - 1); player.getInventory().setItemInMainHand(playerHand); } else if (playerHand.getType() == Material.AIR) { entity.getWorld().dropItem(tamableFox.getBukkitEntity().getLocation().add(0.0D, 0.2D, 0.0D), CraftItemStack.asBukkitCopy(wolfHolding)); tamableFox.setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); } else { playerItemInHandNMS = CraftItemStack.asNMSCopy(playerHand); entity.getWorld().dropItem(tamableFox.getBukkitEntity().getLocation().add(0.0D, 0.2D, 0.0D), CraftItemStack.asBukkitCopy(wolfHolding)); playerItemInHandNMS.setCount(1); tamableFox.setSlot(EnumItemSlot.MAINHAND, playerItemInHandNMS); playerHand.setAmount(playerHand.getAmount() - 1); player.getInventory().setItemInMainHand(playerHand); } } else if (playerHand.getType() == Material.NAME_TAG) { ItemMeta handMeta = playerHand.getItemMeta(); tamableFox.setChosenName(handMeta.getDisplayName()); } else { tamableFox.toggleSitting(); } event.setCancelled(true); } } else if (playerHand.getType() == Material.CHICKEN) { if (Math.random() < 0.33D) { tamableFox.setTamed(true); tamableFox.setOwner(((CraftPlayer) player).getHandle()); foxUUIDs.replace(tamableFox.getUniqueID(), null, player.getUniqueId()); player.getWorld().spawnParticle(Particle.HEART, entity.getLocation(), 6, 0.5D, 0.5D, 0.5D); // Name process player.sendMessage(ChatColor.RED + ChatColor.BOLD.toString() + "You just tamed a wild fox!"); player.sendMessage(ChatColor.RED + "What do you want to call it?"); tamableFox.setChosenName("???"); new AnvilGUI.Builder() .onComplete((plr, text) -> { // Called when the inventory output slot is clicked if(!text.equals("")) { tamableFox.setChosenName(text); plr.sendMessage(getPrefix() + ChatColor.GREEN + text + " is perfect!"); return AnvilGUI.Response.close(); } else { return AnvilGUI.Response.text("Insert a name for your fox!"); } }) .preventClose() // Prevents the inventory from being closed .text("Fox name") // Sets the text the GUI should start with .plugin(this) // Set the plugin instance .open(player); // Opens the GUI for the player provided } else { player.getWorld().spawnParticle(Particle.SMOKE_NORMAL, entity.getLocation(), 10, 0.3D, 0.3D, 0.3D, 0.15D); } if (!player.getGameMode().equals(GameMode.CREATIVE)) { playerHand.setAmount(playerHand.getAmount() - 1); } event.setCancelled(true); } } } @EventHandler public void onPlayerBedEnterEvent(PlayerBedEnterEvent event) { Player player = event.getPlayer(); List dogsToSleep = getFoxesOf(player); for (UUID uuid : dogsToSleep) { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftFox) this.getEntityByUniqueId(uuid)).getHandle(); if (player.getWorld().getTime() > 12541L && player.getWorld().getTime() < 23460L) { tamableFox.setSleeping(true); } } } @EventHandler public void onPlayerBedLeaveEvent(PlayerBedLeaveEvent event) { Player player = event.getPlayer(); final List foxesOf = getFoxesOf(player); for (UUID uuid : foxesOf) { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftFox) this.getEntityByUniqueId(uuid)).getHandle(); tamableFox.setSleeping(false); } } @EventHandler public void onCreatureSpawnEvent(CreatureSpawnEvent event) { org.bukkit.entity.Entity entity = event.getEntity(); if (entity instanceof Fox && !this.isTamableFox(entity)) { net.minecraft.server.v1_15_R1.EntityFox.Type foxType = ((EntityFox) ((CraftEntity) entity).getHandle()).getFoxType(); spawnTamableFox(entity.getLocation(), foxType); event.setCancelled(true); } } public Entity getEntityByUniqueId(UUID uniqueId) { final Entity cacheEntity = lookupCache.get(uniqueId); if (cacheEntity != null) { if (cacheEntity.isDead()) lookupCache.remove(uniqueId); else return cacheEntity; } for (World world : Bukkit.getWorlds()) { for (Chunk loadedChunk : world.getLoadedChunks()) { for (Entity entity : loadedChunk.getEntities()) { if (entity.getUniqueId().equals(uniqueId)) { this.lookupCache.put(uniqueId, entity); return entity; } } } } return null; } @EventHandler public void onEntityDeathEvent(EntityDeathEvent event) { Entity entity = event.getEntity(); if (!this.isTamableFox(entity)) return; this.lookupCache.remove(entity.getUniqueId()); foxUUIDs.remove(entity.getUniqueId()); if (configFoxes.get().getConfigurationSection("Foxes").contains(entity.getUniqueId().toString())) { /*EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) entity).getHandle(); if (tamableFox.getOwner() != null && tamableFox.getOwner() instanceof Player) { Player owner = (Player) tamableFox.getOwner(); owner.sendMessage(getPrefix() + ChatColor.RED + tamableFox.getChosenName() + " was killed!"); }*/ configFoxes.get().set("Foxes." + entity.getUniqueId(), null); fileManager.saveConfig("foxes.yml"); } } public boolean isTamableFox(org.bukkit.entity.Entity entity) { return ((CraftEntity) entity).getHandle().getClass().getName().contains("TamableFox") || ((CraftEntity) entity).getHandle() instanceof EntityTamableFox; } public List getFoxesOf(Player player) { return foxUUIDs.entrySet().stream().filter(foxPlayer -> foxPlayer.getValue() != null && foxPlayer.getValue().equals(player.getUniqueId())) .map(Map.Entry::getKey).collect(Collectors.toList()); } public FileManager getFileManager() { return fileManager; } public FileManager.Config getMainConfig() { return config; } public FileManager.Config getConfigFoxes() { return configFoxes; } public EntityTypes getCustomType() { return customType; } public Map getFoxUUIDs() { return foxUUIDs; } public static String getPrefix() { //return ChatColor.translateAlternateColorCodes('&', (String) config.get("prefix")); return ChatColor.RED + "[Tamable Foxes] "; } public boolean isShowOwnerFoxName() { return (boolean) config.get("show-owner-in-fox-name"); } public boolean isShowNameTags() { return (boolean) config.get("show-nametags"); } public boolean isTamedAttackRabbitChicken() { return (boolean) config.get("tamed-behavior.no-attack-chicken-rabbit"); } }