Clean up code, fix bugs, use Maven

- Switched to Maven for project compilation.
- Added config options to globally bypass permissions for crafting and/or wearing of all tiers.
- Now using Consumer for stuff that previously used the ugly is1_9 boolean.
- Switched to using ArmorEquipEvent for checking if players are allowed to wear something. It's much cleaner and includes some previously omitted equip events (such as from a dispenser).
- Code cleanup.
- Fixed issue where unbreakable armored elytras would still "break".
- Added MENDING enchantment to the default set of enchantments again.
- Using Maven shade for bStats and removed the old Metrics class.
This commit is contained in:
Pim van der Loos 2019-08-23 17:37:16 +02:00
parent 080b6c6dab
commit e906ddfdb3
No known key found for this signature in database
GPG Key ID: C16F020ADAE6D5A8
12 changed files with 351 additions and 681 deletions

160
pom.xml
View File

@ -1,21 +1,27 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nl.pim16aap2</groupId>
<artifactId>ArmoredElytra</artifactId>
<version>2.4.8-SNAPSHOT</version>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nl.pim16aap2</groupId>
<artifactId>ArmoredElytra</artifactId>
<version>2.4.10</version>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
</repository>
</repositories>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>CodeMC</id>
<url>https://repo.codemc.org/repository/maven-public</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
@ -23,24 +29,108 @@
<scope>provided</scope>
</dependency>
<!--bStats -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<dependency>
<groupId>com.github.tr7zw</groupId>
<artifactId>Item-NBT-API</artifactId>
<version>master-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<!--bStats -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.5</version>
<scope>compile</scope>
</dependency>
<!--
<dependency>
<groupId>com.github.Arnuh</groupId>
<artifactId>ArmorEquipEvent</artifactId>
<version>39fadc1393</version>
<scope>compile</scope>
</dependency>
-->
</dependencies>
<build>
<finalName>${project.name}</finalName>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>plugin.yml</include>
<include>config.yml</include>
<include>en_US.txt</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>nl.pim16aap2.armoredElytra</shadedPattern>
</relocation>
<!--<relocation>
<pattern>com.codingforcookies</pattern>
<shadedPattern>nl.pim16aap2.armoredElytra</shadedPattern>
</relocation>-->
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>deploy-artifact</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<ant antfile="${basedir}/mover.xml">
<target name="copyAll" />
</ant>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -3,6 +3,7 @@ package nl.pim16aap2.armoredElytra;
import java.util.Objects;
import java.util.logging.Level;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
@ -19,9 +20,13 @@ import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.Messages;
import nl.pim16aap2.armoredElytra.util.Metrics;
import nl.pim16aap2.armoredElytra.util.Update;
// TODO: Use this for NBT stuff: https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/
// TODO: Figure out if the config really does read the list of enchantments accurately. A bug report with a customized config seemed to load the default settings...
// TODO: Verify enchantments on startup. Remove them from the list if they're invalid.
// TODO: Don't delete the config file. Look at BigDoors.
public class ArmoredElytra extends JavaPlugin implements Listener
{
private NBTEditor nbtEditor;
@ -151,6 +156,16 @@ public class ArmoredElytra extends JavaPlugin implements Listener
elytraLore = (Objects.equals(elytraLore, new String("NONE")) ? null : elytraLore );
}
public boolean playerHasCraftPerm(Player player, ArmorTier armorTier)
{
return getConfigLoader().bypassCraftPerm() || player.hasPermission("armoredelytra.craft." + ArmorTier.getName(armorTier));
}
public boolean playerHasWearPerm(Player player, ArmorTier armorTier)
{
return getConfigLoader().bypassWearPerm() || player.hasPermission("armoredelytra.wear." + ArmorTier.getName(armorTier));
}
// Returns true if this is the latest version of this plugin.
public boolean isUpToDate()
{

View File

@ -18,7 +18,7 @@ import nl.pim16aap2.armoredElytra.util.ArmorTier;
public class CommandHandler implements CommandExecutor
{
private final ArmoredElytra plugin;
private final NBTEditor nbtEditor;
private final NBTEditor nbtEditor;
public CommandHandler(ArmoredElytra plugin, NBTEditor nbtEditor)
{

View File

@ -1,10 +1,13 @@
package nl.pim16aap2.armoredElytra.handlers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.function.Consumer;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
@ -13,14 +16,16 @@ import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.scheduler.BukkitRunnable;
import com.codingforcookies.armorequip.ArmorEquipEvent;
import com.codingforcookies.armorequip.ArmorListener;
import com.codingforcookies.armorequip.ArmorType;
import com.codingforcookies.armorequip.DispenserArmorListener;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
@ -32,48 +37,74 @@ import nl.pim16aap2.armoredElytra.util.XMaterial;
public class EventHandlers implements Listener
{
private final NBTEditor nbtEditor;
private final ArmoredElytra plugin;
private final boolean is1_9;
private final NBTEditor nbtEditor;
private final ArmoredElytra plugin;
private final Consumer<AnvilInventory> cleanAnvilInventory;
private final Consumer<Player> moveChestplateToInventory;
public EventHandlers(ArmoredElytra plugin, NBTEditor nbtEditor, boolean is1_9)
{
this.plugin = plugin;
this.plugin = plugin;
this.nbtEditor = nbtEditor;
this.is1_9 = is1_9;
}
// Remove item from player's chestplate slot and puts it in their normal inventory.
private void unenquipChestPlayer(Player p)
{
if (is1_9)
p.getInventory().getChestplate().setType(XMaterial.AIR.parseMaterial());
else
{
p.getInventory().addItem(p.getInventory().getChestplate());
p.getInventory().getChestplate().setAmount(0);
p.updateInventory();
}
}
// Clear the anvil's inventory (destroy all the items in all 3 slots (second slot is not emptied, when repairing you can safely give multiple items)).
private void cleanAnvil(AnvilInventory anvilInventory)
{
initializeArmorEquipEvent();
if (is1_9)
{
ItemStack air = new ItemStack(XMaterial.AIR.parseMaterial(), 1);
anvilInventory.setItem(0, air);
anvilInventory.setItem(1, air);
anvilInventory.setItem(2, air);
cleanAnvilInventory = this::cleanAnvilOld;
moveChestplateToInventory = this::moveChestplateToInventoryOld;
}
else
{
anvilInventory.getItem(0).setAmount(0);
anvilInventory.getItem(1).setAmount(anvilInventory.getItem(1).getAmount() - 1);
anvilInventory.getItem(2).setAmount(0);
cleanAnvilInventory = this::cleanAnvilNew;
moveChestplateToInventory = this::moveChestplateToInventoryNew;
}
}
private void initializeArmorEquipEvent()
{
Bukkit.getPluginManager().registerEvents(new ArmorListener(new ArrayList<String>()), plugin);
try
{
// Older versions did not have this event. So try to load it if possible, but
// don't worry
// about it not working.
Class.forName("org.bukkit.event.block.BlockDispenseArmorEvent");
plugin.getServer().getPluginManager().registerEvents(new DispenserArmorListener(), plugin);
}
catch (Exception ignored)
{
}
}
private void moveChestplateToInventoryOld(Player player)
{
player.getInventory().getChestplate().setType(XMaterial.AIR.parseMaterial());
}
private void moveChestplateToInventoryNew(Player player)
{
player.getInventory().addItem(player.getInventory().getChestplate());
player.getInventory().getChestplate().setAmount(0);
player.updateInventory();
}
// Clean 1.9 inventories.
private void cleanAnvilOld(AnvilInventory anvilInventory)
{
ItemStack air = new ItemStack(XMaterial.AIR.parseMaterial(), 1);
anvilInventory.setItem(0, air);
anvilInventory.setItem(1, air);
anvilInventory.setItem(2, air);
}
// Clean >=1.10 inventories.
private void cleanAnvilNew(AnvilInventory anvilInventory)
{
anvilInventory.getItem(0).setAmount(0);
anvilInventory.getItem(1).setAmount(anvilInventory.getItem(1).getAmount() - 1);
anvilInventory.getItem(2).setAmount(0);
}
// Check if the enchantment is allowed on elytras.
private boolean isAllowedEnchantment(Enchantment enchant)
{
@ -85,18 +116,20 @@ public class EventHandlers implements Listener
}
// Combine 2 maps of enchantments (and remove any invalid ones).
private Map<Enchantment, Integer> combineEnchantments(Map<Enchantment, Integer> enchantments0, Map<Enchantment, Integer> enchantments1)
private Map<Enchantment, Integer> combineEnchantments(Map<Enchantment, Integer> enchantments0,
Map<Enchantment, Integer> enchantments1)
{
enchantments0 = fixEnchantments(enchantments0);
Map<Enchantment, Integer> combined = new HashMap<>(fixEnchantments(enchantments0));
// If the second set of enchantments is null, the combined enchantments are just the first enchantments.
// If the second set of enchantments is null, the combined enchantments are just
// the first enchantments.
if (enchantments1 == null)
return combined;
enchantments1 = fixEnchantments(enchantments1);
enchantments1 = fixEnchantments(enchantments1);
// Loop through the enchantments of item1.
for (Map.Entry<Enchantment, Integer > entry : enchantments1.entrySet())
for (Map.Entry<Enchantment, Integer> entry : enchantments1.entrySet())
{
Integer enchantLevel = enchantments0.get(entry.getKey());
if (enchantLevel != null)
@ -123,11 +156,13 @@ public class EventHandlers implements Listener
int protVal0 = Util.getProtectionEnchantmentsVal(enchantments0);
int protVal1 = Util.getProtectionEnchantmentsVal(enchantments1);
// If they have different protection enchantments, keep enchantment1's enchantments
// And remove the protection enchantment from enchantments0. Yes, this system only works
// If they have different protection enchantments, keep enchantment1's
// enchantments
// And remove the protection enchantment from enchantments0. Yes, this system
// only works
// If there is 1 protection enchantment on
if (protVal0 != 0 && protVal1 != 0 && protVal0 != protVal1)
switch(protVal0)
switch (protVal0)
{
case 1:
combined.remove(Enchantment.PROTECTION_ENVIRONMENTAL);
@ -154,7 +189,7 @@ public class EventHandlers implements Listener
{
// Get the multiplier for the repair items.
double mult = 0.01;
if ( repairItem.getType().equals(Material.LEATHER))
if (repairItem.getType().equals(Material.LEATHER))
mult *= (100.0f / plugin.getConfigLoader().LEATHER_TO_FULL());
else if (repairItem.getType().equals(Material.GOLD_INGOT))
@ -191,18 +226,16 @@ public class EventHandlers implements Listener
return ret;
}
/*
* Valid inputs:
* - Elytra (armored or not) + chestplate -> Create Armored Elytra
* - Elytra (armored) + enchanted book -> Enchant
* - Elytra (armored) + its repair item -> Repair
* - Elytra (armored) + other elytra (armored) -> Combine (Enchant + Repair)
* ! Elytra (armored, !leather) + leather -> Block
*
* Ignoring:
* - Elytra (not armored) + !chestplate -> None
* - * + * -> None
*/
// Valid inputs:
// - Elytra (armored or not) + chestplate -> Create Armored Elytra
// - Elytra (armored) + enchanted book -> Enchant
// - Elytra (armored) + its repair item -> Repair
// - Elytra (armored) + other elytra (armored) -> Combine (Enchant + Repair)
// ! Elytra (armored, !leather) + leather -> Block
//
// Ignoring:
// - Elytra (not armored) + !chestplate -> None
// - * + * -> None
private Action isValidInput(ItemStack itemOne, ItemStack itemTwo)
{
if (itemOne == null || itemTwo == null)
@ -212,8 +245,8 @@ public class EventHandlers implements Listener
if (itemTwo.getType() == Material.ELYTRA && itemOne.getType() != Material.ELYTRA)
{
ItemStack tmp = itemOne;
itemOne = itemTwo;
itemTwo = tmp;
itemOne = itemTwo;
itemTwo = tmp;
}
if (itemOne.getType() != Material.ELYTRA)
@ -237,7 +270,8 @@ public class EventHandlers implements Listener
if (ArmorTier.getRepairItem(tier) == matTwo)
return Action.REPAIR;
// If the armored elytra is to be combined with another armored elytra of the same tier...
// If the armored elytra is to be combined with another armored elytra of the
// same tier...
if (nbtEditor.getArmorTier(itemTwo) == tier)
return Action.COMBINE;
@ -254,9 +288,9 @@ public class EventHandlers implements Listener
@EventHandler
private void onAnvilInventoryOpen(PrepareAnvilEvent event)
{
Player player = (Player) event.getView().getPlayer();
ItemStack itemA = event.getInventory().getItem(0);
ItemStack itemB = event.getInventory().getItem(1);
Player player = (Player) event.getView().getPlayer();
ItemStack itemA = event.getInventory().getItem(0);
ItemStack itemB = event.getInventory().getItem(1);
ItemStack result = null;
if (itemA != null && itemB != null)
@ -264,42 +298,43 @@ public class EventHandlers implements Listener
if (itemB.getType() == Material.ELYTRA && itemA.getType() != Material.ELYTRA)
{
result = itemA;
itemA = itemB;
itemB = result;
itemA = itemB;
itemB = result;
result = null;
}
// Check if there are items in both input slots.
if (itemA != null && itemB != null)
{
Action action = isValidInput(itemA, itemB);
ArmorTier newTier = ArmorTier.NONE;
ArmorTier curTier = nbtEditor.getArmorTier(itemA);
short durability = 0;
Action action = isValidInput(itemA, itemB);
ArmorTier newTier = ArmorTier.NONE;
ArmorTier curTier = nbtEditor.getArmorTier(itemA);
short durability = 0;
Map<Enchantment, Integer> enchantments = itemA.getEnchantments();
enchantments = fixEnchantments(enchantments);
enchantments = fixEnchantments(enchantments);
switch (action)
{
case REPAIR:
newTier = curTier;
durability = repairItem(itemA.getDurability(), itemB);
newTier = curTier;
durability = repairItem(itemA.getDurability(), itemB);
break;
case COMBINE:
newTier = curTier;
durability = (short) (- itemA.getType().getMaxDurability() - itemA.getDurability() - itemB.getDurability());
durability = durability < 0 ? 0 : durability;
enchantments = combineEnchantments(enchantments, itemB.getEnchantments());
newTier = curTier;
durability = (short) (-itemA.getType().getMaxDurability() - itemA.getDurability()
- itemB.getDurability());
durability = durability < 0 ? 0 : durability;
enchantments = combineEnchantments(enchantments, itemB.getEnchantments());
break;
case CREATE:
newTier = Util.armorToTier(itemB.getType());
durability = 0;
enchantments = combineEnchantments(enchantments, itemB.getEnchantments());
newTier = Util.armorToTier(itemB.getType());
durability = 0;
enchantments = combineEnchantments(enchantments, itemB.getEnchantments());
break;
case ENCHANT:
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) itemB.getItemMeta();
newTier = curTier;
durability = itemA.getDurability();
newTier = curTier;
durability = itemA.getDurability();
// If there aren't any illegal enchantments on the book, continue as normal.
// Otherwise... Block.
if (verifyEnchantments(meta.getStoredEnchants()) != meta.getStoredEnchants().size())
@ -316,7 +351,7 @@ public class EventHandlers implements Listener
return;
}
if (Util.playerHasCraftPerm(player, newTier))
if (plugin.playerHasCraftPerm(player, newTier))
{
result = new ItemStack(Material.ELYTRA, 1);
if (enchantments != null)
@ -329,8 +364,10 @@ public class EventHandlers implements Listener
}
// Check if either itemA or itemB is unoccupied.
if ((itemA == null || itemB == null) && nbtEditor.getArmorTier(event.getInventory().getItem(2)) != ArmorTier.NONE)
// If Item2 is occupied despite itemA or itemB not being occupied. (only for armored elytra)/
if ((itemA == null || itemB == null) &&
nbtEditor.getArmorTier(event.getInventory().getItem(2)) != ArmorTier.NONE)
// If Item2 is occupied despite itemA or itemB not being occupied. (only for
// armored elytra)/
event.setResult(null);
player.updateInventory();
}
@ -357,22 +394,28 @@ public class EventHandlers implements Listener
}
catch (ClassCastException exception)
{
// Print warning to console and exit onInventoryClick event (no support for custom inventories as they are usually used for GUI's).
plugin.debugMsg(Level.WARNING, "Could not cast inventory to anvilInventory for player " + player.getName() + "! Armored Elytras cannot be crafted!");
// Print warning to console and exit onInventoryClick event (no support for
// custom inventories as they are usually used for GUI's).
plugin.debugMsg(Level.WARNING, "Could not cast inventory to anvilInventory for player " + player.getName()
+ "! Armored Elytras cannot be crafted!");
return;
}
int slot = e.getRawSlot();
if (slot == 2 && anvilInventory.getItem(0) != null && anvilInventory.getItem(1) != null && anvilInventory.getItem(2) != null)
if (slot == 2 && anvilInventory.getItem(0) != null && anvilInventory.getItem(1) != null &&
anvilInventory.getItem(2) != null)
{
ArmorTier armortier = nbtEditor.getArmorTier(anvilInventory.getItem(2));
// If there's an armored elytra in the final slot...
if (armortier != ArmorTier.NONE && Util.playerHasCraftPerm(player, armortier))
if (armortier != ArmorTier.NONE && plugin.playerHasCraftPerm(player, armortier))
{
// Create a new armored elytra and give that one to the player instead of the result.
// This is done because after putting item0 in AFTER item1, the first letter of the color code shows up, this gets rid of that problem.
ItemStack result = nbtEditor.addArmorNBTTags(anvilInventory.getItem(2), armortier, plugin.getConfigLoader().unbreakable());
// Create a new armored elytra and give that one to the player instead of the
// result.
// This is done because after putting item0 in AFTER item1, the first letter of
// the color code shows up, this gets rid of that problem.
ItemStack result = nbtEditor.addArmorNBTTags(anvilInventory.getItem(2), armortier,
plugin.getConfigLoader().unbreakable());
// Give the result to the player and clear the anvil's inventory.
if (e.isShiftClick())
{
@ -384,98 +427,34 @@ public class EventHandlers implements Listener
else
player.setItemOnCursor(result);
// Clean the anvil's inventory after transferring the items.
cleanAnvil(anvilInventory);
cleanAnvilInventory.accept(anvilInventory);
player.updateInventory();
return;
}
}
}
// Make sure the player has the correct permission and that the item is not broken.
// Make sure the player has the correct permission and that the item is not
// broken.
private AllowedToWearEnum isAllowedToWear(ItemStack elytra, Player player, ArmorTier armorTier)
{
if (armorTier.equals(ArmorTier.NONE))
return AllowedToWearEnum.ALLOWED;
if (Util.isBroken(elytra))
return AllowedToWearEnum.BROKEN;
if (!Util.playerHasWearPerm(player, armorTier))
if (!plugin.playerHasWearPerm(player, armorTier))
return AllowedToWearEnum.NOPERMISSION;
return AllowedToWearEnum.ALLOWED;
}
// Check if the player tries to equip armor by richt clicking it.
@EventHandler
public void onRightClick(PlayerInteractEvent event)
{
if (!event.getAction().equals(org.bukkit.event.block.Action.RIGHT_CLICK_AIR) &&
!event.getAction().equals(org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK))
return;
ItemStack item = event.getItem();
if (item == null)
return;
Player player = event.getPlayer();
if (item.getType() == Material.ELYTRA)
{
ArmorTier armorTier = nbtEditor.getArmorTier(item);
if (nbtEditor.getArmorTier(item) == ArmorTier.NONE)
return;
AllowedToWearEnum allowed = isAllowedToWear(item, player, armorTier);
if (allowed == AllowedToWearEnum.BROKEN)
{
plugin.messagePlayer(player, plugin.getMyMessages().getString("MESSAGES.RepairNeeded"));
event.setCancelled(true);
player.updateInventory();
}
else if (allowed == AllowedToWearEnum.NOPERMISSION)
{
plugin.usageDeniedMessage(player, armorTier);
event.setCancelled(true);
player.updateInventory();
}
}
}
// Player closes their inventory. Also checks for whether they are allowed to wear the armored elytra they are wearing.
// This is done again here because there are ways to bypass permission check when equipping.
@EventHandler
public void onInventoryClose(InventoryCloseEvent e)
{
verifyArmorInChestSlot((Player) e.getPlayer());
}
// Check if the player is allowed to wear the armored elytra based on their permissions.
private void verifyArmorInChestSlot(Player player)
{
ItemStack chestplate = player.getInventory().getChestplate();
// If the player equips a new chestplate.
if (player.getInventory().getChestplate() == null)
return;
ArmorTier armorTier = nbtEditor.getArmorTier(chestplate);
// If that chestplate is an (armored) elytra.
if (armorTier == ArmorTier.NONE)
return;
AllowedToWearEnum allowed = isAllowedToWear(chestplate, player, armorTier);
if (allowed == AllowedToWearEnum.BROKEN)
{
plugin.messagePlayer(player, plugin.getMyMessages().getString("MESSAGES.RepairNeeded"));
unenquipChestPlayer(player);
}
else if (allowed == AllowedToWearEnum.NOPERMISSION)
{
plugin.usageDeniedMessage(player, armorTier);
unenquipChestPlayer(player);
}
}
// Because the armored elytra doesn't actually give any armor, the damage
// received by players wearing an armored elytra is calculated here.
// Handle armored elytra durability loss.
@EventHandler(ignoreCancelled = true)
public void onPlayerDamage(EntityDamageEvent e)
{
if(!(e.getEntity() instanceof Player))
if (!(e.getEntity() instanceof Player))
return;
if (plugin.getConfigLoader().unbreakable())
return;
Player p = (Player) e.getEntity();
@ -495,21 +474,24 @@ public class EventHandlers implements Listener
if (cause != DamageCause.DROWNING && cause != DamageCause.STARVATION && cause != DamageCause.SUFFOCATION &&
cause != DamageCause.SUICIDE && cause != DamageCause.FLY_INTO_WALL && cause != DamageCause.POISON)
{
int durability = p.getInventory().getChestplate().getDurability();
int durability = p.getInventory().getChestplate().getDurability();
int maxDurability = p.getInventory().getChestplate().getType().getMaxDurability();
int newDurability = durability + ((int) (e.getDamage() / 4) > 1 ? (int) (e.getDamage() / 4) : 1);
// If the elytra has the durability enchantment, we calculate the durability loss ourselves.
// If the elytra has the durability enchantment, we calculate the durability
// loss ourselves.
if (p.getInventory().getChestplate().containsEnchantment(Enchantment.DURABILITY))
{
// Get a random int between 0 and 100 to use in deciding if the durability enchantment will take effect.
Random r = new Random();
int randomInt = r.nextInt(101);
int enchantLevel = p.getInventory().getChestplate().getEnchantmentLevel(Enchantment.DURABILITY);
// Get a random int between 0 and 100 to use in deciding if the durability
// enchantment will take effect.
Random r = new Random();
int randomInt = r.nextInt(101);
int enchantLevel = p.getInventory().getChestplate().getEnchantmentLevel(Enchantment.DURABILITY);
int durabilityDelta = (100 / (enchantLevel + 1)) < randomInt ? 0 : 1;
// If the durability equals/exceeds maxDurability, it's broken (0 = full item durability).
// If the durability equals/exceeds maxDurability, it's broken (0 = full item
// durability).
if (durability >= maxDurability)
unenquipChestPlayer(p);
moveChestplateToInventory.accept(p);
else
newDurability = durability + durabilityDelta;
}
@ -517,28 +499,37 @@ public class EventHandlers implements Listener
if (newDurability >= maxDurability)
{
newDurability = maxDurability;
unenquipChestPlayer(p);
moveChestplateToInventory.accept(p);
}
elytra.setDurability((short) (newDurability));
}
}
}
// Check if the player is trying to equip a broken elytra (and prevent that).
@EventHandler
public void playerEquipsArmor(InventoryClickEvent e)
public void onEquip(ArmorEquipEvent e)
{
if (!(e.getWhoClicked() instanceof Player))
if (!e.getType().equals(ArmorType.CHESTPLATE) ||
!e.getNewArmorPiece().getType().equals(Material.ELYTRA) ||
e.getNewArmorPiece() == null)
return;
Player player = (Player) e.getWhoClicked();
new BukkitRunnable()
ArmorTier armorTier = nbtEditor.getArmorTier(e.getNewArmorPiece());
AllowedToWearEnum allowed = isAllowedToWear(e.getNewArmorPiece(), e.getPlayer(), armorTier);
switch(allowed)
{
@Override
public void run()
{
verifyArmorInChestSlot(player);
}
}.runTaskLater(plugin, 1);
case ALLOWED:
break;
case BROKEN:
plugin.messagePlayer(e.getPlayer(), plugin.getMyMessages().getString("MESSAGES.RepairNeeded"));
e.setCancelled(true);
break;
case NOPERMISSION:
plugin.usageDeniedMessage(e.getPlayer(), armorTier);
e.setCancelled(true);
break;
default:
break;
}
}
}

View File

@ -28,7 +28,6 @@ public class GetArmorValueNew implements GetArmorValue
plugin.myLogger(Level.INFO, "Failed to obtain armor value from NBT!");
return 0;
}
else
return 0;
return 0;
}
}

View File

@ -21,7 +21,6 @@ public class GetArmorValueOld implements GetArmorValue
String stringAtPos = nbtString.substring(pos, pos + 1);
return Integer.parseInt(stringAtPos);
}
else
return 0;
return 0;
}
}

