Compare commits

...
116 Commits
Author SHA1 Message Date
tr7zw 7a914d00e7 Merge branch 'master' into proxyPrototype 2023-09-08 19:12:34 +02:00
tr7zw 7141c56319 Merge pull request #243 from SoSeDiK/fixes
Fix Folia detection and NBT#modify for tile entities
2023-09-04 13:00:42 +02:00
SoSeDiK 992d9717ec Fix NBT#modify fot tile entities 2023-09-04 13:40:13 +03:00
SoSeDiK c5c2652d42 Fix Folia detection 2023-09-04 13:39:51 +03:00
SoSeDiK fb8e2272b3 Some javadoc grammar corrections 2023-09-04 13:33:14 +03:00
tr7zw 120d087edc Add "has" support 2023-08-11 18:52:40 +02:00
tr7zw 12c98d7d94 Make the casing changable 2023-08-11 18:45:50 +02:00
tr7zw 51d06817bc Merge branch 'master' into proxyPrototype 2023-08-11 18:18:17 +02:00
tr7zw 3bb4d7b449 Remove all-contributors since no one ever pr'd to it. Enable spotless for markdown files 2023-08-11 01:07:02 +02:00
tr7zw f562994d18 Update to bStats 3.0.2. Fix #230 2023-08-11 01:02:43 +02:00
tr7zw f86d234c9c Update NBTItemMeta.java 2023-08-10 22:27:58 +02:00
tr7zw cb8e24428b Fix 2023-08-10 22:12:25 +02:00
tr7zw 30bd0bcbaf Add hasCustomModelData 2023-08-10 21:40:52 +02:00
tr7zw bb4d3bb9b0 Add test 2023-08-10 21:19:21 +02:00
tr7zw 523ce8cf60 Start adding an NBTItemMeta wrapper. Add NBT Handlers 2023-08-09 18:01:12 +02:00
tr7zw d3d7a3138c Thanks, merge conflict 2023-08-08 20:11:10 +02:00
tr7zw e71bf88d12 Make a readonly view during meta modification 2023-08-08 20:07:41 +02:00
tr7zw 1ba6fc8706 Fix formatting 2023-08-06 19:40:43 +02:00
tr7zw a341034de0 Add a generic get/set method for NBTHandlers 2023-08-06 19:36:43 +02:00
tr7zw b45b25c255 Add the NBT methods to use Proxies 2023-08-06 19:23:36 +02:00
tr7zw c7eaa51bdb Merge branch 'master' into proxyPrototype 2023-08-06 17:50:12 +02:00
tr7zw 6b1d4e28ed Update NBTModifyItemTest.java 2023-08-06 16:15:51 +02:00
tr7zw 4341cb33ea Merge branch 'master' into proxyPrototype 2023-08-06 16:03:27 +02:00
tr7zw bde0bf7c8d Fix build 2023-08-06 16:03:20 +02:00
tr7zw 55ab9a2918 Merge branch 'master' into proxyPrototype 2023-08-06 15:57:29 +02:00
tr7zw 7f4d134bd0 Add getters with Consumers, so no return is required 2023-08-06 15:55:50 +02:00
tr7zw 6e8b8f3565 Add NBT.readNbt, fix resolveOrDefault 2023-08-06 00:06:47 +02:00
tr7zw a009b521c0 Remove static 2023-08-05 23:48:12 +02:00
tr7zw 54d0db8189 Handler prototype 2023-08-05 23:06:00 +02:00
tr7zw 66ffcf2866 Merge branch 'master' into proxyPrototype 2023-08-05 22:08:15 +02:00
tr7zw d653bc67d8 Fix typo 2023-08-04 21:01:11 +02:00
tr7zw 533765ba74 Add more readonly Validation 2023-08-03 00:49:11 +02:00
tr7zw 0232f720cb Close NBTItems/Entity/BlockEntity after the NBT context, to prevent people leaking objects 2023-08-03 00:29:22 +02:00
tr7zw 205ca93977 Catch attempts trying to smuggle NBT to outside the scope 2023-08-02 23:36:54 +02:00
tr7zw fa62c9825d Fix 2023-08-02 22:42:18 +02:00
tr7zw 5051bc834d Add more sanity checks 2023-08-02 22:33:42 +02:00
tr7zw 10ea4b1767 Don't merge empty tags 2023-08-02 22:24:00 +02:00
tr7zw b801b46bce Set Items nbt tag to null when empty, Add ReadableItemNBT with hasNBTData, add tests 2023-08-02 19:33:09 +02:00
tr7zw fc94dac05b Fix nbt getting lost when modifying meta first, add test, quick return setCompound for finalizer 2023-07-28 17:25:52 +02:00
tr7zw 1ab5f70c88 Merge pull request #242 from U5B/usb-wiki-plugins
add honorable plugin list mentions
2023-07-26 15:35:43 +02:00
usb af6a11d649 add honorable plugin list mentions 2023-07-26 05:53:33 -07:00
tr7zw c90636970a Add and enforce nullable annotations on some interfaces 2023-07-26 00:18:11 +02:00
tr7zw f3b1cc27e7 Add new resolve methods 2023-07-25 23:13:40 +02:00
tr7zw 33eddbaece Further cache during readonly mode 2023-07-25 21:16:45 +02:00
tr7zw 20f9b2f1ce Cache the tag in readOnly mode 2023-07-24 02:59:02 +02:00
tr7zw e8c24c6bc5 Skip making a copy for nms itemstacks during modifications 2023-07-24 02:53:37 +02:00
tr7zw affad0d92c Optimize NBT.modify calls for items 2023-07-24 00:07:08 +02:00
tr7zw ba169438ba Fix NBTFile.saveTo, Boolean -> boolean 2023-07-17 19:53:11 +02:00
tr7zw c2e23af84f Allow sub proxies 2023-07-06 19:40:22 +02:00
tr7zw 9ff2e38d07 Merge pull request #238 from BlackBaroness/patch-1
Fix minor English mistakes in documentation
2023-07-06 00:28:23 +02:00
BlackBaroness 5b12aa70e7 Update Set-a-skull's-skin-using-NBT-API.md 2023-07-06 01:23:14 +03:00
BlackBaroness 5424282fe6 Update Set-a-skull's-skin-using-NBT-API.md 2023-07-06 01:19:52 +03:00
tr7zw 8b76359143 Merge pull request #237 from BlackBaroness/patch-1
Update "set skull skin" documentation
2023-07-06 00:19:19 +02:00
BlackBaroness 2c6d54b32c Update "set skull skin" documentation 2023-07-06 01:15:47 +03:00
tr7zw a341c6a81e Update sonar badges 2023-07-05 00:41:52 +02:00
tr7zw 23b9f9128f Only mention Gson if something went wrong 2023-07-05 00:19:01 +02:00
tr7zw 81c3b75dc9 Add Folia support. Fix #225 2023-07-05 00:16:09 +02:00
tr7zw d3b1d00e15 Missing ! 2023-07-05 00:06:38 +02:00
tr7zw 177d1fe5b7 Update javadoc 2023-07-04 23:34:33 +02:00
tr7zw f1682205d8 Remove functional-annotations, add version checks 2023-07-04 23:19:22 +02:00
tr7zw ced5b15023 Add long[] to the proxy builder 2023-07-04 23:08:27 +02:00
tr7zw f6a150d5be Merge branch 'master' into proxyPrototype 2023-07-04 23:06:00 +02:00
tr7zw 1bfdb93622 Merge pull request #236 from tr7zw/readOnlyOptimization
Read only optimization
2023-07-04 23:05:06 +02:00
tr7zw 09af2df542 Merge pull request #235 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.2.0
Bump license-maven-plugin from 2.1.0 to 2.2.0
2023-07-04 23:04:39 +02:00
tr7zw cd1bdc0436 Add Mojmaped jar support 2023-07-04 22:55:29 +02:00
tr7zw b2ea170f9e Add long array support. Fix #219 2023-07-04 22:02:07 +02:00
dependabot[bot] 769ba78aa1 Bump license-maven-plugin from 2.1.0 to 2.2.0
Bumps [license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.1.0...2.2.0)

---
updated-dependencies:
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 23:46:55 +00:00
tr7zw 0228b68a7c Change NBT.modify calls on (Block-)Entities to be atomic 2023-07-03 23:47:43 +02:00
tr7zw 2a80e8ff63 Add readonly modes for Entity/TileEntity 2023-07-02 20:48:53 +02:00
tr7zw 9bdd79ef7d Add a different error for people accidentally shading the plugin 2023-06-27 14:41:40 +02:00
tr7zw 55893bac8a Add nbtapi version to error messages 2023-06-27 14:27:08 +02:00
tr7zw bfe627658e Make a note that it's 2 different artifacts, add example 2023-06-21 14:10:53 +02:00
tr7zw a929bd2ddc Merge pull request #232 from tr7zw/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-17 01:58:14 +02:00
dependabot[bot] 3a966e0478 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 23:57:38 +00:00
tr7zw 62105ead48 Merge pull request #229 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.1.0
Bump license-maven-plugin from 2.0.1 to 2.1.0
2023-06-15 16:51:56 +02:00
tr7zw cfd04fffa6 Format 2023-06-12 18:21:35 +02:00
tr7zw c8231eb050 Testing readOnly mode for NBT.get(ItemStack) 2023-06-12 18:17:37 +02:00
tr7zw f7bceec594 Start 2.12.0-SNAPSHOT 2023-06-12 18:02:11 +02:00
tr7zw c377927ee6 Tag 2.11.3 2023-06-07 20:20:15 +02:00
tr7zw 0653f47bc1 Add 1.20 support 2023-06-07 20:14:05 +02:00
dependabot[bot] 84e096d8b4 Bump license-maven-plugin from 2.0.1 to 2.1.0
Bumps [license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.0.1...2.1.0)

