Compare commits

...
126 Commits
Author SHA1 Message Date
tr7zw 5bdddc134a Release 2.16.1 2026-09-17 20:06:29 +02:00
tr7zw 733b871c09 Merge pull request #359 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.16.0
Bump org.apache.maven.plugins:maven-compiler-plugin from 3.15.0 to 3.16.0
2026-09-17 20:04:44 +02:00
tr7zw bc48239fdd Start 2.16.1-SNAPSHOT 2026-09-16 18:52:12 +02:00
tr7zw f182204b2f Merge pull request #360 from NriotHrreion/fix/paper-26.3-as-bukkit-mirror
Add Minecraft 26.3 item stack compatibility
2026-09-16 18:51:06 +02:00
NriotHrreion a424ce1e85 Add Minecraft 26.3 item stack compatibility 2026-09-16 14:44:15 +08:00
dependabot[bot] c881566c14 Bump org.apache.maven.plugins:maven-compiler-plugin
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.15.0 to 3.16.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.15.0...maven-compiler-plugin-3.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 23:12:40 +00:00
tr7zw 2d6aa142de Bump version 2.16.0 2026-07-27 18:21:45 +02:00
tr7zw cc1436cd09 Merge pull request #353 from SoSeDiK/blocks
Migrate NBTBlock & NBTChunk to NBT
2026-07-27 18:12:50 +02:00
tr7zw 4506dd37da Remove setClosed calls
They do nothing in this case
2026-07-27 18:09:51 +02:00
tr7zw bbc02e1299 Merge pull request #356 from EverNife/master
Fix 'GET_LIST' and 'OWN_TYPE' to their 'LEGACY' sufixed name on Forge at 1.7.10
2026-07-24 21:28:43 +02:00
Petrus Pradella ca9d5ad140 Fix 'GET_LIST' and 'OWN_TYPE' to thier 'LEGACY' sufixed name on Forge at Minecraft 1.7.10 2026-07-13 03:04:12 -03:00
tr7zw 25c82a5ad0 Merge pull request #354 from SoSeDiK/badges
Update readme badges
2026-07-08 20:06:33 +02:00
tr7zw 17d40445d2 Remove ko-fi 2026-07-08 20:06:04 +02:00
SoSeDiK 47ad3591df Migrate NBTBlock & NBTChunk to NBT 2026-06-26 22:35:25 +03:00
SoSeDiK b3652ecf7d Update readme badges 2026-06-26 12:55:36 +03:00
tr7zw 46a9116613 Start 2.15.8-SNAPSHOT, add 26.2 support 2026-06-16 20:02:53 +02:00
tr7zw 5776be4a35 Add support for 26.1.x, paper 26.1.x support, bump version 2026-04-06 16:07:15 +02:00
tr7zw 9fbee68966 Release 2.15.6 2026-03-28 17:05:22 +01:00
tr7zw b277f0b01d Update the DataVersion when upgrading Item NBT 2026-03-28 16:59:38 +01:00
tr7zw e4793a0d46 Merge pull request #348 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.6.2
Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.1 to 3.6.2
2026-03-28 16:11:44 +01:00
tr7zw 7c34542b86 Add 26.1 2026-03-28 16:08:13 +01:00
dependabot[bot] 856bf5ed6d Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.1 to 3.6.2
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.1 to 3.6.2.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.1...maven-shade-plugin-3.6.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 23:17:37 +00:00
tr7zw 123da87a3a Merge pull request #341 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.7.1
Bump org.codehaus.mojo:license-maven-plugin from 2.7.0 to 2.7.1
2026-03-05 21:45:13 +01:00
tr7zw f3d7545c6b Merge pull request #344 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.15.0
Bump org.apache.maven.plugins:maven-compiler-plugin from 3.14.1 to 3.15.0
2026-03-05 21:45:02 +01:00
tr7zw 3b29fe399e Merge pull request #347 from tr7zw/dependabot/maven/org.bstats-bstats-bukkit-3.2.1
Bump org.bstats:bstats-bukkit from 3.1.0 to 3.2.1
2026-03-05 21:44:50 +01:00
tr7zw c368318bb6 Start 2.15.6-SNAPSHOT 2026-03-05 21:44:05 +01:00
dependabot[bot] c1473dfa97 Bump org.bstats:bstats-bukkit from 3.1.0 to 3.2.1
Bumps [org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.1.0 to 3.2.1.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.1.0...v3.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-24 23:13:02 +00:00
dependabot[bot] a360ca50f4 Bump org.apache.maven.plugins:maven-compiler-plugin
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.1 to 3.15.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.1...maven-compiler-plugin-3.15.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 04:12:03 +00:00
dependabot[bot] 8b1ca8447e Bump org.codehaus.mojo:license-maven-plugin from 2.7.0 to 2.7.1
Bumps [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.7.0 to 2.7.1.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.7.0...2.7.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 23:02:18 +00:00
tr7zw b66353d0d0 Release 2.15.5 2025-12-09 20:19:44 +01:00
tr7zw 5bec1371d2 Add missing line so the api doesn't complain 2025-12-09 19:11:20 +01:00
tr7zw 8cbb6af964 Start 2.15.5-SNAPSHOT 2025-12-09 19:09:48 +01:00
tr7zw 309455ecfc Merge branch 'master' of https://github.com/tr7zw/Item-NBT-API 2025-12-09 18:44:38 +01:00
tr7zw 22c0b0385b Release 2.15.4 2025-12-09 18:44:30 +01:00
tr7zw bc6bd56ce9 Merge pull request #339 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-source-plugin-3.4.0
Bump org.apache.maven.plugins:maven-source-plugin from 3.3.1 to 3.4.0
2025-12-09 18:43:18 +01:00
tr7zw fcdf0929f9 Add 1.21.11 support 2025-12-09 18:37:52 +01:00
tr7zw e1c6ce2575 Start 2.15.4-SNAPSHOT 2025-12-09 18:36:41 +01:00
dependabot[bot] 2fcc1d82ab Bump org.apache.maven.plugins:maven-source-plugin from 3.3.1 to 3.4.0
Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/apache/maven-source-plugin/releases)
- [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.1...maven-source-plugin-3.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 02:01:29 +00:00
tr7zw eec2c0980e tag 2.15.3 2025-10-11 17:05:48 +02:00
tr7zw 3ddf3e53dc Add 1.21.10 support 2025-10-04 20:09:20 +02:00
tr7zw 70d5ab12eb Add 1.21.9 spigot support 2025-09-30 19:25:28 +02:00
tr7zw a63e9ba8f4 Add 1.21.9 Paper support, Fix Gameprofile reading/writing 2025-09-30 19:14:54 +02:00
tr7zw d8a59d6383 Merge pull request #335 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.6.1
Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.0 to 3.6.1
2025-09-28 11:53:27 +02:00
tr7zw 943c7d23d6 Merge pull request #336 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.7.0
Bump org.codehaus.mojo:license-maven-plugin from 2.6.0 to 2.7.0
2025-09-28 11:53:18 +02:00
tr7zw 6c70ce2ccc Merge pull request #337 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.14.1
Bump org.apache.maven.plugins:maven-compiler-plugin from 3.14.0 to 3.14.1
2025-09-28 11:53:09 +02:00
tr7zw c1b7854fbb Merge pull request #338 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.12.0
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.3 to 3.12.0
2025-09-28 11:52:59 +02:00
tr7zw f9f1c204b6 Start 2.15.3-SNAPSHOT 2025-09-28 11:52:38 +02:00
dependabot[bot] 789f1fea96 Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.3 to 3.12.0
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.3 to 3.12.0.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 23:02:47 +00:00
dependabot[bot] 0e40144f09 Bump org.apache.maven.plugins:maven-compiler-plugin
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.0 to 3.14.1.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.0...maven-compiler-plugin-3.14.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 23:02:10 +00:00
dependabot[bot] 1c6315e014 Bump org.codehaus.mojo:license-maven-plugin from 2.6.0 to 2.7.0
Bumps [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.6.0...2.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 23:02:05 +00:00
dependabot[bot] 91f345adf2 Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.0 to 3.6.1
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.0 to 3.6.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.0...v3.6.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 23:09:04 +00:00
tr7zw 71783a8dc9 Release 2.15.2 2025-08-26 21:59:51 +02:00
tr7zw 5d06ae9275 Fix version variables in DataFixerUtil 2025-08-26 21:51:54 +02:00
tr7zw 835fdf48cd Fix codec mapping issue in 1.21.5. Fix #332 2025-08-26 21:35:36 +02:00
tr7zw 31d77d25f9 Merge pull request #333 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.11.3
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.2 to 3.11.3
2025-08-20 21:55:57 +02:00
dependabot[bot] 6cb08ac919 Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.2 to 3.11.3
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.11.3.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 10:47:34 +00:00
tr7zw 08a92ac06c 2.15.2-SNAPSHOT, add 1.21.8 2025-07-17 20:29:51 +02:00
tr7zw 6bcd00d114 Bump to 2.15.1 release 2025-07-01 19:42:39 +02:00
tr7zw bc93986392 Merge pull request #328 from patyhank/master
Mark 1.21.7 as supported
2025-07-01 15:01:39 +02:00
蒟蒻 aa646e35ae mark 1.21.7 as supported 2025-07-01 20:51:49 +08:00
tr7zw 1054d16996 Add Spigot 1.21.6 support 2025-06-24 22:18:48 +02:00
tr7zw 9351a86774 Merge pull request #325 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.6.0
Bump org.codehaus.mojo:license-maven-plugin from 2.5.0 to 2.6.0
2025-06-24 21:57:11 +02:00
tr7zw 56a43d246b Merge pull request #326 from patyhank/master
[1.21.6] support Entity and BlockEntity
2025-06-24 21:57:01 +02:00
蒟蒻 2b895fd4ef Entity and BlockEntity update to MC1_21_R5 2025-06-24 10:54:47 +08:00
dependabot[bot] 8f8bbfd034 Bump org.codehaus.mojo:license-maven-plugin from 2.5.0 to 2.6.0
Bumps [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.5.0...2.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 01:45:14 +00:00
tr7zw df231c2324 Fix RegistryOps initialization for MC1_21_R5 and above
Wrapped the initialization of nbtRegistryOps in a version check to ensure it only runs for Minecraft versions MC1_21_R5 and above. This prevents potential errors on earlier versions where the method may not be available.
2025-06-21 13:55:03 +02:00
tr7zw b068284b6e Fix 1.21.6 items with components 2025-06-19 02:26:08 +02:00
tr7zw 9e9b85f46a WIP update to 1.21.6 Paper (here be dragons) 2025-06-18 22:43:03 +02:00
tr7zw 3baed4b1e7 Catch issues with the yml 2025-06-16 18:44:30 +02:00
tr7zw 432b40b3bc Add paper-plugin.yml support. Stat for plugin type 2025-06-06 22:22:56 +02:00
tr7zw fdf3638948 Add new bStats graphs 2025-06-06 21:39:10 +02:00
tr7zw b240e888cd Shade in the Metrics the correct way 2025-06-06 19:33:17 +02:00
tr7zw ccc096e2d4 Start 2.15.1-SNAPSHOT 2025-06-06 19:00:02 +02:00
tr7zw 35f541d3db Tag 2.15.0 2025-04-17 20:30:44 +02:00
tr7zw d4ee3b998c Fix wiki 2025-04-17 19:46:53 +02:00
tr7zw 2902dca5a5 Follow the javadoc of the list#remove method. Fix #293 2025-04-17 19:45:29 +02:00
tr7zw 1c1e3c8664 Remove "json" wording, add more warnings/info 2025-04-17 19:41:27 +02:00
tr7zw 7f8f021ced Merge pull request #321 from SoSeDiK/diff
Add a method to extract difference between two compounds
2025-04-17 19:24:21 +02:00
SoSeDiK 98ed1850f6 Add a method to extract difference between two compounds 2025-04-12 16:18:22 +03:00
tr7zw 764e01503d Merge pull request #320 from SoSeDiK/wiki
Update "Reading world data" wiki example
2025-03-31 19:04:27 +02:00
SoSeDiK 43d8e2a351 Update "Reading world data" wiki example 2025-03-31 19:09:42 +03:00
tr7zw 689cd4fa3b Remove unused code path 2025-03-31 18:03:11 +02:00
tr7zw c8f68f8b3b Merge pull request #319 from SoSeDiK/dataversion
Add DataVersion for itemstack conversions
2025-03-28 10:22:12 +01:00
tr7zw e52ba209e6 Fix mistake 2025-03-25 22:50:44 +01:00
tr7zw 418f5bea07 1.21.5 update, needs a lot of testing 2025-03-25 22:42:01 +01:00
SoSeDiK 1392e72a8a Only run items through the data fixer on MC1_20_R4+ 2025-03-22 00:37:57 +02:00
SoSeDiK dc39d1d8ef Add DataVersion for itemstack conversions 2025-03-21 23:13:50 +02:00
tr7zw a974142a55 Merge pull request #316 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-compiler-plugin-3.14.0
Bump org.apache.maven.plugins:maven-compiler-plugin from 3.13.0 to 3.14.0
2025-02-23 10:39:14 +00:00
dependabot[bot] f014e71020 Bump org.apache.maven.plugins:maven-compiler-plugin
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.13.0 to 3.14.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.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>
2025-02-21 23:13:30 +00:00
tr7zw da6be72d29 Merge pull request #313 from SoSeDiK/wiki
Update wiki
2024-12-28 19:11:29 +00:00
SoSeDiK d9a93e9306 Add info about paper-plugin.yml 2024-12-28 15:57:04 +02:00
SoSeDiK ba374fe69c Update gradle shadow plugin info 2024-12-28 15:53:01 +02:00
SoSeDiK aa35ee8333 Fixup modifyMeta example 2024-12-28 15:51:28 +02:00
SoSeDiK 3bb0bdd74c Clarify persistent data storage availability 2024-12-28 15:50:49 +02:00
tr7zw f3af749b11 Merge pull request #309 from tr7zw/dependabot/maven/org.codehaus.mojo-license-maven-plugin-2.5.0
Bump org.codehaus.mojo:license-maven-plugin from 2.4.0 to 2.5.0
2024-12-21 23:44:01 +00:00
tr7zw d81c2b4e8e Merge pull request #311 from FireML/patch-1
Add EvenMoreFish to Plugins.md
2024-12-21 23:43:51 +00:00
tr7zw 94192feb8f Merge pull request #312 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.11.2
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.1 to 3.11.2
2024-12-21 23:43:29 +00:00
tr7zw af3d9c8502 Start 2.14.2-SNAPSHOT 2024-12-22 00:42:03 +01:00
tr7zw ae98859524 Tag 2.14.1 2024-12-16 17:11:16 +01:00
tr7zw 70f0bb62f9 Add Util to get ItemStacks in Json Format for hover components 2024-12-14 21:58:53 +01:00
dependabot[bot] 505e172955 Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.1 to 3.11.2
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.1 to 3.11.2.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.1...maven-javadoc-plugin-3.11.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 23:45:07 +00:00
FireML d2b2b6a506 Add EvenMoreFish to Plugins.md, and Update CommandAPI URL 2024-12-08 01:31:10 +00:00
tr7zw e2e8aad70a Merge pull request #310 from ArmyFury/patch-1
Update Plugins.md
2024-12-07 13:17:51 +00:00
ArmyFury 4874366e30 Update Plugins.md
added SummonFriendlyMobs to plugins list
2024-12-07 12:47:39 +05:30
tr7zw d2de90ee25 Add 1.21.4 support 2024-12-03 23:28:21 +01:00
dependabot[bot] a0f93e575e Bump org.codehaus.mojo:license-maven-plugin from 2.4.0 to 2.5.0
Bumps [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.4.0...2.5.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>
2024-11-29 23:30:47 +00:00
tr7zw b339c9e9c2 Merge pull request #306 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.11.1
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.1 to 3.11.1
2024-11-13 23:54:08 +01:00
tr7zw 3425a6672d Merge pull request #308 from SoSeDiK/wiki-changes
Update wiki
2024-11-13 23:53:54 +01:00
tr7zw 67616266da Start snapshots 2.14.1 2024-11-13 23:53:07 +01:00
SoSeDiK b08e4aa12f Update wiki to reflect some 2.14.0 changes
ItemMeta usage clarifications
2024-11-12 14:28:06 +02:00
dependabot[bot] 27411e90da Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.1 to 3.11.1
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.10.1 to 3.11.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.10.1...maven-javadoc-plugin-3.11.1)

---
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>
2024-11-04 23:48:38 +00:00
tr7zw 73776cfec4 Sneak quick fix into release 2024-11-02 15:18:16 +01:00
tr7zw bd674e31c0 Tag 2.14.0 2024-11-02 15:07:50 +01:00
tr7zw 3405ffe0a3 Fix javadoc deploy 2024-11-02 13:24:22 +01:00
tr7zw f1317bcb04 Fix BlockEntity test. Fix #304 2024-11-02 13:06:19 +01:00
tr7zw 4a32343b95 Add 1.21.3 2024-10-28 18:56:04 +01:00
tr7zw 3de6ed83eb Hide NBTFile/NBTContainer fully behind NBT. Update tests to new format. Add missing addCompound(ReadableNBT comp) to ReadWriteNBTCompoundList 2024-10-22 23:45:16 +02:00
tr7zw e5bdce73ea Merge pull request #300 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.10.1
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.0 to 3.10.1
2024-10-22 22:42:25 +02:00
tr7zw 410e88a35b Add getComponents methods 2024-10-22 22:39:35 +02:00
tr7zw e3142b4869 Add 1.21.2 support 2024-10-22 22:24:59 +02:00
tr7zw 243a5c2bcd Fix really stupid edge case of writeCompound 2024-10-22 22:08:04 +02:00
dependabot[bot] 045c710366 Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.0 to 3.10.1
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.10.0...maven-javadoc-plugin-3.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 23:20:24 +00:00
tr7zw 4cd118671f Merge pull request #297 from tr7zw/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.10.0
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.8.0 to 3.10.0
2024-08-27 23:03:05 +02:00
dependabot[bot] d86c91f2e6 Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.8.0 to 3.10.0
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.8.0 to 3.10.0.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.8.0...maven-javadoc-plugin-3.10.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>
2024-08-27 16:08:01 +00:00
tr7zw f8736c8188 Also update old items with "Count" tags 2024-08-27 18:07:27 +02:00
tr7zw b3aec54205 Start 2.13.3-SNAPSHOT 2024-08-27 18:06:56 +02:00
64 changed files with 2033 additions and 1271 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ updates:
ignore:
- dependency-name: com.github.ekryd.sortpom:sortpom-maven-plugin
versions:
- 2.13.2
- 2.13.3-SNAPSHOT
- 2.14.0
- 2.14.1
- 2.15.0
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- name: Copy artifacts
shell: bash
run: |
mkdir -p artifacts/v2-api && cp -r item-nbt-api/target/apidocs/* artifacts/v2-api
mkdir -p artifacts/v2-api && cp -r item-nbt-api/target/reports/apidocs/* artifacts/v2-api
mkdir -p artifacts/v2-plugin && cp -r item-nbt-plugin/target/apidocs/* artifacts/v2-plugin
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.4.1
+3 -2
View File
@@ -1,13 +1,14 @@
# Item-NBT-API
[![Modrinth](https://img.shields.io/modrinth/dt/nbtapi?logo=modrinth&label=Modrinth%20downloads)](https://modrinth.com/plugin/nbtapi)
[![Discord](https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white)](https://discordapp.com/invite/yk4caxM)
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it in 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=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
+15 -3
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
</parent>
<artifactId>item-nbt-api</artifactId>
@@ -18,6 +18,12 @@
<version>4.0.26</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@@ -32,9 +38,15 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.2</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>de.tr7zw.changeme.nbtapi.utils.metrics</shadedPattern>
</relocation>
</relocations>
<createDependencyReducedPom>true</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>LICENSE</resource>
@@ -1,17 +1,24 @@
package de.tr7zw.changeme.nbtapi;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
import javax.annotation.Nullable;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import org.bukkit.Chunk;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import com.mojang.authlib.GameProfile;
import de.tr7zw.changeme.nbtapi.iface.NBTFileHandle;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteItemNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableItemNBT;
@@ -200,9 +207,9 @@ public class NBT {
}
/**
* 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
* 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
*
* @param blockState The block state of the block you want to get the data from.
* @param getter A function that takes a ReadableNBT and returns a value of
@@ -269,17 +276,17 @@ public class NBT {
nbtEnt.setClosed();
return ret;
}
/**
* It takes an ItemStack and a Consumer&lt;ReadWriteNBT&gt;, and then applies
* the Consumer to the ItemStacks Components as NBT. This is for 1.20.5+ only.
* This method is quiet expensive, so don't overuse it.
* This method is quite expensive, so don't overuse it.
*
* @param item The item you want to modify the components of
* @param consumer The consumer that will be used to modify the components.
*/
public static void modifyComponents(ItemStack item, Consumer<ReadWriteNBT> consumer) {
if(!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
if (!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
throw new NbtApiException("This method only works for 1.20.5+!");
}
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
@@ -287,18 +294,18 @@ public class NBT {
ItemStack tmp = NBT.itemStackFromNBT(nbti);
item.setItemMeta(tmp.getItemMeta());
}
/**
* It takes an ItemStack and a Consumer&lt;ReadWriteNBT&gt;, and then applies
* the Consumer to the ItemStacks Components as NBT. This is for 1.20.5+ only.
* This method is quiet expensive, so don't overuse it.
* This method is quite expensive, so don't overuse it.
*
* @param item The item you want to modify the components of
* @param function The consumer that will be used to modify the components.
* @return The return type is the same as the return type of the function.
*/
public static <T> T modifyComponents(ItemStack item, Function<ReadWriteNBT, T> function) {
if(!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
if (!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
throw new NbtApiException("This method only works for 1.20.5+!");
}
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
@@ -308,6 +315,39 @@ public class NBT {
return ret;
}
/**
* It takes an ItemStack and a Consumer&lt;ReadWriteNBT&gt;, and then applies
* the Consumer to the ItemStacks Components as NBT. This is for 1.20.5+ only.
* This method is quite expensive, try to cache the results/use it smartly.
*
* @param item The item you want to read the components of
* @param consumer The consumer that will be used to read the components.
*/
public static void getComponents(ItemStack item, Consumer<ReadableNBT> consumer) {
if (!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
throw new NbtApiException("This method only works for 1.20.5+!");
}
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
consumer.accept(nbti.getOrCreateCompound("components"));
}
/**
* It takes an ItemStack and a Consumer&lt;ReadWriteNBT&gt;, and then applies
* the Consumer to the ItemStacks Components as NBT. This is for 1.20.5+ only.
* This method is quite expensive, try to cache the results/use it smartly.
*
* @param item The item you want to read the components of
* @param function The consumer that will be used to read the components.
* @return The return type is the same as the return type of the function.
*/
public static <T> T getComponents(ItemStack item, Function<ReadableNBT, T> function) {
if (!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
throw new NbtApiException("This method only works for 1.20.5+!");
}
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
return function.apply(nbti.getOrCreateCompound("components"));
}
/**
* It takes an entity and a function that takes a ReadWriteNBT and applies the
* function to the entity
@@ -492,12 +532,70 @@ public class NBT {
* It takes a nbt json string, and returns a ReadWriteNBT object
*
* @param nbtString The NBT string to parse.
* @return A new NBTContainer object.
* @return A new ReadWriteNBT object.
*/
public static ReadWriteNBT parseNBT(String nbtString) {
return new NBTContainer(nbtString);
}
/**
* Reads in an NBT stream and returns a ReadWriteNBT object
*
* @param stream The NBT stream to read.
* @return A new ReadWriteNBT object.
*/
public static ReadWriteNBT readNBT(InputStream stream) {
return new NBTContainer(stream);
}
/**
* Helper method for other developers using NMS. Allows to wrap any
* net.minecraft.nbt.CompoundTag to a NBTAPI ReadWriteNBT object.
*
* @param nmsNbtTag Needs to be a valid net.minecraft.nbt.CompoundTag
* @return A new ReadWriteNBT object.
*/
public static ReadWriteNBT wrapNMSTag(Object nmsNbtTag) {
return new NBTContainer(nmsNbtTag);
}
/**
* Creates a NBTFileHandle that uses @param file to store its data. If this file
* exists, the data will be loaded, otherwise a new file gets created.
*
* @param file
* @throws IOException
*/
public static NBTFileHandle getFileHandle(File file) throws IOException {
return new NBTFile(file);
}
/**
* Reads NBT data from the provided file.
* <p>
* Returns an empty tag if the file does not exist.
*
* @param file file to read
* @return ReadWriteNBT of the files data
* @throws IOException exception
*/
public static ReadWriteNBT readFile(File file) throws IOException {
return NBTFile.readFrom(file);
}
/**
* Saves NBT data to the provided file.
* <p>
* Will fully override the file if it already exists.
*
* @param file file
* @param nbt NBT data
* @throws IOException exception
*/
public static void writeFile(File file, ReadWriteNBT nbt) throws IOException {
NBTFile.saveTo(file, (NBTCompound) nbt);
}
/**
* Create a read only proxy class for NBT, given an annotated interface.
*
@@ -632,4 +730,204 @@ public class NBT {
return val;
}
/**
* Reads block‑specific data that was previously stored in the chunk's
* persistent data container.
* <p>
* The data is stored inside the {@code blocks} compound of the chunk's
* persistent data container, using a key formed as {@code X_Y_Z} where
* X, Y, Z are the block's coordinates.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param block the block whose stored data is to be read
* @param consumer the consumer that receives a {@link ReadableNBT} view of the data
*/
public static void readChunkPDC(Block block, Consumer<ReadableNBT> consumer) {
processBlockChunkPDC(block, false, nbt -> {
consumer.accept(nbt);
return null;
});
}
/**
* Reads block‑specific data from the chunk's persistent data container and
* applies a function to produce a result.
* <p>
* The data is stored inside the {@code blocks} compound of the chunk's
* persistent data container, using a key formed as {@code X_Y_Z} where
* X, Y, Z are the block's coordinates.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param block the block whose stored data is to be read
* @param function the function that processes the {@link ReadableNBT} and
* returns a value
* @param <T> the type of the returned value
* @return the result of the function applied to the data
*/
public static <T> T readAndGetChunkPDC(Block block, Function<ReadableNBT, T> function) {
return processBlockChunkPDC(block, false, function::apply);
}
/**
* Modifies block‑specific data stored in the chunk's persistent data container.
* <p>
* The data is stored inside the {@code blocks} compound of the chunk's
* persistent data container, using a key formed as {@code X_Y_Z} where
* X, Y, Z are the block's coordinates.
* <p>
* <b>Caution:</b> the data remains even if the block is broken, replaced, moved,
* or exploded, etc. You should manually remove the data when it is no longer needed.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param block the block whose data is to be modified
* @param consumer the consumer that receives the {@link ReadWriteNBT} and
* applies modifications
*/
public static void modifyChunkPDC(Block block, Consumer<ReadWriteNBT> consumer) {
processBlockChunkPDC(block, true, nbt -> {
consumer.accept(nbt);
return null;
});
}
/**
* Modifies block‑specific data in the chunk's persistent data container and
* returns a result computed from the modified data.
* <p>
* The data is stored inside the {@code blocks} compound of the chunk's
* persistent data container, using a key formed as {@code X_Y_Z} where
* X, Y, Z are the block's coordinates.
* <p>
* <b>Caution:</b> the data remains even if the block is broken, replaced, moved,
* or exploded, etc. You should manually remove the data when it is no longer needed.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param block the block whose data is to be modified
* @param function the function that receives the {@link ReadWriteNBT}, modifies
* it, and returns a value
* @param <T> the type of the returned value
* @return the result of the function after modification
*/
public static <T> T modifyAndGetChunkPDC(Block block, Function<ReadWriteNBT, T> function) {
return processBlockChunkPDC(block, true, function::apply);
}
private static <T> T processBlockChunkPDC(Block block, boolean createIfAbsent, Function<NBTCompound, T> action) {
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
String blockKey = block.getX() + "_" + block.getY() + "_" + block.getZ();
NBTCompound chunkData = new NBTPersistentDataContainer(block.getChunk().getPersistentDataContainer(), !createIfAbsent);
NBTCompound blocksData = createIfAbsent
? chunkData.getOrCreateCompound("blocks")
: chunkData.getCompound("blocks");
NBTCompound blockData;
if (createIfAbsent) {
blockData = blocksData.getOrCreateCompound(blockKey);
} else {
blockData = blocksData == null ? null : blocksData.getCompound(blockKey);
if (blockData == null) {
blockData = new NBTContainer().setReadOnly(true);
}
}
T result = action.apply(blockData);
if (createIfAbsent) {
if (blockData.isEmpty()) {
blocksData.removeKey(blockKey);
}
if (blocksData.isEmpty()) {
chunkData.removeKey("blocks");
}
}
if (result instanceof ReadableNBT || result instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return result;
}
/**
* Reads the chunk's persistent data container.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param chunk the chunk whose persistent data is to be read
* @param consumer the consumer that receives a {@link ReadableNBT} view of the data
*/
public static void readChunkPDC(Chunk chunk, Consumer<ReadableNBT> consumer) {
processChunkPDC(chunk, false, nbt -> {
consumer.accept(nbt);
return null;
});
}
/**
* Reads the chunk's persistent data container and applies a function to produce a result.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param chunk the chunk whose persistent data is to be read
* @param function the function that processes the {@link ReadableNBT} and
* returns a value
* @param <T> the type of the returned value
* @return the result of the function applied to the data
*/
public static <T> T readAndGetChunkPDC(Chunk chunk, Function<ReadableNBT, T> function) {
return processChunkPDC(chunk, false, function::apply);
}
/**
* Modifies the chunk's persistent data container.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param chunk the chunk whose persistent data is to be modified
* @param consumer the consumer that receives the {@link ReadWriteNBT} and
* applies modifications
*/
public static void modifyChunkPDC(Chunk chunk, Consumer<ReadWriteNBT> consumer) {
processChunkPDC(chunk, true, nbt -> {
consumer.accept(nbt);
return null;
});
}
/**
* Modifies the chunk's persistent data container and returns a result computed from the modified data.
* <p>
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
*
* @param chunk the chunk whose persistent data is to be modified
* @param function the function that receives the {@link ReadWriteNBT}, modifies
* it, and returns a value
* @param <T> the type of the returned value
* @return the result of the function after modification
*/
public static <T> T modifyAndGetChunkPDC(Chunk chunk, Function<ReadWriteNBT, T> function) {
return processChunkPDC(chunk, true, function::apply);
}
private static <T> T processChunkPDC(Chunk chunk, boolean createIfAbsent, Function<NBTCompound, T> action) {
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
NBTCompound chunkData = new NBTPersistentDataContainer(chunk.getPersistentDataContainer(), !createIfAbsent);
T result = action.apply(chunkData);
if (result instanceof ReadableNBT || result instanceof ReadableNBTList<?>) {
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
}
return result;
}
}
@@ -7,16 +7,17 @@ import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
/**
* Helper class to store NBT data to Block Locations. Use getData() to get the
* NBT instance. Important notes:
*
* <p>
* - Non BlockEntities can not have NBT data. This stores the data to the chunk
* instead!
*
* <p>
* - The data is really just on the location. If the block gets
* broken/changed/exploded/moved etc., the data is still on that location!
*
* @author tr7zw
*
* @deprecated use methods in {@link NBT} class to read/modify block's NBT
*/
@Deprecated
public class NBTBlock {
private final Block block;
@@ -30,6 +31,13 @@ public class NBTBlock {
nbtChunk = new NBTChunk(block.getChunk());
}
/**
* Gets the block NBT data stored within the chunk's PDC.
*
* @return block NBT data
* @deprecated use methods in {@link NBT} class to read/modify block's NBT
*/
@Deprecated
public NBTCompound getData() {
return nbtChunk.getPersistentDataContainer().getOrCreateCompound("blocks")
.getOrCreateCompound(block.getX() + "_" + block.getY() + "_" + block.getZ());
@@ -5,6 +5,12 @@ import org.bukkit.Chunk;
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
/**
* Helper class to store NBT data to {@link Chunk}'s PDC (persistent data container).
*
* @deprecated use methods in {@link NBT} class to read/modify chunk's nbt
*/
@Deprecated
public class NBTChunk {
private final Chunk chunk;
@@ -5,6 +5,7 @@ import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
@@ -19,6 +20,7 @@ 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.UUIDUtil;
import de.tr7zw.changeme.nbtapi.utils.PathUtil.PathSegment;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.Forge1710Mappings;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
@@ -98,10 +100,13 @@ public class NBTCompound implements ReadWriteNBT {
return readOnlyCache;
}
Object rootnbttag = getCompound();
if (rootnbttag instanceof Optional<?>) {
rootnbttag = ((Optional<Object>) rootnbttag).orElse(null);
}
if (rootnbttag == null) {
return null;
}
if (!NBTReflectionUtil.valideCompound(this))
if (!NBTReflectionUtil.validCompound(this))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = NBTReflectionUtil.getToCompount(rootnbttag, this);
if (readOnly) {
@@ -657,7 +662,9 @@ public class NBTCompound implements ReadWriteNBT {
public void setUUID(String key, UUID value) {
try {
writeLock.lock();
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
setIntArray(key, UUIDUtil.uuidToIntArray(value));
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_UUID, key, value);
} else {
setString(key, value.toString());
@@ -678,10 +685,13 @@ public class NBTCompound implements ReadWriteNBT {
public UUID getUUID(String key) {
try {
readLock.lock();
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)
&& getType(key) == NBTType.NBTTagIntArray) {
NBTType type = getType(key);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4) && type == NBTType.NBTTagIntArray) {
return UUIDUtil.uuidFromIntArray(getIntArray(key));
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)
&& type == NBTType.NBTTagIntArray) {
return (UUID) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_UUID, key);
} else if (getType(key) == NBTType.NBTTagString) {
} else if (type == NBTType.NBTTagString) {
try {
return UUID.fromString(getString(key));
} catch (IllegalArgumentException ex) {
@@ -786,7 +796,7 @@ public class NBTCompound implements ReadWriteNBT {
if (getType(name) != NBTType.NBTTagCompound)
return null;
NBTCompound next = new NBTCompound(this, name, readOnly);
if (NBTReflectionUtil.valideCompound(next))
if (NBTReflectionUtil.validCompound(next))
return next;
return null;
} finally {
@@ -1343,7 +1353,13 @@ public class NBTCompound implements ReadWriteNBT {
Object nbtbase = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
if (nbtbase == null)
return null;
return NBTType.valueOf((byte) ReflectionMethod.COMPOUND_OWN_TYPE.run(nbtbase));
return NBTType.valueOf((byte) ReflectionMethod.COMPOUND_OWN_TYPE_LEGACY.run(nbtbase));
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
if (o == null)
return null;
return NBTType.fromName((String)ReflectionMethod.TAGTYPE_GET_NAME.run(ReflectionMethod.TAGTYPE_OWN_TYPE.run(o)));
}
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_TYPE, name);
if (o == null)
@@ -1460,6 +1476,106 @@ public class NBTCompound implements ReadWriteNBT {
return false;
}
@Override
public NBTCompound extractDifference(ReadableNBT other) {
if (this == other)
return new NBTContainer();
if (other instanceof NBTCompound) {
return saveDiff(new NBTContainer(), this, (NBTCompound) other);
} else {
throw new NbtApiException("Unknown NBT object: " + other);
}
}
private static NBTCompound saveDiff(NBTCompound saveTo, NBTCompound compA, NBTCompound compB) {
for (String key : compA.getKeys()) {
saveDiff(saveTo, compA, compB, key);
}
return saveTo;
}
private static void saveDiff(NBTCompound saveTo, NBTCompound compA, NBTCompound compB, String key) {
boolean typeMismatch = compA.getType(key) != compB.getType(key);
switch (compA.getType(key)) {
case NBTTagByte:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setByte(key, compA.getByte(key));
}
return;
case NBTTagByteArray:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setByteArray(key, compA.getByteArray(key));
}
return;
case NBTTagCompound: {
NBTCompound tmp1 = compA.getCompound(key);
if (tmp1 == null) return;
if (typeMismatch) {
saveTo.addCompound(key).mergeCompound(tmp1);
} else {
NBTCompound tmp2 = compB.getCompound(key);
if (tmp2 == null) {
saveTo.addCompound(key).mergeCompound(tmp1);
return;
}
NBTCompound tmpDiff = tmp1.extractDifference(tmp2);
if (!tmpDiff.getKeys().isEmpty()) {
saveTo.addCompound(key).mergeCompound(tmpDiff);
}
}
return;
}
case NBTTagDouble:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setDouble(key, compA.getDouble(key));
}
return;
case NBTTagEnd:
return; // ??
case NBTTagFloat:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setFloat(key, compA.getFloat(key));
}
return;
case NBTTagInt:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setInteger(key, compA.getInteger(key));
}
return;
case NBTTagIntArray:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setIntArray(key, compA.getIntArray(key));
}
return;
case NBTTagList:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.set(key, NBTReflectionUtil.getEntry(compA, key));
}
return;
case NBTTagLong:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setLong(key, compA.getLong(key));
}
return;
case NBTTagShort:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setShort(key, compA.getShort(key));
}
return;
case NBTTagString:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setString(key, compA.getString(key));
}
return;
case NBTTagLongArray:
if (typeMismatch || !isEqual(compA, compB, key)) {
saveTo.setLongArray(key, compA.getLongArray(key));
}
return;
}
}
private static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
if (compA.getType(key) != compB.getType(key))
return false;
@@ -2,6 +2,7 @@ package de.tr7zw.changeme.nbtapi;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
@@ -56,7 +57,8 @@ public class NBTCompoundList extends NBTList<ReadWriteNBT> implements ReadWriteN
}
}
public NBTCompound addCompound(ReadWriteNBT comp) {
@Override
public ReadWriteNBT addCompound(ReadableNBT comp) {
if (comp instanceof NBTCompound) {
return addCompound((NBTCompound) comp);
}
@@ -21,7 +21,10 @@ public class NBTContainer extends NBTCompound {
/**
* Creates an empty, standalone NBTCompound
*
* @deprecated use {@link NBT#createNBTObject()}
*/
@Deprecated
public NBTContainer() {
super(null, null);
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
@@ -31,7 +34,9 @@ public class NBTContainer extends NBTCompound {
* Takes in any NMS Compound to wrap it
*
* @param nbt
* @deprecated Use NBT.wrapNMSTag
*/
@Deprecated
public NBTContainer(Object nbt) {
super(null, null);
if (nbt == null) {
@@ -47,7 +52,9 @@ public class NBTContainer extends NBTCompound {
* Reads in a NBT InputStream
*
* @param inputsteam
* @deprecated Use NBT.readNBT
*/
@Deprecated
public NBTContainer(InputStream inputsteam) {
super(null, null);
this.nbt = NBTReflectionUtil.readNBT(inputsteam);
@@ -58,7 +65,9 @@ public class NBTContainer extends NBTCompound {
* {@link NbtApiException} in case something goes wrong.
*
* @param nbtString
* @deprecated Use NBT.parseNBT
*/
@Deprecated
public NBTContainer(String nbtString) {
super(null, null);
if (nbtString == null) {
@@ -4,6 +4,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import de.tr7zw.changeme.nbtapi.iface.NBTFileHandle;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
/**
@@ -12,7 +13,7 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
* @author tr7zw
*
*/
public class NBTFile extends NBTCompound {
public class NBTFile extends NBTCompound implements NBTFileHandle {
private final File file;
private Object nbt;
@@ -23,7 +24,9 @@ public class NBTFile extends NBTCompound {
*
* @param file
* @throws IOException
* @deprecated Use NBT.getFileHandle(file)
*/
@Deprecated
public NBTFile(File file) throws IOException {
super(null, null);
if (file == null) {
@@ -43,6 +46,7 @@ public class NBTFile extends NBTCompound {
*
* @throws IOException
*/
@Override
public void save() throws IOException {
try {
getWriteLock().lock();
@@ -55,6 +59,7 @@ public class NBTFile extends NBTCompound {
/**
* @return The File used to store the data
*/
@Override
public File getFile() {
return file;
}
@@ -77,7 +82,9 @@ public class NBTFile extends NBTCompound {
* @param file file to read
* @return NBTCompound holding file's nbt data
* @throws IOException exception
* @deprecated Use NBT.readFile(file)
*/
@Deprecated
public static NBTCompound readFrom(File file) throws IOException {
if (!file.exists())
return new NBTContainer();
@@ -92,7 +99,9 @@ public class NBTFile extends NBTCompound {
* @param file file
* @param nbt NBT data
* @throws IOException exception
* @deprecated Use NBT.writeFile(file, nbt)
*/
@Deprecated
public static void saveTo(File file, NBTCompound nbt) throws IOException {
if (!file.exists()) {
file.getParentFile().mkdirs();
@@ -304,8 +304,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
try {
parent.getWriteLock().lock();
int size = size();
int id = -1;
while ((id = indexOf(o)) != -1) {
int id = indexOf(o);
if (id != -1) {
remove(id);
}
return size != size();
@@ -11,7 +11,11 @@ public class NBTPersistentDataContainer extends NBTCompound {
private final PersistentDataContainer container;
public NBTPersistentDataContainer(PersistentDataContainer container) {
super(null, null);
this(container, false);
}
protected NBTPersistentDataContainer(PersistentDataContainer container, boolean readOnly) {
super(null, null, readOnly);
this.container = container;
}
@@ -9,8 +9,10 @@ import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.block.BlockState;
@@ -18,11 +20,15 @@ import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DynamicOps;
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
import de.tr7zw.changeme.nbtapi.utils.GsonWrapper;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.changeme.nbtapi.utils.ReflectionUtil;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.CodecHelper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
@@ -40,6 +46,10 @@ public class NBTReflectionUtil {
private static Field field_handle = null;
private static Object type_custom_data = null;
private static Object registry_access = null;
public static Codec<Object> itemstack_codec = null;
public static DynamicOps<Object> nbtOps = null;
public static DynamicOps<Object> nbtRegistryOps = null;
public static Object problemReporter = null;
static {
try {
@@ -56,15 +66,22 @@ public class NBTReflectionUtil {
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
try {
Field typeField = ClassWrapper.NMS_DATACOMPONENTS.getClazz().getDeclaredField(
MojangToMapping.getMapping().get("net.minecraft.core.component.DataComponents#CUSTOM_DATA"));
Field typeField = ReflectionUtil.getMappedField(ClassWrapper.NMS_DATACOMPONENTS.getClazz(), "net.minecraft.core.component.DataComponents#CUSTOM_DATA");
type_custom_data = typeField.get(null);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
} catch (Exception e) {
MinecraftVersion.getLogger().log(Level.WARNING,
"Unable to find DataComponents#CUSTOM_DATA, NBTApi will not be able to read/write custom data on 1.20+",
e);
}
try {
Object nmsServer = ReflectionMethod.NMSSERVER_GETSERVER.run(Bukkit.getServer());
registry_access = ReflectionMethod.NMSSERVER_GETREGISTRYACCESS.run(nmsServer);
itemstack_codec = (Codec<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_ITEMSTACK.getClazz(), "net.minecraft.world.item.ItemStack#CODEC").get(null);
nbtOps = (DynamicOps<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_NBTOPS.getClazz(), "net.minecraft.nbt.NbtOps#INSTANCE").get(null);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
nbtRegistryOps = (DynamicOps<Object>) ReflectionMethod.GET_SERIALIZATION_CONTEXT.run(registry_access, nbtOps);
problemReporter = ReflectionUtil.getMappedField(ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), "net.minecraft.util.ProblemReporter#DISCARDING").get(null);
}
} catch (Exception e) {
e.printStackTrace();
}
@@ -154,6 +171,9 @@ public class NBTReflectionUtil {
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
try {
Object workingtag = comp.getResolvedObject();
if (workingtag == null) {
workingtag = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
}
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
} catch (Exception e) {
throw new NbtApiException("Exception while writing NBT!", e);
@@ -210,21 +230,35 @@ public class NBTReflectionUtil {
* @return NMS ItemStack
*/
public static Object convertNBTCompoundtoNMSItem(NBTCompound nbtcompound) {
Object nmsComp = null;
try {
Object nmsComp = getToCompount(nbtcompound.getCompound(), nbtcompound);
nmsComp = getToCompount(nbtcompound.getCompound(), nbtcompound);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
if (nbtcompound.hasTag("tag")) {
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, DataFixerUtil.VERSION1_20_4,
DataFixerUtil.getCurrentVersion());
if (nbtcompound.hasTag("DataVersion", NBTType.NBTTagInt)) {
int dataVersion = nbtcompound.getInteger("DataVersion");
int currentVersion = DataFixerUtil.getCurrentVersion();
if (dataVersion < currentVersion) {
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, dataVersion, currentVersion);
}
} else if (nbtcompound.hasTag("tag") || nbtcompound.hasTag("Count")) {
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, DataFixerUtil.VERSION1_20_4, DataFixerUtil.getCurrentVersion());
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
return CodecHelper.convertNbtToItemStack(nmsComp);
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
Optional<Object> opt = (Optional<Object>) ReflectionMethod.NMSITEM_LOAD_MODERN.run(null,
registry_access, nmsComp);
return opt.orElse(null);
} else {
return ReflectionMethod.NMSITEM_LOAD.run(null, registry_access, nmsComp);
}
return ReflectionMethod.NMSITEM_LOAD.run(null, registry_access, nmsComp);
} else if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_11_R1.getVersionId()) {
return ObjectCreator.NMS_COMPOUNDFROMITEM.getInstance(nmsComp);
} else {
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nmsComp);
}
} catch (Exception e) {
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack!", e);
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack! " + nmsComp, e);
}
}
@@ -236,13 +270,20 @@ public class NBTReflectionUtil {
*/
public static NBTContainer convertNMSItemtoNBTCompound(Object nmsitem) {
try {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
return new NBTContainer(ReflectionMethod.NMSITEM_SAVE_MODERN.run(nmsitem, registry_access));
NBTContainer container;
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
container = new NBTContainer(CodecHelper.convertItemStackToNbt(nmsitem));
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
container = new NBTContainer(ReflectionMethod.NMSITEM_SAVE_MODERN.run(nmsitem, registry_access));
} else {
Object answer = ReflectionMethod.NMSITEM_SAVE.run(nmsitem,
ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance());
return new NBTContainer(answer);
container = new NBTContainer(answer);
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)) {
container.setInteger("DataVersion", DataFixerUtil.getCurrentVersion());
}
return container;
} catch (Exception e) {
throw new NbtApiException("Exception while converting NMS ItemStack to NBTCompound!", e);
}
@@ -273,7 +314,16 @@ public class NBTReflectionUtil {
public static Object getEntityNBTTagCompound(Object nmsEntity) {
try {
Object nbt = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
Object answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
Object answer;
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
Object output = ReflectionMethod.NMS_GET_TAG_VALUE_OUTPUT.run(null, problemReporter, registry_access);
ReflectionMethod.NMS_ENTITY_GET_NBT_1216.run(nmsEntity, output);
answer = ReflectionMethod.NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND.run(output);
} else {
answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
}
if (answer == null)
answer = nbt;
return answer;
@@ -291,7 +341,14 @@ public class NBTReflectionUtil {
*/
public static Object setEntityNBTTag(Object nbtTag, Object nmsEntity) {
try {
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
Object valueInputTag = ReflectionMethod.NMS_GET_TAG_VALUE_INPUT.run(null, problemReporter, registry_access, nbtTag);
ReflectionMethod.NMS_ENTITY_SET_NBT_1216.run(nmsEntity, valueInputTag);
} else {
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
}
return nmsEntity;
} catch (Exception ex) {
throw new NbtApiException("Exception while setting the NBTCompound of an Entity", ex);
@@ -323,7 +380,13 @@ public class NBTReflectionUtil {
}
Object answer = null;
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
Object output = ReflectionMethod.NMS_GET_TAG_VALUE_OUTPUT.run(null, problemReporter, registry_access);
ReflectionMethod.TILEENTITY_GET_NBT_1216.run(o, output);
answer = ReflectionMethod.NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND.run(output);
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
answer = ReflectionMethod.TILEENTITY_GET_NBT_1205.run(o, registry_access);
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
answer = ReflectionMethod.TILEENTITY_GET_NBT_1181.run(o);
@@ -358,7 +421,11 @@ public class NBTReflectionUtil {
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
Object valueInput = ReflectionMethod.NMS_GET_TAG_VALUE_INPUT.run(null, problemReporter, registry_access, comp);
ReflectionMethod.TILEENTITY_SET_NBT_1216.run(o, valueInput);
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
ReflectionMethod.TILEENTITY_SET_NBT_1205.run(o, comp, registry_access);
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
ReflectionMethod.TILEENTITY_SET_NBT.run(o, comp);
@@ -383,7 +450,11 @@ public class NBTReflectionUtil {
public static Object getSubNBTTagCompound(Object compound, String name) {
try {
if ((boolean) ReflectionMethod.COMPOUND_HAS_KEY.run(compound, name)) {
return ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
Object comp = ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
if (comp instanceof Optional) {
return ((Optional<?>) comp).orElse(null);
}
return comp;
} else {
throw new NbtApiException("Tried getting invalid compound '" + name + "' from '" + compound + "'!");
}
@@ -407,7 +478,7 @@ public class NBTReflectionUtil {
if (nbttag == null) {
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp)) {
if (!validCompound(comp)) {
return;
}
Object workingtag = getToCompount(nbttag, comp);
@@ -424,10 +495,13 @@ public class NBTReflectionUtil {
* Checks if the Compound is correctly linked to it's roots
*
* @param comp
* @return true if this is a valide Compound, else false
* @return true if this is a valid Compound, else false
*/
public static boolean valideCompound(NBTCompound comp) {
public static boolean validCompound(NBTCompound comp) {
Object root = comp.getCompound();
if (root instanceof Optional) {
root = ((Optional<?>) root).orElse(null);
}
if (root == null) {
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
@@ -442,9 +516,15 @@ public class NBTReflectionUtil {
structure.add(comp.getName());
comp = comp.getParent();
}
if (nbttag instanceof Optional) {
nbttag = ((Optional<?>) nbttag).orElse(null);
}
while (!structure.isEmpty()) {
String target = structure.pollLast();
nbttag = getSubNBTTagCompound(nbttag, target);
if (nbttag instanceof Optional) {
nbttag = ((Optional<?>) nbttag).orElse(null);
}
if (nbttag == null) {
throw new NbtApiException("Unable to find tag '" + target + "' in " + nbttag);
}
@@ -467,7 +547,7 @@ public class NBTReflectionUtil {
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
if (!validCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = getToCompount(rootnbttag, comp);
try {
@@ -494,7 +574,7 @@ public class NBTReflectionUtil {
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp)) {
if (!validCompound(comp)) {
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
}
Object workingtag = getToCompount(rootnbttag, comp);
@@ -522,7 +602,18 @@ public class NBTReflectionUtil {
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
}
try {
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
Object nbt = null;
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key);
if (nbt instanceof Optional) {
nbt = ((Optional<?>) nbt).orElse(null);
}
if (nbt == null) {
nbt = ClassWrapper.NMS_NBTTAGLIST.getClazz().newInstance();
}
} else {
nbt = ReflectionMethod.COMPOUND_GET_LIST_LEGACY.run(workingtag, key, type.getId());
}
if (clazz == String.class) {
return (NBTList<T>) new NBTStringList(comp, key, type, nbt);
} else if (clazz == NBTListCompound.class) {
@@ -554,6 +645,26 @@ public class NBTReflectionUtil {
}
try {
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
if (nbt instanceof Optional) {
nbt = ((Optional<?>) nbt).orElse(null);
}
if (nbt == null) {
return NBTType.NBTTagEnd;
}
if (new NBTStringList(comp, key, NBTType.NBTTagString, nbt).isEmpty()) {
return NBTType.NBTTagEnd;
}
Object compound = ReflectionMethod.LIST_GET.run(nbt, 0);
if (compound instanceof Optional) {
compound = ((Optional<?>) compound).orElse(null);
}
if (compound == null) {
return NBTType.NBTTagEnd;
}
return NBTType.fromName((String) ReflectionMethod.TAGTYPE_GET_NAME
.run(ReflectionMethod.TAGTYPE_OWN_TYPE.run(compound)));
}
String fieldname = "type";
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
fieldname = "w";
@@ -628,7 +739,7 @@ public class NBTReflectionUtil {
if (rootnbttag == null) {
return;
}
if (!valideCompound(comp))
if (!validCompound(comp))
return;
Object workingtag = getToCompount(rootnbttag, comp);
ReflectionMethod.COMPOUND_REMOVE_KEY.run(workingtag, key);
@@ -667,7 +778,7 @@ public class NBTReflectionUtil {
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
if (!validCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = getToCompount(rootnbttag, comp);
type.run(workingtag, key, data);
@@ -690,7 +801,41 @@ public class NBTReflectionUtil {
if (workingtag == null) {
workingtag = dummyNBT.getCompound();
}
return type.run(workingtag, key);
if (workingtag instanceof Optional<?>) {
workingtag = ((Optional<Object>) workingtag).orElseGet(() -> dummyNBT.getCompound());
}
Object obj = type.run(workingtag, key);
if (obj instanceof Optional<?>) {
return ((Optional<Object>) obj).orElseGet(() -> getDefaultValue(type));
}
return obj;
}
private static Object getDefaultValue(ReflectionMethod type) {
if (type == ReflectionMethod.COMPOUND_GET_STRING) {
return "";
} else if (type == ReflectionMethod.COMPOUND_GET_BYTE) {
return (byte) 0;
} else if (type == ReflectionMethod.COMPOUND_GET_SHORT) {
return (short) 0;
} else if (type == ReflectionMethod.COMPOUND_GET_BOOLEAN) {
return false;
} else if (type == ReflectionMethod.COMPOUND_GET_INT) {
return 0;
} else if (type == ReflectionMethod.COMPOUND_GET_LONG) {
return 0L;
} else if (type == ReflectionMethod.COMPOUND_GET_FLOAT) {
return 0.0f;
} else if (type == ReflectionMethod.COMPOUND_GET_DOUBLE) {
return 0.0d;
} else if (type == ReflectionMethod.COMPOUND_GET_BYTEARRAY) {
return new byte[0];
} else if (type == ReflectionMethod.COMPOUND_GET_INTARRAY) {
return new int[0];
} else if (type == ReflectionMethod.COMPOUND_GET_LONGARRAY) {
return new long[0];
}
return null;
}
}
@@ -2,6 +2,7 @@ package de.tr7zw.changeme.nbtapi;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
@@ -21,7 +22,11 @@ public class NBTStringList extends NBTList<String> {
@Override
public String get(int index) {
try {
return (String) ReflectionMethod.LIST_GET_STRING.run(listObject, index);
Object ret = ReflectionMethod.LIST_GET_STRING.run(listObject, index);
if (ret instanceof Optional<?>) {
return ((Optional<String>) ret).orElse("");
}
return (String) ret;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
@@ -8,14 +8,16 @@ 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), NBTTagString(8), NBTTagList(9), NBTTagCompound(10), NBTTagIntArray(11), NBTTagLongArray(12);
NBTTagEnd(0, ""), NBTTagByte(1, "BYTE"), NBTTagShort(2, "SHORT"), NBTTagInt(3, "INT"), NBTTagLong(4, "LONG"), NBTTagFloat(5, "FLOAT"), NBTTagDouble(6, "DOUBLE"),
NBTTagByteArray(7, "BYTE[]"), NBTTagString(8, "STRING"), NBTTagList(9, "LIST"), NBTTagCompound(10, "COMPOUND"), NBTTagIntArray(11, "INT[]"), NBTTagLongArray(12, "LONG[]");
NBTType(int i) {
NBTType(int i, String name) {
id = i;
this.name = name;
}
private final int id;
private final String name;
/**
* @return Id used by Minecraft internally
@@ -23,6 +25,13 @@ public enum NBTType {
public int getId() {
return id;
}
/**
* @return Name of the NBTType
*/
public String getName() {
return name;
}
/**
* @param id Internal Minecraft id
@@ -35,4 +44,11 @@ public enum NBTType {
return NBTType.NBTTagEnd;
}
public static NBTType fromName(String name) {
for (NBTType t : values())
if (t.getName().equals(name))
return t;
return NBTType.NBTTagEnd;
}
}
@@ -0,0 +1,20 @@
package de.tr7zw.changeme.nbtapi.iface;
import java.io.File;
import java.io.IOException;
public interface NBTFileHandle extends ReadWriteNBT {
/**
* Saves the data to the file
*
* @throws IOException
*/
void save() throws IOException;
/**
* @return The File used to store the data
*/
File getFile();
}
@@ -10,6 +10,15 @@ public interface ReadWriteNBTCompoundList extends ReadableNBTList<ReadWriteNBT>
* @return A new instance of the class.
*/
ReadWriteNBT addCompound();
/**
* Adds a copy of the Compound to the end of the List and returns it. When null
* is given, a new Compound will be created
*
* @param comp
* @return
*/
ReadWriteNBT addCompound(ReadableNBT comp);
/**
* Removes the element at the specified position in this list
@@ -174,6 +174,15 @@ public interface ReadableNBT {
*/
Set<String> getKeys();
/**
* Checks whether this NBT is empty (has no stored data).
*
* @return whether this NBT is empty
*/
default boolean isEmpty() {
return getKeys().isEmpty();
}
/**
* @param name
* @return The Compound instance or null
@@ -274,7 +283,7 @@ public interface ReadableNBT {
* {@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
* @param key Path key, separated by '.'. For example: "foo.bar.baz". Dots can
* be escaped with a backslash.
* @param type data type
* @param <T> value type
@@ -290,7 +299,7 @@ public interface ReadableNBT {
* {@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".
* @param key Path key, separated by '.'. For example: "foo.bar.baz".
* Dots can be escaped with a backslash.
* @param defaultValue default non-null value
* @param <T> value type
@@ -302,7 +311,7 @@ public interface ReadableNBT {
* Returns the resolved Compound if exists, or null.
* <p>
*
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
* @param key Path key, separated by '.'. For example: "foo.bar.baz". Dots can
* be escaped with a backslash.
* @return The resolved value if exists, or null.
*/
@@ -344,6 +353,17 @@ public interface ReadableNBT {
*/
void writeCompound(OutputStream stream);
/**
* Compares this readable nbt to the other one
* and returns the difference.
* <p>Note: the result will only contain data that is present
* in this readable nbt and is missing/different in the provided one.
*
* @param other other readable nbt
* @return difference between readable nbts
*/
ReadWriteNBT extractDifference(ReadableNBT other);
/**
* @return The NBT as printable NBT-Json.
*/
@@ -5,41 +5,76 @@ import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.DynamicOps;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTReflectionUtil;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
public class DataFixerUtil {
// these values can be found in DetectedVersion inside mc
// Finding pre 1.12.2 values is left as an exercise for the reader,
// as DetectedVersion is not a thing there
// https://minecraft.wiki/w/Data_version
public static final int VERSION1_12R1 = 1343;
public static final int VERSION1_16R1 = 2586;
public static final int VERSION1_17R1 = 2730;
public static final int VERSION1_18R1 = 2975;
public static final int VERSION1_19R1 = 3120;
public static final int VERSION1_19R3 = 3337;
public static final int VERSION1_20R1 = 3465;
public static final int VERSION1_20R2 = 3578;
public static final int VERSION1_20R3 = 3700;
public static final int VERSION1_20R4 = 3837;
public static final int VERSION1_21R1 = 3953;
public static final int VERSION1_21R2 = 4080;
public static final int VERSION1_21R3 = 4189;
public static final int VERSION1_21R4 = 4323;
public static final int VERSION1_21R5 = 4435;
public static final int VERSION1_21R6 = 4554;
public static final int VERSION1_21R7 = 4671;
public static final int VERSION_26_1 = 4786;
public static final int VERSION_26_2 = 4903;
public static final int VERSION_26_3 = 5023;
// There was a mixup between version numbers and revisions, kept for compatibility, use the above revision numbers
@Deprecated
public static final int VERSION1_12_2 = 1343;
@Deprecated
public static final int VERSION1_16_5 = 2586;
@Deprecated
public static final int VERSION1_17_1 = 2730;
@Deprecated
public static final int VERSION1_18_2 = 2975;
@Deprecated
public static final int VERSION1_19_2 = 3120;
@Deprecated
public static final int VERSION1_19_4 = 3337;
@Deprecated
public static final int VERSION1_20_1 = 3465;
@Deprecated
public static final int VERSION1_20_2 = 3578;
@Deprecated
public static final int VERSION1_20_4 = 3700;
@Deprecated
public static final int VERSION1_20_5 = 3837;
// the 1.21 versions use Revision instead of patch versions, unlike the other above. kept for compatibility
@Deprecated
public static final int VERSION1_21 = 3953;
@Deprecated
public static final int VERSION1_21_2 = 4080;
@Deprecated
public static final int VERSION1_21_3 = 4189;
@Deprecated
public static final int VERSION1_21_4 = 4323;
@Deprecated
public static final int VERSION1_21_5 = 4435;
@SuppressWarnings("unchecked")
public static Object fixUpRawItemData(Object nbt, int fromVersion, int toVersion)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
DataFixer dataFixer = (DataFixer) ReflectionMethod.GET_DATAFIXER.run(null);
TypeReference itemStackReference = (TypeReference) ClassWrapper.NMS_REFERENCES.getClazz()
.getField(MojangToMapping.getMapping().get("net.minecraft.util.datafix.fixes.References#ITEM_STACK"))
.get(null);
DynamicOps<Object> nbtOps = (DynamicOps<Object>) ClassWrapper.NMS_NBTOPS.getClazz()
.getField(MojangToMapping.getMapping().get("net.minecraft.nbt.NbtOps#INSTANCE")).get(null);
TypeReference itemStackReference = (TypeReference) ReflectionUtil.getMappedField(ClassWrapper.NMS_REFERENCES.getClazz(),"net.minecraft.util.datafix.fixes.References#ITEM_STACK").get(null);
DynamicOps<Object> nbtOps = (DynamicOps<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_NBTOPS.getClazz(), "net.minecraft.nbt.NbtOps#INSTANCE").get(null);
Dynamic<Object> fixed = dataFixer.update(itemStackReference, new Dynamic<Object>(nbtOps, nbt), fromVersion,
toVersion);
return fixed.getValue();
@@ -47,9 +82,16 @@ public class DataFixerUtil {
public static ReadWriteNBT fixUpItemData(ReadWriteNBT nbt, int fromVersion, int toVersion)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
return new NBTContainer(fixUpRawItemData(
ReadWriteNBT tag = NBT.wrapNMSTag(fixUpRawItemData(
NBTReflectionUtil.getToCompount(((NBTCompound) nbt).getCompound(), ((NBTCompound) nbt)), fromVersion,
toVersion));
tag.setInteger("DataVersion", toVersion);
return tag;
}
public static ReadWriteNBT fixUpItemData(ReadWriteNBT nbt, int fromVersion)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
return fixUpItemData(nbt, fromVersion, getCurrentVersion());
}
/**
@@ -62,26 +104,44 @@ public class DataFixerUtil {
* @return
*/
public static int getCurrentVersion() {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R1)) {
return VERSION1_21;
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_3)) {
return VERSION_26_3;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_2)) {
return VERSION_26_2;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_1)) {
return VERSION_26_1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R7)) {
return VERSION1_21R7;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
return VERSION1_21R6;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
return VERSION1_21R5;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
return VERSION1_21R4;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R3)) {
return VERSION1_21R3;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R2)) {
return VERSION1_21R2;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R1)) {
return VERSION1_21R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
return VERSION1_20_5;
return VERSION1_20R4;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R3)) {
return VERSION1_20_4;
return VERSION1_20R3;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R1)) {
return VERSION1_20_1;
return VERSION1_20R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R3)) {
return VERSION1_19_4;
return VERSION1_19R3;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R1)) {
return VERSION1_19_2;
return VERSION1_19R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
return VERSION1_18_2;
return VERSION1_18R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
return VERSION1_17_1;
return VERSION1_17R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
return VERSION1_16_5;
return VERSION1_16R1;
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)) {
return VERSION1_12_2;
return VERSION1_12R1;
}
throw new NbtApiException(
"Trying to update data *to* a version before 1.12.2? Something is probably going wrong, contact the plugin author.");
@@ -1,12 +1,16 @@
package de.tr7zw.changeme.nbtapi.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
import javax.annotation.Nullable;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.PropertyMap;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
@@ -14,15 +18,42 @@ import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
public class GameprofileUtil {
private static Method GET_NAME = null;
private static Method GET_ID = null;
private static Method GET_VALUE = null;
private static Method GET_SIGNATURE = null;
private static Method GET_PROPERTY_NAME = null;
private static Method GET_PROPERTIES = null;
static {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
GET_NAME = GameProfile.class.getDeclaredMethod("name");
GET_ID = GameProfile.class.getDeclaredMethod("id");
GET_VALUE = com.mojang.authlib.properties.Property.class.getDeclaredMethod("value");
GET_SIGNATURE = com.mojang.authlib.properties.Property.class.getDeclaredMethod("signature");
GET_PROPERTY_NAME = com.mojang.authlib.properties.Property.class.getDeclaredMethod("name");
GET_PROPERTIES = GameProfile.class.getDeclaredMethod("properties");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
@Nullable
public static GameProfile readGameProfile(ReadableNBT arg) {
String string = null;
UUID uUID = null;
if (arg.hasTag("Name") && arg.getType("Name") == NBTType.NBTTagString) {
string = arg.getString("Name");
} else if (arg.hasTag("name") && arg.getType("name") == NBTType.NBTTagString) {
string = arg.getString("name");
}
if (arg.hasTag("Id") && arg.getType("Id") == NBTType.NBTTagIntArray && arg.getIntArray("Id").length == 4) {
uUID = arg.getUUID("Id");
} else if (arg.hasTag("id") && arg.getType("id") == NBTType.NBTTagIntArray
&& arg.getIntArray("id").length == 4) {
uUID = arg.getUUID("id");
}
try {
GameProfile gameProfile = new GameProfile(uUID, string);
@@ -43,6 +74,21 @@ public class GameprofileUtil {
}
}
}
} else if (arg.getType("properties") == NBTType.NBTTagList) { // 1.20.5+
ReadableNBTList<ReadWriteNBT> listTag = arg.getCompoundList("properties");
for (int i = 0; i < listTag.size(); ++i) {
ReadableNBT compoundTag2 = listTag.get(i);
String string2 = compoundTag2.getString("name");
String string3 = compoundTag2.getString("value");
if (compoundTag2.hasTag("signature")
&& compoundTag2.getType("signature") == NBTType.NBTTagString) {
gameProfile.getProperties().put(string2, new com.mojang.authlib.properties.Property(string2,
string3, compoundTag2.getString("signature")));
} else {
gameProfile.getProperties().put(string2,
new com.mojang.authlib.properties.Property(string2, string3));
}
}
}
return gameProfile;
} catch (Throwable var11) {
@@ -51,26 +97,115 @@ public class GameprofileUtil {
}
public static ReadWriteNBT writeGameProfile(ReadWriteNBT arg, GameProfile gameProfile) {
if (!(gameProfile.getName() == null || gameProfile.getName().isEmpty())) {
arg.setString("Name", gameProfile.getName());
String name = getName(gameProfile);
if (!(name == null || name.isEmpty())) {
String nameKey = MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) ? "name" : "Name";
arg.setString(nameKey, getName(gameProfile));
}
if (gameProfile.getId() != null) {
arg.setUUID("Id", gameProfile.getId());
UUID id = getId(gameProfile);
if (id != null) {
String idKey = MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) ? "id" : "Id";
arg.setUUID(idKey, id);
}
if (!gameProfile.getProperties().isEmpty()) {
ReadWriteNBT compoundTag = arg.getOrCreateCompound("Properties");
for (String string : gameProfile.getProperties().keySet()) {
ReadWriteNBTCompoundList list = compoundTag.getCompoundList(string);
for (com.mojang.authlib.properties.Property property : gameProfile.getProperties().get(string)) {
ReadWriteNBT tag = list.addCompound();
tag.setString("Value", property.getValue());
if (property.hasSignature()) {
tag.setString("Signature", property.getSignature());
PropertyMap properties = getProperties(gameProfile);
if (!properties.isEmpty()) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
ReadWriteNBTCompoundList list = arg.getCompoundList("properties");
for (com.mojang.authlib.properties.Property property : properties.values()) {
ReadWriteNBT tag = list.addCompound();
tag.setString("name", getPropertyName(property));
tag.setString("value", getValue(property));
if (property.hasSignature()) {
tag.setString("signature", getSignature(property));
}
}
} else {
ReadWriteNBT compoundTag = arg.getOrCreateCompound("Properties");
for (String string : gameProfile.getProperties().keySet()) {
ReadWriteNBTCompoundList list = compoundTag.getCompoundList(string);
for (com.mojang.authlib.properties.Property property : properties.get(string)) {
ReadWriteNBT tag = list.addCompound();
tag.setString("Value", getValue(property));
if (property.hasSignature()) {
tag.setString("Signature", getSignature(property));
}
}
}
}
}
return arg;
}
private static String getName(GameProfile profile) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return GET_NAME.invoke(profile).toString();
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get GameProfile name via reflection", e);
}
} else {
return profile.getName();
}
}
private static UUID getId(GameProfile profile) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return (UUID) GET_ID.invoke(profile);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get GameProfile id via reflection", e);
}
} else {
return profile.getId();
}
}
private static String getValue(com.mojang.authlib.properties.Property property) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return GET_VALUE.invoke(property).toString();
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get Property value via reflection", e);
}
} else {
return property.getValue();
}
}
private static String getSignature(com.mojang.authlib.properties.Property property) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return GET_SIGNATURE.invoke(property).toString();
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get Property signature via reflection", e);
}
} else {
return property.getSignature();
}
}
private static String getPropertyName(com.mojang.authlib.properties.Property property) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return GET_PROPERTY_NAME.invoke(property).toString();
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get Property name via reflection", e);
}
} else {
return property.getName();
}
}
private static PropertyMap getProperties(GameProfile profile) {
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
try {
return (PropertyMap) GET_PROPERTIES.invoke(profile);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new NbtApiException("Failed to get GameProfile properties via reflection", e);
}
} else {
return profile.getProperties();
}
}
}
@@ -1,881 +0,0 @@
/*
* 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;
}
}
}
}
@@ -2,13 +2,20 @@ package de.tr7zw.changeme.nbtapi.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.DrilldownPie;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
/**
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
* other classes,registers bStats and checks rather Maven shading was done
@@ -24,11 +31,15 @@ public enum MinecraftVersion {
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_R3(1193, true), MC1_20_R1(1201, true), MC1_20_R2(1202, true), MC1_20_R3(1203, true),
MC1_20_R4(1204, true), MC1_21_R1(1211, true);
MC1_20_R4(1204, true), MC1_21_R1(1211, true), MC1_21_R2(1212, true), MC1_21_R3(1213, true), MC1_21_R4(1214, true),
MC1_21_R5(1215, true), MC1_21_R6(1216, true), MC1_21_R7(1217, true), MC26_1(260100, true), MC26_2(260200, true),
MC26_3(260300, true);
private static MinecraftVersion version;
private static Boolean hasGsonSupport;
private static Boolean isForgePresent;
private static Boolean isNeoForgePresent;
private static Boolean isFabricPresent;
private static Boolean isFoliaPresent;
private static boolean bStatsDisabled = false;
private static boolean disablePackageWarning = false;
@@ -39,13 +50,12 @@ public enum MinecraftVersion {
private static Logger logger = Logger.getLogger("NBTAPI");
// NBT-API Version
protected static final String VERSION = "2.13.2";
protected static final String VERSION = "2.16.1";
private final int versionId;
private final boolean mojangMapping;
// TODO: not nice
@SuppressWarnings("serial")
private static final Map<String, MinecraftVersion> VERSION_TO_REVISION = new HashMap<String, MinecraftVersion>() {
{
this.put("1.20", MC1_20_R1);
@@ -57,6 +67,19 @@ public enum MinecraftVersion {
this.put("1.20.6", MC1_20_R4);
this.put("1.21", MC1_21_R1);
this.put("1.21.1", MC1_21_R1);
this.put("1.21.2", MC1_21_R2);
this.put("1.21.3", MC1_21_R2);
this.put("1.21.4", MC1_21_R3);
this.put("1.21.5", MC1_21_R4);
this.put("1.21.6", MC1_21_R5);
this.put("1.21.7", MC1_21_R5);
this.put("1.21.8", MC1_21_R5);
this.put("1.21.9", MC1_21_R6);
this.put("1.21.10", MC1_21_R6);
this.put("1.21.11", MC1_21_R7);
this.put("26.1", MC26_1);
this.put("26.2", MC26_2);
this.put("26.3", MC26_3);
}
};
@@ -138,8 +161,22 @@ public enum MinecraftVersion {
} catch (Exception ex) {
logger.info("[NBTAPI] Found Minecraft: " + Bukkit.getServer().getBukkitVersion().split("-")[0]
+ "! Trying to find NMS support");
version = VERSION_TO_REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0],
MinecraftVersion.UNKNOWN);
version = VERSION_TO_REVISION.get(Bukkit.getServer().getBukkitVersion().split("-")[0]);
if (version == null) {
// check for modern versions with the new versioning scheme, also the new paper version format
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0].split(".build")[0];
for (Entry<String, MinecraftVersion> entry : VERSION_TO_REVISION.entrySet()) {
if (versionString.startsWith(entry.getKey()) && version == null) {
version = entry.getValue();
// pick the highest revision that matches the version string, in case 26.1.3 is somehow different to 26.1
} else if (versionString.startsWith(entry.getKey()) && entry.getValue().getVersionId() > version.getVersionId()) {
version = entry.getValue();
}
}
}
if (version == null) {
version = UNKNOWN;
}
}
if (version != UNKNOWN) {
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
@@ -158,13 +195,70 @@ public enum MinecraftVersion {
}
private static void init() {
// Maven's Relocate is clever and changes strings, too. So we have to use this
// 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' });
try {
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);
Metrics metrics = new Metrics((JavaPlugin) plugin, 1058);
metrics.addCustomChart(new SimplePie("nbtapi_version", () -> {
return VERSION;
}));
metrics.addCustomChart(new DrilldownPie("nms_version", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();
Map<String, Integer> entry = new HashMap<>();
entry.put(Bukkit.getName(), 1);
map.put(getVersion().name(), entry);
return map;
}));
metrics.addCustomChart(new SimplePie("shaded", () -> {
return Boolean.toString(!"NBTAPI".equals(VersionChecker.getPlugin()));
}));
metrics.addCustomChart(new SimplePie("server_software", () -> {
return Bukkit.getName();
}));
metrics.addCustomChart(new SimplePie("parent_plugin", () -> {
return VersionChecker.getPluginforBStats();
}));
metrics.addCustomChart(new SimplePie("parent_plugin_type", () -> {
return VersionChecker.getPluginType();
}));
metrics.addCustomChart(new SimplePie("special_environment", () -> {
if (isFoliaPresent()) {
return "Folia";
} else if (isForgePresent()) {
return "Forge";
} else if (isFabricPresent()) {
return "Fabric";
} else if (isNeoForgePresent()) {
return "NeoForge";
} else {
return "None";
}
}));
metrics.addCustomChart(new SimplePie("bindings_check", () -> {
boolean failedBinding = false;
for (ClassWrapper c : ClassWrapper.values()) {
if (c.isEnabled() && c.getClazz() == null) {
failedBinding = true;
}
}
for (ReflectionMethod method : ReflectionMethod.values()) {
if (method.isCompatible() && !method.isLoaded()) {
failedBinding = true;
}
}
return failedBinding ? "Failed" : "Pass";
}));
} else if (plugin == null) {
getLogger().info("[NBTAPI] Unable to create a bStats instance!!");
}
@@ -181,12 +275,6 @@ public enum MinecraftVersion {
logger.log(Level.WARNING, "[NBTAPI] Error while checking for updates! Error: " + ex.getMessage());
}
}).start();
// Maven's Relocate is clever and changes strings, too. So we have to use this
// 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 -#########################################");
@@ -246,6 +334,22 @@ public enum MinecraftVersion {
return hasGsonSupport;
}
/**
* @return True, if Fabric is present
*/
public static boolean isFabricPresent() {
if (isFabricPresent != null) {
return isFabricPresent;
}
try {
logger.info("[NBTAPI] Found Fabric: " + Class.forName("net.fabricmc.api.ModInitializer"));
isFabricPresent = true;
} catch (Exception ex) {
isFabricPresent = false;
}
return isFabricPresent;
}
/**
* @return True, if Forge is present
*/
@@ -263,6 +367,22 @@ public enum MinecraftVersion {
}
return isForgePresent;
}
/**
* @return True, if NeoForge is present
*/
public static boolean isNeoForgePresent() {
if (isNeoForgePresent != null) {
return isNeoForgePresent;
}
try {
logger.info("[NBTAPI] Found NeoForge: " + Class.forName("net.neoforged.neoforge.common.NeoForge"));
isNeoForgePresent = true;
} catch (Exception ex) {
isNeoForgePresent = false;
}
return isNeoForgePresent;
}
/**
* @return True, if Folia is present
@@ -0,0 +1,41 @@
package de.tr7zw.changeme.nbtapi.utils;
import java.util.Optional;
import org.bukkit.inventory.ItemStack;
import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
public class NBTJsonUtil {
/**
* 1.20.3-1.21.4 only. Used to convert items into Json, used in Chat Hover Components.
*
* @param itemStack
* @return
* @throws NbtApiException
*/
@SuppressWarnings("unchecked")
public static JsonElement itemStackToJson(ItemStack itemStack) {
try {
Codec<Object> itemStackCodec = (Codec<Object>) ClassWrapper.NMS_ITEMSTACK.getClazz()
.getField(MojangToMapping.getMapping().get("net.minecraft.world.item.ItemStack#CODEC")).get(null);
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, itemStack);
DataResult<JsonElement> result = itemStackCodec.encode(stack, JsonOps.INSTANCE,
JsonOps.INSTANCE.emptyMap());
Optional<JsonElement> opt = (Optional<JsonElement>) result.getClass().getMethod("result").invoke(result);
return opt.orElse(null);
} catch (Exception ex) {
throw new NbtApiException("Error trying to get Json of an ItemStack.", ex);
}
}
}
@@ -1,53 +1,24 @@
package de.tr7zw.changeme.nbtapi.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
public final class ReflectionUtil {
private static Field field_modifiers;
static {
public static Field getMappedField(Class<?> clazz, String mapping) {
String mojmapName = mapping.split("#")[1];
try {
field_modifiers = Field.class.getDeclaredField("modifiers");
field_modifiers.setAccessible(true);
} catch (NoSuchFieldException ex) {
try {
// This hacky workaround is for newer jdk versions 11+?
Method fieldGetter = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
fieldGetter.setAccessible(true);
Field[] fields = (Field[]) fieldGetter.invoke(Field.class, false);
for (Field f : fields)
if (f.getName().equals("modifiers")) {
field_modifiers = f;
field_modifiers.setAccessible(true);
break;
}
} catch (Exception e) {
throw new NbtApiException(e);
}
return clazz.getField(mojmapName);
} catch (NoSuchFieldException | SecurityException e) {
// not Mojamp, try remapped
}
if (field_modifiers == null) {
throw new NbtApiException("Unable to init the modifiers Field.");
try {
return clazz.getDeclaredField(MojangToMapping.getMapping().get(mapping));
} catch (Exception e) {
throw new NbtApiException("Unable to find field " + mapping + " in class " + clazz.getName(), e);
}
}
public static Field makeNonFinal(Field field) throws IllegalArgumentException, IllegalAccessException {
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
field_modifiers.set(field, mods & ~Modifier.FINAL);
}
return field;
}
public static void setFinal(Object obj, Field field, Object newValue)
throws IllegalArgumentException, IllegalAccessException {
field.setAccessible(true);
field = makeNonFinal(field);
field.set(obj, newValue);
}
}
@@ -109,7 +109,20 @@ public class VersionChecker {
protected static String getPlugin() {
ClassLoader classLoader = VersionChecker.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("plugin.yml");
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
if (inputStream != null) {
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
return pluginYml.getString("name");
} catch (IOException e) {
// ignored
} catch (IllegalArgumentException e) {
// This can happen if the paper-plugin.yml is not formatted correctly
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading paper-plugin.yml: " + e.getMessage());
}
}
inputStream = classLoader.getResourceAsStream("plugin.yml");
if (inputStream != null) {
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
@@ -117,9 +130,67 @@ public class VersionChecker {
return pluginYml.getString("name");
} catch (IOException e) {
// ignored
} catch (IllegalArgumentException e) {
// This can happen if the plugin.yml is not formatted correctly
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading plugin.yml: " + e.getMessage());
}
}
return NBTItem.class.getPackage().getName();
}
protected static String getPluginforBStats() {
ClassLoader classLoader = VersionChecker.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
if (inputStream != null) {
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
return pluginYml.getString("name");
} catch (IOException e) {
// ignored
} catch (IllegalArgumentException e) {
// This can happen if the paper-plugin.yml is not formatted correctly
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading paper-plugin.yml: " + e.getMessage());
}
}
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
} catch (IllegalArgumentException e) {
// This can happen if the plugin.yml is not formatted correctly
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading plugin.yml: " + e.getMessage());
}
}
return "UnknownPlugin";
}
protected static String getPluginType() {
ClassLoader classLoader = VersionChecker.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// ignored
}
return "PaperPlugin";
}
inputStream = classLoader.getResourceAsStream("plugin.yml");
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// ignored
}
return "SpigotPlugin";
}
return "UnknownPlugin";
}
}
@@ -24,6 +24,7 @@ public enum ClassWrapper {
CRAFT_PERSISTENTDATACONTAINER(PackageWrapper.CRAFTBUKKIT, "persistence.CraftPersistentDataContainer",
MinecraftVersion.MC1_14_R1, null),
NMS_NBTBASE(PackageWrapper.NMS, "NBTBase", null, null, "net.minecraft.nbt", "net.minecraft.nbt.Tag"),
NMS_TAGTYPE(PackageWrapper.NMS, "NBTTagType", MinecraftVersion.MC1_21_R4, null, "net.minecraft.nbt", "net.minecraft.nbt.TagType"),
NMS_NBTTAGSTRING(PackageWrapper.NMS, "NBTTagString", null, null, "net.minecraft.nbt",
"net.minecraft.nbt.StringTag"),
NMS_NBTTAGINT(PackageWrapper.NMS, "NBTTagInt", null, null, "net.minecraft.nbt", "net.minecraft.nbt.IntTag"),
@@ -85,6 +86,18 @@ public enum ClassWrapper {
"net.minecraft.util.datafix.fixes", "net.minecraft.util.datafix.fixes.References"),
NMS_NBTOPS(PackageWrapper.NMS, "DynamicOpsNBT", MinecraftVersion.MC1_20_R4, null,
"net.minecraft.nbt", "net.minecraft.nbt.NbtOps"),
NMS_PROBLEM_REPORTER(PackageWrapper.NMS, "ProblemReporter", MinecraftVersion.MC1_21_R5, null,
"net.minecraft.util", "net.minecraft.util.ProblemReporter"),
NMS_TAG_VALUE_INPUT(PackageWrapper.NMS, "TagValueInput", MinecraftVersion.MC1_21_R5, null,
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.TagValueInput"),
NMS_VALUE_INPUT(PackageWrapper.NMS, "ValueInput", MinecraftVersion.MC1_21_R5, null,
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.ValueInput"),
NMS_TAG_VALUE_OUTPUT(PackageWrapper.NMS, "TagValueOutput", MinecraftVersion.MC1_21_R5, null,
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.TagValueOutput"),
NMS_VALUE_OUTPUT(PackageWrapper.NMS, "ValueOutput", MinecraftVersion.MC1_21_R5, null,
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.ValueOutput"),
NMS_DYNAMICOPS(PackageWrapper.NONE, "DynamicOps", MinecraftVersion.MC1_21_R5, null,
"com.mojang.serialization", "com.mojang.serialization.DynamicOps"),
GAMEPROFILE(PackageWrapper.NONE, "com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3, null);
private Class<?> clazz;
@@ -0,0 +1,35 @@
package de.tr7zw.changeme.nbtapi.utils.nmsmappings;
import java.util.Objects;
import java.util.Optional;
import de.tr7zw.changeme.nbtapi.NBTReflectionUtil;
import de.tr7zw.changeme.nbtapi.NbtApiException;
public class CodecHelper {
public static Object convertItemStackToNbt(Object itemStack) {
Object result = null;
try {
// FIXME caching, this has 0 exception handling
result = NBTReflectionUtil.itemstack_codec.encodeStart(NBTReflectionUtil.nbtRegistryOps, itemStack);
Objects.requireNonNull(result);
return ((Optional<Object>)result.getClass().getMethod("result").invoke(result)).get();
} catch (Exception e) {
throw new NbtApiException("Failed to convert ItemStack to NBT. " + result + " " + itemStack, e);
}
}
public static Object convertNbtToItemStack(Object nbt) {
Object result = null;
try {
// FIXME caching, this has 0 exception handling
result = NBTReflectionUtil.itemstack_codec.parse(NBTReflectionUtil.nbtRegistryOps, nbt);
Objects.requireNonNull(result);
return ((Optional<Object>)result.getClass().getMethod("result").invoke(result)).get();
} catch (Exception e) {
throw new NbtApiException("Failed to convert NBT to ItemStack. " + result + " " + nbt, e);
}
}
}
@@ -56,8 +56,8 @@ public class Forge1710Mappings {
methodMap.put("COMPOUND_MERGE", "merge");// Only present on Crucible
methodMap.put("COMPOUND_SET", "func_74782_a");
methodMap.put("COMPOUND_GET", "func_74781_a");
methodMap.put("COMPOUND_GET_LIST", "func_150295_c");
methodMap.put("COMPOUND_OWN_TYPE", "func_74732_a");
methodMap.put("COMPOUND_GET_LIST_LEGACY", "func_150295_c");
methodMap.put("COMPOUND_OWN_TYPE_LEGACY", "func_74732_a");
methodMap.put("COMPOUND_GET_FLOAT", "func_74760_g");
methodMap.put("COMPOUND_GET_STRING", "func_74779_i");
methodMap.put("COMPOUND_GET_INT", "func_74762_e");
@@ -1,5 +1,6 @@
package de.tr7zw.changeme.nbtapi.utils.nmsmappings;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -145,6 +146,7 @@ public class MojangToMapping {
put("net.minecraft.world.entity.Entity#getEncodeId()", "bw");
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId()", "p");
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "r");
put("net.minecraft.world.item.ItemStack#CODEC", "a");
}
};
@@ -169,6 +171,7 @@ public class MojangToMapping {
put("net.minecraft.util.datafix.DataFixers#getDataFixer()", "a");
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "t");
put("net.minecraft.nbt.NbtOps#INSTANCE", "a");
put("net.minecraft.world.item.ItemStack#CODEC", "b");
}
};
@@ -183,10 +186,156 @@ public class MojangToMapping {
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R2 = new HashMap<String, String>() {
{
putAll(MC1_21R1);
put("net.minecraft.server.MinecraftServer#registryAccess()", "ba");
put("net.minecraft.world.entity.Entity#getEncodeId()", "bK");
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "m");
put("net.minecraft.world.item.ItemStack#CODEC", "a");
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R3 = new HashMap<String, String>() {
{
putAll(MC1_21R2);
put("net.minecraft.world.item.component.CustomData#copyTag()", "d");
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R4 = new HashMap<String, String>() {
{
putAll(MC1_21R3);
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "d");
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "i");
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "b");
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "h");
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "j");
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "g");
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "a");
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "f");
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "l");
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "e");
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "k");
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "m");
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "c");
put("net.minecraft.nbt.ListTag#getString(int)", "m");
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.nbt.CompoundTag)", "h");
put("net.minecraft.world.entity.Entity#getEncodeId()", "bI");
put("net.minecraft.world.entity.Entity#load(net.minecraft.nbt.CompoundTag)", "i");
put("net.minecraft.nbt.Tag#getType()", "c");
put("net.minecraft.nbt.TagType#getName()", "a");
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String)", "o");
put("net.minecraft.world.item.ItemStack#parse(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.Tag)", "a");
put("net.minecraft.world.item.ItemStack#CODEC", "b");
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R5 = new HashMap<String, String>() {
{
putAll(MC1_21R3);
put("net.minecraft.core.HolderLookup$Provider#createSerializationContext(com.mojang.serialization.DynamicOps)", "a");
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "j");
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "h");
put("net.minecraft.nbt.CompoundTag#keySet()", "e");
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "f");
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String)", "o");
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "e");
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "m");
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "c");
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "d");
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "k");
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "a");
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "i");
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "g");
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "l");
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "b");
put("net.minecraft.nbt.ListTag#getString(int)", "m");
put("net.minecraft.nbt.Tag#getType()", "c");
put("net.minecraft.nbt.TagParser#parseCompoundFully(java.lang.String)", "a");
put("net.minecraft.nbt.TagType#getName()", "a");
put("net.minecraft.world.entity.Entity#load(net.minecraft.world.level.storage.ValueInput)", "e");
put("net.minecraft.world.entity.Entity#getEncodeId()", "bN");
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.world.level.storage.ValueOutput)", "d");
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId(net.minecraft.world.level.storage.ValueOutput)", "d");
put("net.minecraft.world.level.block.entity.BlockEntity#loadWithComponents(net.minecraft.world.level.storage.ValueInput)", "b");
put("net.minecraft.world.level.storage.TagValueInput#create(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)", "a");
put("net.minecraft.world.level.storage.TagValueOutput#createWithContext(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider)", "a");
put("net.minecraft.world.level.storage.TagValueOutput#buildResult()", "b");
put("net.minecraft.world.item.ItemStack#CODEC", "b");
put("net.minecraft.util.ProblemReporter#DISCARDING", "a");
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "u");
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R6 = new HashMap<String, String>() {
{
putAll(MC1_21R5);
put("net.minecraft.server.MinecraftServer#registryAccess()", "bg");
put("net.minecraft.world.entity.Entity#load(net.minecraft.world.level.storage.ValueInput)", "d");
put("net.minecraft.world.entity.Entity#getEncodeId()", "bW");
put("net.minecraft.world.item.component.CustomData#copyTag()", "b");
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "o");
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "v");
}
};
@SuppressWarnings("serial")
private static Map<String, String> MC1_21R7 = new HashMap<String, String>() {
{
putAll(MC1_21R6);
put("net.minecraft.nbt.NbtAccounter#unlimitedHeap()", "c");
put("net.minecraft.server.MinecraftServer#registryAccess()", "bc");
put("net.minecraft.world.entity.Entity#getEncodeId()", "ca");
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "x");
}
};
public static Map<String, String> getMapping() {
// No mappings after 26.1
if(MinecraftVersion.getVersion() != MinecraftVersion.UNKNOWN && MinecraftVersion.getVersion().isAtLeastVersion(MinecraftVersion.MC26_1)) {
return Collections.EMPTY_MAP;
}
switch (MinecraftVersion.getVersion()) {
case MC1_21_R7:
return MC1_21R7;
case MC1_21_R6:
return MC1_21R6;
case MC1_21_R5:
return MC1_21R5;
case MC1_21_R4:
return MC1_21R4;
case MC1_21_R3:
return MC1_21R3;
case MC1_21_R2:
return MC1_21R2;
case MC1_21_R1:
return MC1_21R1;
case MC1_20_R4:
@@ -208,7 +357,7 @@ public class MojangToMapping {
case MC1_18_R1:
return MC1_18R1;
case UNKNOWN:
return MC1_20R2; // assume it's a future version, so try the latest known mappings
return MC1_21R7; // assume it's a future version, so try the latest known mappings
default:
// this should never happen, unless a version is forgotten here(like 1.19R3 which uses the 1.19R2 mappings)
throw new NbtApiException("No fitting mapping found for version " + MinecraftVersion.getVersion() + ". This is a bug!");
@@ -5,6 +5,8 @@ import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NbtApiException;
@@ -54,7 +56,7 @@ public enum ReflectionMethod {
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean"),
new Since(MinecraftVersion.MC1_18_R1, "putBoolean(java.lang.String,boolean)")),
COMPOUND_SET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, UUID.class },
MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"),
MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_16_R1, "a"),
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"),
@@ -65,12 +67,18 @@ public enum ReflectionMethod {
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "get"),
new Since(MinecraftVersion.MC1_18_R1, "get(java.lang.String)")),
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int.class },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList"),
COMPOUND_GET_LIST_LEGACY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int.class },
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_7_R4, "getList"),
new Since(MinecraftVersion.MC1_18_R1, "getList(java.lang.String,int)")),
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_21_R4,
new Since(MinecraftVersion.MC1_21_R4, "getList(java.lang.String)")),
// Only needed for 1.7.10 getType
COMPOUND_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4,
COMPOUND_OWN_TYPE_LEGACY(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getTypeId")),
TAGTYPE_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_21_R4,
new Since(MinecraftVersion.MC1_21_R4, "getType()")),
TAGTYPE_GET_NAME(ClassWrapper.NMS_TAGTYPE, new Class[] {}, MinecraftVersion.MC1_21_R4,
new Since(MinecraftVersion.MC1_21_R4, "getName()")),
COMPOUND_GET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getFloat"),
@@ -105,13 +113,14 @@ public enum ReflectionMethod {
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getBoolean"),
new Since(MinecraftVersion.MC1_18_R1, "getBoolean(java.lang.String)")),
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1,
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_21_R3,
new Since(MinecraftVersion.MC1_16_R1, "a"),
new Since(MinecraftVersion.MC1_18_R1, "getUUID(java.lang.String)")),
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getCompound"),
new Since(MinecraftVersion.MC1_18_R1, "getCompound(java.lang.String)")),
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R3,
new Since(MinecraftVersion.MC1_7_R4, "getTag"), new Since(MinecraftVersion.MC1_18_R1, "getTag()")),
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
@@ -126,17 +135,17 @@ public enum ReflectionMethod {
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "hasKey"),
new Since(MinecraftVersion.MC1_18_R1, "contains(java.lang.String)")),
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_8_R3,
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_21_R4,
new Since(MinecraftVersion.MC1_8_R3, "b"), new Since(MinecraftVersion.MC1_9_R1, "d"),
new Since(MinecraftVersion.MC1_15_R1, "e"), new Since(MinecraftVersion.MC1_16_R1, "d"),
new Since(MinecraftVersion.MC1_18_R1, "getTagType(java.lang.String)")),
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()"), new Since(MinecraftVersion.MC1_21_R5, "keySet()")),
// FIXME ?!?
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
// LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
// new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
// new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST, new Class[] { int.class }, MinecraftVersion.MC1_8_R3,
new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove"),
new Since(MinecraftVersion.MC1_18_R1, "remove(int)")),
@@ -166,7 +175,8 @@ public enum ReflectionMethod {
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK, new Class[] { ItemStack.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK, new Class[] { ClassWrapper.NMS_ITEMSTACK.getClazz() },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, resolveItemStackBukkitMirrorMethodName())),
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD, new Class[] {}, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
@@ -219,11 +229,11 @@ public enum ReflectionMethod {
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY, new Class[] {}, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "f"),
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_7_R4, "f"),
new Since(MinecraftVersion.MC1_16_R1, "load"),
new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "e"),
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_7_R4, "e"),
new Since(MinecraftVersion.MC1_12_R1, "save"),
new Since(MinecraftVersion.MC1_18_R1, "saveWithoutId(net.minecraft.nbt.CompoundTag)")),
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY, new Class[] {}, MinecraftVersion.MC1_14_R1,
@@ -251,7 +261,7 @@ public enum ReflectionMethod {
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
new Since(MinecraftVersion.MC1_7_R4, "parse"),
new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)")),
new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)"), new Since(MinecraftVersion.MC1_21_R5, "parseCompoundFully(java.lang.String)")),
REGISTRY_KEYSET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] {}, MinecraftVersion.MC1_11_R1,
MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
REGISTRY_GET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] { Object.class }, MinecraftVersion.MC1_11_R1,
@@ -292,19 +302,41 @@ public enum ReflectionMethod {
NMSITEM_SET(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_DATACOMPONENTTYPE.getClazz(), Object.class}, MinecraftVersion.MC1_20_R4,
new Since(MinecraftVersion.MC1_20_R4, "set(net.minecraft.core.component.DataComponentType,java.lang.Object)")),
NMSITEM_SAVE_MODERN(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz() },
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "save(net.minecraft.core.HolderLookup$Provider)")),
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "save(net.minecraft.core.HolderLookup$Provider)")),
NMSITEM_LOAD(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "parseOptional(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_20_R4, "parseOptional(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
NMSITEM_LOAD_MODERN(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTBASE.getClazz() },
MinecraftVersion.MC1_21_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "parse(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.Tag)")),
NMSSERVER_GETREGISTRYACCESS(ClassWrapper.NMS_SERVER, new Class[] {},
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "registryAccess()")),
NMSSERVER_GETSERVER(ClassWrapper.CRAFT_SERVER, new Class[] {},
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "getServer()")),
TILEENTITY_GET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_20_R4,
TILEENTITY_GET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4,
new Since(MinecraftVersion.MC1_20_R4, "saveWithId(net.minecraft.core.HolderLookup$Provider)")),
TILEENTITY_SET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz() },
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "loadWithComponents(net.minecraft.nbt.CompoundTag,net.minecraft.core.HolderLookup$Provider)")),
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "loadWithComponents(net.minecraft.nbt.CompoundTag,net.minecraft.core.HolderLookup$Provider)")),
GET_DATAFIXER(ClassWrapper.NMS_DATAFIXERS, new Class[] {}, MinecraftVersion.MC1_20_R4,
new Since(MinecraftVersion.MC1_20_R4, "getDataFixer()")),
// 1.21.6 Stuff
GET_SERIALIZATION_CONTEXT(ClassWrapper.NMS_PROVIDER, new Class[] {ClassWrapper.NMS_DYNAMICOPS.getClazz()}, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "createSerializationContext(com.mojang.serialization.DynamicOps)")),
NMS_GET_TAG_VALUE_INPUT(ClassWrapper.NMS_TAG_VALUE_INPUT, new Class[] {ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "create(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
NMS_GET_TAG_VALUE_OUTPUT(ClassWrapper.NMS_TAG_VALUE_OUTPUT, new Class[] {ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "createWithContext(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider)")),
NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND(ClassWrapper.NMS_TAG_VALUE_OUTPUT, new Class[] {}, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "buildResult()")),
NMS_ENTITY_SET_NBT_1216(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_VALUE_INPUT.getClazz() }, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "load(net.minecraft.world.level.storage.ValueInput)")
),
NMS_ENTITY_GET_NBT_1216(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_VALUE_OUTPUT.getClazz() }, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "saveWithoutId(net.minecraft.world.level.storage.ValueOutput)")),
TILEENTITY_GET_NBT_1216(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_VALUE_OUTPUT.getClazz()}, MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "saveWithId(net.minecraft.world.level.storage.ValueOutput)")),
TILEENTITY_SET_NBT_1216(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_VALUE_INPUT.getClazz() },
MinecraftVersion.MC1_21_R5,
new Since(MinecraftVersion.MC1_21_R5, "loadWithComponents(net.minecraft.world.level.storage.ValueInput)")),
;
private MinecraftVersion removedAfter;
@@ -382,6 +414,16 @@ public enum ReflectionMethod {
this(targetClass, args, addedSince, null, methodnames);
}
private static String resolveItemStackBukkitMirrorMethodName() {
try {
ClassWrapper.CRAFT_ITEMSTACK.getClazz().getDeclaredMethod("asCraftMirror",
ClassWrapper.NMS_ITEMSTACK.getClazz());
return "asCraftMirror";
} catch (NullPointerException | NoSuchMethodException | SecurityException ignored) {
return "asBukkitMirror";
}
}
/**
* Runs the method on a given target object using the given args.
*
@@ -396,7 +438,8 @@ public enum ReflectionMethod {
return method.invoke(target, args);
} catch (Exception ex) {
throw new NbtApiException("Error while calling the method '" + methodName + "', loaded: " + loaded
+ ", Enum: " + this + ", Passed Class: " + (target == null ? "null" : target.getClass()) + " Args: " + (args == null ? "null" : Arrays.toString(args)), ex);
+ ", Enum: " + this + ", Passed Class: " + (target == null ? "null" : target.getClass()) + " Args: " + (args == null ? "null" : Arrays.toString(args)) +
" Classes: " + (args == null ? "null" : Arrays.asList(args).stream().map(a -> a == null ? "NULL" : a.getClass().getName()).collect(Collectors.toList())), ex);
}
}
+10 -11
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
</parent>
<artifactId>item-nbt-api-plugin</artifactId>
<packaging>jar</packaging>
@@ -12,7 +12,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -21,6 +21,13 @@
<version>4.0.26</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.2.1</version>
<!-- Overwrite to provided -->
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
@@ -33,7 +40,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.2</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<relocations>
@@ -41,14 +48,6 @@
<pattern>de.tr7zw.changeme.nbtapi</pattern>
<shadedPattern>de.tr7zw.nbtapi</shadedPattern>
</relocation>
<relocation>
<pattern>javassist</pattern>
<shadedPattern>de.tr7zw.nbtinjector.javassist</shadedPattern>
</relocation>
<relocation>
<pattern>de.tr7zw.annotations</pattern>
<shadedPattern>de.tr7zw.nbtapi.utils.annotations</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
@@ -7,7 +7,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import de.tr7zw.changeme.nbtapi.NbtApiException;
@@ -19,6 +18,7 @@ import de.tr7zw.nbtapi.plugin.tests.GameprofileTest;
import de.tr7zw.nbtapi.plugin.tests.NBTFileTest;
import de.tr7zw.nbtapi.plugin.tests.blocks.BlockNBTTest;
import de.tr7zw.nbtapi.plugin.tests.chunks.ChunkNBTPersistentTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.CompoundDifferenceTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.EnumTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.EqualsTest;
import de.tr7zw.nbtapi.plugin.tests.compounds.ForEachTest;
@@ -41,6 +41,7 @@ import de.tr7zw.nbtapi.plugin.tests.items.DirectApplyMetaTest;
import de.tr7zw.nbtapi.plugin.tests.items.DirectApplyTest;
import de.tr7zw.nbtapi.plugin.tests.items.EmptyItemTest;
import de.tr7zw.nbtapi.plugin.tests.items.ItemConversionTest;
import de.tr7zw.nbtapi.plugin.tests.items.ItemJsonTest;
import de.tr7zw.nbtapi.plugin.tests.items.ItemMergingTest;
import de.tr7zw.nbtapi.plugin.tests.items.ItemStackConversionTest;
import de.tr7zw.nbtapi.plugin.tests.items.LegacyItemTest;
@@ -99,6 +100,7 @@ public class NBTAPI extends JavaPlugin {
apiTests.add(new ForEachTest());
apiTests.add(new StreamTest());
apiTests.add(new EqualsTest());
apiTests.add(new CompoundDifferenceTest());
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))
@@ -112,6 +114,9 @@ public class NBTAPI extends JavaPlugin {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
apiTests.add(new LegacyItemTest());
}
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) && !MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
apiTests.add(new ItemJsonTest());
}
apiTests.add(new ComponentsTest());
apiTests.add(new EmptyItemTest());
apiTests.add(new SmuggleTest());
@@ -195,7 +200,7 @@ public class NBTAPI extends JavaPlugin {
} catch (Exception ex) {
results.put(test, ex);
getLogger().log(Level.WARNING, "Error during '" + test.getClass().getSimpleName() + "' test!", ex);
} catch (NoSuchFieldError th) { // NOSONAR
} catch (Throwable th) { // NOSONAR
getLogger().log(Level.SEVERE, "Servere error during '" + test.getClass().getSimpleName() + "' test!");
getLogger().warning(
"WARNING! This version of Item-NBT-API seems to be broken with your Spigot version! Canceled the other tests!");
@@ -4,9 +4,9 @@ import java.util.UUID;
import com.mojang.authlib.GameProfile;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTGameProfile;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
public class GameprofileTest implements Test {
@@ -14,10 +14,9 @@ public class GameprofileTest implements Test {
public void test() throws Exception {
UUID uuid = UUID.randomUUID();
GameProfile profile = new GameProfile(uuid, "random");
NBTCompound nbt = NBTGameProfile.toNBT(profile);
profile = null;
profile = NBTGameProfile.fromNBT(nbt);
if (profile == null || !profile.getId().equals(uuid)) {
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
GameProfile profile2 = NBT.gameProfileFromNBT(nbt);
if (profile == null || !profile.equals(profile2)) {
throw new NbtApiException("Error when converting a GameProfile from/to NBT!");
}
}
@@ -3,10 +3,10 @@ package de.tr7zw.nbtapi.plugin.tests;
import java.io.File;
import java.nio.file.Files;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTFile;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.NBTFileHandle;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.NBTAPI;
public class NBTFileTest implements Test {
@@ -16,18 +16,18 @@ public class NBTFileTest implements Test {
NBTAPI.getInstance().getDataFolder().mkdirs();
File testFile = new File(NBTAPI.getInstance().getDataFolder(), "test.nbt");
Files.deleteIfExists(testFile.toPath());
NBTFile file = new NBTFile(testFile);
file.addCompound("testcomp").setString("test1", "ok");
NBTCompound comp = file.getCompound("testcomp");
NBTFileHandle file = NBT.getFileHandle(testFile);
file.getOrCreateCompound("testcomp").setString("test1", "ok");
ReadWriteNBT comp = file.getOrCreateCompound("testcomp");
if (comp == null) {
throw new NbtApiException("Error getting compound!");
}
comp.setString("test2", "ok");
file.setLong("time", System.currentTimeMillis());
file.setString("test", "test");
NBTCompound chunks = file.addCompound("chunks");
NBTCompound chunk = chunks.addCompound("somechunk");
NBTCompound block = chunk.addCompound("someblock");
ReadWriteNBT chunks = file.getOrCreateCompound("chunks");
ReadWriteNBT chunk = chunks.getOrCreateCompound("somechunk");
ReadWriteNBT block = chunk.getOrCreateCompound("someblock");
block.setString("type", "wool");
file.save();
@@ -35,25 +35,25 @@ public class NBTFileTest implements Test {
throw new NbtApiException("SubCompounds did not work!");
}
NBTFile fileLoaded = new NBTFile(testFile);
NBTFileHandle fileLoaded = NBT.getFileHandle(testFile);
if (!fileLoaded.getString("test").equals("test")) {
throw new NbtApiException("Wasn't able to load NBT File with the correct content!");
}
Files.deleteIfExists(fileLoaded.getFile().toPath());
// String
String str = fileLoaded.toString();
NBTContainer rebuild = new NBTContainer(str);
ReadWriteNBT rebuild = NBT.parseNBT(str);
if (!str.equals(rebuild.toString())) {
throw new NbtApiException("Wasn't able to parse NBT from a String!");
}
NBTCompound dummy = new NBTContainer();
ReadWriteNBT dummy = NBT.createNBTObject();
dummy.setString("test1", "key1");
NBTFile.saveTo(testFile, dummy);
dummy = new NBTContainer();
NBT.writeFile(testFile, dummy);
dummy = NBT.createNBTObject();
dummy.setString("test2", "key2");
NBTFile.saveTo(testFile, dummy);
dummy = NBTFile.readFrom(testFile);
NBT.writeFile(testFile, dummy);
dummy = NBT.readFile(testFile);
if (dummy.hasTag("test1") || !dummy.getString("test2").equals("key2")) {
throw new NbtApiException("Wasn't able to save NBT File with the correct content!");
}
@@ -1,40 +1,77 @@
package de.tr7zw.nbtapi.plugin.tests.blocks;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
import de.tr7zw.changeme.nbtapi.NBTBlock;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.nbtapi.plugin.tests.Test;
import java.util.List;
public class BlockNBTTest implements Test {
@Override
public void test() throws Exception {
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_16_R3.getVersionId())
return;
if (!Bukkit.getWorlds().isEmpty()) {
World world = Bukkit.getWorlds().get(0);
try {
if (world.getLoadedChunks().length > 1) {
Chunk chunk = world.getLoadedChunks()[0];
Block block = chunk.getBlock(0, 254, 0);
NBTBlock comp = new NBTBlock(block);
comp.getData().removeKey("Too");
if (comp.getData().hasTag("Too")) {
throw new NbtApiException("Unable to remove key from Block!");
}
comp.getData().setString("Too", "Bar");
if (!new NBTBlock(block).getData().getString("Too").equals("Bar")) {
throw new NbtApiException("Custom Data did not save to a Block!");
}
}
} catch (Exception ex) {
throw new NbtApiException("Wasn't able to use NBTBlocks!", ex);
List<World> loadedWorlds = Bukkit.getWorlds();
if (loadedWorlds.isEmpty())
return;
World world = loadedWorlds.get(0);
Chunk[] loadedChunks = world.getLoadedChunks();
if (loadedChunks.length == 0)
return;
try {
Chunk chunk = loadedChunks[0];
Block block = chunk.getBlock(0, 254, 0);
String blockKey = block.getX() + "_" + block.getY() + "_" + block.getZ();
NBT.modifyChunkPDC(block, nbt -> nbt.setString("Too", "Bar"));
boolean storedDataPresent = NBT.readAndGetChunkPDC(chunk, nbt -> {
if (!nbt.hasTag("blocks", NBTType.NBTTagCompound)) return false;
ReadableNBT blocksNbt = nbt.getCompound("blocks");
if (blocksNbt == null || !blocksNbt.hasTag(blockKey, NBTType.NBTTagCompound)) return false;
ReadableNBT blockNbt = blocksNbt.getCompound(blockKey);
return blockNbt != null && blockNbt.hasTag("Too", NBTType.NBTTagString) && "Bar".equals(blockNbt.getString("Too"));
});
if (!storedDataPresent) {
throw new NbtApiException("Custom Data did not save to a Block!");
}
if (!NBT.readAndGetChunkPDC(block, nbt -> nbt.hasTag("Too"))) {
throw new NbtApiException("Custom Data did not save to a Block!");
}
boolean blockHadOnlyTestKey = NBT.readAndGetChunkPDC(block, nbt -> nbt.getKeys().size() == 1);
boolean chunkHadOnlyOneBlock = NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getCompound("blocks").getKeys().size() == 1);
NBT.modifyChunkPDC(block, nbt -> nbt.removeKey("Too"));
if (NBT.readAndGetChunkPDC(block, nbt -> nbt.hasTag("Too"))) {
throw new NbtApiException("Unable to remove key from Block!");
}
if (chunkHadOnlyOneBlock) {
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("blocks"))) {
throw new NbtApiException("Chunk data wasn't cleaned up (hanging \"blocks\" compound)!");
}
} else if (blockHadOnlyTestKey) {
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getCompound("blocks").hasTag(blockKey))) {
throw new NbtApiException("Chunk data wasn't cleaned up (hanging block location compound)!");
}
}
} catch (Exception ex) {
throw new NbtApiException("Wasn't able to use NBTBlocks!", ex);
}
}
@@ -1,40 +1,46 @@
package de.tr7zw.nbtapi.plugin.tests.chunks;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTType;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import de.tr7zw.changeme.nbtapi.NBTChunk;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.nbtapi.plugin.tests.Test;
import java.util.List;
public class ChunkNBTPersistentTest implements Test {
@Override
public void test() throws Exception {
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_16_R3.getVersionId())
return;
if (!Bukkit.getWorlds().isEmpty()) {
World world = Bukkit.getWorlds().get(0);
try {
if (world.getLoadedChunks().length > 1) {
Chunk chunk = world.getLoadedChunks()[0];
NBTChunk comp = new NBTChunk(chunk);
NBTCompound persistentData = comp.getPersistentDataContainer();
persistentData.removeKey("Foo");
if (persistentData.hasTag("Foo")) {
throw new NbtApiException("Unable to remove key from Chunk!");
}
persistentData.setString("Foo", "Bar");
if (!new NBTChunk(chunk).getPersistentDataContainer().getString("Foo").equals("Bar")) {
throw new NbtApiException("Custom Data did not save to the Chunk!");
}
}
} catch (Exception ex) {
throw new NbtApiException("Wasn't able to use NBTChunks!", ex);
List<World> loadedWorlds = Bukkit.getWorlds();
if (loadedWorlds.isEmpty())
return;
World world = loadedWorlds.get(0);
Chunk[] loadedChunks = world.getLoadedChunks();
if (loadedChunks.length == 0)
return;
Chunk chunk = loadedChunks[0];
try {
NBT.modifyChunkPDC(chunk, nbt -> nbt.setString("Foo", "Bar"));
if (!NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("Foo", NBTType.NBTTagString) && "Bar".equals(nbt.getString("Foo")))) {
throw new NbtApiException("Custom Data did not save to the Chunk!");
}
NBT.modifyChunkPDC(chunk, nbt -> nbt.removeKey("Foo"));
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("Foo"))) {
throw new NbtApiException("Unable to remove key from Chunk!");
}
} catch (Exception ex) {
throw new NbtApiException("Wasn't able to use NBTChunks!", ex);
}
}
@@ -0,0 +1,69 @@
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;
import java.util.Arrays;
public class CompoundDifferenceTest implements Test {
@Override
public void test() throws Exception {
ReadWriteNBT nbt1 = NBT.createNBTObject();
nbt1.setInteger("intTag", 1);
ReadWriteNBT tmp = NBT.createNBTObject();
tmp.setInteger("foo", 1);
nbt1.getCompoundList("compoundList1").addCompound(tmp);
nbt1.setIntArray("intArray", new int[]{1, 2, 3});
tmp = NBT.createNBTObject();
tmp.setIntArray("foo", new int[]{1, 2, 3});
nbt1.getCompoundList("compoundList2").addCompound(tmp);
nbt1.getOrCreateCompound("compoundTag").setFloat("floatTag", 20F);
nbt1.getOrCreateCompound("compoundTag").setBoolean("booleanTag", true);
nbt1.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D));
nbt1.getOrCreateCompound("compoundTag").getFloatList("floatList").addAll(Arrays.asList(1F, 2F));
nbt1.getOrCreateCompound("compoundTagTwo").setString("stringTag", "string");
ReadWriteNBT nbt2 = NBT.createNBTObject();
nbt2.setInteger("intTag", 1);
tmp = NBT.createNBTObject();
tmp.setInteger("foo", 1);
nbt2.getCompoundList("compoundList1").addCompound(tmp);
nbt2.setInteger("alsoIntTag", 2);
tmp = NBT.createNBTObject();
tmp.setInteger("foo", 2);
nbt2.getCompoundList("compoundList2").addCompound(tmp);
nbt2.getOrCreateCompound("compoundTag").setFloat("floatTag", 20F);
nbt2.getOrCreateCompound("compoundTag").setBoolean("booleanTag", false);
nbt2.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D, 4D));
nbt2.getOrCreateCompound("compoundTag").getFloatList("floatList").addAll(Arrays.asList(1F, 2F));
nbt2.getOrCreateCompound("compoundTagTwo").setString("stringTag", "string");
ReadWriteNBT expectedDiff1 = NBT.createNBTObject();
tmp = NBT.createNBTObject();
tmp.setIntArray("foo", new int[]{1, 2, 3});
expectedDiff1.getCompoundList("compoundList2").addCompound(tmp);
expectedDiff1.setIntArray("intArray", new int[]{1, 2, 3});
expectedDiff1.getOrCreateCompound("compoundTag").setBoolean("booleanTag", true);
expectedDiff1.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D));
ReadWriteNBT expectedDiff2 = NBT.createNBTObject();
tmp = NBT.createNBTObject();
tmp.setInteger("foo", 2);
expectedDiff2.getCompoundList("compoundList2").addCompound(tmp);
expectedDiff2.setInteger("alsoIntTag", 2);
expectedDiff2.getOrCreateCompound("compoundTag").setBoolean("booleanTag", false);
expectedDiff2.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D, 4D));
ReadWriteNBT diff1 = nbt1.extractDifference(nbt2);
ReadWriteNBT diff2 = nbt2.extractDifference(nbt1);
if (!expectedDiff1.equals(diff1)) {
throw new NbtApiException("Diff1: Compounds did not match! " + expectedDiff1 + " " + diff1);
}
if (!expectedDiff2.equals(diff2)) {
throw new NbtApiException("Diff2: Compounds did not match! " + expectedDiff2 + " " + diff2);
}
}
}
@@ -1,16 +1,16 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBT;
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 EnumTest implements Test {
@Override
public void test() throws Exception {
NBTCompound comp = new NBTContainer();
ReadWriteNBT comp = NBT.createNBTObject();
comp.setEnum("test", NBTType.NBTTagEnd);
NBTType type = comp.getEnum("test", NBTType.class);
NBTType typeNonNull = comp.getOrNull("test", NBTType.class);
@@ -5,44 +5,44 @@ import java.util.Arrays;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTItem;
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 EqualsTest implements Test {
@Override
public void test() throws Exception {
NBTContainer cont = new NBTContainer();
ReadWriteNBT cont = NBT.createNBTObject();
cont.setString("hello", "world");
cont.setInteger("theAnswer", 42);
cont.addCompound("sub").setString("me", "too");
cont.getOrCreateCompound("sub").setString("me", "too");
cont.getStringList("somelist").addAll(Arrays.asList("a", "b", "c"));
ItemStack item = new ItemStack(Material.STONE);
NBTItem nbti = new NBTItem(item);
NBTCompound customData = nbti.addCompound("customData");
// reverse order
customData.addCompound("sub").setString("me", "too");
customData.setInteger("theAnswer", 42);
customData.setString("hello", "world");
customData.getStringList("somelist").addAll(Arrays.asList("a", "b", "c"));
if (!customData.equals(cont)) {
throw new NbtApiException("Compounds did not match! " + customData + " " + cont);
}
NBT.modify(item, nbti -> {
ReadWriteNBT customData = nbti.getOrCreateCompound("customData");
// reverse order
customData.getOrCreateCompound("sub").setString("me", "too");
customData.setInteger("theAnswer", 42);
customData.setString("hello", "world");
customData.getStringList("somelist").addAll(Arrays.asList("a", "b", "c"));
if (!customData.equals(cont)) {
throw new NbtApiException("Compounds did not match! " + customData + " " + cont);
}
});
// empty test
if (!new NBTContainer().equals(new NBTContainer())) {
if (!NBT.createNBTObject().equals(NBT.createNBTObject())) {
throw new NbtApiException("Two empty tags did not match!");
}
// not equal test
NBTContainer part1 = new NBTContainer();
ReadWriteNBT part1 = NBT.createNBTObject();
part1.setString("a", "a");
part1.setString("b", "b");
NBTContainer part2 = new NBTContainer();
ReadWriteNBT part2 = NBT.createNBTObject();
part2.setString("a", "a");
part2.setString("b", "a");
if (part1.equals(part2)) {
@@ -3,24 +3,22 @@ package de.tr7zw.nbtapi.plugin.tests.compounds;
import java.util.Arrays;
import java.util.UUID;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTCompoundList;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTList;
import de.tr7zw.changeme.nbtapi.NBTListCompound;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTList;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class ListTest implements Test {
@Override
public void test() throws Exception {
NBTContainer comp = new NBTContainer();
ReadWriteNBT comp = NBT.createNBTObject();
// Strings
NBTList<String> list = comp.getStringList("testlist");
ReadWriteNBTList<String> list = comp.getStringList("testlist");
list.add("test1");
list.add("test2");
list.add("test3");
@@ -32,14 +30,14 @@ public class ListTest implements Test {
}
// Compound
NBTCompoundList taglist = comp.getCompoundList("complist");
NBTListCompound lcomp = taglist.addCompound();
ReadWriteNBTCompoundList taglist = comp.getCompoundList("complist");
ReadWriteNBT lcomp = taglist.addCompound();
lcomp.setDouble("double1", 0.3333);
lcomp.setInteger("int1", 42);
lcomp.setString("test1", "test1");
lcomp.setString("test2", "test2");
lcomp.removeKey("test1");
NBTCompound subsubcomp = lcomp.addCompound("listsubkey");
ReadWriteNBT subsubcomp = lcomp.getOrCreateCompound("listsubkey");
subsubcomp.setString("deep", "String");
subsubcomp.getCompoundList("deeplist").addCompound().setString("test", "test");
subsubcomp.getCompoundList("clonelist").addCompound(comp);
@@ -73,11 +71,11 @@ public class ListTest implements Test {
}
if (comp.getListType("complist") != NBTType.NBTTagCompound) {
throw new NbtApiException("complist had the wrong type! The Item-NBT-API may not work!");
throw new NbtApiException("complist had the wrong type(" + comp.getListType("complist") + ")! The Item-NBT-API may not work!");
}
// Integer
NBTList<Integer> intlist = comp.getIntegerList("inttest");
ReadWriteNBTList<Integer> intlist = comp.getIntegerList("inttest");
intlist.add(42);
intlist.add(69);
if (intlist.size() == 2 && intlist.get(0) == 42 && intlist.get(1) == 69) {
@@ -91,7 +89,7 @@ public class ListTest implements Test {
}
// Double
NBTList<Double> doublelist = comp.getDoubleList("doubletest");
ReadWriteNBTList<Double> doublelist = comp.getDoubleList("doubletest");
doublelist.add(42.23d);
doublelist.add(69.69d);
if (doublelist.size() == 2 && doublelist.get(0) == 42.23d && doublelist.get(1) == 69.69d) {
@@ -106,7 +104,7 @@ public class ListTest implements Test {
}
// Float
NBTList<Float> floatlist = comp.getFloatList("floattest");
ReadWriteNBTList<Float> floatlist = comp.getFloatList("floattest");
floatlist.add(42.23f);
floatlist.add(69.69f);
if (floatlist.size() == 2 && floatlist.get(0) == 42.23f && floatlist.get(1) == 69.69f) {
@@ -120,7 +118,7 @@ public class ListTest implements Test {
}
// Long
NBTList<Long> longlist = comp.getLongList("longtest");
ReadWriteNBTList<Long> longlist = comp.getLongList("longtest");
longlist.add(1241234124124l);
longlist.add(1231454321312l);
if (longlist.size() == 2 && longlist.get(0) == 1241234124124l && longlist.get(1) == 1231454321312l) {
@@ -134,7 +132,7 @@ public class ListTest implements Test {
}
// int[]
NBTList<int[]> intArrayList = comp.getIntArrayList("intatest");
ReadWriteNBTList<int[]> intArrayList = comp.getIntArrayList("intatest");
intArrayList.add(new int[] { 1, 2, 3 });
intArrayList.add(new int[] { 4, 2, 0 });
if (intArrayList.size() == 2 && Arrays.equals(new int[] { 1, 2, 3 }, intArrayList.get(0))
@@ -149,7 +147,7 @@ public class ListTest implements Test {
}
// UUID
NBTList<UUID> uuidList = comp.getUUIDList("uuidtest");
ReadWriteNBTList<UUID> uuidList = comp.getUUIDList("uuidtest");
uuidList.add(UUID.fromString("fce0323d-7f50-4317-9720-5f6b14cf78ea"));
uuidList.add(UUID.fromString("853c80ef-3c37-49fd-aa49-938b674adae6"));
if (uuidList.size() == 2 && UUID.fromString("fce0323d-7f50-4317-9720-5f6b14cf78ea").equals(uuidList.get(0))
@@ -3,24 +3,33 @@ package de.tr7zw.nbtapi.plugin.tests.compounds;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import de.tr7zw.changeme.nbtapi.NBTContainer;
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.ReadWriteNBT;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class StreamTest implements Test {
@Override
public void test() throws Exception {
NBTContainer base = new NBTContainer();
ReadWriteNBT base = NBT.createNBTObject();
base.getOrCreateCompound("sub").setString("hello", "world");
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
base.getOrCreateCompound("sub").writeCompound(outStream);
byte[] data = outStream.toByteArray();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
NBTContainer container = new NBTContainer(inputStream);
ReadWriteNBT container = NBT.readNBT(inputStream);
if (!container.toString().equals(base.getOrCreateCompound("sub").toString())) {
throw new NbtApiException("Component content did not match! " + base.getCompound("sub") + " " + container);
}
ItemStack item = new ItemStack(Material.STICK);
NBT.modify(item, nbt -> {
nbt.writeCompound(outStream);
});
}
}
@@ -1,8 +1,8 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBTCompound;
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 SubCompoundsTest implements Test {
@@ -19,9 +19,9 @@ public class SubCompoundsTest implements Test {
@Override
public void test() throws Exception {
NBTContainer cont = new NBTContainer();
ReadWriteNBT cont = NBT.createNBTObject();
NBTCompound comp = cont.addCompound(COMP_TEST_KEY);
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);
@@ -1,19 +1,19 @@
package de.tr7zw.nbtapi.plugin.tests.compounds;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBT;
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 TypeTest implements Test {
@Override
public void test() throws Exception {
NBTCompound comp = new NBTContainer();
ReadWriteNBT comp = NBT.createNBTObject();
comp.setString("s", "test");
comp.setInteger("i", 42);
comp.addCompound("c");
comp.getOrCreateCompound("c");
if (comp.getType("s") != NBTType.NBTTagString || comp.getType("i") != NBTType.NBTTagInt
|| comp.getType("c") != NBTType.NBTTagCompound)
throw new NbtApiException("One parsed type did not match what it should have been!");
@@ -6,8 +6,7 @@ import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Monster;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTEntity;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.nbtapi.plugin.tests.Test;
@@ -23,14 +22,16 @@ public class EntityCustomNbtPersistentTest implements Test {
try {
if (!world.getEntitiesByClasses(Animals.class, Monster.class).isEmpty()) {
Entity ent = world.getEntitiesByClasses(Animals.class, Monster.class).iterator().next();
NBTEntity nbtEnt = new NBTEntity(ent);
NBTCompound comp = nbtEnt.getPersistentDataContainer();
comp.setString("Hello", "World");
NBTEntity nbtent = new NBTEntity(ent);
if (!nbtent.toString().contains("Hello:\"World\"")) {
throw new NbtApiException("Custom Data did not save to the Entity!");
}
comp.removeKey("Hello");
NBT.modifyPersistentData(ent, comp -> {
comp.setString("Hello", "World");
});
NBT.modifyPersistentData(ent, comp -> {
if (!comp.toString().contains("Hello:\"World\"")) {
throw new NbtApiException("Custom Data did not save to the Entity!");
}
comp.removeKey("Hello");
});
}
} catch (Exception ex) {
@@ -27,7 +27,11 @@ public class ComponentsTest implements Test {
throw new NbtApiException("ReadComponent didn't work!");
}
NBT.modifyComponents(item, nbt -> {
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
nbt.mergeCompound(NBT.parseNBT("{\"minecraft:custom_name\":[{\"text\":\"foobar\",\"italic\":false}]}"));
} else {
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
}
});
if(!item.getItemMeta().getDisplayName().equals("foobar")) {
throw new NbtApiException("ModifyComponent didn't work!");
@@ -1,13 +1,15 @@
package de.tr7zw.nbtapi.plugin.tests.items;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTType;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.google.common.collect.Lists;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.nbtapi.plugin.tests.Test;
@@ -19,14 +21,21 @@ public class ItemConversionTest implements Test {
ItemMeta meta = item.getItemMeta();
meta.setLore(Lists.newArrayList("Firest Line", "Second Line"));
item.setItemMeta(meta);
String nbt = NBTItem.convertItemtoNBT(item).toString();
if (!nbt.contains("Firest Line") || !nbt.contains("Second Line"))
throw new NbtApiException("The Item nbt '" + nbt + "' didn't contain the lore");
ItemStack rebuild = NBTItem.convertNBTtoItem(new NBTContainer(nbt));
ReadWriteNBT nbt = NBT.itemStackToNBT(item);
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)
&& !nbt.hasTag("DataVersion", NBTType.NBTTagInt)) {
throw new NbtApiException("The item nbt '" + nbt + "' didn't contain the data version");
}
String nbtString = nbt.toString();
if (!nbtString.contains("Firest Line") || !nbtString.contains("Second Line"))
throw new NbtApiException("The Item nbt '" + nbtString + "' didn't contain the lore");
ItemStack rebuild = NBT.itemStackFromNBT(NBT.parseNBT(nbtString));
if (!item.isSimilar(rebuild))
throw new NbtApiException("Rebuilt item did not match the original!");
NBTContainer cont = new NBTContainer();
ReadWriteNBT cont = NBT.createNBTObject();
cont.setItemStack("testItem", item);
if (!item.isSimilar(cont.getItemStack("testItem")))
throw new NbtApiException("Rebuilt item did not match the original!");
@@ -0,0 +1,28 @@
package de.tr7zw.nbtapi.plugin.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.google.gson.JsonElement;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.NBTJsonUtil;
import de.tr7zw.nbtapi.plugin.tests.Test;
public class ItemJsonTest implements Test {
@Override
public void test() throws Exception {
ItemStack item = new ItemStack(Material.STONE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("test");
meta.setUnbreakable(true);
item.setItemMeta(meta);
JsonElement elem = NBTJsonUtil.itemStackToJson(item);
if (elem == null) {
throw new NbtApiException("Getting the Json didn't work correctly! " + item);
}
}
}
@@ -3,9 +3,9 @@ package de.tr7zw.nbtapi.plugin.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
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 ItemStackConversionTest implements Test {
@@ -14,8 +14,8 @@ public class ItemStackConversionTest implements Test {
public void test() throws Exception {
ItemStack[] src = new ItemStack[] { new ItemStack(Material.STONE), new ItemStack(Material.STICK),
new ItemStack(Material.AIR), new ItemStack(Material.STONE) };
NBTCompound comp = NBTItem.convertItemArraytoNBT(src);
ItemStack[] recreated = NBTItem.convertNBTtoItemArray(comp);
ReadWriteNBT comp = NBT.itemStackArrayToNBT(src);
ItemStack[] recreated = NBT.itemStackArrayFromNBT(comp);
if (recreated == null || src.length != recreated.length) {
throw new NbtApiException("Size did not match!");
}
@@ -29,6 +29,15 @@ public class LegacyItemTest implements Test {
|| !"test".equals(item.getItemMeta().getDisplayName())) {
throw new NbtApiException("1.12.2 item didn't load correctly! " + item);
}
ItemStack item2 = NBT.itemStackFromNBT(NBT.parseNBT("{DataVersion:" + DataFixerUtil.VERSION1_12_2 + ",id:cobblestone,Count:42,tag:{display:{Name:\"test\"},ench:[{lvl:3,id:34}]}}"));
if (!item.equals(item2))
throw new NbtApiException("Data-fixed 1.12.2 item didn't load correctly! " + item2);
ReadWriteNBT testNBT = NBT.parseNBT("{id:cobblestone,Count:42,tag:{display:{Name:\"test\"},ench:[{lvl:3,id:34}]},DataVersion:1343}");
testNBT = DataFixerUtil.fixUpItemData(testNBT, DataFixerUtil.VERSION1_12R1);
if (testNBT.getInteger("DataVersion") != DataFixerUtil.getCurrentVersion())
throw new NbtApiException("DataVersion wasn't updated correctly! " + testNBT);
}
}
@@ -5,6 +5,7 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTTileEntity;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
@@ -24,6 +25,14 @@ public class TileTest implements Test {
world.getSpawnLocation().getBlockZ());
if (world.isChunkLoaded(block.getX() >> 4, block.getZ() >> 4) && block.getType() == Material.AIR) {
block.setType(Material.CHEST);
if (MinecraftVersion.isNewerThan(MinecraftVersion.MC1_21_R1)) {
// 1.21 changed the lock logic. So just try to get/set data, dont check for now
NBT.modify(block.getState(), nbt -> {
nbt.setString("foo", "bar");
});
block.setType(Material.AIR);
return;
}
NBTTileEntity tile = new NBTTileEntity(block.getState());
if (!MinecraftVersion.isNewerThan(MinecraftVersion.MC1_17_R1)) {
if (tile.getInteger("y") != 254) {
@@ -5,8 +5,7 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTTileEntity;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NbtApiException;
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
import de.tr7zw.nbtapi.plugin.tests.Test;
@@ -27,14 +26,16 @@ public class TilesCustomNBTPersistentTest implements Test {
world.getSpawnLocation().getBlockZ());
if (world.isChunkLoaded(block.getX() >> 4, block.getZ() >> 4) && block.getType() == Material.AIR) {
block.setType(Material.CHEST);
NBTTileEntity comp = new NBTTileEntity(block.getState());
NBTCompound persistentData = comp.getPersistentDataContainer();
persistentData.setString("Foo", "Bar");
if (!new NBTTileEntity(block.getState()).getPersistentDataContainer().getString("Foo")
.equals("Bar")) {
block.setType(Material.AIR);
throw new NbtApiException("Custom Data did not save to the Tile!");
}
NBT.modifyPersistentData(block.getState(), persistentData -> {
persistentData.setString("Foo", "Bar");
});
NBT.getPersistentData(block.getState(), persistentData -> {
if (!persistentData.getString("Foo").equals("Bar")) {
block.setType(Material.AIR);
throw new NbtApiException("Custom Data did not save to the Tile!");
}
return null;
});
block.setType(Material.AIR);
}
} catch (Exception ex) {
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</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.13.2</version>
<version>2.16.1</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.21.txt");
File input = new File("minecraft_server.1.21.9.txt");
List<String> lines = Files.readAllLines(input.toPath());
Map<String, ClassWrapper> classes = new HashMap<>();
@@ -75,6 +75,33 @@ public class MappingsParser {
}
// System.out.println(currentClass + ": " + mojangName + " -> " + unmappedName);
}
for(String type : MojangToMapping.getMapping().keySet()) {
if(type.contains("(")) {
continue;
}
// Field
String clazz = type.split("#")[0];
String fieldName = type.split("#")[1];
for (String line : lines) {
if (line.startsWith("#")) {
continue;
}
if (!line.startsWith(" ")) {
currentClass = line.split(" ")[0];
continue;
}
mojangName = line.trim().split(" ")[1];
unmappedName = line.trim().split(" ")[3];
if (currentClass.equals(clazz) && mojangName.equals(fieldName)) {
if (MojangToMapping.getMapping().get(type) == null) {
System.out.println("Missing mapping for " + type);
} else if (!MojangToMapping.getMapping().get(type)
.equals(unmappedName)) {
builder.append("put(\"" + type + "\", \"" + unmappedName + "\");\n");
}
}
}
}
System.out.println(builder);
}
+2 -2
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
</parent>
<artifactId>nbt-data-api</artifactId>
@@ -14,7 +14,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</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.13.2</version>
<version>2.16.1</version>
<scope>compile</scope>
</dependency>
<dependency>
+5 -5
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-parent</artifactId>
<version>2.13.2</version>
<version>2.16.1</version>
<packaging>pom</packaging>
<inceptionYear>2015</inceptionYear>
<organization>
@@ -88,7 +88,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<version>3.16.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -97,7 +97,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
<version>3.4.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -110,7 +110,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.8.0</version>
<version>3.12.0</version>
<configuration>
<source>8</source>
</configuration>
@@ -139,7 +139,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.4.0</version>
<version>2.7.1</version>
<configuration>
<licenseName>mit</licenseName>
<licenseFile>LICENSE</licenseFile>
+2 -2
View File
@@ -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.13.2</version>
<version>2.16.1</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.13.2</version>
<version>2.16.1</version>
</dependency>
...
<plugins>
+2 -2
View File
@@ -100,7 +100,7 @@ NBT.modifyPersistentData(zombie, nbt -> {
File worldDataFolder = Bukkit.getWorlds().getFirst().getWorldFolder();
// Read level data
NBTFile levelNbtFile = new NBTFile(new File(worldDataFolder, "level.dat"));
NBTFileHandle levelNbtFile = NBT.getFileHandle(new File(worldDataFolder, "level.dat"));
// Obtain world name
String worldName = levelNbtFile.resolveOrNull("Data.LevelName", String.class);
@@ -113,7 +113,7 @@ if (!playerFile.exists()) {
return;
}
NBTFile playerNbtFile = new NBTFile(playerFile);
NBTFileHandle playerNbtFile = NBT.getFileHandle(playerFile);
// Change player's health
float health = playerNbtFile.getFloat("Health");
+1 -3
View File
@@ -21,6 +21,4 @@ The experimental NBTInjector became unsupported since Minecraft 1.14 and was rem
NBTInjector is incompatible with reloads and may break things, and thus is not recommended.
If you're using Minecraft 1.14+, you should switch to the persistent storage API instead.
In versions prior to 1.14, you may use a workaround like storing data inside an item's nbt that the entity is wearing (e.g. having a button in mob's helmet, and storing data on that button).
See [this wiki section](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#accessing-custom-data) on how to handle custom entity nbt data without NBTInjector.
+3 -1
View File
@@ -2,9 +2,11 @@
- [IllegalStack](https://github.com/dniym/IllegalStack)
- [Prison](https://github.com/PrisonTeam/Prison)
- [CommandAPI](https://github.com/JorelAli/CommandAPI)
- [CommandAPI](https://github.com/CommandAPI/CommandAPI)
- [SkBee](https://github.com/ShaneBeee/SkBee/)
- [Monumenta](https://github.com/TeamMonumenta/monumenta-plugins-public/)
- [SummonFriendlyMobs](https://github.com/ArmyFury/SummonFriendlyMobs)
- [EvenMoreFish](https://github.com/Oheers/EvenMoreFish)
___
+27 -5
View File
@@ -2,8 +2,11 @@ To start using NB-API, you either need to depend on its plugin version, or shade
> [!IMPORTANT]
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
>
> [!IMPORTANT]
> Alternative ways of loading the api like Libby are not supported on Discord/Github issues.
# Option 1) NBT-API as a dependency
# Option 1) NBT-API as a dependency (recommended)
Add the following entries to your Gradle build at the correct locations:
@@ -30,19 +33,38 @@ Add the API as dependency to your ``plugin.yml``:
depend: [NBTAPI]
```
# Option 2) Shading the NBT-API into your plugin
Or, if you are using ``paper-plugin.yml``:
To include NBT-API directly in your plugin, you can use the gradle shadow plugin.
```yml
dependencies:
server:
NBTAPI:
load: BEFORE
required: true
join-classpath: true
```
# Option 2) Shading the NBT-API into your plugin (Not recommended)
> [!WARNING]
> Due to the increasing amount of pitfalls in modern Paper, shading is not recommended. Please test with the normal plugin dependency before reporting issues.
To include NBT-API directly in your plugin, you can use the [Gradle Shadow Plugin](https://gradleup.com/shadow/).
Create an empty ``.mojang-mapped`` file in the META-INF folder of your plugin for 1.20+ Paper support.
> [!WARNING]
> Not adding this flag will make the shaded api not work on the latest Paper. It might also break other reflections/nms logic!
Add the plugin to the build configuration, as shown here:
```groovy
plugins {
id("com.github.johnrengelman.shadow") version "VERSION"
id("com.gradleup.shadow") version "VERSION"
}
```
The latest version of the shadow plugin can be found [here](https://github.com/johnrengelman/shadow/releases).
The latest version of the shadow plugin can be found [here](https://plugins.gradle.org/plugin/com.gradleup.shadow).
Add NBT-API to your dependencies:
+24 -2
View File
@@ -2,8 +2,11 @@ To start using NB-API, you either need to depend on its plugin version, or shade
> [!IMPORTANT]
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
>
> [!IMPORTANT]
> Alternative ways of loading the api like Libby are not supported on Discord/Github issues.
# Option 1) NBT-API as a dependency
# Option 1) NBT-API as a dependency (recommended)
Add the following entries to your pom at the correct locations:
@@ -37,10 +40,29 @@ Add the API as dependency to your ``plugin.yml``:
depend: [NBTAPI]
```
# Option 2) Shading the NBT-API into your plugin
Or, if you are using ``paper-plugin.yml``:
```yml
dependencies:
server:
NBTAPI:
load: BEFORE
required: true
join-classpath: true
```
# Option 2) Shading the NBT-API into your plugin (Not recommended)
> [!WARNING]
> Due to the increasing amount of pitfalls in modern Paper, shading is not recommended. Please test with the normal plugin dependency before reporting issues.
To include NBT-API directly in your plugin, you can use the maven shade plugin.
Create an empty ``.mojang-mapped`` file in the META-INF folder of your plugin for 1.20+ Paper support.
> [!WARNING]
> Not adding this flag will make the shaded api not work on the latest Paper. It might also break other reflections/nms logic!
Add the plugin to the build configuration, as shown here:
```xml
+101 -30
View File
@@ -2,7 +2,7 @@
### Basics overview
Most of the things are exposed inside the [NBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/NBT.html) class. Make sure to explore the Javadoc, or ask your IDE for suggestions!
Most of the things are exposed inside the [NBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/NBT.html) class. Your specific use-case might not yet be covered by the wiki, so make sure to explore the [Javadoc](https://tr7zw.github.io/Item-NBT-API/v2-api/), or ask your IDE for suggestions!
#### Basic getting and setting of data
@@ -43,14 +43,34 @@ nbt.getOrCreateCompound("subtag"); // Get or create a subtag
#### Converting NBT to String and the String back to NBT
```java
// Parse Mojang-Json string to nbt
// Parse SNBT to nbt
ReadWriteNBT nbt = NBT.parseNBT("{Health:20.0f,Motion:[0.0d,10.0d,0.0d],Silent:1b}");
// Get the NBT back as a Mojang-Json string (works with any NBT object)
String json = nbt.toString();
// Get the NBT back as a SNBT (works with any NBT object)
String snbt = nbt.toString();
// Turn back into nbt again
ReadWriteNBT nbt2 = NBT.parseNBT(json);
ReadWriteNBT nbt2 = NBT.parseNBT(snbt);
```
#### Comparing NBT data
As with other objects in Java, you may compare nbt using `equals`.
To see what exactly mismatched, you can use `ReadableNBT#extractDifference`:
```java
ReadWriteNBT nbt1 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:1b}},intArray:[I;1,2]");
ReadWriteNBT nbt2 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:0b}},intArray:[I;1,2,3],alsoIntTag:2");
ReadWriteNBT diff1 = nbt1.extractDifference(nbt2);
ReadWriteNBT diff2 = nbt2.extractDifference(nbt1);
```
For the example above, `ReadableNBT#toString` will produce:
diff1: `{compoundTag:{booleanTag:1b},intArray:[I;1,2]}`
diff2: `{compoundTag:{booleanTag:0b},intArray:[I;1,2,3],alsoIntTag:2}`
#### Merging Compounds
You can "merge" the data from any given Compound onto some other Compound. This will overwrite the tags with the same name.
@@ -115,9 +135,13 @@ nbt.resolveOrDefault("list_key[-1]", "fallback_value"); // Get the last value in
// NBT compound lists
// Get the list (will create it if needed)
ReadWriteNBTCompoundList nbtList = nbt.getOrCreateCompound("foo").getCompoundList("other_key");
ReadWriteNBT nbtListEntry = addCompound();
// Add new compound to the list
ReadWriteNBT nbtListEntry = nbtList.addCompound();
nbtListEntry.setBoolean("bar", true);
// Add existing nbt compound to the list
ReadWriteNBT otherNbtListEntry = nbtList.addCompound(NBT.parseNBT("{foo_bar:1b}"));
// You can fetch compounds in lists
nbt.resolveCompound("foo.other_key[0]"); // Get the first compound in list, or null
@@ -162,12 +186,33 @@ int someValue = NBT.modify(itemStack, nbt -> {
> [!WARNING]
> Never mix the usage of ItemMeta and NBT.
> Setting ItemMeta will override any changes made to item's nbt.
```java
// WRONG, do not do this!
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName("Modified!");
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
itemStack.setItemMeta(meta); // Will undo all changes to nbt!
// Correct, both ItemMeta and NBT will be applied
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
// Taking ItemMeta snapshot after changes to nbt
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName("Modified!");
itemStack.setItemMeta(meta);
// If making any further changes with NBT & ItemMeta,
// re-take the item's ItemMeta after changing the nbt!
```
Alternatively, you may safely modify ItemMeta inside the NBT scope:
```java
// Updating ItemMeta using NBT
NBT.modify(itemStack, nbt -> {
nbt.setInteger("kills", nbt.getOrDefault("kills", 0) + 1);
nbt.modifyMeta((meta, readOnlyNbt) -> { // Do not modify the nbt while modifying the meta!
nbt.modifyMeta((readOnlyNbt, meta) -> { // Do not modify the nbt while modifying the meta!
meta.setDisplayName("Kills: " + readOnlyNbt.getOrDefault("kills", 0));
});
// Do more stuff
@@ -184,6 +229,15 @@ NBT.modify(itemStack, nbt -> {
```java
// NOTE: This code is only for 1.20.5+!
// Only reading vanilla data
NBT.getComponents(item, nbt -> {
if (nbt.hasTag("minecraft:custom_name")) {
String customName = nbt.getString("minecraft:custom_name");
}
});
// Modifying vanilla data
NBT.modifyComponents(item, nbt -> {
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
});
@@ -209,7 +263,12 @@ NBT.modify(entity, nbt -> {
#### Accessing custom data
For reading/storing custom data on (block-)entities, you should use methods that end with PersistentData.
> [!WARNING]
> Persistent data storage for custom (block-)entity nbt is only available in 1.14+.
>
> If you need to support versions below that, for entities you may use a workaround like storing data inside an item's nbt that the entity is wearing (e.g. having a button in mob's helmet, and storing data on that button).
For reading/storing custom data on (block-)entities, you should use methods that end with ``PersistentData``.
> [!IMPORTANT]
> When working with block entities, make sure that the block entity exists in the world.
@@ -247,20 +306,26 @@ You can store data in block entities (block entities like chest, furnace, etc.)
Thus, you have to use your own block data storage to store custom block data.
You can store data inside Chunks since 1.16.4, and NBT-API allows you to do so by using ``NBTChunk``:
You can store data inside Chunks using their PDC (Persistent Data Container) since Minecraft **1.16.4**. The NBT‑API provides read and modify methods in the `NBT` class to interact with it:
```java
ReadWriteNBT nbt = new NBTChunk(chunk).getPersistentDataContainer();
// Read the entire PDC or a specific value
NBT.readChunkPDC(chunk, nbt -> { /* ... */ });
String str = NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getOrNull("key", String.class));
```
Similarly, there is ``NBTBlock``, which allows you to store block data inside chunk's data.
For convenience, you can also pass Blocks into `NBT#read/modify[AndGet]ChunkPDC` methods to store block data inside Chunk's PDC.
Blocks are stored inside their chunk's PDC under a `blocks` compound, using the block's location (`X_Y_Z`) as the key for a subcompound.
```java
// Block's data will be stored in Chunk's data in "blocks.x_y_z" subtag
ReadWriteNBT nbt = new NBTBlock(block).getData();
// Modify and retrieve data for a specific block
boolean bool = NBT.modifyAndGetChunkPDC(block, nbt -> {
nbt.setString("owner_name", "Player123");
return nbt.getOrDefault("key", false);
});
```
**However**, keep in mind that this data is linked only to the location, and if the block gets broken/changed/exploded/moved/etc., the data will still be on that location unless manually cleared/moved!
**However**, keep in mind that the block's data is linked only to the location, and if the block gets broken/changed/exploded/moved/etc., the data will still be on that location unless manually cleared/moved!
Moreover, since the data is stored inside a Chunk, this will increase the chunk's size on the disk.
@@ -271,26 +336,32 @@ Moreover, since the data is stored inside a Chunk, this will increase the chunk'
### NBT files
```java
// Creating nbt file
// NBTFile will automatically create the file if it does not exist
NBTFile file = new NBTFile(new File("directory"), "test.nbt"); // Will be created at ./directory/test.nbt
// Setting data
file.setString("foo", "bar");
// Saving file
file.save();
NBTFileHandle allows working with nbt files.
// Alternatively, you may use helper methods in NBTFile class
// To read the file without creating it if does not exist:
ReadWriteNBT nbt = NBTFile.readFrom(file);
// To save nbt to the file:
NBTFile.saveTo(file, nbt);
```java
// Will automatically create the file if it does not exist
NBTFileHandle nbtFile = NBT.getFileHandle(new File("directory", "test.nbt"));
// Setting data
nbtFile.setString("foo", "bar");
// Saving the file after applying changes
nbtFile.save();
```
Alternatively, you may read the nbt file without maintaining the link to it.
Unlike NBTFileHandle, the file won't be automatically created if it does not exist.
```java
File file = new File("directory", "test.nbt");
// Reading data from file (will return an empty compound if the file does not exist)
ReadWriteNBT nbt = NBT.readFile(file);
// Saving nbt to file
NBT.writeFile(file, nbt);
```
### Converting Minecraft Objects to NBT and Strings
The basic idea is that you can convert data into different representations:
Minecraft Objects <-> NBT <-> String (Mojang-Json)
Minecraft Objects <-> NBT <-> String (SNBT)
#### Items
@@ -318,8 +389,8 @@ ReadWriteNBT nbt = NBT.itemStackArrayToNBT(itemStacks);
ItemStack itemStack = NBT.itemStackFromNBT(nbt);
ItemStack[] itemStacks = NBT.itemStackArrayFromNBT(nbt);
// Reminder (NBT <-> String)
String json = nbt.toString();
ReadWriteNBT nbt = NBT.parseNBT(json);
String snbt = nbt.toString();
ReadWriteNBT nbt = NBT.parseNBT(snbt);
```
#### Entities and block entities