View File

@ -163,7 +163,7 @@ public class NBTEditor
Object nmsStack = asNMSCopy.invoke(null, item);
Object compound = ((boolean) hasTag.invoke(nmsStack) ? getTag.invoke(nmsStack) : NBTTagCompound.newInstance());
Object modifiers = NBTTagList.newInstance();
Object armor = NBTTagCompound.newInstance();
Object armor = NBTTagCompound.newInstance(); // I should be able to simply add custom tags here!
setTag.invoke (armor, "AttributeName", NBTTagStringCtor.newInstance("generic.armor"));
setTag.invoke (armor, "Name", NBTTagStringCtor.newInstance("generic.armor"));
setTag.invoke (armor, "Amount", NBTTagIntCtor.newInstance(armorProtection));

View File

@ -30,6 +30,8 @@ public class ConfigLoader
private boolean noFlightDurability;
private List<String> allowedEnchantments;
private boolean allowMultipleProtectionEnchantments;
public boolean bypassWearPerm;
public boolean bypassCraftPerm;
private ArrayList<ConfigOption<?>> configOptionsList;
private ArmoredElytra plugin;
@ -87,7 +89,7 @@ public class ConfigLoader
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
"It will check player's inventories and their end chest upon login and any regular chest when it is opened.",
"This means it will take a while for all armored elytras to be removed from your server, but it doesn't take up ",
"a lot of resources, so you can just leave it installed and ignore it.",
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
"Please do not forget to MAKE A BACKUP before enabling this option!"
};
String[] languageFileComment =
@ -102,11 +104,16 @@ public class ConfigLoader
"elytra that has the protection enchantment with an enchanted book that has the blast protection enchantment",
"would result in removal of the protection enchantment and addition of the blast protection enchantment."
};
String[] permissionsComment =
{
"Globally bypass permissions for wearing and/or crafting amored elytras. Useful if permissions are unavailable."
};
// Set default list of allowed enchantments.
allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL", "THORNS",
"BINDING_CURSE", "VANISHING_CURSE"));
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
FileConfiguration config = plugin.getConfig();
@ -123,6 +130,8 @@ public class ConfigLoader
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
uninstallMode = addNewConfigOption(config, "uninstallMode", false, uninstallComment);
languageFile = addNewConfigOption(config, "languageFile", "en_US", languageFileComment);
bypassWearPerm = addNewConfigOption(config, "bypassWearPermissions", true, permissionsComment);
bypassCraftPerm = addNewConfigOption(config, "bypassCraftPermissions", true, null);
writeConfig();
}
@ -239,4 +248,14 @@ public class ConfigLoader
{
return allowedEnchantments;
}
public boolean bypassWearPerm()
{
return bypassWearPerm;
}
public boolean bypassCraftPerm()
{
return bypassCraftPerm;
}
}

