Compare commits

..

94 Commits
1.5 ... 1.7.1

Author SHA1 Message Date
hyperdefined
cc4176cfbe awrf :3 2023-10-22 20:05:44 -04:00
hyperdefined
bd30b05246 Update README.md 2023-10-22 19:52:34 -04:00
hyperdefined
2ace1c4f16 bump version 2023-10-22 19:46:59 -04:00
hyperdefined
c3f47707c4 remove all "config" things into it's own thing 2023-10-22 19:43:22 -04:00
hyperdefined
ee1c197d72 more config updater changes 2023-10-22 19:35:41 -04:00
hyperdefined
74b529ba4f Update CommandToolStats.java 2023-10-22 19:23:30 -04:00
hyperdefined
c328b9e019 add "arrows shot" + fix jank 2023-10-22 19:13:44 -04:00
hyperdefined
38c37ea601 new images 2023-10-22 18:54:51 -04:00
hyperdefined
700e7bca2c more config changes
* fixed typo on config updater
* handle missing config messages a lot better
* only try to update lore if it's enabled, don't try then check if it's enabled
2023-10-21 18:52:58 -04:00
hyperdefined
59bfdf69ca improve various config things 2023-10-21 18:11:28 -04:00
hyperdefined
68aed25834 Update README.md 2023-10-19 18:04:57 -04:00
hyperdefined
0968497fc0 added a config updater 2023-10-19 17:59:53 -04:00
hyperdefined
c7a5aef9ee make item checking better
fixes #42
2023-10-19 17:05:15 -04:00
hyperdefined
e40e16c273 Update modrinth.yml 2023-10-19 12:25:22 -04:00
hyperdefined
fc9ef05afd Update modrinth.yml 2023-10-19 12:24:30 -04:00
hyperdefined
26ae28dcd8 Update README.md 2023-10-19 12:23:12 -04:00
hyperdefined
02a7920ad2 Create modrinth.yml 2023-10-19 12:22:08 -04:00
hyperdefined
68c9685477 added "spawned by" for creative items
closes #41
2023-10-17 14:17:41 -04:00
hyperdefined
9f81cb1f74 Merge pull request #40 from hyperdefined/dependabot/maven/net.kyori-adventure-platform-bukkit-4.3.1 2023-10-02 09:10:39 -04:00
hyperdefined
e068aeb9f4 never ending battle with deepsource 2023-09-30 20:52:30 -04:00
hyperdefined
b0733935ae handle loot generation better
edit the loot itself, not the contents of chest

