Compare commits

...

84 Commits
1.8 ... 1.9.1

Author SHA1 Message Date
hyperdefined
434f0dfe30 Update pom.xml 2025-01-28 23:04:57 -05:00
hyperdefined
06a3bb2498 Update ItemLore.java 2025-01-28 23:04:40 -05:00
hyperdefined
5624c02ee2 make token system listen to config 2025-01-28 23:02:37 -05:00
hyperdefined
3e1c2dcbc3 adjust origins to listen to config 2025-01-28 22:51:49 -05:00
hyperdefined
8a1759b6b1 remove/add hashes when enabled/disabled 2025-01-28 21:53:39 -05:00
hyperdefined
b4be9eb741 make config updater better 2025-01-28 21:34:02 -05:00
hyperdefined
7a18649474 adding missing config values 2025-01-28 21:28:51 -05:00
hyperdefined
32f1e78630 don't add data when config is disabled
previously, this would only not add lore, but add data. this now does not add data. this also removes the data if it's present and disabled
2025-01-28 21:25:35 -05:00
hyperdefined
f78609b9ed Update Version9.java 2025-01-26 19:37:51 -05:00
hyperdefined
9ee1f773da handle lore better for tokens 2025-01-26 19:35:03 -05:00
hyperdefined
61f87a94f7 fix italics being on in names 2025-01-26 19:15:13 -05:00
hyperdefined
fd7f75fbb3 idk 2025-01-26 18:59:36 -05:00
hyperdefined
24ee5f9d42 im silly 2025-01-26 18:53:49 -05:00
hyperdefined
2b7c1350c3 Update Version9.java 2025-01-26 18:53:08 -05:00
hyperdefined
393f9b74c0 support for swapping modes
this makes sure the PDC matches when token mode is off so you can swap without issue
2025-01-26 18:51:36 -05:00
hyperdefined
c597b4ffeb make sure to do this for hoes 2025-01-26 18:16:03 -05:00
hyperdefined
a3f3e0552d Update config.yml 2025-01-26 17:15:15 -05:00
hyperdefined
636374134c Update ToolStats.java 2025-01-26 17:14:24 -05:00
hyperdefined
93f4363c9a Create reset-token.png 2025-01-26 17:01:00 -05:00
hyperdefined
a48a1492d2 Update TokenCrafting.java 2025-01-26 16:55:34 -05:00
hyperdefined
8e2e389f78 add this warning 2025-01-26 16:53:59 -05:00
hyperdefined
3da98afd49 correctly do the reset token 2025-01-26 16:48:34 -05:00
hyperdefined
e3629bf813 not possible, wroops 2025-01-26 16:14:46 -05:00
hyperdefined
f7697bccfa rename things 2025-01-26 14:43:47 -05:00
hyperdefined
73e472af3a add reset token 2025-01-26 14:37:24 -05:00
hyperdefined
4dda215805 use meta here instead 2025-01-26 14:36:45 -05:00
hyperdefined
eada8cc448 added missing picture 2025-01-26 13:41:33 -05:00
hyperdefined
5bb179bfa9 typo 2025-01-26 13:28:21 -05:00
hyperdefined
d515d66da8 allow axes player/mob kills 2025-01-26 13:25:22 -05:00
hyperdefined
b025fd0b46 removed more debugs 2025-01-26 13:24:45 -05:00
hyperdefined
390aeac226 allow hoes to have blocks mined 2025-01-26 13:21:16 -05:00
hyperdefined
985fb5380d Update config.yml 2025-01-26 13:16:01 -05:00
hyperdefined
dd584fd2b8 don't let the player use multiple tokens at once 2025-01-26 13:15:39 -05:00
hyperdefined
b8ac32f9b4 misc cleanup 2025-01-25 17:39:01 -05:00
hyperdefined
b14427ced2 Update README.md 2025-01-25 17:32:27 -05:00
hyperdefined
d0d2d1ad41 more images 2025-01-25 17:12:05 -05:00
hyperdefined
7051cfd8cf add images 2025-01-25 17:07:05 -05:00
hyperdefined
73254b3c88 classic 2025-01-25 16:59:11 -05:00
hyperdefined
d5a4c4e3ef Update pom.xml 2025-01-25 16:56:25 -05:00
hyperdefined
30b26533ef tokens update 2025-01-25 16:56:03 -05:00
hyperdefined
ff578e170e 1.8.7 2025-01-22 18:19:12 -05:00
hyperdefined
87aa26c7e9 Update ItemChecker.java 2025-01-22 18:17:49 -05:00
hyperdefined
2b5f784b93 Update config.yml 2025-01-22 18:17:33 -05:00
hyperdefined
94ff8f251d go back to text comparison
this broke something with another plugin
2025-01-22 18:10:35 -05:00
hyperdefined
f9fa15dbaf finally handle shift clicking
closes #89
2025-01-22 17:54:50 -05:00
hyperdefined
1a15fb94f4 misc cleanup 2025-01-22 17:02:31 -05:00
hyperdefined
44b70a8394 fix #90 2025-01-22 17:02:05 -05:00
hyperdefined
98ee84ad13 fix handling empty messages 2025-01-18 13:06:38 -05:00
hyperdefined
c68a04851b Update README.md 2024-12-31 13:21:41 -05:00
hyperdefined
06e063a072 Update hangar.yml 2024-12-30 23:29:24 -05:00
hyperdefined
c00c534d49 test complete 2024-12-30 23:28:35 -05:00
hyperdefined
d6f8176ce1 remove deprecated thing 2024-12-30 23:27:11 -05:00
hyperdefined
eb06dff0bc bro 2024-12-30 23:23:36 -05:00
hyperdefined
9a72c509ef hangar API test 2024-12-30 23:22:31 -05:00
hyperdefined
eaca8ac87d Update README.md 2024-12-23 19:24:04 -05:00
hyperdefined
44a2c5cd26 Update README.md 2024-12-23 19:16:29 -05:00
hyperdefined
0957a1c989 Update README.md 2024-12-23 19:07:03 -05:00
hyperdefined
7b12a130a0 1.8.5 2024-12-17 17:47:14 -05:00
hyperdefined
6b263be041 patience wizard 2024-12-15 18:31:38 -05:00
hyperdefined
aeaeee680b revert this since we can now 2024-12-15 18:31:09 -05:00
hyperdefined
d8c7425a5a 1.8.4 2024-12-15 18:29:46 -05:00
hyperdefined
86c92c9706 change these 2024-12-15 17:10:57 -05:00
hyperdefined
cd62f7309e update these 2024-12-14 18:31:14 -05:00
hyperdefined
e2b18cb5f1 Update README.md 2024-12-14 18:06:16 -05:00
hyperdefined
150956b2c9 Update pom.xml 2024-12-14 18:05:01 -05:00
hyperdefined
585e020baf migration to Paper API 2024-12-14 17:28:02 -05:00
hyperdefined
50b5823daf remove debug 2024-10-07 21:19:21 -04:00
hyperdefined
c87d0ca3bf Update pom.xml 2024-10-07 21:18:52 -04:00
hyperdefined
d1a16ff2e6 added missing breaks 2024-10-07 21:18:02 -04:00
hyperdefined
9134da31f4 support for mace #82 2024-10-07 21:16:07 -04:00
hyperdefined
0cf85edda6 support for custom mob names
closes #71
2024-10-07 21:05:46 -04:00
hyperdefined
7fe0234785 fix for older MC versions
fixes #81
2024-10-05 18:37:51 -04:00
hyperdefined
6385814b52 Merge pull request #80 from hyperdefined/dependabot/maven/org.bstats-bstats-bukkit-3.1.0
Bump org.bstats:bstats-bukkit from 3.0.3 to 3.1.0
2024-10-05 18:10:25 -04:00
hyperdefined
603e1ac1fd fix #79 2024-09-25 19:50:26 -04:00
dependabot[bot]
86dc5f421a Bump org.bstats:bstats-bukkit from 3.0.3 to 3.1.0
Bumps [org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.0.3 to 3.1.0.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.0.3...v3.1.0)