View File

@ -37,7 +37,11 @@ public class Messages
// Load the default en_US from the resources.
plugin.saveResource("en_US.txt", true);
defaultFile.setWritable(false);
if (!defaultFile.setWritable(false))
{
plugin.myLogger(Level.WARNING, "Could not set default language file to read-only!");
plugin.myLogger(Level.WARNING, "This is not a big problem. Just remember not to edit the file!");
}
}
// Read locale file.
@ -58,7 +62,7 @@ public class Messages
String[] parts = sCurrentLine.split("=", 2);
key = parts[0];
value = parts[1].replaceAll("&((?i)[0-9a-fk-or])", "\u00A7$1");
String[] newLineSplitter = value.split("\\\\n"); // Wut? Can I haz more backslash?
String[] newLineSplitter = value.split("\\\\n");
String values = newLineSplitter[0];
@ -83,22 +87,11 @@ public class Messages
// Get a string from a key. Returns "null" if null.
public String getString(String key)
{
String value = null;
value = messageMap.get(key);
if (value == null)
{
value = "ArmoredElytra: Translation not found! Contact server admin!";
plugin.myLogger(Level.WARNING, "Failed to get translation for key " + key);
}
return value;
}
String value = messageMap.get(key);
if (value != null)
return value;
public String getStringReverse(String value)
{
return messageMap.entrySet().stream()
.filter(e -> e.getValue().equals(value))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
plugin.myLogger(Level.WARNING, "Failed to get the translation for key " + key);
return "Translation for key \"" + key + "\" not found! Contact server admin!";
}
}