also handle breaking chest instead of opening it
2023-09-30 20:43:54 -04:00
hyperdefined
664eddeab0 more hash work 2023-09-30 20:22:33 -04:00
hyperdefined
cda8d94199 ok 2023-09-30 20:07:46 -04:00
hyperdefined
5551c24202 lots of updates
- add hash to items when created
- added "crops harvested" for hoes
2023-09-30 20:05:31 -04:00
dependabot[bot]
951106177b Bump net.kyori:adventure-platform-bukkit from 4.3.0 to 4.3.1
Bumps [net.kyori:adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.3.0...v4.3.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>
2023-09-29 11:18:32 +00:00
hyperdefined
0a3f46fc6e Merge pull request #39 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.5.1
Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.1
2023-09-27 22:00:23 -04:00
dependabot[bot]
2894e2021a Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.1
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 11:47:01 +00:00
hyperdefined
2e34cb812a Merge pull request #38 from hyperdefined/dependabot/maven/lol.hyper-github-release-api-1.0.5
Bump lol.hyper:github-release-api from 1.0.4 to 1.0.5
2023-09-18 12:01:07 -04:00
dependabot[bot]
a38c148b80 Bump lol.hyper:github-release-api from 1.0.4 to 1.0.5
Bumps [lol.hyper:github-release-api](https://github.com/hyperdefined/GitHubReleaseAPI) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/hyperdefined/GitHubReleaseAPI/releases)
- [Commits](https://github.com/hyperdefined/GitHubReleaseAPI/compare/1.0.4...1.0.5)

---
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>
2023-09-18 11:35:07 +00:00
hyperdefined
a55cea4b5d more work 2023-08-30 17:02:50 -04:00
hyperdefined
d0fcd4fc61 Update pom.xml 2023-08-30 16:38:13 -04:00
hyperdefined
f14b9bc474 imports 2023-08-30 16:37:36 -04:00
hyperdefined
366a54052a basic folia support 2023-08-29 10:41:08 -04:00
hyperdefined
fe96235ed1 removed unused function 2023-07-17 19:24:45 -04:00
hyperdefined
fe8052f9d6 add origin tag to existing items 2023-07-17 19:23:04 -04:00
hyperdefined
884bdef444 add missing elytra to item list 2023-07-17 19:22:32 -04:00
hyperdefined
be31202f65 config changes 2023-07-17 15:29:33 -04:00
hyperdefined
39d8a42110 clean this up a bit 2023-07-17 15:29:26 -04:00
hyperdefined
91090dd6cc fix this 2023-07-17 15:28:56 -04:00
hyperdefined
29a2a5150c update various things for origin NBT 2023-07-13 19:16:02 -04:00
hyperdefined
b38d2825cb added NBT for origins of items 2023-07-13 19:04:29 -04:00
hyperdefined
fd6c120a58 Merge pull request #36 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-clean-plugin-3.3.1
Bump maven-clean-plugin from 3.2.0 to 3.3.1
2023-06-20 18:50:48 -04:00
dependabot[bot]
459569a1dc Bump maven-clean-plugin from 3.2.0 to 3.3.1
Bumps [maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.2.0 to 3.3.1.
- [Release notes](https://github.com/apache/maven-clean-plugin/releases)
- [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.2.0...maven-clean-plugin-3.3.1)

---
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>
2023-06-20 11:59:37 +00:00
hyperdefined
9ba6200415 Merge pull request #35 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.5.0
Bump maven-shade-plugin from 3.4.1 to 3.5.0
2023-06-19 13:24:21 -04:00
dependabot[bot]
b94e0e8c36 Bump maven-shade-plugin from 3.4.1 to 3.5.0
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.1...maven-shade-plugin-3.5.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>
2023-06-16 11:59:30 +00:00
hyperdefined
a42c526198 Merge pull request #34 from hyperdefined/dependabot/maven/net.kyori-adventure-text-minimessage-4.14.0
Bump adventure-text-minimessage from 4.13.1 to 4.14.0
2023-06-07 12:35:33 -04:00
dependabot[bot]
851a494935 Bump adventure-text-minimessage from 4.13.1 to 4.14.0
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.13.1 to 4.14.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.13.1...v4.14.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>
2023-06-07 11:59:12 +00:00
hyperdefined
72e869d5e2 Update pom.xml 2023-05-22 20:22:02 -04:00
hyperdefined
300fe56c5a use continue here, not return
closes #33
2023-05-22 20:21:16 -04:00
hyperdefined
fe5e55d746 Update pom.xml 2023-05-22 00:05:00 -04:00
hyperdefined
0574fb61a6 fix traded-by tags :3333 2023-05-22 00:03:42 -04:00
hyperdefined
433ab547cb minor adjustments 2023-05-21 23:49:07 -04:00
hyperdefined
9746789f2b added int damage value 2023-04-21 15:31:54 -04:00
hyperdefined
fd3c3ca8ed Merge pull request #31 from hyperdefined/dependabot/maven/org.bstats-bstats-bukkit-3.0.2
Bump bstats-bukkit from 3.0.1 to 3.0.2
2023-04-10 16:38:20 -04:00
hyperdefined
c6dc2c3368 Merge pull request #32 from hyperdefined/dependabot/maven/net.kyori-adventure-text-minimessage-4.13.1
Bump adventure-text-minimessage from 4.13.0 to 4.13.1
2023-04-10 16:36:01 -04:00
dependabot[bot]
d11211b1c7 Bump adventure-text-minimessage from 4.13.0 to 4.13.1
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.13.0 to 4.13.1.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.13.0...v4.13.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 12:02:00 +00:00
dependabot[bot]
e8c2296bd7 Bump bstats-bukkit from 3.0.1 to 3.0.2
Bumps [bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.0.1...v3.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 12:00:34 +00:00
hyperdefined
d8f296a085 Merge pull request #29 from hyperdefined/dependabot/maven/net.kyori-adventure-text-minimessage-4.13.0
Bump adventure-text-minimessage from 4.12.0 to 4.13.0
2023-03-16 10:42:18 -04:00
hyperdefined
58dee809f3 Merge pull request #30 from hyperdefined/dependabot/maven/net.kyori-adventure-platform-bukkit-4.3.0
Bump adventure-platform-bukkit from 4.2.0 to 4.3.0
2023-03-16 10:40:21 -04:00
dependabot[bot]
590cf6e8b0 Bump adventure-platform-bukkit from 4.2.0 to 4.3.0
Bumps [adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.2.0...v4.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-16 12:00:42 +00:00
dependabot[bot]
a7cc8671bd Bump adventure-text-minimessage from 4.12.0 to 4.13.0
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.12.0 to 4.13.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.12.0...v4.13.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>
2023-03-14 12:00:26 +00:00
hyperdefined
37a7c642d3 Merge pull request #28 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.11.0
Bump maven-compiler-plugin from 3.10.1 to 3.11.0
2023-02-27 18:57:28 -05:00
dependabot[bot]
31acdd9527 Bump maven-compiler-plugin from 3.10.1 to 3.11.0
Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.1...maven-compiler-plugin-3.11.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>
2023-02-27 12:10:48 +00:00
hyperdefined
9b0ac2d912 stfu deepsource 2023-02-20 22:28:33 -05:00
hyperdefined
4ea9e1600b return item lore back rather than nulling it 2023-02-20 22:02:48 -05:00
hyperdefined
72b8b31779 Merge pull request #27 from hyperdefined/dependabot/maven/org.bstats-bstats-bukkit-3.0.1
Bump bstats-bukkit from 3.0.0 to 3.0.1
2023-02-16 17:43:01 -05:00
dependabot[bot]
fce135f2f8 Bump bstats-bukkit from 3.0.0 to 3.0.1
Bumps [bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.0.0...v3.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 12:09:50 +00:00
hyperdefined
425b315104 Revert "Update ToolStats.java"
This reverts commit f2739cf433.
2023-01-16 13:41:50 -05:00
hyperdefined
50a0bc800c small command changes 2022-12-12 17:05:46 -05:00
hyperdefined
583cd824f4 Update EntityDamage.java 2022-12-12 12:06:06 -05:00
hyperdefined
d63f00166d Update NumberFormat.java 2022-12-12 11:30:26 -05:00
hyperdefined
0bcb0c7370 added missing tracking for mobs 2022-12-12 11:21:06 -05:00
hyperdefined
e4859de614 Merge pull request #26 from hyperdefined/dependabot/maven/net.kyori-adventure-platform-bukkit-4.2.0
Bump adventure-platform-bukkit from 4.1.2 to 4.2.0
2022-12-11 23:41:50 -05:00
dependabot[bot]
f7c1af153f Bump adventure-platform-bukkit from 4.1.2 to 4.2.0
Bumps [adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.1.2 to 4.2.0.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.1.2...v4.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-09 11:05:50 +00:00
hyperdefined
946dc591f5 Update pom.xml 2022-12-08 13:40:27 -05:00
hyperdefined
d279569199 Update EntityDamage.java 2022-12-08 13:40:12 -05:00
hyperdefined
f2739cf433 Update ToolStats.java 2022-12-07 16:21:14 -05:00
hyperdefined
bef854be14 removed logging 2022-12-07 15:19:32 -05:00
hyperdefined
2c971da1b9 unify lore management 2022-12-07 14:56:57 -05:00
hyperdefined
2e0ee18594 more possible plugin compatibility? 2022-12-01 17:56:41 -05:00
hyperdefined
98a5f20a86 better offhand/main hand detection 2022-11-30 14:41:34 -05:00
hyperdefined
dd8a0db3b6 fix using rods in offhand 2022-11-30 14:22:35 -05:00
hyperdefined
fee7d5cd99 Merge pull request #25 from hyperdefined/dependabot/maven/net.kyori-adventure-text-minimessage-4.12.0
Bump adventure-text-minimessage from 4.11.0 to 4.12.0
2022-11-29 23:21:12 -05:00
hyperdefined
70ebf49958 Merge branch 'master' of https://github.com/hyperdefined/ToolStats 2022-11-29 20:06:40 -05:00
dependabot[bot]
50d2ec0507 Bump adventure-text-minimessage from 4.11.0 to 4.12.0
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.11.0 to 4.12.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.11.0...v4.12.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-11-28 11:09:14 +00:00
hyperdefined
6428925e40 support for complete customization of numbers
closes #24
2022-11-24 00:44:59 -05:00
hyperdefined
1d4faa89a1 Merge branch 'master' of https://github.com/hyperdefined/ToolStats 2022-11-23 02:22:46 -05:00
hyperdefined
1ad650f42b Merge pull request #23 from hyperdefined/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.4.0 to 3.4.1
2022-11-06 19:20:20 -05:00
hyperdefined
fdb6626c42 update all items 1 tick later
this should fix #18
2022-11-04 20:26:55 -04:00
dependabot[bot]
cd80e6a675 Bump maven-shade-plugin from 3.4.0 to 3.4.1
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 11:23:52 +00:00
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
31 changed files with 1985 additions and 768 deletions

12
.github/workflows/modrinth.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
on:
push:
branches: [ master ]
jobs:
modrinth-desc:
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v3
- uses: funnyboy-roks/modrinth-auto-desc@v1.6
with:
auth-token: ${{ secrets.MODRINTH }}
slug: 'oBZj9E15'

View File

@@ -1,9 +1,10 @@
<h1 align="center">ToolStats</h1>
<p align="center">
<img src="https://img.shields.io/badge/Minecraft-1.15--1.19.2-orange" alt="Minecraft versions">
<img src="https://img.shields.io/badge/Minecraft-1.15--1.20.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>
<img src="https://img.shields.io/badge/made%20with-love%20&%20fluff-red" alt="Made with love & fluff">
<a href="https://ko-fi.com/hyperdefined"><img src="https://img.shields.io/badge/Donate-Ko--fi-red" alt="Donate via Ko-fi"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
<a href="https://wakatime.com/badge/user/992a7647-176a-477c-8086-e1abfba87ff4/project/0200f07a-f303-4103-a5f2-34b38c9c1fa4"><img src="https://wakatime.com/badge/user/992a7647-176a-477c-8086-e1abfba87ff4/project/0200f07a-f303-4103-a5f2-34b38c9c1fa4.svg" alt="wakatime"></a>
@@ -13,15 +14,17 @@ ToolStats is a small plugin that display various stats about tools. This plugin
Here is everything it tracks:
* Blocks mined (pickaxes, shovels, axes, hoes, shears)
* Crops mined (hoes)
* Player/mob kills (swords, axes, tridents, bows/crossbows)
* Ownership of items when crafted, looted (from chests), traded, and caught from fishing.
* Ownership of items when crafted, looted (from chests), traded, spawned via creative, and caught from fishing.
* Armor damage taken.
* Fish caught.
* Sheep sheared.
* Arrows shot (bows/crossbows)
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.
If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding.
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image2.png)
@@ -33,6 +36,8 @@ If item lore is ever incorrect, you can run `/toolstats reset` to reset the item
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image8.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image9.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image10.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image11.png)
![Image](https://raw.githubusercontent.com/hyperdefined/ToolStats/master/images/image12.png)
## Documentation
Visit the [wiki](https://github.com/hyperdefined/ToolStats/wiki) for help.

BIN
images/image11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
images/image12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

32
pom.xml
View File

@@ -23,7 +23,7 @@
<groupId>lol.hyper</groupId>
<artifactId>toolstats</artifactId>
<version>1.5</version>
<version>1.7.1</version>
<packaging>jar</packaging>
<name>ToolStats</name>
@@ -37,7 +37,7 @@
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.1</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.10.1</version>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -60,7 +60,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.1</version>
<configuration>
<relocations>
<relocation>
@@ -75,6 +75,10 @@
<pattern>lol.hyper.githubreleaseapi</pattern>
<shadedPattern>lol.hyper.toolstats.updater</shadedPattern>
</relocation>
<relocation>
<pattern>space.arim.morepaperlib</pattern>
<shadedPattern>lol.hyper.toolstats.morepaperlib</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
@@ -103,37 +107,47 @@
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>arim-mvn-lgpl3</id>
<url>https://mvn-repo.arim.space/lesser-gpl3/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19-R0.1-SNAPSHOT</version>
<version>1.20.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>lol.hyper</groupId>
<artifactId>github-release-api</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.11.0</version>
<version>4.14.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.1.2</version>
<version>4.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>space.arim.morepaperlib</groupId>
<artifactId>morepaperlib</artifactId>
<version>0.4.3</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -21,46 +21,98 @@ import lol.hyper.githubreleaseapi.GitHubRelease;
import lol.hyper.githubreleaseapi.GitHubReleaseAPI;
import lol.hyper.toolstats.commands.CommandToolStats;
import lol.hyper.toolstats.events.*;
import lol.hyper.toolstats.tools.HashMaker;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.ItemLore;
import lol.hyper.toolstats.tools.NumberFormat;
import lol.hyper.toolstats.tools.config.ConfigTools;
import lol.hyper.toolstats.tools.config.ConfigUpdater;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import space.arim.morepaperlib.MorePaperLib;
import java.io.File;
import java.io.IOException;
import java.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 {
// stores who created an item
/**
* Stores who created an item.
*/
public final NamespacedKey genericOwner = new NamespacedKey(this, "owner");
// stores when an item was created
/**
* Stores when the item was created.
*/
public final NamespacedKey timeCreated = new NamespacedKey(this, "time-created");
// stores how many player kills by sword
/**
* Stores how many player kills.
*/
public final NamespacedKey swordPlayerKills = new NamespacedKey(this, "player-kills");
// stores how many mob kills by sword
/**
* Stores how many mob kills.
*/
public final NamespacedKey swordMobKills = new NamespacedKey(this, "mob-kills");
// stores how blocks mined (used for all tools)
/**
* Stores how many blocks were mined.
*/
public final NamespacedKey genericMined = new NamespacedKey(this, "generic-mined");
// stores how many fish were caught
/**
* Stores how many crops were harvested.
*/
public final NamespacedKey cropsHarvested = new NamespacedKey(this, "crops-mined");
/**
* Stores how many fish were caught.
*/
public final NamespacedKey fishingRodCaught = new NamespacedKey(this, "fish-caught");
// stores how many times sheep were sheared
/**
* Stores how many sheep were sheared.
*/
public final NamespacedKey shearsSheared = new NamespacedKey(this, "sheared");
// stores how much damage armor has taken
/**
* Stores how much damage an armor piece has taken.
*/
public final NamespacedKey armorDamage = new NamespacedKey(this, "damage-taken");
// used for tracking new elytras
/**
* Stores how much damage an armor piece has taken (as an int).
*/
public final NamespacedKey armorDamageInt = new NamespacedKey(this, "damage-taken-int");
/**
* Key for tracking new elytras that spawn.
*/
public final NamespacedKey newElytra = new NamespacedKey(this, "new");
/**
* Key for item has.
*/
public final NamespacedKey hash = new NamespacedKey(this, "hash");
/**
* Key for arrows shot.
*/
public final NamespacedKey arrowsShot = new NamespacedKey(this, "arrows-shot");
/**
* Stores how an item was created.
* 0 = crafted.
* 1 = dropped.
* 2 = looted.
* 3 = traded.
* 4 = founded (for elytras).
* 5 = fished.
* 6 = spawned in (creative).
*/
public final NamespacedKey originType = new NamespacedKey(this, "origin");
public final int CONFIG_VERSION = 6;
public final Logger logger = this.getLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml");
public SimpleDateFormat dateFormat;
public BlocksMined blocksMined;
public ChunkPopulate chunkPopulate;
public CraftItem craftItem;
@@ -73,22 +125,29 @@ public final class ToolStats extends JavaPlugin {
public SheepShear sheepShear;
public VillagerTrade villagerTrade;
public CommandToolStats commandToolStats;
public final Logger logger = this.getLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml");
public FileConfiguration config;
public final int CONFIG_VERSION = 3;
public ItemLore itemLore;
public InventoryOpen inventoryOpen;
public PlayerJoin playerJoin;
public NumberFormat numberFormat;
public YamlConfiguration config;
private BukkitAudiences adventure;
public MorePaperLib morePaperLib;
public HashMaker hashMaker;
public CreativeEvent creativeEvent;
public ItemChecker itemChecker;
public ShootBow shootBow;
public ConfigTools configTools;
@Override
public void onEnable() {
this.adventure = BukkitAudiences.create(this);
morePaperLib = new MorePaperLib(this);
if (!configFile.exists()) {
this.saveResource("config.yml", true);
logger.info("Copying default config!");
}
loadConfig();
hashMaker = new HashMaker(this);
blocksMined = new BlocksMined(this);
craftItem = new CraftItem(this);
chunkPopulate = new ChunkPopulate(this);
@@ -101,6 +160,13 @@ public final class ToolStats extends JavaPlugin {
sheepShear = new SheepShear(this);
villagerTrade = new VillagerTrade(this);
commandToolStats = new CommandToolStats(this);
itemLore = new ItemLore(this);
inventoryOpen = new InventoryOpen(this);
playerJoin = new PlayerJoin(this);
creativeEvent = new CreativeEvent(this);
itemChecker = new ItemChecker();
shootBow = new ShootBow(this);
configTools = new ConfigTools(this);
Bukkit.getServer().getPluginManager().registerEvents(blocksMined, this);
Bukkit.getServer().getPluginManager().registerEvents(chunkPopulate, this);
@@ -113,33 +179,27 @@ public final class ToolStats extends JavaPlugin {
Bukkit.getServer().getPluginManager().registerEvents(playerInteract, this);
Bukkit.getServer().getPluginManager().registerEvents(sheepShear, this);
Bukkit.getServer().getPluginManager().registerEvents(villagerTrade, this);
Bukkit.getServer().getPluginManager().registerEvents(inventoryOpen, this);
Bukkit.getServer().getPluginManager().registerEvents(playerJoin, this);
Bukkit.getServer().getPluginManager().registerEvents(creativeEvent, this);
Bukkit.getServer().getPluginManager().registerEvents(shootBow, this);
this.getCommand("toolstats").setExecutor(commandToolStats);
new Metrics(this, 14110);
Bukkit.getScheduler().runTaskAsynchronously(this, this::checkForUpdates);
morePaperLib.scheduling().asyncScheduler().run(this::checkForUpdates);
}
public void loadConfig() {
config = YamlConfiguration.loadConfiguration(configFile);
if (config.getInt("config-version") != CONFIG_VERSION) {
logger.warning("Your config file is outdated! Please regenerate the config.");
logger.warning("Your config file is outdated! We will try to update it, but you should regenerate it!");
ConfigUpdater configUpdater = new ConfigUpdater(this);
configUpdater.updateConfig();
}
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);
}
numberFormat = new NumberFormat(this);
}
public void checkForUpdates() {
@@ -165,120 +225,22 @@ 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") || 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);
}
if (itemType == null) {
return false;
}
switch (itemType) {
case "pickaxe": {
return config.getBoolean("enabled." + configName + ".pickaxe");
}
case "sword": {
return config.getBoolean("enabled." + configName + ".sword");
}
case "shovel": {
return config.getBoolean("enabled." + configName + ".shovel");
}
case "axe": {
return config.getBoolean("enabled." + configName + ".axe");
}
case "hoe": {
return config.getBoolean("enabled." + configName + ".hoe");
}
case "shears": {
return config.getBoolean("enabled." + configName + ".shears");
}
case "bow": {
return config.getBoolean("enabled." + configName + ".bow");
}
case "trident": {
return config.getBoolean("enabled." + configName + ".trident");
}
case "helmet":
case "chestplate":
case "leggings":
case "boots": {
return config.getBoolean("enabled." + configName + ".armor");
}
}
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;
}
public void scheduleEntity(BukkitRunnable runnable, Entity entity, int delay) {
morePaperLib.scheduling().entitySpecificScheduler(entity).runDelayed(runnable, null, delay);
}
public void scheduleGlobal(BukkitRunnable runnable, int delay) {
morePaperLib.scheduling().globalRegionalScheduler().runDelayed(runnable, delay);
}
public void scheduleRegion(BukkitRunnable runnable, World world, Chunk chunk, int delay) {
morePaperLib.scheduling().regionSpecificScheduler(world, chunk.getX(), chunk.getZ()).runDelayed(runnable, delay);
}
}

View File

@@ -18,14 +18,16 @@
package lol.hyper.toolstats.commands;
import lol.hyper.toolstats.ToolStats;
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.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -50,51 +52,55 @@ public class CommandToolStats implements TabExecutor {
@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));
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
if (args.length == 0) {
audiences.sender(sender).sendMessage(Component.text("ToolStats version " + toolStats.getDescription().getVersion() + ". Created by hyperdefined.").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("ToolStats version " + toolStats.getDescription().getVersion() + ". Created by hyperdefined.", NamedTextColor.GREEN));
return true;
}
switch (args[0]) {
case "reload": {
if (sender.hasPermission("toolstats.reload")) {
toolStats.loadConfig();
audiences.sender(sender).sendMessage(Component.text("Configuration reloaded!").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Configuration reloaded!", NamedTextColor.GREEN));
} else {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.").color(NamedTextColor.RED));
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
}
return true;
}
case "reset": {
if (!sender.hasPermission("toolstats.reset")) {
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.").color(NamedTextColor.RED));
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
if (sender instanceof ConsoleCommandSender) {
audiences.sender(sender).sendMessage(Component.text("You must be a player for this command.", NamedTextColor.RED));
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));
audiences.sender(sender).sendMessage(Component.text("You do not have permission for this command.", NamedTextColor.RED));
return true;
}
Player player = (Player) sender;
ItemStack heldItem = player.getInventory().getItemInMainHand();
if (heldItem.getType() == Material.AIR) {
audiences.sender(sender).sendMessage(Component.text("You must hold an item!").color(NamedTextColor.RED));
if (!toolStats.itemChecker.isValidItem(heldItem.getType())) {
audiences.sender(sender).sendMessage(Component.text("You must hold a valid item.", NamedTextColor.RED));
return true;
}
fixItemLore(heldItem, player);
audiences.sender(sender).sendMessage(Component.text("The lore was reset!").color(NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("The lore was reset!", NamedTextColor.GREEN));
return true;
}
audiences.sender(sender).sendMessage(Component.text("This will remove ALL current lore from the held item and replace it with the correct lore.").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));
audiences.sender(sender).sendMessage(Component.text("This will remove ALL current lore from the held item and replace it with the correct lore.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("If the owner of the item is broken, it will reset to the person holding it.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Only use this if the tags on the tool are incorrect.", NamedTextColor.GREEN));
audiences.sender(sender).sendMessage(Component.text("Type /toolstats reset confirm to confirm this.", NamedTextColor.GREEN));
return true;
}
default: {
audiences.sender(sender).sendMessage(Component.text("Invalid sub-command.").color(NamedTextColor.RED));
audiences.sender(sender).sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED));
}
}
return true;
@@ -115,43 +121,35 @@ public class CommandToolStats implements TabExecutor {
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);
String caughtByLore = toolStats.configTools.getLoreFromConfig("fished.caught-by", false);
String lootedByLore = toolStats.configTools.getLoreFromConfig("looted.found-by", false);
String tradedByLore = toolStats.configTools.getLoreFromConfig("traded.traded-by", false);
String spawnedByLore = toolStats.configTools.getLoreFromConfig("spawned-in.spawned-by", false);
// make sure the config messages are not null
if (caughtByLore == null || lootedByLore == null || tradedByLore == null) {
if (caughtByLore == null || lootedByLore == null || tradedByLore == null || spawnedByLore == 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";
}
}
}
// set how the item was obtained
Integer origin = -1;
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
origin = container.get(toolStats.originType, PersistentDataType.INTEGER);
}
// set to -1 if it's invalid
if (origin == null) {
origin = -1;
}
// 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()));
lore.add(toolStats.configTools.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))));
lore.add(toolStats.configTools.getLoreFromConfig("looted.found-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
}
}
finalMeta.setLore(lore);
@@ -162,77 +160,116 @@ public class CommandToolStats implements TabExecutor {
}
}
if (toolStats.checkConfig(original, "created-by")) {
if (toolStats.configTools.checkConfig(original.getType(), "created-by")) {
if (container.has(toolStats.genericOwner, new UUIDDataType())) {
container.set(toolStats.genericOwner, new UUIDDataType(), player.getUniqueId());
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
String ownerName = null;
// if we can read the current owner
if (owner != null) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner);
ownerName = offlinePlayer.getName();
}
// if the owner's name is null for whatever reason, set the new owner
// to the current player running the command
if (ownerName == null) {
audiences.player(player).sendMessage(Component.text("The owner of this item is null. Setting to " + player.getName() + ".", NamedTextColor.RED));
ownerName = player.getName();
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()));
switch (origin) {
case 0: {
lore.add(toolStats.configTools.getLoreFromConfig("created.created-by", true).replace("{player}", ownerName));
break;
}
case "CAUGHT": {
lore.add(toolStats.getLoreFromConfig("fished.caught-by", true).replace("{player}", player.getName()));
case 2: {
lore.add(toolStats.configTools.getLoreFromConfig("looted.looted-by", true).replace("{player}", ownerName));
break;
}
case "LOOTED": {
lore.add(toolStats.getLoreFromConfig("looted.found-by", true).replace("{player}", player.getName()));
case 3: {
lore.add(toolStats.configTools.getLoreFromConfig("traded.traded-by", true).replace("{player}", ownerName));
break;
}
case "TRADED": {
lore.add(toolStats.getLoreFromConfig("traded.traded-by", true).replace("{player}", player.getName()));
case 4: {
lore.add(toolStats.configTools.getLoreFromConfig("looted.found-by", true).replace("{player}", ownerName));
break;
}
case 5: {
lore.add(toolStats.configTools.getLoreFromConfig("fished.caught-by", true).replace("{player}", ownerName));
break;
}
case 6: {
lore.add(toolStats.configTools.getLoreFromConfig("spawned-in.spawned-by", true).replace("{player}", ownerName));
break;
}
}
}
}
if (toolStats.checkConfig(original, "created-date")) {
if (toolStats.configTools.checkConfig(original.getType(), "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))));
switch (origin) {
case 0: {
lore.add(toolStats.configTools.getLoreFromConfig("created.created-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
case "CAUGHT": {
lore.add(toolStats.getLoreFromConfig("fished.caught-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
case 2: {
lore.add(toolStats.configTools.getLoreFromConfig("looted.looted-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
case "LOOTED": {
lore.add(toolStats.getLoreFromConfig("looted.found-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
case 3: {
lore.add(toolStats.configTools.getLoreFromConfig("traded.traded-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
case "TRADED": {
lore.add(toolStats.getLoreFromConfig("traded.traded-on", true).replace("{date}", toolStats.dateFormat.format(new Date(time))));
case 4: {
lore.add(toolStats.configTools.getLoreFromConfig("looted.found-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
case 5: {
lore.add(toolStats.configTools.getLoreFromConfig("fished.caught-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
case 6: {
lore.add(toolStats.configTools.getLoreFromConfig("spawned-in.spawned-on", true).replace("{date}", toolStats.numberFormat.formatDate(new Date(time))));
break;
}
}
}
}
}
if (toolStats.checkConfig(original, "player-kills")) {
if (toolStats.configTools.checkConfig(original.getType(), "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)));
lore.add(toolStats.configTools.getLoreFromConfig("kills.player", true).replace("{kills}", toolStats.numberFormat.formatInt(kills)));
}
}
}
if (toolStats.checkConfig(original, "mob-kills")) {
if (toolStats.configTools.checkConfig(original.getType(), "mob-kills")) {
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
Integer kills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
if (kills != null) {
lore.add(toolStats.getLoreFromConfig("kills.mob", true).replace("{kills}", NumberFormat.formatInt(kills)));
lore.add(toolStats.configTools.getLoreFromConfig("kills.mob", true).replace("{kills}", toolStats.numberFormat.formatInt(kills)));
}
}
}
if (toolStats.checkConfig(original, "blocks-mined")) {
if (toolStats.configTools.checkConfig(original.getType(), "blocks-mined")) {
if (original.getType().toString().toLowerCase(Locale.ROOT).contains("hoe")) {
if (container.has(toolStats.cropsHarvested, PersistentDataType.INTEGER)) {
Integer crops = container.get(toolStats.cropsHarvested, PersistentDataType.INTEGER);
if (crops != null) {
lore.add(toolStats.configTools.getLoreFromConfig("crops-harvested", true).replace("{crops}", toolStats.numberFormat.formatInt(crops)));
}
}
}
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)));
lore.add(toolStats.configTools.getLoreFromConfig("blocks-mined", true).replace("{blocks}", toolStats.numberFormat.formatInt(blocksMined)));
}
}
}
@@ -240,7 +277,7 @@ public class CommandToolStats implements TabExecutor {
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)));
lore.add(toolStats.configTools.getLoreFromConfig("fished.fish-caught", true).replace("{fish}", toolStats.numberFormat.formatInt(fish)));
}
}
}
@@ -248,7 +285,7 @@ public class CommandToolStats implements TabExecutor {
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)));
lore.add(toolStats.configTools.getLoreFromConfig("sheep-sheared", true).replace("{sheep}", toolStats.numberFormat.formatInt(sheep)));
}
}
}
@@ -256,7 +293,15 @@ public class CommandToolStats implements TabExecutor {
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)));
lore.add(toolStats.configTools.getLoreFromConfig("damage-taken", true).replace("{damage}", toolStats.numberFormat.formatDouble(damage)));
}
}
}
if (toolStats.config.getBoolean("enabled.arrows-shot")) {
if (container.has(toolStats.arrowsShot, PersistentDataType.INTEGER)) {
Integer arrows = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
if (arrows != null) {
lore.add(toolStats.configTools.getLoreFromConfig("arrows-shot", true).replace("{arrows}", toolStats.numberFormat.formatInt(arrows)));
}
}
}

View File

@@ -18,22 +18,24 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class BlocksMined implements Listener {
@@ -43,7 +45,7 @@ public class BlocksMined implements Listener {
this.toolStats = toolStats;
}
@EventHandler (priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.MONITOR)
public void onBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
@@ -52,18 +54,35 @@ public class BlocksMined implements Listener {
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;
PlayerInventory inventory = player.getInventory();
ItemStack heldItem = inventory.getItemInMainHand();
Block block = event.getBlock();
if (block.getType() == Material.CHEST) {
toolStats.playerInteract.openedChests.put(block, player);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
toolStats.playerInteract.openedChests.remove(block);
}
};
toolStats.scheduleGlobal(runnable, 20);
}
// only check certain items
if (!ItemChecker.isMineTool(heldItem.getType())) {
if (!toolStats.itemChecker.isMineTool(heldItem.getType())) {
return;
}
// if it's an item we want, update the stats
updateBlocksMined(heldItem);
if (heldItem.getType().toString().toLowerCase(Locale.ROOT).contains("hoe")) {
// player is breaking crops with a hoe
if (block.getBlockData() instanceof Ageable) {
updateCropsMined(heldItem, (Ageable) block.getBlockData());
}
} else {
// update the blocks mined
updateBlocksMined(heldItem);
}
}
private void updateBlocksMined(ItemStack playerTool) {
@@ -88,40 +107,47 @@ public class BlocksMined implements Listener {
blocksMined++;
container.set(toolStats.genericMined, PersistentDataType.INTEGER, blocksMined);
String configLore = toolStats.getLoreFromConfig("blocks-mined", false);
String configLoreRaw = toolStats.getLoreFromConfig("blocks-mined", true);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(playerTool.getType(), "blocks-mined")) {
String blocksMinedFormatted = toolStats.numberFormat.formatInt(blocksMined);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{blocks}", blocksMinedFormatted, "blocks-mined");
meta.setLore(newLore);
}
playerTool.setItemMeta(meta);
}
if (configLore == null || configLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages.blocks-mined!");
private void updateCropsMined(ItemStack playerTool, Ageable block) {
// ignore crops that are not fully grown
if (block.getAge() != block.getMaximumAge()) {
return;
}
List<String> lore;
String newLine = configLoreRaw.replace("{blocks}", NumberFormat.formatInt(blocksMined));
if (meta.hasLore()) {
lore = meta.getLore();
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(configLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(newLine);
ItemMeta meta = playerTool.getItemMeta();
if (meta == null) {
toolStats.logger.warning(playerTool + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer cropsMined = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.cropsHarvested, PersistentDataType.INTEGER)) {
cropsMined = container.get(toolStats.cropsHarvested, PersistentDataType.INTEGER);
}
if (cropsMined == null) {
cropsMined = 0;
toolStats.logger.warning(playerTool + " does not have valid crops-mined set! Resting to zero. This should NEVER happen.");
}
cropsMined++;
container.set(toolStats.cropsHarvested, PersistentDataType.INTEGER, cropsMined);
// do we add the lore based on the config?
if (toolStats.checkConfig(playerTool, "blocks-mined")) {
meta.setLore(lore);
if (toolStats.configTools.checkConfig(playerTool.getType(), "blocks-mined")) {
String cropsMinedFormatted = toolStats.numberFormat.formatInt(cropsMined);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{crops}", cropsMinedFormatted, "crops-harvested");
meta.setLore(newLore);
}
playerTool.setItemMeta(meta);
}

View File

@@ -18,7 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
@@ -32,11 +31,12 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
public class ChunkPopulate implements Listener {
// this tags all elytras with a "new" tag
// this let's us track any new elytras player loot
// this lets us track any new elytras player loot
private final ToolStats toolStats;
@@ -51,18 +51,22 @@ public class ChunkPopulate implements Listener {
}
// this is delayed because entities are not loaded instantly
// we just check 1 second later
Bukkit.getScheduler().runTaskLater(toolStats, () -> {
Chunk chunk = event.getChunk();
for (Entity entity : chunk.getEntities()) {
// if there is a new item frame
if (entity instanceof ItemFrame) {
Chunk chunk = event.getChunk();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
for (Entity entity : chunk.getEntities()) {
// if there is a new item frame
if (!(entity instanceof ItemFrame)) {
continue;
}
ItemFrame itemFrame = (ItemFrame) entity;
// if the item frame has an elytra
if (itemFrame.getItem().getType() == Material.ELYTRA) {
ItemStack elytraCopy = itemFrame.getItem();
ItemMeta meta = elytraCopy.getItemMeta();
if (meta == null) {
return;
continue;
}
// add the new tag so we know it's new
PersistentDataContainer container = meta.getPersistentDataContainer();
@@ -72,6 +76,7 @@ public class ChunkPopulate implements Listener {
}
}
}
}, 20);
};
toolStats.scheduleRegion(runnable, chunk.getWorld(), chunk, 20);
}
}

View File

@@ -18,7 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
@@ -57,7 +56,7 @@ public class CraftItem implements Listener {
return;
}
// only check certain items
if (!ItemChecker.isValidItem(itemStack.getType())) {
if (!toolStats.itemChecker.isValidItem(itemStack.getType())) {
return;
}
@@ -104,20 +103,15 @@ public class CraftItem implements Listener {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
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;
}
container.set(toolStats.originType, PersistentDataType.INTEGER, 0);
List<String> lore;
// get the current lore the item
@@ -127,13 +121,24 @@ public class CraftItem implements Listener {
lore = new ArrayList<>();
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "created-date")) {
lore.add(createdOnRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
if (toolStats.configTools.checkConfig(itemStack.getType(), "created-date")) {
String createdOnRaw = toolStats.configTools.getLoreFromConfig("created.created-on", true);
if (createdOnRaw == null) {
toolStats.logger.warning("There is no lore message for messages.created.created-on!");
return null;
}
lore.add(createdOnRaw.replace("{date}", toolStats.numberFormat.formatDate(finalDate)));
meta.setLore(lore);
}
if (toolStats.checkConfig(itemStack, "created-by")) {
if (toolStats.configTools.checkConfig(itemStack.getType(), "created-by")) {
String createdByRaw = toolStats.configTools.getLoreFromConfig("created.created-by", true);
if (createdByRaw == null) {
toolStats.logger.warning("There is no lore message for messages.created.created-by!");
return null;
}
lore.add(createdByRaw.replace("{player}", owner.getName()));
meta.setLore(lore);
}
meta.setLore(lore);
newItem.setItemMeta(meta);
return newItem;
}

View File

@@ -0,0 +1,109 @@
/*
* 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 lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Date;
import java.util.List;
public class CreativeEvent implements Listener {
private final ToolStats toolStats;
public CreativeEvent(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
public void onCreativeEvent(InventoryCreativeEvent event) {
Player player = (Player) event.getWhoClicked();
// make sure they are in creative mode
if (player.getGameMode() != GameMode.CREATIVE) {
return;
}
ItemStack spawnedItem = event.getCursor();
if (!toolStats.itemChecker.isValidItem(spawnedItem.getType())) {
return;
}
ItemMeta spawnedItemMeta = spawnedItem.getItemMeta();
if (spawnedItemMeta == null) {
return;
}
// if the item already has an origin set, don't add it again
// this is needed since you can spam click an item and the event will fire again
PersistentDataContainer container = spawnedItemMeta.getPersistentDataContainer();
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
return;
}
// add the tags to the item
ItemStack newItem = addLore(spawnedItem, player);
if (newItem != null) {
event.setCursor(newItem);
}
}
/**
* Adds tags to newly spawned items in creative.
*
* @param spawnedItem The item.
*/
private ItemStack addLore(ItemStack spawnedItem, Player owner) {
ItemStack newSpawnedItem = spawnedItem.clone();
ItemMeta meta = newSpawnedItem.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newSpawnedItem + " does NOT have any meta! Unable to update stats.");
return null;
}
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(spawnedItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 6);
if (toolStats.configTools.checkConfig(newSpawnedItem.getType(), "spawned-in")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
newSpawnedItem.setItemMeta(meta);
return newSpawnedItem;
}
}

View File

@@ -18,8 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.*;
@@ -47,7 +45,7 @@ public class EntityDamage implements Listener {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.MONITOR)
public void onDamage(EntityDamageByEntityEvent event) {
if (event.isCancelled()) {
return;
@@ -56,7 +54,7 @@ public class EntityDamage implements Listener {
if (!(event.getEntity() instanceof LivingEntity)) {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
LivingEntity mobBeingAttacked = (LivingEntity) event.getEntity();
// ignore void and /kill damage
EntityDamageEvent.DamageCause cause = event.getCause();
@@ -65,89 +63,103 @@ public class EntityDamage implements Listener {
}
// mob is going to die
if (livingEntity.getHealth() - event.getFinalDamage() <= 0) {
if (mobBeingAttacked.getHealth() - event.getFinalDamage() <= 0) {
// a player is killing something
if (event.getDamager() instanceof Player) {
Player player = (Player) event.getDamager();
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) {
Player attackingPlayer = (Player) event.getDamager();
if (attackingPlayer.getGameMode() == GameMode.CREATIVE || attackingPlayer.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory attackingPlayerInventory = attackingPlayer.getInventory();
ItemStack heldItem = attackingPlayerInventory.getItemInMainHand();
// only check certain items
if (!ItemChecker.isMeleeWeapon(heldItem.getType())) {
if (!toolStats.itemChecker.isMeleeWeapon(heldItem.getType())) {
return;
}
// a player is killing another player
if (livingEntity instanceof Player) {
player.getInventory().setItem(player.getInventory().getHeldItemSlot(), updatePlayerKills(heldItem));
if (mobBeingAttacked instanceof Player) {
updatePlayerKills(heldItem);
return;
}
// player is killing regular mob
player.getInventory().setItem(player.getInventory().getHeldItemSlot(), updateMobKills(heldItem));
trackedMobs.add(livingEntity.getUniqueId());
updateMobKills(heldItem);
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
// trident is being thrown at something
if (event.getDamager() instanceof Trident) {
Trident trident = (Trident) event.getDamager();
ItemStack clone;
ItemStack newTrident;
// trident is killing player
if (livingEntity instanceof Player) {
clone = updatePlayerKills(trident.getItem());
if (mobBeingAttacked instanceof Player) {
newTrident = tridentPlayerKills(trident.getItem());
} else {
clone = updateMobKills(trident.getItem());
// trident is killing a mob
newTrident = tridentMobKills(trident.getItem());
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
if (clone == null) {
return;
if (newTrident != null) {
trident.setItem(newTrident);
}
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) {
Player shootingPlayer = (Player) arrow.getShooter();
if (shootingPlayer.getGameMode() == GameMode.CREATIVE || shootingPlayer.getGameMode() == GameMode.SPECTATOR) {
return;
}
ItemStack heldItem = player.getInventory().getItem(player.getInventory().getHeldItemSlot());
if (heldItem == null) {
PlayerInventory inventory = shootingPlayer.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
ItemStack heldBow = null;
if (isMainHand) {
heldBow = inventory.getItemInMainHand();
}
if (isOffHand) {
heldBow = inventory.getItemInOffHand();
}
// if the player is holding a bow in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
heldBow = inventory.getItemInMainHand();
}
// player swapped
if (heldBow == 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));
} else {
player.getInventory().setItem(player.getInventory().getHeldItemSlot(), updateMobKills(heldItem));
}
// player is shooting another player
if (mobBeingAttacked instanceof Player) {
updatePlayerKills(heldBow);
} else {
updateMobKills(heldBow);
trackedMobs.add(mobBeingAttacked.getUniqueId());
}
}
}
}
// 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) {
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
}
}
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler(priority = EventPriority.MONITOR)
public void onDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
return;
@@ -159,25 +171,25 @@ public class EntityDamage implements Listener {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
LivingEntity mobBeingAttacked = (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) {
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
}
}
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler(priority = EventPriority.MONITOR)
public void onDamage(EntityDamageByBlockEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) {
return;
@@ -189,18 +201,18 @@ public class EntityDamage implements Listener {
return;
}
LivingEntity livingEntity = (LivingEntity) event.getEntity();
LivingEntity mobBeingAttacked = (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) {
if (mobBeingAttacked instanceof Player) {
Player playerTakingDamage = (Player) mobBeingAttacked;
if (playerTakingDamage.getGameMode() == GameMode.CREATIVE || playerTakingDamage.getGameMode() == GameMode.SPECTATOR) {
return;
}
PlayerInventory inventory = player.getInventory();
for (ItemStack armor : inventory.getArmorContents()) {
if (armor != null) {
if (ItemChecker.isArmor(armor.getType())) {
updateArmorDamage(armor, event.getFinalDamage());
PlayerInventory playerInventory = playerTakingDamage.getInventory();
for (ItemStack armorPiece : playerInventory.getArmorContents()) {
if (armorPiece != null) {
if (toolStats.itemChecker.isArmor(armorPiece.getType())) {
updateDamage(armorPiece, event.getFinalDamage());
}
}
}
@@ -211,14 +223,12 @@ public class EntityDamage implements Listener {
* 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();
private void updatePlayerKills(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return null;
return;
}
Integer playerKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
@@ -234,57 +244,25 @@ public class EntityDamage implements Listener {
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();
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(playerKillsLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(newLine);
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "player-kills")) {
meta.setLore(lore);
if (toolStats.configTools.checkConfig(itemStack.getType(), "player-kills")) {
String playerKillsFormatted = toolStats.numberFormat.formatInt(playerKills);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{kills}", playerKillsFormatted, "kills.player");
meta.setLore(newLore);
}
finalItem.setItemMeta(meta);
return finalItem;
itemStack.setItemMeta(meta);
}
/**
* 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();
private void updateMobKills(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
return null;
return;
}
Integer mobKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
@@ -300,43 +278,13 @@ public class EntityDamage implements Listener {
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();
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(mobKillsLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(newLine);
}
// do we add the lore based on the config?
if (toolStats.checkConfig(itemStack, "mob-kills")) {
meta.setLore(lore);
if (toolStats.configTools.checkConfig(itemStack.getType(), "mob-kills")) {
String mobKillsFormatted = toolStats.numberFormat.formatInt(mobKills);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{kills}", mobKillsFormatted, "kills.mob");
meta.setLore(newLore);
}
finalItem.setItemMeta(meta);
return finalItem;
itemStack.setItemMeta(meta);
}
/**
@@ -345,7 +293,7 @@ public class EntityDamage implements Listener {
* @param itemStack The armor piece.
* @param damage How much damage is being added.
*/
private void updateArmorDamage(ItemStack itemStack, double damage) {
private void updateDamage(ItemStack itemStack, double damage) {
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
toolStats.logger.warning(itemStack + " does NOT have any meta! Unable to update stats.");
@@ -364,41 +312,85 @@ public class EntityDamage implements Listener {
damageTaken = damageTaken + damage;
container.set(toolStats.armorDamage, PersistentDataType.DOUBLE, damageTaken);
container.set(toolStats.armorDamageInt, PersistentDataType.INTEGER, damageTaken.intValue());
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();
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(damageTakenLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(newLine);
}
if (toolStats.config.getBoolean("enabled.armor-damage")) {
meta.setLore(lore);
String damageTakenFormatted = toolStats.numberFormat.formatDouble(damageTaken);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{damage}", damageTakenFormatted, "damage-taken");
meta.setLore(newLore);
}
itemStack.setItemMeta(meta);
}
/**
* Updates a trident's mob kills.
*
* @param trident The item to update.
*/
private ItemStack tridentMobKills(ItemStack trident) {
ItemStack newTrident = trident.clone();
ItemMeta meta = newTrident.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newTrident + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer mobKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordMobKills, PersistentDataType.INTEGER)) {
mobKills = container.get(toolStats.swordMobKills, PersistentDataType.INTEGER);
}
if (mobKills == null) {
mobKills = 0;
toolStats.logger.warning(newTrident + " does not have valid mob-kills set! Resting to zero. This should NEVER happen.");
}
mobKills++;
container.set(toolStats.swordMobKills, PersistentDataType.INTEGER, mobKills);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(newTrident.getType(), "mob-kills")) {
String mobKillsFormatted = toolStats.numberFormat.formatInt(mobKills);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{kills}", mobKillsFormatted, "kills.mob");
meta.setLore(newLore);
}
newTrident.setItemMeta(meta);
return newTrident;
}
/**
* Updates a trident's player kills.
*
* @param trident The item to update.
*/
private ItemStack tridentPlayerKills(ItemStack trident) {
ItemStack newTrident = trident.clone();
ItemMeta meta = newTrident.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newTrident + " does NOT have any meta! Unable to update stats.");
return null;
}
Integer playerKills = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.swordPlayerKills, PersistentDataType.INTEGER)) {
playerKills = container.get(toolStats.swordPlayerKills, PersistentDataType.INTEGER);
}
if (playerKills == null) {
playerKills = 0;
toolStats.logger.warning(newTrident + " does not have valid player-kills set! Resting to zero. This should NEVER happen.");
}
playerKills++;
container.set(toolStats.swordPlayerKills, PersistentDataType.INTEGER, playerKills);
// do we add the lore based on the config?
if (toolStats.configTools.checkConfig(newTrident.getType(), "player-kills")) {
String playerKillsFormatted = toolStats.numberFormat.formatInt(playerKills);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{kills}", playerKillsFormatted, "kills.player");
meta.setLore(newLore);
}
newTrident.setItemMeta(meta);
return newTrident;
}
}

View File

@@ -18,7 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemChecker;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -27,6 +26,8 @@ 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.*;
@@ -48,9 +49,17 @@ public class EntityDeath implements Listener {
// if it's a mob we are tracking that matters
if (toolStats.mobKill.trackedMobs.contains(livingEntityUUID)) {
for (int i = 0; i < event.getDrops().size(); i++) {
ItemStack current = event.getDrops().get(i);
if (ItemChecker.isValidItem(current.getType())) {
ItemStack newItem = addLore(current, livingEntity.getName());
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 (toolStats.itemChecker.isValidItem(droppedItem.getType())) {
ItemStack newItem = addLore(droppedItem, livingEntity.getName());
if (newItem != null) {
event.getDrops().set(i, newItem);
}
@@ -64,7 +73,7 @@ public class EntityDeath implements Listener {
* Adds "drop by" tag to item.
*
* @param oldItem The item to add lore to.
* @param mob The mob or player name.
* @param mob The mob or player name.
*/
private ItemStack addLore(ItemStack oldItem, String mob) {
ItemStack newItem = oldItem.clone();
@@ -72,35 +81,13 @@ public class EntityDeath implements Listener {
if (meta == null) {
return null;
}
boolean hasTag = false;
String droppedByLore = toolStats.getLoreFromConfig("dropped-by", false);
String droppedByLoreRaw = toolStats.getLoreFromConfig("dropped-by", true);
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(toolStats.originType, PersistentDataType.INTEGER, 1);
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();
for (int x = 0; x < lore.size(); x++) {
if (lore.get(x).contains(droppedByLore)) {
// replace existing tag
lore.set(x, droppedByLoreRaw.replace("{name}", mob));
hasTag = true;
}
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
}
if (!hasTag) {
lore.add(droppedByLoreRaw.replace("{name}", mob));
}
if (toolStats.config.getBoolean("enabled.dropped-by")) {
meta.setLore(lore);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{name}", mob, "dropped-by");
meta.setLore(newLore);
}
newItem.setItemMeta(meta);
return newItem;

View File

@@ -18,9 +18,7 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.ItemChecker;
import lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -31,14 +29,12 @@ 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;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -57,12 +53,11 @@ public class GenerateLoot implements Listener {
return;
}
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
// 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();
@@ -78,45 +73,14 @@ public class GenerateLoot implements Listener {
return;
}
// 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);
Player player = toolStats.playerInteract.openedChests.get(openedChest);
setLoot(event.getLoot(), player);
}
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);
}
}
}
setLoot(event.getLoot(), player);
}
}
}
@@ -142,29 +106,44 @@ public class GenerateLoot implements Listener {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 2);
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;
if (toolStats.configTools.checkConfig(newItem.getType(), "looted-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
List<String> lore;
if (meta.hasLore()) {
lore = meta.getLore();
} else {
lore = new ArrayList<>();
}
if (toolStats.checkConfig(newItem, "looted-tag")) {
lore.add(foundOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(foundByLoreRaw.replace("{player}", owner.getName()));
}
meta.setLore(lore);
newItem.setItemMeta(meta);
return newItem;
}
/**
* Add tags to the generated loot.
*
* @param loot The loot from the event.
* @param player The player triggering the event.
*/
private void setLoot(List<ItemStack> loot, Player player) {
for (int i = 0; i < loot.size(); i++) {
ItemStack itemStack = loot.get(i);
// ignore air
if (itemStack == null || itemStack.getType() == Material.AIR) {
continue;
}
if (toolStats.itemChecker.isValidItem(itemStack.getType())) {
ItemStack newItem = addLore(itemStack, player);
if (newItem != null) {
loot.set(i, newItem);
}
}
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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 lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID;
public class InventoryOpen implements Listener {
private final ToolStats toolStats;
public InventoryOpen(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
public void onOpen(InventoryOpenEvent event) {
if (event.isCancelled()) {
return;
}
Inventory inventory = event.getInventory();
Location location = event.getInventory().getLocation();
for (ItemStack itemStack : inventory) {
if (itemStack == null) {
continue;
}
// ignore items that are not the right type
if (!toolStats.itemChecker.isValidItem(itemStack.getType())) {
continue;
}
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null) {
continue;
}
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
// generate a hash if the item doesn't have one (if it's enabled in the config)
if (toolStats.config.getBoolean("generate-hash-for-items")) {
if (!container.has(toolStats.hash, PersistentDataType.STRING)) {
// make sure the item has an owner
if (!container.has(toolStats.genericOwner, new UUIDDataType())) {
continue;
}
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
if (owner == null) {
continue;
}
Long timestamp = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (timestamp == null) {
continue;
}
String hash = toolStats.hashMaker.makeHash(itemStack.getType(), owner, timestamp);
toolStats.logger.info(hash);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
}
// add origin tag
if (!container.has(toolStats.originType, PersistentDataType.INTEGER)) {
itemMeta = toolStats.itemLore.getOrigin(itemMeta, itemStack.getType() == Material.ELYTRA);
if (itemMeta == null) {
continue;
}
}
ItemMeta clone = itemMeta.clone();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
itemStack.setItemMeta(clone);
}
};
if (location != null) {
toolStats.scheduleRegion(runnable, location.getWorld(), location.getChunk(), 1);
}
}
}
}

View File

@@ -31,7 +31,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -90,29 +89,23 @@ public class PickupItem implements Listener {
long timeCreated = System.currentTimeMillis();
Date finalDate = new Date(timeCreated);
PersistentDataContainer container = meta.getPersistentDataContainer();
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(finalItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 4);
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();
} else {
lore = new ArrayList<>();
}
if (toolStats.config.getBoolean("enabled.elytra-tag")) {
lore.add(foundOnLoreRaw.replace("{date}", toolStats.dateFormat.format(finalDate)));
lore.add(foundByLoreRaw.replace("{player}", owner.getName()));
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
meta.setLore(lore);
finalItem.setItemMeta(meta);
return finalItem;
}

View File

@@ -18,8 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
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;
@@ -30,11 +28,11 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -46,7 +44,7 @@ public class PlayerFish implements Listener {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.HIGH)
@EventHandler(priority = EventPriority.MONITOR)
public void onFish(PlayerFishEvent event) {
if (event.isCancelled()) {
return;
@@ -61,19 +59,39 @@ public class PlayerFish implements Listener {
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) {
// player can fish with their offhand
PlayerInventory inventory = player.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.FISHING_ROD;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.FISHING_ROD;
ItemStack fishingRod = null;
if (isMainHand) {
fishingRod = inventory.getItemInMainHand();
}
if (isOffHand) {
fishingRod = inventory.getItemInOffHand();
}
// if the player is hold fishing rods in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
fishingRod = inventory.getItemInMainHand();
}
// player swapped items?
if (fishingRod == null) {
return;
}
// update the fishing rod to the new one
updateFishCount(heldItem);
// update the fishing rod!
updateFishCount(fishingRod);
// check if the player caught an item
if (event.getCaught() == null) {
return;
}
ItemStack caughtItem = ((Item) event.getCaught()).getItemStack();
Item caughtItemEntity = (Item) event.getCaught();
if (ItemChecker.isValidItem(caughtItem.getType())) {
if (toolStats.itemChecker.isValidItem(caughtItem.getType())) {
ItemStack newItem = addNewLore(caughtItem, player);
if (newItem != null) {
caughtItemEntity.setItemStack(newItem);
@@ -83,6 +101,7 @@ public class PlayerFish implements Listener {
/**
* Update a fishing rod's fish count.
*
* @param fishingRod The fishing rod to update.
*/
private void updateFishCount(ItemStack fishingRod) {
@@ -105,60 +124,19 @@ public class PlayerFish implements Listener {
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();
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(fishCaughtLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
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);
String fishCaughtFormatted = toolStats.numberFormat.formatInt(fishCaught);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{fish}", fishCaughtFormatted, "fished.fish-caught");
meta.setLore(newLore);
}
fishingRod.setItemMeta(meta);
}
/**
* Add lore to newly caught item.
*
* @param originalItem The original item to add lore.
* @param owner The player who caught it.
* @param owner The player who caught it.
* @return A copy of the new item with lore.
*/
private ItemStack addNewLore(ItemStack originalItem, Player owner) {
@@ -175,28 +153,17 @@ public class PlayerFish implements Listener {
return null;
}
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 5);
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();
assert lore != null;
} else {
lore = new ArrayList<>();
}
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);
if (toolStats.configTools.checkConfig(newItem.getType(), "fished-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
newItem.setItemMeta(meta);
return newItem;

View File

@@ -18,7 +18,6 @@
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;
@@ -31,15 +30,17 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.Map;
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 final Map<Block, Player> openedChests = new HashMap<>();
public final Map<StorageMinecart, Player> openedMineCarts = new HashMap<>();
public PlayerInteract(ToolStats toolStats) {
this.toolStats = toolStats;
@@ -63,7 +64,13 @@ public class PlayerInteract implements Listener {
// store when a player opens a chest
if (block.getType() != Material.AIR && block.getType() == Material.CHEST) {
openedChests.put(block, player);
Bukkit.getScheduler().runTaskLater(toolStats, () -> openedChests.remove(block), 20);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
openedChests.remove(block);
}
};
toolStats.scheduleGlobal(runnable, 20);
}
}
@@ -78,7 +85,13 @@ public class PlayerInteract implements Listener {
if (clicked.getType() == EntityType.MINECART_CHEST) {
StorageMinecart storageMinecart = (StorageMinecart) clicked;
openedMineCarts.put(storageMinecart, player);
Bukkit.getScheduler().runTaskLater(toolStats, () -> openedMineCarts.remove(storageMinecart), 20);
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
openedMineCarts.remove(storageMinecart);
}
};
toolStats.scheduleGlobal(runnable, 20);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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 lol.hyper.toolstats.tools.UUIDDataType;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID;
public class PlayerJoin implements Listener {
private final ToolStats toolStats;
public PlayerJoin(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Inventory inventory = player.getInventory();
for (ItemStack itemStack : inventory) {
if (itemStack == null) {
continue;
}
// ignore items that are not the right type
if (!toolStats.itemChecker.isValidItem(itemStack.getType())) {
continue;
}
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null) {
continue;
}
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
// generate a hash if the item doesn't have one
if (!container.has(toolStats.hash, PersistentDataType.STRING)) {
// make sure the item has an owner
if (!container.has(toolStats.genericOwner, new UUIDDataType())) {
continue;
}
UUID owner = container.get(toolStats.genericOwner, new UUIDDataType());
if (owner == null) {
continue;
}
Long timestamp = container.get(toolStats.timeCreated, PersistentDataType.LONG);
if (timestamp == null) {
continue;
}
String hash = toolStats.hashMaker.makeHash(itemStack.getType(), owner, timestamp);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
// add origin tag
if (!container.has(toolStats.originType, PersistentDataType.INTEGER)) {
itemMeta = toolStats.itemLore.getOrigin(itemMeta, itemStack.getType() == Material.ELYTRA);
if (itemMeta == null) {
continue;
}
}
ItemMeta clone = itemMeta.clone();
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
itemStack.setItemMeta(clone);
}
};
toolStats.scheduleEntity(runnable, player, 1);
}
}
}

View File

@@ -18,7 +18,6 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.NumberFormat;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
@@ -29,11 +28,11 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
public class SheepShear implements Listener {
@@ -44,7 +43,7 @@ public class SheepShear implements Listener {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.MONITOR)
public void onShear(PlayerInteractEntityEvent event) {
if (event.isCancelled()) {
return;
@@ -57,34 +56,52 @@ public class SheepShear implements Listener {
if (!(entity instanceof Sheep)) {
return;
}
// 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) {
// make sure the player is holding shears
// player can shear with their offhand
PlayerInventory inventory = player.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.SHEARS;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.SHEARS;
ItemStack shears = null;
if (isMainHand) {
shears = inventory.getItemInMainHand();
}
if (isOffHand) {
shears = inventory.getItemInOffHand();
}
// if the player is hold fishing rods in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
shears = inventory.getItemInMainHand();
}
// player swapped items?
if (shears == null) {
return;
}
Sheep sheep = (Sheep) entity;
// make sure the sheep is not sheared
if (!sheep.isSheared()) {
ItemStack newShears = addLore(heldItem);
if (newShears != null) {
player.getInventory().setItem(heldItemSlot, newShears);
}
if (sheep.isSheared()) {
return;
}
// update the stats
ItemStack finalShears = shears;
addLore(finalShears);
}
/**
* Adds tags to shears.
*
* @param oldShears The shears.
* @param newShears The shears.
*/
private ItemStack addLore(ItemStack oldShears) {
ItemStack newShears = oldShears.clone();
private void addLore(ItemStack newShears) {
ItemMeta meta = newShears.getItemMeta();
if (meta == null) {
toolStats.logger.warning(newShears + " does NOT have any meta! Unable to update stats.");
return null;
return;
}
Integer sheepSheared = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
@@ -100,41 +117,11 @@ public class SheepShear implements Listener {
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();
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(sheepShearedLore)) {
hasLore = true;
lore.set(x, newLine);
break;
}
}
// if the item has lore but doesn't have the tag, add it
if (!hasLore) {
lore.add(newLine);
}
} else {
// if the item has no lore, create a new list and add the string
lore = new ArrayList<>();
lore.add(newLine);
}
if (toolStats.config.getBoolean("enabled.sheep-sheared")) {
meta.setLore(lore);
String sheepShearedFormatted = toolStats.numberFormat.formatInt(sheepSheared);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{sheep}", sheepShearedFormatted, "sheep-sheared");
meta.setLore(newLore);
}
newShears.setItemMeta(meta);
return newShears;
}
}

View File

@@ -0,0 +1,113 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
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.EntityShootBowEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.List;
public class ShootBow implements Listener {
private final ToolStats toolStats;
public ShootBow(ToolStats toolStats) {
this.toolStats = toolStats;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onShoot(EntityShootBowEvent event) {
Entity shooter = event.getEntity();
// only listen for players
if (!(shooter instanceof Player)) {
return;
}
Player player = (Player) shooter;
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.ADVENTURE) {
return;
}
PlayerInventory inventory = player.getInventory();
boolean isMainHand = inventory.getItemInMainHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = inventory.getItemInOffHand().getType() == Material.BOW || inventory.getItemInMainHand().getType() == Material.CROSSBOW;
ItemStack heldBow = null;
if (isMainHand) {
heldBow = inventory.getItemInMainHand();
}
if (isOffHand) {
heldBow = inventory.getItemInOffHand();
}
// if the player is holding a bow in both hands
// default to main hand since that takes priority
if (isMainHand && isOffHand) {
heldBow = inventory.getItemInMainHand();
}
// player swapped
if (heldBow == null) {
return;
}
updateArrowsShot(heldBow);
}
private void updateArrowsShot(ItemStack bow) {
ItemMeta meta = bow.getItemMeta();
if (meta == null) {
toolStats.logger.warning(bow + " does NOT have any meta! Unable to update stats.");
return;
}
// read the current stats from the item
// if they don't exist, then start from 0
Integer arrowsShot = 0;
PersistentDataContainer container = meta.getPersistentDataContainer();
if (container.has(toolStats.arrowsShot, PersistentDataType.INTEGER)) {
arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER);
}
if (arrowsShot == null) {
arrowsShot = 0;
toolStats.logger.warning(arrowsShot + " does not have valid arrows-shot set! Resting to zero. This should NEVER happen.");
}
arrowsShot++;
container.set(toolStats.arrowsShot, PersistentDataType.INTEGER, arrowsShot);
// do we add the lore based on the config?
if (toolStats.config.getBoolean("enabled.arrows-shot")) {
String arrowsShotFormatted = toolStats.numberFormat.formatInt(arrowsShot);
List<String> newLore = toolStats.itemLore.addItemLore(meta, "{arrows}", arrowsShotFormatted, "arrows-shot");
meta.setLore(newLore);
}
bow.setItemMeta(meta);
}
}

View File

@@ -18,9 +18,7 @@
package lol.hyper.toolstats.events;
import lol.hyper.toolstats.ToolStats;
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;
@@ -36,7 +34,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -67,20 +64,21 @@ public class VillagerTrade implements Listener {
if (event.getSlotType() == InventoryType.SlotType.RESULT) {
ItemStack item = event.getCurrentItem();
// 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);
if (!toolStats.itemChecker.isValidItem(item.getType())) {
return;
}
// if the player shift clicks, show the warning
if (event.isShiftClick()) {
String configMessage = toolStats.config.getString("messages.shift-click-warning.trading");
if (configMessage != null) {
player.sendMessage(ChatColor.translateAlternateColorCodes('&', configMessage));
}
}
ItemStack newItem = addLore(item, player);
if (newItem != null) {
// set the new item
inventory.setItem(event.getSlot(), newItem);
}
}
}
}
@@ -107,27 +105,20 @@ public class VillagerTrade implements Listener {
return null;
}
// only make the hash if it's enabled
if (toolStats.config.getBoolean("generate-hash-for-items")) {
String hash = toolStats.hashMaker.makeHash(newItem.getType(), owner.getUniqueId(), timeCreated);
container.set(toolStats.hash, PersistentDataType.STRING, hash);
}
container.set(toolStats.timeCreated, PersistentDataType.LONG, timeCreated);
container.set(toolStats.genericOwner, new UUIDDataType(), owner.getUniqueId());
container.set(toolStats.originType, PersistentDataType.INTEGER, 3);
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();
} else {
lore = new ArrayList<>();
}
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);
if (toolStats.configTools.checkConfig(newItem.getType(), "traded-tag")) {
String formattedDate = toolStats.numberFormat.formatDate(finalDate);
List<String> newLore = toolStats.itemLore.addNewOwner(meta, owner.getName(), formattedDate);
meta.setLore(newLore);
}
newItem.setItemMeta(meta);
return newItem;

View File

@@ -0,0 +1,60 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Material;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class HashMaker {
private final ToolStats toolStats;
public HashMaker(ToolStats toolStats) {
this.toolStats = toolStats;
}
public String makeHash(Material itemType, UUID player, long timestamp) {
String input = itemType.toString() + player.toString() + timestamp;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException exception) {
toolStats.logger.warning("Unable to generate hash for " + player + "!");
toolStats.logger.warning("Generating a random UUID instead.");
exception.printStackTrace();
return java.util.UUID.randomUUID().toString();
}
}
}

View File

@@ -19,15 +19,48 @@ package lol.hyper.toolstats.tools;
import org.bukkit.Material;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
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" };
private final List<Material> validItems = new ArrayList<>();
private final List<Material> armorItems = new ArrayList<>();
private final List<Material> meleeItems = new ArrayList<>();
private final List<Material> mineItems = new ArrayList<>();
/**
* Creates an item checker and saves all valid items we want.
*/
public ItemChecker() {
for (Material material : Material.values()) {
String lowerCase = material.toString().toLowerCase(Locale.ROOT);
if (lowerCase.contains("_pickaxe") || lowerCase.contains("_axe") || lowerCase.contains("_hoe") || lowerCase.contains("_shovel")) {
mineItems.add(material);
}
if (lowerCase.contains("_sword") || lowerCase.contains("_axe")) {
meleeItems.add(material);
}
if (lowerCase.contains("_helmet") || lowerCase.contains("_chestplate") || lowerCase.contains("_leggings") || lowerCase.contains("_boots")) {
armorItems.add(material);
}
}
// hardcode these
mineItems.add(Material.SHEARS);
meleeItems.add(Material.TRIDENT);
validItems.add(Material.BOW);
validItems.add(Material.FISHING_ROD);
validItems.add(Material.CROSSBOW);
// combine the lists
validItems.addAll(armorItems);
validItems.addAll(meleeItems);
validItems.addAll(mineItems);
}
/**
* Check if item is an armor piece.
@@ -35,8 +68,8 @@ public class ItemChecker {
* @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));
public boolean isArmor(Material itemType) {
return armorItems.contains(itemType);
}
/**
@@ -45,8 +78,8 @@ public class ItemChecker {
* @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));
public boolean isValidItem(Material itemType) {
return validItems.contains(itemType);
}
/**
@@ -55,8 +88,8 @@ public class ItemChecker {
* @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));
public boolean isMeleeWeapon(Material itemType) {
return meleeItems.contains(itemType);
}
/**
@@ -65,7 +98,7 @@ public class ItemChecker {
* @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));
public boolean isMineTool(Material itemType) {
return mineItems.contains(itemType);
}
}

View File

@@ -0,0 +1,265 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
public class ItemLore {
private final ToolStats toolStats;
public ItemLore(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Adds/updates lore for an item.
*
* @param placeholder The placeholder from the config. ex: {kills}
* @param placeholderValue The value to replace the placeholder.
* @param configLorePath The path to the config message.
* @return The item's new lore.
*/
public List<String> addItemLore(ItemMeta itemMeta, String placeholder, String placeholderValue, String configLorePath) {
String configLore = toolStats.configTools.getLoreFromConfig(configLorePath, false);
String configLoreRaw = toolStats.configTools.getLoreFromConfig(configLorePath, true);
if (configLore == null || configLoreRaw == null) {
toolStats.logger.warning("There is no lore message for messages." + configLorePath + "!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
List<String> newLore;
// replace the placeholder with the value
// ex: {kills} -> a number
String newLine = configLoreRaw.replace(placeholder, placeholderValue);
if (itemMeta.hasLore()) {
newLore = itemMeta.getLore();
// keep track of line index
// this doesn't mess the lore of existing items
for (int x = 0; x < newLore.size(); x++) {
// check to see if the line matches the config value
// this means we update this line only!
String line = newLore.get(x);
if (line.contains(configLore)) {
newLore.set(x, newLine);
return newLore;
}
}
// if the item has lore, but we didn't find the line
newLore.add(newLine);
} else {
// if the item has no lore, create a new list and add the line
newLore = new ArrayList<>();
newLore.add(newLine);
}
return newLore;
}
/**
* Adds new ownership to an item.
*
* @param itemMeta The item meta.
* @param playerName The new owner of item.
* @param formattedDate The date of the ownership.
* @return The item's new lore.
*/
public List<String> addNewOwner(ItemMeta itemMeta, String playerName, String formattedDate) {
String dateCreatedLore;
String itemOwnerLore;
Integer origin = null;
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
if (container.has(toolStats.originType, PersistentDataType.INTEGER)) {
origin = container.get(toolStats.originType, PersistentDataType.INTEGER);
}
// if the origin is broken, don't try to set the lore
if (origin == null) {
toolStats.logger.info("Unable to determine origin for item " + itemMeta.getAsString());
toolStats.logger.info("This IS a bug, please report this to the GitHub.");
return itemMeta.getLore();
}
// set the lore based on the origin
switch (origin) {
case 2: {
dateCreatedLore = toolStats.configTools.getLoreFromConfig("looted.looted-on", true);
itemOwnerLore = toolStats.configTools.getLoreFromConfig("looted.looted-by", true);
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.looted.looted-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.looted.looted-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
break;
}
case 3: {
dateCreatedLore = toolStats.configTools.getLoreFromConfig("traded.traded-on", true);
itemOwnerLore = toolStats.configTools.getLoreFromConfig("traded.traded-by", true);
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.traded.traded-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.traded.traded-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
break;
}
case 4: {
dateCreatedLore = toolStats.configTools.getLoreFromConfig("looted.found-on", true);
itemOwnerLore = toolStats.configTools.getLoreFromConfig("looted.found-by", true);
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.looted.found-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.looted.found-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
break;
}
case 5: {
dateCreatedLore = toolStats.configTools.getLoreFromConfig("fished.caught-on", true);
itemOwnerLore = toolStats.configTools.getLoreFromConfig("fished.caught-by", true);
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.fished.caught-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.fished.caught-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
break;
}
case 6: {
dateCreatedLore = toolStats.configTools.getLoreFromConfig("spawned-in.spawned-on", true);
itemOwnerLore = toolStats.configTools.getLoreFromConfig("spawned-in.spawned-by", true);
if (dateCreatedLore == null) {
toolStats.logger.warning("messages.spawned-in.spawned-on is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
if (itemOwnerLore == null) {
toolStats.logger.warning("messages.spawned-in.spawned-by is not set in your config!");
toolStats.logger.warning("Unable to update lore for item.");
return itemMeta.getLore();
}
break;
}
default: {
toolStats.logger.warning("Origin " + origin + " was found. Data was modified OR something REALLY broke.");
toolStats.logger.warning(itemMeta.getAsString());
return itemMeta.getLore();
}
}
List<String> newLore;
if (itemMeta.hasLore()) {
newLore = itemMeta.getLore();
} else {
newLore = new ArrayList<>();
}
newLore.add(dateCreatedLore.replace("{date}", formattedDate));
newLore.add(itemOwnerLore.replace("{player}", playerName));
return newLore;
}
/**
* Determine an item's origin based on lore.
*
* @param itemMeta The item's meta.
* @param elytra If they item is an elytra.
* @return The new item meta with the new origin tag. Returns null if origin cannot be determined.
*/
public ItemMeta getOrigin(ItemMeta itemMeta, boolean elytra) {
List<String> lore;
if (!itemMeta.hasLore()) {
return null;
}
lore = itemMeta.getLore();
Integer origin = null;
String createdBy = toolStats.configTools.getLoreFromConfig("created.created-by", false);
String createdOn = toolStats.configTools.getLoreFromConfig("created.created-on", false);
String caughtBy = toolStats.configTools.getLoreFromConfig("fished.caught-by", false);
String lootedBy = toolStats.configTools.getLoreFromConfig("looted.looted-by", false);
String foundBy = toolStats.configTools.getLoreFromConfig("looted.found-by", false);
String tradedBy = toolStats.configTools.getLoreFromConfig("traded.traded-by", false);
for (String line : lore) {
// this is the worst code I have ever written
if (createdBy != null && line.contains(createdBy)) {
origin = 0;
}
if (createdOn != null && line.contains(createdOn)) {
origin = 0;
}
if (caughtBy != null && line.contains(caughtBy)) {
origin = 5;
}
if (lootedBy != null && line.contains(lootedBy)) {
origin = 2;
}
// because the config changed, "found-by" was being used for ALL looted items
// this includes elytras, so we have to check for this mistake
if (foundBy != null && line.contains(foundBy)) {
if (elytra) {
origin = 4;
} else {
origin = 5;
}
}
if (tradedBy != null && line.contains(tradedBy)) {
origin = 3;
}
}
if (origin == null) {
return null;
}
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
container.set(toolStats.originType, PersistentDataType.INTEGER, origin);
return itemMeta;
}
}

View File

@@ -17,40 +17,123 @@
package lol.hyper.toolstats.tools;
import lol.hyper.toolstats.ToolStats;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
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()));
private DecimalFormat DECIMAL_FORMAT;
private DecimalFormat COMMA_FORMAT;
private SimpleDateFormat DATE_FORMAT;
/**
* Utility class to format different numbers
* @param toolStats Plugin instance.
*/
public NumberFormat(ToolStats toolStats) {
String dateFormat = toolStats.config.getString("date-format");
String decimalSeparator = toolStats.config.getString("number-formats.decimal-separator");
String commaSeparator = toolStats.config.getString("number-formats.comma-separator");
String commaFormat = toolStats.config.getString("number-formats.comma-format");
String decimalFormat = toolStats.config.getString("number-formats.decimal-format");
// if these config values are missing, use the default ones
if (dateFormat == null) {
dateFormat = "M/dd/yyyy";
toolStats.logger.warning("date-format is missing! Using default American English format.");
}
if (decimalSeparator == null) {
decimalSeparator = ".";
toolStats.logger.warning("number-formats.decimal-separator is missing! Using default \".\" instead.");
}
if (commaSeparator == null) {
commaSeparator = ",";
toolStats.logger.warning("number-formats.comma-separator is missing! Using default \",\" instead.");
}
if (commaFormat == null) {
commaFormat = "#,###";
toolStats.logger.warning("number-formats.comma-format is missing! Using default #,### instead.");
}
if (decimalFormat == null) {
decimalFormat = "#,###.00";
toolStats.logger.warning("number-formats.comma-separator is missing! Using default #,###.00 instead.");
}
// test the date format
try {
DATE_FORMAT = new SimpleDateFormat(dateFormat, Locale.getDefault());
} catch (NullPointerException | IllegalArgumentException exception) {
toolStats.logger.warning("date-format is NOT a valid format! Using default American English format.");
exception.printStackTrace();
DATE_FORMAT = new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH);
}
// set the separators
DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(Locale.getDefault());
formatSymbols.setDecimalSeparator(decimalSeparator.charAt(0));
formatSymbols.setGroupingSeparator(commaSeparator.charAt(0));
// test the comma format
try {
COMMA_FORMAT = new DecimalFormat(commaFormat, formatSymbols);
} catch (NullPointerException | IllegalArgumentException exception) {
toolStats.logger.warning("number-formats.comma-format is NOT a valid format! Using default #,### instead.");
exception.printStackTrace();
COMMA_FORMAT = new DecimalFormat("#,###", formatSymbols);
}
// test the decimal format
try {
DECIMAL_FORMAT = new DecimalFormat(decimalFormat, formatSymbols);
} catch (NullPointerException | IllegalArgumentException exception) {
toolStats.logger.warning("number-formats.decimal-format is NOT a valid format! Using default #,###.00 instead.");
exception.printStackTrace();
DECIMAL_FORMAT = new DecimalFormat("#,###.00", formatSymbols);
}
}
/**
* 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) {
public String formatInt(int number) {
String finalNumber = COMMA_FORMAT.format(number);
// hardcode French system because Minecraft bad
if (Locale.getDefault() == Locale.FRANCE || Locale.getDefault() == Locale.FRENCH) {
finalNumber = finalNumber.replaceAll("[\\x{202f}\\x{00A0}]", " ");
}
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) {
public String formatDouble(double number) {
String finalNumber = DECIMAL_FORMAT.format(number);
// hardcode French system because Minecraft bad
if (Locale.getDefault() == Locale.FRANCE || Locale.getDefault() == Locale.FRENCH) {
finalNumber = finalNumber.replaceAll("[\\x{202f}\\x{00A0}]", " ");
}
finalNumber = finalNumber.replaceAll("[\\x{202f}\\x{00A0}]", " ");
return finalNumber;
}
/**
* Formats a date into the readable format.
*
* @param date The date to format.
* @return The date into a readable format.
*/
public String formatDate(Date date) {
return DATE_FORMAT.format(date);
}
}

View File

@@ -0,0 +1,162 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools.config;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import java.util.regex.Pattern;
public class ConfigTools {
private final ToolStats toolStats;
private final Pattern COLOR_CODES = Pattern.compile("(?i)&[0-9A-FK-ORX]");
public ConfigTools(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Checks the config to see if we want to show lore on certain items.
*
* @param material The item type to check.
* @param configName The config we are checking under.
* @return If we want to allow lore or not.
*/
public boolean checkConfig(Material material, String configName) {
String itemName = material.toString().toLowerCase();
String itemType = null;
// hardcode these
if (material == Material.BOW || material == Material.CROSSBOW || material == Material.SHEARS || material == Material.TRIDENT || material == Material.FISHING_ROD) {
switch (material) {
case CROSSBOW:
case BOW: {
itemType = "bow";
break;
}
case SHEARS: {
itemType = "shears";
break;
}
case TRIDENT: {
itemType = "trident";
break;
}
case FISHING_ROD: {
itemType = "fishing-rod";
break;
}
}
} else {
itemType = itemName.substring(itemName.indexOf('_') + 1);
}
switch (itemType) {
case "pickaxe": {
return toolStats.config.getBoolean("enabled." + configName + ".pickaxe");
}
case "sword": {
return toolStats.config.getBoolean("enabled." + configName + ".sword");
}
case "shovel": {
return toolStats.config.getBoolean("enabled." + configName + ".shovel");
}
case "axe": {
return toolStats.config.getBoolean("enabled." + configName + ".axe");
}
case "hoe": {
return toolStats.config.getBoolean("enabled." + configName + ".hoe");
}
case "shears": {
return toolStats.config.getBoolean("enabled." + configName + ".shears");
}
case "crossbow":
case "bow": {
return toolStats.config.getBoolean("enabled." + configName + ".bow");
}
case "trident": {
return toolStats.config.getBoolean("enabled." + configName + ".trident");
}
case "fishing-rod": {
return toolStats.config.getBoolean("enabled." + configName + ".fishing-rod");
}
case "helmet":
case "chestplate":
case "leggings":
case "boots": {
return toolStats.config.getBoolean("enabled." + configName + ".armor");
}
}
return false;
}
/**
* 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 = toolStats.config.getString("messages." + configName);
if (lore == null) {
return null;
}
if (raw) {
return ChatColor.translateAlternateColorCodes('&', lore);
} else {
// remove all color codes
// this is used to compare the current lore on the item
// Example: [§7Arrows shot: §8] is on the lore
// this will return [Arrows shot: ] so we can match it
lore = COLOR_CODES.matcher(lore).replaceAll("");
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}", "");
}
if (lore.contains("{crops}")) {
lore = lore.replace("{crops}", "");
}
if (lore.contains("{arrows}")) {
lore = lore.replace("{arrows}", "");
}
}
return lore;
}
}

View File

@@ -0,0 +1,57 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools.config;
import lol.hyper.toolstats.ToolStats;
import lol.hyper.toolstats.tools.config.versions.Version6;
public class ConfigUpdater {
private final ToolStats toolStats;
public ConfigUpdater(ToolStats toolStats) {
this.toolStats = toolStats;
}
public void updateConfig() {
int version = toolStats.config.getInt("config-version");
// this will be a switch in the future
if (version == 5) {
Version6 version6 = new Version6(toolStats);
version6.update();
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools.config.versions;
import lol.hyper.toolstats.ToolStats;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Version6 {
private final ToolStats toolStats;
/**
* Used for updating from version 5 to 6.
*
* @param toolStats ToolStats instance.
*/
public Version6(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Perform the config update.
*/
public void update() {
// save the old config first
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config-5.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config-5.yml!");
throw new RuntimeException(exception);
}
// we make this super verbose so that admins can see what's being added
toolStats.logger.info("Updating config.yml to version 6.");
toolStats.config.set("config-version", 6);
toolStats.logger.info("Adding enabled.spawned-in.pickaxe to config.yml.");
toolStats.config.set("enabled.spawned-in.pickaxe", true);
toolStats.logger.info("Adding enabled.spawned-in.sword to config.yml.");
toolStats.config.set("enabled.spawned-in.sword", true);
toolStats.logger.info("Adding enabled.spawned-in.shovel to config.yml.");
toolStats.config.set("enabled.spawned-in.shovel", true);
toolStats.logger.info("Adding enabled.spawned-in.axe to config.yml.");
toolStats.config.set("enabled.spawned-in.axe", true);
toolStats.logger.info("Adding enabled.spawned-in.hoe to config.yml.");
toolStats.config.set("enabled.spawned-in.hoe", true);
toolStats.logger.info("Adding enabled.spawned-in.fishing-rod to config.yml.");
toolStats.config.set("enabled.spawned-in.fishing-rod", true);
toolStats.logger.info("Adding enabled.spawned-in.shears to config.yml.");
toolStats.config.set("enabled.spawned-in.shears", true);
toolStats.logger.info("Adding enabled.spawned-in.bow to config.yml.");
toolStats.config.set("enabled.spawned-in.bow", true);
toolStats.logger.info("Adding enabled.spawned-in.armor to config.yml.");
toolStats.config.set("enabled.spawned-in.armor", true);
toolStats.logger.info("Adding messages.spawned-in.spawned-by to config.yml.");
toolStats.config.set("messages.spawned-in.spawned-by", "&7Spawned in by: &8{player}");
toolStats.logger.info("Adding messages.spawned-in.spawned-on to config.yml.");
toolStats.config.set("messages.spawned-in.spawned-on", "&7Spawned on: &8{date}");
toolStats.logger.info("Adding generate-hash-for-items to config.yml.");
toolStats.config.set("generate-hash-for-items", true);
toolStats.logger.info("Adding enabled.arrows-shot to config.yml.");
toolStats.config.set("enabled.arrows-shot", true);
toolStats.logger.info("Adding messages.arrows-shot to config.yml.");
toolStats.config.set("messages.arrows-shot", "&7Arrows shot: &8{arrows}");
List<String> hashComments = new ArrayList<>();
hashComments.add("When any tool is created, it will generate a hash for the item.");
hashComments.add("This hash is not on the item lore, only stored in the NBT data.");
hashComments.add("This has no use currently, but can be used for future features for dupe detection.");
toolStats.config.setComments("generate-hash-for-items", hashComments);
toolStats.config.setComments("enabled.spawned-in", Collections.singletonList("Will show \"Spawned in by <player>\""));
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.severe("Unable to save config.yml!");
throw new RuntimeException(exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 6. A copy of version 5 has been saved as config-5.yml");
}
}

View File

@@ -65,11 +65,22 @@ enabled:
axe: true
hoe: true
shears: true
# Will show "Spawned in by <player>"
spawned-in:
pickaxe: true
sword: true
shovel: true
axe: true
hoe: true
shears: true
bow: true
armor: true
fish-caught: true
sheep-sheared: true
armor-damage: true
dropped-by: true
elytra-tag: true
arrows-shot: true
messages:
created:
@@ -80,6 +91,8 @@ messages:
caught-on: "&7Caught on: &8{date}"
fish-caught: "&7Fish caught: &8{fish}"
looted:
looted-by: "&7Looted by: &8{player}"
looted-on: "&7Looted on: &8{date}"
found-by: "&7Found by: &8{player}"
found-on: "&7Found on: &8{date}"
traded:
@@ -88,10 +101,15 @@ messages:
kills:
mob: "&7Mob kills: &8{kills}"
player: "&7Player kills: &8{kills}"
spawned-in:
spawned-by: "&7Spawned in by: &8{player}"
spawned-on: "&7Spawned on: &8{date}"
blocks-mined: "&7Blocks mined: &8{blocks}"
crops-harvested: "&7Crops harvested: &8{crops}"
sheep-sheared: "&7Sheep sheared: &8{sheep}"
dropped-by: "&7Dropped by: &8{name}" # name will be player/mob name
damage-taken: "&7Damage taken: &8{damage}"
arrows-shot: "&7Arrows shot: &8{arrows}"
# 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:
@@ -100,7 +118,20 @@ messages:
# Change the default formatting for dates.
# See: https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format
# Example: "dd/mm/yyyy"
# Example: "dd/MM/yyyy"
date-format: "M/dd/yyyy"
config-version: 3
# Change number formatting.
# You probably do not need to touch this.
number-formats:
comma-separator: ","
decimal-separator: "."
comma-format: "#,###"
decimal-format: "#,###.00"
# When any tool is created, it will generate a hash for the item.
# This hash is not on the item lore, only stored in the NBT data.
# This has no use currently, but can be used for future features for dupe detection.
generate-hash-for-items: true
config-version: 6

View File

@@ -4,6 +4,7 @@ main: lol.hyper.toolstats.ToolStats
api-version: 1.15
author: hyperdefined
description: Track various tool stats!
folia-supported: true
commands:
toolstats:
usage: /toolstats