---
updated-dependencies:
- dependency-name: org.bstats:bstats-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 11:36:47 +00:00
hyperdefined
7d9be0cfd1 Update README.md 2024-09-10 17:19:15 -04:00
hyperdefined
f7bacec617 Update README.md 2024-09-10 17:17:51 -04:00
hyperdefined
227723fe10 fix #75 2024-09-10 17:08:40 -04:00
hyperdefined
785333e8d1 Update pom.xml 2024-09-10 16:02:04 -04:00
hyperdefined
bc784b8d46 fix elytra flight calculations
fixes #74
2024-09-10 16:01:45 -04:00
hyperdefined
f5ddada892 support for gradient coloring
fixes #72
2024-09-10 16:01:24 -04:00
hyperdefined
a43155b0c5 Fix armor damage + arrows on reset command 2024-09-08 22:57:41 -04:00
hyperdefined
e327d132e2 Update pom.xml 2024-09-08 22:52:55 -04:00
hyperdefined
f09f2d3703 Fix armor damage breaking (#70) 2024-09-08 22:51:49 -04:00
53 changed files with 3135 additions and 1186 deletions

31
.github/workflows/hangar.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Update Hangar Description
on:
push:
branches: [ master ]
jobs:
update-hangar-page:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Authenticate with Hangar
id: authenticate
run: |
RESPONSE=$(curl -s -X POST "https://hangar.papermc.io/api/v1/authenticate?apiKey=${{ secrets.HANGAR }}" -H 'accept: application/json')
TOKEN=$(echo $RESPONSE | jq -r '.token')
if [[ "$TOKEN" == "null" ]]; then
echo "Error: Unable to fetch JWT token"
exit 1
fi
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Update Project Description
run: |
README_CONTENT=$(cat README.md | jq -Rs .)
curl -s -X PATCH "https://hangar.papermc.io/api/v1/pages/editmain/ToolStats" \
-H "content-type: application/json" \
-H "Authorization: HangarAuth ${{ steps.authenticate.outputs.token }}" \
-d "{\"content\":$README_CONTENT}"

View File

@@ -15,10 +15,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn -B package --file pom.xml
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Build with Maven
run: mvn -B package --file pom.xml

View File

@@ -1,3 +1,4 @@
name: Update Modrinth Description
on:
push:
branches: [ master ]

View File

@@ -1,28 +1,28 @@
<h1 align="center">ToolStats</h1>
<p align="center">
<img src="https://img.shields.io/badge/Minecraft-1.15--1.21-orange" alt="Minecraft versions">
<img src="https://img.shields.io/github/v/release/hyperdefined/ToolStats" alt="GitHub release (latest by date)">
<a href="https://github.com/hyperdefined/ToolStats/releases"><img src="https://img.shields.io/github/downloads/hyperdefined/ToolStats/total?logo=github" alt="Downloads"></a>
<img src="https://img.shields.io/badge/made%20with-love%20&%20fluff-red" alt="Made with love & fluff">
<a href="https://ko-fi.com/hyperdefined"><img src="https://img.shields.io/badge/Donate-Ko--fi-red" alt="Donate via Ko-fi"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
<a href="https://wakatime.com/badge/user/992a7647-176a-477c-8086-e1abfba87ff4/project/0200f07a-f303-4103-a5f2-34b38c9c1fa4"><img src="https://wakatime.com/badge/user/992a7647-176a-477c-8086-e1abfba87ff4/project/0200f07a-f303-4103-a5f2-34b38c9c1fa4.svg" alt="wakatime"></a>
<a href="https://modrinth.com/plugin/ToolStats"><img alt="modrinth" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/available/modrinth_vector.svg"></a>
<a href="https://hangar.papermc.io/hyperdefined/ToolStats"><img alt="hangar" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/available/hangar_vector.svg"></a>
<a href="https://papermc.io"><img alt="paper" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/paper_vector.svg"></a>
<a href="https://github.com/hyperdefined/ToolStats/wiki"><img alt="ghpages" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/documentation/ghpages_vector.svg"></a>
<a href="https://discord.gg/rJuQXVcJz8"><img alt="discord-singular" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/discord-singular_vector.svg"></a>
<a href="https://buymeacoffee.com/hyperdefined"><img alt="buymeacoffee-singular" height="40" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/donate/buymeacoffee-singular_vector.svg"></a>
</p>
ToolStats is a small plugin that display various stats about tools. This plugin is inspired off of [GearStats](https://www.spigotmc.org/resources/gearstats.12960/). You can disable/enable which stats are shown on which tools via the config. Note: stats are tracked regardless of config setting. The config is to disable the lore on the item.
ToolStats is a Paper plugin that display various stats about tools. This plugin is inspired off of [GearStats](https://www.spigotmc.org/resources/gearstats.12960/). You can either track all statistics by default, or a use a token system to add statistics to tool/armor. You can configure how each statistic is shown on the item, or disable it!
Here is everything it tracks:
* Blocks mined (pickaxes, shovels, axes, hoes, shears)
* Crops mined (hoes)
* Player/mob kills (swords, axes, tridents, bows/crossbows)
* Player/mob kills (swords, axes, tridents, bows/crossbows, mace)
* Ownership of items when crafted, looted (from chests), traded, spawned via creative, and caught from fishing.
* Armor damage taken.
* Fish caught.
* Sheep sheared.
* Arrows shot (bows/crossbows)
* Flight time with elytras.
The best part is, this data is stored on the item itself. You can also change how the lore is displayed on the items!
The best part is, this data is stored on the item itself.
If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding.
@@ -37,6 +37,8 @@ If item lore is ever incorrect/missing, you can run `/toolstats reset`. This com
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image9.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image10.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image11.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image13.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image14.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image12.png)
## Documentation

BIN
images/crafting/anvil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
images/image13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
images/image14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

52
pom.xml
View File

@@ -23,13 +23,13 @@
<groupId>lol.hyper</groupId>
<artifactId>toolstats</artifactId>
<version>1.8</version>
<version>1.9.1</version>
<packaging>jar</packaging>
<name>ToolStats</name>
<properties>
<java.version>1.8</java.version>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -67,18 +67,10 @@
<pattern>org.bstats</pattern>
<shadedPattern>lol.hyper.toolstats.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>net.kyori.adventure</pattern>
<shadedPattern>lol.hyper.toolstats.adventure</shadedPattern>
</relocation>
<relocation>
<pattern>lol.hyper.githubreleaseapi</pattern>
<shadedPattern>lol.hyper.toolstats.updater</shadedPattern>
</relocation>
<relocation>
<pattern>space.arim.morepaperlib</pattern>
<shadedPattern>lol.hyper.toolstats.morepaperlib</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
@@ -104,26 +96,22 @@
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>arim-mvn-lgpl3</id>
<url>https://mvn-repo.arim.space/lesser-gpl3/</url>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.2-R0.1-SNAPSHOT</version>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.3</version>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -132,29 +120,5 @@
<version>1.0.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.17.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.3.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>space.arim.morepaperlib</groupId>
<artifactId>morepaperlib</artifactId>
<version>0.4.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>1.20-R0.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -21,26 +21,21 @@ import lol.hyper.githubreleaseapi.GitHubRelease;
import lol.hyper.githubreleaseapi.GitHubReleaseAPI;
import lol.hyper.toolstats.commands.CommandToolStats;
import lol.hyper.toolstats.events.*;
import lol.hyper.toolstats.tools.HashMaker;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.ItemLore;
import lol.hyper.toolstats.tools.NumberFormat;
import lol.hyper.toolstats.tools.*;
import lol.hyper.toolstats.tools.config.ConfigTools;
import lol.hyper.toolstats.tools.config.ConfigUpdater;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import lol.hyper.toolstats.tools.config.TokenItems;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import space.arim.morepaperlib.MorePaperLib;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
public final class ToolStats extends JavaPlugin {
@@ -48,7 +43,7 @@ public final class ToolStats extends JavaPlugin {
/**
* Stores who created an item.
*/
public final NamespacedKey genericOwner = new NamespacedKey(this, "owner");
public final NamespacedKey itemOwner = new NamespacedKey(this, "owner");
/**
* Stores when the item was created.
*/
@@ -56,15 +51,15 @@ public final class ToolStats extends JavaPlugin {
/**
* Stores how many player kills.
*/
public final NamespacedKey swordPlayerKills = new NamespacedKey(this, "player-kills");
public final NamespacedKey playerKills = new NamespacedKey(this, "player-kills");
/**
* Stores how many mob kills.
*/
public final NamespacedKey swordMobKills = new NamespacedKey(this, "mob-kills");
public final NamespacedKey mobKills = new NamespacedKey(this, "mob-kills");
/**
* Stores how many blocks were mined.
*/
public final NamespacedKey genericMined = new NamespacedKey(this, "generic-mined");
public final NamespacedKey blocksMined = new NamespacedKey(this, "generic-mined");
/**
* Stores how many crops were harvested.
*/
@@ -72,19 +67,15 @@ public final class ToolStats extends JavaPlugin {
/**
* Stores how many fish were caught.
*/
public final NamespacedKey fishingRodCaught = new NamespacedKey(this, "fish-caught");
public final NamespacedKey fishCaught = new NamespacedKey(this, "fish-caught");
/**
* Stores how many sheep were sheared.
*/
public final NamespacedKey shearsSheared = new NamespacedKey(this, "sheared");
public final NamespacedKey sheepSheared = new NamespacedKey(this, "sheared");
/**
* Stores how much damage an armor piece has taken.
*/
public final NamespacedKey armorDamage = new NamespacedKey(this, "damage-taken");
/**
* Stores how much damage an armor piece has taken (as an int).
*/
public final NamespacedKey armorDamageInt = new NamespacedKey(this, "damage-taken-int");
/**
* Key for tracking new elytras that spawn.
*/
@@ -101,6 +92,14 @@ public final class ToolStats extends JavaPlugin {
* Key for tracking flight time.
*/
public final NamespacedKey flightTime = new NamespacedKey(this, "flightTime");
/**
* Key for token type. This is for the token itself.
*/
public final NamespacedKey tokenType = new NamespacedKey(this, "token-type");
/**
* Key for applied token. This is what goes onto the tool/armor to record the type.
*/
public final NamespacedKey tokenApplied = new NamespacedKey(this, "token-applied");
/**
* Stores how an item was created.
* 0 = crafted.
@@ -113,11 +112,13 @@ public final class ToolStats extends JavaPlugin {
*/
public final NamespacedKey originType = new NamespacedKey(this, "origin");
public final int CONFIG_VERSION = 7;
public final int CONFIG_VERSION = 10;
public final Logger logger = this.getLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml");
public boolean tokens = false;
public final Set<NamespacedKey> tokenKeys = new HashSet<>();
public BlocksMined blocksMined;
public BlockBreak blockBreak;
public ChunkPopulate chunkPopulate;
public CraftItem craftItem;
public EntityDeath entityDeath;
@@ -134,26 +135,36 @@ public final class ToolStats extends JavaPlugin {
public PlayerJoin playerJoin;
public NumberFormat numberFormat;
public YamlConfiguration config;
private BukkitAudiences adventure;
public MorePaperLib morePaperLib;
public HashMaker hashMaker;
public CreativeEvent creativeEvent;
public PlayerMove playerMove;
public ItemChecker itemChecker;
public ShootBow shootBow;
public ConfigTools configTools;
public TokenItems tokenItems;
public TokenCrafting tokenCrafting;
public AnvilEvent anvilEvent;
public PrepareCraft prepareCraft;
@Override
public void onEnable() {
this.adventure = BukkitAudiences.create(this);
morePaperLib = new MorePaperLib(this);
if (!configFile.exists()) {
this.saveResource("config.yml", true);
logger.info("Copying default config!");
}
loadConfig();
configTools = new ConfigTools(this);
tokenItems = new TokenItems(this);
tokenCrafting = new TokenCrafting(this);
tokenCrafting.setup();
for (ShapedRecipe recipe : tokenCrafting.getRecipes()) {
if (tokens && config.getBoolean("tokens.craft-tokens")) {
Bukkit.addRecipe(recipe);
}
}
hashMaker = new HashMaker(this);
blocksMined = new BlocksMined(this);
blockBreak = new BlockBreak(this);
craftItem = new CraftItem(this);
chunkPopulate = new ChunkPopulate(this);
entityDeath = new EntityDeath(this);
@@ -170,11 +181,24 @@ public final class ToolStats extends JavaPlugin {
playerJoin = new PlayerJoin(this);
creativeEvent = new CreativeEvent(this);
playerMove = new PlayerMove(this);
itemChecker = new ItemChecker();
itemChecker = new ItemChecker(this);
itemChecker.setup();
shootBow = new ShootBow(this);
configTools = new ConfigTools(this);
anvilEvent = new AnvilEvent(this);
prepareCraft = new PrepareCraft(this);
Bukkit.getServer().getPluginManager().registerEvents(blocksMined, this);
// save which stat can be used by a reset token
tokenKeys.add(blocksMined);
tokenKeys.add(playerKills);
tokenKeys.add(mobKills);
tokenKeys.add(cropsHarvested);
tokenKeys.add(sheepSheared);
tokenKeys.add(fishCaught);
tokenKeys.add(flightTime);
tokenKeys.add(arrowsShot);
tokenKeys.add(armorDamage);
Bukkit.getServer().getPluginManager().registerEvents(blockBreak, this);
Bukkit.getServer().getPluginManager().registerEvents(chunkPopulate, this);
Bukkit.getServer().getPluginManager().registerEvents(craftItem, this);
Bukkit.getServer().getPluginManager().registerEvents(entityDeath, this);
@@ -190,12 +214,13 @@ public final class ToolStats extends JavaPlugin {
Bukkit.getServer().getPluginManager().registerEvents(creativeEvent, this);
Bukkit.getServer().getPluginManager().registerEvents(shootBow, this);
Bukkit.getServer().getPluginManager().registerEvents(playerMove, this);
Bukkit.getServer().getPluginManager().registerEvents(anvilEvent, this);
Bukkit.getServer().getPluginManager().registerEvents(prepareCraft, this);
this.getCommand("toolstats").setExecutor(commandToolStats);
new Metrics(this, 14110);
morePaperLib.scheduling().asyncScheduler().run(this::checkForUpdates);
Bukkit.getAsyncScheduler().runNow(this, scheduledTask -> checkForUpdates());
}
public void loadConfig() {
@@ -206,6 +231,14 @@ public final class ToolStats extends JavaPlugin {
configUpdater.updateConfig();
}
if (config.getBoolean("tokens.enabled")) {
logger.info("The token system is enabled! This means you must apply tokens to items in order for them to track.");
logger.info("Item origins (crafted, traded, found, etc) will still apply to items.");
logger.info("https://github.com/hyperdefined/ToolStats/wiki/Token-System");
}
tokens = config.getBoolean("tokens.enabled");
numberFormat = new NumberFormat(this);
}
@@ -218,7 +251,7 @@ public final class ToolStats extends JavaPlugin {
e.printStackTrace();
return;
}
GitHubRelease current = api.getReleaseByTag(this.getDescription().getVersion());
GitHubRelease current = api.getReleaseByTag(this.getPluginMeta().getVersion());
GitHubRelease latest = api.getLatestVersion();
if (current == null) {
logger.warning("You are running a version that does not exist on GitHub. If you are in a dev environment, you can ignore this. Otherwise, this is a bug!");
@@ -231,23 +264,4 @@ public final class ToolStats extends JavaPlugin {
logger.warning("A new version is available (" + latest.getTagVersion() + ")! You are running version " + current.getTagVersion() + ". You are " + buildsBehind + " version(s) behind.");
}
}
public BukkitAudiences getAdventure() {
if (this.adventure == null) {
throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!");
}
return this.adventure;
}
public void scheduleEntity(BukkitRunnable runnable, Entity entity, int delay) {
morePaperLib.scheduling().entitySpecificScheduler(entity).runDelayed(runnable, null, delay);
}
public void scheduleGlobal(BukkitRunnable runnable, int delay) {
morePaperLib.scheduling().globalRegionalScheduler().runDelayed(runnable, delay);
}
public void scheduleRegion(BukkitRunnable runnable, World world, Chunk chunk, int delay) {
morePaperLib.scheduling().regionSpecificScheduler(world, chunk.getX(), chunk.getZ()).runDelayed(runnable, delay);
}
}

View File

@@ -19,7 +19,6 @@ package lol.hyper.toolstats.commands;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
@@ -31,6 +30,7 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@@ -42,65 +42,130 @@ import java.util.*;
public class CommandToolStats implements TabExecutor {
private final ToolStats toolStats;
private final BukkitAudiences audiences;
public CommandToolStats(ToolStats toolStats) {
this.toolStats = toolStats;
this.audiences = toolStats.getAdventure();
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("toolstats.command")) {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
if (args.length == 0) {
audiences.sender(sender).sendMessage(Component.text("ToolStats version " + toolStats.getDescription().getVersion() + ". Created by hyperdefined.", NamedTextColor.GREEN));
sender.sendMessage(Component.text("ToolStats version " + toolStats.getPluginMeta().getVersion() + ". Created by hyperdefined.", NamedTextColor.GREEN));
return true;
}
switch (args[0]) {
case "reload": {
if (sender.hasPermission("toolstats.reload")) {
boolean oldTokensStatus = toolStats.tokens;
toolStats.loadConfig();
audiences.sender(sender).sendMessage(Component.text("Configuration reloaded!", NamedTextColor.GREEN));
toolStats.tokenCrafting.getRecipes().clear();
toolStats.tokenCrafting.setup();
// if the server went from tokens off -> on, add the recipes
// if the server went from tokens on -> off, remove the recipes
if (toolStats.tokens != oldTokensStatus) {
// tokens are now enabled
if (toolStats.tokens) {
sender.sendMessage(Component.text("It looks like you ENABLED the token system. While this is fine, it can break. Please restart your server instead.", NamedTextColor.YELLOW));
if (toolStats.config.getBoolean("tokens.craft-token")) {
for (ShapedRecipe recipe : toolStats.tokenCrafting.getRecipes()) {
Bukkit.addRecipe(recipe);
}
}
} else {
// tokens are now disabled
sender.sendMessage(Component.text("It looks like you DISABLED the token system. While this is fine, it can break. Please restart your server instead.", NamedTextColor.YELLOW));
for (ShapedRecipe recipe : toolStats.tokenCrafting.getRecipes()) {
Bukkit.removeRecipe(recipe.getKey());
}
}
}
sender.sendMessage(Component.text("Configuration reloaded!", NamedTextColor.GREEN));
} else {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
}
return true;
}
case "reset": {
if (!sender.hasPermission("toolstats.reset")) {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
if (sender instanceof ConsoleCommandSender) {
audiences.sender(sender).sendMessage(Component.text("You must be a player for this command.", NamedTextColor.RED));
sender.sendMessage(Component.text("You must be a player for this command.", NamedTextColor.RED));
return true;
}
if (args.length == 2 && args[1].equalsIgnoreCase("confirm")) {
if (!sender.hasPermission("toolstats.reset.confirm")) {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
Player player = (Player) sender;
ItemStack heldItem = player.getInventory().getItemInMainHand();
if (!toolStats.itemChecker.isValidItem(heldItem.getType())) {
audiences.sender(sender).sendMessage(Component.text("You must hold a valid item.", NamedTextColor.RED));
sender.sendMessage(Component.text("You must hold a valid item.", NamedTextColor.RED));
return true;
}
fixItemLore(heldItem, player);
audiences.sender(sender).sendMessage(Component.text("The lore was reset!", NamedTextColor.GREEN));
sender.sendMessage(Component.text("The lore was reset!", NamedTextColor.GREEN));
return true;
}
audiences.sender(sender).sendMessage(Component.text("This will remove ALL current lore from the held item and replace it with the correct lore.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("If the owner of the item is broken, it will reset to the person holding it.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Only use this if the tags on the tool are incorrect.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Type /toolstats reset confirm to confirm this.", NamedTextColor.GREEN));
sender.sendMessage(Component.text("This will remove ALL current lore from the held item and replace it with the correct lore.", NamedTextColor.GREEN));
sender.sendMessage(Component.text("If the owner of the item is broken, it will reset to the person holding it.", NamedTextColor.GREEN));
sender.sendMessage(Component.text("Only use this if the tags on the tool are incorrect.", NamedTextColor.GREEN));
sender.sendMessage(Component.text("Type /toolstats reset confirm to confirm this.", NamedTextColor.GREEN));
return true;
}
case "givetokens": {
if (!sender.hasPermission("toolstats.givetokens")) {
sender.sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
// Make sure /toolstats givetoken <player> <token> is present
if (args.length < 3) {
sender.sendMessage(Component.text("Invalid syntax. Usage: /toolstats givetokens <player> <token>", NamedTextColor.RED));
return true;
}
Player target = Bukkit.getPlayerExact(args[1]);
if (target == null) {
sender.sendMessage(Component.text("Player not found.", NamedTextColor.RED));
return true;
}
String tokenType = args[2];
if (!toolStats.tokenCrafting.getTokenTypes().contains(tokenType)) {
sender.sendMessage(Component.text("Invalid token type.", NamedTextColor.RED));
return true;
}
// make sure tokens are enabled before giving
if (!toolStats.config.getBoolean("tokens.enabled")) {
sender.sendMessage(Component.text("Unable to give tokens. Tokens are disabled", NamedTextColor.RED));
return true;
}
// if the user does not send in a number, default to 1
int amount = 1;
if (args.length >= 4) {
try {
amount = Integer.parseInt(args[3]);
if (amount <= 0) { // Optional: Prevent negative or zero values
sender.sendMessage(Component.text("Token quantity must be above or 1.", NamedTextColor.RED));
return true;
}
} catch (NumberFormatException exception) {
sender.sendMessage(Component.text("Invalid token quantity.", NamedTextColor.RED));
return true;
}
}
giveToken(target, tokenType, amount);
if (sender instanceof Player) {
sender.sendMessage(Component.text("Gave " + target.getName() + " " + amount + " " + tokenType + " tokens.", NamedTextColor.GREEN));
}
return true;
}
default: {
audiences.sender(sender).sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED));
sender.sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED));
}
}
return true;
@@ -119,7 +184,7 @@ public class CommandToolStats implements TabExecutor {
return;
}
PersistentDataContainer container = finalMeta.getPersistentDataContainer();
List<String> lore = new ArrayList<>();
List<Component> lore = new ArrayList<>();
// set how the item was obtained
Integer origin = -1;
@@ -145,27 +210,27 @@ public class CommandToolStats implements TabExecutor {
if (flightTime != null) {
if (toolStats.config.getBoolean("enabled.flight-time")) {
String line = toolStats.configTools.formatLore("flight-time", "{time}", toolStats.numberFormat.formatDouble((double) flightTime / 1000));
Component line = toolStats.configTools.formatLore("flight-time", "{time}", toolStats.numberFormat.formatDouble((double) flightTime / 1000));
lore.add(line);
}
}
if (timeCreated != null) {
String timeCreatedLine = toolStats.configTools.formatLore("looted.found-by", "{player}", player.getName());
String playerOwnerLine = toolStats.configTools.formatLore("looted.found-on", "{date}", toolStats.numberFormat.formatDate(new Date(timeCreated)));
Component timeCreatedLine = toolStats.configTools.formatLore("looted.found-by", "{player}", player.getName());
Component playerOwnerLine = toolStats.configTools.formatLore("looted.found-on", "{date}", toolStats.numberFormat.formatDate(new Date(timeCreated)));
lore.add(timeCreatedLine);
lore.add(playerOwnerLine);
}
finalMeta.setLore(lore);
finalMeta.lore(lore);
finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot();
player.getInventory().setItem(slot, finalItem);
}
if (toolStats.configTools.checkConfig(original.getType(), "created-by")) {
if (container.has(toolStats.genericOwner, new UUIDDataType())) {
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
if (container.has(toolStats.itemOwner, new UUIDDataType())) {
UUID owner = container.get(toolStats.itemOwner, new UUIDDataType());
String ownerName = null;
// if we can read the current owner
if (owner != null) {
@@ -176,9 +241,9 @@ public class CommandToolStats implements TabExecutor {
// if the owner's name is null for whatever reason, set the new owner
// to the current player running the command
if (ownerName == null) {
audiences.player(player).sendMessage(Component.text("The owner of this item is null. Setting to " + player.getName() + ".", NamedTextColor.RED));
player.sendMessage(Component.text("The owner of this item is null. Setting to " + player.getName() + ".", NamedTextColor.RED));
ownerName = player.getName();
container.set(toolStats.genericOwner, new UUIDDataType(), player.getUniqueId());
container.set(toolStats.itemOwner, new UUIDDataType(), player.getUniqueId());
}
// show how the item was created based on the previous lore
@@ -246,16 +311,16 @@ public class CommandToolStats implements TabExecutor {
}
}
if (toolStats.configTools.checkConfig(original.getType(), "player-kills")) {
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
if (container.has(toolStats.playerKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.playerKills, PersistentDataType.INTEGER);
if (kills != null) {
lore.add(toolStats.configTools.formatLore("kills.player", "{kills}", toolStats.numberFormat.formatInt(kills)));
}
}
}
if (toolStats.configTools.checkConfig(original.getType(), "mob-kills")) {
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
if (container.has(toolStats.mobKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.mobKills, PersistentDataType.INTEGER);
if (kills != null) {
lore.add(toolStats.configTools.formatLore("kills.mob", "{kills}", toolStats.numberFormat.formatInt(kills)));
}
@@ -270,24 +335,24 @@ public class CommandToolStats implements TabExecutor {
}
}
}
if (container.has(toolStats.genericMined, PersistentDataType.INTEGER)) {
Integer blocksMined = container.get(toolStats.genericMined, PersistentDataType.INTEGER);
if (container.has(toolStats.blocksMined, PersistentDataType.INTEGER)) {
Integer blocksMined = container.get(toolStats.blocksMined, PersistentDataType.INTEGER);
if (blocksMined != null) {
lore.add(toolStats.configTools.formatLore("blocks-mined", "{blocks}", toolStats.numberFormat.formatInt(blocksMined)));
}
}
}
if (toolStats.config.getBoolean("enabled.fish-caught")) {
if (container.has(toolStats.fishingRodCaught, PersistentDataType.INTEGER)) {
Integer fish = container.get(toolStats.fishingRodCaught, PersistentDataType.INTEGER);
if (container.has(toolStats.fishCaught, PersistentDataType.INTEGER)) {
Integer fish = container.get(toolStats.fishCaught, PersistentDataType.INTEGER);
if (fish != null) {
lore.add(toolStats.configTools.formatLore("fished.fish-caught", "{fish}", toolStats.numberFormat.formatInt(fish)));
}
}
}
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
if (container.has(toolStats.shearsSheared, PersistentDataType.INTEGER)) {
Integer sheep = container.get(toolStats.shearsSheared, PersistentDataType.INTEGER);
if (container.has(toolStats.sheepSheared, PersistentDataType.INTEGER)) {
Integer sheep = container.get(toolStats.sheepSheared, PersistentDataType.INTEGER);
if (sheep != null) {
lore.add(toolStats.configTools.formatLore("sheep-sheared", "{sheep}", toolStats.numberFormat.formatInt(sheep)));
}
@@ -297,7 +362,7 @@ public class CommandToolStats implements TabExecutor {
if (container.has(toolStats.armorDamage, PersistentDataType.DOUBLE)) {
Double damage = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
if (damage != null) {
toolStats.configTools.formatLore("damage-taken", "{damage}", toolStats.numberFormat.formatDouble(damage));
lore.add(toolStats.configTools.formatLore("damage-taken", "{damage}", toolStats.numberFormat.formatDouble(damage)));
}
}
}
@@ -305,34 +370,111 @@ public class CommandToolStats implements TabExecutor {
if (container.has(toolStats.arrowsShot, PersistentDataType.INTEGER)) {
Integer arrows = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
if (arrows != null) {
toolStats.configTools.formatLore("arrows-shot", "{arrows}", toolStats.numberFormat.formatInt(arrows));
lore.add(toolStats.configTools.formatLore("arrows-shot", "{arrows}", toolStats.numberFormat.formatInt(arrows)));
}
}
}
finalMeta.setLore(lore);
finalMeta.lore(lore);
finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot();
player.getInventory().setItem(slot, finalItem);
}
/**
* Gives a player a token.
*
* @param target The player.
* @param tokenType The token type.
*/
private void giveToken(Player target, String tokenType, int amount) {
switch (tokenType) {
case "crops-mined": {
ItemStack itemStack = toolStats.tokenItems.cropsMined();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "blocks-mined": {
ItemStack itemStack = toolStats.tokenItems.blocksMined();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "damage-taken": {
ItemStack itemStack = toolStats.tokenItems.damageTaken();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "mob-kills": {
ItemStack itemStack = toolStats.tokenItems.mobKills();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "player-kills": {
ItemStack itemStack = toolStats.tokenItems.playerKills();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "arrows-shot": {
ItemStack itemStack = toolStats.tokenItems.arrowsShot();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "sheep-sheared": {
ItemStack itemStack = toolStats.tokenItems.sheepSheared();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "flight-time": {
ItemStack itemStack = toolStats.tokenItems.flightTime();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "fish-caught": {
ItemStack itemStack = toolStats.tokenItems.fishCaught();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
case "reset": {
ItemStack itemStack = toolStats.tokenItems.resetToken();
itemStack.setAmount(amount);
target.getInventory().addItem(itemStack);
break;
}
}
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
if (args.length == 1) {
List<String> suggestions = new ArrayList<>();
if (sender.hasPermission("toolstats.reload")) {
return Arrays.asList("reset", "reload");
suggestions.add("reload");
}
if (sender.hasPermission("toolstats.reset")) {
return Collections.singletonList("reset");
suggestions.add("reset");
}
}
if (args.length == 2) {
if (args[0].equalsIgnoreCase("reset")) {
if (sender.hasPermission("toolstats.reset.confirm")) {
return Collections.singletonList("confirm");
}
if (sender.hasPermission("toolstats.givetokens")) {
suggestions.add("givetokens");
}
return suggestions.isEmpty() ? null : suggestions;
}
if (args.length == 2 && args[0].equalsIgnoreCase("reset") && sender.hasPermission("toolstats.reset.confirm")) {
return Collections.singletonList("confirm");
}
if (args.length == 3 && args[0].equalsIgnoreCase("givetokens") && sender.hasPermission("toolstats.givetokens")) {
return toolStats.tokenCrafting.getTokenTypes();
}
return null;
}
}

View File

@@ -0,0 +1,363 @@
/*
* 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.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Locale;
public class AnvilEvent implements Listener {
private final ToolStats toolStats;
public AnvilEvent(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onAnvilEvent(PrepareAnvilEvent event) {
// only listen if the token system is enabled
if (!toolStats.config.getBoolean("tokens.enabled")) {
return;
}
AnvilInventory inventory = event.getInventory();
ItemStack firstSlot = inventory.getItem(0);
ItemStack secondSlot = inventory.getItem(1);
// make sure both slots have items
if (firstSlot == null || secondSlot == null) {
return;
}
Material firstSlotMaterial = firstSlot.getType();
Material secondSlotMaterial = secondSlot.getType();
// make sure the first item is a valid item
if (!toolStats.itemChecker.isValidItem(firstSlotMaterial)) {
return;
}
PersistentDataContainer secondSlotContainer = secondSlot.getItemMeta().getPersistentDataContainer();
// make sure the 2nd item is one of ours
if (secondSlotMaterial != Material.PAPER || !secondSlotContainer.has(toolStats.tokenType, PersistentDataType.STRING)) {
return;
}
// get the type from the token
String tokenType = secondSlotContainer.get(toolStats.tokenType, PersistentDataType.STRING);
if (tokenType == null) {
return;
}
// don't let the player use more than one
if (secondSlot.getAmount() > 1) {
return;
}
// clone the item
ItemStack clone = firstSlot.clone();
if (tokenType.equalsIgnoreCase("reset")) {
reset(event, clone);
return;
}
// if the item is a mining tool
if (toolStats.itemChecker.isMineTool(firstSlotMaterial)) {
if (firstSlotMaterial.toString().toLowerCase(Locale.ROOT).contains("hoe")) {
// the item is a hoe
if (tokenType.equalsIgnoreCase("blocks-mined")) {
addToken(event, tokenType, "blocks-mined", clone);
}
if (tokenType.equalsIgnoreCase("crops-mined")) {
addToken(event, tokenType, "crops-mined", clone);
}
} else {
// since shears will fall under here, check if the token is for sheep sheared
if (firstSlotMaterial == Material.SHEARS && tokenType.equals("sheep-sheared")) {
addToken(event, tokenType, "sheep-sheared", clone);
return;
}
addToken(event, tokenType, "blocks-mined", clone);
}
// axes are a mining tool, so double check them here for player/mob kills
if (firstSlotMaterial.toString().toLowerCase(Locale.ROOT).contains("_axe")) {
if (tokenType.equalsIgnoreCase("player-kills")) {
addToken(event, tokenType, "player-kills", clone);
return;
}
if (tokenType.equalsIgnoreCase("mob-kills")) {
addToken(event, tokenType, "mob-kills", clone);
return;
}
}
return;
}
if (toolStats.itemChecker.isArmor(firstSlotMaterial)) {
addToken(event, tokenType, "damage-taken", clone);
return;
}
if (toolStats.itemChecker.isMeleeWeapon(firstSlotMaterial)) {
if (tokenType.equalsIgnoreCase("player-kills")) {
addToken(event, tokenType, "player-kills", clone);
return;
}
if (tokenType.equalsIgnoreCase("mob-kills")) {
addToken(event, tokenType, "mob-kills", clone);
return;
}
return;
}
if (firstSlotMaterial == Material.BOW || firstSlotMaterial == Material.CROSSBOW) {
if (tokenType.equalsIgnoreCase("player-kills")) {
addToken(event, tokenType, "player-kills", clone);
return;
}
if (tokenType.equalsIgnoreCase("mob-kills")) {
addToken(event, tokenType, "mob-kills", clone);
return;
}
if (tokenType.equalsIgnoreCase("arrows-shot")) {
addToken(event, tokenType, "arrows-shot", clone);
return;
}
return;
}
if (firstSlotMaterial == Material.ELYTRA) {
addToken(event, tokenType, "flight-time", clone);
return;
}
if (firstSlotMaterial == Material.FISHING_ROD) {
addToken(event, tokenType, "fish-caught", clone);
}
}
/**
* Add token to an item.
*
* @param event The anvil event.
* @param attemptToken The token in the 2nd slot of the anvil. The one the player wants to add.
* @param targetToken The token we are checking for. This should match the tool.
* @param firstSlotItem The item in the first slot.
*/
private void addToken(PrepareAnvilEvent event, String attemptToken, String targetToken, ItemStack firstSlotItem) {
// make sure the token we are using is for this tool
if (!attemptToken.equalsIgnoreCase(targetToken)) {
event.setResult(null);
return;
}
// if the item already has the token, ignore
if (toolStats.itemChecker.checkTokens(firstSlotItem.getItemMeta().getPersistentDataContainer(), targetToken)) {
event.setResult(null);
return;
}
// apply the token and set the result
ItemStack newItem = toolStats.itemChecker.addToken(firstSlotItem, targetToken);
switch (targetToken) {
case "crops-mined": {
if (toolStats.config.getBoolean("enabled.crops-harvested")) {
newItem.setItemMeta(toolStats.itemLore.updateCropsMined(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "blocks-mined": {
if (toolStats.configTools.checkConfig(newItem.getType(), "blocks-mined")) {
newItem.setItemMeta(toolStats.itemLore.updateBlocksMined(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "damage-taken": {
if (toolStats.config.getBoolean("enabled.armor-damage")) {
newItem.setItemMeta(toolStats.itemLore.updateDamage(newItem, 0.0, false));
} else {
event.setResult(null);
return;
}
break;
}
case "mob-kills": {
if (toolStats.configTools.checkConfig(newItem.getType(), "mob-kills")) {
newItem.setItemMeta(toolStats.itemLore.updateMobKills(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "player-kills": {
if (toolStats.configTools.checkConfig(newItem.getType(), "player-kills")) {
newItem.setItemMeta(toolStats.itemLore.updatePlayerKills(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "arrows-shot": {
if (toolStats.config.getBoolean("enabled.arrows-shot")) {
newItem.setItemMeta(toolStats.itemLore.updateArrowsShot(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "sheep-sheared": {
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
newItem.setItemMeta(toolStats.itemLore.updateSheepSheared(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "flight-time": {
if (toolStats.config.getBoolean("enabled.flight-time")) {
newItem.setItemMeta(toolStats.itemLore.updateFlightTime(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
case "fish-caught": {
if (toolStats.config.getBoolean("enabled.fish-caught")) {
newItem.setItemMeta(toolStats.itemLore.updateFishCaught(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
}
event.setResult(newItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken));
}
/**
* Reset an item's stats. This function is... gross.
* Because of how the lore system is set up, we have to basically revert the stats.
* The lore function requires the old value, then adds x to the current stat.
* This is required, so it can find the old line in the lore and update it.
* So we simply make the stat negative, and add it to reset it to zero.
* Gross? Yeah, but I don't want to rewrite the lore system again...
*
* @param event The PrepareAnvilEvent event.
* @param inputItem The input item to reset.
*/
private void reset(PrepareAnvilEvent event, ItemStack inputItem) {
ItemStack finalItem = inputItem.clone();
ItemMeta meta = finalItem.getItemMeta();
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.playerKills)) {
Integer playerKills = container.get(toolStats.playerKills, PersistentDataType.INTEGER);
if (playerKills == null) {
return;
}
meta = toolStats.itemLore.updatePlayerKills(finalItem, -playerKills);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.mobKills)) {
Integer mobKills = container.get(toolStats.mobKills, PersistentDataType.INTEGER);
if (mobKills == null) {
return;
}
meta = toolStats.itemLore.updateMobKills(finalItem, -mobKills);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.blocksMined)) {
Integer blocksMined = container.get(toolStats.blocksMined, PersistentDataType.INTEGER);
if (blocksMined == null) {
return;
}
meta = toolStats.itemLore.updateBlocksMined(finalItem, -blocksMined);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.cropsHarvested)) {
Integer cropsHarvested = container.get(toolStats.playerKills, PersistentDataType.INTEGER);
if (cropsHarvested == null) {
return;
}
meta = toolStats.itemLore.updateCropsMined(finalItem, -cropsHarvested);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.fishCaught)) {
Integer fishCaught = container.get(toolStats.fishCaught, PersistentDataType.INTEGER);
if (fishCaught == null) {
return;
}
meta = toolStats.itemLore.updateFishCaught(finalItem, -fishCaught);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.sheepSheared)) {
Integer sheepSheared = container.get(toolStats.sheepSheared, PersistentDataType.INTEGER);
if (sheepSheared == null) {
return;
}
meta = toolStats.itemLore.updateSheepSheared(finalItem, -sheepSheared);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.armorDamage)) {
Double armorDamage = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
if (armorDamage == null) {
return;
}
meta = toolStats.itemLore.updateDamage(finalItem, -armorDamage, true);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.arrowsShot)) {
Integer arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
if (arrowsShot == null) {
return;
}
meta = toolStats.itemLore.updateArrowsShot(finalItem, -arrowsShot);
finalItem.setItemMeta(meta);
}
if (container.has(toolStats.flightTime)) {
Long flightTime = container.get(toolStats.flightTime, PersistentDataType.LONG);
if (flightTime == null) {
return;
}
meta = toolStats.itemLore.updateFlightTime(finalItem, -flightTime);
finalItem.setItemMeta(meta);
}
event.setResult(finalItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost("reset"));
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Locale;
public class BlockBreak implements Listener {
private final ToolStats toolStats;
public BlockBreak(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
ItemStack heldItem = inventory.getItemInMainHand();
Block block = event.getBlock();
if (block.getType() == Material.CHEST) {
toolStats.playerInteract.openedChests.put(block, player);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> toolStats.playerInteract.openedChests.remove(block), 20);
}
// only check certain items
if (!toolStats.itemChecker.isMineTool(heldItem.getType())) {
return;
}
// if the item is a hoe
if (heldItem.getType().toString().toLowerCase(Locale.ROOT).contains("hoe")) {
// player is breaking crops with a hoe
if (block.getBlockData() instanceof Ageable ageable) {
// ignore crops that are not fully grown
if (ageable.getAge() != ageable.getMaximumAge()) {
return;
}
ItemMeta newMeta = toolStats.itemLore.updateCropsMined(heldItem, 1);
if (newMeta != null) {
// replace item in main hand
heldItem.setItemMeta(newMeta);
}
} else {
// item is a hoe, but not breaking crops
ItemMeta newMeta = toolStats.itemLore.updateBlocksMined(heldItem, 1);
if (newMeta != null) {
// replace item in main hand
heldItem.setItemMeta(newMeta);
}
}
} else {
// item is not a hoe
// update the blocks mined
ItemMeta newMeta = toolStats.itemLore.updateBlocksMined(heldItem, 1);
if (newMeta != null) {
// replace item in main hand
heldItem.setItemMeta(newMeta);
}
}
}
}

View File

@@ -1,164 +0,0 @@
/*
* 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.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.List;
import java.util.Locale;
public class BlocksMined implements Listener {
private final ToolStats toolStats;
public BlocksMined(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
ItemStack heldItem = inventory.getItemInMainHand();
Block block = event.getBlock();
if (block.getType() == Material.CHEST) {
toolStats.playerInteract.openedChests.put(block, player);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
toolStats.playerInteract.openedChests.remove(block);
}
};
toolStats.scheduleGlobal(runnable, 20);
}
// only check certain items
if (!toolStats.itemChecker.isMineTool(heldItem.getType())) {
return;
}
if (heldItem.getType().toString().toLowerCase(Locale.ROOT).contains("hoe")) {
// player is breaking crops with a hoe
if (block.getBlockData() instanceof Ageable) {
updateCropsMined(heldItem, (Ageable) block.getBlockData());
}
} else {
// update the blocks mined
updateBlocksMined(heldItem);
}
}
private void updateBlocksMined(ItemStack playerTool) {
ItemMeta meta = playerTool.getItemMeta();
if (meta == null) {
toolStats.logger.warning(playerTool + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer blocksMined = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.genericMined, PersistentDataType.INTEGER)) {
blocksMined = container.get(toolStats.genericMined, PersistentDataType.INTEGER);
}
if (blocksMined == null) {
blocksMined = 0;
toolStats.logger.warning(playerTool + " does not have valid generic-mined set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.genericMined, PersistentDataType.INTEGER, blocksMined + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(playerTool.getType(), "blocks-mined")) {
String oldBlocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined);
String newBlocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined + 1);
String oldLine = toolStats.configTools.formatLore("blocks-mined", "{blocks}", oldBlocksMinedFormatted);
String newLine = toolStats.configTools.formatLore("blocks-mined", "{blocks}", newBlocksMinedFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
playerTool.setItemMeta(meta);
}
private void updateCropsMined(ItemStack playerTool, Ageable block) {
// ignore crops that are not fully grown
if (block.getAge() != block.getMaximumAge()) {
return;
}
ItemMeta meta = playerTool.getItemMeta();
if (meta == null) {
toolStats.logger.warning(playerTool + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer cropsMined = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.cropsHarvested, PersistentDataType.INTEGER)) {
cropsMined = container.get(toolStats.cropsHarvested, PersistentDataType.INTEGER);
}
if (cropsMined == null) {
cropsMined = 0;
toolStats.logger.warning(playerTool + " does not have valid crops-mined set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.cropsHarvested, PersistentDataType.INTEGER, cropsMined + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(playerTool.getType(), "blocks-mined")) {
String oldCropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined);
String newCropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined + 1);
String oldLine = toolStats.configTools.formatLore("crops-harvested", "{crops}", oldCropsMinedFormatted);
String newLine = toolStats.configTools.formatLore("crops-harvested", "{crops}", newCropsMinedFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
playerTool.setItemMeta(meta);
}
}

View File

@@ -18,6 +18,7 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
@@ -31,7 +32,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
public class ChunkPopulate implements Listener {
@@ -49,34 +49,30 @@ public class ChunkPopulate implements Listener {
if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) {
return;
}
World world = event.getChunk().getWorld();
// this is delayed because entities are not loaded instantly
// we just check 1 second later
Chunk chunk = event.getChunk();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
for (Entity entity : chunk.getEntities()) {
// if there is a new item frame
if (!(entity instanceof ItemFrame)) {
Bukkit.getRegionScheduler().runDelayed(toolStats, world, chunk.getX(), chunk.getZ(), scheduledTask -> {
for (Entity entity : chunk.getEntities()) {
// if there is a new item frame
if (!(entity instanceof ItemFrame itemFrame)) {
continue;
}
// if the item frame has an elytra
if (itemFrame.getItem().getType() == Material.ELYTRA) {
ItemStack elytraCopy = itemFrame.getItem();
ItemMeta meta = elytraCopy.getItemMeta();
if (meta == null) {
continue;
}
ItemFrame itemFrame = (ItemFrame) entity;
// if the item frame has an elytra
if (itemFrame.getItem().getType() == Material.ELYTRA) {
ItemStack elytraCopy = itemFrame.getItem();
ItemMeta meta = elytraCopy.getItemMeta();
if (meta == null) {
continue;
}
// add the new tag so we know it's new
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.newElytra, PersistentDataType.INTEGER, 1);
elytraCopy.setItemMeta(meta);
itemFrame.setItem(elytraCopy);
}
// add the new tag so we know it's new
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.newElytra, PersistentDataType.INTEGER, 1);
elytraCopy.setItemMeta(meta);
itemFrame.setItem(elytraCopy);
}
}
};
toolStats.scheduleRegion(runnable, chunk.getWorld(), chunk, 20);
}, 20);
}
}

View File

@@ -19,7 +19,7 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.ChatColor;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -32,7 +32,9 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CraftItem implements Listener {
@@ -51,26 +53,51 @@ public class CraftItem implements Listener {
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
ItemStack itemStack = event.getCurrentItem();
if (itemStack == null || itemStack.getType() == Material.AIR) {
ItemStack craftedItem = event.getCurrentItem();
if (craftedItem == null || craftedItem.getType() == Material.AIR) {
return;
}
Material craftedMaterial = craftedItem.getType();
// only check certain items
if (!toolStats.itemChecker.isValidItem(itemStack.getType())) {
if (!toolStats.itemChecker.isValidItem(craftedMaterial)) {
return;
}
// if the player shift clicks, send them this warning
// if the player shift clicks
if (event.isShiftClick()) {
String configMessage = toolStats.config.getString("messages.shift-click-warning.crafting");
if (configMessage != null) {
if (!configMessage.isEmpty()) {
event.getWhoClicked().sendMessage(ChatColor.translateAlternateColorCodes('&', configMessage));
// store the player inventory before they craft the items
ItemStack[] beforeCraft = player.getInventory().getContents();
// run a tick after to see the changes
player.getScheduler().runDelayed(toolStats, scheduledTask -> {
// get their inventory after the craft
ItemStack[] afterCraft = player.getInventory().getContents();
for (int i = 0; i < afterCraft.length; i++) {
ItemStack newSlotItem = afterCraft[i];
ItemStack oldSlotItem = beforeCraft[i];
// if this slot is empty after crafting, skip it
if (newSlotItem == null) {
continue;
}
// if the item matches what we crafted
if (newSlotItem.getType() == craftedMaterial) {
// if the slot was empty before we crafted, this means we just made it
if (oldSlotItem == null) {
// add the lore
ItemStack newItem = addCraftOrigin(newSlotItem, player);
if (newItem != null) {
player.getInventory().setItem(i, newItem);
}
}
}
}
}
}, null, 1);
return;
}
// test the item before setting it
ItemStack newItem = addLore(itemStack, player);
// the player did not shift click
ItemStack newItem = addCraftOrigin(craftedItem, player);
if (newItem != null) {
// set the result
event.setCurrentItem(newItem);
@@ -84,7 +111,7 @@ public class CraftItem implements Listener {
* @param owner The player crafting.
* @return A copy of the item with the tags + lore.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
private ItemStack addCraftOrigin(ItemStack itemStack, Player owner) {
// clone the item
ItemStack newItem = itemStack.clone();
ItemMeta meta = newItem.getItemMeta();
@@ -99,44 +126,49 @@ public class CraftItem implements Listener {
// if the item already has the tag
// this is to prevent duplicate tags
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.itemOwner, PersistentDataType.LONG)) {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 0);
List<String> lore;
// get the current lore the item
List<Component> lore;
if (meta.hasLore()) {
lore = meta.getLore();
lore = meta.lore();
} else {
lore = new ArrayList<>();
}
// do we add the lore based on the config?
// if creation date is enabled, add it
if (toolStats.configTools.checkConfig(itemStack.getType(), "created-date")) {
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.originType, PersistentDataType.INTEGER, 0);
String date = toolStats.numberFormat.formatDate(finalDate);
String newLine = toolStats.configTools.formatLore("created.created-on", "{date}", date);
Component newLine = toolStats.configTools.formatLore("created.created-on", "{date}", date);
if (newLine == null) {
return null;
}
lore.add(newLine);
meta.setLore(lore);
meta.lore(lore);
}
// if creation owner is enabled, add it
if (toolStats.configTools.checkConfig(itemStack.getType(), "created-by")) {
String newLine = toolStats.configTools.formatLore("created.created-by", "{player}", owner.getName());
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 0);
Component newLine = toolStats.configTools.formatLore("created.created-by", "{player}", owner.getName());
if (newLine == null) {
return null;
}
lore.add(newLine);
meta.setLore(lore);
meta.lore(lore);
}
// if hash is enabled, add it
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
newItem.setItemMeta(meta);
return newItem;

View File

@@ -19,6 +19,7 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -64,50 +65,53 @@ public class CreativeEvent implements Listener {
}
// add the tags to the item
ItemStack newItem = addLore(spawnedItem, player);
ItemStack newItem = addCreativeOrigin(spawnedItem, player);
if (newItem != null) {
event.setCursor(newItem);
}
}
/**
* Adds tags to newly spawned items in creative.
* Adds spawned in tags to item.
*
* @param spawnedItem The item.
* @param itemStack The item add item to.
* @param owner The player spawning in.
* @return A copy of the item with the tags + lore.
*/
private ItemStack addLore(ItemStack spawnedItem, Player owner) {
ItemStack newSpawnedItem = spawnedItem.clone();
private ItemStack addCreativeOrigin(ItemStack itemStack, Player owner) {
ItemStack newSpawnedItem = itemStack.clone();
ItemMeta meta = newSpawnedItem.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newSpawnedItem + " does NOT have any meta! Unable to update stats.");
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return null;
}
// get the current time
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
// if the item already has an origin set, don't add it again
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
// if the item already has the tag
// this is to prevent duplicate tags
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.itemOwner, PersistentDataType.LONG)) {
return null;
}
// only make the hash if it's enabled
// if hash is enabled, add it
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(spawnedItem.getType(), owner.getUniqueId(), timeCreated);
String hash = toolStats.hashMaker.makeHash(newSpawnedItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 6);
// if spawned in is enabled, add it
if (toolStats.configTools.checkConfig(newSpawnedItem.getType(), "spawned-in")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 6);
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<Component> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.lore(newLore);
}
newSpawnedItem.setItemMeta(meta);
return newSpawnedItem;
}

View File

@@ -19,8 +19,10 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.*;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Trident;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -30,8 +32,6 @@ import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.*;
@@ -51,10 +51,9 @@ public class EntityDamage implements Listener {
return;
}
if (!(event.getEntity() instanceof LivingEntity)) {
if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) {
return;
}
LivingEntity mobBeingAttacked = (LivingEntity) event.getEntity();
// ignore void and /kill damage
EntityDamageEvent.DamageCause cause = event.getCause();
@@ -65,8 +64,7 @@ public class EntityDamage implements Listener {
// mob is going to die
if (mobBeingAttacked.getHealth() - event.getFinalDamage() <= 0) {
// a player is killing something
if (event.getDamager() instanceof Player) {
Player attackingPlayer = (Player) event.getDamager();
if (event.getDamager() instanceof Player attackingPlayer) {
if (attackingPlayer.getGameMode() == GameMode.CREATIVE || attackingPlayer.getGameMode() == GameMode.SPECTATOR) {
return;
}
@@ -78,90 +76,92 @@ public class EntityDamage implements Listener {
}
// a player is killing another player
if (mobBeingAttacked instanceof Player) {
updatePlayerKills(heldItem);
ItemMeta newItem = toolStats.itemLore.updatePlayerKills(heldItem, 1);
if (newItem != null) {
attackingPlayerInventory.getItemInMainHand().setItemMeta(newItem);
}
return;
}
// player is killing regular mob
updateMobKills(heldItem);
ItemMeta newItem = toolStats.itemLore.updateMobKills(heldItem, 1);
if (newItem != null) {
attackingPlayerInventory.getItemInMainHand().setItemMeta(newItem);
}
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
// trident is being thrown at something
if (event.getDamager() instanceof Trident) {
Trident trident = (Trident) event.getDamager();
ItemStack newTrident;
if (event.getDamager() instanceof Trident trident) {
ItemStack newTrident = trident.getItemStack();
ItemMeta newMeta;
// trident is killing player
if (mobBeingAttacked instanceof Player) {
newTrident = tridentPlayerKills(trident.getItem());
newMeta = toolStats.itemLore.updatePlayerKills(trident.getItemStack(), 1);
} else {
// trident is killing a mob
newTrident = tridentMobKills(trident.getItem());
newMeta = toolStats.itemLore.updateMobKills(trident.getItemStack(), 1);
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
if (newTrident != null) {
trident.setItem(newTrident);
if (newMeta != null) {
newTrident.setItemMeta(newMeta);
trident.setItemStack(newTrident);
}
}
// arrow is being shot
if (event.getDamager() instanceof Arrow) {
Arrow arrow = (Arrow) event.getDamager();
if (event.getDamager() instanceof Arrow arrow) {
// if the shooter is a player
if (arrow.getShooter() instanceof Player) {
Player shootingPlayer = (Player) arrow.getShooter();
if (arrow.getShooter() instanceof Player shootingPlayer) {
if (shootingPlayer.getGameMode() == GameMode.CREATIVE || shootingPlayer.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = shootingPlayer.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
ItemStack heldBow = null;
if (isMainHand) {
heldBow = inventory.getItemInMainHand();
}
if (isOffHand) {
heldBow = inventory.getItemInOffHand();
}
// if the player is holding a bow in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
heldBow = inventory.getItemInMainHand();
}
// player swapped
PlayerInventory shootingPlayerInventory = shootingPlayer.getInventory();
ItemStack heldBow = toolStats.itemChecker.getBow(shootingPlayerInventory);
if (heldBow == null) {
return;
}
// player is shooting another player
if (mobBeingAttacked instanceof Player) {
updatePlayerKills(heldBow);
ItemMeta newItem = toolStats.itemLore.updatePlayerKills(heldBow, 1);
if (newItem != null) {
shootingPlayerInventory.getItemInMainHand().setItemMeta(newItem);
}
} else {
updateMobKills(heldBow);
// player is shooting a mob
ItemMeta newItem = toolStats.itemLore.updateMobKills(heldBow, 1);
if (newItem != null) {
shootingPlayerInventory.getItemInMainHand().setItemMeta(newItem);
}
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
}
}
}
// player is taken damage but not being killed
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (mobBeingAttacked instanceof Player playerTakingDamage) {
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
ItemStack[] armorContents = playerInventory.getArmorContents();
for (ItemStack armorPiece : armorContents) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
ItemMeta newItem = toolStats.itemLore.updateDamage(armorPiece, event.getFinalDamage(), false);
if (newItem != null) {
armorPiece.setItemMeta(newItem);
}
}
}
}
// apply the new armor
playerInventory.setArmorContents(armorContents);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) {
return;
}
@@ -171,27 +171,32 @@ public class EntityDamage implements Listener {
return;
}
LivingEntity mobBeingAttacked = (LivingEntity) event.getEntity();
// player is taken damage but not being killed
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (mobBeingAttacked instanceof Player playerTakingDamage) {
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
ItemStack[] armorContents = playerInventory.getArmorContents();
for (ItemStack armorPiece : armorContents) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
ItemMeta newItem = toolStats.itemLore.updateDamage(armorPiece, event.getFinalDamage(),false);
if (newItem != null) {
armorPiece.setItemMeta(newItem);
}
}
}
}
// apply the new armor
playerInventory.setArmorContents(armorContents);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onDamage(EntityDamageByBlockEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) {
return;
}
@@ -201,225 +206,26 @@ public class EntityDamage implements Listener {
return;
}
LivingEntity mobBeingAttacked = (LivingEntity) event.getEntity();
// player is taken damage but not being killed
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (mobBeingAttacked instanceof Player playerTakingDamage) {
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
ItemStack[] armorContents = playerInventory.getArmorContents();
for (ItemStack armorPiece : armorContents) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
ItemMeta newItem = toolStats.itemLore.updateDamage(armorPiece, event.getFinalDamage(), false);
if (newItem != null) {
armorPiece.setItemMeta(newItem);
}
}
}
}
}
}
/**
* Updates a weapon's player kills.
*
* @param itemStack The item to update.
*/
private void updatePlayerKills(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return;
// apply the new armor
playerInventory.setArmorContents(armorContents);
}
Integer playerKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
playerKills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
}
if (playerKills == null) {
playerKills = 0;
toolStats.logger.warning(itemStack + " does not have valid player-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.swordPlayerKills, PersistentDataType.INTEGER, playerKills + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(itemStack.getType(), "player-kills")) {
String oldPlayerKillsFormatted = toolStats.numberFormat.formatInt(playerKills);
String newPlayerKillsFormatted = toolStats.numberFormat.formatInt(playerKills + 1);
String oldLine = toolStats.configTools.formatLore("kills.player", "{kills}", oldPlayerKillsFormatted);
String newLine = toolStats.configTools.formatLore("kills.player", "{kills}", newPlayerKillsFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
itemStack.setItemMeta(meta);
}
/**
* Updates a weapon's mob kills.
*
* @param itemStack The item to update.
*/
private void updateMobKills(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return;
}
Integer mobKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
mobKills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
}
if (mobKills == null) {
mobKills = 0;
toolStats.logger.warning(itemStack + " does not have valid mob-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.swordMobKills, PersistentDataType.INTEGER, mobKills + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(itemStack.getType(), "mob-kills")) {
String oldMobKillsFormatted = toolStats.numberFormat.formatInt(mobKills);
String newMobKillsFormatted = toolStats.numberFormat.formatInt(mobKills + 1);
String oldLine = toolStats.configTools.formatLore("kills.mob", "{kills}", oldMobKillsFormatted);
String newLine = toolStats.configTools.formatLore("kills.mob", "{kills}", newMobKillsFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
itemStack.setItemMeta(meta);
}
/**
* Updates a player's armor damage stats.
*
* @param itemStack The armor piece.
* @param damage How much damage is being added.
*/
private void updateDamage(ItemStack itemStack, double damage) {
// ignore if the damage is zero or negative
if (damage < 0) {
return;
}
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return;
}
Double damageTaken = 0.0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.armorDamage, PersistentDataType.DOUBLE)) {
damageTaken = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
}
if (damageTaken == null) {
damageTaken = 0.0;
toolStats.logger.warning(itemStack + " does not have valid damage-taken set! Resting to zero. This should NEVER happen.");
}
damageTaken = damageTaken + damage;
container.set(toolStats.armorDamage, PersistentDataType.DOUBLE, damageTaken + damage);
if (toolStats.config.getBoolean("enabled.armor-damage")) {
String oldDamageFormatted = toolStats.numberFormat.formatDouble(damageTaken);
String newDamageFormatted = toolStats.numberFormat.formatDouble(damageTaken + damage);
String oldLine = toolStats.configTools.formatLore("damage-taken", "{damage}", oldDamageFormatted);
String newLine = toolStats.configTools.formatLore("damage-taken", "{damage}", newDamageFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
itemStack.setItemMeta(meta);
}
/**
* Updates a trident's mob kills.
*
* @param trident The item to update.
*/
private ItemStack tridentMobKills(ItemStack trident) {
ItemStack newTrident = trident.clone();
ItemMeta meta = newTrident.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newTrident + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer mobKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
mobKills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
}
if (mobKills == null) {
mobKills = 0;
toolStats.logger.warning(newTrident + " does not have valid mob-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.swordMobKills, PersistentDataType.INTEGER, mobKills + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(newTrident.getType(), "mob-kills")) {
String oldMobKillsFormatted = toolStats.numberFormat.formatDouble(mobKills);
String newMobKillsFormatted = toolStats.numberFormat.formatDouble(mobKills + 1);
String oldLine = toolStats.configTools.formatLore("kills.mob", "{kills}", oldMobKillsFormatted);
String newLine = toolStats.configTools.formatLore("kills.mob", "{kills}", newMobKillsFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
newTrident.setItemMeta(meta);
return newTrident;
}
/**
* Updates a trident's player kills.
*
* @param trident The item to update.
*/
private ItemStack tridentPlayerKills(ItemStack trident) {
ItemStack newTrident = trident.clone();
ItemMeta meta = newTrident.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newTrident + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer playerKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
playerKills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
}
if (playerKills == null) {
playerKills = 0;
toolStats.logger.warning(newTrident + " does not have valid player-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.swordPlayerKills, PersistentDataType.INTEGER, playerKills + 1);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(newTrident.getType(), "player-kills")) {
String oldPlayerKillsFormatted = toolStats.numberFormat.formatDouble(playerKills);
String newPlayerKillsFormatted = toolStats.numberFormat.formatDouble(playerKills + 1);
String oldLine = toolStats.configTools.formatLore("kills.player", "{kills}", oldPlayerKillsFormatted);
String newLine = toolStats.configTools.formatLore("kills.player", "{kills}", newPlayerKillsFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
newTrident.setItemMeta(meta);
return newTrident;
}
}

View File

@@ -18,6 +18,7 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -29,7 +30,8 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.*;
import java.util.List;
import java.util.UUID;
public class EntityDeath implements Listener {
@@ -53,13 +55,13 @@ public class EntityDeath implements Listener {
ItemMeta droppedItemMeta = droppedItem.getItemMeta();
if (droppedItemMeta != null) {
PersistentDataContainer container = droppedItemMeta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG)) {
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
continue; // ignore any items that have our tags
}
}
if (toolStats.itemChecker.isValidItem(droppedItem.getType())) {
ItemStack newItem = addLore(droppedItem, livingEntity.getName());
ItemStack newItem = addLore(droppedItem, livingEntity);
if (newItem != null) {
event.getDrops().set(i, newItem);
}
@@ -73,23 +75,28 @@ public class EntityDeath implements Listener {
* Adds "drop by" tag to item.
*
* @param oldItem The item to add lore to.
* @param mob The mob or player name.
* @param entity The mob dying.
*/
private ItemStack addLore(ItemStack oldItem, String mob) {
private ItemStack addLore(ItemStack oldItem, LivingEntity entity) {
ItemStack newItem = oldItem.clone();
ItemMeta meta = newItem.getItemMeta();
if (meta == null) {
return null;
}
if (!toolStats.config.getBoolean("enabled.dropped-by")) {
return null;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.originType, PersistentDataType.INTEGER, 1);
if (toolStats.config.getBoolean("enabled.dropped-by")) {
String newLine = toolStats.configTools.formatLore("dropped-by", "{name}", mob);
List<String> newLore = toolStats.itemLore.addItemLore(meta, newLine);
meta.setLore(newLore);
String mobName = toolStats.config.getString("messages.mob." + entity.getType());
if (mobName == null) {
mobName = entity.getName();
}
Component newLine = toolStats.configTools.formatLore("dropped-by", "{name}", mobName);
List<Component> newLore = toolStats.itemLore.addItemLore(meta, newLine);
meta.lore(newLore);
newItem.setItemMeta(meta);
return newItem;
}

View File

@@ -19,6 +19,7 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -76,8 +77,7 @@ public class GenerateLoot implements Listener {
Player player = toolStats.playerInteract.openedChests.get(openedChest);
setLoot(event.getLoot(), player);
}
if (inventoryHolder instanceof StorageMinecart) {
StorageMinecart mineCart = (StorageMinecart) inventoryHolder;
if (inventoryHolder instanceof StorageMinecart mineCart) {
if (toolStats.playerInteract.openedMineCarts.containsKey(mineCart)) {
Player player = toolStats.playerInteract.openedMineCarts.get(mineCart);
setLoot(event.getLoot(), player);
@@ -92,7 +92,7 @@ public class GenerateLoot implements Listener {
* @param owner The player that found the item.
* @return The item with the lore.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
private ItemStack addLootedOrigin(ItemStack itemStack, Player owner) {
ItemStack newItem = itemStack.clone();
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
@@ -102,7 +102,11 @@ public class GenerateLoot implements Listener {
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
if (!toolStats.configTools.checkConfig(newItem.getType(), "looted-tag")) {
return null;
}
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.itemOwner, PersistentDataType.LONG)) {
return null;
}
@@ -113,14 +117,11 @@ public class GenerateLoot implements Listener {
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 2);
if (toolStats.configTools.checkConfig(newItem.getType(), "looted-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<Component> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.lore(newLore);
newItem.setItemMeta(meta);
return newItem;
}
@@ -139,7 +140,7 @@ public class GenerateLoot implements Listener {
continue;
}
if (toolStats.itemChecker.isValidItem(itemStack.getType())) {
ItemStack newItem = addLore(itemStack, player);
ItemStack newItem = addLootedOrigin(itemStack, player);
if (newItem != null) {
loot.set(i, newItem);
}

View File

@@ -19,7 +19,7 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
@@ -28,7 +28,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID;
@@ -47,7 +46,7 @@ public class InventoryOpen implements Listener {
}
Inventory inventory = event.getInventory();
Location location = event.getInventory().getLocation();
Player player = (Player) event.getPlayer();
for (ItemStack itemStack : inventory) {
if (itemStack == null) {
continue;
@@ -62,35 +61,47 @@ public class InventoryOpen implements Listener {
}
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
// generate a hash if the item doesn't have one (if it's enabled in the config)
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the token system is on and the item doesn't have stat keys
if (toolStats.itemChecker.keyCheck(container) && !container.has(toolStats.tokenType)) {
// add the tokens
String newTokens = toolStats.itemChecker.addTokensToExisting(itemStack);
if (newTokens == null) {
return;
}
container.set(toolStats.tokenApplied, PersistentDataType.STRING, newTokens);
itemStack.setItemMeta(itemMeta);
}
}
// generate a hash if the item doesn't have one (and enabled)
// if hashes are disabled and the item has one, remove it.
if (toolStats.config.getBoolean("generate-hash-for-items")) {
if (!container.has(toolStats.hash, PersistentDataType.STRING)) {
// make sure the item has an owner
if (!container.has(toolStats.genericOwner, new UUIDDataType())) {
continue;
UUID owner = null;
// get the current owner if there is one.
if (container.has(toolStats.itemOwner, new UUIDDataType())) {
owner = container.get(toolStats.itemOwner, new UUIDDataType());
}
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
// if there is no owner, use the player holding it
if (owner == null) {
continue;
owner = player.getUniqueId();
}
Long timestamp = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (timestamp == null) {
continue;
// if there is no time created, use now
timestamp = System.currentTimeMillis();
}
String hash = toolStats.hashMaker.makeHash(itemStack.getType(), owner, timestamp);
toolStats.logger.info(hash);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
itemStack.setItemMeta(itemMeta);
}
}
ItemMeta clone = itemMeta.clone();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
itemStack.setItemMeta(clone);
} else {
// if hashes are disabled but the item has one, remove it.
if (container.has(toolStats.hash, PersistentDataType.STRING)) {
container.remove(toolStats.hash);
itemStack.setItemMeta(itemMeta);
}
};
if (location != null) {
toolStats.scheduleRegion(runnable, location.getWorld(), location.getChunk(), 1);
}
}
}

View File

@@ -19,9 +19,13 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -48,13 +52,12 @@ public class PickupItem implements Listener {
return;
}
Entity entity = event.getEntity();
if (entity instanceof Player) {
Player player = (Player) entity;
if (entity instanceof Player player) {
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
Item item = event.getItem();
if (item.getType() == EntityType.DROPPED_ITEM) {
if (item.getType() == EntityType.ITEM) {
ItemStack itemStack = event.getItem().getItemStack();
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
@@ -64,7 +67,7 @@ public class PickupItem implements Listener {
if (itemStack.getType() == Material.ELYTRA) {
// the elytra has the new key, set the lore to it
if (container.has(toolStats.newElytra, PersistentDataType.INTEGER)) {
ItemStack newElytra = addLore(itemStack, (Player) event.getEntity());
ItemStack newElytra = addElytraOrigin(itemStack, (Player) event.getEntity());
if (newElytra != null) {
item.setItemStack(newElytra);
}
@@ -80,7 +83,7 @@ public class PickupItem implements Listener {
* @param itemStack The elytra to add lore to.
* @param owner The player who found it.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
private ItemStack addElytraOrigin(ItemStack itemStack, Player owner) {
ItemStack finalItem = itemStack.clone();
ItemMeta meta = finalItem.getItemMeta();
if (meta == null) {
@@ -90,6 +93,10 @@ public class PickupItem implements Listener {
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (!toolStats.config.getBoolean("enabled.elytra-tag")) {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(finalItem.getType(), owner.getUniqueId(), timeCreated);
@@ -97,15 +104,12 @@ public class PickupItem implements Listener {
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 4);
container.remove(toolStats.newElytra);
if (toolStats.config.getBoolean("enabled.elytra-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<Component> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.lore(newLore);
finalItem.setItemMeta(meta);
return finalItem;
}

View File

@@ -19,6 +19,7 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Item;
@@ -32,7 +33,6 @@ import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.util.Date;
import java.util.List;
@@ -60,14 +60,25 @@ public class PlayerFish implements Listener {
return;
}
ItemStack fishingRod = getItemStack(player);
ItemStack fishingRod = toolStats.itemChecker.getFishingRod(player.getInventory());
// player swapped items?
if (fishingRod == null) {
return;
}
// update the fishing rod!
updateFishCount(fishingRod);
ItemMeta newFishingRod = toolStats.itemLore.updateFishCaught(fishingRod, 1);
if (newFishingRod != null) {
PlayerInventory inventory = player.getInventory();
boolean isMain = inventory.getItemInMainHand().getType() == Material.FISHING_ROD;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.FISHING_ROD;
if (isMain) {
inventory.getItemInMainHand().setItemMeta(newFishingRod);
}
if (isOffHand) {
inventory.getItemInOffHand().setItemMeta(newFishingRod);
}
}
// check if the player caught an item
if (event.getCaught() == null) {
@@ -83,64 +94,6 @@ public class PlayerFish implements Listener {
}
}
private static @Nullable ItemStack getItemStack(Player player) {
PlayerInventory inventory = player.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.FISHING_ROD;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.FISHING_ROD;
ItemStack fishingRod = null;
if (isMainHand) {
fishingRod = inventory.getItemInMainHand();
}
if (isOffHand) {
fishingRod = inventory.getItemInOffHand();
}
// if the player is hold fishing rods in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
fishingRod = inventory.getItemInMainHand();
}
return fishingRod;
}
/**
* Update a fishing rod's fish count.
*
* @param fishingRod The fishing rod to update.
*/
private void updateFishCount(ItemStack fishingRod) {
ItemMeta meta = fishingRod.getItemMeta();
if (meta == null) {
toolStats.logger.warning(fishingRod + " does NOT have any meta! Unable to update stats.");
return;
}
Integer fishCaught = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.fishingRodCaught, PersistentDataType.INTEGER)) {
fishCaught = container.get(toolStats.fishingRodCaught, PersistentDataType.INTEGER);
}
if (fishCaught == null) {
fishCaught = 0;
toolStats.logger.warning(fishingRod + " does not have valid fish-caught set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.fishingRodCaught, PersistentDataType.INTEGER, fishCaught + 1);
if (toolStats.config.getBoolean("enabled.fish-caught")) {
String oldFishFormatted = toolStats.numberFormat.formatInt(fishCaught);
String newFishFormatted = toolStats.numberFormat.formatInt(fishCaught + 1);
String oldLine = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", oldFishFormatted);
String newLine = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", newFishFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
fishingRod.setItemMeta(meta);
}
/**
* Add lore to newly caught item.
*
@@ -158,22 +111,26 @@ public class PlayerFish implements Listener {
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
if (!toolStats.configTools.checkConfig(newItem.getType(), "fished-tag")) {
return null;
}
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 5);
if (toolStats.configTools.checkConfig(newItem.getType(), "fished-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.itemOwner, PersistentDataType.LONG)) {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 5);
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<Component> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.lore(newLore);
newItem.setItemMeta(meta);
return newItem;
}

View File

@@ -18,6 +18,7 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -30,7 +31,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.Map;
@@ -64,13 +64,7 @@ public class PlayerInteract implements Listener {
// store when a player opens a chest
if (block.getType() != Material.AIR && block.getType() == Material.CHEST) {
openedChests.put(block, player);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
openedChests.remove(block);
}
};
toolStats.scheduleGlobal(runnable, 20);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedChests.remove(block), 20);
}
}
@@ -82,16 +76,10 @@ public class PlayerInteract implements Listener {
return;
}
// store when a player opens a minecart
if (clicked.getType() == EntityType.MINECART_CHEST) {
if (clicked.getType() == EntityType.CHEST_MINECART) {
StorageMinecart storageMinecart = (StorageMinecart) clicked;
openedMineCarts.put(storageMinecart, player);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
openedMineCarts.remove(storageMinecart);
}
};
toolStats.scheduleGlobal(runnable, 20);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedMineCarts.remove(storageMinecart), 20);
}
}
}

View File

@@ -28,7 +28,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID;
@@ -59,31 +58,47 @@ public class PlayerJoin implements Listener {
}
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
// generate a hash if the item doesn't have one
if (!container.has(toolStats.hash, PersistentDataType.STRING)) {
// make sure the item has an owner
if (!container.has(toolStats.genericOwner, new UUIDDataType())) {
continue;
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the token system is on and the item doesn't have stat keys
if (toolStats.itemChecker.keyCheck(container) && !container.has(toolStats.tokenType)) {
// add the tokens
String newTokens = toolStats.itemChecker.addTokensToExisting(itemStack);
if (newTokens == null) {
return;
}
container.set(toolStats.tokenApplied, PersistentDataType.STRING, newTokens);
itemStack.setItemMeta(itemMeta);
}
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
if (owner == null) {
continue;
}
Long timestamp = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (timestamp == null) {
continue;
}
String hash = toolStats.hashMaker.makeHash(itemStack.getType(), owner, timestamp);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
ItemMeta clone = itemMeta.clone();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
itemStack.setItemMeta(clone);
// generate a hash if the item doesn't have one
if (toolStats.config.getBoolean("generate-hash-for-items")) {
if (!container.has(toolStats.hash, PersistentDataType.STRING)) {
UUID owner = null;
// get the current owner if there is one.
if (container.has(toolStats.itemOwner, new UUIDDataType())) {
owner = container.get(toolStats.itemOwner, new UUIDDataType());
}
// if there is no owner, use the player holding it
if (owner == null) {
owner = player.getUniqueId();
}
Long timestamp = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (timestamp == null) {
// if there is no time created, use now
timestamp = System.currentTimeMillis();
}
String hash = toolStats.hashMaker.makeHash(itemStack.getType(), owner, timestamp);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
itemStack.setItemMeta(itemMeta);
}
};
toolStats.scheduleEntity(runnable, player, 1);
} else {
// if hashes are disabled but the item has one, remove it.
if (container.has(toolStats.hash, PersistentDataType.STRING)) {
container.remove(toolStats.hash);
itemStack.setItemMeta(itemMeta);
}
}
}
}
}

View File

@@ -25,12 +25,10 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PlayerMove implements Listener {
@@ -54,53 +52,18 @@ public class PlayerMove implements Listener {
} else {
// player is not flying
if (playerStartFlight.containsKey(player)) {
trackFlight(player, playerStartFlight.get(player));
PlayerInventory inventory = player.getInventory();
ItemStack chest = inventory.getChestplate();
// make sure the player is wearing an elytra
if (chest != null && chest.getType() == Material.ELYTRA) {
long duration = (System.currentTimeMillis() - playerStartFlight.get(player));
ItemMeta newItem = toolStats.itemLore.updateFlightTime(chest, duration);
if (newItem != null) {
inventory.getChestplate().setItemMeta(newItem);
}
}
playerStartFlight.remove(player);
}
}
}
private void trackFlight(Player player, long startTime) {
ItemStack chest = player.getInventory().getChestplate();
// make sure their chest piece is an elytra
if (chest == null || chest.getType() != Material.ELYTRA) {
return;
}
ItemMeta meta = chest.getItemMeta();
if (meta == null) {
toolStats.logger.warning(chest + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Long flightTime = 0L;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.flightTime, PersistentDataType.LONG)) {
flightTime = container.get(toolStats.flightTime, PersistentDataType.LONG);
}
if (flightTime == null) {
flightTime = 0L;
toolStats.logger.warning(flightTime + " does not have valid flight-time set! Resting to zero. This should NEVER happen.");
}
// get the duration of the flight
long duration = (System.currentTimeMillis() - startTime) + flightTime;
container.set(toolStats.flightTime, PersistentDataType.LONG, flightTime + duration);
// do we add the lore based on the config?
if (toolStats.config.getBoolean("enabled.flight-time")) {
String oldFlightFormatted = toolStats.numberFormat.formatDouble((double) flightTime / 1000);
String newFlightFormatted = toolStats.numberFormat.formatDouble((double) (flightTime + duration) / 1000);
String oldLine = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightFormatted);
String newLine = toolStats.configTools.formatLore("flight-time", "{time}", newFlightFormatted);
if (oldLine == null || newLine == null) {
return;
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
chest.setItemMeta(meta);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
public class PrepareCraft implements Listener {
private final ToolStats toolStats;
public PrepareCraft(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onCraft(PrepareItemCraftEvent event) {
// get the items in the crafting grid
ItemStack[] grid = event.getInventory().getMatrix();
for (ItemStack item : grid) {
if (item == null || item.getType() != Material.PAPER) {
continue;
}
ItemMeta meta = item.getItemMeta();
if (meta == null) {
continue;
}
// if the paper item has our PDC, cancel it
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.tokenType)) {
event.getInventory().setResult(null);
}
}
}
}

View File

@@ -30,11 +30,6 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class SheepShear implements Listener {
@@ -54,82 +49,33 @@ public class SheepShear implements Listener {
return;
}
Entity entity = event.getRightClicked();
if (!(entity instanceof Sheep)) {
if (!(entity instanceof Sheep sheep)) {
return;
}
ItemStack shears = getShears(player);
// player swapped items?
if (shears == null) {
ItemStack heldShears = toolStats.itemChecker.getShears(player.getInventory());
// player swapped or we can't get the shears
if (heldShears == null) {
return;
}
Sheep sheep = (Sheep) entity;
// make sure the sheep is not sheared
if (sheep.isSheared()) {
return;
}
// update the stats
ItemStack finalShears = shears;
addLore(finalShears);
}
private static @Nullable ItemStack getShears(Player player) {
PlayerInventory inventory = player.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.SHEARS;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.SHEARS;
ItemStack shears = null;
if (isMainHand) {
shears = inventory.getItemInMainHand();
}
if (isOffHand) {
shears = inventory.getItemInOffHand();
}
// if the player is hold shears in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
shears = inventory.getItemInMainHand();
}
return shears;
}
/**
* Adds tags to shears.
*
* @param newShears The shears.
*/
private void addLore(ItemStack newShears) {
ItemMeta meta = newShears.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newShears + " does NOT have any meta! Unable to update stats.");
return;
}
Integer sheepSheared = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.shearsSheared, PersistentDataType.INTEGER)) {
sheepSheared = container.get(toolStats.shearsSheared, PersistentDataType.INTEGER);
}
if (sheepSheared == null) {
sheepSheared = 0;
toolStats.logger.warning(newShears + " does not have valid sheared set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.shearsSheared, PersistentDataType.INTEGER, sheepSheared + 1);
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
String oldSheepFormatted = toolStats.numberFormat.formatInt(sheepSheared);
String newSheepFormatted = toolStats.numberFormat.formatInt(sheepSheared + 1);
String oldLine = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", oldSheepFormatted);
String newLine = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", newSheepFormatted);
if (oldLine == null || newLine == null) {
return;
ItemMeta newItem = toolStats.itemLore.updateSheepSheared(heldShears, 1);
if (newItem != null) {
PlayerInventory inventory = player.getInventory();
boolean isMain = inventory.getItemInMainHand().getType() == Material.SHEARS;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.SHEARS;
if (isMain) {
inventory.getItemInMainHand().setItemMeta(newItem);
}
if (isOffHand) {
inventory.getItemInOffHand().setItemMeta(newItem);
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
newShears.setItemMeta(meta);
}
}

View File

@@ -29,11 +29,6 @@ import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ShootBow implements Listener {
@@ -47,78 +42,31 @@ public class ShootBow implements Listener {
public void onShoot(EntityShootBowEvent event) {
Entity shooter = event.getEntity();
// only listen for players
if (!(shooter instanceof Player)) {
if (!(shooter instanceof Player player)) {
return;
}
Player player = (Player) shooter;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.ADVENTURE) {
return;
}
PlayerInventory inventory = player.getInventory();
ItemStack heldBow = getBow(inventory);
// player swapped
ItemStack heldBow = toolStats.itemChecker.getBow(player.getInventory());
// player swapped or we can't get the bow
if (heldBow == null) {
return;
}
updateArrowsShot(heldBow);
}
private static @Nullable ItemStack getBow(PlayerInventory inventory) {
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
ItemStack heldBow = null;
if (isMainHand) {
heldBow = inventory.getItemInMainHand();
}
if (isOffHand) {
heldBow = inventory.getItemInOffHand();
}
// if the player is holding a bow in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
heldBow = inventory.getItemInMainHand();
}
return heldBow;
}
private void updateArrowsShot(ItemStack bow) {
ItemMeta meta = bow.getItemMeta();
if (meta == null) {
toolStats.logger.warning(bow + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer arrowsShot = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.arrowsShot, PersistentDataType.INTEGER)) {
arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
}
if (arrowsShot == null) {
arrowsShot = 0;
toolStats.logger.warning(arrowsShot + " does not have valid arrows-shot set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.arrowsShot, PersistentDataType.INTEGER, arrowsShot + 1);
// do we add the lore based on the config?
if (toolStats.config.getBoolean("enabled.arrows-shot")) {
String oldArrowsFormatted = toolStats.numberFormat.formatInt(arrowsShot);
String newArrowsFormatted = toolStats.numberFormat.formatInt(arrowsShot + 1);
String oldLine = toolStats.configTools.formatLore("arrows-shot", "{arrows}", oldArrowsFormatted);
String newLine = toolStats.configTools.formatLore("arrows-shot", "{arrows}", newArrowsFormatted);
if (oldLine == null || newLine == null) {
return;
ItemMeta newItem = toolStats.itemLore.updateArrowsShot(heldBow, 1);
if (newItem != null) {
PlayerInventory inventory = player.getInventory();
boolean isMain = inventory.getItemInMainHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.BOW || inventory.getItemInOffHand().getType() == Material.CROSSBOW;
if (isMain) {
inventory.getItemInMainHand().setItemMeta(newItem);
}
if (isOffHand) {
inventory.getItemInOffHand().setItemMeta(newItem);
}
List<String> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.setLore(newLore);
}
bow.setItemMeta(meta);
}
}

View File

@@ -19,8 +19,9 @@ package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.ChatColor;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -51,35 +52,62 @@ public class VillagerTrade implements Listener {
return;
}
Inventory inventory = event.getClickedInventory();
if (!(event.getWhoClicked() instanceof Player)) {
if (!(event.getWhoClicked() instanceof Player player)) {
return;
}
Player player = (Player) event.getWhoClicked();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// only check villager inventories
if (inventory instanceof MerchantInventory) {
// only check the result slot (the item you receive)
if (event.getSlotType() == InventoryType.SlotType.RESULT) {
ItemStack item = event.getCurrentItem();
// only check items we want
if (!toolStats.itemChecker.isValidItem(item.getType())) {
return;
}
// if the player shift clicks, show the warning
if (event.isShiftClick()) {
String configMessage = toolStats.config.getString("messages.shift-click-warning.trading");
if (configMessage != null) {
player.sendMessage(ChatColor.translateAlternateColorCodes('&', configMessage));
if (!(inventory instanceof MerchantInventory)) {
return;
}
// only check the result slot (the item you receive)
if (event.getSlotType() != InventoryType.SlotType.RESULT) {
return;
}
ItemStack tradedItem = event.getCurrentItem();
Material tradedMaterial = tradedItem.getType();
// only check items we want
if (!toolStats.itemChecker.isValidItem(tradedMaterial)) {
return;
}
// if the player shift clicks
if (event.isShiftClick()) {
// store the player inventory before they trade the items
ItemStack[] beforeTrade = player.getInventory().getContents();
// run a tick after to see the changes
player.getScheduler().runDelayed(toolStats, scheduledTask -> {
// get their inventory after the trade
ItemStack[] afterTrade = player.getInventory().getContents();
for (int i = 0; i < afterTrade.length; i++) {
ItemStack newSlotItem = afterTrade[i];
ItemStack oldSlotItem = beforeTrade[i];
// if this slot is empty after trading, skip it
if (newSlotItem == null) {
continue;
}
// if the item matches what we traded
if (newSlotItem.getType() == tradedMaterial) {
// if the slot was empty before we traded, this means we just traded it
if (oldSlotItem == null) {
// add the lore
ItemStack newItem = addTradeOrigin(newSlotItem, player);
if (newItem != null) {
player.getInventory().setItem(i, newItem);
}
}
}
}
ItemStack newItem = addLore(item, player);
if (newItem != null) {
// set the new item
inventory.setItem(event.getSlot(), newItem);
}
}
}, null, 1);
return;
}
ItemStack newItem = addTradeOrigin(tradedItem, player);
if (newItem != null) {
// set the new item
inventory.setItem(event.getSlot(), newItem);
}
}
@@ -90,7 +118,7 @@ public class VillagerTrade implements Listener {
* @param owner The player who traded.
* @return The item with lore.
*/
private ItemStack addLore(ItemStack oldItem, Player owner) {
private ItemStack addTradeOrigin(ItemStack oldItem, Player owner) {
ItemStack newItem = oldItem.clone();
ItemMeta meta = newItem.getItemMeta();
if (meta == null) {
@@ -101,7 +129,11 @@ public class VillagerTrade implements Listener {
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.itemOwner, PersistentDataType.LONG)) {
return null;
}
if (!toolStats.configTools.checkConfig(newItem.getType(), "traded-tag")) {
return null;
}
@@ -112,14 +144,11 @@ public class VillagerTrade implements Listener {
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.itemOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 3);
if (toolStats.configTools.checkConfig(newItem.getType(), "traded-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<Component> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.lore(newLore);
newItem.setItemMeta(meta);
return newItem;
}

View File

@@ -17,9 +17,18 @@
package lol.hyper.toolstats.tools;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -29,11 +38,16 @@ public class ItemChecker {
private final List<Material> armorItems = new ArrayList<>();
private final List<Material> meleeItems = new ArrayList<>();
private final List<Material> mineItems = new ArrayList<>();
private final ToolStats toolStats;
public ItemChecker(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Creates an item checker and saves all valid items we want.
* Set up the item checker.
*/
public ItemChecker() {
public void setup() {
for (Material material : Material.values()) {
String lowerCase = material.toString().toLowerCase(Locale.ROOT);
if (lowerCase.contains("_pickaxe") || lowerCase.contains("_axe") || lowerCase.contains("_hoe") || lowerCase.contains("_shovel")) {
@@ -52,9 +66,11 @@ public class ItemChecker {
// hardcode these
mineItems.add(Material.SHEARS);
meleeItems.add(Material.TRIDENT);
meleeItems.add(Material.MACE);
validItems.add(Material.BOW);
validItems.add(Material.FISHING_ROD);
validItems.add(Material.CROSSBOW);
validItems.add(Material.FISHING_ROD);
validItems.add(Material.ELYTRA);
// combine the lists
@@ -102,4 +118,228 @@ public class ItemChecker {
public boolean isMineTool(Material itemType) {
return mineItems.contains(itemType);
}
/**
* Check a given item for a target token.
*
* @param container The PDC of the item.
* @param targetToken The target to look for.
* @return True if the item has a given token, false if not.
*/
public boolean checkTokens(PersistentDataContainer container, String targetToken) {
// make sure the item has tokens
if (!container.has(toolStats.tokenApplied, PersistentDataType.STRING)) {
return false;
}
// get the tokens for this item
String tokens = container.get(toolStats.tokenApplied, PersistentDataType.STRING);
if (tokens == null) {
return false;
}
return tokens.contains(targetToken);
}
/**
* Get the tokens for a given item.
*
* @param item The item.
* @return An array of the tokens, empty if there are none.
*/
private String[] getTokens(ItemStack item) {
// make sure the item has tokens
ItemMeta meta = item.getItemMeta();
if (meta == null) {
return new String[0];
}
PersistentDataContainer container = meta.getPersistentDataContainer();
if (!container.has(toolStats.tokenApplied, PersistentDataType.STRING)) {
return new String[0];
}
// get the tokens for this item
String tokensRaw = container.get(toolStats.tokenApplied, PersistentDataType.STRING);
if (tokensRaw == null) {
return new String[0];
}
return tokensRaw.split(",");
}
/**
* Add a token to an item.
*
* @param item The item.
* @param token The token to add.
* @return The new PDC with the new token. Null if something went wrong.
*/
public ItemStack addToken(ItemStack item, String token) {
ItemMeta meta = item.getItemMeta();
if (meta == null) {
return null;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
String[] tokens = getTokens(item);
// there are no tokens
if (tokens.length == 0) {
container.set(toolStats.tokenApplied, PersistentDataType.STRING, token);
} else {
// other tokens exist, so add
String[] newTokens = Arrays.copyOf(tokens, tokens.length + 1);
newTokens[tokens.length] = token;
container.set(toolStats.tokenApplied, PersistentDataType.STRING, String.join(",", newTokens));
}
item.setItemMeta(meta);
return item;
}
/**
* Get the XP levels required to use token in anvil.
*
* @param tokenType The token type.
* @return The amount of levels to use.
*/
public int getCost(String tokenType) {
return toolStats.config.getInt("tokens.data." + tokenType + ".levels");
}
/**
* Get the player's bow/crossbow.
*
* @param inventory Their inventory.
* @return Their bow/crossbow, either main or offhand.
*/
public @Nullable ItemStack getBow(PlayerInventory inventory) {
ItemStack main = inventory.getItemInMainHand();
ItemStack offHand = inventory.getItemInOffHand();
boolean isMain = main.getType() == Material.BOW || main.getType() == Material.CROSSBOW;
boolean isOffHand = offHand.getType() == Material.BOW || offHand.getType() == Material.CROSSBOW;
// if the player is holding a bow in their main hand, use that one
// if the bow 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) {
return main;
}
if (isOffHand) {
return offHand;
}
return null;
}
/**
* Get the player's shears.
*
* @param inventory Their inventory.
* @return Their shears, either main or offhand.
*/
public @Nullable ItemStack getShears(PlayerInventory inventory) {
ItemStack main = inventory.getItemInMainHand();
ItemStack offHand = inventory.getItemInOffHand();
boolean isMain = main.getType() == Material.SHEARS;
boolean isOffHand = offHand.getType() == Material.SHEARS;
// if the player is holding shears in their main hand, use that one
// if the shears are in their offhand instead, use that one after checking main hand
// Minecraft prioritizes main hand if the player holds in both hands
if (isMain) {
return main;
}
if (isOffHand) {
return offHand;
}
return null;
}
/**
* Get the player's fishing rod.
*
* @param inventory Their inventory.
* @return Their fishing rod, either main or offhand.
*/
public @Nullable ItemStack getFishingRod(PlayerInventory inventory) {
ItemStack main = inventory.getItemInMainHand();
ItemStack offHand = inventory.getItemInOffHand();
boolean isMain = main.getType() == Material.FISHING_ROD;
boolean isOffHand = offHand.getType() == Material.FISHING_ROD;
// if the player is holding a fishing rod in their main hand, use that one
// if the fishing rod 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) {
return main;
}
if (isOffHand) {
return offHand;
}
return null;
}
/**
* Checks the keys of the item and returns the tokens we should add.
* If the server swaps token systems this should allow compatability.
*
* @param item The input item.
* @return The tokens we should add.
*/
public String addTokensToExisting(ItemStack item) {
ItemStack clone = item.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
return null;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
ArrayList<String> tokens = new ArrayList<>();
if (container.has(toolStats.playerKills)) {
tokens.add("player-kills");
}
if (container.has(toolStats.mobKills)) {
tokens.add("mob-kills");
}
if (container.has(toolStats.blocksMined)) {
tokens.add("blocks-mined");
}
if (container.has(toolStats.cropsHarvested)) {
tokens.add("crops-mined");
}
if (container.has(toolStats.fishCaught)) {
tokens.add("fish-caught");
}
if (container.has(toolStats.sheepSheared)) {
tokens.add("sheep-sheared");
}
if (container.has(toolStats.armorDamage)) {
tokens.add("damage-taken");
}
if (container.has(toolStats.arrowsShot)) {
tokens.add("arrows-shot");
}
if (container.has(toolStats.flightTime)) {
tokens.add("flight-time");
}
if (tokens.isEmpty()) {
return null;
}
return String.join(",", tokens);
}
/**
* Check to see if a given container has our keys for stats.
*
* @param container The container.
* @return True/false if the container has keys.
*/
public boolean keyCheck(PersistentDataContainer container) {
return container.getKeys().stream()
.map(NamespacedKey::getKey)
.anyMatch(key -> toolStats.tokenKeys.stream().anyMatch(tokenKey -> tokenKey.getKey().equalsIgnoreCase(key)));
}
}

View File

@@ -18,19 +18,19 @@
package lol.hyper.toolstats.tools;
import lol.hyper.toolstats.ToolStats;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class ItemLore {
private final ToolStats toolStats;
public static final Pattern COLOR_CODES = Pattern.compile("[&§]([0-9a-fk-or])");
public static final Pattern HEX_PATTERN = Pattern.compile("[&§]#([A-Fa-f0-9]{6})");
public ItemLore(ToolStats toolStats) {
this.toolStats = toolStats;
@@ -44,18 +44,17 @@ public class ItemLore {
* @param newLine The new line to replace oldLine.
* @return The item's new lore.
*/
public List<String> updateItemLore(ItemMeta itemMeta, String oldLine, String newLine) {
List<String> itemLore;
oldLine = toolStats.configTools.removeColor(oldLine);
public List<Component> updateItemLore(ItemMeta itemMeta, Component oldLine, Component newLine) {
List<Component> itemLore;
if (itemMeta.hasLore()) {
itemLore = itemMeta.getLore();
itemLore = itemMeta.lore();
// keep track of line index
// this doesn't mess the lore of existing items
for (int x = 0; x < itemLore.size(); x++) {
// check to see if the line matches the config value
String line = PlainTextComponentSerializer.plainText().serialize(itemLore.get(x));
// find the old line to update, keeping index
// this means we update this line only!
String line = toolStats.configTools.removeColor(itemLore.get(x));
if (line.equals(oldLine)) {
if (line.equals(PlainTextComponentSerializer.plainText().serialize(oldLine))) {
itemLore.set(x, newLine);
return itemLore;
}
@@ -70,10 +69,17 @@ public class ItemLore {
return itemLore;
}
public List<String> addItemLore(ItemMeta itemMeta, String newLine) {
List<String> itemLore;
/**
* Add lore to a given item.
*
* @param itemMeta The item's meta.
* @param newLine The new line to add to the lore.
* @return The new item's lore.
*/
public List<Component> addItemLore(ItemMeta itemMeta, Component newLine) {
List<Component> itemLore;
if (itemMeta.hasLore()) {
itemLore = itemMeta.getLore();
itemLore = itemMeta.lore();
itemLore.add(newLine);
} else {
itemLore = new ArrayList<>();
@@ -82,6 +88,19 @@ public class ItemLore {
return itemLore;
}
/**
* Remove a given lore from an item.
*
* @param inputLore The item's lore.
* @param toRemove The line to remove.
* @return The lore with the line removed.
*/
public List<Component> removeLore(List<Component> inputLore, Component toRemove) {
List<Component> newLore = new ArrayList<>(inputLore);
newLore.removeIf(line -> PlainTextComponentSerializer.plainText().serialize(line).equals(PlainTextComponentSerializer.plainText().serialize(toRemove)));
return newLore;
}
/**
* Adds new ownership to an item.
*
@@ -90,9 +109,9 @@ public class ItemLore {
* @param formattedDate The date of the ownership.
* @return The item's new lore.
*/
public List<String> addNewOwner(ItemMeta itemMeta, String playerName, String formattedDate) {
String dateCreatedLore;
String itemOwnerLore;
public List<Component> addNewOwner(ItemMeta itemMeta, String playerName, String formattedDate) {
Component dateCreatedLore;
Component itemOwnerLore;
Integer origin = null;
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
@@ -103,7 +122,7 @@ public class ItemLore {
if (origin == null) {
toolStats.logger.info("Unable to determine origin for item " + itemMeta.getAsString());
toolStats.logger.info("This IS a bug, please report this to the GitHub.");
return itemMeta.getLore();
return itemMeta.lore();
}
// set the lore based on the origin
@@ -115,12 +134,12 @@ public class ItemLore {
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.looted.looted-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.looted.looted-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
break;
}
@@ -131,12 +150,12 @@ public class ItemLore {
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.traded.traded-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.traded.traded-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
break;
}
@@ -147,12 +166,12 @@ public class ItemLore {
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.looted.found-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.looted.found-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
break;
}
@@ -163,12 +182,12 @@ public class ItemLore {
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.fished.caught-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.fished.caught-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
break;
}
@@ -179,31 +198,792 @@ public class ItemLore {
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.spawned-in.spawned-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.spawned-in.spawned-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
return itemMeta.lore();
}
break;
}
default: {
toolStats.logger.warning("Origin " + origin + " was found. Data was modified OR something REALLY broke.");
toolStats.logger.warning(itemMeta.getAsString());
return itemMeta.getLore();
return itemMeta.lore();
}
}
List<String> newLore;
List<Component> newLore;
if (itemMeta.hasLore()) {
newLore = itemMeta.getLore();
newLore = itemMeta.lore();
} else {
newLore = new ArrayList<>();
}
newLore.add(dateCreatedLore.replace("{date}", formattedDate));
newLore.add(itemOwnerLore.replace("{player}", playerName));
newLore.add(dateCreatedLore);
newLore.add(itemOwnerLore);
return newLore;
}
/**
* Add x to the crops mined stat.
*
* @param playerTool The tool to update.
*/
public ItemMeta updateCropsMined(ItemStack playerTool, int add) {
ItemStack clone = playerTool.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
return null;
}
// read the current stats from the item
// if they don't exist, then start from 0
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.crops-harvested")) {
if (container.has(toolStats.cropsHarvested)) {
Integer cropsMined = container.get(toolStats.cropsHarvested, PersistentDataType.INTEGER);
if (cropsMined == null) {
return null;
}
container.remove(toolStats.cropsHarvested);
if (meta.hasLore()) {
String oldCropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined);
Component lineToRemove = toolStats.configTools.formatLore("crops-harvested", "{crops}", oldCropsMinedFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "crops-mined");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.cropsHarvested) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Integer cropsMined = 0;
if (container.has(toolStats.cropsHarvested, PersistentDataType.INTEGER)) {
cropsMined = container.get(toolStats.cropsHarvested, PersistentDataType.INTEGER);
}
if (cropsMined == null) {
cropsMined = 0;
toolStats.logger.warning(clone + " does not have valid crops-mined set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.cropsHarvested, PersistentDataType.INTEGER, cropsMined + add);
String oldCropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined);
String newCropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined + add);
Component oldLine = toolStats.configTools.formatLore("crops-harvested", "{crops}", oldCropsMinedFormatted);
Component newLine = toolStats.configTools.formatLore("crops-harvested", "{crops}", newCropsMinedFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add x to the blocks mined stat.
*
* @param playerTool The tool to update.
*/
public ItemMeta updateBlocksMined(ItemStack playerTool, int add) {
ItemStack clone = playerTool.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.configTools.checkConfig(clone.getType(), "blocks-mined")) {
if (container.has(toolStats.blocksMined)) {
Integer blocksMined = container.get(toolStats.blocksMined, PersistentDataType.INTEGER);
if (blocksMined == null) {
return null;
}
container.remove(toolStats.blocksMined);
if (meta.hasLore()) {
String oldBlocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined);
Component lineToRemove = toolStats.configTools.formatLore("blocks-mined", "{blocks}", oldBlocksMinedFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
boolean validToken = toolStats.itemChecker.checkTokens(container, "blocks-mined");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.blocksMined) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer blocksMined = 0;
if (container.has(toolStats.blocksMined, PersistentDataType.INTEGER)) {
blocksMined = container.get(toolStats.blocksMined, PersistentDataType.INTEGER);
}
if (blocksMined == null) {
blocksMined = 0;
toolStats.logger.warning(clone + " does not have valid generic-mined set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.blocksMined, PersistentDataType.INTEGER, blocksMined + add);
String oldBlocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined);
String newBlocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined + add);
Component oldLine = toolStats.configTools.formatLore("blocks-mined", "{blocks}", oldBlocksMinedFormatted);
Component newLine = toolStats.configTools.formatLore("blocks-mined", "{blocks}", newBlocksMinedFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add +1 to the player kills stat.
*
* @param playerWeapon The tool to update.
*/
public ItemMeta updatePlayerKills(ItemStack playerWeapon, int add) {
ItemStack clone = playerWeapon.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.configTools.checkConfig(clone.getType(), "player-kills")) {
if (container.has(toolStats.playerKills)) {
Integer playerKills = container.get(toolStats.playerKills, PersistentDataType.INTEGER);
if (playerKills == null) {
return null;
}
container.remove(toolStats.playerKills);
if (meta.hasLore()) {
String oldPlayerKillsFormatted = toolStats.numberFormat.formatInt(playerKills);
Component lineToRemove = toolStats.configTools.formatLore("player-kills", "{kills}", oldPlayerKillsFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "player-kills");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.playerKills) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Integer playerKills = 0;
if (container.has(toolStats.playerKills, PersistentDataType.INTEGER)) {
playerKills = container.get(toolStats.playerKills, PersistentDataType.INTEGER);
}
if (playerKills == null) {
playerKills = 0;
toolStats.logger.warning(clone + " does not have valid player-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.playerKills, PersistentDataType.INTEGER, playerKills + add);
String oldPlayerKillsFormatted = toolStats.numberFormat.formatInt(playerKills);
String newPlayerKillsFormatted = toolStats.numberFormat.formatInt(playerKills + add);
Component oldLine = toolStats.configTools.formatLore("kills.player", "{kills}", oldPlayerKillsFormatted);
Component newLine = toolStats.configTools.formatLore("kills.player", "{kills}", newPlayerKillsFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add x to the mob kills stat.
*
* @param playerWeapon The tool to update.
*/
public ItemMeta updateMobKills(ItemStack playerWeapon, int add) {
ItemStack clone = playerWeapon.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.configTools.checkConfig(clone.getType(), "mob-kills")) {
if (container.has(toolStats.mobKills)) {
Integer mobKills = container.get(toolStats.mobKills, PersistentDataType.INTEGER);
if (mobKills == null) {
return null;
}
container.remove(toolStats.mobKills);
if (meta.hasLore()) {
String oldMobKillsFormatted = toolStats.numberFormat.formatInt(mobKills);
Component lineToRemove = toolStats.configTools.formatLore("mob-kills", "{kills}", oldMobKillsFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "mob-kills");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.mobKills) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Integer mobKills = 0;
if (container.has(toolStats.mobKills, PersistentDataType.INTEGER)) {
mobKills = container.get(toolStats.mobKills, PersistentDataType.INTEGER);
}
if (mobKills == null) {
mobKills = 0;
toolStats.logger.warning(clone + " does not have valid mob-kills set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.mobKills, PersistentDataType.INTEGER, mobKills + add);
String oldMobKillsFormatted = toolStats.numberFormat.formatInt(mobKills);
String newMobKillsFormatted = toolStats.numberFormat.formatInt(mobKills + add);
Component oldLine = toolStats.configTools.formatLore("kills.mob", "{kills}", oldMobKillsFormatted);
Component newLine = toolStats.configTools.formatLore("kills.mob", "{kills}", newMobKillsFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add damage to an armor piece.
*
* @param armorPiece The armor to update.
* @param damage The amount of damage to apply.
* @param bypass Bypass the negative damage check.
*/
public ItemMeta updateDamage(ItemStack armorPiece, double damage, boolean bypass) {
// ignore if the damage is zero or negative
if (damage < 0) {
if (!bypass) {
return null;
}
}
ItemStack clone = armorPiece.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.armor-damage")) {
if (container.has(toolStats.armorDamage)) {
Double armorDamage = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
if (armorDamage == null) {
return null;
}
container.remove(toolStats.armorDamage);
if (meta.hasLore()) {
String oldDamageTakenFormatted = toolStats.numberFormat.formatDouble(armorDamage);
Component lineToRemove = toolStats.configTools.formatLore("damage-taken", "{damage}", oldDamageTakenFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "damage-taken");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.armorDamage) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Double damageTaken = 0.0;
if (container.has(toolStats.armorDamage, PersistentDataType.DOUBLE)) {
damageTaken = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
}
if (damageTaken == null) {
damageTaken = 0.0;
toolStats.logger.warning(clone + " does not have valid damage-taken set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.armorDamage, PersistentDataType.DOUBLE, damageTaken + damage);
String oldDamageFormatted = toolStats.numberFormat.formatDouble(damageTaken);
String newDamageFormatted = toolStats.numberFormat.formatDouble(damageTaken + damage);
Component oldLine = toolStats.configTools.formatLore("damage-taken", "{damage}", oldDamageFormatted);
Component newLine = toolStats.configTools.formatLore("damage-taken", "{damage}", newDamageFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add flight time to an elytra.
*
* @param elytra The player's elytra.
*/
public ItemMeta updateFlightTime(ItemStack elytra, long duration) {
ItemStack clone = elytra.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.flight-time")) {
if (container.has(toolStats.flightTime)) {
Long flightTime = container.get(toolStats.flightTime, PersistentDataType.LONG);
if (flightTime == null) {
return null;
}
container.remove(toolStats.flightTime);
if (meta.hasLore()) {
String oldFlightTimeFormatted = toolStats.numberFormat.formatDouble(flightTime);
Component lineToRemove = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightTimeFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "flight-time");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.flightTime) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
// read the current stats from the item
// if they don't exist, then start from 0
Long flightTime = 0L;
if (container.has(toolStats.flightTime, PersistentDataType.LONG)) {
flightTime = container.get(toolStats.flightTime, PersistentDataType.LONG);
}
if (flightTime == null) {
flightTime = 0L;
toolStats.logger.warning(flightTime + " does not have valid flight-time set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.flightTime, PersistentDataType.LONG, flightTime + duration);
String oldFlightFormatted = toolStats.numberFormat.formatDouble((double) flightTime / 1000);
String newFlightFormatted = toolStats.numberFormat.formatDouble((double) (flightTime + duration) / 1000);
Component oldLine = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightFormatted);
Component newLine = toolStats.configTools.formatLore("flight-time", "{time}", newFlightFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add x to sheep sheared stat.
*
* @param shears The shears.
*/
public ItemMeta updateSheepSheared(ItemStack shears, int add) {
ItemStack clone = shears.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.sheep-sheared")) {
if (container.has(toolStats.sheepSheared)) {
Integer sheepSheared = container.get(toolStats.sheepSheared, PersistentDataType.INTEGER);
if (sheepSheared == null) {
return null;
}
container.remove(toolStats.sheepSheared);
if (meta.hasLore()) {
String oldSheepShearedFormatted = toolStats.numberFormat.formatDouble(sheepSheared);
Component lineToRemove = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", oldSheepShearedFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "sheep-sheared");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.sheepSheared) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Integer sheepSheared = 0;
if (container.has(toolStats.sheepSheared, PersistentDataType.INTEGER)) {
sheepSheared = container.get(toolStats.sheepSheared, PersistentDataType.INTEGER);
}
if (sheepSheared == null) {
sheepSheared = 0;
toolStats.logger.warning(clone + " does not have valid sheared set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.sheepSheared, PersistentDataType.INTEGER, sheepSheared + add);
String oldSheepFormatted = toolStats.numberFormat.formatInt(sheepSheared);
String newSheepFormatted = toolStats.numberFormat.formatInt(sheepSheared + add);
Component oldLine = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", oldSheepFormatted);
Component newLine = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", newSheepFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add x to arrows shot stat.
*
* @param bow The bow.
*/
public ItemMeta updateArrowsShot(ItemStack bow, int add) {
ItemStack clone = bow.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.arrows-shot")) {
if (container.has(toolStats.arrowsShot)) {
Integer arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
if (arrowsShot == null) {
return null;
}
container.remove(toolStats.arrowsShot);
if (meta.hasLore()) {
String oldArrowsShotFormatted = toolStats.numberFormat.formatDouble(arrowsShot);
Component lineToRemove = toolStats.configTools.formatLore("arrows-shot", "{arrows}", oldArrowsShotFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "arrows-shot");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.arrowsShot) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer arrowsShot = 0;
if (container.has(toolStats.arrowsShot, PersistentDataType.INTEGER)) {
arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
}
if (arrowsShot == null) {
arrowsShot = 0;
toolStats.logger.warning(arrowsShot + " does not have valid arrows-shot set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.arrowsShot, PersistentDataType.INTEGER, arrowsShot + add);
String oldArrowsFormatted = toolStats.numberFormat.formatInt(arrowsShot);
String newArrowsFormatted = toolStats.numberFormat.formatInt(arrowsShot + add);
Component oldLine = toolStats.configTools.formatLore("arrows-shot", "{arrows}", oldArrowsFormatted);
Component newLine = toolStats.configTools.formatLore("arrows-shot", "{arrows}", newArrowsFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
/**
* Add x to fish caught stat.
*
* @param fishingRod The fishing rod.
*/
public ItemMeta updateFishCaught(ItemStack fishingRod, int add) {
ItemStack clone = fishingRod.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warning(clone + " does NOT have any meta! Unable to update stats.");
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.fish-caught")) {
if (container.has(toolStats.fishCaught)) {
Integer fishCaught = container.get(toolStats.fishCaught, PersistentDataType.INTEGER);
if (fishCaught == null) {
return null;
}
container.remove(toolStats.fishCaught);
if (meta.hasLore()) {
String oldFishCaught = toolStats.numberFormat.formatDouble(fishCaught);
Component lineToRemove = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", oldFishCaught);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "fish-caught");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.fishCaught) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.tokenApplied, 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.tokenApplied, PersistentDataType.STRING, newTokens);
}
}
}
Integer fishCaught = 0;
if (container.has(toolStats.fishCaught, PersistentDataType.INTEGER)) {
fishCaught = container.get(toolStats.fishCaught, PersistentDataType.INTEGER);
}
if (fishCaught == null) {
fishCaught = 0;
toolStats.logger.warning(clone + " does not have valid fish-caught set! Resting to zero. This should NEVER happen.");
}
container.set(toolStats.fishCaught, PersistentDataType.INTEGER, fishCaught + add);
String oldFishFormatted = toolStats.numberFormat.formatInt(fishCaught);
String newFishFormatted = toolStats.numberFormat.formatInt(fishCaught + add);
Component oldLine = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", oldFishFormatted);
Component newLine = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", newFishFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = toolStats.itemLore.updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta;
}
}

View File

@@ -65,7 +65,7 @@ public class NumberFormat {
}
if (decimalFormat == null) {
decimalFormat = "#,###.00";
decimalFormat = "#,##0.00";
toolStats.logger.warning("number-formats.comma-separator is missing! Using default #,###.00 instead.");
}

View File

@@ -0,0 +1,128 @@
/*
* 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;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ShapedRecipe;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class TokenCrafting {
private final ToolStats toolStats;
private final Set<ShapedRecipe> recipes = new HashSet<>();
private final ArrayList<String> tokenTypes = new ArrayList<>();
public TokenCrafting(ToolStats toolStats) {
this.toolStats = toolStats;
}
public void setup() {
NamespacedKey playerKillsKey = new NamespacedKey(toolStats, "player-kills-token");
ShapedRecipe playerKillRecipe = new ShapedRecipe(playerKillsKey, toolStats.tokenItems.playerKills());
playerKillRecipe.shape(" P ", "PSP", " P ");
playerKillRecipe.setIngredient('P', Material.PAPER);
playerKillRecipe.setIngredient('S', Material.WOODEN_SWORD);
recipes.add(playerKillRecipe);
NamespacedKey mobKillsKey = new NamespacedKey(toolStats, "mob-kills-token");
ShapedRecipe mobKillsRecipe = new ShapedRecipe(mobKillsKey, toolStats.tokenItems.mobKills());
mobKillsRecipe.shape(" P ", "PRP", " P ");
mobKillsRecipe.setIngredient('P', Material.PAPER);
mobKillsRecipe.setIngredient('R', Material.ROTTEN_FLESH);
recipes.add(mobKillsRecipe);
NamespacedKey blocksMinedKey = new NamespacedKey(toolStats, "blocks-mined-token");
ShapedRecipe blocksMinedRecipe = new ShapedRecipe(blocksMinedKey, toolStats.tokenItems.blocksMined());
blocksMinedRecipe.shape(" P ", "PSP", " P ");
blocksMinedRecipe.setIngredient('P', Material.PAPER);
blocksMinedRecipe.setIngredient('S', Material.WOODEN_PICKAXE);
recipes.add(blocksMinedRecipe);
NamespacedKey cropsMinedKey = new NamespacedKey(toolStats, "crops-mined-token");
ShapedRecipe cropsMinedRecipe = new ShapedRecipe(cropsMinedKey, toolStats.tokenItems.cropsMined());
cropsMinedRecipe.shape(" P ", "PHP", " P ");
cropsMinedRecipe.setIngredient('P', Material.PAPER);
cropsMinedRecipe.setIngredient('H', Material.WOODEN_HOE);
recipes.add(cropsMinedRecipe);
NamespacedKey fishCaughtKey = new NamespacedKey(toolStats, "fish-caught-token");
ShapedRecipe fishCaughtRecipe = new ShapedRecipe(fishCaughtKey, toolStats.tokenItems.fishCaught());
fishCaughtRecipe.shape(" P ", "PCP", " P ");
fishCaughtRecipe.setIngredient('P', Material.PAPER);
fishCaughtRecipe.setIngredient('C', Material.COD);
recipes.add(fishCaughtRecipe);
NamespacedKey sheepShearedKey = new NamespacedKey(toolStats, "sheep-sheared-token");
ShapedRecipe sheepShearedRecipe = new ShapedRecipe(sheepShearedKey, toolStats.tokenItems.sheepSheared());
sheepShearedRecipe.shape(" P ", "PWP", " P ");
sheepShearedRecipe.setIngredient('P', Material.PAPER);
sheepShearedRecipe.setIngredient('W', Material.WHITE_WOOL);
recipes.add(sheepShearedRecipe);
NamespacedKey armorDamageKey = new NamespacedKey(toolStats, "damage-taken-token");
ShapedRecipe armorDamageRecipe = new ShapedRecipe(armorDamageKey, toolStats.tokenItems.damageTaken());
armorDamageRecipe.shape(" P ", "PCP", " P ");
armorDamageRecipe.setIngredient('P', Material.PAPER);
armorDamageRecipe.setIngredient('C', Material.LEATHER_CHESTPLATE);
recipes.add(armorDamageRecipe);
NamespacedKey arrowsShotKey = new NamespacedKey(toolStats, "arrows-shot-token");
ShapedRecipe arrowsShotRecipe = new ShapedRecipe(arrowsShotKey, toolStats.tokenItems.arrowsShot());
arrowsShotRecipe.shape(" P ", "PAP", " P ");
arrowsShotRecipe.setIngredient('P', Material.PAPER);
arrowsShotRecipe.setIngredient('A', Material.ARROW);
recipes.add(arrowsShotRecipe);
NamespacedKey flightTimeKey = new NamespacedKey(toolStats, "flight-time-token");
ShapedRecipe flightTimeRecipe = new ShapedRecipe(flightTimeKey, toolStats.tokenItems.flightTime());
flightTimeRecipe.shape(" P ", "PFP", " P ");
flightTimeRecipe.setIngredient('P', Material.PAPER);
flightTimeRecipe.setIngredient('F', Material.FEATHER);
recipes.add(flightTimeRecipe);
NamespacedKey resetKey = new NamespacedKey(toolStats, "reset-token");
ShapedRecipe resetRecipe = new ShapedRecipe(resetKey, toolStats.tokenItems.resetToken());
resetRecipe.shape(" P ", "PPP", " P ");
resetRecipe.setIngredient('P', Material.PAPER);
recipes.add(resetRecipe);
tokenTypes.add("crops-mined");
tokenTypes.add("blocks-mined");
tokenTypes.add("damage-taken");
tokenTypes.add("mob-kills");
tokenTypes.add("player-kills");
tokenTypes.add("arrows-shot");
tokenTypes.add("sheep-sheared");
tokenTypes.add("flight-time");
tokenTypes.add("fish-caught");
tokenTypes.add("reset");
}
public Set<ShapedRecipe> getRecipes() {
return recipes;
}
public ArrayList<String> getTokenTypes() {
return tokenTypes;
}
}

View File

@@ -15,23 +15,6 @@
* 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.tools;
import org.bukkit.persistence.PersistentDataAdapterContext;

View File

@@ -18,15 +18,23 @@
package lol.hyper.toolstats.tools.config;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemLore;
import org.bukkit.ChatColor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Material;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ConfigTools {
private final ToolStats toolStats;
public static final Pattern COLOR_CODES = Pattern.compile("[&§]([0-9a-fk-or])");
public static final Pattern CONFIG_HEX_PATTERN = Pattern.compile("[&§]#([A-Fa-f0-9]{6})");
public ConfigTools(ToolStats toolStats) {
this.toolStats = toolStats;
@@ -37,7 +45,7 @@ public class ConfigTools {
*
* @param material The item type to check.
* @param configName The config we are checking under.
* @return If we want to allow lore or not.
* @return If we want to add data or not.
*/
public boolean checkConfig(Material material, String configName) {
String itemName = material.toString().toLowerCase();
@@ -67,43 +75,21 @@ public class ConfigTools {
itemType = itemName.substring(itemName.indexOf('_') + 1);
}
switch (itemType) {
case "pickaxe": {
return toolStats.config.getBoolean("enabled." + configName + ".pickaxe");
}
case "sword": {
return toolStats.config.getBoolean("enabled." + configName + ".sword");
}
case "shovel": {
return toolStats.config.getBoolean("enabled." + configName + ".shovel");
}
case "axe": {
return toolStats.config.getBoolean("enabled." + configName + ".axe");
}
case "hoe": {
return toolStats.config.getBoolean("enabled." + configName + ".hoe");
}
case "shears": {
return toolStats.config.getBoolean("enabled." + configName + ".shears");
}
case "crossbow":
case "bow": {
return toolStats.config.getBoolean("enabled." + configName + ".bow");
}
case "trident": {
return toolStats.config.getBoolean("enabled." + configName + ".trident");
}
case "fishing-rod": {
return toolStats.config.getBoolean("enabled." + configName + ".fishing-rod");
}
case "helmet":
case "chestplate":
case "leggings":
case "boots": {
return toolStats.config.getBoolean("enabled." + configName + ".armor");
}
}
return false;
return switch (itemType) {
case "pickaxe" -> toolStats.config.getBoolean("enabled." + configName + ".pickaxe");
case "sword" -> toolStats.config.getBoolean("enabled." + configName + ".sword");
case "shovel" -> toolStats.config.getBoolean("enabled." + configName + ".shovel");
case "axe" -> toolStats.config.getBoolean("enabled." + configName + ".axe");
case "hoe" -> toolStats.config.getBoolean("enabled." + configName + ".hoe");
case "shears" -> toolStats.config.getBoolean("enabled." + configName + ".shears");
case "crossbow", "bow" -> toolStats.config.getBoolean("enabled." + configName + ".bow");
case "trident" -> toolStats.config.getBoolean("enabled." + configName + ".trident");
case "fishing-rod" -> toolStats.config.getBoolean("enabled." + configName + ".fishing-rod");
case "mace" -> toolStats.config.getBoolean("enabled." + configName + ".mace");
case "helmet", "chestplate", "leggings", "boots" ->
toolStats.config.getBoolean("enabled." + configName + ".armor");
default -> false;
};
}
/**
@@ -114,39 +100,99 @@ public class ConfigTools {
* @param value The value to set the placeholder.
* @return Formatted string, null if the configName doesn't exist.
*/
public String formatLore(String configName, String placeHolder, Object value) {
public Component formatLore(String configName, String placeHolder, Object value) {
String lore = toolStats.config.getString("messages." + configName);
if (lore == null) {
toolStats.logger.warning("Unable to find config message for: messages." + configName);
return null;
}
// if the config message is empty, don't send it
if (lore.isEmpty()) {
return null;
}
// the final component for this lore
Component component;
// set the placeholder to the value
lore = lore.replace(placeHolder, String.valueOf(value));
Matcher hexMatcher = ItemLore.HEX_PATTERN.matcher(lore);
while (hexMatcher.find()) {
String hexCode = hexMatcher.group(1);
lore = lore.replaceAll(hexMatcher.group(), net.md_5.bungee.api.ChatColor.of("#" + hexCode).toString());
if (placeHolder != null && value != null) {
lore = lore.replace(placeHolder, String.valueOf(value));
}
Matcher colorMatcher = ItemLore.COLOR_CODES.matcher(lore);
while (colorMatcher.find()) {
String colorCode = colorMatcher.group(1);
lore = lore.replaceAll("&" + colorCode, ChatColor.getByChar(colorCode).toString());
// if we match the old color codes, then format them as so
Matcher hexMatcher = CONFIG_HEX_PATTERN.matcher(lore);
Matcher colorMatcher = COLOR_CODES.matcher(lore);
if (hexMatcher.find() || colorMatcher.find()) {
component = LegacyComponentSerializer.legacyAmpersand().deserialize(lore);
} else {
// otherwise format them normally
component = MiniMessage.miniMessage().deserialize(lore);
}
return ChatColor.translateAlternateColorCodes('§', lore);
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
}
/**
* Remove all color codes from a message.
* Format a string from the config.
*
* @param message The message.
* @return The message without color codes.
* @param configName The config to format.
* @return Formatted string, null if the configName doesn't exist.
*/
public String removeColor(String message) {
message = ItemLore.COLOR_CODES.matcher(message).replaceAll("");
message = ItemLore.HEX_PATTERN.matcher(message).replaceAll("");
return message;
public Component format(String configName) {
String message = toolStats.config.getString(configName);
if (message == null) {
toolStats.logger.warning("Unable to find config message for: " + configName);
return null;
}
// if the config message is empty, don't send it
if (message.isEmpty()) {
return null;
}
// the final component for this lore
Component component;
// if we match the old color codes, then format them as so
Matcher hexMatcher = CONFIG_HEX_PATTERN.matcher(message);
Matcher colorMatcher = COLOR_CODES.matcher(message);
if (hexMatcher.find() || colorMatcher.find()) {
component = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
} else {
// otherwise format them normally
component = MiniMessage.miniMessage().deserialize(message);
}
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
}
/**
* Get the token item's lore from config.
*
* @param tokenType The type.
* @return The lore.
*/
public List<Component> getTokenLore(String tokenType) {
List<String> raw = toolStats.config.getStringList("tokens.data." + tokenType + ".lore");
if (raw.isEmpty()) {
return Collections.emptyList();
}
List<Component> finalLore = new ArrayList<>();
for (String line : raw) {
Component component;
// if we match the old color codes, then format them as so
Matcher hexMatcher = CONFIG_HEX_PATTERN.matcher(line);
Matcher colorMatcher = COLOR_CODES.matcher(line);
if (hexMatcher.find() || colorMatcher.find()) {
component = LegacyComponentSerializer.legacyAmpersand().deserialize(line);
} else {
// otherwise format them normally
component = MiniMessage.miniMessage().deserialize(line);
}
component = component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
finalLore.add(component);
}
return finalLore;
}
}

View File

@@ -15,28 +15,10 @@
* 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.tools.config;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.config.versions.Version6;
import lol.hyper.toolstats.tools.config.versions.Version7;
import lol.hyper.toolstats.tools.config.versions.*;
public class ConfigUpdater {
@@ -49,17 +31,30 @@ public class ConfigUpdater {
public void updateConfig() {
int version = toolStats.config.getInt("config-version");
// this will be a switch in the future
// Upgrade 5 to 6
// Version 5 to 6
if (version == 5) {
Version6 version6 = new Version6(toolStats);
version6.update();
}
// Upgrade 6 to 7
// Version 6 to 7
if (version == 6) {
Version7 version7 = new Version7(toolStats);
version7.update();
}
// Version 7 to 8
if (version == 7) {
Version8 version8 = new Version8(toolStats);
version8.update();
}
// Version 8 to 9
if (version == 8) {
Version9 version9 = new Version9(toolStats);
version9.update();
}
// Version 9 to 10
if (version == 9) {
Version10 version10 = new Version10(toolStats);
version10.update();
}
}
}

View File

@@ -0,0 +1,218 @@
/*
* 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;
import lol.hyper.toolstats.ToolStats;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
public class TokenItems {
private final ToolStats toolStats;
public TokenItems(ToolStats toolStats) {
this.toolStats = toolStats;
}
public ItemStack playerKills() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.player-kills.title");
List<Component> lore = toolStats.configTools.getTokenLore("player-kills");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "player-kills");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack mobKills() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.mob-kills.title");
List<Component> lore = toolStats.configTools.getTokenLore("mob-kills");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "mob-kills");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack blocksMined() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.blocks-mined.title");
List<Component> lore = toolStats.configTools.getTokenLore("blocks-mined");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "blocks-mined");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack cropsMined() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.crops-mined.title");
List<Component> lore = toolStats.configTools.getTokenLore("crops-mined");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "crops-mined");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack fishCaught() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.fish-caught.title");
List<Component> lore = toolStats.configTools.getTokenLore("fight-caught");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "fish-caught");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack sheepSheared() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.sheep-sheared.title");
List<Component> lore = toolStats.configTools.getTokenLore("sheep-sheared");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "sheep-sheared");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack damageTaken() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.damage-taken.title");
List<Component> lore = toolStats.configTools.getTokenLore("damage-taken");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "damage-taken");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack arrowsShot() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.arrows-shot.title");
List<Component> lore = toolStats.configTools.getTokenLore("arrows-shot");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "arrows-shot");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack flightTime() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.flight-time.title");
List<Component> lore = toolStats.configTools.getTokenLore("flight-time");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "flight-time");
token.setItemMeta(tokenMeta);
return token;
}
public ItemStack resetToken() {
// set up the item
ItemStack token = new ItemStack(Material.PAPER);
ItemMeta tokenMeta = token.getItemMeta();
PersistentDataContainer tokenData = tokenMeta.getPersistentDataContainer();
// set the title and lore
Component title = toolStats.configTools.format("tokens.data.reset.title");
List<Component> lore = toolStats.configTools.getTokenLore("reset");
tokenMeta.displayName(title);
tokenMeta.lore(lore);
// set the PDC
tokenData.set(toolStats.tokenType, PersistentDataType.STRING, "reset");
token.setItemMeta(tokenMeta);
return token;
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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;
public class Version10 {
private final ToolStats toolStats;
/**
* Used for updating from version 9 to 10.
*
* @param toolStats ToolStats instance.
*/
public Version10(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-9.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config-9.yml!");
throw new RuntimeException(exception);
}
// we make this super verbose so that admins can see what's being added
toolStats.logger.info("Updating config.yml to version 10.");
toolStats.config.set("config-version", 10);
// Add missing values I forgot...
toolStats.logger.info("Adding entry for enabled.created-by.fishing-rod");
toolStats.config.set("enabled.created-by.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.created-date.fishing-rod");
toolStats.config.set("enabled.created-date.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.fished-tag.fishing-rod");
toolStats.config.set("enabled.fished-tag.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.looted-tag.fishing-rod");
toolStats.config.set("enabled.looted-tag.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.traded-tag.fishing-rod");
toolStats.config.set("enabled.traded-tag.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.spawned-in.fishing-rod");
toolStats.config.set("enabled.spawned-in.fishing-rod", true);
toolStats.logger.info("Adding entry for enabled.crops-harvested");
toolStats.config.set("enabled.crops-harvested", true);
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config.yml!");
throw new RuntimeException(exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 10. A copy of version 9 has been saved as config-9.yml");
}
}

View File

@@ -43,7 +43,7 @@ public class Version7 {
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config-6.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config-5.yml!");
toolStats.logger.severe("Unable to save config-6.yml!");
throw new RuntimeException(exception);
}
@@ -65,6 +65,6 @@ public class Version7 {
throw new RuntimeException(exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 7. A copy of version 5 has been saved as config-6.yml");
toolStats.logger.info("Config has been updated to version 7. A copy of version 6 has been saved as config-6.yml");
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.ArrayList;
import java.util.List;
public class Version8 {
private final ToolStats toolStats;
/**
* Used for updating from version 7 to 8.
*
* @param toolStats ToolStats instance.
*/
public Version8(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-7.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config-7.yml!");
throw new RuntimeException(exception);
}
// we make this super verbose so that admins can see what's being added
toolStats.logger.info("Updating config.yml to version 8.");
toolStats.config.set("config-version", 8);
// Add example to setting mob names
toolStats.logger.info("Adding example for messages.mob.ZOMBIE");
toolStats.config.set("messages.mob.ZOMBIE", "Zombie");
// Add mace to enabled sections
toolStats.logger.info("Adding entry for enabled.created-by.mace");
toolStats.config.set("enabled.created-by.mace", true);
toolStats.logger.info("Adding entry for enabled.created-date.mace");
toolStats.config.set("enabled.created-date.mace", true);
toolStats.logger.info("Adding entry for enabled.player-kills.mace");
toolStats.config.set("enabled.player-kills.mace", true);
toolStats.logger.info("Adding entry for enabled.mob-kills.mace");
toolStats.config.set("enabled.mob-kills.mace", true);
List<String> mobComments = new ArrayList<>();
mobComments.add("Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html");
toolStats.config.setComments("messages.mob", mobComments);
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config.yml!");
throw new RuntimeException(exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 8. A copy of version 7 has been saved as config-7.yml");
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.ArrayList;
import java.util.List;
public class Version9 {
private final ToolStats toolStats;
/**
* Used for updating from version 8 to 9.
*
* @param toolStats ToolStats instance.
*/
public Version9(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-8.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config-8.yml!");
throw new RuntimeException(exception);
}
toolStats.logger.info("Updating config.yml to version 9.");
toolStats.config.set("config-version", 9);
toolStats.logger.info("Adding new tokens configuration! Don't worry, this is disabled by default.");
toolStats.logger.info("You can find more information here: https://github.com/hyperdefined/ToolStats/wiki/Token-System");
// false by default so it doesn't break servers on updating
toolStats.config.set("tokens.enabled", false);
toolStats.config.set("tokens.craft-tokens", true);
List<String> tokenComments = new ArrayList<>();
tokenComments.add("Use token system for tracking stats.");
tokenComments.add("See https://github.com/hyperdefined/ToolStats/wiki/Token-System");
toolStats.config.setComments("tokens", tokenComments);
addToken("player-kills", "&7ToolStats: &8Player Kills Token", "&8Combine with a melee or ranged weapon in an anvil to track player kills.");
addToken("mob-kills", "&7ToolStats: &8Mob Kills Token", "&8Combine with a melee or ranged weapon in an anvil to track mob kills.");
addToken("blocks-mined", "&7ToolStats: &8Blocks Mined Token", "&8Combine with a pickaxe, axe, shovel, or shears in an anvil to track blocks mined.");
addToken("crops-mined", "&7ToolStats: &8Crops Mined Token", "&8Combine with a hoe in an anvil to track crops broken.");
addToken("fish-caught", "&7ToolStats: &8Fish Caught Token", "&8Combine with a fishing rod in an anvil to track fish caught.");
addToken("sheep-sheared", "&7ToolStats: &8Sheep Sheared Token", "&8Combine with shears in an anvil to track sheep sheared.");
addToken("damage-taken", "&7ToolStats: &8Damage Taken Token", "&8Combine with an armor piece in an anvil to track damage taken.");
addToken("arrows-shot", "&7ToolStats: &8Arrows Shot Token", "&8Combine with a bow or crossbow in an anvil to track arrows shot.");
addToken("flight-time", "&7ToolStats: &8Flight Time Token", "&8Combine with an elytra in an anvil to track flight time.");
addToken("reset", "&7ToolStats: &8Reset Token", "&8Combine in an anvil with to reset ALL stats for this item. Tokens on this item stay.");
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config.yml!");
throw new RuntimeException(exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 9. A copy of version 8 has been saved as config-8.yml");
}
/**
* Add a given token to the config. Made this since I was lazy.
*
* @param tokenType The token type to add.
* @param title The title for the item.
* @param lore The lore of the item.
*/
private void addToken(String tokenType, String title, String lore) {
toolStats.logger.info("Adding token type configuration for " + tokenType);
toolStats.config.set("tokens.data." + tokenType + ".title", title);
List<String> loreList = new ArrayList<>();
loreList.add(lore);
toolStats.config.set("tokens.data." + tokenType + ".lore", loreList);
toolStats.config.set("tokens.data." + tokenType + ".levels", 1);
}
}

View File

@@ -1,3 +1,60 @@
# Use token system for tracking stats.
# See https://github.com/hyperdefined/ToolStats/wiki/Token-System
tokens:
enabled: false
craft-tokens: true
data:
player-kills:
title: "&7ToolStats: &8Player Kills Token"
lore:
- "&8Combine with a melee or ranged weapon in an anvil to track player kills."
levels: 1
mob-kills:
title: "&7ToolStats: &8Mob Kills Token"
lore:
- "&8Combine with a melee or ranged weapon in an anvil to track mob kills."
levels: 1
blocks-mined:
title: "&7ToolStats: &8Blocks Mined Token"
lore:
- "&8Combine with a pickaxe, axe, shovel, or shears in an anvil to track blocks mined."
levels: 1
crops-mined:
title: "&7ToolStats: &8Crops Mined Token"
lore:
- "&8Combine with a hoe in an anvil to track crops broken."
levels: 1
fish-caught:
title: "&7ToolStats: &8Fish Caught Token"
lore:
- "&8Combine with a fishing rod in an anvil to track fish caught."
levels: 1
sheep-sheared:
title: "&7ToolStats: &8Sheep Sheared Token"
lore:
- "&8Combine with shears in an anvil to track sheep sheared."
levels: 1
damage-taken:
title: "&7ToolStats: &8Damage Taken Token"
lore:
- "&8Combine with an armor piece in an anvil to track damage taken."
levels: 1
arrows-shot:
title: "&7ToolStats: &8Arrows Shot Token"
lore:
- "&8Combine with a bow or crossbow in an anvil to track arrows shot."
levels: 1
flight-time:
title: "&7ToolStats: &8Flight Time Token"
lore:
- "&8Combine with an elytra in an anvil to track flight time."
levels: 1
reset:
title: "&7ToolStats: &8Reset Token"
lore:
- "&8Combine in an anvil with to reset ALL stats for this item. Tokens on this item stay."
levels: 1
enabled:
# Will show ownership of items when they are created/found.
created-by:
@@ -9,6 +66,8 @@ enabled:
shears: true
bow: true
armor: true
mace: true
fishing-rod: true
# Will show time the item is created
created-date:
pickaxe: true
@@ -19,6 +78,8 @@ enabled:
shears: true
bow: true
armor: true
mace: true
fishing-rod: true
# Will show "Fished by <player>"
fished-tag:
pickaxe: true
@@ -29,6 +90,7 @@ enabled:
shears: true
bow: true
armor: true
fishing-rod: true
# Will show "Found by <player>"
looted-tag:
pickaxe: true
@@ -39,6 +101,7 @@ enabled:
shears: true
bow: true
armor: true
fishing-rod: true
# Will show "Trade by <player>"
traded-tag:
pickaxe: true
@@ -49,16 +112,19 @@ enabled:
shears: true
bow: true
armor: true
fishing-rod: true
player-kills:
sword: true
axe: true
trident: true
bow: true
mace: true
mob-kills:
sword: true
axe: true
trident: true
bow: true
mace: true
blocks-mined:
pickaxe: true
shovel: true
@@ -75,6 +141,8 @@ enabled:
shears: true
bow: true
armor: true
mace: true
fishing-rod: true
fish-caught: true
sheep-sheared: true
armor-damage: true
@@ -82,6 +150,7 @@ enabled:
elytra-tag: true
arrows-shot: true
flight-time: true
crops-harvested: true
messages:
created:
@@ -112,11 +181,9 @@ messages:
damage-taken: "&7Damage taken: &8{damage}"
arrows-shot: "&7Arrows shot: &8{arrows}"
flight-time: "&7Flight time: &8{time}"
# Display this message if the player shift click trades/crafts items. It's not really easy to get every single item
# that is crafted. The tag will only be added to the first item. If you don't want this message, simply replace them both with ""
shift-click-warning:
crafting: "&cCrafting items via shift clicking does not fully apply tags to each item. This is a limitation with the Bukkit API."
trading: "&cTrading items via shift clicking does not fully apply tags to each item. This is a limitation with the Bukkit API."
# Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html
mobs:
ZOMBIE: "Zombie"
# Change the default formatting for dates.
# See: https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format
@@ -129,11 +196,11 @@ number-formats:
comma-separator: ","
decimal-separator: "."
comma-format: "#,###"
decimal-format: "#,###.00"
decimal-format: "#,##0.00"
# When any tool is created, it will generate a hash for the item.
# This hash is not on the item lore, only stored in the NBT data.
# This has no use currently, but can be used for future features for dupe detection.
generate-hash-for-items: true
generate-hash-for-items: false
config-version: 7
config-version: 9

View File

@@ -1,9 +1,10 @@
name: ToolStats
version: '${project.version}'
main: lol.hyper.toolstats.ToolStats
api-version: 1.15
api-version: 1.21
author: hyperdefined
description: Track various tool stats!
website: https://github.com/hyperdefined/ToolStats
folia-supported: true
commands:
toolstats:
@@ -22,3 +23,6 @@ permissions:
toolstats.reset.confirm:
description: Allows the usage of /toolstats reset confirm.
default: true
toolstats.givetokens:
description: Allows the usage of /toolstats givetoken.
default: op