Compare commits

...

96 Commits
1.1 ... 1.5.1

Author SHA1 Message Date
hyperdefined
03d45ea8f7 Update pom.xml 2022-10-26 18:10:22 -04:00
hyperdefined
91a98cf664 fix the symbols regardless of Locale 2022-10-26 18:08:00 -04:00
hyperdefined
72b00e96f6 fix example date format in config 2022-10-24 21:27:40 -04:00
hyperdefined
07458bfc11 ignore mob drops with our tags
fixes #22
2022-10-24 21:21:45 -04:00
hyperdefined
24d30f6059 fix deepsource problems 2022-10-13 18:09:19 -04:00
hyperdefined
bea7e52347 Update pom.xml 2022-10-13 18:05:00 -04:00
hyperdefined
1b1b45ef6d even BETTER number formatting?!?!
closes #19
2022-10-13 17:58:40 -04:00
hyperdefined
2de2ae82a7 config for date formatting
closes #20
2022-10-12 13:21:54 -04:00
hyperdefined
7639943fea better formatting for numbers 2022-10-12 13:04:35 -04:00
hyperdefined
2b75ea094d maybe not do this? 2022-10-09 16:51:26 -04:00
hyperdefined
bc8496fad4 changed event priorities 2022-10-05 20:46:19 -04:00
hyperdefined
1cef74311e big cleanup 2022-10-05 16:20:36 -04:00
hyperdefined
cbbdb4e9c4 remove dumb stuff 2022-10-04 12:59:16 -04:00
hyperdefined
f6d35c459c Merge pull request #17 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0
2022-09-19 22:32:43 -04:00
dependabot[bot]
4624d7a847 Bump maven-shade-plugin from 3.3.0 to 3.4.0
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-15 11:29:08 +00:00
hyperdefined
3766db3454 Merge branch 'master' of https://github.com/hyperdefined/ToolStats 2022-09-12 16:57:35 -04:00
hyperdefined
5a5e29c5e2 ignore CUSTOM damage causes 2022-09-12 16:57:19 -04:00
hyperdefined
d188877f18 Update README.md 2022-08-30 10:28:29 -04:00
hyperdefined
05200f075b Update pom.xml 2022-08-19 18:27:57 -04:00
hyperdefined
9973d743a5 better handling of /kill
apparently minecraft:kill runs in multiple entity damage events?
2022-08-19 18:27:34 -04:00
hyperdefined
a89b9d6f6a Update CommandToolStats.java 2022-08-19 17:39:25 -04:00
hyperdefined
082f529c32 hopefully fixed long standing bug 2022-08-18 11:59:20 -04:00
hyperdefined
05f940026f ignore void and /kill damage 2022-08-18 10:27:43 -04:00
hyperdefined
59d0295b18 permission stuff 2022-08-15 11:51:23 -04:00
hyperdefined
af908f2eb6 Merge pull request #16 from hyperdefined/dependabot/maven/lol.hyper-github-release-api-1.0.4
Bump github-release-api from 1.0.3 to 1.0.4
2022-08-15 11:45:05 -04:00
dependabot[bot]
c86e96e200 Bump github-release-api from 1.0.3 to 1.0.4
Bumps [github-release-api](https://github.com/hyperdefined/GitHubReleaseAPI) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/hyperdefined/GitHubReleaseAPI/releases)
- [Commits](https://github.com/hyperdefined/GitHubReleaseAPI/compare/1.0.3...1.0.4)

---
updated-dependencies:
- dependency-name: lol.hyper:github-release-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-11 13:32:36 +00:00
hyperdefined
8061293810 Merge pull request #15 from hyperdefined/dependabot/maven/net.kyori-adventure-platform-bukkit-4.1.2
Bump adventure-platform-bukkit from 4.1.1 to 4.1.2
2022-08-04 12:25:29 -04:00
dependabot[bot]
656e265006 Bump adventure-platform-bukkit from 4.1.1 to 4.1.2
Bumps [adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.1.1...v4.1.2)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-platform-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-04 11:30:53 +00:00
hyperdefined
507813f106 Update pom.xml 2022-06-27 20:22:13 -04:00
hyperdefined
e3e1c6a690 Update pom.xml 2022-06-18 17:09:25 -04:00
hyperdefined
5eebcb9ff4 make sure chests are in the same world
fixes #14
2022-06-18 17:09:12 -04:00
hyperdefined
63d508fded fixed typo 2022-06-16 00:04:23 -04:00
hyperdefined
59a8079ba2 Update pom.xml 2022-06-16 00:00:19 -04:00
hyperdefined
3767ee2f63 fixed reseting lore for armor 2022-06-16 00:00:04 -04:00
hyperdefined
7d5f73ae96 better number formatting 2022-06-15 23:59:19 -04:00
hyperdefined
b581d9d178 much better item checking 2022-06-15 23:48:39 -04:00
hyperdefined
d355b7a429 moved class 2022-06-15 23:26:20 -04:00
hyperdefined
d06a1c4a40 support for adding lore to minecart items 2022-06-15 23:25:21 -04:00
hyperdefined
e22fc7b530 Merge pull request #13 from hyperdefined/dependabot/maven/net.kyori-adventure-platform-bukkit-4.1.1
Bump adventure-platform-bukkit from 4.1.0 to 4.1.1
2022-06-13 18:32:54 -04:00
dependabot[bot]
8478411b8f Bump adventure-platform-bukkit from 4.1.0 to 4.1.1
Bumps [adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-platform-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-13 11:38:33 +00:00
hyperdefined
85d072667a Update CommandToolStats.java 2022-06-13 03:10:39 -04:00
hyperdefined
010ca54144 gamemode check 2022-06-13 03:07:37 -04:00
hyperdefined
6af49d6c30 better null handling 2022-06-13 02:55:11 -04:00
hyperdefined
c4e4ce01fd removed debug message 2022-06-13 02:21:32 -04:00
hyperdefined
983a35bd0b Update README.md 2022-06-08 21:01:15 -04:00
hyperdefined
d9ad7ebcad shade stuff 2022-06-07 17:58:46 -04:00
hyperdefined
995f80e159 Update plugin.yml 2022-06-06 13:26:05 -04:00
hyperdefined
56850c40d1 Merge pull request #11 from hyperdefined/dependabot/maven/net.kyori-adventure-text-minimessage-4.11.0
Bump adventure-text-minimessage from 4.10.1 to 4.11.0
2022-06-06 13:25:15 -04:00
dependabot[bot]
b9e4bbd1e8 Bump adventure-text-minimessage from 4.10.1 to 4.11.0
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.10.1 to 4.11.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.10.1...v4.11.0)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-text-minimessage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-02 11:28:36 +00:00
hyperdefined
6a23c7a724 version bump 2022-05-10 15:31:52 -04:00
hyperdefined
cd6cb999c0 handle item pickup better 2022-05-10 15:29:53 -04:00
hyperdefined
f9cff275b5 fixed trident lore not being added 2022-05-10 15:27:51 -04:00
hyperdefined
ba1ae01801 use minimessage for various messages 2022-05-10 14:41:01 -04:00
hyperdefined
fd35cf6e9c Update README.md 2022-04-19 18:55:48 -04:00
hyperdefined
5971ecc919 Merge pull request #10 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-clean-plugin-3.2.0
Bump maven-clean-plugin from 3.1.0 to 3.2.0
2022-04-06 11:38:22 -04:00
dependabot[bot]
4c4ca0c31e Bump maven-clean-plugin from 3.1.0 to 3.2.0
Bumps [maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/apache/maven-clean-plugin/releases)
- [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.1.0...maven-clean-plugin-3.2.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-clean-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 11:24:12 +00:00
hyperdefined
294b753430 Merge pull request #9 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.3.0
Bump maven-shade-plugin from 3.2.4 to 3.3.0
2022-03-30 15:11:18 -04:00
dependabot[bot]
adf6a3149b Bump maven-shade-plugin from 3.2.4 to 3.3.0
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.4 to 3.3.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.4...maven-shade-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 11:32:46 +00:00
hyperdefined
ea68650038 Merge pull request #8 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.10.1
Bump maven-compiler-plugin from 3.10.0 to 3.10.1
2022-03-11 16:47:40 -05:00
dependabot[bot]
114a27e9b5 Bump maven-compiler-plugin from 3.10.0 to 3.10.1
Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.0...maven-compiler-plugin-3.10.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-11 11:26:04 +00:00
hyperdefined
7cdb71df50 cleanup 2022-03-01 23:33:55 -05:00
hyperdefined
bd80d6718c Update CommandToolStats.java 2022-03-01 23:33:26 -05:00
hyperdefined
b9907eedec unify item arrays & date formats 2022-03-01 23:32:30 -05:00
hyperdefined
61cf44a407 handle finding who opens a newly spawned chest better 2022-03-01 23:21:09 -05:00
hyperdefined
337f88ce36 Update pom.xml 2022-02-24 11:46:50 -05:00
hyperdefined
42f100f0b0 added missing return (fixes #7) 2022-02-24 11:46:08 -05:00
hyperdefined
63b3cb1d03 Update pom.xml 2022-02-23 19:57:41 -05:00
hyperdefined
7717b6b5a5 Update EntityDamage.java 2022-02-23 19:53:54 -05:00
hyperdefined
a421430fa8 only add damage to valid armor
closes #6
2022-02-23 19:52:58 -05:00
hyperdefined
7a89b31766 removed unused set 2022-02-22 19:05:00 -05:00
hyperdefined
0334681971 getFinalDamage() instead because of armor reductions 2022-02-22 17:33:48 -05:00
hyperdefined
df23f40d6b Update pom.xml 2022-02-22 15:08:38 -05:00
hyperdefined
0221ee40cf handle logic of lore reset better 2022-02-22 15:07:51 -05:00
hyperdefined
a01d07af4a fixed removing new tag for elytras
this fixes #5
2022-02-22 15:07:30 -05:00
hyperdefined
e402d3078a Merge pull request #4 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.10.0
Bump maven-compiler-plugin from 3.9.0 to 3.10.0
2022-02-14 21:07:02 -05:00
dependabot[bot]
4f44ca0777 Bump maven-compiler-plugin from 3.9.0 to 3.10.0
Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.9.0...maven-compiler-plugin-3.10.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-14 11:38:14 +00:00
hyperdefined
53fd7f734c Update pom.xml 2022-02-12 20:47:52 -05:00
hyperdefined
75cd4fa17e fixed small typo 2022-02-12 20:47:32 -05:00
hyperdefined
e296c27a5b added comments to better explain what is going on 2022-02-11 19:19:05 -05:00
hyperdefined
adb1c637dc smh 2022-02-11 18:34:42 -05:00
hyperdefined
4b8f8061b4 Merge branch 'master' of https://github.com/hyperdefined/ToolStats 2022-02-11 15:09:49 -05:00
hyperdefined
464916e08b Update CommandToolStats.java 2022-02-11 15:09:32 -05:00
hyperdefined
6b6b65d1df Update README.md 2022-02-11 15:05:55 -05:00
hyperdefined
a1c39dffd0 Update pom.xml 2022-02-11 15:05:18 -05:00
hyperdefined
88eab81906 added tab completions to sub commands 2022-02-11 15:03:37 -05:00
hyperdefined
be233de90d fixed plugin.yml 2022-02-11 15:03:21 -05:00
hyperdefined
2437424e39 added reset lore command 2022-02-11 14:46:25 -05:00
hyperdefined
63b08062c8 Update CraftItem.java 2022-02-11 14:04:12 -05:00
hyperdefined
d23c2446dd fix fishing rods not updating lore correctly 2022-02-11 14:03:51 -05:00
hyperdefined
77b1be62d4 fixes 2022-02-10 21:41:23 -05:00
hyperdefined
29e6c998cf bump 2022-02-06 12:27:58 -05:00
hyperdefined
89951611fe changed api version to support more versions 2022-02-06 12:27:18 -05:00
hyperdefined
1c18a1a90c added missing check (fixes #2) 2022-02-05 16:42:42 -05:00
hyperdefined
f0ec409ef4 Update README.md 2022-01-31 17:40:51 -05:00
hyperdefined
0f9d75db06 Update pom.xml 2022-01-31 17:40:32 -05:00
hyperdefined
975445b8f3 support for custom lore messages 2022-01-31 17:40:10 -05:00
20 changed files with 1281 additions and 272 deletions

View File

@@ -1,11 +1,9 @@
<h1 align="center">ToolStats</h1>
<p align="center">
<img src="https://img.shields.io/badge/Minecraft-1.18+-orange" alt="Minecraft versions">
<img src="https://img.shields.io/badge/Minecraft-1.15--1.19.2-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>
<a href="https://en.cryptobadges.io/donate/1F29aNKQzci3ga5LDcHHawYzFPXvELTFoL"><img src="https://en.cryptobadges.io/badge/micro/1F29aNKQzci3ga5LDcHHawYzFPXvELTFoL" alt="Donate with Bitcoin"></a>
<a href="https://en.cryptobadges.io/donate/0xF3b4e87E4c11f586949ca8740eD33A1e473F924c"><img src="https://en.cryptobadges.io/badge/micro/0xF3b4e87E4c11f586949ca8740eD33A1e473F924c" alt="Donate with Ethereum"></a>
<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>
@@ -21,7 +19,9 @@ Here is everything it tracks:
* Fish caught.
* Sheep sheared.
The best part is, this data is stored on the item itself.
The best part is, this data is stored on the item itself. You can also change how the lore is displayed on the items!
If item lore is ever incorrect, you can run `/toolstats reset` to reset the item lore so it's correct.
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image2.png)
@@ -34,5 +34,8 @@ The best part is, this data is stored on the item itself.
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image9.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image10.png)
## Documentation
Visit the [wiki](https://github.com/hyperdefined/ToolStats/wiki) for help.
## License
This plugin is released under GNU General Public License v3. See [LICENSE](https://github.com/hyperdefined/ToolStats/blob/master/LICENSE).

39
pom.xml
View File

@@ -23,7 +23,7 @@
<groupId>lol.hyper</groupId>
<artifactId>toolstats</artifactId>
<version>1.1</version>
<version>1.5.1</version>
<packaging>jar</packaging>
<name>ToolStats</name>
@@ -37,7 +37,7 @@
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<executions>
<execution>
<id>auto-clean</id>
@@ -51,7 +51,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -60,13 +60,21 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.4.0</version>
<configuration>
<relocations>
<relocation>
<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>
</relocations>
</configuration>
<executions>
@@ -101,15 +109,9 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18.1-R0.1-SNAPSHOT</version>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
@@ -119,7 +121,20 @@
<dependency>
<groupId>lol.hyper</groupId>
<artifactId>github-release-api</artifactId>
<version>1.0.1</version>
<version>1.0.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.11.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.1.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -21,8 +21,10 @@ import lol.hyper.githubreleaseapi.GitHubRelease;
import lol.hyper.githubreleaseapi.GitHubReleaseAPI;
import lol.hyper.toolstats.commands.CommandToolStats;
import lol.hyper.toolstats.events.*;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -31,8 +33,10 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.logging.Logger;
public final class ToolStats extends JavaPlugin {
@@ -56,8 +60,7 @@ public final class ToolStats extends JavaPlugin {
// used for tracking new elytras
public final NamespacedKey newElytra = new NamespacedKey(this, "new");
public final Set<NamespacedKey> keys = new HashSet<>();
public SimpleDateFormat dateFormat;
public BlocksMined blocksMined;
public ChunkPopulate chunkPopulate;
public CraftItem craftItem;
@@ -66,6 +69,7 @@ public final class ToolStats extends JavaPlugin {
public PickupItem pickupItem;
public EntityDamage mobKill;
public PlayerFish playerFish;
public PlayerInteract playerInteract;
public SheepShear sheepShear;
public VillagerTrade villagerTrade;
public CommandToolStats commandToolStats;
@@ -73,10 +77,13 @@ public final class ToolStats extends JavaPlugin {
public final Logger logger = this.getLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml");
public FileConfiguration config;
public final int CONFIG_VERSION = 2;
public final int CONFIG_VERSION = 3;
private BukkitAudiences adventure;
@Override
public void onEnable() {
this.adventure = BukkitAudiences.create(this);
if (!configFile.exists()) {
this.saveResource("config.yml", true);
logger.info("Copying default config!");
@@ -90,6 +97,7 @@ public final class ToolStats extends JavaPlugin {
pickupItem = new PickupItem(this);
mobKill = new EntityDamage(this);
playerFish = new PlayerFish(this);
playerInteract = new PlayerInteract(this);
sheepShear = new SheepShear(this);
villagerTrade = new VillagerTrade(this);
commandToolStats = new CommandToolStats(this);
@@ -102,6 +110,7 @@ public final class ToolStats extends JavaPlugin {
Bukkit.getServer().getPluginManager().registerEvents(pickupItem, this);
Bukkit.getServer().getPluginManager().registerEvents(mobKill, this);
Bukkit.getServer().getPluginManager().registerEvents(playerFish, this);
Bukkit.getServer().getPluginManager().registerEvents(playerInteract, this);
Bukkit.getServer().getPluginManager().registerEvents(sheepShear, this);
Bukkit.getServer().getPluginManager().registerEvents(villagerTrade, this);
@@ -110,15 +119,6 @@ public final class ToolStats extends JavaPlugin {
new Metrics(this, 14110);
Bukkit.getScheduler().runTaskAsynchronously(this, this::checkForUpdates);
keys.add(genericOwner);
keys.add(timeCreated);
keys.add(swordPlayerKills);
keys.add(swordMobKills);
keys.add(genericMined);
keys.add(fishingRodCaught);
keys.add(shearsSheared);
keys.add(armorDamage);
}
public void loadConfig() {
@@ -126,6 +126,20 @@ public final class ToolStats extends JavaPlugin {
if (config.getInt("config-version") != CONFIG_VERSION) {
logger.warning("Your config file is outdated! Please regenerate the config.");
}
String dateFormatConfig = config.getString("date-format");
if (dateFormatConfig != null) {
try {
dateFormat = new SimpleDateFormat(dateFormatConfig, Locale.getDefault());
} catch (IllegalArgumentException exception) {
logger.severe("date-format is NOT a valid format! Using default American English format.");
exception.printStackTrace();
dateFormat = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
}
} else {
logger.warning("date-format is missing from your config! Using default American English format.");
dateFormat = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
}
}
public void checkForUpdates() {
@@ -151,18 +165,28 @@ public final class ToolStats extends JavaPlugin {
}
}
/**
* Checks the config to see if we want to show lore on certain items.
*
* @param itemStack The item to check.
* @param configName The config we are checking under.
* @return If we want to allow lore or not.
*/
public boolean checkConfig(ItemStack itemStack, String configName) {
String itemName = itemStack.getType().toString().toLowerCase();
String itemType = null;
if (itemName.contains("bow") || itemName.contains("shears")) {
if (itemName.contains("bow") || itemName.contains("shears") || itemName.contains("trident")) {
if (itemName.contains("bow")) {
itemType = "bow";
}
if (itemName.contains("shears")) {
itemType = "shears";
}
if (itemName.contains("trident")) {
itemType = "trident";
}
} else {
itemType = itemName.substring(itemName.indexOf("_") + 1);
itemType = itemName.substring(itemName.indexOf('_') + 1);
}
if (itemType == null) {
@@ -191,6 +215,9 @@ public final class ToolStats extends JavaPlugin {
case "bow": {
return config.getBoolean("enabled." + configName + ".bow");
}
case "trident": {
return config.getBoolean("enabled." + configName + ".trident");
}
case "helmet":
case "chestplate":
case "leggings":
@@ -200,4 +227,58 @@ public final class ToolStats extends JavaPlugin {
}
return false;
}
/**
* Gets the lore message from the config.
*
* @param configName The config name, "messages." is already in front.
* @param raw If you want the raw message with the formatting codes and placeholders.
* @return The lore message.
*/
public String getLoreFromConfig(String configName, boolean raw) {
String lore = config.getString("messages." + configName);
if (lore == null) {
return null;
}
if (raw) {
return ChatColor.translateAlternateColorCodes('&', lore);
} else {
// we basically add the color codes then remove them
// this is a dirty trick to remove color codes
lore = ChatColor.translateAlternateColorCodes('&', lore);
lore = ChatColor.stripColor(lore);
if (lore.contains("{player}")) {
lore = lore.replace("{player}", "");
}
if (lore.contains("{date}")) {
lore = lore.replace("{date}", "");
}
if (lore.contains("{name}")) {
lore = lore.replace("{name}", "");
}
if (lore.contains("{kills}")) {
lore = lore.replace("{kills}", "");
}
if (lore.contains("{blocks}")) {
lore = lore.replace("{blocks}", "");
}
if (lore.contains("{sheep}")) {
lore = lore.replace("{sheep}", "");
}
if (lore.contains("{damage}")) {
lore = lore.replace("{damage}", "");
}
if (lore.contains("{fish}")) {
lore = lore.replace("{fish}", "");
}
}
return lore;
}
public BukkitAudiences getAdventure() {
if (this.adventure == null) {
throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!");
}
return this.adventure;
}
}

View File

@@ -18,36 +18,272 @@
package lol.hyper.toolstats.commands;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.NumberFormat;
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.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CommandToolStats implements CommandExecutor {
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.").color(NamedTextColor.RED));
return true;
}
if (args.length == 0) {
sender.sendMessage(ChatColor.GREEN + "ToolStats version " + toolStats.getDescription().getVersion() + ". Created by hyperdefined.");
} else if (args.length == 1) {
if (args[0].equalsIgnoreCase("reload")) {
if (sender.isOp() || sender.hasPermission("toolstats.reload")) {
audiences.sender(sender).sendMessage(Component.text("ToolStats version " + toolStats.getDescription().getVersion() + ". Created by hyperdefined.").color(NamedTextColor.GREEN));
return true;
}
switch (args[0]) {
case "reload": {
if (sender.hasPermission("toolstats.reload")) {
toolStats.loadConfig();
sender.sendMessage(ChatColor.GREEN + "Configuration reloaded!");
audiences.sender(sender).sendMessage(Component.text("Configuration reloaded!").color(NamedTextColor.GREEN));
} else {
sender.sendMessage(ChatColor.RED + "You do not have permission for this command.");
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.").color(NamedTextColor.RED));
}
} else {
sender.sendMessage(ChatColor.RED + "Invalid sub-command.");
return true;
}
case "reset": {
if (!sender.hasPermission("toolstats.reset")) {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.").color(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.").color(NamedTextColor.RED));
return true;
}
Player player = (Player) sender;
ItemStack heldItem = player.getInventory().getItemInMainHand();
if (heldItem.getType() == Material.AIR) {
audiences.sender(sender).sendMessage(Component.text("You must hold an item!").color(NamedTextColor.RED));
return true;
}
fixItemLore(heldItem, player);
audiences.sender(sender).sendMessage(Component.text("The lore was reset!").color(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.").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("The item owner will be who ever is currently running this command.").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Only use this if the tags on the tool are incorrect.").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Type /toolstats reset confirm to confirm this.").color(NamedTextColor.GREEN));
return true;
}
default: {
audiences.sender(sender).sendMessage(Component.text("Invalid sub-command.").color(NamedTextColor.RED));
}
}
return true;
}
/**
* Fixes lore on a given item. This will wipe all lore and reapply our custom ones.
*
* @param original The item we are fixing.
* @param player The player running the command.
*/
private void fixItemLore(ItemStack original, Player player) {
ItemStack finalItem = original.clone();
ItemMeta finalMeta = finalItem.getItemMeta();
if (finalMeta == null) {
return;
}
PersistentDataContainer container = finalMeta.getPersistentDataContainer();
List<String> lore = new ArrayList<>();
String caughtByLore = toolStats.getLoreFromConfig("fished.caught-by", false);
String lootedByLore = toolStats.getLoreFromConfig("looted.found-by", false);
String tradedByLore = toolStats.getLoreFromConfig("traded.traded-by", false);
// make sure the config messages are not null
if (caughtByLore == null || lootedByLore == null || tradedByLore == null) {
return;
}
// determine how the item was originally created
// this doesn't get saved, so we just rely on the lore
// if there isn't a tag, default to crafted
String type = "DEFAULT";
if (finalMeta.hasLore()) {
if (finalMeta.getLore() != null) {
for (String line : finalMeta.getLore()) {
if (line.contains(caughtByLore)) {
type = "CAUGHT";
}
if (line.contains(lootedByLore)) {
type = "LOOTED";
}
if (line.contains(tradedByLore)) {
type = "TRADED";
}
}
}
}
// hard code elytras
if (finalItem.getType() == Material.ELYTRA) {
if (toolStats.config.getBoolean("enabled.elytra-tag")) {
lore.add(toolStats.getLoreFromConfig("looted.found-by", true).replace("{player}", player.getName()));
if (container.has(toolStats.timeCreated, PersistentDataType.LONG)) {
Long time = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (time != null) {
lore.add(toolStats.getLoreFromConfig("looted.found-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
}
}
finalMeta.setLore(lore);
finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot();
player.getInventory().setItem(slot, finalItem);
return;
}
}
if (toolStats.checkConfig(original, "created-by")) {
if (container.has(toolStats.genericOwner, new UUIDDataType())) {
container.set(toolStats.genericOwner, new UUIDDataType(), player.getUniqueId());
// show how the item was created based on the previous lore
switch (type) {
case "DEFAULT": {
lore.add(toolStats.getLoreFromConfig("created.created-by", true).replace("{player}", player.getName()));
break;
}
case "CAUGHT": {
lore.add(toolStats.getLoreFromConfig("fished.caught-by", true).replace("{player}", player.getName()));
break;
}
case "LOOTED": {
lore.add(toolStats.getLoreFromConfig("looted.found-by", true).replace("{player}", player.getName()));
break;
}
case "TRADED": {
lore.add(toolStats.getLoreFromConfig("traded.traded-by", true).replace("{player}", player.getName()));
break;
}
}
}
}
if (toolStats.checkConfig(original, "created-date")) {
if (container.has(toolStats.timeCreated, PersistentDataType.LONG)) {
Long time = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (time != null) {
// show how when the item was created based on the previous lore
switch (type) {
case "DEFAULT": {
lore.add(toolStats.getLoreFromConfig("created.created-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
break;
}
case "CAUGHT": {
lore.add(toolStats.getLoreFromConfig("fished.caught-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
break;
}
case "LOOTED": {
lore.add(toolStats.getLoreFromConfig("looted.found-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
break;
}
case "TRADED": {
lore.add(toolStats.getLoreFromConfig("traded.traded-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
break;
}
}
}
}
}
if (toolStats.checkConfig(original, "player-kills")) {
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
if (kills != null) {
lore.add(toolStats.getLoreFromConfig("kills.player", true).replace("{kills}", NumberFormat.formatInt(kills)));
}
}
}
if (toolStats.checkConfig(original, "mob-kills")) {
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
if (kills != null) {
lore.add(toolStats.getLoreFromConfig("kills.mob", true).replace("{kills}", NumberFormat.formatInt(kills)));
}
}
}
if (toolStats.checkConfig(original, "blocks-mined")) {
if (container.has(toolStats.genericMined, PersistentDataType.INTEGER)) {
Integer blocksMined = container.get(toolStats.genericMined, PersistentDataType.INTEGER);
if (blocksMined != null) {
lore.add(toolStats.getLoreFromConfig("blocks-mined", true).replace("{blocks}", 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 (fish != null) {
lore.add(toolStats.getLoreFromConfig("fished.fish-caught", true).replace("{fish}", 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 (sheep != null) {
lore.add(toolStats.getLoreFromConfig("sheep-sheared", true).replace("{sheep}", NumberFormat.formatInt(sheep)));
}
}
}
if (toolStats.config.getBoolean("enabled.armor-damage")) {
if (container.has(toolStats.armorDamage, PersistentDataType.DOUBLE)) {
Double damage = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
if (damage != null) {
lore.add(toolStats.getLoreFromConfig("damage-taken", true).replace("{damage}", NumberFormat.formatDouble(damage)));
}
}
}
finalMeta.setLore(lore);
finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot();
player.getInventory().setItem(slot, finalItem);
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
if (args.length == 1) {
if (sender.hasPermission("toolstats.reload")) {
return Arrays.asList("reset", "reload");
}
if (sender.hasPermission("toolstats.reset")) {
return Collections.singletonList("reset");
}
}
if (args.length == 2) {
if (args[0].equalsIgnoreCase("reset")) {
if (sender.hasPermission("toolstats.reset.confirm")) {
return Collections.singletonList("confirm");
}
}
}
return null;
}
}

View File

@@ -18,11 +18,13 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
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;
@@ -31,79 +33,96 @@ import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BlocksMined implements Listener {
private final ToolStats toolStats;
private final String[] validTools = {"pickaxe", "axe", "hoe", "shovel", "shear"};
private final String blocksMinedLore = ChatColor.GRAY + "Blocks mined: " + ChatColor.DARK_GRAY + "X";
public BlocksMined(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler (priority = EventPriority.HIGHEST)
public void onBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
if (player.getGameMode() != GameMode.SURVIVAL) {
if (event.isCancelled()) {
return;
}
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// if the player mines something with their fist
int heldItemSlot = player.getInventory().getHeldItemSlot();
ItemStack heldItem = player.getInventory().getItem(heldItemSlot);
if (heldItem == null || heldItem.getType() == Material.AIR) {
return;
}
String itemName = heldItem.getType().toString().toLowerCase();
if (Arrays.stream(validTools).noneMatch(itemName::contains)) {
// only check certain items
if (!ItemChecker.isMineTool(heldItem.getType())) {
return;
}
// if it's an item we want, update the stats
updateBlocksMined(heldItem);
}
private void updateBlocksMined(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
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) {
return;
} else {
blocksMined++;
blocksMined = 0;
toolStats.logger.warning(playerTool + " does not have valid generic-mined set! Resting to zero. This should NEVER happen.");
}
blocksMined++;
container.set(toolStats.genericMined, PersistentDataType.INTEGER, blocksMined);
String configLore = toolStats.getLoreFromConfig("blocks-mined", false);
String configLoreRaw = toolStats.getLoreFromConfig("blocks-mined", true);
if (configLore == null || configLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.blocks-mined!");
return;
}
List<String> lore;
String newLine = configLoreRaw.replace("{blocks}", NumberFormat.formatInt(blocksMined));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Blocks mined")) {
if (lore.get(x).contains(configLore)) {
hasLore = true;
lore.set(x, blocksMinedLore.replace("X", Integer.toString(blocksMined)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(blocksMinedLore.replace("X", Integer.toString(blocksMined)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(blocksMinedLore.replace("X", Integer.toString(blocksMined)));
lore.add(newLine);
}
if (toolStats.checkConfig(itemStack, "blocks-mined")) {
// do we add the lore based on the config?
if (toolStats.checkConfig(playerTool, "blocks-mined")) {
meta.setLore(lore);
}
itemStack.setItemMeta(meta);
playerTool.setItemMeta(meta);
}
}

View File

@@ -25,6 +25,7 @@ import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkPopulateEvent;
import org.bukkit.inventory.ItemStack;
@@ -35,7 +36,7 @@ import org.bukkit.persistence.PersistentDataType;
public class ChunkPopulate implements Listener {
// this tags all elytras with a "new" tag
// this let's use tag any new elytras player loot
// this let's us track any new elytras player loot
private final ToolStats toolStats;
@@ -43,7 +44,7 @@ public class ChunkPopulate implements Listener {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onPopulate(ChunkPopulateEvent event) {
if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) {
return;
@@ -53,14 +54,17 @@ public class ChunkPopulate implements Listener {
Bukkit.getScheduler().runTaskLater(toolStats, () -> {
Chunk chunk = event.getChunk();
for (Entity entity : chunk.getEntities()) {
// if there is a new item frame
if (entity instanceof ItemFrame) {
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) {
return;
}
// add the new tag so we know it's new
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.newElytra, PersistentDataType.INTEGER, 1);
elytraCopy.setItemMeta(meta);

View File

@@ -18,11 +18,14 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.UUIDDataType;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.CraftItemEvent;
import org.bukkit.inventory.ItemStack;
@@ -30,61 +33,105 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.text.SimpleDateFormat;
import java.util.*;
public class CraftItem implements Listener {
private final ToolStats toolStats;
public final String[] validItems = {
"pickaxe", "sword", "shovel", "axe", "hoe", "bow", "helmet", "chestplate", "leggings", "boots", "fishing"
};
private final String timeCreatedLore = ChatColor.GRAY + "Crafted on: " + ChatColor.DARK_GRAY + "X";
private final String ownerLore = ChatColor.GRAY + "Crafted by: " + ChatColor.DARK_GRAY + "X";
private final SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
public CraftItem(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onCraft(CraftItemEvent event) {
if (event.isCancelled()) {
return;
}
Player player = (Player) event.getWhoClicked();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
ItemStack itemStack = event.getCurrentItem();
if (itemStack == null || itemStack.getType() == Material.AIR) {
return;
}
String name = itemStack.getType().toString().toLowerCase(Locale.ROOT);
for (String x : validItems) {
if (name.contains(x)) {
event.setCurrentItem(addLore(itemStack, player));
// only check certain items
if (!ItemChecker.isValidItem(itemStack.getType())) {
return;
}
// if the player shift clicks, send them this warning
if (event.isShiftClick()) {
String configMessage = toolStats.config.getString("messages.shift-click-warning.crafting");
if (configMessage != null) {
if (configMessage.length() != 0) {
event.getWhoClicked().sendMessage(ChatColor.translateAlternateColorCodes('&', configMessage));
}
}
}
// test the item before setting it
ItemStack newItem = addLore(itemStack, player);
if (newItem != null) {
// set the result
event.setCurrentItem(newItem);
}
}
/**
* Adds crafted tags to item.
*
* @param itemStack The item add item to.
* @param owner The player crafting.
* @return A copy of the item with the tags + lore.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
// clone the item
ItemStack newItem = itemStack.clone();
ItemMeta meta = newItem.getItemMeta();
if (meta == null) {
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 the tag
// this is to prevent duplicate tags
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
return null;
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
String createdByRaw = toolStats.getLoreFromConfig("created.created-by", true);
String createdOnRaw = toolStats.getLoreFromConfig("created.created-on", true);
if (createdOnRaw == null) {
toolStats.logger.warning("There is no lore message for messages.created.created-on!");
return null;
}
if (createdByRaw == null) {
toolStats.logger.warning("There is no lore message for messages.created.created-by!");
return null;
}
List<String> lore;
// get the current lore the item
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
} else {
lore = new ArrayList<>();
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "created-date")) {
lore.add(timeCreatedLore.replace("X", format.format(finalDate)));
lore.add(createdOnRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
}
if (toolStats.checkConfig(itemStack, "created-by")) {
lore.add(ownerLore.replace("X", owner.getName()));
lore.add(createdByRaw.replace("{player}", owner.getName()));
}
meta.setLore(lore);
newItem.setItemMeta(meta);

View File

@@ -18,11 +18,13 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
@@ -33,44 +35,50 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
public class EntityDamage implements Listener {
private final ToolStats toolStats;
private final String[] validTools = {"sword", "trident", "axe"};
private final String playerKillsLore = ChatColor.GRAY + "Player kills: " + ChatColor.DARK_GRAY + "X";
private final String mobKillsLore = ChatColor.GRAY + "Mob kills: " + ChatColor.DARK_GRAY + "X";
private final String damageTakenLore = ChatColor.GRAY + "Damage taken: " + ChatColor.DARK_GRAY + "X";
private final DecimalFormat decimalFormat = new DecimalFormat("0.00");
public final Set<UUID> trackedMobs = new HashSet<>();
private final List<EntityDamageEvent.DamageCause> ignoredCauses = Arrays.asList(EntityDamageEvent.DamageCause.SUICIDE, EntityDamageEvent.DamageCause.VOID, EntityDamageEvent.DamageCause.CUSTOM);
public EntityDamage(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onDamage(EntityDamageByEntityEvent event) {
if (event.isCancelled()) {
return;
}
if (!(event.getEntity() instanceof LivingEntity)) {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
// ignore void and /kill damage
EntityDamageEvent.DamageCause cause = event.getCause();
if (ignoredCauses.contains(cause)) {
return;
}
// mob is going to die
if (livingEntity.getHealth() - event.getFinalDamage() <= 0) {
// a player is killing something
if (event.getDamager() instanceof Player) {
Player player = (Player) event.getDamager();
if (player.getGameMode() != GameMode.SURVIVAL) {
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// a player killed something with their fist
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
if (heldItem == null || heldItem.getType() == Material.AIR) {
return;
}
String itemName = heldItem.getType().toString().toLowerCase();
if (Arrays.stream(validTools).noneMatch(itemName::contains)) {
// only check certain items
if (!ItemChecker.isMeleeWeapon(heldItem.getType())) {
return;
}
// a player is killing another player
@@ -82,9 +90,11 @@ public class EntityDamage implements Listener {
player.getInventory().setItem(player.getInventory().getHeldItemSlot(), updateMobKills(heldItem));
trackedMobs.add(livingEntity.getUniqueId());
}
// trident is being thrown at something
if (event.getDamager() instanceof Trident) {
Trident trident = (Trident) event.getDamager();
ItemStack clone;
// trident is killing player
if (livingEntity instanceof Player) {
clone = updatePlayerKills(trident.getItem());
} else {
@@ -95,14 +105,21 @@ public class EntityDamage implements Listener {
}
trident.setItem(clone);
}
// arrow is being shot
if (event.getDamager() instanceof Arrow) {
Arrow arrow = (Arrow) event.getDamager();
// if the shooter is a player
if (arrow.getShooter() instanceof Player) {
Player player = (Player) arrow.getShooter();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
if (heldItem == null) {
return;
}
// if the player is holding the bow/crossbow
// if they switch then oh well
if (heldItem.getType() == Material.BOW || heldItem.getType() == Material.CROSSBOW) {
if (livingEntity instanceof Player) {
player.getInventory().setItem(player.getInventory().getHeldItemSlot(), updatePlayerKills(heldItem));
@@ -116,50 +133,91 @@ public class EntityDamage implements Listener {
// player is taken damage but not being killed
if (livingEntity instanceof Player) {
Player player = (Player) livingEntity;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
updateArmorDamage(armor, event.getDamage());
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
}
}
}
}
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
return;
}
// ignore void and /kill damage
EntityDamageEvent.DamageCause cause = event.getCause();
if (ignoredCauses.contains(cause)) {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
// player is taken damage but not being killed
if (livingEntity instanceof Player) {
Player player = (Player) livingEntity;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
updateArmorDamage(armor, event.getDamage());
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
}
}
}
}
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onDamage(EntityDamageByBlockEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
return;
}
// ignore void and /kill damage
EntityDamageEvent.DamageCause cause = event.getCause();
if (ignoredCauses.contains(cause)) {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
// player is taken damage but not being killed
if (livingEntity instanceof Player) {
Player player = (Player) livingEntity;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
updateArmorDamage(armor, event.getDamage());
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
}
}
}
}
}
/**
* Updates a weapon's player kills.
*
* @param itemStack The item to update.
* @return A copy of the item.
*/
private ItemStack updatePlayerKills(ItemStack itemStack) {
ItemStack finalItem = itemStack.clone();
ItemMeta meta = finalItem.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer playerKills = 0;
@@ -167,36 +225,47 @@ public class EntityDamage implements Listener {
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
playerKills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
}
if (playerKills == null) {
return null;
} else {
playerKills++;
playerKills = 0;
toolStats.logger.warning(itemStack + " does not have valid player-kills set! Resting to zero. This should NEVER happen.");
}
playerKills++;
container.set(toolStats.swordPlayerKills, PersistentDataType.INTEGER, playerKills);
String playerKillsLore = toolStats.getLoreFromConfig("kills.player", false);
String playerKillsLoreRaw = toolStats.getLoreFromConfig("kills.player", true);
if (playerKillsLore == null || playerKillsLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.kills.player!");
return null;
}
List<String> lore;
String newLine = playerKillsLoreRaw.replace("{kills}", NumberFormat.formatInt(playerKills));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Player kills")) {
if (lore.get(x).contains(playerKillsLore)) {
hasLore = true;
lore.set(x, playerKillsLore.replace("X", Integer.toString(playerKills)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(playerKillsLore.replace("X", Integer.toString(playerKills)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(playerKillsLore.replace("X", Integer.toString(playerKills)));
lore.add(newLine);
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "player-kills")) {
meta.setLore(lore);
}
@@ -204,10 +273,17 @@ public class EntityDamage implements Listener {
return finalItem;
}
/**
* Updates a weapon's mob kills.
*
* @param itemStack The item to update.
* @return A copy of the item.
*/
private ItemStack updateMobKills(ItemStack itemStack) {
ItemStack finalItem = itemStack.clone();
ItemMeta meta = finalItem.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer mobKills = 0;
@@ -215,36 +291,47 @@ public class EntityDamage implements Listener {
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
mobKills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
}
if (mobKills == null) {
return null;
} else {
mobKills++;
mobKills = 0;
toolStats.logger.warning(itemStack + " does not have valid mob-kills set! Resting to zero. This should NEVER happen.");
}
mobKills++;
container.set(toolStats.swordMobKills, PersistentDataType.INTEGER, mobKills);
String mobKillsLore = toolStats.getLoreFromConfig("kills.mob", false);
String mobKillsLoreRaw = toolStats.getLoreFromConfig("kills.mob", true);
if (mobKillsLore == null || mobKillsLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.kills.mob!");
return null;
}
List<String> lore;
String newLine = mobKillsLoreRaw.replace("{kills}", NumberFormat.formatInt(mobKills));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Mob kills")) {
if (lore.get(x).contains(mobKillsLore)) {
hasLore = true;
lore.set(x, mobKillsLore.replace("X", Integer.toString(mobKills)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(mobKillsLore.replace("X", Integer.toString(mobKills)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(mobKillsLore.replace("X", Integer.toString(mobKills)));
lore.add(newLine);
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "mob-kills")) {
meta.setLore(lore);
}
@@ -252,9 +339,16 @@ public class EntityDamage implements Listener {
return finalItem;
}
/**
* Updates a player's armor damage stats.
*
* @param itemStack The armor piece.
* @param damage How much damage is being added.
*/
private void updateArmorDamage(ItemStack itemStack, double damage) {
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;
@@ -262,36 +356,45 @@ public class EntityDamage implements Listener {
if (container.has(toolStats.armorDamage, PersistentDataType.DOUBLE)) {
damageTaken = container.get(toolStats.armorDamage, PersistentDataType.DOUBLE);
}
if (damageTaken == null) {
return;
} else {
damageTaken = damageTaken + damage;
damageTaken = 0.0;
toolStats.logger.warning(itemStack + " does not have valid damage-taken set! Resting to zero. This should NEVER happen.");
}
decimalFormat.setRoundingMode(RoundingMode.DOWN);
damageTaken = damageTaken + damage;
container.set(toolStats.armorDamage, PersistentDataType.DOUBLE, damageTaken);
String damageTakenLore = toolStats.getLoreFromConfig("damage-taken", false);
String damageTakenLoreRaw = toolStats.getLoreFromConfig("damage-taken", true);
if (damageTakenLore == null || damageTakenLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.damage-taken!");
return;
}
List<String> lore;
String newLine = damageTakenLoreRaw.replace("{damage}", NumberFormat.formatDouble(damageTaken));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Damage taken")) {
if (lore.get(x).contains(damageTakenLore)) {
hasLore = true;
lore.set(x, damageTakenLore.replace("X", decimalFormat.format(damageTaken)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(damageTakenLore.replace("X", decimalFormat.format(damageTaken)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(damageTakenLore.replace("X", decimalFormat.format(damageTaken)));
lore.add(newLine);
}
if (toolStats.config.getBoolean("enabled.armor-damage")) {
meta.setLore(lore);

View File

@@ -18,39 +18,51 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.ItemChecker;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.*;
public class EntityDeath implements Listener {
private final ToolStats toolStats;
private final String droppedLore = ChatColor.GRAY + "Dropped by: " + ChatColor.DARK_GRAY + "X";
public EntityDeath(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onDeath(EntityDeathEvent event) {
LivingEntity livingEntity = event.getEntity();
if (livingEntity instanceof Player) {
return;
}
UUID livingEntityUUID = event.getEntity().getUniqueId();
// if it's a mob we are tracking that matters
if (toolStats.mobKill.trackedMobs.contains(livingEntityUUID)) {
for (ItemStack current : event.getDrops()) {
String name = current.getType().toString().toLowerCase(Locale.ROOT);
for (String item : toolStats.craftItem.validItems) {
if (name.contains(item)) {
addLore(current, livingEntity.getName());
for (int i = 0; i < event.getDrops().size(); i++) {
ItemStack droppedItem = event.getDrops().get(i);
ItemMeta droppedItemMeta = droppedItem.getItemMeta();
if (droppedItemMeta != null) {
PersistentDataContainer container = droppedItemMeta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG)) {
continue; // ignore any items that have our tags
}
}
if (ItemChecker.isValidItem(droppedItem.getType())) {
ItemStack newItem = addLore(droppedItem, livingEntity.getName());
if (newItem != null) {
event.getDrops().set(i, newItem);
}
}
}
@@ -58,20 +70,35 @@ public class EntityDeath implements Listener {
}
}
private void addLore(ItemStack itemStack, String mob) {
ItemMeta meta = itemStack.getItemMeta();
/**
* Adds "drop by" tag to item.
*
* @param oldItem The item to add lore to.
* @param mob The mob or player name.
*/
private ItemStack addLore(ItemStack oldItem, String mob) {
ItemStack newItem = oldItem.clone();
ItemMeta meta = newItem.getItemMeta();
if (meta == null) {
return;
return null;
}
boolean hasTag = false;
String droppedByLore = toolStats.getLoreFromConfig("dropped-by", false);
String droppedByLoreRaw = toolStats.getLoreFromConfig("dropped-by", true);
if (droppedByLore == null || droppedByLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.dropped-by!");
return null;
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Dropped by")) {
if (lore.get(x).contains(droppedByLore)) {
// replace existing tag
lore.set(x, droppedLore.replace("X", mob));
lore.set(x, droppedByLoreRaw.replace("{name}", mob));
hasTag = true;
}
}
@@ -80,11 +107,12 @@ public class EntityDeath implements Listener {
lore = new ArrayList<>();
}
if (!hasTag) {
lore.add(droppedLore.replace("X", mob));
lore.add(droppedByLoreRaw.replace("{name}", mob));
}
if (toolStats.config.getBoolean("enabled.dropped-by")) {
meta.setLore(lore);
}
itemStack.setItemMeta(meta);
newItem.setItemMeta(meta);
return newItem;
}
}

View File

@@ -18,12 +18,17 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.UUIDDataType;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.LootGenerateEvent;
import org.bukkit.inventory.Inventory;
@@ -33,51 +38,96 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class GenerateLoot implements Listener {
private final ToolStats toolStats;
private final String LOOT_OWNER = ChatColor.GRAY + "Looted by: " + ChatColor.DARK_GRAY + "X";
private final String LOOT_TIME = ChatColor.GRAY + "Looted on: " + ChatColor.DARK_GRAY + "X";
public final String[] validItems = {
"pickaxe", "sword", "shovel", "axe", "hoe", "bow", "helmet", "chestplate", "leggings", "boots", "fishing"
};
private final SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
public GenerateLoot(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onGenerateLoot(LootGenerateEvent event) {
InventoryHolder inventoryHolder = event.getInventoryHolder();
if (inventoryHolder == null) {
return;
}
Inventory chest = inventoryHolder.getInventory();
Bukkit.getScheduler().runTaskLater(toolStats, () -> {
Player player = (Player) chest.getViewers().get(0);
for (int i = 0; i < chest.getContents().length; i++) {
ItemStack itemStack = chest.getItem(i);
if (itemStack == null || itemStack.getType() == Material.AIR) {
continue;
}
String name = itemStack.getType().toString().toLowerCase(Locale.ROOT);
for (String x : validItems) {
if (name.contains(x)) {
chest.setItem(i, addLore(itemStack, player));
Location lootLocation = event.getLootContext().getLocation();
Inventory chestInv = inventoryHolder.getInventory();
if (inventoryHolder instanceof Chest) {
Block openedChest = null;
// look at the current list of opened chest and get the distance
// between the lootcontext location and chest location
// if the distance is less than 1, it's the same chest
for (Block chest : toolStats.playerInteract.openedChests.keySet()) {
Location chestLocation = chest.getLocation();
if (chest.getWorld() == lootLocation.getWorld()) {
double distance = lootLocation.distance(chestLocation);
if (distance <= 1.0) {
openedChest = chest;
}
}
}
// ignore if the chest is not in the same location
if (openedChest == null) {
return;
}
},1);
// run task later since if it runs on the same tick it breaks idk
Block finalOpenedChest = openedChest;
Bukkit.getScheduler().runTaskLater(toolStats, () -> {
Player player = toolStats.playerInteract.openedChests.get(finalOpenedChest);
// do a classic for loop, so we keep track of chest index of item
for (int i = 0; i < chestInv.getContents().length; i++) {
ItemStack itemStack = chestInv.getItem(i);
// ignore air
if (itemStack == null || itemStack.getType() == Material.AIR) {
continue;
}
if (ItemChecker.isValidItem(itemStack.getType())) {
ItemStack newItem = addLore(itemStack, player);
if (newItem != null) {
chestInv.setItem(i, newItem);
}
}
}
}, 1);
}
if (inventoryHolder instanceof StorageMinecart) {
StorageMinecart mineCart = (StorageMinecart) inventoryHolder;
if (toolStats.playerInteract.openedMineCarts.containsKey(mineCart)) {
Player player = toolStats.playerInteract.openedMineCarts.get(mineCart);
// player clicked this minecart
for (int i = 0; i < chestInv.getContents().length; i++) {
ItemStack itemStack = chestInv.getItem(i);
// ignore air
if (itemStack == null || itemStack.getType() == Material.AIR) {
continue;
}
if (ItemChecker.isValidItem(itemStack.getType())) {
ItemStack newItem = addLore(itemStack, player);
if (newItem != null) {
chestInv.setItem(i, newItem);
}
}
}
}
}
}
/**
* Adds lore to newly generated items.
*
* @param itemStack The item to add lore to.
* @param owner The player that found the item.
* @return The item with the lore.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
ItemStack newItem = itemStack.clone();
ItemMeta meta = itemStack.getItemMeta();
@@ -87,18 +137,31 @@ public class GenerateLoot implements Listener {
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
return null;
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
String foundByLoreRaw = toolStats.getLoreFromConfig("looted.found-by", true);
String foundOnLoreRaw = toolStats.getLoreFromConfig("looted.found-on", true);
if (foundByLoreRaw == null || foundOnLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.looted!");
return null;
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
} else {
lore = new ArrayList<>();
}
if (toolStats.checkConfig(newItem, "looted-tag")) {
lore.add(LOOT_TIME.replace("X", format.format(finalDate)));
lore.add(LOOT_OWNER.replace("X", owner.getName()));
lore.add(foundOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(foundByLoreRaw.replace("{player}", owner.getName()));
}
meta.setLore(lore);
newItem.setItemMeta(meta);

View File

@@ -18,12 +18,12 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.UUIDDataType;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.inventory.ItemStack;
@@ -31,65 +31,89 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class PickupItem implements Listener {
private final ToolStats toolStats;
private final String FOUND_BY = ChatColor.GRAY + "Found by: " + ChatColor.DARK_GRAY + "X";
private final String FOUND_ON = ChatColor.GRAY + "Found on: " + ChatColor.DARK_GRAY + "X";
private final SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
public PickupItem(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onPickup(EntityPickupItemEvent event) {
if (event.isCancelled()) {
return;
}
Entity entity = event.getEntity();
if (entity instanceof Player) {
ItemStack itemStack = event.getItem().getItemStack();
if (itemStack.getType() == Material.ELYTRA) {
Player player = (Player) entity;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
Item item = event.getItem();
if (item.getType() == EntityType.DROPPED_ITEM) {
ItemStack itemStack = event.getItem().getItemStack();
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
return;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
// the elytra has the new key, set the lore to it
if (container.has(toolStats.newElytra, PersistentDataType.INTEGER)) {
container.remove(toolStats.newElytra);
addLore(itemStack, (Player) event.getEntity());
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());
if (newElytra != null) {
item.setItemStack(newElytra);
}
}
}
}
}
}
private void addLore(ItemStack itemStack, Player owner) {
ItemMeta meta = itemStack.getItemMeta();
/**
* Adds "looted by" tags for elytras.
*
* @param itemStack The elytra to add lore to.
* @param owner The player who found it.
*/
private ItemStack addLore(ItemStack itemStack, Player owner) {
ItemStack finalItem = itemStack.clone();
ItemMeta meta = finalItem.getItemMeta();
if (meta == null) {
return;
return null;
}
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.remove(toolStats.newElytra);
String foundByLoreRaw = toolStats.getLoreFromConfig("looted.found-by", true);
String foundOnLoreRaw = toolStats.getLoreFromConfig("looted.found-on", true);
if (foundByLoreRaw == null || foundOnLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.looted!");
return null;
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
} else {
lore = new ArrayList<>();
}
if (toolStats.config.getBoolean("enabled.elytra-tag")) {
lore.add(FOUND_ON.replace("X", format.format(finalDate)));
lore.add(FOUND_BY.replace("X", owner.getName()));
lore.add(foundOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(foundByLoreRaw.replace("{player}", owner.getName()));
}
meta.setLore(lore);
itemStack.setItemMeta(meta);
finalItem.setItemMeta(meta);
return finalItem;
}
}

View File

@@ -18,12 +18,15 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.UUIDDataType;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.NumberFormat;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.GameMode;
import org.bukkit.Material;
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;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
@@ -31,54 +34,61 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class PlayerFish implements Listener {
private final ToolStats toolStats;
private final String fishCaughtLore = ChatColor.GRAY + "Fish caught: " + ChatColor.DARK_GRAY + "X";
private final String FISH_OWNER = ChatColor.GRAY + "Caught by: " + ChatColor.DARK_GRAY + "X";
private final String FISH_TIME = ChatColor.GRAY + "Caught on: " + ChatColor.DARK_GRAY + "X";
public final String[] validItems = {
"pickaxe", "sword", "shovel", "axe", "hoe", "bow", "helmet", "chestplate", "leggings", "boots", "fishing"
};
private final SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
public PlayerFish(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGH)
public void onFish(PlayerFishEvent event) {
if (event.isCancelled()) {
return;
}
// only listen to when a player catches a fish
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// make sure the player is holding a fishing rod
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
if (heldItem == null || heldItem.getType() == Material.AIR || heldItem.getType() != Material.FISHING_ROD) {
return;
}
// update the fishing rod to the new one
updateFishCount(heldItem);
// check if the player caught an item
if (event.getCaught() == null) {
return;
}
ItemStack caughtItem = ((Item) event.getCaught()).getItemStack();
for (String x : validItems) {
if (caughtItem.getType().toString().toLowerCase(Locale.ROOT).contains(x)) {
addNewLore(caughtItem, player);
Item caughtItemEntity = (Item) event.getCaught();
if (ItemChecker.isValidItem(caughtItem.getType())) {
ItemStack newItem = addNewLore(caughtItem, player);
if (newItem != null) {
caughtItemEntity.setItemStack(newItem);
}
}
}
private void updateFishCount(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
/**
* 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;
@@ -86,51 +96,96 @@ public class PlayerFish implements Listener {
if (container.has(toolStats.fishingRodCaught, PersistentDataType.INTEGER)) {
fishCaught = container.get(toolStats.fishingRodCaught, PersistentDataType.INTEGER);
}
if (fishCaught == null) {
return;
} else {
fishCaught++;
fishCaught = 0;
toolStats.logger.warning(fishingRod + " does not have valid fish-caught set! Resting to zero. This should NEVER happen.");
}
fishCaught++;
container.set(toolStats.fishingRodCaught, PersistentDataType.INTEGER, fishCaught);
String fishCaughtLore = toolStats.getLoreFromConfig("fished.fish-caught", false);
String fishCaughtLoreRaw = toolStats.getLoreFromConfig("fished.fish-caught", true);
if (fishCaughtLore == null || fishCaughtLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.fish-caught!");
return;
}
List<String> lore;
String newLine = fishCaughtLoreRaw.replace("{fish}", NumberFormat.formatInt(fishCaught));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Fish caught")) {
if (lore.get(x).contains(fishCaughtLore)) {
hasLore = true;
lore.set(x, fishCaughtLore.replace("X", Integer.toString(fishCaught)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(fishCaughtLore.replace("X", Integer.toString(fishCaught)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(fishCaughtLore.replace("X", Integer.toString(fishCaught)));
lore.add(newLine);
}
/*
if (Bukkit.getPluginManager().isPluginEnabled("EvenMoreFish")) {
ListIterator<String> iterator = lore.listIterator();
while (iterator.hasNext()) {
String line = iterator.next();
toolStats.logger.info(line);
if (line.equalsIgnoreCase("§f")) {
iterator.remove();
}
}
}*/
if (toolStats.config.getBoolean("enabled.fish-caught")) {
meta.setLore(lore);
}
itemStack.setItemMeta(meta);
fishingRod.setItemMeta(meta);
}
private void addNewLore(ItemStack itemStack, Player owner) {
ItemMeta meta = itemStack.getItemMeta();
/**
* Add lore to newly caught item.
* @param originalItem The original item to add lore.
* @param owner The player who caught it.
* @return A copy of the new item with lore.
*/
private ItemStack addNewLore(ItemStack originalItem, Player owner) {
ItemStack newItem = originalItem.clone();
ItemMeta meta = originalItem.getItemMeta();
if (meta == null) {
return;
return null;
}
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
return null;
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
String caughtByLoreRaw = toolStats.getLoreFromConfig("fished.caught-by", true);
String caughtOnLoreRaw = toolStats.getLoreFromConfig("fished.caught-on", true);
if (caughtByLoreRaw == null || caughtOnLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.fished!");
return null;
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
@@ -138,11 +193,12 @@ public class PlayerFish implements Listener {
} else {
lore = new ArrayList<>();
}
if (toolStats.checkConfig(itemStack, "fished-tag")) {
lore.add(FISH_TIME.replace("X", format.format(finalDate)));
lore.add(FISH_OWNER.replace("X", owner.getName()));
if (toolStats.checkConfig(newItem, "fished-tag")) {
lore.add(caughtOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(caughtByLoreRaw.replace("{player}", owner.getName()));
meta.setLore(lore);
}
meta.setLore(lore);
itemStack.setItemMeta(meta);
newItem.setItemMeta(meta);
return newItem;
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import java.util.HashMap;
public class PlayerInteract implements Listener {
private final ToolStats toolStats;
public final HashMap<Block, Player> openedChests = new HashMap<>();
public final HashMap<StorageMinecart, Player> openedMineCarts = new HashMap<>();
public PlayerInteract(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Block block = event.getClickedBlock();
if (block == null) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// store when a player opens a chest
if (block.getType() != Material.AIR && block.getType() == Material.CHEST) {
openedChests.put(block, player);
Bukkit.getScheduler().runTaskLater(toolStats, () -> openedChests.remove(block), 20);
}
}
@EventHandler
public void onInteract(PlayerInteractEntityEvent event) {
Entity clicked = event.getRightClicked();
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
// store when a player opens a minecart
if (clicked.getType() == EntityType.MINECART_CHEST) {
StorageMinecart storageMinecart = (StorageMinecart) clicked;
openedMineCarts.put(storageMinecart, player);
Bukkit.getScheduler().runTaskLater(toolStats, () -> openedMineCarts.remove(storageMinecart), 20);
}
}
}

View File

@@ -18,12 +18,14 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Sheep;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
@@ -37,72 +39,102 @@ import java.util.List;
public class SheepShear implements Listener {
private final ToolStats toolStats;
private final String sheepShearLore = ChatColor.GRAY + "Sheep sheared: " + ChatColor.DARK_GRAY + "X";
public SheepShear(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onShear(PlayerInteractEntityEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
Entity entity = event.getRightClicked();
if (!(entity instanceof Sheep)) {
return;
}
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
// check if the player is right-clicking with shears only
int heldItemSlot = player.getInventory().getHeldItemSlot();
ItemStack heldItem = player.getInventory().getItem(heldItemSlot);
if (heldItem == null || heldItem.getType() == Material.AIR || heldItem.getType() != Material.SHEARS) {
return;
}
Sheep sheep = (Sheep) entity;
// make sure the sheep is not sheared
if (!sheep.isSheared()) {
addLore(heldItem);
ItemStack newShears = addLore(heldItem);
if (newShears != null) {
player.getInventory().setItem(heldItemSlot, newShears);
}
}
}
private void addLore(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
/**
* Adds tags to shears.
*
* @param oldShears The shears.
*/
private ItemStack addLore(ItemStack oldShears) {
ItemStack newShears = oldShears.clone();
ItemMeta meta = newShears.getItemMeta();
if (meta == null) {
return;
toolStats.logger.warning(newShears + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer sheepSheared = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.shearsSheared, PersistentDataType.INTEGER)) {
sheepSheared = container.get(toolStats.shearsSheared, PersistentDataType.INTEGER);
}
if (sheepSheared == null) {
return;
} else {
sheepSheared++;
sheepSheared = 0;
toolStats.logger.warning(newShears + " does not have valid sheared set! Resting to zero. This should NEVER happen.");
}
sheepSheared++;
container.set(toolStats.shearsSheared, PersistentDataType.INTEGER, sheepSheared);
String sheepShearedLore = toolStats.getLoreFromConfig("sheep-sheared", false);
String sheepShearedLoreRaw = toolStats.getLoreFromConfig("sheep-sheared", true);
if (sheepShearedLore == null || sheepShearedLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.sheep-sheared!");
return null;
}
List<String> lore;
String newLine = sheepShearedLoreRaw.replace("{sheep}", NumberFormat.formatInt(sheepSheared));
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
boolean hasLore = false;
// we do a for loop like this, we can keep track of index
// this doesn't mess the lore up of existing items
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains("Sheep sheared")) {
if (lore.get(x).contains(sheepShearedLore)) {
hasLore = true;
lore.set(x, sheepShearLore.replace("X", Integer.toString(sheepSheared)));
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(sheepShearLore.replace("X", Integer.toString(sheepSheared)));
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(sheepShearLore.replace("X", Integer.toString(sheepSheared)));
lore.add(newLine);
}
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
meta.setLore(lore);
}
itemStack.setItemMeta(meta);
newShears.setItemMeta(meta);
return newShears;
}
}

View File

@@ -18,10 +18,14 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.UUIDDataType;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType;
@@ -32,68 +36,100 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class VillagerTrade implements Listener {
private final ToolStats toolStats;
private final String TRADED_OWNER = ChatColor.GRAY + "Traded by: " + ChatColor.DARK_GRAY + "X";
private final String TRADED_TIME = ChatColor.GRAY + "Traded on: " + ChatColor.DARK_GRAY + "X";
public final String[] validItems = {
"pickaxe", "sword", "shovel", "axe", "hoe", "bow", "helmet", "chestplate", "leggings", "boots", "fishing"
};
private final SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
public VillagerTrade(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onTrade(InventoryClickEvent event) {
if (event.isCancelled() || event.getCurrentItem() == null) {
return;
}
Inventory inventory = event.getClickedInventory();
if (!(event.getWhoClicked() instanceof 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();
for (String x : validItems) {
if (item.getType().toString().toLowerCase(Locale.ROOT).contains(x)) {
ItemStack newItem = addLore(item, (Player) event.getWhoClicked());
event.getView().setCursor(newItem);
// only check items we want
if (ItemChecker.isValidItem(item.getType())) {
// if the player shift clicks, show the warning
if (event.isShiftClick()) {
String configMessage = toolStats.config.getString("messages.shift-click-warning.trading");
if (configMessage != null) {
event.getWhoClicked().sendMessage(ChatColor.translateAlternateColorCodes('&', configMessage));
}
}
ItemStack newItem = addLore(item, player);
if (newItem != null) {
// this gets delayed since villager inventories suck for no reason
Bukkit.getScheduler().runTaskLater(toolStats, () -> event.setCurrentItem(newItem), 5);
}
}
}
}
}
private ItemStack addLore(ItemStack itemStack, Player owner) {
ItemMeta meta = itemStack.getItemMeta();
/**
* Adds "traded by" tags to item.
*
* @param oldItem The item to add lore.
* @param owner The player who traded.
* @return The item with lore.
*/
private ItemStack addLore(ItemStack oldItem, Player owner) {
ItemStack newItem = oldItem.clone();
ItemMeta meta = newItem.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newItem + " does NOT have any meta! Unable to update stats.");
return null;
}
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.timeCreated, PersistentDataType.LONG) || container.has(toolStats.genericOwner, PersistentDataType.LONG)) {
return null;
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
String tradedByLoreRaw = toolStats.getLoreFromConfig("traded.traded-by", true);
String tradedOnLoreRaw = toolStats.getLoreFromConfig("traded.traded-on", true);
if (tradedByLoreRaw == null || tradedOnLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.traded!");
return null;
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
assert lore != null;
} else {
lore = new ArrayList<>();
}
if (toolStats.checkConfig(itemStack, "traded-tag")) {
lore.add(TRADED_TIME.replace("X", format.format(finalDate)));
lore.add(TRADED_OWNER.replace("X", owner.getName()));
if (toolStats.checkConfig(newItem, "traded-tag")) {
lore.add(tradedOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(tradedByLoreRaw.replace("{player}", owner.getName()));
meta.setLore(lore);
}
meta.setLore(lore);
itemStack.setItemMeta(meta);
return itemStack;
newItem.setItemMeta(meta);
return newItem;
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.Material;
import java.util.Arrays;
import java.util.Locale;
public class ItemChecker {
private static final String[] validItems = { "pickaxe", "sword", "shovel", "axe", "hoe", "bow", "helmet", "chestplate", "leggings", "boots", "fishing" };
private static final String[] validArmor = { "helmet", "chestplate", "leggings", "boots" };
private static final String[] validMelee = {"sword", "trident", "axe"};
private static final String[] validMine = { "pickaxe", "axe", "hoe", "shovel", "shear" };
/**
* Check if item is an armor piece.
*
* @param itemType The item type, not name.
* @return If the item is an armor piece.
*/
public static boolean isArmor(Material itemType) {
return Arrays.stream(validArmor).anyMatch(type -> itemType.toString().toLowerCase(Locale.ROOT).contains(type));
}
/**
* Check if item is a tool or armor piece we want to track.
*
* @param itemType The item type, not name.
* @return If the item something we want to track.
*/
public static boolean isValidItem(Material itemType) {
return Arrays.stream(validItems).anyMatch(type -> itemType.toString().toLowerCase(Locale.ROOT).contains(type));
}
/**
* Check if item is a melee weapon.
*
* @param itemType The item type, not name.
* @return If the item is a melee weapon.
*/
public static boolean isMeleeWeapon(Material itemType) {
return Arrays.stream(validMelee).anyMatch(type -> itemType.toString().toLowerCase(Locale.ROOT).contains(type));
}
/**
* Check if item is a mining tool.
*
* @param itemType The item type, not name.
* @return If the item is a mining tool.
*/
public static boolean isMineTool(Material itemType) {
return Arrays.stream(validMine).anyMatch(type -> itemType.toString().toLowerCase(Locale.ROOT).contains(type));
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
public class NumberFormat {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.00", new DecimalFormatSymbols(Locale.getDefault()));
private static final DecimalFormat COMMA_FORMAT = new DecimalFormat("#,###", new DecimalFormatSymbols(Locale.getDefault()));
/**
* Formats a number to make it pretty. Example: 4322 to 4,322
* @param number The number to format.
* @return The formatted number.
*/
public static String formatInt(int number) {
String finalNumber = COMMA_FORMAT.format(number);
finalNumber = finalNumber.replaceAll("[\\x{202f}\\x{00A0}]", " ");
return finalNumber;
}
/**
* Formats a number to make it pretty. Example: 4322.33 to 4,322.33
* @param number The number to format.
* @return The formatted number.
*/
public static String formatDouble(double number) {
String finalNumber = DECIMAL_FORMAT.format(number);
finalNumber = finalNumber.replaceAll("[\\x{202f}\\x{00A0}]", " ");
return finalNumber;
}
}

View File

@@ -15,7 +15,24 @@
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats;
/*
* 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;
import org.bukkit.persistence.PersistentDataType;

View File

@@ -71,4 +71,36 @@ enabled:
dropped-by: true
elytra-tag: true
config-version: 2
messages:
created:
created-by: "&7Crafted by: &8{player}"
created-on: "&7Crafted on: &8{date}"
fished:
caught-by: "&7Caught by: &8{player}"
caught-on: "&7Caught on: &8{date}"
fish-caught: "&7Fish caught: &8{fish}"
looted:
found-by: "&7Found by: &8{player}"
found-on: "&7Found on: &8{date}"
traded:
traded-by: "&7Traded by: &8{player}"
traded-on: "&7Traded on: &8{date}"
kills:
mob: "&7Mob kills: &8{kills}"
player: "&7Player kills: &8{kills}"
blocks-mined: "&7Blocks mined: &8{blocks}"
sheep-sheared: "&7Sheep sheared: &8{sheep}"
dropped-by: "&7Dropped by: &8{name}" # name will be player/mob name
damage-taken: "&7Damage taken: &8{damage}"
# 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."
# Change the default formatting for dates.
# See: https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format
# Example: "dd/MM/yyyy"
date-format: "M/dd/yyyy"
config-version: 3

View File

@@ -1,15 +1,23 @@
name: ToolStats
version: '${project.version}'
main: lol.hyper.toolstats.ToolStats
api-version: 1.18
api-version: 1.15
author: hyperdefined
description: Track various tool stats!
commands:
toolstats:
usage: /toolstats
description: Main command.
permission: randomenchant.reload
permission: toolstats.command
permissions:
randomenchant.reload:
description: Allows the usage of /randomenchant reload
toolstats.command:
description: Allows the usage of /toolstats.
default: true
toolstats.reload:
description: Allows the usage of /toolstats reload.
default: op
toolstats.reset:
description: Allows the usage of /toolstats reset.
default: true
toolstats.reset.confirm:
description: Allows the usage of /toolstats reset confirm.
default: true