---
updated-dependencies:
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 23:57:51 +00:00
tr7zw 8bc066872c Allow annotations to customize the interface methods 2023-05-29 21:32:12 +02:00
tr7zw 4491f6780a Spotless 2023-05-28 23:15:56 +02:00
tr7zw af19868289 Add support for default methods 2023-05-28 23:06:24 +02:00
tr7zw 5588fc60b4 Merge pull request #227 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-source-plugin-3.3.0
Bump maven-source-plugin from 3.2.1 to 3.3.0
2023-05-23 10:08:50 +02:00
dependabot[bot] 7c27630c10 Bump maven-source-plugin from 3.2.1 to 3.3.0
Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0.
- [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-22 23:58:27 +00:00
tr7zw 4c538aad7b Innitial test, prototyping proxies 2023-05-20 21:33:36 +02:00
tr7zw 45dd70afad Merge pull request #224 from tom-devv/typo
Fix a small typo in nbt-injector
2023-05-10 21:50:05 +02:00
tom-devv 280ce7a50d Fix typo 2023-05-10 20:40:00 +01:00
tr7zw 175065dac4 Try to use the actual plugin name for issues.
Add a warning for using "de.tr7zw.nbtapi" as relocation target in other plugins.
Fix #206
2023-05-08 00:14:48 +02:00
tr7zw f4bfca8ba8 Disable the update check by default for shaded versions 2023-05-07 20:14:17 +02:00
tr7zw 37378b4ee1 Merge pull request #221 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.0.1
Bump license-maven-plugin from 2.0.0 to 2.0.1
2023-05-06 16:16:23 +02:00
tr7zw 662c0bb92d Merge pull request #223 from SoSeDiK/tagcheck
Allow checking nbt type alongside the key
2023-05-06 16:15:57 +02:00
SoSeDiK c485f8c967 Allow checking nbt type alongside the key 2023-05-06 17:01:23 +03:00
tr7zw 5759b68d89 Merge pull request #222 from SoSeDiK/bool
Add support for boolean in getOrDefault/getOrNull
2023-05-06 15:50:28 +02:00
tr7zw c22cbaaaf0 Start next snapshot 2023-05-06 15:49:40 +02:00
SoSeDiK 4a0e1b5557 Add support for boolean in getOrDefault/getOrNull 2023-05-06 16:48:45 +03:00
dependabot[bot] 1c2e61875e Bump license-maven-plugin from 2.0.0 to 2.0.1
Bumps [license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/license-maven-plugin-2.0.0...2.0.1)

---
updated-dependencies:
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 23:57:37 +00:00
tr7zw 6f25881eda Add 1.19.4 as supported version. 2.11.2 2023-03-16 19:15:55 +01:00
tr7zw bd69504f54 Merge pull request #216 from tr7zw/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-03-09 21:08:23 +01:00
dependabot[bot] 85d7224fdd 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-28 00:00:07 +00:00
tr7zw 8dc51a3d30 Merge pull request #214 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.5.0
Bump maven-javadoc-plugin from 3.4.1 to 3.5.0
2023-02-16 10:03:07 +01:00
dependabot[bot] 8a440889b0 Bump maven-javadoc-plugin from 3.4.1 to 3.5.0
Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.1...maven-javadoc-plugin-3.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-15 23:57:59 +00:00
tr7zw 4a93fdc869 Fix issue with ReadWriteNBT returning ReadableNBT 2023-02-15 22:06:17 +01:00
tr7zw c905de852d Merge pull request #210 from tr7zw/revert-209-dependabot/maven/com.diffplug.spotless-spotless-maven-plugin-2.31.0
Revert "Bump spotless-maven-plugin from 2.30.0 to 2.31.0"
2023-01-31 12:16:37 +01:00
tr7zw fd7da1e3fb Revert "Bump spotless-maven-plugin from 2.30.0 to 2.31.0" 2023-01-31 12:03:09 +01:00
tr7zw 1123e27b10 Merge pull request #209 from tr7zw/dependabot/maven/com.diffplug.spotless-spotless-maven-plugin-2.31.0
Bump spotless-maven-plugin from 2.30.0 to 2.31.0
2023-01-31 11:42:42 +01:00
dependabot[bot] a97ed32097 Bump spotless-maven-plugin from 2.30.0 to 2.31.0
Bumps [spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.30.0 to 2.31.0.
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/lib/2.30.0...lib/2.31.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-26 23:01:52 +00:00
tr7zw 05b4249bb4 Merge pull request #207 from tr7zw/dependabot/maven/com.diffplug.spotless-spotless-maven-plugin-2.30.0
Bump spotless-maven-plugin from 2.29.0 to 2.30.0
2023-01-25 20:33:17 +01:00
tr7zw e399590d1d Bump for 2.11.2-SNAPSHOT 2023-01-25 20:32:30 +01:00
tr7zw 310682ea25 Merge pull request #208 from FREE2WIN2/master
Fix Wiki repository url for maven and gradle.
2023-01-24 11:02:17 +01:00
FREE2WIN2 6b70966926 Wiki: Fix repository url of codemc repo 2023-01-20 23:03:47 +01:00
FREE2WIN2 79275c0c19 Wiki: Fix repository url of codemc repo 2023-01-20 23:03:03 +01:00
dependabot[bot] 2b21ef46bb Bump spotless-maven-plugin from 2.29.0 to 2.30.0
Bumps [spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.29.0 to 2.30.0.
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/lib/2.29.0...lib/2.30.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 23:03:41 +00:00
tr7zw 79e1e62862 Merge pull request #205 from tr7zw/dependabot/maven/com.diffplug.spotless-spotless-maven-plugin-2.29.0
Bump spotless-maven-plugin from 2.28.0 to 2.29.0
2023-01-03 23:51:49 +01:00
dependabot[bot] 852ddf4a22 Bump spotless-maven-plugin from 2.28.0 to 2.29.0
Bumps [spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.28.0 to 2.29.0.
- [Release notes](https://github.com/diffplug/spotless/releases)
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/lib/2.28.0...lib/2.29.0)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 23:03:22 +00:00
82 changed files with 3265 additions and 951 deletions

No files matched your search

-176
View File
@@ -1,176 +0,0 @@
{
"projectName": "Item-NBT-API",
"projectOwner": "tr7zw",
"imageSize": 100,
"contributorsPerLine": 7,
"repoHost": "https://github.com",
"types": {
"discord": {
"symbol": "🤝",
"description": "Discord help"
}
},
"contributors": [
{
"login": "sgdc3",
"name": "Gabriele C.",
"avatar_url": "https://avatars3.githubusercontent.com/u/8779252?s=460&v=4",
"profile": "https://github.com/sgdc3",
"contributions": [
"code",
"infra"
],
"links": {
"infra": "https://codemc.io/"
}
},
{
"login": "SoSeDiK",
"name": "SoSeDiK",
"avatar_url": "https://avatars0.githubusercontent.com/u/19875118?s=460&v=4",
"profile": "https://github.com/SoSeDiK",
"contributions": [
"code",
"userTesting"
],
"links": {
"userTesting": "https://github.com/SoSeDiK?tab=repositories"
}
},
{
"login": "e3ndr",
"name": "Ender",
"avatar_url": "https://avatars1.githubusercontent.com/u/33337309?s=460&v=4",
"profile": "https://github.com/e3ndr",
"contributions": [
"doc",
"discord"
],
"links": {
"doc": "https://github.com/tr7zw/Item-NBT-API/wiki"
}
},
{
"login": "inventivetalentdev",
"name": "Haylee Schäfer",
"avatar_url": "https://avatars3.githubusercontent.com/u/6525296?s=460&v=4",
"profile": "https://github.com/inventivetalentdev",
"contributions": [
"code",
"ideas"
],
"links": {
"code": "https://github.com/tr7zw/Item-NBT-API/tree/master/nbt-injector",
"ideas": "https://github.com/InventivetalentDev/NBTInjector"
}
},
{
"login": "SpiGet",
"name": "SpiGet",
"avatar_url": "https://avatars3.githubusercontent.com/u/15632072?s=200&v=4",
"profile": "https://spiget.org/",
"contributions": [
"plugin"
],
"links": {
"plugin": "https://github.com/tr7zw/Item-NBT-API/blob/master/item-nbt-api/src/main/java/de/tr7zw/changeme/nbtapi/utils/VersionChecker.java"
}
},
{
"login": "bStats",
"name": "bStats",
"avatar_url": "https://avatars2.githubusercontent.com/u/5033001?s=460&v=4",
"profile": "https://github.com/Bastian/bStats",
"contributions": [
"plugin"
],
"links": {
"plugin": "https://bstats.org/plugin/bukkit/ItemNBTAPI/1058"
}
},
{
"login": "MrAxeTv",
"name": "MrAxeTv",
"avatar_url": "https://avatars0.githubusercontent.com/u/17033332?s=460&v=4",
"profile": "https://github.com/MrAxeTv",
"contributions": [
"financial",
"userTesting",
"discord"
],
"links": {
"financial": "https://tr7zw.dev/donate/",
"userTesting": "https://www.spigotmc.org/resources/beastwithdraw-multy-withdraw-plugin.13896/"
}
},
{
"login": "MrDienns",
"name": "Dennis van der Veeke",
"avatar_url": "https://avatars3.githubusercontent.com/u/15938901?s=460&v=4",
"profile": "https://github.com/MrDienns",
"contributions": [
"financial"
],
"links": {
"financial": "https://tr7zw.dev/donate/"
}
},
{
"login": "iGabyTM",
"name": "Gabriel Dumitru",
"avatar_url": "https://avatars2.githubusercontent.com/u/32105478?s=460&v=4",
"profile": "https://github.com/iGabyTM",
"contributions": [
"discord"
]
},
{
"login": "TauCubed",
"name": "TauCubed",
"avatar_url": "https://avatars0.githubusercontent.com/u/35522873?s=460&v=4",
"profile": "https://github.com/TauCubed",
"contributions": [
"discord"
]
},
{
"login": "LoneDev",
"name": "LoneDev",
"avatar_url": "https://www.spigotmc.org/data/avatars/l/88/88296.jpg?1563463602",
"profile": "https://www.spigotmc.org/members/lonedev.88296",
"contributions": [
"code",
"ideas",
"financial"
],
"links": {
"ideas": "https://www.spigotmc.org/threads/nbt-api.70503/page-7#post-3652832",
"financial": "https://tr7zw.dev/donate/"
}
},
{
"login": "GC.",
"name": "GC.",
"avatar_url": "https://www.spigotmc.org/data/avatars/l/25/25090.jpg?1502396477",
"profile": "https://www.spigotmc.org/members/gc.25090",
"contributions": [
"financial"
],
"links": {
"financial": "https://tr7zw.dev/donate/"
}
},
{
"login": "Peaches_MLG",
"name": "Peaches_MLG",
"avatar_url": "https://www.spigotmc.org/data/avatars/l/194/194338.jpg?1561235996",
"profile": "https://www.spigotmc.org/members/peaches_mlg100.194338/",
"contributions": [
"financial"
],
"links": {
"financial": "https://tr7zw.dev/donate/"
}
}
]
}
+6 -34
View File
@@ -1,16 +1,20 @@
# Item-NBT-API
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it Files, other NBT or as String in yaml/json/SQL/Redis.
[Server Owner/Developer Wiki](https://github.com/tr7zw/Item-NBT-API/wiki)
### Build/Maven/Sonar Status
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API)](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=ncloc)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API)](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[![](https://sonarcloud.io/api/project_badges/measure?project=tr7zw_Item-NBT-API&metric=ncloc)](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)[![](https://sonarcloud.io/api/project_badges/measure?project=tr7zw_Item-NBT-API&metric=duplicated_lines_density)](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)[![](https://sonarcloud.io/api/project_badges/measure?project=tr7zw_Item-NBT-API&metric=sqale_rating)](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)
[![Discord](https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white)](https://discordapp.com/invite/yk4caxM)
[![Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dtr7zw%26type%3Dpatrons&style=for-the-badge)](https://www.patreon.com/tr7zw)
### SpigotMC Status
[![Version](https://badges.spiget.org/resources/version/Version-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![Rating](https://badges.spiget.org/resources/rating/Rating-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![Rating](https://badges.spiget.org/resources/downloads/SpigotDownloads-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![bStats Servers](https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic)](https://bstats.org/plugin/bukkit/ItemNBTAPI)[![bStats Players](https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic)](https://bstats.org/plugin/bukkit/ItemNBTAPI)[![All Contributors](https://img.shields.io/badge/all_contributors-13-green.svg?style=plastic)](#contributors-%e2%9c%a8)
## Getting started
Import the API [using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API).
### bStats
@@ -18,37 +22,5 @@ Import the API [using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Ma
[![bStats Stats](https://bstats.org/signatures/bukkit/ItemNBTAPI.svg)](https://bstats.org/plugin/bukkit/ItemNBTAPI)
### Github Stargazers
<img src="https://starchart.cc/tr7zw/Item-NBT-API.svg" alt="stargazers" width="800"/>
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/sgdc3"><img src="https://avatars3.githubusercontent.com/u/8779252?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Gabriele C.</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=sgdc3" title="Code">💻</a> <a href="https://codemc.io/" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/SoSeDiK"><img src="https://avatars0.githubusercontent.com/u/19875118?s=460&v=4" width="100px;" alt=""/><br /><sub><b>SoSeDiK</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=SoSeDiK" title="Code">💻</a> <a href="https://github.com/SoSeDiK?tab=repositories" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/e3ndr"><img src="https://avatars1.githubusercontent.com/u/33337309?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Ender</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/wiki" title="Documentation">📖</a> <a href="#discord-e3ndr" title="Discord help">🤝</a></td>
<td align="center"><a href="https://github.com/inventivetalentdev"><img src="https://avatars3.githubusercontent.com/u/6525296?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Haylee Schäfer</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/tree/master/nbt-injector" title="Code">💻</a> <a href="https://github.com/InventivetalentDev/NBTInjector" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://spiget.org/"><img src="https://avatars3.githubusercontent.com/u/15632072?s=200&v=4" width="100px;" alt=""/><br /><sub><b>SpiGet</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/blob/master/item-nbt-api/src/main/java/de/tr7zw/changeme/nbtapi/utils/VersionChecker.java" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="https://github.com/Bastian/bStats"><img src="https://avatars2.githubusercontent.com/u/5033001?s=460&v=4" width="100px;" alt=""/><br /><sub><b>bStats</b></sub></a><br /><a href="https://bstats.org/plugin/bukkit/ItemNBTAPI/1058" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="https://github.com/MrAxeTv"><img src="https://avatars0.githubusercontent.com/u/17033332?s=460&v=4" width="100px;" alt=""/><br /><sub><b>MrAxeTv</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a> <a href="https://www.spigotmc.org/resources/beastwithdraw-multy-withdraw-plugin.13896/" title="User Testing">📓</a> <a href="#discord-MrAxeTv" title="Discord help">🤝</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/MrDienns"><img src="https://avatars3.githubusercontent.com/u/15938901?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Dennis van der Veeke</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
<td align="center"><a href="https://github.com/iGabyTM"><img src="https://avatars2.githubusercontent.com/u/32105478?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Gabriel Dumitru</b></sub></a><br /><a href="#discord-iGabyTM" title="Discord help">🤝</a></td>
<td align="center"><a href="https://github.com/TauCubed"><img src="https://avatars0.githubusercontent.com/u/35522873?s=460&v=4" width="100px;" alt=""/><br /><sub><b>TauCubed</b></sub></a><br /><a href="#discord-TauCubed" title="Discord help">🤝</a></td>
<td align="center"><a href="https://www.spigotmc.org/members/lonedev.88296"><img src="https://www.spigotmc.org/data/avatars/l/88/88296.jpg?1563463602" width="100px;" alt=""/><br /><sub><b>LoneDev</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=LoneDev" title="Code">💻</a> <a href="https://www.spigotmc.org/threads/nbt-api.70503/page-7#post-3652832" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
<td align="center"><a href="https://www.spigotmc.org/members/gc.25090"><img src="https://www.spigotmc.org/data/avatars/l/25/25090.jpg?1502396477" width="100px;" alt=""/><br /><sub><b>GC.</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
<td align="center"><a href="https://www.spigotmc.org/members/peaches_mlg100.194338/"><img src="https://www.spigotmc.org/data/avatars/l/194/194338.jpg?1561235996" width="100px;" alt=""/><br /><sub><b>Peaches_MLG</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://allcontributors.org) specification.
Contributions of any kind are welcome! Shoot me a message or pullrequest to be added!
+5
View File
@@ -1,16 +1,21 @@
# (Item-)NBT-API
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it Files, other NBT or as String in yaml/json/SQL/Redis.
[Server Owner/Developer Wiki](https://github.com/tr7zw/Item-NBT-API/wiki)
### Build/Maven/Sonar Status
[![Build Status](https://github.com/tr7zw/Item-NBT-API/workflows/Java%20CI/badge.svg)](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=ncloc)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[![](https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)
Discord:[![Discord](https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white)](https://discordapp.com/invite/yk4caxM)
### SpigotMC Status
[![Version](https://badges.spiget.org/resources/version/Version-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![Rating](https://badges.spiget.org/resources/rating/Rating-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![Rating](https://badges.spiget.org/resources/downloads/SpigotDownloads-yellow-7939.svg)](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[![bStats Servers](https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic)](https://bstats.org/plugin/bukkit/ItemNBTAPI)[![bStats Players](https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic)](https://bstats.org/plugin/bukkit/ItemNBTAPI)
## Getting started
Import the API [using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API).
### bStats
[![bStats Stats](https://bstats.org/signatures/bukkit/ItemNBTAPI.svg)](https://bstats.org/plugin/bukkit/ItemNBTAPI)
+2 -17
View File
@@ -5,21 +5,12 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</parent>
<artifactId>item-nbt-api</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>functional-annotations</artifactId>
<version>0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean install javadoc:javadoc</defaultGoal>
<resources>
@@ -32,15 +23,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<version>3.5.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>de.tr7zw.annotations</pattern>
<shadedPattern>de.tr7zw.changeme.nbtapi.utils.annotations</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>LICENSE</resource>
@@ -3,6 +3,8 @@ package de.tr7zw.changeme.nbtapi;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
@@ -11,7 +13,11 @@ import com.mojang.authlib.GameProfile;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteItemNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableItemNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
import de.tr7zw.changeme.nbtapi.wrapper.ProxyBuilder;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
/**
* General utility class for a clean and simple nbt access.
@@ -25,6 +31,18 @@ public class NBT {
// No instances of NBT. Utility class
}
/**
* Get a read only instance of the items NBT. This method is slightly slower
* than calling NBT.get due to having to create a copy of the ItemStack, but
* allows context free access to the data.
*
* @param item
* @return
*/
public static ReadableNBT readNbt(ItemStack item) {
return new NBTItem(item.clone(), false, true, false);
}
/**
* It takes an ItemStack, and a function that takes a ReadableNBT and returns a
* generic type T. It then returns the result of the function applied to a new
@@ -35,8 +53,26 @@ public class NBT {
* T.
* @return The function is being returned.
*/
public static <T> T get(ItemStack item, Function<ReadableNBT, T> getter) {
return getter.apply(new NBTItem(item));
public static <T> T get(ItemStack item, Function<ReadableItemNBT, T> getter) {
NBTItem nbt = new NBTItem(item, false, true, false);
T ret = getter.apply(nbt);
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbt.setClosed();
return ret;
}
/**
* It takes an ItemStack, and a Consumer that takes a ReadableNBT. Applies the
* Consumer on the NBT of the item
*
* @param item The itemstack you want to get the NBT from
*/
public static void get(ItemStack item, Consumer<ReadableItemNBT> getter) {
NBTItem nbt = new NBTItem(item, false, true, false);
getter.accept(nbt);
nbt.setClosed();
}
/**
@@ -48,7 +84,25 @@ public class NBT {
* @return The NBTEntity class is being returned.
*/
public static <T> T get(Entity entity, Function<ReadableNBT, T> getter) {
return getter.apply(new NBTEntity(entity));
NBTEntity nbt = new NBTEntity(entity, true);
T ret = getter.apply(nbt);
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbt.setClosed();
return ret;
}
/**
* It takes an Entity, and a Consumer that takes a ReadableNBT. Applies the
* Consumer on the NBT of the Entity
*
* @param entity The entity to get the NBT from
*/
public static void get(Entity entity, Consumer<ReadableNBT> getter) {
NBTEntity nbt = new NBTEntity(entity, true);
getter.accept(nbt);
nbt.setClosed();
}
/**
@@ -62,7 +116,25 @@ public class NBT {
* @return The return type is the same as the type of the getter function.
*/
public static <T> T get(BlockState blockState, Function<ReadableNBT, T> getter) {
return getter.apply(new NBTTileEntity(blockState));
NBTTileEntity nbt = new NBTTileEntity(blockState, true);
T ret = getter.apply(nbt);
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbt.setClosed();
return ret;
}
/**
* It takes an BlockEntity, and a Consumer that takes a ReadableNBT. Applies the
* Consumer on the NBT of the BlockEntity
*
* @param blockState The block state of the block you want to get the NBT from.
*/
public static void get(BlockState blockState, Consumer<ReadableNBT> getter) {
NBTTileEntity nbt = new NBTTileEntity(blockState, true);
getter.accept(nbt);
nbt.setClosed();
}
/**
@@ -76,11 +148,15 @@ public class NBT {
* @return The return type is T, which is a generic type.
*/
public static <T> T getPersistentData(Entity entity, Function<ReadableNBT, T> getter) {
return getter.apply(new NBTEntity(entity).getPersistentDataContainer());
T ret = getter.apply(new NBTEntity(entity).getPersistentDataContainer());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return ret;
}
/**
* It takes an block entity and a function that takes a ReadableNBT and returns
* It takes a block entity and a function that takes a ReadableNBT and returns
* a generic type T, and returns the result of the function, applied to the
* block entities persistent data container
*
@@ -90,7 +166,11 @@ public class NBT {
* @return The value of the NBT tag.
*/
public static <T> T getPersistentData(BlockState blockState, Function<ReadableNBT, T> getter) {
return getter.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
T ret = getter.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return ret;
}
/**
@@ -102,9 +182,13 @@ public class NBT {
* @return The return value of the function.
*/
public static <T> T modify(ItemStack item, Function<ReadWriteItemNBT, T> function) {
NBTItem nbti = new NBTItem(item, true);
NBTItem nbti = new NBTItem(item, false, false, true);
T val = function.apply(nbti);
nbti.applyNBT(item);
nbti.finalizeChanges();
if (val instanceof ReadableNBT || val instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbti.setClosed();
return val;
}
@@ -116,9 +200,10 @@ public class NBT {
* @param consumer The consumer that will be used to modify the NBT.
*/
public static void modify(ItemStack item, Consumer<ReadWriteItemNBT> consumer) {
NBTItem nbti = new NBTItem(item, true);
NBTItem nbti = new NBTItem(item, false, false, true);
consumer.accept(nbti);
nbti.applyNBT(item);
nbti.finalizeChanges();
nbti.setClosed();
}
/**
@@ -130,7 +215,15 @@ public class NBT {
* @return The return type is the same as the return type of the function.
*/
public static <T> T modify(Entity entity, Function<ReadWriteNBT, T> function) {
return function.apply(new NBTEntity(entity));
NBTEntity nbtEnt = new NBTEntity(entity);
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
T ret = function.apply(cont);
nbtEnt.setCompound(cont.getCompound());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbtEnt.setClosed();
return ret;
}
/**
@@ -141,7 +234,11 @@ public class NBT {
* @param consumer The consumer that will be called with the NBTEntity.
*/
public static void modify(Entity entity, Consumer<ReadWriteNBT> consumer) {
consumer.accept(new NBTEntity(entity));
NBTEntity nbtEnt = new NBTEntity(entity);
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
consumer.accept(cont);
nbtEnt.setCompound(cont.getCompound());
nbtEnt.setClosed();
}
/**
@@ -154,7 +251,11 @@ public class NBT {
* @return The return type is the same as the return type of the function.
*/
public static <T> T modifyPersistentData(Entity entity, Function<ReadWriteNBT, T> function) {
return function.apply(new NBTEntity(entity).getPersistentDataContainer());
T ret = function.apply(new NBTEntity(entity).getPersistentDataContainer());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return ret;
}
/**
@@ -177,7 +278,15 @@ public class NBT {
* @return The return type is the same as the return type of the function.
*/
public static <T> T modify(BlockState blockState, Function<ReadWriteNBT, T> function) {
return function.apply(new NBTTileEntity(blockState));
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
T ret = function.apply(cont);
blockEnt.setCompound(cont.getCompound());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
blockEnt.setClosed();
return ret;
}
/**
@@ -190,7 +299,11 @@ public class NBT {
* takes a ReadWriteNBT and does something with it.
*/
public static void modify(BlockState blockState, Consumer<ReadWriteNBT> consumer) {
consumer.accept(new NBTTileEntity(blockState));
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
consumer.accept(cont);
blockEnt.setCompound(cont.getCompound());
blockEnt.setClosed();
}
/**
@@ -203,7 +316,11 @@ public class NBT {
* @return The return type is the same as the return type of the function.
*/
public static <T> T modifyPersistentData(BlockState blockState, Function<ReadWriteNBT, T> function) {
return function.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
T ret = function.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return ret;
}
/**
@@ -254,6 +371,7 @@ public class NBT {
* @param compound The NBT tag to convert to an ItemStack
* @return An ItemStack
*/
@Nullable
public static ItemStack itemStackFromNBT(ReadableNBT compound) {
return NBTItem.convertNBTtoItem((NBTCompound) compound);
}
@@ -274,6 +392,7 @@ public class NBT {
* @param compound The NBT tag to convert to an ItemStack array.
* @return An array of ItemStacks.
*/
@Nullable
public static ItemStack[] itemStackArrayFromNBT(ReadableNBT compound) {
return NBTItem.convertNBTtoItemArray((NBTCompound) compound);
}
@@ -297,4 +416,138 @@ public class NBT {
return new NBTContainer(nbtString);
}
/**
* Create a read only proxy class for NBT, given an annotated interface.
*
* @param <T>
* @param item
* @param wrapper
* @return
*/
public static <T extends NBTProxy> T readNbt(ItemStack item, Class<T> wrapper) {
return new ProxyBuilder<>(new NBTItem(item, false, true, false), wrapper).readOnly().build();
}
/**
* Create a read only proxy class for NBT, given an annotated interface.
*
* @param <T>
* @param entity
* @param wrapper
* @return
*/
public static <T extends NBTProxy> T readNbt(Entity entity, Class<T> wrapper) {
return new ProxyBuilder<>(new NBTEntity(entity, true), wrapper).readOnly().build();
}
/**
* Create a read only proxy class for NBT, given an annotated interface.
*
* @param <T>
* @param blockState
* @param wrapper
* @return
*/
public static <T extends NBTProxy> T readNbt(BlockState blockState, Class<T> wrapper) {
return new ProxyBuilder<>(new NBTTileEntity(blockState, true), wrapper).readOnly().build();
}
/**
* It takes an ItemStack, applies a function to its NBT wrapped in a proxy, and
* returns the result of the function
*
* @param item The item you want to
* @param wrapper The target Proxy class
* @param function The function that will be applied to the item.
* @return The return value of the function.
*/
public static <T, X extends NBTProxy> T modify(ItemStack item, Class<X> wrapper, Function<X, T> function) {
NBTItem nbti = new NBTItem(item, false, false, true);
T val = function.apply(new ProxyBuilder<>(nbti, wrapper).build());
nbti.finalizeChanges();
if (val instanceof ReadableNBT || val instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
nbti.setClosed();
return val;
}
/**
* It takes an ItemStack, applies a function to its NBT wrapped in a proxy.
*
* @param item The item you want to modify
* @param wrapper The target Proxy class
* @param consumer The consumer that will be used to modify the NBT.
*/
public static <X extends NBTProxy> void modify(ItemStack item, Class<X> wrapper, Consumer<X> consumer) {
NBTItem nbti = new NBTItem(item, false, false, true);
consumer.accept(new ProxyBuilder<>(nbti, wrapper).build());
nbti.finalizeChanges();
nbti.setClosed();
}
/**
* It takes an entity and a function to modify the entity via the proxy
*
* @param entity The entity to modify
* @param wrapper The target Proxy class
* @param consumer The consumer that will be called with the proxy.
*/
public static <X extends NBTProxy> void modify(Entity entity, Class<X> wrapper, Consumer<X> consumer) {
NBTEntity nbtEnt = new NBTEntity(entity);
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
consumer.accept(new ProxyBuilder<>(cont, wrapper).build());
nbtEnt.setCompound(cont.getCompound());
cont.setClosed();
}
/**
* It takes an entity and a function to modify the entity via the proxy
*
* @param entity The entity to modify
* @param wrapper The target Proxy class
* @param function The Function that will be called with the proxy.
* @return The return value of the function.
*/
public static <T, X extends NBTProxy> T modify(Entity entity, Class<X> wrapper, Function<X, T> function) {
NBTEntity nbtEnt = new NBTEntity(entity);
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
T val = function.apply(new ProxyBuilder<>(cont, wrapper).build());
nbtEnt.setCompound(cont.getCompound());
cont.setClosed();
return val;
}
/**
* It takes an block entity and a function to modify the entity via the proxy
*
* @param blockState The blockstate you want to modify
* @param wrapper The target Proxy class
* @param consumer The Consumer that will be called.
*/
public static <X extends NBTProxy> void modify(BlockState blockState, Class<X> wrapper, Consumer<X> consumer) {
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
consumer.accept(new ProxyBuilder<>(cont, wrapper).build());
blockEnt.setCompound(cont);
cont.setClosed();
}
/**
* It takes an block entity and a function to modify the entity via the proxy
*
* @param blockState The blockstate you want to modify
* @param wrapper The target Proxy class
* @param function The function that will be called.
* @return The return value of the function.
*/
public static <T, X extends NBTProxy> T modify(BlockState blockState, Class<X> wrapper, Function<X, T> function) {
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
T val = function.apply(new ProxyBuilder<>(cont, wrapper).build());
blockEnt.setCompound(cont);
cont.setClosed();
return val;
}
}
@@ -12,7 +12,7 @@ import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
* instead!
*
* - The data is really just on the location. If the block gets
* broken/changed/explodes/moved etc, the data is still on that location!
* broken/changed/exploded/moved etc., the data is still on that location!
*
* @author tr7zw
*
@@ -2,10 +2,8 @@ package de.tr7zw.changeme.nbtapi;
import org.bukkit.Chunk;
import de.tr7zw.annotations.FAUtil;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
public class NBTChunk {
@@ -21,9 +19,8 @@ public class NBTChunk {
*
* @return NBTCompound containing the data of the PersistentDataAPI
*/
@AvailableSince(version = MinecraftVersion.MC1_16_R3)
public NBTCompound getPersistentDataContainer() {
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
return new NBTPersistentDataContainer(chunk.getPersistentDataContainer());
}
@@ -4,6 +4,7 @@ import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
@@ -12,9 +13,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.PathUtil;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.Forge1710Mappings;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
@@ -33,10 +37,17 @@ public class NBTCompound implements ReadWriteNBT {
private String compundName;
private NBTCompound parent;
private final boolean readOnly;
private Object readOnlyCache;
protected NBTCompound(NBTCompound owner, String name) {
this(owner, name, false);
}
protected NBTCompound(NBTCompound owner, String name, boolean readOnly) {
this.compundName = name;
this.parent = owner;
this.readOnly = readOnly;
}
protected Lock getReadLock() {
@@ -52,6 +63,52 @@ public class NBTCompound implements ReadWriteNBT {
parent.saveCompound();
}
protected void setResolvedObject(Object object) {
if (isClosed()) {
throw new NbtApiException("Tried using closed NBT data!");
}
if (readOnly) {
this.readOnlyCache = object;
}
}
protected void setClosed() {
if (parent != null) {
parent.setClosed();
}
}
protected boolean isClosed() {
if (parent != null) {
return parent.isClosed();
}
return false;
}
protected boolean isReadOnly() {
return readOnly;
}
protected Object getResolvedObject() {
if (isClosed()) {
throw new NbtApiException("Tried using closed NBT data!");
}
if (readOnlyCache != null) {
return readOnlyCache;
}
Object rootnbttag = getCompound();
if (rootnbttag == null) {
return null;
}
if (!NBTReflectionUtil.valideCompound(this))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = NBTReflectionUtil.gettoCompount(rootnbttag, this);
if (readOnly) {
this.readOnlyCache = workingtag;
}
return workingtag;
}
/**
* @return The Compound name
*/
@@ -84,6 +141,9 @@ public class NBTCompound implements ReadWriteNBT {
* @param comp
*/
public void mergeCompound(NBTCompound comp) {
if (comp == null) {
return;
}
try {
writeLock.lock();
NBTReflectionUtil.mergeOtherNBTCompound(this, comp);
@@ -403,6 +463,45 @@ public class NBTCompound implements ReadWriteNBT {
}
}
/**
* Setter
*
* Requires at least 1.16
*
* @param key
* @param value
*/
@Override
public void setLongArray(String key, long[] value) {
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R1);
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_LONGARRAY, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* Requires at least 1.16
*
* @param key
* @return The stored value or NMS fallback
*/
@Override
public long[] getLongArray(String key) {
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R1);
try {
readLock.lock();
return (long[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_LONGARRAY, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
@@ -600,6 +699,8 @@ public class NBTCompound implements ReadWriteNBT {
}
/**
* Checks whether the provided key exists
*
* @param key String key
* @return True if the key is set
* @deprecated Use {@link #hasTag(String)} instead
@@ -610,6 +711,8 @@ public class NBTCompound implements ReadWriteNBT {
}
/**
* Checks whether the provided key exists
*
* @param key String key
* @return true, if the key is set
*/
@@ -685,7 +788,7 @@ public class NBTCompound implements ReadWriteNBT {
readLock.lock();
if (getType(name) != NBTType.NBTTagCompound)
return null;
NBTCompound next = new NBTCompound(this, name);
NBTCompound next = new NBTCompound(this, name, readOnly);
if (NBTReflectionUtil.valideCompound(next))
return next;
return null;
@@ -856,8 +959,8 @@ public class NBTCompound implements ReadWriteNBT {
* Returns the stored value if exists, or provided value otherwise.
* <p>
* Supported types:
* {@code byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double, byte[], int[]},
* {@link String}, {@link UUID}
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key key
* @param defaultValue default non-null value
@@ -873,22 +976,26 @@ public class NBTCompound implements ReadWriteNBT {
return defaultValue;
Class<?> clazz = defaultValue.getClass();
if (clazz == Byte.class)
if (clazz == Boolean.class || clazz == boolean.class)
return (T) getBoolean(key);
if (clazz == Byte.class || clazz == byte.class)
return (T) getByte(key);
if (clazz == Short.class)
if (clazz == Short.class || clazz == short.class)
return (T) getShort(key);
if (clazz == Integer.class)
if (clazz == Integer.class || clazz == int.class)
return (T) getInteger(key);
if (clazz == Long.class)
if (clazz == Long.class || clazz == long.class)
return (T) getLong(key);
if (clazz == Float.class)
if (clazz == Float.class || clazz == float.class)
return (T) getFloat(key);
if (clazz == Double.class)
if (clazz == Double.class || clazz == double.class)
return (T) getDouble(key);
if (clazz == byte[].class)
return (T) getByteArray(key);
if (clazz == int[].class)
return (T) getIntArray(key);
if (clazz == long[].class)
return (T) getLongArray(key);
if (clazz == String.class)
return (T) getString(key);
if (clazz == UUID.class) {
@@ -908,13 +1015,13 @@ public class NBTCompound implements ReadWriteNBT {
* Returns the stored value if exists, or null.
* <p>
* Supported types:
* {@code Byte, Short, Integer, Long, Float, Double, byte[], int[]},
* {@link String}, {@link UUID}
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key key
* @param type data type
* @param <T> value type
* @return Stored or provided value
* @return Stored value or null
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -924,22 +1031,26 @@ public class NBTCompound implements ReadWriteNBT {
if (!hasTag(key))
return null;
if (type == Byte.class)
if (type == Boolean.class || type == boolean.class)
return (T) getBoolean(key);
if (type == Byte.class || type == byte.class)
return (T) getByte(key);
if (type == Short.class)
if (type == Short.class || type == short.class)
return (T) getShort(key);
if (type == Integer.class)
if (type == Integer.class || type == int.class)
return (T) getInteger(key);
if (type == Long.class)
if (type == Long.class || type == long.class)
return (T) getLong(key);
if (type == Float.class)
if (type == Float.class || type == float.class)
return (T) getFloat(key);
if (type == Double.class)
if (type == Double.class || type == double.class)
return (T) getDouble(key);
if (type == byte[].class)
return (T) getByteArray(key);
if (type == int[].class)
return (T) getIntArray(key);
if (type == long[].class)
return (T) getLongArray(key);
if (type == String.class)
return (T) getString(key);
if (type == UUID.class)
@@ -950,6 +1061,58 @@ public class NBTCompound implements ReadWriteNBT {
throw new NbtApiException("Unsupported type for getOrNull: " + type.getName());
}
@Override
public <T> T resolveOrNull(String key, Class<?> type) {
List<String> keys = PathUtil.splitPath(key);
NBTCompound tag = this;
for (int i = 0; i < keys.size() - 1; i++) {
tag = tag.getCompound(keys.get(i));
if (tag == null) {
return null;
}
}
return tag.getOrNull(keys.get(keys.size() - 1), type);
}
@Override
public <T> T resolveOrDefault(String key, T defaultValue) {
List<String> keys = PathUtil.splitPath(key);
NBTCompound tag = this;
for (int i = 0; i < keys.size() - 1; i++) {
tag = tag.getCompound(keys.get(i));
if (tag == null) {
return defaultValue;
}
}
return tag.getOrDefault(keys.get(keys.size() - 1), defaultValue);
}
@Override
public ReadWriteNBT resolveCompound(String key) {
List<String> keys = PathUtil.splitPath(key);
NBTCompound tag = this;
for (int i = 0; i < keys.size(); i++) {
tag = tag.getCompound(keys.get(i));
if (tag == null) {
return null;
}
}
return tag;
}
@Override
public ReadWriteNBT resolveOrCreateCompound(String key) {
List<String> keys = PathUtil.splitPath(key);
NBTCompound tag = this;
for (int i = 0; i < keys.size(); i++) {
tag = tag.getOrCreateCompound(keys.get(i));
if (tag == null) {
return null;
}
}
return tag;
}
/**
* Set a key to the given Enum value. It gets stored as a String. Passing null
* as value will call removeKey(key) instead.
@@ -1024,6 +1187,16 @@ public class NBTCompound implements ReadWriteNBT {
}
}
@Override
public <T> T get(String key, NBTHandler<T> handler) {
return handler.get(this, key);
}
@Override
public <T> void set(String key, T value, NBTHandler<T> handler) {
handler.set(this, key, value);
}
@Override
public String toString() {
/*
@@ -1069,7 +1242,7 @@ public class NBTCompound implements ReadWriteNBT {
public String asNBTString() {
try {
readLock.lock();
Object comp = NBTReflectionUtil.gettoCompount(getCompound(), this);
Object comp = getResolvedObject();
if (comp == null)
return "{}";
if (MinecraftVersion.isForgePresent() && MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
@@ -1118,8 +1291,10 @@ public class NBTCompound implements ReadWriteNBT {
return compA.getByte(key).equals(compB.getByte(key));
case NBTTagByteArray:
return Arrays.equals(compA.getByteArray(key), compB.getByteArray(key));
case NBTTagCompound:
return compA.getCompound(key).equals(compB.getCompound(key));
case NBTTagCompound: {
NBTCompound tmp = compA.getCompound(key);
return tmp != null && tmp.equals(compB.getCompound(key));
}
case NBTTagDouble:
return compA.getDouble(key).equals(compB.getDouble(key));
case NBTTagEnd:
@@ -1139,6 +1314,8 @@ public class NBTCompound implements ReadWriteNBT {
return compA.getShort(key).equals(compB.getShort(key));
case NBTTagString:
return compA.getString(key).equals(compB.getString(key));
case NBTTagLongArray:
return Arrays.equals(compA.getLongArray(key), compB.getLongArray(key));
}
return false;
}
@@ -35,6 +35,9 @@ public class NBTCompoundList extends NBTList<ReadWriteNBT> implements ReadWriteN
* @return
*/
public NBTCompound addCompound(NBTCompound comp) {
if (getParent().isReadOnly()) {
throw new NbtApiException("Tried setting data in read only mode!");
}
try {
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
@@ -79,6 +82,9 @@ public class NBTCompoundList extends NBTList<ReadWriteNBT> implements ReadWriteN
if (element != null) {
throw new NbtApiException("You need to pass null! ListCompounds from other lists won't work.");
}
if (getParent().isReadOnly()) {
throw new NbtApiException("Tried setting data in read only mode!");
}
try {
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
@@ -16,6 +16,8 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
public class NBTContainer extends NBTCompound {
private Object nbt;
private boolean closed;
private boolean readOnly;
/**
* Creates an empty, standalone NBTCompound
@@ -79,4 +81,23 @@ public class NBTContainer extends NBTCompound {
nbt = tag;
}
@Override
protected void setClosed() {
this.closed = true;
}
@Override
protected boolean isClosed() {
return closed;
}
protected boolean isReadOnly() {
return readOnly;
}
protected NBTContainer setReadOnly(boolean readOnly) {
this.readOnly = true;
return this;
}
}
@@ -3,10 +3,8 @@ package de.tr7zw.changeme.nbtapi;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import de.tr7zw.annotations.FAUtil;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
/**
* NBT class to access vanilla tags from Entities. Entities don't support custom
@@ -19,6 +17,27 @@ import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
public class NBTEntity extends NBTCompound {
private final Entity ent;
private final boolean readonly;
private final Object compound;
private boolean closed = false;
/**
* @param entity Any valid Bukkit Entity
* @param readonly Readonly makes a copy at init, only reading from that copy
*/
protected NBTEntity(Entity entity, boolean readonly) {
super(null, null);
if (entity == null) {
throw new NullPointerException("Entity can't be null!");
}
this.readonly = readonly;
ent = entity;
if (readonly) {
this.compound = getCompound();
} else {
this.compound = null;
}
}
/**
* @param entity Any valid Bukkit Entity
@@ -28,11 +47,32 @@ public class NBTEntity extends NBTCompound {
if (entity == null) {
throw new NullPointerException("Entity can't be null!");
}
this.readonly = false;
this.compound = null;
ent = entity;
}
@Override
protected void setClosed() {
this.closed = true;
}
@Override
protected boolean isClosed() {
return closed;
}
@Override
protected boolean isReadOnly() {
return readonly;
}
@Override
public Object getCompound() {
// this runs before async check, since it's just a copy
if (readonly && compound != null) {
return compound;
}
if (!Bukkit.isPrimaryThread())
throw new NbtApiException("Entity NBT needs to be accessed sync!");
return NBTReflectionUtil.getEntityNBTTagCompound(NBTReflectionUtil.getNMSEntity(ent));
@@ -40,6 +80,9 @@ public class NBTEntity extends NBTCompound {
@Override
protected void setCompound(Object compound) {
if (readonly) {
throw new NbtApiException("Tried setting data in read only mode!");
}
if (!Bukkit.isPrimaryThread())
throw new NbtApiException("Entity NBT needs to be accessed sync!");
NBTReflectionUtil.setEntityNBTTag(compound, NBTReflectionUtil.getNMSEntity(ent));
@@ -51,9 +94,8 @@ public class NBTEntity extends NBTCompound {
*
* @return NBTCompound containing the data of the PersistentDataAPI
*/
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
CheckUtil.assertAvailable(MinecraftVersion.MC1_14_R1);
return new NBTPersistentDataContainer(ent.getPersistentDataContainer());
}
@@ -18,7 +18,7 @@ public class NBTFile extends NBTCompound {
private Object nbt;
/**
* Creates a NBTFile that uses @param file to store it's data. If this file
* Creates a NBTFile that uses @param file to store its data. If this file
* exists, the data will be loaded.
*
* @param file
@@ -99,7 +99,7 @@ public class NBTFile extends NBTCompound {
if (!file.createNewFile())
throw new IOException("Unable to create file at " + file.getAbsolutePath());
}
NBTReflectionUtil.writeNBT(nbt.getCompound(), Files.newOutputStream(file.toPath()));
nbt.writeCompound(Files.newOutputStream(file.toPath()));
}
}
@@ -1,7 +1,10 @@
package de.tr7zw.changeme.nbtapi;
import java.util.Set;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@@ -9,6 +12,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteItemNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
/**
@@ -21,8 +25,11 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
private ItemStack bukkitItem;
private boolean directApply;
private final boolean directApply;
private final boolean finalizer;
private ItemStack originalSrcStack = null;
private Object cachedCompound = null;
private boolean closed = false;
/**
* Constructor for NBTItems. The ItemStack will be cloned!
@@ -33,6 +40,35 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
this(item, false);
}
/**
* @param item
* @param directApply
* @param readOnly When turned on, no copy of the source item is created.
* Modifying the stack in that case is not valid! Also
* overwrites directApply
*/
protected NBTItem(ItemStack item, boolean directApply, boolean readOnly, boolean finalizer) {
super(null, null, readOnly);
if (item == null || item.getType() == Material.AIR || item.getAmount() <= 0) {
throw new NullPointerException("ItemStack can't be null/air/amount of 0! This is not a NBTAPI bug!");
}
this.finalizer = finalizer;
if (finalizer) {
this.bukkitItem = item;
this.originalSrcStack = item;
this.directApply = false;
} else if (readOnly) {
bukkitItem = item;
this.directApply = false;
} else {
this.directApply = directApply;
bukkitItem = item.clone();
if (directApply) {
this.originalSrcStack = item;
}
}
}
/**
* Constructor for NBTItems. The ItemStack will be cloned! If directApply is
* true, all changed will be mapped to the original item. Changes to the NBTItem
@@ -41,11 +77,13 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @param item
* @param directApply
*/
@Deprecated
public NBTItem(ItemStack item, boolean directApply) {
super(null, null);
if (item == null || item.getType() == Material.AIR || item.getAmount() <= 0) {
throw new NullPointerException("ItemStack can't be null/air/amount of 0! This is not a NBTAPI bug!");
}
this.finalizer = false;
this.directApply = directApply;
bukkitItem = item.clone();
if (directApply) {
@@ -55,14 +93,87 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
@Override
public Object getCompound() {
if (closed) {
throw new NbtApiException("Tried using closed NBT data!");
}
if (isReadOnly() && (cachedCompound != null
|| ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(bukkitItem.getClass()))) {
if (cachedCompound == null) {
cachedCompound = NBTReflectionUtil
.getItemRootNBTTagCompound(NBTReflectionUtil.getCraftItemHandle(bukkitItem));
}
return cachedCompound;
}
if (finalizer) {
if (cachedCompound == null) {
updateCachedCompound();
}
return cachedCompound;
}
return NBTReflectionUtil.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
}
private void updateCachedCompound() {
if (finalizer) {
cachedCompound = NBTReflectionUtil
.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
}
}
protected void finalizeChanges() {
if (!finalizer || cachedCompound == null) {
return;
}
// There was data, but not anymore, delete the tag from the itemstack
if (NBTReflectionUtil.getKeys(this).isEmpty()) {
cachedCompound = null;
}
if (ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(originalSrcStack.getClass())) {
Object nmsStack = NBTReflectionUtil.getCraftItemHandle(originalSrcStack);
ReflectionMethod.ITEMSTACK_SET_TAG.run(nmsStack, cachedCompound);
bukkitItem = originalSrcStack;
} else {
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, cachedCompound);
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
originalSrcStack.setItemMeta(bukkitItem.getItemMeta());
}
}
@Override
protected void setClosed() {
this.closed = true;
}
@Override
protected boolean isClosed() {
return closed;
}
@SuppressWarnings("unchecked")
@Override
protected void setCompound(Object compound) {
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, compound);
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
if (isReadOnly()) {
throw new NbtApiException("Tried setting data in read only mode!");
}
if (closed) {
throw new NbtApiException("Tried using closed NBT data!");
}
if (finalizer) {
cachedCompound = compound;
return;
}
if (compound != null && ((Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(compound)).isEmpty()) {
compound = null;
}
if (ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(bukkitItem.getClass())) {
Object nmsStack = NBTReflectionUtil.getCraftItemHandle(bukkitItem);
ReflectionMethod.ITEMSTACK_SET_TAG.run(nmsStack, compound);
} else {
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, compound);
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
}
}
/**
@@ -115,6 +226,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @return true when custom tags are present
*/
public boolean hasCustomNbtData() {
finalizeChanges();
ItemMeta meta = bukkitItem.getItemMeta();
return !NBTReflectionUtil.getUnhandledNBTTags(meta).isEmpty();
}
@@ -123,9 +235,11 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* Remove all custom (non-vanilla) NBT tags from the NBTItem.
*/
public void clearCustomNBT() {
finalizeChanges();
ItemMeta meta = bukkitItem.getItemMeta();
NBTReflectionUtil.getUnhandledNBTTags(meta).clear();
bukkitItem.setItemMeta(meta);
updateCachedCompound();
}
/**
@@ -140,7 +254,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
}
/**
* This may return true even when the NBT is empty.
* Returns true if the item has NBT data.
*
* @return Does the ItemStack have a NBTCompound.
*/
@@ -159,9 +273,11 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @param handler
*/
public void modifyMeta(BiConsumer<ReadableNBT, ItemMeta> handler) {
finalizeChanges();
ItemMeta meta = bukkitItem.getItemMeta();
handler.accept(this, meta);
handler.accept(new NBTContainer(getResolvedObject()).setReadOnly(true), meta);
bukkitItem.setItemMeta(meta);
updateCachedCompound();
if (directApply) {
applyNBT(originalSrcStack);
}
@@ -178,10 +294,12 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @param handler
*/
public <T extends ItemMeta> void modifyMeta(Class<T> type, BiConsumer<ReadableNBT, T> handler) {
finalizeChanges();
@SuppressWarnings("unchecked")
T meta = (T) bukkitItem.getItemMeta();
handler.accept(this, meta);
handler.accept(new NBTContainer(getResolvedObject()).setReadOnly(true), meta);
bukkitItem.setItemMeta(meta);
updateCachedCompound();
if (directApply) {
applyNBT(originalSrcStack);
}
@@ -205,6 +323,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @param comp
* @return ItemStack using the {@link NBTCompound}'s data
*/
@Nullable
public static ItemStack convertNBTtoItem(NBTCompound comp) {
return (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null,
NBTReflectionUtil.convertNBTCompoundtoNMSItem(comp));
@@ -212,7 +331,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
/**
* Helper method that converts {@link ItemStack}[] to {@link NBTContainer} with
* all it data like Material, Damage, Amount and Tags. This is a custom
* all its data like Material, Damage, Amount and Tags. This is a custom
* implementation and won't work with vanilla code(Shulker content etc).
*
* @param items
@@ -237,7 +356,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
/**
* Helper method to do the inverse of "convertItemArraytoNBT". Creates an
* {@link ItemStack}[] using the {@link NBTCompound}. This is a custom
* implementation and won't work with vanilla code(Shulker content etc).
* implementation and won't work with vanilla code (Shulker content, etc.).
*
* Will return null for invalid data. Empty slots in the array are filled with
* AIR Stacks!
@@ -245,6 +364,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
* @param comp
* @return ItemStack[] using the {@link NBTCompound}'s data
*/
@Nullable
public static ItemStack[] convertNBTtoItemArray(NBTCompound comp) {
if (!comp.hasTag("size")) {
return null;
@@ -48,7 +48,20 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
return parent;
}
private void validateClosed() {
if (parent.isClosed()) {
throw new NbtApiException("Tried using closed NBT data!");
}
}
private void validateWritable() {
if (getParent().isReadOnly()) {
throw new NbtApiException("Tried setting data in read only mode!");
}
}
protected void save() {
validateClosed();
parent.set(listName, listObject);
}
@@ -56,6 +69,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean add(T element) {
validateClosed();
validateWritable();
try {
parent.getWriteLock().lock();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
@@ -74,6 +89,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public void add(int index, T element) {
validateClosed();
validateWritable();
try {
parent.getWriteLock().lock();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
@@ -91,6 +108,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public T set(int index, T element) {
validateClosed();
validateWritable();
try {
parent.getWriteLock().lock();
T prev = get(index);
@@ -106,6 +125,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public T remove(int i) {
validateClosed();
validateWritable();
try {
parent.getWriteLock().lock();
T old = get(i);
@@ -121,6 +142,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public int size() {
validateClosed();
try {
parent.getReadLock().lock();
return (int) ReflectionMethod.LIST_SIZE.run(listObject);
@@ -153,6 +175,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean contains(Object o) {
validateClosed();
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
@@ -167,6 +190,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public int indexOf(Object o) {
validateClosed();
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
@@ -181,6 +205,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean addAll(Collection<? extends T> c) {
validateClosed();
try {
parent.getWriteLock().lock();
int size = size();
@@ -195,6 +220,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean addAll(int index, Collection<? extends T> c) {
validateClosed();
try {
parent.getWriteLock().lock();
int size = size();
@@ -209,6 +235,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean containsAll(Collection<?> c) {
validateClosed();
try {
parent.getReadLock().lock();
for (Object ele : c) {
@@ -223,6 +250,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public int lastIndexOf(Object o) {
validateClosed();
try {
parent.getReadLock().lock();
int index = -1;
@@ -238,6 +266,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean removeAll(Collection<?> c) {
validateClosed();
try {
parent.getWriteLock().lock();
int size = size();
@@ -252,6 +281,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean retainAll(Collection<?> c) {
validateClosed();
try {
parent.getWriteLock().lock();
int size = size();
@@ -270,6 +300,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public boolean remove(Object o) {
validateClosed();
try {
parent.getWriteLock().lock();
int size = size();
@@ -375,6 +406,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public Object[] toArray() {
validateClosed();
try {
parent.getReadLock().lock();
Object[] ar = new Object[size()];
@@ -389,6 +421,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@SuppressWarnings("unchecked")
@Override
public <E> E[] toArray(E[] a) {
validateClosed();
try {
parent.getReadLock().lock();
E[] ar = Arrays.copyOf(a, size());
@@ -410,6 +443,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public List<T> subList(int fromIndex, int toIndex) {
validateClosed();
try {
parent.getReadLock().lock();
ArrayList<T> list = new ArrayList<>();
@@ -428,6 +462,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
@Override
public String toString() {
validateClosed();
try {
parent.getReadLock().lock();
return listObject.toString();
@@ -24,13 +24,32 @@ public class NBTListCompound extends NBTCompound {
return owner;
}
@Override
protected boolean isClosed() {
return owner.getParent().isClosed();
}
@Override
protected boolean isReadOnly() {
return owner.getParent().isReadOnly();
}
@Override
public Object getCompound() {
if (isClosed()) {
throw new NbtApiException("Tried using closed NBT data!");
}
return compound;
}
@Override
protected void setCompound(Object compound) {
if (isClosed()) {
throw new NbtApiException("Tried using closed NBT data!");
}
if (isReadOnly()) {
throw new NbtApiException("Tried setting data in read only mode!");
}
this.compound = compound;
}
@@ -11,8 +11,11 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import de.tr7zw.changeme.nbtapi.utils.GsonWrapper;
@@ -32,6 +35,7 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
public class NBTReflectionUtil {
private static Field field_unhandledTags = null;
private static Field field_handle = null;
static {
try {
@@ -39,6 +43,12 @@ public class NBTReflectionUtil {
field_unhandledTags.setAccessible(true);
} catch (NoSuchFieldException e) {
}
try {
field_handle = ClassWrapper.CRAFT_ITEMSTACK.getClazz().getDeclaredField("handle");
field_handle.setAccessible(true);
} catch (NoSuchFieldException e) {
}
}
@@ -96,6 +106,21 @@ public class NBTReflectionUtil {
}
}
/**
* Gets the nms handle ItemStack from a CraftItemStack. Passing Spigot
* ItemStacks will cause an error!
*
* @param item
* @return
*/
public static Object getCraftItemHandle(ItemStack item) {
try {
return field_handle.get(item);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new NbtApiException("Error getting handle from " + item.getClass(), e);
}
}
/**
* Writes a Compound to an OutputStream
*
@@ -104,13 +129,7 @@ public class NBTReflectionUtil {
*/
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
try {
Object nbttag = comp.getCompound();
if (nbttag == null) {
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return;
Object workingtag = gettoCompount(nbttag, comp);
Object workingtag = comp.getResolvedObject();
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
} catch (Exception e) {
throw new NbtApiException("Exception while writing NBT!", e);
@@ -127,7 +146,7 @@ public class NBTReflectionUtil {
public static Object getItemRootNBTTagCompound(Object nmsitem) {
try {
Object answer = ReflectionMethod.NMSITEM_GETTAG.run(nmsitem);
return answer != null ? answer : ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
return answer;
} catch (Exception e) {
throw new NbtApiException("Exception while getting an Itemstack's NBTCompound!", e);
}
@@ -294,7 +313,7 @@ public class NBTReflectionUtil {
if ((boolean) ReflectionMethod.COMPOUND_HAS_KEY.run(compound, name)) {
return ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
} else {
throw new NbtApiException("Tried getting invalide compound '" + name + "' from '" + compound + "'!");
throw new NbtApiException("Tried getting invalid compound '" + name + "' from '" + compound + "'!");
}
} catch (Exception e) {
throw new NbtApiException("Exception while getting NBT subcompounds!", e);
@@ -335,12 +354,14 @@ public class NBTReflectionUtil {
* @param comp
* @return true if this is a valide Compound, else false
*/
public static Boolean valideCompound(NBTCompound comp) {
public static boolean valideCompound(NBTCompound comp) {
Object root = comp.getCompound();
if (root == null) {
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
return (gettoCompount(root, comp)) != null;
Object tmp = gettoCompount(root, comp);
comp.setResolvedObject(tmp);
return tmp != null;
}
protected static Object gettoCompount(Object nbttag, NBTCompound comp) {
@@ -366,6 +387,10 @@ public class NBTReflectionUtil {
* @param nbtcompoundSrc Data to merge
*/
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompoundSrc) {
Object workingtagSrc = nbtcompoundSrc.getResolvedObject();
if (workingtagSrc == null) {
return;
}
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
@@ -373,13 +398,6 @@ public class NBTReflectionUtil {
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
Object rootnbttagSrc = nbtcompoundSrc.getCompound();
if (rootnbttagSrc == null) {
rootnbttagSrc = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(nbtcompoundSrc))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtagSrc = gettoCompount(rootnbttagSrc, nbtcompoundSrc);
try {
ReflectionMethod.COMPOUND_MERGE.run(workingtag, workingtagSrc);
comp.setCompound(rootnbttag);
@@ -396,13 +414,7 @@ public class NBTReflectionUtil {
* @return Content saved under this key
*/
public static String getContent(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
Object workingtag = comp.getResolvedObject();
try {
return ReflectionMethod.COMPOUND_GET.run(workingtag, key).toString();
} catch (Exception e) {
@@ -445,17 +457,14 @@ public class NBTReflectionUtil {
* @param key
* @param type
* @param clazz
* @return The list at that key. Null if it's an invalide type
* @return The list at that key. Null if it's an invalid type
*/
@SuppressWarnings("unchecked")
public static <T> NBTList<T> getList(NBTCompound comp, String key, NBTType type, Class<T> clazz) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
Object workingtag = comp.getResolvedObject();
if (workingtag == null) {
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
try {
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
if (clazz == String.class) {
@@ -483,20 +492,23 @@ public class NBTReflectionUtil {
}
public static NBTType getListType(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
Object workingtag = comp.getResolvedObject();
if (workingtag == null) {
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
try {
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
String fieldname = "type";
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
fieldname = "w";
}
Field f = nbt.getClass().getDeclaredField(fieldname);
Field f;
try {
f = nbt.getClass().getDeclaredField(fieldname);
} catch (NoSuchFieldException ignore) {
// fallback try mojang mapped/legacy
f = nbt.getClass().getDeclaredField("type");
}
f.setAccessible(true);
return NBTType.valueOf(f.getByte(nbt));
} catch (Exception ex) {
@@ -505,16 +517,9 @@ public class NBTReflectionUtil {
}
public static Object getEntry(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
Object workingtag = comp.getResolvedObject();
try {
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
return nbt;
return ReflectionMethod.COMPOUND_GET.run(workingtag, key);
} catch (Exception ex) {
throw new NbtApiException("Exception while getting an Entry!", ex);
}
@@ -582,13 +587,7 @@ public class NBTReflectionUtil {
*/
@SuppressWarnings("unchecked")
public static Set<String> getKeys(NBTCompound comp) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
Object workingtag = comp.getResolvedObject();
return (Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(workingtag);
}
@@ -616,6 +615,8 @@ public class NBTReflectionUtil {
comp.setCompound(rootnbttag);
}
private final static NBTContainer dummyNBT = new NBTContainer();
/**
* Gets data from the Compound
*
@@ -625,13 +626,11 @@ public class NBTReflectionUtil {
* @return The value or default fallback from NMS
*/
public static Object getData(NBTCompound comp, ReflectionMethod type, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
return null;
Object workingtag = comp.getResolvedObject();
// return default behavior data as if there was a compound
if (workingtag == null) {
workingtag = dummyNBT.getCompound();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
return type.run(workingtag, key);
}
@@ -3,10 +3,8 @@ package de.tr7zw.changeme.nbtapi;
import org.bukkit.Bukkit;
import org.bukkit.block.BlockState;
import de.tr7zw.annotations.FAUtil;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
/**
* NBT class to access vanilla tags from TileEntities. TileEntities don't
@@ -20,6 +18,27 @@ import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
public class NBTTileEntity extends NBTCompound {
private final BlockState tile;
private final boolean readonly;
private final Object compound;
private boolean closed = false;
/**
* @param tile BlockState from any TileEntity
* @param readonly Readonly makes a copy at init, only reading from that copy
*/
public NBTTileEntity(BlockState tile, boolean readonly) {
super(null, null);
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
throw new NullPointerException("Tile can't be null/not placed!");
}
this.tile = tile;
this.readonly = readonly;
if (readonly) {
this.compound = getCompound();
} else {
this.compound = null;
}
}
/**
* @param tile BlockState from any TileEntity
@@ -29,11 +48,32 @@ public class NBTTileEntity extends NBTCompound {
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
throw new NullPointerException("Tile can't be null/not placed!");
}
this.readonly = false;
this.compound = null;
this.tile = tile;
}
@Override
protected void setClosed() {
this.closed = true;
}
@Override
protected boolean isClosed() {
return closed;
}
@Override
protected boolean isReadOnly() {
return readonly;
}
@Override
public Object getCompound() {
// this runs before async check, since it's just a copy
if (readonly && compound != null) {
return compound;
}
if (!Bukkit.isPrimaryThread())
throw new NbtApiException("BlockEntity NBT needs to be accessed sync!");
return NBTReflectionUtil.getTileEntityNBTTagCompound(tile);
@@ -41,6 +81,9 @@ public class NBTTileEntity extends NBTCompound {
@Override
protected void setCompound(Object compound) {
if (readonly) {
throw new NbtApiException("Tried setting data in read only mode!");
}
if (!Bukkit.isPrimaryThread())
throw new NbtApiException("BlockEntity NBT needs to be accessed sync!");
NBTReflectionUtil.setTileEntityNBTTagCompound(tile, compound);
@@ -52,9 +95,8 @@ public class NBTTileEntity extends NBTCompound {
*
* @return NBTCompound containing the data of the PersistentDataAPI
*/
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
CheckUtil.assertAvailable(MinecraftVersion.MC1_14_R1);
if (hasTag("PublicBukkitValues")) {
return getCompound("PublicBukkitValues");
} else {
@@ -9,7 +9,7 @@ package de.tr7zw.changeme.nbtapi;
@SuppressWarnings("javadoc")
public enum NBTType {
NBTTagEnd(0), NBTTagByte(1), NBTTagShort(2), NBTTagInt(3), NBTTagLong(4), NBTTagFloat(5), NBTTagDouble(6),
NBTTagByteArray(7), NBTTagIntArray(11), NBTTagString(8), NBTTagList(9), NBTTagCompound(10);
NBTTagByteArray(7), NBTTagString(8), NBTTagList(9), NBTTagCompound(10), NBTTagIntArray(11), NBTTagLongArray(12);
NBTType(int i) {
id = i;
@@ -65,12 +65,12 @@ public class NbtApiException extends RuntimeException {
if (message == null)
return null;
if (confirmedBroken == null) {
return "[?]" + message;
return "[?][" + MinecraftVersion.getNBTAPIVersion() + "]" + message;
} else if (confirmedBroken == false) {
return "[Selfchecked]" + message;
return "[Selfchecked][" + MinecraftVersion.getNBTAPIVersion() + "]" + message;
}
return "[" + MinecraftVersion.getVersion()
return "[" + MinecraftVersion.getVersion() + "][" + MinecraftVersion.getNBTAPIVersion()
+ "]There were errors detected during the server self-check! Please, make sure that NBT-API is up to date. Error message: "
+ message;
}
@@ -0,0 +1,89 @@
package de.tr7zw.changeme.nbtapi.handler;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
public class NBTHandlers {
public static final NBTHandler<ItemStack> ITEM_STACK = new NBTHandler<ItemStack>() {
@Override
public boolean fuzzyMatch(Object obj) {
return obj instanceof ItemStack;
}
@Override
public void set(ReadWriteNBT nbt, String key, ItemStack value) {
nbt.removeKey(key);
ReadWriteNBT tag = nbt.getOrCreateCompound(key);
tag.mergeCompound(NBT.itemStackToNBT(value));
}
@Override
public ItemStack get(ReadableNBT nbt, String key) {
ReadableNBT tag = nbt.getCompound(key);
if (tag != null) {
return NBT.itemStackFromNBT(tag);
}
return null;
}
};
public static final NBTHandler<ReadableNBT> STORE_READABLE_TAG = new NBTHandler<ReadableNBT>() {
@Override
public boolean fuzzyMatch(Object obj) {
return obj instanceof ReadableNBT;
}
@Override
public void set(ReadWriteNBT nbt, String key, ReadableNBT value) {
nbt.removeKey(key);
nbt.getOrCreateCompound(key).mergeCompound(value);
}
@Override
public ReadableNBT get(ReadableNBT nbt, String key) {
ReadableNBT tag = nbt.getCompound(key);
if (tag != null) {
ReadWriteNBT value = NBT.createNBTObject();
value.mergeCompound(tag);
return value;
}
return null;
}
};
public static final NBTHandler<ReadWriteNBT> STORE_READWRITE_TAG = new NBTHandler<ReadWriteNBT>() {
@Override
public boolean fuzzyMatch(Object obj) {
return obj instanceof ReadWriteNBT;
}
@Override
public void set(ReadWriteNBT nbt, String key, ReadWriteNBT value) {
nbt.removeKey(key);
nbt.getOrCreateCompound(key).mergeCompound(value);
}
@Override
public ReadWriteNBT get(ReadableNBT nbt, String key) {
ReadableNBT tag = nbt.getCompound(key);
if (tag != null) {
ReadWriteNBT value = NBT.createNBTObject();
value.mergeCompound(tag);
return value;
}
return null;
}
};
}
@@ -0,0 +1,15 @@
package de.tr7zw.changeme.nbtapi.iface;
import javax.annotation.Nonnull;
public interface NBTHandler<T> {
public default boolean fuzzyMatch(Object obj) {
return false;
}
public void set(@Nonnull ReadWriteNBT nbt, @Nonnull String key, @Nonnull T value);
public T get(@Nonnull ReadableNBT nbt, @Nonnull String key);
}
@@ -7,7 +7,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import de.tr7zw.changeme.nbtapi.NBTItem;
public interface ReadWriteItemNBT extends ReadWriteNBT {
public interface ReadWriteItemNBT extends ReadWriteNBT, ReadableItemNBT {
/**
* True, if the item has any tags now known for this item type.
@@ -2,6 +2,8 @@ package de.tr7zw.changeme.nbtapi.iface;
import java.util.UUID;
import javax.annotation.Nullable;
import org.bukkit.inventory.ItemStack;
public interface ReadWriteNBT extends ReadableNBT {
@@ -86,6 +88,16 @@ public interface ReadWriteNBT extends ReadableNBT {
*/
void setIntArray(String key, int[] value);
/**
* Setter
*
* Requires 1.16+
*
* @param key
* @param value
*/
void setLongArray(String key, long[] value);
/**
* Setter
*
@@ -129,7 +141,34 @@ public interface ReadWriteNBT extends ReadableNBT {
* @param name
* @return
*/
ReadableNBT getOrCreateCompound(String name);
ReadWriteNBT getOrCreateCompound(String name);
/**
* @param name
* @return The Compound instance or null
*/
@Nullable
ReadWriteNBT getCompound(String name);
/**
* Returns the resolved and creates compounds as required.
* <p>
*
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
* be escaped with a backslash.
* @return The resolved compound.
*/
ReadWriteNBT resolveOrCreateCompound(String key);
/**
* Set an Object to a key via the provided handler.
*
* @param <T>
* @param key
* @param value
* @param handler
*/
<T> void set(String key, T value, NBTHandler<T> handler);
/**
* Set a key to the given Enum value. It gets stored as a String. Passing null
@@ -0,0 +1,12 @@
package de.tr7zw.changeme.nbtapi.iface;
public interface ReadableItemNBT extends ReadableNBT {
/**
* Returns true if the item has NBT data.
*
* @return Does the ItemStack have a NBTCompound.
*/
public boolean hasNBTData();
}
@@ -4,6 +4,8 @@ import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBTType;
@@ -84,6 +86,7 @@ public interface ReadableNBT {
* @param key The key to use to retrieve the value.
* @return A byte array.
*/
@Nullable
byte[] getByteArray(String key);
/**
@@ -93,8 +96,21 @@ public interface ReadableNBT {
* @param key The key of the value you want to get.
* @return An array of integers.
*/
@Nullable
int[] getIntArray(String key);
/**
* Returns the value associated with the given key as an array of longs, or null
* if the key does not exist.
*
* Requires 1.16+
*
* @param key The key of the value you want to get.
* @return An array of integers.
*/
@Nullable
long[] getLongArray(String key);
/**
* Returns the value associated with the given key, or false if the key is not
* found.
@@ -111,6 +127,7 @@ public interface ReadableNBT {
* @param key The key of the itemstack you want to get.
* @return An ItemStack
*/
@Nullable
ItemStack getItemStack(String key);
/**
@@ -121,6 +138,7 @@ public interface ReadableNBT {
* @return The stored {@link ItemStack} array, or null if stored data wasn't
* found
*/
@Nullable
ItemStack[] getItemStackArray(String key);
/**
@@ -129,14 +147,28 @@ public interface ReadableNBT {
* @param key The key to get the value from
* @return A UUID object.
*/
@Nullable
UUID getUUID(String key);
/**
* Checks whether the provided key exists
*
* @param key String key
* @return true, if the key is set
*/
boolean hasTag(String key);
/**
* Checks whether the provided key exists and has the specified type
*
* @param key String key
* @param type nbt tag type
* @return whether the key is set and has the specified type
*/
default boolean hasTag(String key, NBTType type) {
return hasTag(key) && getType(key) == type;
}
/**
* @return Set of all stored Keys
*/
@@ -146,6 +178,7 @@ public interface ReadableNBT {
* @param name
* @return The Compound instance or null
*/
@Nullable
ReadableNBT getCompound(String name);
/**
@@ -196,6 +229,7 @@ public interface ReadableNBT {
* @param name
* @return
*/
@Nullable
NBTType getListType(String name);
/**
@@ -208,8 +242,8 @@ public interface ReadableNBT {
* Returns the stored value if exists, or provided value otherwise.
* <p>
* Supported types:
* {@code byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double, byte[], int[]},
* {@link String}, {@link UUID}
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key key
* @param defaultValue default non-null value
@@ -222,16 +256,69 @@ public interface ReadableNBT {
* Returns the stored value if exists, or null.
* <p>
* Supported types:
* {@code Byte, Short, Integer, Long, Float, Double, byte[], int[]},
* {@link String}, {@link UUID}
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key key
* @param type data type
* @param <T> value type
* @return Stored or provided value
*/
@Nullable
<T> T getOrNull(String key, Class<?> type);
/**
* Returns the resolved value if exists, or null.
* <p>
* Supported types:
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
* be escaped with a backslash.
* @param type data type
* @param <T> value type
* @return resolved or provided value
*/
@Nullable
<T> T resolveOrNull(String key, Class<?> type);
/**
* Returns the resolved value if exists, or provided value otherwise.
* <p>
* Supported types:
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
* {@link String}, {@link UUID}, and {@link Enum}
*
* @param key Path key, seperated by '.'. For example: "foo.bar.baz".
* Dots can be escaped with a backslash.
* @param defaultValue default non-null value
* @param <T> value type
* @return resolved or provided value
*/
<T> T resolveOrDefault(String key, T defaultValue);
/**
* Returns the resolved Compound if exists, or null.
* <p>
*
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
* be escaped with a backslash.
* @return The resolved value if exists, or null.
*/
@Nullable
ReadableNBT resolveCompound(String key);
/**
* Get the object at the specified key via the handler.
*
* @param <T>
* @param key
* @param handler
* @return
*/
<T> T get(String key, NBTHandler<T> handler);
/**
* Get an Enum value that has been set via setEnum or setString(key,
* value.name()). Passing null/invalid keys will return null.
@@ -241,6 +328,7 @@ public interface ReadableNBT {
* @param type
* @return
*/
@Nullable
<E extends Enum<E>> E getEnum(String key, Class<E> type);
/**
@@ -1,412 +0,0 @@
package de.tr7zw.changeme.nbtapi.utils;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import static de.tr7zw.changeme.nbtapi.utils.MinecraftVersion.getLogger;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*
* This class is modified by tr7zw to work when the api is shaded into other
* peoples plugins.
*/
public class ApiMetricsLite {
private static final String PLUGINNAME = "ItemNBTAPI"; // DO NOT CHANGE THE NAME! else it won't link the data on
// bStats
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The version of the NBT-Api bStats
public static final int NBT_BSTATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private Plugin plugin;
/**
* Class constructor.
*
*/
public ApiMetricsLite() {
// The register method just uses any enabled plugin it can find to register.
// This *shouldn't* cause any problems, since the plugin isn't used any other
// way.
// Register our service
for (Plugin plug : Bukkit.getPluginManager().getPlugins()) {
plugin = plug;
if (plugin != null)
break;
}
if (plugin == null) {
return;// Didn't find any plugin that could work
}
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n"
+ "To honor their work, you should not disable it.\n"
+ "This has nearly no effect on the server performance!\n"
+ "Check out https://bStats.org/ to learn more :)")
.copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) {
}
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
enabled = config.getBoolean("enabled", true);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("NBT_BSTATS_VERSION"); // Create only one instance of the nbt-api bstats.
return;
} catch (NoSuchFieldException ignored) {
}
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) {
}
}
boolean fFound = found;
// Register our service
if (Bukkit.isPrimaryThread()) {
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
if (!fFound) {
getLogger()
.info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
// We are the first!
startSubmitting();
}
} else {
Bukkit.getScheduler().runTask(plugin, () -> {
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
if (!fFound) {
getLogger().info(
"[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
// We are the first!
startSubmitting();
}
});
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to
// use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the
// stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000l * 60l * 5l, 1000l * 60l * 30l);
// Submit the data every 30 minutes, first time after 5 minutes to give other
// plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be
// blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data. This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
data.addProperty("pluginName", PLUGINNAME); // Append the name of the plugin
data.addProperty("pluginVersion", MinecraftVersion.VERSION); // Append the version of the plugin
data.add("customCharts", new JsonArray());
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError:
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
getLogger().log(Level.WARNING,
"[NBTAPI][BSTATS] Encountered exception while posting request!", e);
// Not using the plugins logger since the plugin isn't the plugin containing the
// NBT-Api most of the time
// this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception
// ", e);
}
continue; // continue looping since we cannot do any other thing.
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException
| InvocationTargetException ignored) {
}
}
} catch (NoSuchFieldException ignored) {
}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
getLogger().log(Level.WARNING,
"[NBTAPI][BSTATS] Could not submit plugin stats of " + plugin.getName(), e);
// Not using the plugins logger since the plugin isn't the plugin containing the
// NBT-Api most of the time
// plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " +
// plugin.getName(), e);
}
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
MinecraftVersion.getLogger().info("[NBTAPI][BSTATS] Sending data to bStats: " + data.toString());
// Not using the plugins logger since the plugin isn't the plugin containing the
// NBT-Api most of the time
// plugin.getLogger().info("Sending data to bStats: " + data.toString());
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
bufferedReader.close();
if (logResponseStatusText) {
getLogger().info("[NBTAPI][BSTATS] Sent data to bStats and received response: " + builder.toString());
// Not using the plugins logger since the plugin isn't the plugin containing the
// NBT-Api most of the time
// plugin.getLogger().info("Sent data to bStats and received response: " +
// builder.toString());
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return new byte[0];
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
}
@@ -0,0 +1,17 @@
package de.tr7zw.changeme.nbtapi.utils;
import de.tr7zw.changeme.nbtapi.NbtApiException;
public class CheckUtil {
private CheckUtil() {
// util
}
public static void assertAvailable(MinecraftVersion version) {
if (!MinecraftVersion.isAtLeastVersion(version))
throw new NbtApiException(
"This Method is only avaliable for the version " + version.name() + " and above!");
}
}
@@ -0,0 +1,881 @@
/*
* This Metrics class was auto-generated and can be copied into your project if you are
* not using a build tool like Gradle or Maven for dependency management.
*
* IMPORTANT: You are not allowed to modify this class, except changing the package.
*
* Disallowed modifications include but are not limited to:
* - Remove the option for users to opt-out
* - Change the frequency for data submission
* - Obfuscate the code (every obfuscator should allow you to make an exception for specific files)
* - Reformat the code (if you use a linter, add an exception)
*
* Violations will result in a ban of your plugin and account from bStats.
*/
package de.tr7zw.changeme.nbtapi.utils;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HttpsURLConnection;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
public class Metrics {
private final Plugin plugin;
private final MetricsBase metricsBase;
/**
* Creates a new Metrics instance.
*
* @param plugin Your plugin instance.
* @param serviceId The id of the service. It can be found at <a
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(JavaPlugin plugin, int serviceId) {
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
if (!config.isSet("serverUuid")) {
config.addDefault("enabled", true);
config.addDefault("serverUuid", UUID.randomUUID().toString());
config.addDefault("logFailedRequests", false);
config.addDefault("logSentData", false);
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config
.options()
.header(
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
+ "anonymous.")
.copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) {
}
}
// Load the data
boolean enabled = config.getBoolean("enabled", true);
String serverUUID = config.getString("serverUuid");
boolean logErrors = config.getBoolean("logFailedRequests", false);
boolean logSentData = config.getBoolean("logSentData", false);
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
metricsBase =
new MetricsBase(
"bukkit",
serverUUID,
serviceId,
enabled,
this::appendPlatformData,
this::appendServiceData,
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
plugin::isEnabled,
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
(message) -> this.plugin.getLogger().log(Level.INFO, message),
logErrors,
logSentData,
logResponseStatusText);
}
/** Shuts down the underlying scheduler service. */
public void shutdown() {
metricsBase.shutdown();
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
metricsBase.addCustomChart(chart);
}
private void appendPlatformData(JsonObjectBuilder builder) {
builder.appendField("playerAmount", getPlayerAmount());
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
builder.appendField("bukkitVersion", Bukkit.getVersion());
builder.appendField("bukkitName", Bukkit.getName());
builder.appendField("javaVersion", System.getProperty("java.version"));
builder.appendField("osName", System.getProperty("os.name"));
builder.appendField("osArch", System.getProperty("os.arch"));
builder.appendField("osVersion", System.getProperty("os.version"));
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
}
private void appendServiceData(JsonObjectBuilder builder) {
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
}
private int getPlayerAmount() {
try {
// Around MC 1.8 the return type was changed from an array to a collection,
// This fixes java.lang.NoSuchMethodError:
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
return onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
// Just use the new method if the reflection failed
return Bukkit.getOnlinePlayers().size();
}
}
public static class MetricsBase {
/** The version of the Metrics class. */
public static final String METRICS_VERSION = "3.0.2";
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
private final ScheduledExecutorService scheduler;
private final String platform;
private final String serverUuid;
private final int serviceId;
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
private final Consumer<Runnable> submitTaskConsumer;
private final Supplier<Boolean> checkServiceEnabledSupplier;
private final BiConsumer<String, Throwable> errorLogger;
private final Consumer<String> infoLogger;
private final boolean logErrors;
private final boolean logSentData;
private final boolean logResponseStatusText;
private final Set<CustomChart> customCharts = new HashSet<>();
private final boolean enabled;
/**
* Creates a new MetricsBase class instance.
*
* @param platform The platform of the service.
* @param serviceId The id of the service.
* @param serverUuid The server uuid.
* @param enabled Whether or not data sending is enabled.
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
* appends all platform-specific data.
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
* appends all service-specific data.
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
* used to delegate the data collection to a another thread to prevent errors caused by
* concurrency. Can be {@code null}.
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
* @param errorLogger A consumer that accepts log message and an error.
* @param infoLogger A consumer that accepts info log messages.
* @param logErrors Whether or not errors should be logged.
* @param logSentData Whether or not the sent data should be logged.
* @param logResponseStatusText Whether or not the response status text should be logged.
*/
public MetricsBase(
String platform,
String serverUuid,
int serviceId,
boolean enabled,
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
Consumer<Runnable> submitTaskConsumer,
Supplier<Boolean> checkServiceEnabledSupplier,
BiConsumer<String, Throwable> errorLogger,
Consumer<String> infoLogger,
boolean logErrors,
boolean logSentData,
boolean logResponseStatusText) {
ScheduledThreadPoolExecutor scheduler =
new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics"));
// We want delayed tasks (non-periodic) that will execute in the future to be
// cancelled when the scheduler is shutdown.
// Otherwise, we risk preventing the server from shutting down even when
// MetricsBase#shutdown() is called
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.scheduler = scheduler;
this.platform = platform;
this.serverUuid = serverUuid;
this.serviceId = serviceId;
this.enabled = enabled;
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
this.appendServiceDataConsumer = appendServiceDataConsumer;
this.submitTaskConsumer = submitTaskConsumer;
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
this.errorLogger = errorLogger;
this.infoLogger = infoLogger;
this.logErrors = logErrors;
this.logSentData = logSentData;
this.logResponseStatusText = logResponseStatusText;
checkRelocation();
if (enabled) {
// WARNING: Removing the option to opt-out will get your plugin banned from
// bStats
startSubmitting();
}
}
public void addCustomChart(CustomChart chart) {
this.customCharts.add(chart);
}
public void shutdown() {
scheduler.shutdown();
}
private void startSubmitting() {
final Runnable submitTask =
() -> {
if (!enabled || !checkServiceEnabledSupplier.get()) {
// Submitting data or service is disabled
scheduler.shutdown();
return;
}
if (submitTaskConsumer != null) {
submitTaskConsumer.accept(this::submitData);
} else {
this.submitData();
}
};
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven
// distribution of requests on the
// bStats backend. To circumvent this problem, we introduce some randomness into
// the initial and second delay.
// WARNING: You must not modify and part of this Metrics class, including the
// submit delay or frequency!
// WARNING: Modifying this code will get your plugin banned on bStats. Just
// don't do it!
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
}
private void submitData() {
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
appendPlatformDataConsumer.accept(baseJsonBuilder);
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
appendServiceDataConsumer.accept(serviceJsonBuilder);
JsonObjectBuilder.JsonObject[] chartData =
customCharts.stream()
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
.filter(Objects::nonNull)
.toArray(JsonObjectBuilder.JsonObject[]::new);
serviceJsonBuilder.appendField("id", serviceId);
serviceJsonBuilder.appendField("customCharts", chartData);
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
baseJsonBuilder.appendField("serverUUID", serverUuid);
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
scheduler.execute(
() -> {
try {
// Send the data
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logErrors) {
errorLogger.accept("Could not submit bStats metrics data", e);
}
}
});
}
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
if (logSentData) {
infoLogger.accept("Sent bStats metrics data: " + data.toString());
}
String url = String.format(REPORT_URL, platform);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip");
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
infoLogger.accept("Sent data to bStats and received response: " + builder);
}
}
/** Checks that the class was properly relocated. */
private void checkRelocation() {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this
// little "trick" ... :D
final String defaultPackage =
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
final String examplePackage =
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure no one just copy & pastes the example and uses the wrong
// package names
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
/**
* Gzips the given string.
*
* @param str The string to gzip.
* @return The gzipped string.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
}
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("value", value).build();
}
}
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public abstract static class CustomChart {
private final String chartId;
protected CustomChart(String chartId) {
if (chartId == null) {
throw new IllegalArgumentException("chartId must not be null");
}
this.chartId = chartId;
}
public JsonObjectBuilder.JsonObject getRequestJsonObject(
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
JsonObjectBuilder builder = new JsonObjectBuilder();
builder.appendField("chartId", chartId);
try {
JsonObjectBuilder.JsonObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
builder.appendField("data", data);
} catch (Throwable t) {
if (logErrors) {
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return builder.build();
}
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
}
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("value", value).build();
}
}
/**
* An extremely simple JSON builder.
*
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
* for its use-case.
*/
public static class JsonObjectBuilder {
private StringBuilder builder = new StringBuilder();
private boolean hasAtLeastOneField = false;
public JsonObjectBuilder() {
builder.append("{");
}
/**
* Appends a null field to the JSON.
*
* @param key The key of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendNull(String key) {
appendFieldUnescaped(key, "null");
return this;
}
/**
* Appends a string field to the JSON.
*
* @param key The key of the field.
* @param value The value of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, String value) {
if (value == null) {
throw new IllegalArgumentException("JSON value must not be null");
}
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
return this;
}
/**
* Appends an integer field to the JSON.
*
* @param key The key of the field.
* @param value The value of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, int value) {
appendFieldUnescaped(key, String.valueOf(value));
return this;
}
/**
* Appends an object to the JSON.
*
* @param key The key of the field.
* @param object The object.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, JsonObject object) {
if (object == null) {
throw new IllegalArgumentException("JSON object must not be null");
}
appendFieldUnescaped(key, object.toString());
return this;
}
/**
* Appends a string array to the JSON.
*
* @param key The key of the field.
* @param values The string array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, String[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values)
.map(value -> "\"" + escape(value) + "\"")
.collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends an integer array to the JSON.
*
* @param key The key of the field.
* @param values The integer array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, int[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends an object array to the JSON.
*
* @param key The key of the field.
* @param values The integer array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends a field to the object.
*
* @param key The key of the field.
* @param escapedValue The escaped value of the field.
*/
private void appendFieldUnescaped(String key, String escapedValue) {
if (builder == null) {
throw new IllegalStateException("JSON has already been built");
}
if (key == null) {
throw new IllegalArgumentException("JSON key must not be null");
}
if (hasAtLeastOneField) {
builder.append(",");
}
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
hasAtLeastOneField = true;
}
/**
* Builds the JSON string and invalidates this builder.
*
* @return The built JSON string.
*/
public JsonObject build() {
if (builder == null) {
throw new IllegalStateException("JSON has already been built");
}
JsonObject object = new JsonObject(builder.append("}").toString());
builder = null;
return object;
}
/**
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
*
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
*
* @param value The value to escape.
* @return The escaped value.
*/
private static String escape(String value) {
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '"') {
builder.append("\\\"");
} else if (c == '\\') {
builder.append("\\\\");
} else if (c <= '\u000F') {
builder.append("\\u000").append(Integer.toHexString(c));
} else if (c <= '\u001F') {
builder.append("\\u00").append(Integer.toHexString(c));
} else {
builder.append(c);
}
}
return builder.toString();
}
/**
* A super simple representation of a JSON object.
*
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
* JsonObject)}.
*/
public static class JsonObject {
private final String value;
private JsonObject(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
}
}
@@ -4,6 +4,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
/**
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
@@ -19,21 +21,22 @@ public enum MinecraftVersion {
MC1_7_R4(174), MC1_8_R3(183), MC1_9_R1(191), MC1_9_R2(192), MC1_10_R1(1101), MC1_11_R1(1111), MC1_12_R1(1121),
MC1_13_R1(1131), MC1_13_R2(1132), MC1_14_R1(1141), MC1_15_R1(1151), MC1_16_R1(1161), MC1_16_R2(1162),
MC1_16_R3(1163), MC1_17_R1(1171), MC1_18_R1(1181, true), MC1_18_R2(1182, true), MC1_19_R1(1191, true),
MC1_19_R2(1192, true);
MC1_19_R2(1192, true), MC1_19_R3(1193, true), MC1_20_R1(1201, true);
private static MinecraftVersion version;
private static Boolean hasGsonSupport;
private static Boolean isForgePresent;
private static Boolean isFoliaPresent;
private static boolean bStatsDisabled = false;
private static boolean disablePackageWarning = false;
private static boolean updateCheckDisabled = false;
private static boolean updateCheckDisabled = true;
/**
* Logger used by the api
*/
private static Logger logger = Logger.getLogger("NBTAPI");
// NBT-API Version
protected static final String VERSION = "2.11.1";
protected static final String VERSION = "2.12.0-SNAPSHOT";
private final int versionId;
private final boolean mojangMapping;
@@ -116,17 +119,29 @@ public enum MinecraftVersion {
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
} else {
logger.warning("[NBTAPI] This Server-Version(" + ver + ") is not supported by this NBT-API Version("
+ VERSION + ") located at " + MinecraftVersion.class.getName()
+ VERSION + ") located in " + VersionChecker.getPlugin()
+ ". The NBT-API will try to work as good as it can! Some functions may not work!");
}
init();
return version;
}
public static String getNBTAPIVersion() {
return VERSION;
}
private static void init() {
try {
if (hasGsonSupport() && !bStatsDisabled)
new ApiMetricsLite();
if (hasGsonSupport() && !bStatsDisabled) {
Plugin plugin = Bukkit.getPluginManager().getPlugin(VersionChecker.getPlugin());
if (plugin != null && plugin instanceof JavaPlugin) {
getLogger()
.info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
new Metrics((JavaPlugin) plugin, 1058);
} else if (plugin == null) {
getLogger().info("[NBTAPI] Unable to create a bStats instance!!");
}
}
} catch (Exception ex) {
logger.log(Level.WARNING, "[NBTAPI] Error enabling Metrics!", ex);
}
@@ -143,6 +158,8 @@ public enum MinecraftVersion {
// little "trick" ... :D (from bStats)
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
final String reservedPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'n', 'b',
't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
if (!disablePackageWarning && MinecraftVersion.class.getPackage().getName().equals(defaultPackage)) {
logger.warning(
"#########################################- NBTAPI -#########################################");
@@ -156,6 +173,33 @@ public enum MinecraftVersion {
logger.warning(
"#########################################- NBTAPI -#########################################");
}
if (!disablePackageWarning && !"NBTAPI".equals(VersionChecker.getPlugin())) { // we are not the nbtapi, check
// for common shading errors
if (!"de.tr7zw.nbtapi.utils".equals(reservedPackage)) {
logger.warning(
"#########################################- NBTAPI -#########################################");
logger.warning(
"The NBT-API inside " + VersionChecker.getPlugin() + " is the plugin version, not the API!");
logger.warning(
"The plugin itself should never be shaded! Remove the `-plugin` from the dependency and fix your shading setup.");
logger.warning(
"For more info check: https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven#option-2-shading-the-nbt-api-into-your-plugin");
logger.warning(
"#########################################- NBTAPI -#########################################");
return; // don't also print the second error
}
if (MinecraftVersion.class.getPackage().getName().equals("de.tr7zw.nbtapi.utils")) {
logger.warning(
"#########################################- NBTAPI -#########################################");
logger.warning(
"The NBT-API inside " + VersionChecker.getPlugin() + " is located at 'de.tr7zw.nbtapi.utils'!");
logger.warning(
"This package name is reserved for the official NBTAPI plugin, and not intended to be used for shading!");
logger.warning("Please change the relocate to something else. For example: com.example.util.nbtapi");
logger.warning(
"#########################################- NBTAPI -#########################################");
}
}
}
/**
@@ -166,7 +210,7 @@ public enum MinecraftVersion {
return hasGsonSupport;
}
try {
logger.info("[NBTAPI] Found Gson: " + Class.forName("com.google.gson.Gson"));
Class.forName("com.google.gson.Gson");
hasGsonSupport = true;
} catch (Exception ex) {
logger.info("[NBTAPI] Gson not found! This will not allow the usage of some methods!");
@@ -193,6 +237,23 @@ public enum MinecraftVersion {
return isForgePresent;
}
/**
* @return True, if Folia is present
*/
public static boolean isFoliaPresent() {
if (isFoliaPresent != null) {
return isFoliaPresent;
}
try {
logger.info("[NBTAPI] Found Folia: "
+ Class.forName("io.papermc.paper.threadedregions.RegionizedServer"));
isFoliaPresent = true;
} catch (Exception ex) {
isFoliaPresent = false;
}
return isFoliaPresent;
}
/**
* Calling this function before the NBT-Api is used will disable bStats stats
* collection. Please consider not to do that, since it won't affect your plugin
@@ -210,6 +271,14 @@ public enum MinecraftVersion {
updateCheckDisabled = true;
}
/**
* Enables the update check. Uses Spiget to get the current version and prints a
* warning when outdated.
*/
public static void enableUpdateCheck() {
updateCheckDisabled = false;
}
/**
* Forcefully disables the log message for plugins not shading the API to
* another location. This may be helpful for networks or development
@@ -0,0 +1,24 @@
package de.tr7zw.changeme.nbtapi.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class PathUtil {
private static final Pattern pattern = Pattern.compile("[^\\\\](\\.)");
public static List<String> splitPath(String path) {
List<String> list = new ArrayList<>();
Matcher matcher = pattern.matcher(path);
int startIndex = 0;
while (matcher.find(startIndex)) {
list.add(path.substring(startIndex, matcher.end() - 1).replace("\\.", "."));
startIndex = matcher.end();
}
list.add(path.substring(startIndex).replace("\\.", "."));
return list;
}
}
@@ -1,11 +1,13 @@
package de.tr7zw.changeme.nbtapi.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import org.bukkit.configuration.file.YamlConfiguration;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -44,7 +46,7 @@ public class VersionChecker {
int versionDifference = getVersionDifference(latest.get("name").getAsString());
if (versionDifference == -1) { // Outdated
MinecraftVersion.getLogger().log(Level.WARNING,
"[NBTAPI] The NBT-API located at '" + NBTItem.class.getPackage() + "' seems to be outdated!");
"[NBTAPI] The NBT-API in '" + getPlugin() + "' seems to be outdated!");
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
MinecraftVersion.getLogger().log(Level.WARNING,
@@ -54,7 +56,7 @@ public class VersionChecker {
if (!hideOk)
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] The NBT-API seems to be up-to-date!");
} else if (versionDifference == 1) {
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage()
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] The NBT-API in '" + getPlugin()
+ "' seems to be a future Version, not yet released on Spigot/CurseForge! This is not an error!");
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
@@ -105,4 +107,19 @@ public class VersionChecker {
return 1;
}
protected static String getPlugin() {
ClassLoader classLoader = VersionChecker.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("plugin.yml");
if (inputStream != null) {
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
return pluginYml.getString("name");
} catch (IOException e) {
// ignored
}
}
return NBTItem.class.getPackage().getName();
}
}
@@ -1,17 +0,0 @@
package de.tr7zw.changeme.nbtapi.utils.annotations;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
@Retention(RUNTIME)
@Target({ METHOD })
public @interface AvailableSince {
MinecraftVersion version();
}
@@ -1,18 +0,0 @@
package de.tr7zw.changeme.nbtapi.utils.annotations;
import java.lang.reflect.Method;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
public class CheckUtil {
public static boolean isAvaliable(Method method) {
if (MinecraftVersion.getVersion().getVersionId() < method.getAnnotation(AvailableSince.class).version()
.getVersionId())
throw new NbtApiException("The Method '" + method.getName() + "' is only avaliable for the Versions "
+ method.getAnnotation(AvailableSince.class).version() + "+, but still got called!");
return true;
}
}
@@ -83,6 +83,15 @@ public enum ClassWrapper {
}
enabled = true;
try {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1) && mojangName != null) {
// check for Mojmapped enviroment
try {
clazz = Class.forName(mojangName);
return;
} catch (ClassNotFoundException ex) {
// ignored, not mojang mapped
}
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1) && mojangMap != null) {
clazz = Class.forName(mojangMap + "." + clazzName);
} else if (packageId == PackageWrapper.NONE) {
@@ -28,10 +28,12 @@ public class MojangToMapping {
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "n");
put("net.minecraft.nbt.CompoundTag#remove(java.lang.String)", "r");
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "c");
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
put("net.minecraft.nbt.CompoundTag#merge(net.minecraft.nbt.CompoundTag)", "a");
put("net.minecraft.nbt.CompoundTag#putBoolean(java.lang.String,boolean)", "a");
put("net.minecraft.nbt.CompoundTag#getTagType(java.lang.String)", "d");
put("net.minecraft.nbt.CompoundTag#putLong(java.lang.String,long)", "a");
put("net.minecraft.nbt.CompoundTag#putLongArray(java.lang.String,long[])", "a");
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "o");
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "l");
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "h");
put("net.minecraft.nbt.CompoundTag#putString(java.lang.String,java.lang.String)", "a");
@@ -41,7 +43,6 @@ public class MojangToMapping {
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "g");
put("net.minecraft.nbt.CompoundTag#putByte(java.lang.String,byte)", "a");
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
put("net.minecraft.nbt.CompoundTag#putUUID(java.lang.String,java.util.UUID)", "a");
put("net.minecraft.nbt.CompoundTag#putShort(java.lang.String,short)", "a");
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "i");
@@ -71,7 +72,7 @@ public class MojangToMapping {
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId()", "n");
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "q");
put("net.minecraft.world.level.block.entity.BlockEntity#load(net.minecraft.nbt.CompoundTag)", "a");
put("net.minecraft.server.level.ServerLevel#getBlockEntity(net.minecraft.core.BlockPos)", "c_");
put("net.minecraft.server.level.ServerLevel#getBlockState(net.minecraft.core.BlockPos)", "c_");
}
};
@@ -108,8 +109,22 @@ public class MojangToMapping {
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_20R1 = new HashMap<String, String>() {
{
putAll(MC1_19R2);
put("net.minecraft.world.entity.Entity#getEncodeId()", "br");
put("net.minecraft.world.item.ItemStack#getTag()", "v");
}
};
public static Map<String, String> getMapping() {
switch (MinecraftVersion.getVersion()) {
case MC1_20_R1:
return MC1_20R1;
case MC1_19_R2:
return MC1_19R2;
case MC1_19_R1:
@@ -36,6 +36,9 @@ public enum ReflectionMethod {
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int[].class },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray"),
new Since(MinecraftVersion.MC1_18_R1, "putIntArray(java.lang.String,int[])")),
COMPOUND_SET_LONGARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, long[].class },
MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"),
new Since(MinecraftVersion.MC1_18_R1, "putLongArray(java.lang.String,long[])")),
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, long.class },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong"),
new Since(MinecraftVersion.MC1_18_R1, "putLong(java.lang.String,long)")),
@@ -56,7 +59,7 @@ public enum ReflectionMethod {
new Since(MinecraftVersion.MC1_18_R1, "putUUID(java.lang.String,java.util.UUID)")),
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"),
new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
new Since(MinecraftVersion.MC1_18_R1, "merge(net.minecraft.nbt.CompoundTag)")),
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, ClassWrapper.NMS_NBTBASE.getClazz() },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set"),
new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
@@ -85,6 +88,9 @@ public enum ReflectionMethod {
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getIntArray"),
new Since(MinecraftVersion.MC1_18_R1, "getIntArray(java.lang.String)")),
COMPOUND_GET_LONGARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1,
new Since(MinecraftVersion.MC1_16_R1, "getLongArray"),
new Since(MinecraftVersion.MC1_18_R1, "getLongArray(java.lang.String)")),
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getLong"),
new Since(MinecraftVersion.MC1_18_R1, "getLong(java.lang.String)")),
@@ -167,7 +173,7 @@ public enum ReflectionMethod {
new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[] { ClassWrapper.NMS_BLOCKPOSITION.getClazz() },
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "getTileEntity"),
new Since(MinecraftVersion.MC1_18_R1, "getBlockEntity(net.minecraft.core.BlockPos)")),
new Since(MinecraftVersion.MC1_18_R1, "getBlockState(net.minecraft.core.BlockPos)")),
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER,
new Class[] { ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz() },
MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_16_R3,
@@ -310,6 +316,17 @@ public enum ReflectionMethod {
if (MinecraftVersion.isForgePresent() && MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
targetMethodName = Forge1710Mappings.getMethodMapping().getOrDefault(this.name(), targetMethodName);
} else if (targetVersion.version.isMojangMapping()) {
try {
// check for mojang mapped method
String name = targetVersion.name.split("\\(")[0];
method = targetClass.getClazz().getMethod(name, args);
method.setAccessible(true);
loaded = true;
methodName = name;
return;
} catch (NoSuchMethodException ignore) {
// not mojang mapped
}
targetMethodName = MojangToMapping.getMapping().getOrDefault(
targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
}
@@ -0,0 +1,42 @@
package de.tr7zw.changeme.nbtapi.wrapper;
import java.util.function.UnaryOperator;
public enum Casing {
camelCase(s -> {
if (s.length() < 2) {
return s.toLowerCase();
}
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
}), snake_case(s -> {
StringBuilder result = new StringBuilder();
// Convert the first letter to lowercase
result.append(Character.toLowerCase(s.charAt(0)));
// Iterate through the rest of the string
for (int i = 1; i < s.length(); i++) {
char currentChar = s.charAt(i);
// Convert uppercase letters to lowercase and add underscore
if (Character.isUpperCase(currentChar)) {
result.append('_').append(Character.toLowerCase(currentChar));
} else {
result.append(currentChar);
}
}
return result.toString();
}), PascalCase(s -> {
if (s.length() < 2) {
return s.toUpperCase();
}
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}), lowercase(String::toLowerCase), UPPERCASE(String::toUpperCase);
private UnaryOperator<String> convert;
Casing(UnaryOperator<String> function) {
this.convert = function;
}
public String convertString(String str) {
return convert.apply(str);
}
}
@@ -0,0 +1,55 @@
package de.tr7zw.changeme.nbtapi.wrapper;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import de.tr7zw.changeme.nbtapi.NbtApiException;
class DefaultMethodInvoker {
private static Method invokeDefaultMethod;
static {
try {
invokeDefaultMethod = InvocationHandler.class.getDeclaredMethod("invokeDefault",
new Class[] { Object.class, Method.class, Object[].class });
invokeDefaultMethod.setAccessible(true);
} catch (NoSuchMethodException | SecurityException e) {
// we are in java 8, use the fallback
}
}
/**
* Using reflections to access reflections, since some are still on java 8.
*
* @param target
* @param method
* @param args
* @return
*/
public static Object invokeDefault(Class<?> srcInt, Object target, Method method, Object[] args) {
if (invokeDefaultMethod != null) { // java 9+
try {
return invokeDefaultMethod.invoke(null, target, method, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Error while trying to invoke a default method for Java 9+. " + target + " "
+ method + " " + Arrays.toString(args), e);
}
} else {
try {
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
return constructor.newInstance(srcInt).in(srcInt).unreflectSpecial(method, srcInt).bindTo(target)
.invokeWithArguments(args);
} catch (Throwable e) {
throw new NbtApiException("Error while trying to invoke a default method for Java 8. " + target + " "
+ method + " " + Arrays.toString(args), e);
}
}
}
}
@@ -0,0 +1,35 @@
package de.tr7zw.changeme.nbtapi.wrapper;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
public interface NBTProxy {
final Map<Class<?>, NBTHandler<Object>> handlers = new HashMap<>();
public default void init() {
}
public default Casing getCasing() {
return Casing.PascalCase;
}
@SuppressWarnings("unchecked")
public default <T> NBTHandler<T> getHandler(Class<T> clazz) {
return (NBTHandler<T>) handlers.get(clazz);
}
public default Collection<NBTHandler<Object>> getHandlers() {
return handlers.values();
}
@SuppressWarnings("unchecked")
public default <T> void registerHandler(Class<T> clazz, NBTHandler<T> handler) {
handlers.put(clazz, (NBTHandler<Object>) handler);
}
}
@@ -0,0 +1,19 @@
package de.tr7zw.changeme.nbtapi.wrapper;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface NBTTarget {
public String value();
public Type type() default Type.AUTOMATIC;
public enum Type {
AUTOMATIC, GET, SET, HAS
}
}
@@ -0,0 +1,199 @@
package de.tr7zw.changeme.nbtapi.wrapper;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
public class ProxyBuilder<T extends NBTProxy> implements InvocationHandler {
private static final Map<Method, Function<Arguments, Object>> METHOD_CACHE = new ConcurrentHashMap<>();
private final Class<T> target;
private final ReadWriteNBT nbt;
private boolean readOnly;
public ProxyBuilder(ReadWriteNBT nbt, Class<T> target) {
if (!target.isInterface()) {
throw new NbtApiException("A proxy can only be built from an interface! Check the wiki for examples.");
}
this.target = target;
this.nbt = nbt;
}
public ProxyBuilder<T> readOnly() {
this.readOnly = true;
return this;
}
@SuppressWarnings("unchecked")
public T build() {
T inst = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { target }, this);
inst.init();
return inst;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
METHOD_CACHE.computeIfAbsent(method, m -> ProxyBuilder.createFunction((NBTProxy) proxy, m));
return METHOD_CACHE.get(method).apply(new Arguments(target, (NBTProxy) proxy, readOnly, nbt, args));
}
private static class Arguments {
Class<?> target;
NBTProxy proxy;
ReadWriteNBT nbt;
Object[] args;
boolean readOnly;
public Arguments(Class<?> target, NBTProxy proxy, boolean readOnly, ReadWriteNBT nbt, Object[] args) {
this.target = target;
this.proxy = proxy;
this.nbt = nbt;
this.args = args;
this.readOnly = readOnly;
}
}
@SuppressWarnings("unchecked")
private static Function<Arguments, Object> createFunction(NBTProxy proxy, Method method) {
if ("toString".equals(method.getName()) && method.getParameterCount() == 0
&& method.getReturnType() == String.class) {
return (arguments) -> arguments.nbt.toString();
}
if (method.isDefault()) {
return (arguments) -> DefaultMethodInvoker.invokeDefault(arguments.target, arguments.proxy, method,
arguments.args);
}
Type action = getAction(method);
if (action == Type.SET) {
String fieldName = getNBTName(proxy.getCasing(), method);
return (arguments) -> {
if (arguments.readOnly) {
throw new NbtApiException("Tried calling a set method on a read only object.");
}
return setNBT(arguments.nbt, arguments.proxy, fieldName, arguments.args[0]);
};
}
if (action == Type.GET) {
Class<?> retType = method.getReturnType();
String fieldName = getNBTName(proxy.getCasing(), method);
// Allow stacking of proxies
if (retType.isInterface() && NBTProxy.class.isAssignableFrom(retType)) {
return (arguments) -> {
if (arguments.nbt.hasTag(fieldName) && arguments.nbt.getType(fieldName) != NBTType.NBTTagCompound) {
throw new NbtApiException("Tried getting a '" + retType + "' proxy from the field '" + fieldName
+ "', but it's not a TagCompound!");
}
return new ProxyBuilder<NBTProxy>(arguments.nbt.getOrCreateCompound(fieldName),
(Class<NBTProxy>) retType).build();
};
}
NBTHandler<Object> handler = (NBTHandler<Object>) proxy.getHandler(retType);
if (handler != null) {
return (arguments) -> handler.get(arguments.nbt, fieldName);
}
return (arguments) -> arguments.nbt.getOrNull(fieldName, retType);
}
if(action == Type.HAS) {
String fieldName = getNBTName(proxy.getCasing(), method);
return (arguments) -> arguments.nbt.hasTag(fieldName);
}
throw new IllegalArgumentException(
"The method '" + method.getName() + "' in '" + method.getDeclaringClass().getName()
+ "' can not be handled by the NBT-API. Please check the Wiki for examples!");
}
private static Type getAction(Method method) {
NBTTarget target = method.getAnnotation(NBTTarget.class);
if (target != null) {
if (target.type() == Type.HAS && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
return Type.HAS;
}
if (target.type() == Type.GET && method.getParameterCount() == 0) {
return Type.GET;
}
if (target.type() == Type.SET && method.getParameterCount() == 1) {
return Type.SET;
}
}
if (method.getName().startsWith("set") && method.getParameterCount() == 1) {
return Type.SET;
}
if (method.getName().startsWith("get") && method.getParameterCount() == 0) {
return Type.GET;
}
if (method.getName().startsWith("has") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
return Type.HAS;
}
return null;
}
private static String getNBTName(Casing casing, Method method) {
NBTTarget target = method.getAnnotation(NBTTarget.class);
if (target != null) {
return target.value();
}
return casing.convertString(method.getName().substring(3));
}
private static Object setNBT(ReadWriteNBT nbt, NBTProxy proxy, String key, Object value) {
// welcome to the "I wish we all could use java 17" method. Thanks, legacy mc
// versions
if (value == null) {
nbt.removeKey(key);
} else if (value instanceof Boolean) {
nbt.setBoolean(key, (Boolean) value);
} else if (value instanceof Byte) {
nbt.setByte(key, (Byte) value);
} else if (value instanceof Short) {
nbt.setShort(key, (Short) value);
} else if (value instanceof Integer) {
nbt.setInteger(key, (Integer) value);
} else if (value instanceof Long) {
nbt.setLong(key, (Long) value);
} else if (value instanceof Float) {
nbt.setFloat(key, (Float) value);
} else if (value instanceof Double) {
nbt.setDouble(key, (Double) value);
} else if (value instanceof byte[]) {
nbt.setByteArray(key, (byte[]) value);
} else if (value instanceof int[]) {
nbt.setIntArray(key, (int[]) value);
} else if (value instanceof long[]) {
nbt.setLongArray(key, (long[]) value);
} else if (value instanceof String) {
nbt.setString(key, (String) value);
} else if (value instanceof UUID) {
nbt.setUUID(key, (UUID) value);
} else if (value.getClass().isEnum()) {
nbt.setEnum(key, (Enum<?>) value);
} else {
@SuppressWarnings("unchecked")
NBTHandler<Object> handler = (NBTHandler<Object>) proxy.getHandler(value.getClass());
if (handler != null) {
handler.set(nbt, key, value);
} else {
for (NBTHandler<Object> nbth : proxy.getHandlers()) {
if (nbth.fuzzyMatch(value)) {
nbth.set(nbt, key, value);
return null;
}
}
throw new IllegalArgumentException("Tried setting an object of type '" + value.getClass().getName()
+ "'. This is not a supported NBT value. Please check the Wiki for examples!");
}
}
return null;
}
}
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</parent>
<artifactId>item-nbt-api-plugin</artifactId>
<packaging>jar</packaging>
@@ -12,19 +12,19 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>nbt-injector</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>nbt-data-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -39,7 +39,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<version>3.5.0</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<relocations>
@@ -26,8 +26,11 @@ import de.tr7zw.nbtapi.plugin.tests.compounds.GetterSetterTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.InterfaceTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.IteratorTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.ListTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.LongArrayTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.MergeTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.ModernSubCompoundsTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.RemovingKeys;
import de.tr7zw.nbtapi.plugin.tests.compounds.ResolveTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.StreamTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.SubCompoundsTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.TypeTest;
@@ -47,6 +50,11 @@ import de.tr7zw.nbtapi.plugin.tests.tiles.TilesCustomNBTPersistentTest;
import de.tr7zw.nbtinjector.NBTInjector;
import de.tr7zw.nbtapi.plugin.tests.items.ItemMergingTest;
import de.tr7zw.nbtapi.plugin.tests.items.ItemStackConversionTest;
import de.tr7zw.nbtapi.plugin.tests.proxy.DataItemProxyTest;
import de.tr7zw.nbtapi.plugin.tests.proxy.SimpleProxyTest;
import de.tr7zw.nbtapi.plugin.tests.items.MetaTest;
import de.tr7zw.nbtapi.plugin.tests.items.NBTModifyItemTest;
import de.tr7zw.nbtapi.plugin.tests.items.SmuggleTest;
public class NBTAPI extends JavaPlugin {
@@ -77,6 +85,8 @@ public class NBTAPI extends JavaPlugin {
if (!getConfig().getBoolean("updateCheck.enabled")) {
getLogger().info("Update check disabled");
MinecraftVersion.disableUpdateCheck();
} else {
MinecraftVersion.enableUpdateCheck();
}
// Disabled by default since 2.1. Enable it yourself by calling
@@ -98,9 +108,11 @@ public class NBTAPI extends JavaPlugin {
apiTests.add(new RemovingKeys());
apiTests.add(new EnumTest());
apiTests.add(new InterfaceTest());
apiTests.add(new ResolveTest());
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 list support is not complete at all
apiTests.add(new ListTest());
apiTests.add(new SubCompoundsTest());
apiTests.add(new ModernSubCompoundsTest());
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 not a thing
apiTests.add(new MergeTest());
apiTests.add(new ForEachTest());
@@ -108,6 +120,8 @@ public class NBTAPI extends JavaPlugin {
apiTests.add(new EqualsTest());
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 list support is not complete at all
apiTests.add(new IteratorTest());
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1))
apiTests.add(new LongArrayTest());
// Items
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) { // 1.7.10 not a thing
@@ -115,11 +129,17 @@ public class NBTAPI extends JavaPlugin {
apiTests.add(new ItemStackConversionTest());
}
apiTests.add(new EmptyItemTest());
apiTests.add(new SmuggleTest());
apiTests.add(new DataItemProxyTest());
apiTests.add(new MetaTest());
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) { // 1.7.10 not a thing
apiTests.add(new ItemMergingTest());
apiTests.add(new DirectApplyTest());
apiTests.add(new DirectApplyMetaTest());
apiTests.add(new NBTModifyItemTest());
}
// Proxies
apiTests.add(new SimpleProxyTest());
// Entity
apiTests.add(new EntityTest());
@@ -163,8 +183,7 @@ public class NBTAPI extends JavaPlugin {
}
getLogger().info("Adding listeners...");
Bukkit.getPluginManager().registerEvents(new ReloadListener(), this);
getLogger().info("Gson:");
MinecraftVersion.hasGsonSupport();
MinecraftVersion.hasGsonSupport(); // init gson(if it hasn't already)
getLogger().info("Checking bindings...");
MinecraftVersion.getVersion();
@@ -19,6 +19,9 @@ public class NBTFileTest implements Test {
NBTFile file = new NBTFile(testFile);
file.addCompound("testcomp").setString("test1", "ok");
NBTCompound comp = file.getCompound("testcomp");
if (comp == null) {
throw new NbtApiException("Error getting compound!");
}
comp.setString("test2", "ok");
file.setLong("time", System.currentTimeMillis());
file.setString("test", "test");
@@ -1,7 +1,8 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class GetterSetterTest implements Test {
@@ -30,7 +31,7 @@ public class GetterSetterTest implements Test {
@Override
public void test() throws Exception {
NBTContainer comp = new NBTContainer();
ReadWriteNBT comp = NBT.createNBTObject();
comp.setString(STRING_TEST_KEY, STRING_TEST_VALUE);
comp.setInteger(INT_TEST_KEY, INT_TEST_VALUE);
@@ -46,13 +47,15 @@ public class GetterSetterTest implements Test {
if (!comp.hasTag(STRING_TEST_KEY)) {
throw new NbtApiException("Wasn't able to check a key! The Item-NBT-API may not work!");
}
int[] intArray = comp.getIntArray(INTARRAY_TEST_KEY);
byte[] byteArray = comp.getByteArray(BYTEARRAY_TEST_KEY);
if (!(STRING_TEST_VALUE).equals(comp.getString(STRING_TEST_KEY))
|| comp.getInteger(INT_TEST_KEY) != INT_TEST_VALUE
|| comp.getDouble(DOUBLE_TEST_KEY) != DOUBLE_TEST_VALUE
|| comp.getByte(BYTE_TEST_KEY) != BYTE_TEST_VALUE || comp.getShort(SHORT_TEST_KEY) != SHORT_TEST_VALUE
|| comp.getFloat(FLOAT_TEST_KEY) != FLOAT_TEST_VALUE || comp.getLong(LONG_TEST_KEY) != LONG_TEST_VALUE
|| comp.getIntArray(INTARRAY_TEST_KEY).length != (INTARRAY_TEST_VALUE).length
|| comp.getByteArray(BYTEARRAY_TEST_KEY).length != (BYTEARRAY_TEST_VALUE).length
|| (intArray != null && intArray.length != (INTARRAY_TEST_VALUE).length)
|| (byteArray != null && byteArray.length != (BYTEARRAY_TEST_VALUE).length)
|| !comp.getBoolean(BOOLEAN_TEST_KEY).equals(BOOLEAN_TEST_VALUE)) {
throw new NbtApiException("One key does not equal the original value! The Item-NBT-API may not work!");
}
@@ -10,6 +10,7 @@ import de.tr7zw.changeme.nbtapi.NBTList;
import de.tr7zw.changeme.nbtapi.NBTListCompound;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class ListTest implements Test {
@@ -50,20 +51,22 @@ public class ListTest implements Test {
taglist = comp.getCompoundList("complist");
if (taglist.size() == 1) {
lcomp = taglist.get(0);
ReadWriteNBT sublist = lcomp.getCompound("listsubkey");
if (lcomp.getKeys().size() != 4) {
throw new NbtApiException("Wrong key amount in Taglist (" + lcomp.getKeys().size() + ")!");
} else if (sublist == null) {
throw new NbtApiException("The sublist is null! The Item-NBT-API may not work!");
} else if (!(lcomp.getDouble("double1") == 0.3333 && lcomp.getInteger("int1") == 42
&& lcomp.getString("test2").equals("test2") && !lcomp.hasTag("test1"))) {
throw new NbtApiException("One key in the Taglist changed! The Item-NBT-API may not work!");
} else if (lcomp.getCompound("listsubkey") == null
|| !"String".equals(lcomp.getCompound("listsubkey").getString("deep"))) {
} else if (!"String".equals(sublist.getString("deep"))) {
throw new NbtApiException(
"The Compound nested in the listcompound was not correct! The Item-NBT-API may not work!");
} else if (lcomp.getCompound("listsubkey").getType("deep") != NBTType.NBTTagString) {
} else if (sublist.getType("deep") != NBTType.NBTTagString) {
throw new NbtApiException("The nested key's type wasn't correct! The Item-NBT-API may not work!");
} else if (lcomp.getCompound("listsubkey").getType("deeplist") != NBTType.NBTTagList) {
throw new NbtApiException("The nested list's type wasn't correct '"
+ lcomp.getCompound("listsubkey").getType("deeplist") + "'! The Item-NBT-API may not work!");
} else if (sublist.getType("deeplist") != NBTType.NBTTagList) {
throw new NbtApiException("The nested list's type wasn't correct '" + sublist.getType("deeplist")
+ "'! The Item-NBT-API may not work!");
}
} else {
throw new NbtApiException("Taglist is empty! The Item-NBT-API may not work!");
@@ -0,0 +1,25 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import java.util.Arrays;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class LongArrayTest implements Test {
@Override
public void test() throws Exception {
NBTContainer comp = new NBTContainer();
comp.setLongArray("test", new long[] { 1, 2, 3, 4, Long.MAX_VALUE });
if (!comp.hasTag("test")) {
throw new NbtApiException("Wasn't able to check a key! The Item-NBT-API may not work!");
}
if (!Arrays.equals(comp.getLongArray("test"), new long[] { 1, 2, 3, 4, Long.MAX_VALUE })) {
throw new NbtApiException("The long key does not equal the original value! The Item-NBT-API may not work!");
}
}
}
@@ -0,0 +1,56 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class ModernSubCompoundsTest implements Test {
private static final String COMP_TEST_KEY = "componentTest";
private static final String STRING_TEST_KEY = "stringTest";
private static final String INT_TEST_KEY = "intTest";
private static final String DOUBLE_TEST_KEY = "doubleTest";
private static final String BOOLEAN_TEST_KEY = "booleanTest";
private static final String STRING_TEST_VALUE = "TestString";
private static final int INT_TEST_VALUE = 42;
private static final double DOUBLE_TEST_VALUE = 1.5d;
private static final boolean BOOLEAN_TEST_VALUE = true;
@Override
public void test() throws Exception {
ReadWriteNBT cont = NBT.createNBTObject();
ReadWriteNBT comp = cont.getOrCreateCompound(COMP_TEST_KEY);
comp.setString(STRING_TEST_KEY, STRING_TEST_VALUE + "2");
comp.setInteger(INT_TEST_KEY, INT_TEST_VALUE * 2);
comp.setDouble(DOUBLE_TEST_KEY, DOUBLE_TEST_VALUE * 2);
if (cont.getCompound("invalide") != null) {
throw new NbtApiException("An invalide compound did not return null!");
}
comp = null;
comp = cont.getCompound(COMP_TEST_KEY);
if (comp == null) {
throw new NbtApiException("Wasn't able to get the NBTCompound!");
}
if (!comp.hasTag(STRING_TEST_KEY)) {
throw new NbtApiException("Wasn't able to check a compound key!");
}
if (!(STRING_TEST_VALUE + "2").equals(comp.getString(STRING_TEST_KEY))
|| comp.getInteger(INT_TEST_KEY) != INT_TEST_VALUE * 2
|| comp.getDouble(DOUBLE_TEST_KEY) != DOUBLE_TEST_VALUE * 2
|| comp.getBoolean(BOOLEAN_TEST_KEY) == BOOLEAN_TEST_VALUE) {
throw new NbtApiException("One key does not equal the original compound value!");
}
// Using getOrCreateCompound twice
comp.getOrCreateCompound("someName").setString("test", "abc");
if (!comp.getOrCreateCompound("someName").getString("test").equals("abc")) {
throw new NbtApiException("getOrCreateCompound did not return the same compound!");
}
}
}
@@ -0,0 +1,27 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class ResolveTest implements Test {
@Override
public void test() throws Exception {
ReadWriteNBT src = NBT.createNBTObject();
src.resolveOrCreateCompound("foo.bar.baz").setInteger("test", 42);
if (src.resolveOrDefault("foo.bar.baz.test", 0) != 42) {
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
}
if (!Integer.valueOf(42).equals(src.resolveOrNull("foo.bar.baz.test", Integer.class))) {
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
}
src.resolveOrCreateCompound("foo.some\\.key.baz").setInteger("other", 123);
if (src.getOrCreateCompound("foo").getOrCreateCompound("some.key").getOrCreateCompound("baz")
.getInteger("other") != 123) {
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
}
}
}
@@ -12,13 +12,13 @@ public class StreamTest implements Test {
@Override
public void test() throws Exception {
NBTContainer base = new NBTContainer();
base.addCompound("sub").setString("hello", "world");
base.getOrCreateCompound("sub").setString("hello", "world");
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
base.getCompound("sub").writeCompound(outStream);
base.getOrCreateCompound("sub").writeCompound(outStream);
byte[] data = outStream.toByteArray();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
NBTContainer container = new NBTContainer(inputStream);
if (!container.toString().equals(base.getCompound("sub").toString())) {
if (!container.toString().equals(base.getOrCreateCompound("sub").toString())) {
throw new NbtApiException("Component content did not match! " + base.getCompound("sub") + " " + container);
}
}
@@ -23,7 +23,11 @@ public class DirectApplyTest implements Test {
nbt.setString("SomeKey", "SomeValue");
return nbt.getString("SomeKey");
});
String outside = NBT.get(baseItem, nbt -> nbt.getString("SomeKey"));
baseItem = NBT.itemStackFromNBT(NBT.itemStackToNBT(baseItem)); // trick to force the item to be "real", not a
// Spigot only item
String outside = NBT.get(baseItem, nbt -> {
return nbt.getString("SomeKey");
});
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
throw new NbtApiException("The data was not applied!");
}
@@ -3,6 +3,7 @@ package de.tr7zw.nbtapi.plugin.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.nbtapi.plugin.tests.Test;
@@ -13,8 +14,20 @@ public class EmptyItemTest implements Test {
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
NBTItem nbti = new NBTItem(item);
if (nbti.getBoolean("test") == null || nbti.getString("test") == null)
if (nbti.hasNBTData()) {
throw new NbtApiException("Item reported to have data");
}
if (nbti.getBoolean("test") == null || nbti.getString("test") == null || nbti.getFloatList("test") == null) {
throw new NbtApiException("Getters return null instead of the default value");
}
nbti.setString("test", "test");
if (!nbti.hasNBTData()) {
throw new NbtApiException("Item reported to have no data");
}
nbti.removeKey("test");
if (nbti.hasNBTData()) {
throw new NbtApiException("Item reported to have data after deletion");
}
try {
Material barrel = Material.valueOf("BARREL");
@@ -23,6 +36,31 @@ public class EmptyItemTest implements Test {
} catch (IllegalArgumentException ex) {
// old version
}
item = new ItemStack(Material.STONE);
NBT.modify(item, nbt -> {
nbt.setString("test", "test");
});
NBT.get(item, nbt -> {
if (!nbt.hasNBTData()) {
throw new NbtApiException("Item reported to have no data");
}
return nbt.hasNBTData();
});
NBT.modify(item, nbt -> {
nbt.removeKey("test");
});
NBT.get(item, nbt -> {
if (nbt.hasNBTData()) {
throw new NbtApiException("Item reported to have data after deletion");
}
return nbt.hasNBTData();
});
ItemStack testItem = new ItemStack(Material.STONE);
NBTItem nbt = new NBTItem(testItem, true);
nbt.removeKey("not there");
nbt.setBoolean("test", true);
}
}
@@ -28,7 +28,7 @@ public class ItemConversionTest implements Test {
NBTContainer cont = new NBTContainer();
cont.setItemStack("testItem", item);
if (!cont.getItemStack("testItem").isSimilar(item))
if (!item.isSimilar(cont.getItemStack("testItem")))
throw new NbtApiException("Rebuilt item did not match the original!");
}
@@ -16,7 +16,7 @@ public class ItemStackConversionTest implements Test {
new ItemStack(Material.AIR), new ItemStack(Material.STONE) };
NBTCompound comp = NBTItem.convertItemArraytoNBT(src);
ItemStack[] recreated = NBTItem.convertNBTtoItemArray(comp);
if (src.length != recreated.length) {
if (recreated == null || src.length != recreated.length) {
throw new NbtApiException("Size did not match!");
}
for (int i = 0; i < src.length; i++) {
@@ -0,0 +1,27 @@
package de.tr7zw.nbtapi.plugin.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class MetaTest implements Test {
@Override
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
NBT.modify(item, nbt -> {
nbt.setInteger("HideFlags", 1);
nbt.modifyMeta((rnbt, meta) -> {
if (!meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS) || rnbt.getInteger("HideFlags") != 1) {
throw new NbtApiException("The meta did not correctly update or read! " + rnbt);
}
});
});
}
}
@@ -0,0 +1,97 @@
package de.tr7zw.nbtapi.plugin.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class NBTModifyItemTest implements Test {
@Override
public void test() throws Exception {
ItemStack baseItem = new ItemStack(Material.STONE);
NBT.modify(baseItem, nbti -> {
nbti.setString("SomeKey", "SomeValue");
});
if (baseItem.equals(new ItemStack(Material.STONE)) || !new NBTItem(baseItem).hasTag("SomeKey")) {
throw new NbtApiException("The item's where not equal!");
}
baseItem = new ItemStack(Material.STONE);
// test using a "real" item
baseItem = NBT.itemStackFromNBT(NBT.itemStackToNBT(baseItem));
String inside = NBT.modify(baseItem, nbt -> {
nbt.setString("SomeKey", "SomeValue");
nbt.modifyMeta((r, meta) -> {
meta.setDisplayName("Test");
});
nbt.getOrCreateCompound("sub").setInteger("val", 42);
return nbt.getString("SomeKey");
});
String outside = NBT.get(baseItem, nbt -> {
return nbt.getString("SomeKey");
});
if (baseItem == null) {
throw new NbtApiException("Base item was null!");
}
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
throw new NbtApiException("The data was not applied!");
}
if (new NBTItem(baseItem).getOrCreateCompound("sub").getInteger("val") != 42) {
throw new NbtApiException("The sub value was not applied!");
}
if (!"SomeValue".equals(inside)) {
throw new NbtApiException("Inside returned the wrong value!");
}
if (!"SomeValue".equals(outside)) {
throw new NbtApiException("Outside returned the wrong value!");
}
if (!"Test".equals(baseItem.getItemMeta().getDisplayName())) {
throw new NbtApiException("The display name was not applied!");
}
baseItem = new ItemStack(Material.STONE);
// test using a "fake" item
inside = NBT.modify(baseItem, nbt -> {
nbt.setString("SomeKey", "SomeValue");
nbt.modifyMeta((r, meta) -> {
meta.setDisplayName("Test");
});
nbt.getOrCreateCompound("sub").setInteger("val", 42);
return nbt.getString("SomeKey");
});
outside = NBT.get(baseItem, nbt -> (String) nbt.getString("SomeKey"));
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
throw new NbtApiException("The data was not applied!");
}
if (new NBTItem(baseItem).getOrCreateCompound("sub").getInteger("val") != 42) {
throw new NbtApiException("The sub value was not applied!");
}
if (!"SomeValue".equals(inside)) {
throw new NbtApiException("Inside returned the wrong value!");
}
if (!"SomeValue".equals(outside)) {
throw new NbtApiException("Outside returned the wrong value!");
}
if (!"Test".equals(baseItem.getItemMeta().getDisplayName())) {
throw new NbtApiException("The display name was not applied!");
}
// other ordering
baseItem = new ItemStack(Material.STONE);
NBT.modify(baseItem, nbti -> {
nbti.setString("a", "SomeValue");
});
NBT.modify(baseItem, nbt -> {
nbt.modifyMeta((r, meta) -> {
meta.setDisplayName("Test");
});
nbt.setInteger("b", 12);
});
if (!new NBTItem(baseItem).hasTag("a") || !new NBTItem(baseItem).hasTag("b")
|| !"Test".equals(baseItem.getItemMeta().getDisplayName())) {
throw new NbtApiException("The data was not applied: " + new NBTItem(baseItem).toString());
}
}
}
@@ -0,0 +1,61 @@
package de.tr7zw.nbtapi.plugin.tests.items;
import java.util.concurrent.atomic.AtomicReference;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTList;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class SmuggleTest implements Test {
@Override
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
Exception error = null;
try {
ReadWriteNBTList<Long> list = NBT.modify(item, nbt -> {
nbt.getLongList("test").add(123l);
return nbt.getLongList("test");
});
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
} catch (Exception e) {
// this is the excpected behavior
}
if (error != null) {
throw error;
}
try {
ReadableNBTList<Long> list = NBT.get(item, nbt -> {
return nbt.getLongList("test");
});
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
} catch (Exception e) {
// this is the excpected behavior
}
if (error != null) {
throw error;
}
AtomicReference<ReadableNBTList<Long>> list = new AtomicReference<>();
try {
NBT.get(item, nbt -> {
list.set(nbt.getLongList("test")); // smuggling the nbt out of the context
return null;
});
if (list.get().size() == 1) {
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
}
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
} catch (Exception e) {
// this is the excpected behavior
}
if (error != null) {
throw error;
}
}
}
@@ -0,0 +1,48 @@
package de.tr7zw.nbtapi.plugin.tests.proxy;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.data.proxy.NBTItemMeta;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class DataItemProxyTest implements Test {
@Override
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("Test");
item.setItemMeta(meta);
NBT.modify(item, NBTItemMeta.class, nmeta -> {
nmeta.setCustomModelData(123);
nmeta.setUnbreakable(true);
if (!nmeta.getDisplayData().getRawName().contains("Test")) {
throw new NbtApiException("Raw name didn't containg the expected String: " + nmeta);
}
ReadWriteNBT container = NBT.createNBTObject();
container.setString("foo", "bar");
nmeta.setBlockStateTag(container);
if (!container.equals(nmeta.getBlockStateTag())) {
throw new NbtApiException("BlockStateTag did not match! " + nmeta);
}
});
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
meta = item.getItemMeta();
if (!meta.hasCustomModelData() || meta.getCustomModelData() != 123) {
throw new NbtApiException("Custom Model Data did not match! " + new NBTItem(item));
}
if (!meta.isUnbreakable()) {
throw new NbtApiException("Unbreakable did not set!");
}
}
}
}
@@ -0,0 +1,93 @@
package de.tr7zw.nbtapi.plugin.tests.proxy;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.handler.NBTHandlers;
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget;
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class SimpleProxyTest implements Test {
@Override
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
NBT.modify(item, TestInterface.class, ti -> {
if(ti.hasKills()) {
throw new NbtApiException("Item reported to have kills before setting data!");
}
ti.setKills(42);
ti.addKill();
if(!ti.hasKills()) {
throw new NbtApiException("Item reported to not have kills after setting data!");
}
if (!"{Kills:43}".equals(ti.toString())) {
throw new NbtApiException("ToString returned the wrong string. " + ti.toString());
}
});
if (new NBTItem(item).getInteger("Kills") != 43) {
throw new NbtApiException("The item was not modified correctly by the proxy!");
}
NBT.modify(item, TestInterface.class, ti -> {
if (ti.theKillsWithADifferentMethodNameAndNoGet() != 43) {
throw new NbtApiException("The annotation didn't work correctly!");
}
Statistic jumps = ti.getJumps();
jumps.setPoints(9000);
jumps.addPoint();
if (ti.getJumps().getPoints() != 9001) {
throw new NbtApiException("The stacked proxy didn't work correctly!");
}
ItemStack stack = new ItemStack(Material.STONE, 42);
ti.setItem(stack);
if (!stack.equals(ti.getItem())) {
throw new NbtApiException("The handler in the proxy didn't work correctly!");
}
});
}
public interface TestInterface extends NBTProxy {
@Override
default void init() {
registerHandler(ItemStack.class, NBTHandlers.ITEM_STACK);
}
public boolean hasKills();
public void setKills(int amount);
public int getKills();
public default void addKill() {
setKills(getKills() + 1);
}
@NBTTarget(value = "Kills", type = Type.GET)
public int theKillsWithADifferentMethodNameAndNoGet();
public Statistic getJumps();
public ItemStack getItem();
public void setItem(ItemStack item);
}
public interface Statistic extends NBTProxy {
public void setPoints(int amount);
public int getPoints();
public default void addPoint() {
setPoints(getPoints() + 1);
}
}
}
@@ -14,12 +14,15 @@ public class TileTest implements Test {
@Override
public void test() throws Exception {
if (MinecraftVersion.isFoliaPresent()) {
return;
}
if (!Bukkit.getWorlds().isEmpty()) {
World world = Bukkit.getWorlds().get(0);
try {
Block block = world.getBlockAt(world.getSpawnLocation().getBlockX(), 254,
world.getSpawnLocation().getBlockZ());
if (block.getType() == Material.AIR) {
if (block.getChunk().isLoaded() && block.getType() == Material.AIR) {
block.setType(Material.CHEST);
NBTTileEntity tile = new NBTTileEntity(block.getState());
if (!MinecraftVersion.isNewerThan(MinecraftVersion.MC1_17_R1)) {
@@ -17,6 +17,9 @@ public class TilesCustomNBTPersistentTest implements Test {
public void test() throws Exception {
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_14_R1.getVersionId())
return;
if (MinecraftVersion.isFoliaPresent()) {
return;
}
if (!Bukkit.getWorlds().isEmpty()) {
World world = Bukkit.getWorlds().get(0);
try {
@@ -3,4 +3,5 @@ name: NBTAPI
version: ${project.version}
author: tr7zw
loadbefore: [ItemNBTAPI]
api-version: 1.13
api-version: 1.13
folia-supported: true
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</parent>
<artifactId>mappings-parser</artifactId>
<packaging>jar</packaging>
@@ -12,7 +12,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -24,7 +24,7 @@ public class MappingsParser {
public static StringBuilder builder = new StringBuilder();
public static void main(String[] args) throws IOException {
File input = new File("minecraft_server.1.18.2.txt");
File input = new File("minecraft_server.1.20.txt");
List<String> lines = Files.readAllLines(input.toPath());
Map<String, ClassWrapper> classes = new HashMap<>();
@@ -59,13 +59,15 @@ public class MappingsParser {
mojangName = line.trim().split(" ")[1];
unmappedName = line.trim().split(" ")[3];
classes.remove(currentClass);
for (ReflectionMethod method : mappedNames.keySet()) {
if (method.getSelectedVersionInfo() == null)
continue;
if (!mojangName.contains("("))
continue;
if (mojangName.equals(method.getSelectedVersionInfo().name)) {
mappedNames.put(method, unmappedName);
if (mappedNames != null) {
for (ReflectionMethod method : mappedNames.keySet()) {
if (method.getSelectedVersionInfo() == null)
continue;
if (!mojangName.contains("("))
continue;
if (mojangName.equals(method.getSelectedVersionInfo().name)) {
mappedNames.put(method, unmappedName);
}
}
}
// System.out.println(currentClass + ": " + mojangName + " -> " + unmappedName);
+2 -2
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</parent>
<artifactId>nbt-data-api</artifactId>
@@ -14,7 +14,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -37,20 +37,23 @@ public class WorldData {
}
public String getWorldName() {
return file.getCompound("Data").getString("LevelName");
return file.resolveOrNull("Data.LevelName", String.class);
}
public void setWorldName(String name) {
file.getCompound("Data").setString("LevelName", name);
file.getOrCreateCompound("Data").setString("LevelName", name);
}
public Vector getSpawnPosition() {
NBTCompound data = file.getCompound("Data");
if (data == null) {
return null;
}
return new Vector(data.getInteger("SpawnX"), data.getInteger("SpawnY"), data.getInteger("SpawnZ"));
}
public void setSpawnPosition(Vector vec) {
NBTCompound data = file.getCompound("Data");
NBTCompound data = file.getOrCreateCompound("Data");
data.setInteger("SpawnX", vec.getBlockX());
data.setInteger("SpawnY", vec.getBlockY());
data.setInteger("SpawnZ", vec.getBlockZ());
@@ -0,0 +1,44 @@
package de.tr7zw.changeme.nbtapi.data.proxy;
import de.tr7zw.changeme.nbtapi.handler.NBTHandlers;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget;
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
public interface NBTItemMeta extends NBTProxy {
@Override
default void init() {
registerHandler(ReadableNBT.class, NBTHandlers.STORE_READABLE_TAG);
registerHandler(ReadWriteNBT.class, NBTHandlers.STORE_READWRITE_TAG);
}
public int getCustomModelData();
public void setCustomModelData(int customModelData);
@NBTTarget(type = Type.GET, value = "Unbreakable")
public boolean isUnbreakable();
public void setUnbreakable(boolean unbreakable);
public ReadWriteNBT getBlockStateTag();
public void setBlockStateTag(ReadableNBT blockState);
@NBTTarget(value = "display")
public DisplayData getDisplayData();
public interface DisplayData extends NBTProxy {
@NBTTarget(value = "Name")
public void setRawName(String rawName);
@NBTTarget(value = "Name")
public String getRawName();
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</parent>
<artifactId>nbt-injector</artifactId>
<packaging>jar</packaging>
@@ -12,7 +12,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -52,7 +52,7 @@ public class NBTInjector {
if (MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4
|| MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
logger.warning(
"[NBTINJECTOR] The NBT-Injector is not compatibel with this Minecraft Version! For 1.16+ please use the persistent storage API.");
"[NBTINJECTOR] The NBT-Injector is not compatible with this Minecraft Version! For 1.16+ please use the persistent storage API.");
return;
}
isInjected = true;
+22 -21
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
<packaging>pom</packaging>
<inceptionYear>2015</inceptionYear>
<organization>
@@ -90,7 +90,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>1.8</source>
<target>1.8</target>
@@ -99,7 +99,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -112,7 +112,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<version>3.5.0</version>
<configuration>
<source>8</source>
</configuration>
@@ -138,23 +138,10 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.tr7zw</groupId>
<artifactId>allcontributors</artifactId>
<version>0.0.1-SNAPSHOT</version>
<executions>
<execution>
<id>update-contributors</id>
<goals>
<goal>update</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.0.0</version>
<version>2.2.0</version>
<configuration>
<licenseName>mit</licenseName>
<licenseFile>LICENSE</licenseFile>
@@ -164,22 +151,36 @@
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.28.0</version>
<version>2.30.0</version>
<configuration>
<formats>
<format>
<includes>
<include>*.java</include>
<include>.gitignore</include>
</includes>
<trimTrailingWhitespace></trimTrailingWhitespace>
<endWithNewline></endWithNewline>
</format>
</formats>
<markdown>
<includes> <!-- You have to set the target manually -->
<include>**/*.md</include>
</includes>
<excludes>
<exclude>target/**</exclude>
</excludes>
<flexmark/> <!-- has its own section below -->
</markdown>
<!-- define a language-specific format -->
<java>
<!-- no need to specify files, inferred automatically, but you can if you want -->
<excludes>
<exclude>Metrics.java</exclude>
</excludes>
<includes>
<include>*.java</include>
</includes>
<eclipse>
<file>formatter.xml</file>
</eclipse>
+3 -3
View File
@@ -1,5 +1,5 @@
[B][SIZE=6](Item/Entity/Tile) NBT API[/SIZE][/B]
[URL='https://ci.codemc.io/job/Tr7zw/job/Item-NBT-API/'][IMG]https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API[/IMG][/URL] [URL='https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent'][IMG]https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=ncloc[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic[/IMG] [/URL][URL='https://discordapp.com/invite/yk4caxM'][IMG]https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white[/IMG][/URL]
[URL='https://ci.codemc.io/job/Tr7zw/job/Item-NBT-API/'][IMG]https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API[/IMG][/URL] [URL='https://sonarcloud.io/dashboard?id=tr7zw_Item-NBT-API'][IMG]https://sonarcloud.io/api/project_badges/measure?project=tr7zw_Item-NBT-API&metric=ncloc[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic[/IMG] [/URL][URL='https://discordapp.com/invite/yk4caxM'][IMG]https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white[/IMG][/URL]
The NBT API allows you to add custom NBT tags to Itemstacks, TileEntities and Entities, or modify excisting ones!
It completely uses reflections to interact with NMS code and works with all the latest versions. On server start the plugin checks all reflections.
@@ -112,7 +112,7 @@ Just use the Plugin as Maven dependency and remember to add "NBTAPI" as dependen
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api-plugin</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</dependency>
[/CODE]
[/SPOILER]
@@ -122,7 +122,7 @@ Using this method the API gets copied into your plugin. Server owners don't have
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.11.1</version>
<version>2.12.0-SNAPSHOT</version>
</dependency>
...
<plugins>
@@ -39,3 +39,4 @@ In general "new NBTEntity(entity).mergeCompound(nbt)", but you might want to rem
// Gameprofile
GameProfile profile = NBT.nbtToGameProfile(nbt);
```
+5 -1
View File
@@ -6,17 +6,21 @@ NBT API has been tested on Spigot
Full JavaDoc can be found [here](https://tr7zw.github.io/Item-NBT-API/v2-api/)!
### What do I have to do as a server owner?
Just download the jar and drop it in the plugins folder. Done.
Note that outdated plugins may ask for "ItemNBTAPI". In this case, download version 1.8.3 from the versions tab. The outdated "ItemNBTAPI" and "NBTAPI" can be used at the same time.
### 1.7 Notes
* Use 1.7.10
* NBTLists may not work
* NBTTypes don't work as 1.7.x is missing this feature.
* TLDR: 1.7.10 is a bit broken and not everything will work! Also it's not supported anymore!
### Don't reload the NBT-Injector
Reloading in general is a horrible thing and it will break the NBTInjector in horrible ways! When updating plugins/changing configs always restart the server normally!
### How can I use the API as a developer?
See [Using the NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API)
See [Using the NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API)
@@ -1,4 +1,5 @@
# Merging Compounds
You can "merge" the data from any given Compound onto some other Compound. This will overwrite tags with the same name.
Example:
@@ -20,3 +21,4 @@ NBT.modify(zombie, nbt -> {
nbt.mergeCompound(NBT.parseNBT("{Silent:1b,Invulnerable:1b,Glowing:1b,IsBaby:1b}"));
});
```
+10
View File
@@ -0,0 +1,10 @@
## List of some plugins on Github that utilize NBTAPI
Want your plugin added to this list? Submit a PR editing this file located in `~/wiki/Plugins.md`!
- [IllegalStack](https://github.com/dniym/IllegalStack)
- [Prison](https://github.com/PrisonTeam/Prison)
- [CommandAPI](https://github.com/JorelAli/CommandAPI)
- [SkBee](https://github.com/ShaneBeee/SkBee/)
- [Monumenta](https://github.com/TeamMonumenta/monumenta-plugins-public/)
+21 -19
View File
@@ -1,27 +1,29 @@
### We will be using [this head](https://minecraft-heads.com/custom-heads/food%20&%20drinks/28194-cup-of-soda) as example.
# This is an old example and needs to be updated! Proceed with caution!
We'll be using [this head](https://minecraft-heads.com/custom-heads/decoration/32112-dragonsbreath-opal "Dragonsbreath Opal") for this demonstration.
```java
String textureValue = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWY1MjQxNjZmN2NlODhhNTM3MTU4NzY2YTFjNTExZTMyMmE5M2E1ZTExZGJmMzBmYTZlODVlNzhkYTg2MWQ4In19fQ=="; // Pulled from the head link, scroll to the bottom and the "Other Value" field has this texture id.
// This is the base64 texture value from the bottom of the previously mentioned website.
final String textureValue = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQyY2M5MjAzYzkwYjg5YmRhYzFkZjI4NDE2NzI2NmI5NTNkZmViZjNjNDY5MGE3Y2QwYjE1NzkxYTYyZTU4MiJ9fX0=";
ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1); // Creating the ItemStack, your input may vary.
NBTItem nbti = new NBTItem(head); // Creating the wrapper.
// Creating ItemStack
NBTCompound disp = nbti.addCompound("display");
disp.setString("Name", "Testitem"); // Setting the name of the Item
// For Minecraft 1.12 and above
final ItemStack item = new ItemStack(Material.SKULL_ITEM);
item.setDurability((short) 3);
NBTList l = disp.getStringList("Lore");
l.add("Some lore"); // Adding a bit of lore.
// For Minecraft 1.13 and newer
final ItemStack item = new ItemStack(Material.PLAYER_HEAD);
NBTCompound skull = nbti.addCompound("SkullOwner"); // Getting the compound, that way we can set the skin information
skull.setString("Name", "Dragonsbreath Opal"); // Owner's name
skull.setString("Id", "fce0323d-7f50-4317-9720-5f6b14cf78ea");
// The UUID, note that skulls with the same UUID but different textures will misbehave and only one texture will load
// (They'll share it), if skulls have different UUIDs and same textures they won't stack. See UUID.randomUUID();
NBT.modify(item, nbt -> {
final ReadWriteNBT skullOwnerCompound = nbt.getOrCreateCompound("SkullOwner");
NBTListCompound texture = skull.addCompound("Properties").getCompoundList("textures").addCompound();
texture.setString("Value", textureValue);
// The owner UUID. Note that skulls with the same UUID but different textures will misbehave and only one texture will load.
// They will share the texture. To avoid this limitation, it is recommended to use a random UUID.
skullOwnerCompound.setUUID("Id", UUID.randomUUID());
skullOwnerCompound.getOrCreateCompound("Properties")
.getCompoundList("textures")
.addCompound()
.setString("Value", textureValue);
});
```
head = nbti.getItem(); // Refresh the ItemStack
```
+13 -3
View File
@@ -1,9 +1,11 @@
# Option 1) NBT-API as a dependency
Add the following Entries to your Gradle build at the correct location:
```groovy
compileOnly("de.tr7zw:item-nbt-api-plugin:VERSION")
```
(Get the current Version from [here](https://www.spigotmc.org/resources/nbt-api.7939/))
```groovy
@@ -11,21 +13,24 @@ repositories {
...
maven {
name = "CodeMC"
url = uri("https://repo.codemc.org/repository/maven-public/")
url = uri("https://repo.codemc.io/repository/maven-public/")
}
...
}
```
Finally, add the API as dependency to your ``plugin.yml``
```yml
depend: [NBTAPI]
```
# Option 2) Shading the NBT-API into your plugin
To include NBT-API directly in your plugin, you can use the gradle shadow plugin.
Add the plugin to the build configuration, as shown here:
```groovy
plugins {
id("com.github.johnrengelman.shadow") version "VERSION"
@@ -36,9 +41,11 @@ The latest version of the shadow plugin can be found [here](https://github.com/j
<br/>
Add NBT-API to your dependencies:
```groovy
implementation("de.tr7zw:item-nbt-api:VERSION")
```
(Get the current Version from [here](https://www.spigotmc.org/resources/nbt-api.7939/))
```groovy
@@ -46,13 +53,14 @@ repositories {
...
maven {
name = "CodeMC"
url = uri("https://repo.codemc.org/repository/maven-public/")
url = uri("https://repo.codemc.io/repository/maven-public/")
}
...
}
```
After this you can add the shadowjar configuration to relocate the api package:
```groovy
shadowJar {
relocate("de.tr7zw.changeme.nbtapi", "YOUR PACKAGE WHERE THE API SHOULD END UP")
@@ -60,8 +68,10 @@ shadowJar {
```
If you want to run the shadowJar task when the build task is executed, you can write like this:
```groovy
build {
dependsOn(shadowJar)
}
```
```
+55 -32
View File
@@ -1,6 +1,8 @@
# Option 1) NBT-API as a dependency
Add the following Entries to your pom at the correct location:
```
```xml
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api-plugin</artifactId>
@@ -8,69 +10,90 @@ Add the following Entries to your pom at the correct location:
<scope>provided</scope>
</dependency>
```
(Get the current Version from [here](https://modrinth.com/plugin/nbtapi/versions))
```
```xml
<repositories>
...
<!-- CodeMC -->
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
<url>https://repo.codemc.io/repository/maven-public/</url>
<layout>default</layout>
</repository>
...
</repositories>
```
Add the API as dependency to your plugin.yml
```
```yaml
depend: [NBTAPI]
```
# Option 2) Shading the NBT-API into your plugin
Add the following Entries to your pom at the correct location:
```
```xml
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>VERSION</version>
</dependency>
```
(Get the current Version from [here](https://modrinth.com/plugin/nbtapi/versions))
```
**IMPORTANT: This is not the same ``artifactId`` as using it as dependency! Never shade the ``-plugin`` artifact!**
```xml
<repositories>
...
<!-- CodeMC -->
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
<url>https://repo.codemc.io/repository/maven-public/</url>
<layout>default</layout>
</repository>
...
</repositories>
```
```xml
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>de.tr7zw.changeme.nbtapi</pattern>
<shadedPattern>YOUR PACKAGE WHERE THE API SHOULD END UP</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
```
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>de.tr7zw.changeme.nbtapi</pattern>
<shadedPattern>YOUR PACKAGE WHERE THE API SHOULD END UP</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
```
Example:
```xml
<relocation>
<pattern>de.tr7zw.changeme.nbtapi</pattern>
<shadedPattern>com.yourname.pluginname.nbtapi</shadedPattern>
</relocation>
```
+2 -1
View File
@@ -1,4 +1,3 @@
## Using the ``NBT`` class (prefered)
```java
@@ -57,6 +56,7 @@ NBTContainer container = new NBTContainer(); // Empty NBTCompound
```
## Using the wrapper(NBTCompound/)
```java
//Set
nbti.setString("Stringtest", "Teststring");
@@ -85,3 +85,4 @@ NBTCompound comp = nbti.getCompound("subtag"); // Get the tag or null
B.mergeCompound(A); // Merge the data from compound A into B (Like the /data merge command)
comp.toString(); // Get the nbt as a Mojang-Json string
```
@@ -1,4 +1,3 @@
```java
Zombie zombie = (Zombie) b.getWorld().spawnEntity(b.getLocation().add(0, 1, 0), EntityType.ZOMBIE);
NBT.modify(zombie, nbt -> {
@@ -12,3 +11,4 @@ NBT.modify(zombie, nbt -> {
}
});
```
+2 -1
View File
@@ -1,4 +1,5 @@
[tr7zw](https://github.com/tr7zw)
[SpigotMC](https://www.spigotmc.org/resources/nbt-api.7939/)
[Modrinth](https://modrinth.com/plugin/nbtapi)
[Bukkit](https://dev.bukkit.org/projects/nbt-api)
[Bukkit](https://dev.bukkit.org/projects/nbt-api)
[Hangar](https://hangar.papermc.io/tr7zw/NBTAPI)
+8 -1
View File
@@ -5,10 +5,17 @@
[Using Gradle](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Gradle)
## Code Examples
### General
* [Converting Minecraft Objects to NBT and Strings](https://github.com/tr7zw/Item-NBT-API/wiki/Converting-Minecraft-Objects-to-NBT-and-Strings)
* [Merging Compounds](https://github.com/tr7zw/Item-NBT-API/wiki/Merging-Compounds-(Copy-the-data-from-one-onto-another-compound))
### Items
* [Set a skull's skin using NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API)
### Entities
* [Zombie that can pick up loot and does 0.5 hearths of damage](https://github.com/tr7zw/Item-NBT-API/wiki/Zombie-that-can-pick-up-loot-and-does-0.5-hearths-of-damage)
* [Zombie that can pick up loot and does 0.5 hearths of damage](https://github.com/tr7zw/Item-NBT-API/wiki/Zombie-that-can-pick-up-loot-and-does-0.5-hearths-of-damage)