View File

@ -1,425 +0,0 @@
package nl.pim16aap2.armoredElytra.util;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.net.ssl.HttpsURLConnection;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
*
* Check out https://bStats.org/ to learn more about bStats!
*/
public class Metrics
{
static
{
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false"))
{
// Maven's Relocate is clever and changes strings, too. So we have to use this
// little "trick" ... :D
final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't' });
final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' });
// We want to make sure nobody just copy & pastes the example and use the wrong
// package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage))
{
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Should failed requests be logged?
private static boolean logFailedRequests;
// The uuid of the server
private static String serverUUID;
// The plugin
private final JavaPlugin plugin;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
*/
public Metrics(JavaPlugin plugin)
{
if (plugin == null)
{
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid"))
{
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Inform the server owners about bStats
config.options().header("bStats collects some data for plugin authors like how many servers are using their plugins.\n" + "To honor their work, you should not disable it.\n" + "This has nearly no effect on the server performance!\n" + "Check out https://bStats.org/ to learn more :)").copyDefaults(true);
try
{
config.save(configFile);
}
catch (IOException ignored)
{
}
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices())
{
try
{
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
}
catch (NoSuchFieldException ignored)
{
}
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found)
{
// We are the first!
startSubmitting();
}
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting()
{
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask()
{
@Override
public void run()
{
if (!plugin.isEnabled())
{ // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to
// use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the
// stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, new Runnable()
{
@Override
public void run()
{
submitData();
}
});
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other
// plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be
// blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data. This method is called using Reflection.
*
* @return The plugin specific data.
*/
@SuppressWarnings("unchecked")
public JSONObject getPluginData()
{
JSONObject data = new JSONObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.put("pluginName", pluginName); // Append the name of the plugin
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
JSONArray customCharts = new JSONArray();
for (CustomChart customChart : charts)
{
// Add the data of the custom charts
JSONObject chart = customChart.getRequestJsonObject();
if (chart == null)
{ // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.put("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
@SuppressWarnings("unchecked")
private JSONObject getServerData()
{
// Minecraft specific data
int playerAmount;
try
{
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError:
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) ? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size() : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
}
catch (Exception e)
{
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = org.bukkit.Bukkit.getVersion();
bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JSONObject data = new JSONObject();
data.put("serverUUID", serverUUID);
data.put("playerAmount", playerAmount);
data.put("onlineMode", onlineMode);
data.put("bukkitVersion", bukkitVersion);
data.put("javaVersion", javaVersion);
data.put("osName", osName);
data.put("osArch", osArch);
data.put("osVersion", osVersion);
data.put("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
@SuppressWarnings("unchecked")
private void submitData()
{
final JSONObject data = getServerData();
JSONArray pluginData = new JSONArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices())
{
try
{
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service))
{
try
{
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
}
catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored)
{
}
}
}
catch (NoSuchFieldException ignored)
{
}
}
data.put("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
// Send the data
sendData(data);
}
catch (Exception e)
{
// Something went wrong! :(
if (logFailedRequests)
{
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(JSONObject data) throws Exception
{
if (data == null)
{
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread())
{
throw new IllegalAccessException("This method must not be called from the main thread!");
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException
{
if (str == null)
{
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes("UTF-8"));
gzip.close();
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart
{
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
CustomChart(String chartId)
{
if (chartId == null || chartId.isEmpty())
{
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
@SuppressWarnings("unchecked")
private JSONObject getRequestJsonObject()
{
JSONObject chart = new JSONObject();
chart.put("chartId", chartId);
try
{
JSONObject data = getChartData();
if (data == null)
{
// If the data is null we don't send the chart.
return null;
}
chart.put("data", data);
}
catch (Throwable t)
{
if (logFailedRequests)
{
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract JSONObject getChartData() throws Exception;
}
}

View File

@ -6,7 +6,6 @@ import java.util.Map;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class Util
@ -92,14 +91,4 @@ public class Util
ret += 16;
return ret;
}
public static boolean playerHasCraftPerm(Player player, ArmorTier armorTier)
{
return player.hasPermission("armoredelytra.craft." + ArmorTier.getName(armorTier));
}
public static boolean playerHasWearPerm(Player player, ArmorTier armorTier)
{
return player.hasPermission("armoredelytra.wear." + ArmorTier.getName(armorTier));
}
}

View File

@ -1,6 +1,6 @@
name: ArmoredElytra
main: nl.pim16aap2.armoredElytra.ArmoredElytra
version: 2.4.8
version: ${project.version}
author: pim16aap2
commands:
ArmoredElytra: