Compare commits

...

8 Commits

Author SHA1 Message Date
hyperdefined
a881f60ed8 ok git just forget this file 2026-04-09 19:06:45 -04:00
hyperdefined
bdef9453c7 2.0.4 2026-04-09 19:06:22 -04:00
hyperdefined
11d6a5bf91 add new stuff to readme 2026-04-06 14:27:57 -04:00
hyperdefined
df4fadb1f0 remove dupe comment 2026-04-06 12:20:09 -04:00
hyperdefined
c5deb0f7fd config updater 17 2026-04-06 12:19:11 -04:00
hyperdefined
27427c9ee6 RS multikill support
still experimental
2026-04-05 21:07:54 -04:00
hyperdefined
7cd5e8f0d0 make sure we check for canceled events 2026-04-05 20:22:30 -04:00
hyperdefined
6a13c7fef7 add logs stripped 2026-04-05 20:18:43 -04:00
32 changed files with 731 additions and 104 deletions

View File

@@ -25,9 +25,13 @@ Here is everything it tracks:
* Flight time with elytras. * Flight time with elytras.
* Critical strikes for melee weapons. * Critical strikes for melee weapons.
* Times trident thrown. * Times trident thrown.
* Logs stripped.
The best part is, this data is stored on the item itself. The best part is, this data is stored on the item itself.
This plugin also has compatibility for:
* [RoseStacker](https://modrinth.com/plugin/rosestacker)
If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding. If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding.
![Image](https://docs.hyper.lol/plugins/toolstats/assets/image.png) ![Image](https://docs.hyper.lol/plugins/toolstats/assets/image.png)

View File

@@ -20,7 +20,7 @@ dependencies {
} }
group = "lol.hyper" group = "lol.hyper"
version = "2.0.3" version = "2.0.4"
description = "ToolStats" description = "ToolStats"
java.sourceCompatibility = JavaVersion.VERSION_25 java.sourceCompatibility = JavaVersion.VERSION_25

View File

@@ -37,7 +37,7 @@ import java.io.File;
public final class ToolStats extends JavaPlugin { public final class ToolStats extends JavaPlugin {
public final int CONFIG_VERSION = 16; public final int CONFIG_VERSION = 17;
public final ComponentLogger logger = this.getComponentLogger(); public final ComponentLogger logger = this.getComponentLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml"); public final File configFile = new File(this.getDataFolder(), "config.yml");
public boolean tokens = false; public boolean tokens = false;
@@ -132,7 +132,7 @@ public final class ToolStats extends JavaPlugin {
playerDrop = new PlayerDrop(this); playerDrop = new PlayerDrop(this);
if (Bukkit.getPluginManager().isPluginEnabled("RoseStacker")) { if (Bukkit.getPluginManager().isPluginEnabled("RoseStacker")) {
logger.info("RoseStacker has been detected, adding support!"); logger.info("RoseStacker has been detected, adding support!");
roseStacker = new RoseStacker(); roseStacker = new RoseStacker(this);
} }
Bukkit.getServer().getPluginManager().registerEvents(blockBreak, this); Bukkit.getServer().getPluginManager().registerEvents(blockBreak, this);

View File

@@ -29,6 +29,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
@@ -227,6 +228,29 @@ public class CommandToolStats implements BasicCommand {
} }
return; return;
} }
case "add": {
if (!sender.hasPermission("toolstats.add")) {
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return;
}
if (sender instanceof ConsoleCommandSender) {
sender.sendMessage(Component.text("You must be a player for this command.", NamedTextColor.RED));
return;
}
// Make sure /toolstats add <stat> is present
if (args.length < 2) {
sender.sendMessage(Component.text("Invalid syntax. Usage: /toolstats add <stat>", NamedTextColor.RED));
return;
}
//Make sure they typed in a valid stat
String stat = args[1];
if (!toolStats.tokenData.getTokenTypes().contains(stat)) {
sender.sendMessage(Component.text("That is not a valid stat.", NamedTextColor.RED));
return;
}
addStat(stat, (Player) sender);
return;
}
default: { default: {
sender.sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED)); sender.sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED));
} }
@@ -423,6 +447,14 @@ public class CommandToolStats implements BasicCommand {
} }
} }
} }
if (toolStats.config.getBoolean("enabled.logs-stripped")) {
if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped != null) {
lore.add(toolStats.configTools.formatLore("logs-stripped", "{logs}", toolStats.numberFormat.formatInt(logsStripped)));
}
}
}
finalMeta.lore(lore); finalMeta.lore(lore);
finalItem.setItemMeta(finalMeta); finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot(); int slot = player.getInventory().getHeldItemSlot();
@@ -441,6 +473,169 @@ public class CommandToolStats implements BasicCommand {
target.getInventory().addItem(token); target.getInventory().addItem(token);
} }
/**
* Add a stat to an item, setting it to zero.
* @param stat The stat to add.
* @param player The player running the command.
*/
private void addStat(String stat, Player player) {
PlayerInventory playerInventory = player.getInventory();
ItemStack heldItem = playerInventory.getItemInMainHand();
ItemMeta heldItemMeta = heldItem.getItemMeta();
if (heldItemMeta == null) {
return;
}
if (toolStats.itemChecker.checkTokens(heldItemMeta.getPersistentDataContainer(), stat)) {
player.sendMessage(Component.text("This item already has this stat.", NamedTextColor.RED));
return;
}
if (stat.equalsIgnoreCase("remove") || stat.equalsIgnoreCase("reset")) {
player.sendMessage(Component.text("That is not a valid stat.", NamedTextColor.RED));
return;
}
ItemStack newItem = toolStats.itemChecker.addToken(heldItem, stat);
switch (stat) {
case "crops-mined": {
if (toolStats.config.getBoolean("enabled.crops-harvested")) {
newItem.setItemMeta(toolStats.itemLore.updateCropsMined(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "blocks-mined": {
if (toolStats.configTools.checkConfig(newItem.getType(), "blocks-mined")) {
newItem.setItemMeta(toolStats.itemLore.updateBlocksMined(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "damage-taken": {
if (toolStats.config.getBoolean("enabled.armor-damage")) {
newItem.setItemMeta(toolStats.itemLore.updateArmorDamage(newItem, 0.0, false));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "damage-done": {
if (toolStats.configTools.checkConfig(newItem.getType(), "damage-done")) {
newItem.setItemMeta(toolStats.itemLore.updateWeaponDamage(newItem, 0.0, false));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "mob-kills": {
if (toolStats.configTools.checkConfig(newItem.getType(), "mob-kills")) {
newItem.setItemMeta(toolStats.itemLore.updateMobKills(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "player-kills": {
if (toolStats.configTools.checkConfig(newItem.getType(), "player-kills")) {
newItem.setItemMeta(toolStats.itemLore.updatePlayerKills(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "arrows-shot": {
if (toolStats.config.getBoolean("enabled.arrows-shot")) {
newItem.setItemMeta(toolStats.itemLore.updateArrowsShot(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "sheep-sheared": {
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
newItem.setItemMeta(toolStats.itemLore.updateSheepSheared(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "flight-time": {
if (toolStats.config.getBoolean("enabled.flight-time")) {
newItem.setItemMeta(toolStats.itemLore.updateFlightTime(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "fish-caught": {
if (toolStats.config.getBoolean("enabled.fish-caught")) {
newItem.setItemMeta(toolStats.itemLore.updateFishCaught(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "wither-kills": {
if (toolStats.config.getBoolean("enabled.bosses-killed.wither")) {
newItem.setItemMeta(toolStats.itemLore.updateBossesKilled(newItem, 0, "wither"));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "enderdragon-kills": {
if (toolStats.config.getBoolean("enabled.bosses-killed.enderdragon")) {
newItem.setItemMeta(toolStats.itemLore.updateBossesKilled(newItem, 0, "enderdragon"));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "critical-strikes": {
if (toolStats.config.getBoolean("enabled.critical-strikes")) {
newItem.setItemMeta(toolStats.itemLore.updateCriticalStrikes(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "trident-throws": {
if (toolStats.config.getBoolean("enabled.trident-throws")) {
newItem.setItemMeta(toolStats.itemLore.updateTridentThrows(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
case "logs-stripped": {
if (toolStats.config.getBoolean("enabled.logs-stripped")) {
newItem.setItemMeta(toolStats.itemLore.updateLogsStripped(newItem, 0));
} else {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
break;
}
}
player.sendMessage(Component.text(stat + " has been added!", NamedTextColor.GREEN));
}
/** /**
* Handle edit subcommand. * Handle edit subcommand.
* *
@@ -878,6 +1073,36 @@ public class CommandToolStats implements BasicCommand {
} }
break; break;
} }
case "logs-stripped": {
if (!toolStats.config.getBoolean("enabled.logs-stripped")) {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
int value;
try {
value = Integer.parseInt((String) userValue);
} catch (NumberFormatException exception) {
player.sendMessage(Component.text("That is not a valid number.", NamedTextColor.RED));
return;
}
if (value < 0) {
player.sendMessage(Component.text("Number must be positive.", NamedTextColor.RED));
return;
}
Integer statValue = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (statValue == null) {
player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED));
return;
}
int difference = value - statValue;
editedItemMeta = toolStats.itemLore.updateLogsStripped(editedItem, difference);
updated = true;
} else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
}
break;
}
default: { default: {
player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED)); player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED));
return; return;
@@ -931,6 +1156,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -959,6 +1185,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -987,6 +1214,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1015,6 +1243,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1043,6 +1272,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1071,6 +1301,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1099,6 +1330,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1128,6 +1360,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1156,6 +1389,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1184,6 +1418,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1212,6 +1447,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1240,6 +1476,7 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1268,6 +1505,36 @@ public class CommandToolStats implements BasicCommand {
editedItemMeta.lore(newLore); editedItemMeta.lore(newLore);
} else { } else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
}
break;
}
case "logs-stripped": {
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer statValue = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (statValue == null) {
player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED));
return;
}
String tokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING);
if (tokens == null) {
player.sendMessage(Component.text("Unable to get tokens from item.", NamedTextColor.RED));
return;
}
container.remove(toolStats.toolStatsKeys.getLogsStripped());
List<String> newTokens = toolStats.itemChecker.removeToken(tokens, "logs-stripped");
if (newTokens.isEmpty()) {
container.remove(toolStats.toolStatsKeys.getTokenApplied());
} else {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens));
}
Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", toolStats.numberFormat.formatInt(statValue));
List<Component> newLore = toolStats.itemLore.removeLore(editedItemMeta.lore(), oldLine);
editedItemMeta.lore(newLore);
} else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
return;
} }
break; break;
} }
@@ -1281,6 +1548,8 @@ public class CommandToolStats implements BasicCommand {
player.sendMessage(Component.text("Removed stat " + stat + " for held item!", NamedTextColor.GREEN)); player.sendMessage(Component.text("Removed stat " + stat + " for held item!", NamedTextColor.GREEN));
} }
@Override @Override
public @NonNull Collection<String> suggest(@NonNull CommandSourceStack source, String[] args) { public @NonNull Collection<String> suggest(@NonNull CommandSourceStack source, String[] args) {
CommandSender sender = source.getSender(); CommandSender sender = source.getSender();
@@ -1305,6 +1574,9 @@ public class CommandToolStats implements BasicCommand {
if (sender.hasPermission("toolstats.purge")) { if (sender.hasPermission("toolstats.purge")) {
suggestions.add("purge"); suggestions.add("purge");
} }
if (sender.hasPermission("toolstats.add")) {
suggestions.add("add");
}
return suggestions; return suggestions;
} }
@@ -1326,6 +1598,15 @@ public class CommandToolStats implements BasicCommand {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
// suggest keys for add
if (args.length == 2 && args[0].equalsIgnoreCase("add") && sender.hasPermission("toolstats.add")) {
// yes I am lazy
return toolStats.tokenData.getTokenTypes().stream()
.filter(s -> !s.equals("remove") && !s.equals("reset"))
.map(s -> s.equals("crops-mined") ? "crops-harvested" : s)
.collect(Collectors.toList());
}
// suggest keys for remove // suggest keys for remove
if (args.length == 2 && args[0].equalsIgnoreCase("remove") && sender.hasPermission("toolstats.remove")) { if (args.length == 2 && args[0].equalsIgnoreCase("remove") && sender.hasPermission("toolstats.remove")) {
// yes I am lazy // yes I am lazy

View File

@@ -39,7 +39,7 @@ public class AnvilEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onAnvilEvent(PrepareAnvilEvent event) { public void onAnvilEvent(PrepareAnvilEvent event) {
// only listen if the token system is enabled // only listen if the token system is enabled
if (!toolStats.config.getBoolean("tokens.enabled")) { if (!toolStats.config.getBoolean("tokens.enabled")) {
@@ -139,6 +139,10 @@ public class AnvilEvent implements Listener {
addToken(event, tokenType, "critical-strikes", clone); addToken(event, tokenType, "critical-strikes", clone);
return; return;
} }
if (tokenType.equalsIgnoreCase("logs-stripped")) {
addToken(event, tokenType, "logs-stripped", clone);
return;
}
} }
return; return;
} }
@@ -363,6 +367,15 @@ public class AnvilEvent implements Listener {
} }
break; break;
} }
case "logs-stripped": {
if (toolStats.config.getBoolean("enabled.logs-stripped")) {
newItem.setItemMeta(toolStats.itemLore.updateLogsStripped(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
} }
event.setResult(newItem); event.setResult(newItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken)); event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken));
@@ -496,6 +509,14 @@ public class AnvilEvent implements Listener {
meta = toolStats.itemLore.updateTridentThrows(finalItem, -tridentThrows); meta = toolStats.itemLore.updateTridentThrows(finalItem, -tridentThrows);
finalItem.setItemMeta(meta); finalItem.setItemMeta(meta);
} }
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped == null) {
return;
}
meta = toolStats.itemLore.updateLogsStripped(finalItem, -logsStripped);
finalItem.setItemMeta(meta);
}
event.setResult(finalItem); event.setResult(finalItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost("reset")); event.getView().setRepairCost(toolStats.itemChecker.getCost("reset"));
} }

View File

@@ -39,17 +39,14 @@ import java.util.Locale;
public class BlockBreak implements Listener { public class BlockBreak implements Listener {
private final ToolStats toolStats; private final ToolStats toolStats;
public List<Block> brokenContainers = new ArrayList<>(); public final List<Block> brokenContainers = new ArrayList<>();
public BlockBreak(ToolStats toolStats) { public BlockBreak(ToolStats toolStats) {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBreak(BlockBreakEvent event) { public void onBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {
return; return;

View File

@@ -45,7 +45,7 @@ public class BlockDispenseEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onDispense(BlockDispenseLootEvent event) { public void onDispense(BlockDispenseLootEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (player == null) { if (player == null) {

View File

@@ -44,7 +44,7 @@ public class ChunkPopulate implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPopulate(ChunkPopulateEvent event) { public void onPopulate(ChunkPopulateEvent event) {
if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) { if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) {
return; return;
@@ -56,7 +56,7 @@ public class ChunkPopulate implements Listener {
// this is delayed because entities are not loaded instantly // this is delayed because entities are not loaded instantly
// we just check 1 second later // we just check 1 second later
Chunk chunk = event.getChunk(); Chunk chunk = event.getChunk();
Bukkit.getRegionScheduler().runDelayed(toolStats, world, chunk.getX(), chunk.getZ(), scheduledTask -> { Bukkit.getRegionScheduler().runDelayed(toolStats, world, chunk.getX(), chunk.getZ(), _ -> {
for (Entity entity : chunk.getEntities()) { for (Entity entity : chunk.getEntities()) {
// if there is a new item frame // if there is a new item frame
if (!(entity instanceof ItemFrame itemFrame)) { if (!(entity instanceof ItemFrame itemFrame)) {

View File

@@ -44,11 +44,8 @@ public class CraftItem implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCraft(CraftItemEvent event) { public void onCraft(CraftItemEvent event) {
if (event.isCancelled()) {
return;
}
Player player = (Player) event.getWhoClicked(); Player player = (Player) event.getWhoClicked();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {
return; return;

View File

@@ -23,6 +23,7 @@ import net.kyori.adventure.text.Component;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCreativeEvent; import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -42,7 +43,7 @@ public class CreativeEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCreativeEvent(InventoryCreativeEvent event) { public void onCreativeEvent(InventoryCreativeEvent event) {
Player player = (Player) event.getWhoClicked(); Player player = (Player) event.getWhoClicked();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -34,7 +34,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.projectiles.ProjectileSource; import org.bukkit.projectiles.ProjectileSource;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class EntityDamage implements Listener { public class EntityDamage implements Listener {
@@ -47,12 +46,8 @@ public class EntityDamage implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent event) { public void onDamage(EntityDamageByEntityEvent event) {
if (event.isCancelled()) {
return;
}
if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) { if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) {
return; return;
} }
@@ -264,12 +259,22 @@ public class EntityDamage implements Listener {
if (type.equalsIgnoreCase("mob")) { if (type.equalsIgnoreCase("mob")) {
// player is shooting a mob // player is shooting a mob
ItemMeta newBow;
int count = 1;
if (toolStats.roseStacker != null) { if (toolStats.roseStacker != null) {
count = toolStats.roseStacker.countMobs(entity); toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newBow = toolStats.itemLore.updateMobKills(heldBow, count);
if (newBow != null) {
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newBow);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newBow);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newBow);
} }
newBow = toolStats.itemLore.updateMobKills(heldBow, count); }
});
return;
}
ItemMeta newBow = toolStats.itemLore.updateMobKills(heldBow, 1);
if (newBow != null) { if (newBow != null) {
if (isMain && isOffHand) { if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newBow); playerInventory.getItemInMainHand().setItemMeta(newBow);
@@ -297,20 +302,31 @@ public class EntityDamage implements Listener {
private void updateTridentKills(Trident trident, String type, LivingEntity entity) { private void updateTridentKills(Trident trident, String type, LivingEntity entity) {
ItemStack newTrident = trident.getItemStack(); ItemStack newTrident = trident.getItemStack();
ItemMeta newTridentMeta;
if (type.equalsIgnoreCase("player")) { if (type.equalsIgnoreCase("player")) {
newTridentMeta = toolStats.itemLore.updatePlayerKills(trident.getItemStack(), 1); ItemMeta newTridentMeta = toolStats.itemLore.updatePlayerKills(trident.getItemStack(), 1);
} else {
int count = 1;
if (toolStats.roseStacker != null) {
count = toolStats.roseStacker.countMobs(entity);
}
newTridentMeta = toolStats.itemLore.updateMobKills(newTrident, count);
}
if (newTridentMeta != null) { if (newTridentMeta != null) {
newTrident.setItemMeta(newTridentMeta); newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident); trident.setItemStack(newTrident);
} }
return;
}
if (type.equalsIgnoreCase("mob")) {
if (toolStats.roseStacker != null) {
toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newTridentMeta = toolStats.itemLore.updateMobKills(newTrident, count);
if (newTridentMeta != null) {
newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident);
}
});
return;
}
ItemMeta newTridentMeta = toolStats.itemLore.updateMobKills(trident.getItemStack(), 1);
if (newTridentMeta != null) {
newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident);
}
}
} }
private void updateTridentDamage(Trident trident, double damage) { private void updateTridentDamage(Trident trident, double damage) {
@@ -332,27 +348,36 @@ public class EntityDamage implements Listener {
private void updateWeaponKills(PlayerInventory playerInventory, String type, LivingEntity entity) { private void updateWeaponKills(PlayerInventory playerInventory, String type, LivingEntity entity) {
ItemStack heldWeapon = playerInventory.getItemInMainHand(); ItemStack heldWeapon = playerInventory.getItemInMainHand();
ItemMeta newHeldWeaponMeta = null;
if (type.equalsIgnoreCase("player")) { if (type.equalsIgnoreCase("player")) {
newHeldWeaponMeta = toolStats.itemLore.updatePlayerKills(heldWeapon, 1); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updatePlayerKills(heldWeapon, 1);
}
if (type.equalsIgnoreCase("mob")) {
int count = 1;
if (toolStats.roseStacker != null) {
count = toolStats.roseStacker.countMobs(entity);
}
newHeldWeaponMeta = toolStats.itemLore.updateMobKills(heldWeapon, count);
}
if (newHeldWeaponMeta != null) { if (newHeldWeaponMeta != null) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} }
return;
}
if (type.equalsIgnoreCase("mob")) {
if (toolStats.roseStacker != null) {
toolStats.roseStacker.countMobs(entity, count -> {
ItemStack currentHeldWeapon = playerInventory.getItemInMainHand();
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateMobKills(currentHeldWeapon, count);
if (newHeldWeaponMeta != null) {
currentHeldWeapon.setItemMeta(newHeldWeaponMeta);
}
});
} else {
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateMobKills(heldWeapon, 1);
if (newHeldWeaponMeta != null) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
}
}
}
} }
private void updateBossesKilled(PlayerInventory playerInventory, String boss, LivingEntity entity) { private void updateBossesKilled(PlayerInventory playerInventory, String boss, LivingEntity entity) {
ItemStack heldWeapon = playerInventory.getItemInMainHand(); ItemStack heldWeapon = playerInventory.getItemInMainHand();
int count = 1; int count = 1;
if (toolStats.roseStacker != null) { if (toolStats.roseStacker != null) {
count = toolStats.roseStacker.countMobs(entity); //count = toolStats.roseStacker.countMobs(entity);
} }
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, count, boss); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, count, boss);
if (newHeldWeaponMeta != null) { if (newHeldWeaponMeta != null) {
@@ -369,12 +394,23 @@ public class EntityDamage implements Listener {
boolean isMain = playerInventory.getItemInMainHand().getType() == Material.BOW || playerInventory.getItemInMainHand().getType() == Material.CROSSBOW; boolean isMain = playerInventory.getItemInMainHand().getType() == Material.BOW || playerInventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = playerInventory.getItemInOffHand().getType() == Material.BOW || playerInventory.getItemInOffHand().getType() == Material.CROSSBOW; boolean isOffHand = playerInventory.getItemInOffHand().getType() == Material.BOW || playerInventory.getItemInOffHand().getType() == Material.CROSSBOW;
int count = 1;
if (toolStats.roseStacker != null) { if (toolStats.roseStacker != null) {
count = toolStats.roseStacker.countMobs(entity); toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, count, boss);
if (newHeldWeaponMeta != null) {
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newHeldWeaponMeta);
}
}
});
return;
} }
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, count, boss); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, 1, boss);
if (newHeldWeaponMeta != null) { if (newHeldWeaponMeta != null) {
if (isMain && isOffHand) { if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);

View File

@@ -43,7 +43,7 @@ public class EntityDeath implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onDeath(EntityDeathEvent event) { public void onDeath(EntityDeathEvent event) {
LivingEntity livingEntity = event.getEntity(); LivingEntity livingEntity = event.getEntity();
if (livingEntity instanceof Player) { if (livingEntity instanceof Player) {

View File

@@ -39,14 +39,14 @@ import java.util.Map;
public class GenerateLoot implements Listener { public class GenerateLoot implements Listener {
private final ToolStats toolStats; private final ToolStats toolStats;
public Map<Inventory, Location> generatedInventory = new HashMap<>(); public final Map<Inventory, Location> generatedInventory = new HashMap<>();
public List<Location> droppedLootLocations = new ArrayList<>(); public final List<Location> droppedLootLocations = new ArrayList<>();
public GenerateLoot(ToolStats toolStats) { public GenerateLoot(ToolStats toolStats) {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onGenerateLoot(LootGenerateEvent event) { public void onGenerateLoot(LootGenerateEvent event) {
InventoryHolder inventoryHolder = event.getInventoryHolder(); InventoryHolder inventoryHolder = event.getInventoryHolder();
if (inventoryHolder == null) { if (inventoryHolder == null) {

View File

@@ -50,7 +50,7 @@ public class InventoryClose implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onClose(InventoryCloseEvent event) { public void onClose(InventoryCloseEvent event) {
if (toolStats.generateLoot.generatedInventory.isEmpty()) { if (toolStats.generateLoot.generatedInventory.isEmpty()) {
return; return;
@@ -106,7 +106,7 @@ public class InventoryClose implements Listener {
}, null, 1); }, null, 1);
} }
if (holder instanceof Container container) { if (holder instanceof Container) {
Chunk chestChunk = chestLocation.getChunk(); Chunk chestChunk = chestLocation.getChunk();
Bukkit.getRegionScheduler().runDelayed(toolStats, chestLocation.getWorld(), chestChunk.getX(), chestChunk.getZ(), scheduledTask -> { Bukkit.getRegionScheduler().runDelayed(toolStats, chestLocation.getWorld(), chestChunk.getX(), chestChunk.getZ(), scheduledTask -> {
BlockState blockState = chestLocation.getWorld().getBlockAt(chestLocation).getState(); BlockState blockState = chestLocation.getWorld().getBlockAt(chestLocation).getState();

View File

@@ -42,12 +42,8 @@ public class InventoryOpen implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onOpen(InventoryOpenEvent event) { public void onOpen(InventoryOpenEvent event) {
if (event.isCancelled()) {
return;
}
Inventory inventory = event.getInventory(); Inventory inventory = event.getInventory();
InventoryHolder holder = inventory.getHolder(); InventoryHolder holder = inventory.getHolder();
boolean isBlockInventory = holder instanceof BlockInventoryHolder || holder instanceof DoubleChest; boolean isBlockInventory = holder instanceof BlockInventoryHolder || holder instanceof DoubleChest;

View File

@@ -47,11 +47,8 @@ public class PickupItem implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPickup(EntityPickupItemEvent event) { public void onPickup(EntityPickupItemEvent event) {
if (event.isCancelled()) {
return;
}
Entity entity = event.getEntity(); Entity entity = event.getEntity();
if (entity instanceof Player player) { if (entity instanceof Player player) {
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -34,7 +34,7 @@ public class PlayerDrop implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onDrop(PlayerDropItemEvent event) { public void onDrop(PlayerDropItemEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -46,11 +46,8 @@ public class PlayerFish implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onFish(PlayerFishEvent event) { public void onFish(PlayerFishEvent event) {
if (event.isCancelled()) {
return;
}
// only listen to when a player catches a fish // only listen to when a player catches a fish
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) { if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) {
return; return;

View File

@@ -33,9 +33,13 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
public class PlayerInteract implements Listener { public class PlayerInteract implements Listener {
@@ -50,7 +54,7 @@ public class PlayerInteract implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEvent event) { public void onInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return; return;
@@ -74,11 +78,35 @@ public class PlayerInteract implements Listener {
Inventory holderInventory = holder.getInventory(); Inventory holderInventory = holder.getInventory();
openedChests.add(block); openedChests.add(block);
chestInventories.add(holderInventory); chestInventories.add(holderInventory);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedChests.remove(block), 20); Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedChests.remove(block), 20);
}
// player right-clicked a log
String blockType = block.getType().toString().toLowerCase(Locale.ROOT);
if (blockType.endsWith("_log") && !blockType.contains("stripped")) {
PlayerInventory playerInventory = player.getInventory();
ItemStack axe = toolStats.itemChecker.getAxe(playerInventory);
// not holding an axe
if (axe == null) {
return;
}
ItemMeta newAxe = toolStats.itemLore.updateLogsStripped(axe, 1);
if (newAxe != null) {
boolean isMain = playerInventory.getItemInMainHand().getType().toString().endsWith("_AXE");
boolean isOffHand = playerInventory.getItemInOffHand().getType().toString().endsWith("_AXE");
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newAxe);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newAxe);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newAxe);
}
}
} }
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEntityEvent event) { public void onInteract(PlayerInteractEntityEvent event) {
Entity clicked = event.getRightClicked(); Entity clicked = event.getRightClicked();
Player player = event.getPlayer(); Player player = event.getPlayer();
@@ -91,7 +119,7 @@ public class PlayerInteract implements Listener {
Inventory mineCartInventory = storageMinecart.getInventory(); Inventory mineCartInventory = storageMinecart.getInventory();
mineCartChestInventories.add(mineCartInventory); mineCartChestInventories.add(mineCartInventory);
openedMineCarts.add(storageMinecart); openedMineCarts.add(storageMinecart);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedMineCarts.remove(storageMinecart), 20); Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedMineCarts.remove(storageMinecart), 20);
} }
} }
} }

View File

@@ -39,7 +39,7 @@ public class PlayerMove implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent event) { public void onMove(PlayerMoveEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -39,7 +39,7 @@ public class SheepShear implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onShear(PlayerInteractEntityEvent event) { public void onShear(PlayerInteractEntityEvent event) {
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;

View File

@@ -38,7 +38,7 @@ public class ShootBow implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onShoot(EntityShootBowEvent event) { public void onShoot(EntityShootBowEvent event) {
Entity shooter = event.getEntity(); Entity shooter = event.getEntity();
// only listen for players // only listen for players

View File

@@ -47,9 +47,9 @@ public class VillagerTrade implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onTrade(InventoryClickEvent event) { public void onTrade(InventoryClickEvent event) {
if (event.isCancelled() || event.getCurrentItem() == null) { if (event.getCurrentItem() == null) {
return; return;
} }
Inventory inventory = event.getClickedInventory(); Inventory inventory = event.getClickedInventory();

View File

@@ -15,53 +15,61 @@
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>. * along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/ */
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.support.rosestacker; package lol.hyper.toolstats.support.rosestacker;
import dev.rosewood.rosestacker.api.RoseStackerAPI; import dev.rosewood.rosestacker.api.RoseStackerAPI;
import dev.rosewood.rosestacker.stack.StackedEntity; import dev.rosewood.rosestacker.stack.StackedEntity;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import java.util.function.Consumer;
public class RoseStacker { public class RoseStacker {
private final ToolStats toolStats;
private final RoseStackerAPI rsAPI; private final RoseStackerAPI rsAPI;
public RoseStacker() { public RoseStacker(ToolStats toolStats) {
this.toolStats = toolStats;
this.rsAPI = RoseStackerAPI.getInstance(); this.rsAPI = RoseStackerAPI.getInstance();
} }
public int countMobs(LivingEntity entity) { public void countMobs(LivingEntity entity, Consumer<Integer> callback) {
if (!rsAPI.isEntityStacked(entity)) { if (!rsAPI.isEntityStacked(entity)) {
// if the entity is not stacked, ignore // if the entity is not stacked, ignore
return 1; callback.accept(1);
return;
} }
StackedEntity stackedEntity = rsAPI.getStackedEntity(entity); StackedEntity stackedEntity = rsAPI.getStackedEntity(entity);
if (stackedEntity == null) { if (stackedEntity == null) {
return 1; callback.accept(1);
return;
} }
int before = stackedEntity.getStackSize();
boolean killAll = stackedEntity.getStackSettings().shouldKillEntireStackOnDeath(); boolean killAll = stackedEntity.getStackSettings().shouldKillEntireStackOnDeath();
// if we kill the entire stack, add the entire stack to the count // if we kill the entire stack, add the entire stack to the count
if (killAll) { if (killAll) {
return stackedEntity.getStackSize(); callback.accept(before);
return;
} }
return 1; Location stackedLocation = stackedEntity.getLocation();
Chunk stackedChunk = stackedEntity.getLocation().getChunk();
// check the stack size after a tick to see the difference
Bukkit.getRegionScheduler().runDelayed(toolStats, stackedLocation.getWorld(), stackedChunk.getX(), stackedChunk.getZ(), _ -> {
int after = stackedEntity.getStackSize();
int difference = before - after;
// if the diff goes negative, we killed more than the stack
// we killed the entire stack, so return the size
if (difference <= 0) {
difference = before;
}
callback.accept(difference);
}, 1);
} }
} }

View File

@@ -306,6 +306,35 @@ public class ItemChecker {
return null; return null;
} }
/**
* Get the player's axe.
*
* @param inventory Their inventory.
* @return Their axe, either main or offhand.
*/
public @Nullable ItemStack getAxe(PlayerInventory inventory) {
ItemStack main = inventory.getItemInMainHand();
ItemStack offHand = inventory.getItemInOffHand();
boolean isMain = main.getType().toString().endsWith("_AXE");
boolean isOffHand = offHand.getType().toString().endsWith("_AXE");
// if the player is holding an axe in their main hand, use that one
// if the axe is in their offhand instead, use that one after checking main hand
// Minecraft prioritizes main hand if the player holds in both hands
if (isMain && isOffHand) {
return main;
}
if (isMain) {
return main;
}
if (isOffHand) {
return offHand;
}
return null;
}
/** /**
* Checks the keys of the item and returns the tokens we should add. * Checks the keys of the item and returns the tokens we should add.
* If the server swaps token systems this should allow compatability. * If the server swaps token systems this should allow compatability.
@@ -363,6 +392,9 @@ public class ItemChecker {
if (container.has(toolStats.toolStatsKeys.getTridentThrows())) { if (container.has(toolStats.toolStatsKeys.getTridentThrows())) {
tokens.add("trident-throws"); tokens.add("trident-throws");
} }
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
tokens.add("logs-stripped");
}
if (tokens.isEmpty()) { if (tokens.isEmpty()) {
return null; return null;
} }

View File

@@ -1299,7 +1299,7 @@ public class ItemLore {
if (criticalStrikes == null) { if (criticalStrikes == null) {
criticalStrikes = 0; criticalStrikes = 0;
toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); toolStats.logger.warn("{} does not have valid critical-strikes set! Resting to zero. This should NEVER happen.", clone);
} }
container.set(toolStats.toolStatsKeys.getCriticalStrikes(), PersistentDataType.INTEGER, criticalStrikes + add); container.set(toolStats.toolStatsKeys.getCriticalStrikes(), PersistentDataType.INTEGER, criticalStrikes + add);
@@ -1397,7 +1397,7 @@ public class ItemLore {
if (tridentThrows == null) { if (tridentThrows == null) {
tridentThrows = 0; tridentThrows = 0;
toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); toolStats.logger.warn("{} does not have valid trident-throws set! Resting to zero. This should NEVER happen.", clone);
} }
container.set(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER, tridentThrows + add); container.set(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER, tridentThrows + add);
@@ -1413,6 +1413,104 @@ public class ItemLore {
return meta; return meta;
} }
/**
* Add x to logs stripped.
*
* @param axe The axe used.
*/
public ItemMeta updateLogsStripped(ItemStack axe, int add) {
ItemStack clone = axe.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warn("{} does NOT have any meta! Unable to update stats.", clone);
return null;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
// if it's disabled, don't update the stats
// check to see if the item has the stats, remove them if it does
if (!toolStats.config.getBoolean("enabled.logs-stripped")) {
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped == null) {
return null;
}
container.remove(toolStats.toolStatsKeys.getLogsStripped());
// remove the applied token if this stat is disabled
if (container.has(toolStats.toolStatsKeys.getTokenApplied())) {
String appliedTokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING);
if (appliedTokens != null) {
// remove the token from the list
// if the list is empty, remove the PDC
// otherwise set the PDC back with the new list
List<String> newTokens = toolStats.itemChecker.removeToken(appliedTokens, "logs-stripped");
if (!newTokens.isEmpty()) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens));
} else {
container.remove(toolStats.toolStatsKeys.getTokenApplied());
}
}
}
if (meta.hasLore()) {
String oldLogsStripped = toolStats.numberFormat.formatInt(logsStripped);
Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStripped);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "logs-stripped");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.toolStatsKeys.getLogsStripped()) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, newTokens);
}
}
// the item does not have a valid token
if (!validToken) {
return null;
}
} else {
if (!validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, newTokens);
}
}
}
Integer logsStripped = 0;
if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) {
logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
}
if (logsStripped == null) {
logsStripped = 0;
toolStats.logger.warn("{} does not have valid logs-stripped set! Resting to zero. This should NEVER happen.", clone);
}
container.set(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER, logsStripped + add);
String oldLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped);
String newLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped + add);
Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStrippedFormatted);
Component newLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", newLogsStrippedFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/** /**
* Format the item owner lore. * Format the item owner lore.
* *
@@ -1667,6 +1765,26 @@ public class ItemLore {
finalItem.setItemMeta(meta); finalItem.setItemMeta(meta);
} }
} }
if (container.has(toolStats.toolStatsKeys.getTridentThrows())) {
Integer tridentThrows = container.get(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER);
if (tridentThrows != null) {
container.remove(toolStats.toolStatsKeys.getTridentThrows());
String tridentThrowsFormatted = toolStats.numberFormat.formatInt(tridentThrows);
Component lineToRemove = toolStats.configTools.formatLore("trident-throws", "{times}", tridentThrowsFormatted);
meta.lore(removeLore(meta.lore(), lineToRemove));
finalItem.setItemMeta(meta);
}
}
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped != null) {
container.remove(toolStats.toolStatsKeys.getLogsStripped());
String logsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped);
Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", logsStrippedFormatted);
meta.lore(removeLore(meta.lore(), lineToRemove));
finalItem.setItemMeta(meta);
}
}
if (removeMeta) { if (removeMeta) {
Integer origin = null; Integer origin = null;
if (container.has(toolStats.toolStatsKeys.getOriginType())) { if (container.has(toolStats.toolStatsKeys.getOriginType())) {

View File

@@ -153,6 +153,13 @@ public class TokenData {
tridentThrowsRecipe.setIngredient('S', Material.PRISMARINE_SHARD); tridentThrowsRecipe.setIngredient('S', Material.PRISMARINE_SHARD);
recipes.add(tridentThrowsRecipe); recipes.add(tridentThrowsRecipe);
NamespacedKey logsStrippedKey = new NamespacedKey(toolStats, "logs-stripped-token");
ShapedRecipe logsStrippedRecipe = new ShapedRecipe(logsStrippedKey, createToken("logs-stripped"));
logsStrippedRecipe.shape(" P ", "PSP", " P ");
logsStrippedRecipe.setIngredient('P', Material.PAPER);
logsStrippedRecipe.setIngredient('S', Material.WOODEN_AXE);
recipes.add(logsStrippedRecipe);
tokenTypes.add("crops-mined"); tokenTypes.add("crops-mined");
tokenTypes.add("blocks-mined"); tokenTypes.add("blocks-mined");
tokenTypes.add("damage-taken"); tokenTypes.add("damage-taken");
@@ -169,6 +176,7 @@ public class TokenData {
tokenTypes.add("enderdragon-kills"); tokenTypes.add("enderdragon-kills");
tokenTypes.add("critical-strikes"); tokenTypes.add("critical-strikes");
tokenTypes.add("trident-throws"); tokenTypes.add("trident-throws");
tokenTypes.add("logs-stripped");
} }
public Set<ShapedRecipe> getRecipes() { public Set<ShapedRecipe> getRecipes() {

View File

@@ -37,6 +37,7 @@ public class ToolStatsKeys {
private NamespacedKey criticalStrikes; private NamespacedKey criticalStrikes;
private NamespacedKey tridentThrows; private NamespacedKey tridentThrows;
private NamespacedKey originType; private NamespacedKey originType;
private NamespacedKey logsStripped;
public void make() { public void make() {
itemOwner = new NamespacedKey(toolStats, "owner"); itemOwner = new NamespacedKey(toolStats, "owner");
@@ -61,6 +62,7 @@ public class ToolStatsKeys {
criticalStrikes = new NamespacedKey(toolStats, "critical-strikes"); criticalStrikes = new NamespacedKey(toolStats, "critical-strikes");
tridentThrows = new NamespacedKey(toolStats, "trident-throws"); tridentThrows = new NamespacedKey(toolStats, "trident-throws");
originType = new NamespacedKey(toolStats, "origin"); originType = new NamespacedKey(toolStats, "origin");
logsStripped = new NamespacedKey(toolStats, "logs-stripped");
// save which stat can be used by a reset token // save which stat can be used by a reset token
tokenKeys.add(blocksMined); tokenKeys.add(blocksMined);
@@ -76,6 +78,7 @@ public class ToolStatsKeys {
tokenKeys.add(enderDragonKills); tokenKeys.add(enderDragonKills);
tokenKeys.add(criticalStrikes); tokenKeys.add(criticalStrikes);
tokenKeys.add(tridentThrows); tokenKeys.add(tridentThrows);
tokenKeys.add(logsStripped);
} }
public NamespacedKey getItemOwner() { public NamespacedKey getItemOwner() {
@@ -162,6 +165,10 @@ public class ToolStatsKeys {
return tridentThrows; return tridentThrows;
} }
public NamespacedKey getLogsStripped() {
return logsStripped;
}
/** /**
* Stores how an item was created. * Stores how an item was created.
* 0 = crafted. * 0 = crafted.

View File

@@ -43,6 +43,7 @@ public class ConfigUpdater {
case 13 -> new Version14(toolStats).update(); // 13 to 14 case 13 -> new Version14(toolStats).update(); // 13 to 14
case 14 -> new Version15(toolStats).update(); // 14 to 15 case 14 -> new Version15(toolStats).update(); // 14 to 15
case 15 -> new Version16(toolStats).update(); // 15 to 16 case 15 -> new Version16(toolStats).update(); // 15 to 16
case 16 -> new Version17(toolStats).update(); // 16 to 17
} }
} }
} }

View File

@@ -0,0 +1,82 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools.config.versions;
import lol.hyper.toolstats.ToolStats;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class Version17 {
private final ToolStats toolStats;
/**
* Used for updating from version 16 to 17.
*
* @param toolStats ToolStats instance.
*/
public Version17(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Perform the config update.
*/
public void update() {
// save the old config first
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config-16.yml");
} catch (IOException exception) {
toolStats.logger.error("Unable to save config-16.yml!", exception);
}
toolStats.logger.info("Updating config.yml to version 17.");
toolStats.config.set("config-version", 17);
toolStats.logger.info("Adding new token to config: logs-stripped");
toolStats.config.set("tokens.data.logs-stripped.title", "&7ToolStats: &8Logs Stripped Token");
toolStats.config.set("tokens.data.logs-stripped.lore", List.of(
"&8Combine with an axe in an anvil to track logs stripped.",
"&8Uses &7{levels} &8level."
));
toolStats.config.set("tokens.data.logs-stripped.levels", 1);
toolStats.config.set("tokens.data.logs-stripped.material", "PAPER");
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.enabled", false);
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.type", "float");
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.value", 1001);
toolStats.logger.info("Adding enabled.logs-stripped");
toolStats.config.set("enabled.logs-stripped", true);
toolStats.logger.info("Adding messages.logs-stripped");
toolStats.config.set("messages.logs-stripped", "&7Logs stripped: &8{logs}");
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.error("Unable to save config.yml!", exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 17. A copy of version 16 has been saved as config-16.yml");
}
}

View File

@@ -180,6 +180,17 @@ tokens:
enabled: false enabled: false
type: float type: float
value: 1001 value: 1001
logs-stripped:
title: "&7ToolStats: &8Logs Stripped Token"
lore:
- "&8Combine with an axe in an anvil to track logs stripped."
- "&8Uses &7{levels} &8level."
levels: 1
material: PAPER
custom-model-data:
enabled: false
type: float
value: 1001
enabled: enabled:
# Will show "Crafted by <player>" # Will show "Crafted by <player>"
@@ -349,6 +360,7 @@ enabled:
crops-harvested: true crops-harvested: true
critical-strikes: true critical-strikes: true
trident-throws: true trident-throws: true
logs-stripped: true
messages: messages:
crafted: crafted:
@@ -386,6 +398,7 @@ messages:
damage-done: "&7Damage done: &8{damage}" damage-done: "&7Damage done: &8{damage}"
critical-strikes: "&7Critical strikes: &8{strikes}" critical-strikes: "&7Critical strikes: &8{strikes}"
trident-throws: "&7Times thrown: &8{times}" trident-throws: "&7Times thrown: &8{times}"
logs-stripped: "&7Logs stripped: &8{logs}"
# Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html # Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html
mobs: mobs:
ZOMBIE: "Zombie" ZOMBIE: "Zombie"
@@ -424,4 +437,4 @@ world-limit:
- world_1 - world_1
- world_2 - world_2
config-version: 15 config-version: 17

View File

@@ -30,6 +30,9 @@ permissions:
toolstats.remove: toolstats.remove:
description: Allows the usage of /toolstats remove. description: Allows the usage of /toolstats remove.
default: op default: op
toolstats.add:
description: Allows the usage of /toolstats add.
default: op
dependencies: dependencies:
server: server: