Compare commits
188
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5280ba82a2 | ||
|
|
522f732ed1 | ||
|
|
ac84a4d69a | ||
|
|
d7e18c1cb6 | ||
|
|
ffa612634a | ||
|
|
739682122b | ||
|
|
2e20af40e5 | ||
|
|
e6527515be | ||
|
|
e6f33a35ae | ||
|
|
39801ada18 | ||
|
|
75fab7622a | ||
|
|
33eef1bcf1 | ||
|
|
877a42fd06 | ||
|
|
0a7e560e2e | ||
|
|
174728a681 | ||
|
|
79ba1dd275 | ||
|
|
25c59b0eae | ||
|
|
0057d3f2e8 | ||
|
|
59c67a3f0b | ||
|
|
3ec606ae21 | ||
|
|
ff0a94f279 | ||
|
|
53216d3b7d | ||
|
|
7cf23201ce | ||
|
|
fe1d586f44 | ||
|
|
956ef15a1d | ||
|
|
ed867749f8 | ||
|
|
88fbc0140e | ||
|
|
82e52267b8 | ||
|
|
c16548ff4e | ||
|
|
cf30cf996e | ||
|
|
633433d6d0 | ||
|
|
153c72818a | ||
|
|
4798880a53 | ||
|
|
1f149dbae4 | ||
|
|
8570c6f77e | ||
|
|
30f75e0714 | ||
|
|
af7a6b0a1c | ||
|
|
c0a9197113 | ||
|
|
e14dd4f82a | ||
|
|
198dbfabae | ||
|
|
8bd49db49b | ||
|
|
caf1d7cd98 | ||
|
|
027f2a2edd | ||
|
|
b74b0b852e | ||
|
|
6ea6ab7e13 | ||
|
|
372225626c | ||
|
|
2483f9b947 | ||
|
|
fdc16aad69 | ||
|
|
c723eef68b | ||
|
|
03299012ac | ||
|
|
eabf0a9696 | ||
|
|
9367942b54 | ||
|
|
167487e6ab | ||
|
|
6395dd1964 | ||
|
|
d3ef690f2e | ||
|
|
012f32dca9 | ||
|
|
0b8d5b153c | ||
|
|
246e3fa38e | ||
|
|
19777de6d4 | ||
|
|
3afd9df2ae | ||
|
|
2cb16ed0c4 | ||
|
|
ac64acf271 | ||
|
|
85cc13a817 | ||
|
|
75c8575b5b | ||
|
|
67557d5f94 | ||
|
|
330fb7711c | ||
|
|
e7f48c72bd | ||
|
|
3eefb1d315 | ||
|
|
a37a2c6729 | ||
|
|
e42fad0761 | ||
|
|
eee2dfa181 | ||
|
|
4728449294 | ||
|
|
ccd78d1b00 | ||
|
|
83bea69710 | ||
|
|
87fd1ee8ee | ||
|
|
af0528328f | ||
|
|
86c802e2db | ||
|
|
7bf91d0510 | ||
|
|
aed5af12a2 | ||
|
|
dd6e9e8312 | ||
|
|
42871b6c06 | ||
|
|
4ad7a172a0 | ||
|
|
6f112c0130 | ||
|
|
5a948f017a | ||
|
|
f5a1b21761 | ||
|
|
4af722752e | ||
|
|
af3b7a49d0 | ||
|
|
189ff2f369 | ||
|
|
ddf8ab3c9b | ||
|
|
a8a81eee83 | ||
|
|
fb384784f0 | ||
|
|
256abbc500 | ||
|
|
dc333ae25a | ||
|
|
d18adb459d | ||
|
|
bc914b73bc | ||
|
|
60172ea2a7 | ||
|
|
7b30c1533e | ||
|
|
a1c90658f3 | ||
|
|
0b0fdeaa60 | ||
|
|
d989797453 | ||
|
|
c70aefe670 | ||
|
|
7d0fc09470 | ||
|
|
427113e6f0 | ||
|
|
20ae1cb178 | ||
|
|
83b011fafa | ||
|
|
3affeee151 | ||
|
|
06da2114ad | ||
|
|
a3efdc622b | ||
|
|
9b60978af6 | ||
|
|
598db19d8d | ||
|
|
7a914d00e7 | ||
|
|
7141c56319 | ||
|
|
992d9717ec | ||
|
|
c5c2652d42 | ||
|
|
fb8e2272b3 | ||
|
|
120d087edc | ||
|
|
12c98d7d94 | ||
|
|
51d06817bc | ||
|
|
3bb4d7b449 | ||
|
|
f562994d18 | ||
|
|
f86d234c9c | ||
|
|
cb8e24428b | ||
|
|
30bd0bcbaf | ||
|
|
bb4d3bb9b0 | ||
|
|
523ce8cf60 | ||
|
|
d3d7a3138c | ||
|
|
e71bf88d12 | ||
|
|
1ba6fc8706 | ||
|
|
a341034de0 | ||
|
|
b45b25c255 | ||
|
|
c7eaa51bdb | ||
|
|
6b1d4e28ed | ||
|
|
4341cb33ea | ||
|
|
bde0bf7c8d | ||
|
|
55ab9a2918 | ||
|
|
7f4d134bd0 | ||
|
|
6e8b8f3565 | ||
|
|
a009b521c0 | ||
|
|
54d0db8189 | ||
|
|
66ffcf2866 | ||
|
|
d653bc67d8 | ||
|
|
533765ba74 | ||
|
|
0232f720cb | ||
|
|
205ca93977 | ||
|
|
fa62c9825d | ||
|
|
5051bc834d | ||
|
|
10ea4b1767 | ||
|
|
b801b46bce | ||
|
|
fc94dac05b | ||
|
|
1ab5f70c88 | ||
|
|
af6a11d649 | ||
|
|
c90636970a | ||
|
|
f3b1cc27e7 | ||
|
|
33eddbaece | ||
|
|
20f9b2f1ce | ||
|
|
e8c24c6bc5 | ||
|
|
affad0d92c | ||
|
|
ba169438ba | ||
|
|
c2e23af84f | ||
|
|
9ff2e38d07 | ||
|
|
5b12aa70e7 | ||
|
|
5424282fe6 | ||
|
|
8b76359143 | ||
|
|
2c6d54b32c | ||
|
|
a341c6a81e | ||
|
|
23b9f9128f | ||
|
|
81c3b75dc9 | ||
|
|
d3b1d00e15 | ||
|
|
177d1fe5b7 | ||
|
|
f1682205d8 | ||
|
|
ced5b15023 | ||
|
|
f6a150d5be | ||
|
|
1bfdb93622 | ||
|
|
09af2df542 | ||
|
|
cd1bdc0436 | ||
|
|
b2ea170f9e | ||
|
|
769ba78aa1 | ||
|
|
9bdd79ef7d | ||
|
|
55893bac8a | ||
|
|
bfe627658e | ||
|
|
a929bd2ddc | ||
|
|
3a966e0478 | ||
|
|
62105ead48 | ||
|
|
84e096d8b4 | ||
|
|
8bc066872c | ||
|
|
4491f6780a | ||
|
|
af19868289 | ||
|
|
4c538aad7b |
No files matched your search
@@ -1,176 +0,0 @@
|
||||
{
|
||||
"projectName": "Item-NBT-API",
|
||||
"projectOwner": "tr7zw",
|
||||
"imageSize": 100,
|
||||
"contributorsPerLine": 7,
|
||||
"repoHost": "https://github.com",
|
||||
"types": {
|
||||
"discord": {
|
||||
"symbol": "🤝",
|
||||
"description": "Discord help"
|
||||
}
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"login": "sgdc3",
|
||||
"name": "Gabriele C.",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8779252?s=460&v=4",
|
||||
"profile": "https://github.com/sgdc3",
|
||||
"contributions": [
|
||||
"code",
|
||||
"infra"
|
||||
],
|
||||
"links": {
|
||||
"infra": "https://codemc.io/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "SoSeDiK",
|
||||
"name": "SoSeDiK",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/19875118?s=460&v=4",
|
||||
"profile": "https://github.com/SoSeDiK",
|
||||
"contributions": [
|
||||
"code",
|
||||
"userTesting"
|
||||
],
|
||||
"links": {
|
||||
"userTesting": "https://github.com/SoSeDiK?tab=repositories"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "e3ndr",
|
||||
"name": "Ender",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/33337309?s=460&v=4",
|
||||
"profile": "https://github.com/e3ndr",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"discord"
|
||||
],
|
||||
"links": {
|
||||
"doc": "https://github.com/tr7zw/Item-NBT-API/wiki"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "inventivetalentdev",
|
||||
"name": "Haylee Schäfer",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/6525296?s=460&v=4",
|
||||
"profile": "https://github.com/inventivetalentdev",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas"
|
||||
],
|
||||
"links": {
|
||||
"code": "https://github.com/tr7zw/Item-NBT-API/tree/master/nbt-injector",
|
||||
"ideas": "https://github.com/InventivetalentDev/NBTInjector"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "SpiGet",
|
||||
"name": "SpiGet",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/15632072?s=200&v=4",
|
||||
"profile": "https://spiget.org/",
|
||||
"contributions": [
|
||||
"plugin"
|
||||
],
|
||||
"links": {
|
||||
"plugin": "https://github.com/tr7zw/Item-NBT-API/blob/master/item-nbt-api/src/main/java/de/tr7zw/changeme/nbtapi/utils/VersionChecker.java"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "bStats",
|
||||
"name": "bStats",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/5033001?s=460&v=4",
|
||||
"profile": "https://github.com/Bastian/bStats",
|
||||
"contributions": [
|
||||
"plugin"
|
||||
],
|
||||
"links": {
|
||||
"plugin": "https://bstats.org/plugin/bukkit/ItemNBTAPI/1058"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "MrAxeTv",
|
||||
"name": "MrAxeTv",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/17033332?s=460&v=4",
|
||||
"profile": "https://github.com/MrAxeTv",
|
||||
"contributions": [
|
||||
"financial",
|
||||
"userTesting",
|
||||
"discord"
|
||||
],
|
||||
"links": {
|
||||
"financial": "https://tr7zw.dev/donate/",
|
||||
"userTesting": "https://www.spigotmc.org/resources/beastwithdraw-multy-withdraw-plugin.13896/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "MrDienns",
|
||||
"name": "Dennis van der Veeke",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/15938901?s=460&v=4",
|
||||
"profile": "https://github.com/MrDienns",
|
||||
"contributions": [
|
||||
"financial"
|
||||
],
|
||||
"links": {
|
||||
"financial": "https://tr7zw.dev/donate/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "iGabyTM",
|
||||
"name": "Gabriel Dumitru",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/32105478?s=460&v=4",
|
||||
"profile": "https://github.com/iGabyTM",
|
||||
"contributions": [
|
||||
"discord"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TauCubed",
|
||||
"name": "TauCubed",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/35522873?s=460&v=4",
|
||||
"profile": "https://github.com/TauCubed",
|
||||
"contributions": [
|
||||
"discord"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LoneDev",
|
||||
"name": "LoneDev",
|
||||
"avatar_url": "https://www.spigotmc.org/data/avatars/l/88/88296.jpg?1563463602",
|
||||
"profile": "https://www.spigotmc.org/members/lonedev.88296",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas",
|
||||
"financial"
|
||||
],
|
||||
"links": {
|
||||
"ideas": "https://www.spigotmc.org/threads/nbt-api.70503/page-7#post-3652832",
|
||||
"financial": "https://tr7zw.dev/donate/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "GC.",
|
||||
"name": "GC.",
|
||||
"avatar_url": "https://www.spigotmc.org/data/avatars/l/25/25090.jpg?1502396477",
|
||||
"profile": "https://www.spigotmc.org/members/gc.25090",
|
||||
"contributions": [
|
||||
"financial"
|
||||
],
|
||||
"links": {
|
||||
"financial": "https://tr7zw.dev/donate/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"login": "Peaches_MLG",
|
||||
"name": "Peaches_MLG",
|
||||
"avatar_url": "https://www.spigotmc.org/data/avatars/l/194/194338.jpg?1561235996",
|
||||
"profile": "https://www.spigotmc.org/members/peaches_mlg100.194338/",
|
||||
"contributions": [
|
||||
"financial"
|
||||
],
|
||||
"links": {
|
||||
"financial": "https://tr7zw.dev/donate/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 - 2021, tr7zw
|
||||
Copyright (c) 2015 - 2024, tr7zw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,54 +1,35 @@
|
||||
# Item-NBT-API
|
||||
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it Files, other NBT or as String in yaml/json/SQL/Redis.
|
||||
|
||||
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
|
||||
[](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)
|
||||
|
||||
[](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)[](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)[](https://sonarcloud.io/summary/overall?id=tr7zw_Item-NBT-API)
|
||||
[](https://discordapp.com/invite/yk4caxM)
|
||||
[](https://www.patreon.com/tr7zw)
|
||||
|
||||
### SpigotMC Status
|
||||
|
||||
[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://bstats.org/plugin/bukkit/ItemNBTAPI)[](https://bstats.org/plugin/bukkit/ItemNBTAPI)[](#contributors-%e2%9c%a8)
|
||||
|
||||
## Getting started
|
||||
Import the API [using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API).
|
||||
|
||||
Import the API using [Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven) or [Gradle](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Gradle), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Example-Usages#set-a-skulls-skin).
|
||||
|
||||
### bStats
|
||||
|
||||
[](https://bstats.org/plugin/bukkit/ItemNBTAPI)
|
||||
|
||||
### Github Stargazers
|
||||
|
||||
<img src="https://starchart.cc/tr7zw/Item-NBT-API.svg" alt="stargazers" width="800"/>
|
||||
|
||||
## Contributors ✨
|
||||
### Supported by YourKit
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/sgdc3"><img src="https://avatars3.githubusercontent.com/u/8779252?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Gabriele C.</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=sgdc3" title="Code">💻</a> <a href="https://codemc.io/" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center"><a href="https://github.com/SoSeDiK"><img src="https://avatars0.githubusercontent.com/u/19875118?s=460&v=4" width="100px;" alt=""/><br /><sub><b>SoSeDiK</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=SoSeDiK" title="Code">💻</a> <a href="https://github.com/SoSeDiK?tab=repositories" title="User Testing">📓</a></td>
|
||||
<td align="center"><a href="https://github.com/e3ndr"><img src="https://avatars1.githubusercontent.com/u/33337309?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Ender</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/wiki" title="Documentation">📖</a> <a href="#discord-e3ndr" title="Discord help">🤝</a></td>
|
||||
<td align="center"><a href="https://github.com/inventivetalentdev"><img src="https://avatars3.githubusercontent.com/u/6525296?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Haylee Schäfer</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/tree/master/nbt-injector" title="Code">💻</a> <a href="https://github.com/InventivetalentDev/NBTInjector" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://spiget.org/"><img src="https://avatars3.githubusercontent.com/u/15632072?s=200&v=4" width="100px;" alt=""/><br /><sub><b>SpiGet</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/blob/master/item-nbt-api/src/main/java/de/tr7zw/changeme/nbtapi/utils/VersionChecker.java" title="Plugin/utility libraries">🔌</a></td>
|
||||
<td align="center"><a href="https://github.com/Bastian/bStats"><img src="https://avatars2.githubusercontent.com/u/5033001?s=460&v=4" width="100px;" alt=""/><br /><sub><b>bStats</b></sub></a><br /><a href="https://bstats.org/plugin/bukkit/ItemNBTAPI/1058" title="Plugin/utility libraries">🔌</a></td>
|
||||
<td align="center"><a href="https://github.com/MrAxeTv"><img src="https://avatars0.githubusercontent.com/u/17033332?s=460&v=4" width="100px;" alt=""/><br /><sub><b>MrAxeTv</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a> <a href="https://www.spigotmc.org/resources/beastwithdraw-multy-withdraw-plugin.13896/" title="User Testing">📓</a> <a href="#discord-MrAxeTv" title="Discord help">🤝</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/MrDienns"><img src="https://avatars3.githubusercontent.com/u/15938901?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Dennis van der Veeke</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
|
||||
<td align="center"><a href="https://github.com/iGabyTM"><img src="https://avatars2.githubusercontent.com/u/32105478?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Gabriel Dumitru</b></sub></a><br /><a href="#discord-iGabyTM" title="Discord help">🤝</a></td>
|
||||
<td align="center"><a href="https://github.com/TauCubed"><img src="https://avatars0.githubusercontent.com/u/35522873?s=460&v=4" width="100px;" alt=""/><br /><sub><b>TauCubed</b></sub></a><br /><a href="#discord-TauCubed" title="Discord help">🤝</a></td>
|
||||
<td align="center"><a href="https://www.spigotmc.org/members/lonedev.88296"><img src="https://www.spigotmc.org/data/avatars/l/88/88296.jpg?1563463602" width="100px;" alt=""/><br /><sub><b>LoneDev</b></sub></a><br /><a href="https://github.com/tr7zw/Item-NBT-API/commits?author=LoneDev" title="Code">💻</a> <a href="https://www.spigotmc.org/threads/nbt-api.70503/page-7#post-3652832" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
|
||||
<td align="center"><a href="https://www.spigotmc.org/members/gc.25090"><img src="https://www.spigotmc.org/data/avatars/l/25/25090.jpg?1502396477" width="100px;" alt=""/><br /><sub><b>GC.</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
|
||||
<td align="center"><a href="https://www.spigotmc.org/members/peaches_mlg100.194338/"><img src="https://www.spigotmc.org/data/avatars/l/194/194338.jpg?1561235996" width="100px;" alt=""/><br /><sub><b>Peaches_MLG</b></sub></a><br /><a href="https://tr7zw.dev/donate/" title="Financial Support">💵</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://allcontributors.org) specification.
|
||||
Contributions of any kind are welcome! Shoot me a message or pullrequest to be added!
|
||||

|
||||
YourKit supports open source projects with innovative and intelligent tools
|
||||
for monitoring and profiling Java and .NET applications.
|
||||
YourKit is the creator of <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>,
|
||||
<a href="https://www.yourkit.com/.net/profiler/">YourKit .NET Profiler</a>,
|
||||
and <a href="https://www.yourkit.com/youmonitor/">YourKit YouMonitor</a>.
|
||||
+7
-2
@@ -1,16 +1,21 @@
|
||||
# (Item-)NBT-API
|
||||
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it Files, other NBT or as String in yaml/json/SQL/Redis.
|
||||
|
||||
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
|
||||
|
||||
[](https://ci.codemc.org/job/Tr7zw/job/Item-NBT-API/)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)[](https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent)
|
||||
Discord:[](https://discordapp.com/invite/yk4caxM)
|
||||
|
||||
### SpigotMC Status
|
||||
|
||||
[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/)[](https://bstats.org/plugin/bukkit/ItemNBTAPI)[](https://bstats.org/plugin/bukkit/ItemNBTAPI)
|
||||
|
||||
## Getting started
|
||||
Import the API [using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API).
|
||||
|
||||
Import the API using [Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven) or [Gradle](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Gradle), then check out the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Example-Usages#set-a-skulls-skin).
|
||||
|
||||
### bStats
|
||||
|
||||
[](https://bstats.org/plugin/bukkit/ItemNBTAPI)
|
||||
+8
-14
@@ -5,19 +5,19 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>functional-annotations</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>datafixerupper</artifactId>
|
||||
<version>4.0.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -32,15 +32,9 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.annotations</pattern>
|
||||
<shadedPattern>de.tr7zw.changeme.nbtapi.utils.annotations</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>LICENSE</resource>
|
||||
|
||||
@@ -2,6 +2,9 @@ package de.tr7zw.changeme.nbtapi;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -11,7 +14,14 @@ import com.mojang.authlib.GameProfile;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteItemNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableItemNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.ProxyBuilder;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* General utility class for a clean and simple nbt access.
|
||||
@@ -19,12 +29,64 @@ import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class NBT {
|
||||
|
||||
private NBT() {
|
||||
// No instances of NBT. Utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for shaded versions to preload and check the API during
|
||||
* onEnable. This method does not throw an exception and instead logs them. Will
|
||||
* return false if something fundamentally is wrong and the NBTAPI is in a non
|
||||
* functioning state. The shading plugin then needs to handle this. Note:
|
||||
* Calling this method during onLoad will cause an failure, so please wait till
|
||||
* onEnable.
|
||||
*
|
||||
* @return true if everything went fine
|
||||
*/
|
||||
public static boolean preloadApi() {
|
||||
try {
|
||||
// boiled down version of the plugin selfcheck without tests
|
||||
if (MinecraftVersion.getVersion() == MinecraftVersion.UNKNOWN) {
|
||||
NbtApiException.confirmedBroken = true;
|
||||
return false;
|
||||
}
|
||||
for (ClassWrapper c : ClassWrapper.values()) {
|
||||
if (c.isEnabled() && c.getClazz() == null) {
|
||||
NbtApiException.confirmedBroken = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (ReflectionMethod method : ReflectionMethod.values()) {
|
||||
if (method.isCompatible() && !method.isLoaded()) {
|
||||
NbtApiException.confirmedBroken = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// not settings NbtApiException.confirmedBroken = false, as no actual tests were done.
|
||||
// This just means the version was found, and all reflections seem to work.
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
NbtApiException.confirmedBroken = true;
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error during the selfcheck!", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a read only instance of the items NBT. This method is slightly slower
|
||||
* than calling NBT.get due to having to create a copy of the ItemStack, but
|
||||
* allows context free access to the data.
|
||||
*
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
public static ReadableNBT readNbt(ItemStack item) {
|
||||
return new NBTItem(item.clone(), false, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack, and a function that takes a ReadableNBT and returns a
|
||||
* generic type T. It then returns the result of the function applied to a new
|
||||
@@ -35,8 +97,26 @@ public class NBT {
|
||||
* T.
|
||||
* @return The function is being returned.
|
||||
*/
|
||||
public static <T> T get(ItemStack item, Function<ReadableNBT, T> getter) {
|
||||
return getter.apply(new NBTItem(item, false, true));
|
||||
public static <T> T get(ItemStack item, Function<ReadableItemNBT, T> getter) {
|
||||
NBTItem nbt = new NBTItem(item, false, true, false);
|
||||
T ret = getter.apply(nbt);
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbt.setClosed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack, and a Consumer that takes a ReadableNBT. Applies the
|
||||
* Consumer on the NBT of the item
|
||||
*
|
||||
* @param item The itemstack you want to get the NBT from
|
||||
*/
|
||||
public static void get(ItemStack item, Consumer<ReadableItemNBT> getter) {
|
||||
NBTItem nbt = new NBTItem(item, false, true, false);
|
||||
getter.accept(nbt);
|
||||
nbt.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +128,25 @@ public class NBT {
|
||||
* @return The NBTEntity class is being returned.
|
||||
*/
|
||||
public static <T> T get(Entity entity, Function<ReadableNBT, T> getter) {
|
||||
return getter.apply(new NBTEntity(entity, true));
|
||||
NBTEntity nbt = new NBTEntity(entity, true);
|
||||
T ret = getter.apply(nbt);
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbt.setClosed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an Entity, and a Consumer that takes a ReadableNBT. Applies the
|
||||
* Consumer on the NBT of the Entity
|
||||
*
|
||||
* @param entity The entity to get the NBT from
|
||||
*/
|
||||
public static void get(Entity entity, Consumer<ReadableNBT> getter) {
|
||||
NBTEntity nbt = new NBTEntity(entity, true);
|
||||
getter.accept(nbt);
|
||||
nbt.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +160,25 @@ public class NBT {
|
||||
* @return The return type is the same as the type of the getter function.
|
||||
*/
|
||||
public static <T> T get(BlockState blockState, Function<ReadableNBT, T> getter) {
|
||||
return getter.apply(new NBTTileEntity(blockState, true));
|
||||
NBTTileEntity nbt = new NBTTileEntity(blockState, true);
|
||||
T ret = getter.apply(nbt);
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbt.setClosed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an BlockEntity, and a Consumer that takes a ReadableNBT. Applies the
|
||||
* Consumer on the NBT of the BlockEntity
|
||||
*
|
||||
* @param blockState The block state of the block you want to get the NBT from.
|
||||
*/
|
||||
public static void get(BlockState blockState, Consumer<ReadableNBT> getter) {
|
||||
NBTTileEntity nbt = new NBTTileEntity(blockState, true);
|
||||
getter.accept(nbt);
|
||||
nbt.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,11 +192,15 @@ public class NBT {
|
||||
* @return The return type is T, which is a generic type.
|
||||
*/
|
||||
public static <T> T getPersistentData(Entity entity, Function<ReadableNBT, T> getter) {
|
||||
return getter.apply(new NBTEntity(entity).getPersistentDataContainer());
|
||||
T ret = getter.apply(new NBTEntity(entity).getPersistentDataContainer());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an block entity and a function that takes a ReadableNBT and returns
|
||||
* It takes a block entity and a function that takes a ReadableNBT and returns
|
||||
* a generic type T, and returns the result of the function, applied to the
|
||||
* block entities persistent data container
|
||||
*
|
||||
@@ -90,7 +210,11 @@ public class NBT {
|
||||
* @return The value of the NBT tag.
|
||||
*/
|
||||
public static <T> T getPersistentData(BlockState blockState, Function<ReadableNBT, T> getter) {
|
||||
return getter.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
|
||||
T ret = getter.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,9 +226,13 @@ public class NBT {
|
||||
* @return The return value of the function.
|
||||
*/
|
||||
public static <T> T modify(ItemStack item, Function<ReadWriteItemNBT, T> function) {
|
||||
NBTItem nbti = new NBTItem(item, true);
|
||||
NBTItem nbti = new NBTItem(item, false, false, true);
|
||||
T val = function.apply(nbti);
|
||||
nbti.applyNBT(item);
|
||||
nbti.finalizeChanges();
|
||||
if (val instanceof ReadableNBT || val instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbti.setClosed();
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -116,9 +244,10 @@ public class NBT {
|
||||
* @param consumer The consumer that will be used to modify the NBT.
|
||||
*/
|
||||
public static void modify(ItemStack item, Consumer<ReadWriteItemNBT> consumer) {
|
||||
NBTItem nbti = new NBTItem(item, true);
|
||||
NBTItem nbti = new NBTItem(item, false, false, true);
|
||||
consumer.accept(nbti);
|
||||
nbti.applyNBT(item);
|
||||
nbti.finalizeChanges();
|
||||
nbti.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,6 +263,48 @@ public class NBT {
|
||||
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
|
||||
T ret = function.apply(cont);
|
||||
nbtEnt.setCompound(cont.getCompound());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbtEnt.setClosed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack and a Consumer<ReadWriteNBT>, 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.
|
||||
*
|
||||
* @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)) {
|
||||
throw new NbtApiException("This method only works for 1.20.5+!");
|
||||
}
|
||||
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
|
||||
consumer.accept(nbti.getOrCreateCompound("components"));
|
||||
ItemStack tmp = NBT.itemStackFromNBT(nbti);
|
||||
item.setItemMeta(tmp.getItemMeta());
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack and a Consumer<ReadWriteNBT>, 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.
|
||||
*
|
||||
* @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)) {
|
||||
throw new NbtApiException("This method only works for 1.20.5+!");
|
||||
}
|
||||
ReadWriteNBT nbti = NBT.itemStackToNBT(item);
|
||||
T ret = function.apply(nbti.getOrCreateCompound("components"));
|
||||
ItemStack tmp = NBT.itemStackFromNBT(nbti);
|
||||
item.setItemMeta(tmp.getItemMeta());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -149,6 +320,7 @@ public class NBT {
|
||||
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
|
||||
consumer.accept(cont);
|
||||
nbtEnt.setCompound(cont.getCompound());
|
||||
nbtEnt.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +333,11 @@ public class NBT {
|
||||
* @return The return type is the same as the return type of the function.
|
||||
*/
|
||||
public static <T> T modifyPersistentData(Entity entity, Function<ReadWriteNBT, T> function) {
|
||||
return function.apply(new NBTEntity(entity).getPersistentDataContainer());
|
||||
T ret = function.apply(new NBTEntity(entity).getPersistentDataContainer());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +363,11 @@ public class NBT {
|
||||
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
|
||||
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
|
||||
T ret = function.apply(cont);
|
||||
blockEnt.setCompound(cont);
|
||||
blockEnt.setCompound(cont.getCompound());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
blockEnt.setClosed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -204,7 +384,8 @@ public class NBT {
|
||||
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
|
||||
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
|
||||
consumer.accept(cont);
|
||||
blockEnt.setCompound(cont);
|
||||
blockEnt.setCompound(cont.getCompound());
|
||||
blockEnt.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +398,11 @@ public class NBT {
|
||||
* @return The return type is the same as the return type of the function.
|
||||
*/
|
||||
public static <T> T modifyPersistentData(BlockState blockState, Function<ReadWriteNBT, T> function) {
|
||||
return function.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
|
||||
T ret = function.apply(new NBTTileEntity(blockState).getPersistentDataContainer());
|
||||
if (ret instanceof ReadableNBT || ret instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +453,7 @@ public class NBT {
|
||||
* @param compound The NBT tag to convert to an ItemStack
|
||||
* @return An ItemStack
|
||||
*/
|
||||
@Nullable
|
||||
public static ItemStack itemStackFromNBT(ReadableNBT compound) {
|
||||
return NBTItem.convertNBTtoItem((NBTCompound) compound);
|
||||
}
|
||||
@@ -288,6 +474,7 @@ public class NBT {
|
||||
* @param compound The NBT tag to convert to an ItemStack array.
|
||||
* @return An array of ItemStacks.
|
||||
*/
|
||||
@Nullable
|
||||
public static ItemStack[] itemStackArrayFromNBT(ReadableNBT compound) {
|
||||
return NBTItem.convertNBTtoItemArray((NBTCompound) compound);
|
||||
}
|
||||
@@ -311,4 +498,138 @@ public class NBT {
|
||||
return new NBTContainer(nbtString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a read only proxy class for NBT, given an annotated interface.
|
||||
*
|
||||
* @param <T>
|
||||
* @param item
|
||||
* @param wrapper
|
||||
* @return
|
||||
*/
|
||||
public static <T extends NBTProxy> T readNbt(ItemStack item, Class<T> wrapper) {
|
||||
return new ProxyBuilder<>(new NBTItem(item, false, true, false), wrapper).readOnly().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a read only proxy class for NBT, given an annotated interface.
|
||||
*
|
||||
* @param <T>
|
||||
* @param entity
|
||||
* @param wrapper
|
||||
* @return
|
||||
*/
|
||||
public static <T extends NBTProxy> T readNbt(Entity entity, Class<T> wrapper) {
|
||||
return new ProxyBuilder<>(new NBTEntity(entity, true), wrapper).readOnly().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a read only proxy class for NBT, given an annotated interface.
|
||||
*
|
||||
* @param <T>
|
||||
* @param blockState
|
||||
* @param wrapper
|
||||
* @return
|
||||
*/
|
||||
public static <T extends NBTProxy> T readNbt(BlockState blockState, Class<T> wrapper) {
|
||||
return new ProxyBuilder<>(new NBTTileEntity(blockState, true), wrapper).readOnly().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack, applies a function to its NBT wrapped in a proxy, and
|
||||
* returns the result of the function
|
||||
*
|
||||
* @param item The item you want to
|
||||
* @param wrapper The target Proxy class
|
||||
* @param function The function that will be applied to the item.
|
||||
* @return The return value of the function.
|
||||
*/
|
||||
public static <T, X extends NBTProxy> T modify(ItemStack item, Class<X> wrapper, Function<X, T> function) {
|
||||
NBTItem nbti = new NBTItem(item, false, false, true);
|
||||
T val = function.apply(new ProxyBuilder<>(nbti, wrapper).build());
|
||||
nbti.finalizeChanges();
|
||||
if (val instanceof ReadableNBT || val instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
nbti.setClosed();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an ItemStack, applies a function to its NBT wrapped in a proxy.
|
||||
*
|
||||
* @param item The item you want to modify
|
||||
* @param wrapper The target Proxy class
|
||||
* @param consumer The consumer that will be used to modify the NBT.
|
||||
*/
|
||||
public static <X extends NBTProxy> void modify(ItemStack item, Class<X> wrapper, Consumer<X> consumer) {
|
||||
NBTItem nbti = new NBTItem(item, false, false, true);
|
||||
consumer.accept(new ProxyBuilder<>(nbti, wrapper).build());
|
||||
nbti.finalizeChanges();
|
||||
nbti.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an entity and a function to modify the entity via the proxy
|
||||
*
|
||||
* @param entity The entity to modify
|
||||
* @param wrapper The target Proxy class
|
||||
* @param consumer The consumer that will be called with the proxy.
|
||||
*/
|
||||
public static <X extends NBTProxy> void modify(Entity entity, Class<X> wrapper, Consumer<X> consumer) {
|
||||
NBTEntity nbtEnt = new NBTEntity(entity);
|
||||
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
|
||||
consumer.accept(new ProxyBuilder<>(cont, wrapper).build());
|
||||
nbtEnt.setCompound(cont.getCompound());
|
||||
cont.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an entity and a function to modify the entity via the proxy
|
||||
*
|
||||
* @param entity The entity to modify
|
||||
* @param wrapper The target Proxy class
|
||||
* @param function The Function that will be called with the proxy.
|
||||
* @return The return value of the function.
|
||||
*/
|
||||
public static <T, X extends NBTProxy> T modify(Entity entity, Class<X> wrapper, Function<X, T> function) {
|
||||
NBTEntity nbtEnt = new NBTEntity(entity);
|
||||
NBTContainer cont = new NBTContainer(nbtEnt.getCompound());
|
||||
T val = function.apply(new ProxyBuilder<>(cont, wrapper).build());
|
||||
nbtEnt.setCompound(cont.getCompound());
|
||||
cont.setClosed();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an block entity and a function to modify the entity via the proxy
|
||||
*
|
||||
* @param blockState The blockstate you want to modify
|
||||
* @param wrapper The target Proxy class
|
||||
* @param consumer The Consumer that will be called.
|
||||
*/
|
||||
public static <X extends NBTProxy> void modify(BlockState blockState, Class<X> wrapper, Consumer<X> consumer) {
|
||||
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
|
||||
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
|
||||
consumer.accept(new ProxyBuilder<>(cont, wrapper).build());
|
||||
blockEnt.setCompound(cont);
|
||||
cont.setClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an block entity and a function to modify the entity via the proxy
|
||||
*
|
||||
* @param blockState The blockstate you want to modify
|
||||
* @param wrapper The target Proxy class
|
||||
* @param function The function that will be called.
|
||||
* @return The return value of the function.
|
||||
*/
|
||||
public static <T, X extends NBTProxy> T modify(BlockState blockState, Class<X> wrapper, Function<X, T> function) {
|
||||
NBTTileEntity blockEnt = new NBTTileEntity(blockState);
|
||||
NBTContainer cont = new NBTContainer(blockEnt.getCompound());
|
||||
T val = function.apply(new ProxyBuilder<>(cont, wrapper).build());
|
||||
blockEnt.setCompound(cont);
|
||||
cont.setClosed();
|
||||
return val;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
* instead!
|
||||
*
|
||||
* - The data is really just on the location. If the block gets
|
||||
* broken/changed/explodes/moved etc, the data is still on that location!
|
||||
* broken/changed/exploded/moved etc., the data is still on that location!
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
|
||||
@@ -2,10 +2,8 @@ package de.tr7zw.changeme.nbtapi;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
|
||||
import de.tr7zw.annotations.FAUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
|
||||
|
||||
public class NBTChunk {
|
||||
|
||||
@@ -21,9 +19,8 @@ public class NBTChunk {
|
||||
*
|
||||
* @return NBTCompound containing the data of the PersistentDataAPI
|
||||
*/
|
||||
@AvailableSince(version = MinecraftVersion.MC1_16_R3)
|
||||
public NBTCompound getPersistentDataContainer() {
|
||||
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
|
||||
return new NBTPersistentDataContainer(chunk.getPersistentDataContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -12,9 +13,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.PathUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.PathUtil.PathSegment;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.Forge1710Mappings;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
@@ -33,10 +38,17 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
|
||||
private String compundName;
|
||||
private NBTCompound parent;
|
||||
private final boolean readOnly;
|
||||
private Object readOnlyCache;
|
||||
|
||||
protected NBTCompound(NBTCompound owner, String name) {
|
||||
this(owner, name, false);
|
||||
}
|
||||
|
||||
protected NBTCompound(NBTCompound owner, String name, boolean readOnly) {
|
||||
this.compundName = name;
|
||||
this.parent = owner;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
protected Lock getReadLock() {
|
||||
@@ -52,6 +64,52 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
parent.saveCompound();
|
||||
}
|
||||
|
||||
protected void setResolvedObject(Object object) {
|
||||
if (isClosed()) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
if (readOnly) {
|
||||
this.readOnlyCache = object;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setClosed() {
|
||||
if (parent != null) {
|
||||
parent.setClosed();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isClosed() {
|
||||
if (parent != null) {
|
||||
return parent.isClosed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
protected Object getResolvedObject() {
|
||||
if (isClosed()) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
if (readOnlyCache != null) {
|
||||
return readOnlyCache;
|
||||
}
|
||||
Object rootnbttag = getCompound();
|
||||
if (rootnbttag == null) {
|
||||
return null;
|
||||
}
|
||||
if (!NBTReflectionUtil.valideCompound(this))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = NBTReflectionUtil.getToCompount(rootnbttag, this);
|
||||
if (readOnly) {
|
||||
this.readOnlyCache = workingtag;
|
||||
}
|
||||
return workingtag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Compound name
|
||||
*/
|
||||
@@ -84,6 +142,9 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
* @param comp
|
||||
*/
|
||||
public void mergeCompound(NBTCompound comp) {
|
||||
if (comp == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.mergeOtherNBTCompound(this, comp);
|
||||
@@ -135,10 +196,6 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
}
|
||||
}
|
||||
|
||||
protected String getContent(String key) {
|
||||
return NBTReflectionUtil.getContent(this, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
@@ -403,6 +460,45 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* Requires at least 1.16
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
@Override
|
||||
public void setLongArray(String key, long[] value) {
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R1);
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_LONGARRAY, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* Requires at least 1.16
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
@Override
|
||||
public long[] getLongArray(String key) {
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R1);
|
||||
try {
|
||||
readLock.lock();
|
||||
return (long[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_LONGARRAY, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
@@ -689,7 +785,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
readLock.lock();
|
||||
if (getType(name) != NBTType.NBTTagCompound)
|
||||
return null;
|
||||
NBTCompound next = new NBTCompound(this, name);
|
||||
NBTCompound next = new NBTCompound(this, name, readOnly);
|
||||
if (NBTReflectionUtil.valideCompound(next))
|
||||
return next;
|
||||
return null;
|
||||
@@ -860,7 +956,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
* Returns the stored value if exists, or provided value otherwise.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[]},
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key key
|
||||
@@ -877,24 +973,26 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return defaultValue;
|
||||
|
||||
Class<?> clazz = defaultValue.getClass();
|
||||
if (clazz == Boolean.class)
|
||||
if (clazz == Boolean.class || clazz == boolean.class)
|
||||
return (T) getBoolean(key);
|
||||
if (clazz == Byte.class)
|
||||
if (clazz == Byte.class || clazz == byte.class)
|
||||
return (T) getByte(key);
|
||||
if (clazz == Short.class)
|
||||
if (clazz == Short.class || clazz == short.class)
|
||||
return (T) getShort(key);
|
||||
if (clazz == Integer.class)
|
||||
if (clazz == Integer.class || clazz == int.class)
|
||||
return (T) getInteger(key);
|
||||
if (clazz == Long.class)
|
||||
if (clazz == Long.class || clazz == long.class)
|
||||
return (T) getLong(key);
|
||||
if (clazz == Float.class)
|
||||
if (clazz == Float.class || clazz == float.class)
|
||||
return (T) getFloat(key);
|
||||
if (clazz == Double.class)
|
||||
if (clazz == Double.class || clazz == double.class)
|
||||
return (T) getDouble(key);
|
||||
if (clazz == byte[].class)
|
||||
return (T) getByteArray(key);
|
||||
if (clazz == int[].class)
|
||||
return (T) getIntArray(key);
|
||||
if (clazz == long[].class)
|
||||
return (T) getLongArray(key);
|
||||
if (clazz == String.class)
|
||||
return (T) getString(key);
|
||||
if (clazz == UUID.class) {
|
||||
@@ -914,7 +1012,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
* Returns the stored value if exists, or null.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[]},
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key key
|
||||
@@ -930,24 +1028,26 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
if (!hasTag(key))
|
||||
return null;
|
||||
|
||||
if (type == Boolean.class)
|
||||
if (type == Boolean.class || type == boolean.class)
|
||||
return (T) getBoolean(key);
|
||||
if (type == Byte.class)
|
||||
if (type == Byte.class || type == byte.class)
|
||||
return (T) getByte(key);
|
||||
if (type == Short.class)
|
||||
if (type == Short.class || type == short.class)
|
||||
return (T) getShort(key);
|
||||
if (type == Integer.class)
|
||||
if (type == Integer.class || type == int.class)
|
||||
return (T) getInteger(key);
|
||||
if (type == Long.class)
|
||||
if (type == Long.class || type == long.class)
|
||||
return (T) getLong(key);
|
||||
if (type == Float.class)
|
||||
if (type == Float.class || type == float.class)
|
||||
return (T) getFloat(key);
|
||||
if (type == Double.class)
|
||||
if (type == Double.class || type == double.class)
|
||||
return (T) getDouble(key);
|
||||
if (type == byte[].class)
|
||||
return (T) getByteArray(key);
|
||||
if (type == int[].class)
|
||||
return (T) getIntArray(key);
|
||||
if (type == long[].class)
|
||||
return (T) getLongArray(key);
|
||||
if (type == String.class)
|
||||
return (T) getString(key);
|
||||
if (type == UUID.class)
|
||||
@@ -958,6 +1058,238 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
throw new NbtApiException("Unsupported type for getOrNull: " + type.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T resolveOrNull(String key, Class<?> type) {
|
||||
List<PathSegment> keys = PathUtil.splitPath(key);
|
||||
NBTCompound tag = this;
|
||||
for (int i = 0; i < keys.size() - 1; i++) {
|
||||
PathSegment segment = keys.get(i);
|
||||
if (!segment.hasIndex()) {
|
||||
tag = tag.getCompound(segment.getPath());
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (tag.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& tag.getListType(segment.getPath()) == NBTType.NBTTagCompound) {
|
||||
NBTCompoundList list = tag.getCompoundList(segment.getPath());
|
||||
if (segment.getIndex() >= 0) {
|
||||
tag = list.get(segment.getIndex());
|
||||
} else {
|
||||
tag = list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PathSegment segment = keys.get(keys.size() - 1);
|
||||
if (!segment.hasIndex()) {
|
||||
return tag.getOrNull(segment.getPath(), type);
|
||||
} else {
|
||||
return getIndexedValue(tag, segment, (Class<T>)type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T resolveOrDefault(String key, T defaultValue) {
|
||||
List<PathSegment> keys = PathUtil.splitPath(key);
|
||||
NBTCompound tag = this;
|
||||
for (int i = 0; i < keys.size() - 1; i++) {
|
||||
PathSegment segment = keys.get(i);
|
||||
if (!segment.hasIndex()) {
|
||||
tag = tag.getCompound(segment.getPath());
|
||||
if (tag == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
if (tag.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& tag.getListType(segment.getPath()) == NBTType.NBTTagCompound) {
|
||||
NBTCompoundList list = tag.getCompoundList(segment.getPath());
|
||||
if (segment.getIndex() >= 0) {
|
||||
tag = list.get(segment.getIndex());
|
||||
} else {
|
||||
tag = list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PathSegment segment = keys.get(keys.size() - 1);
|
||||
if (!segment.hasIndex()) {
|
||||
return tag.getOrDefault(segment.getPath(), defaultValue);
|
||||
} else {
|
||||
return getIndexedValue(tag, segment, (Class<T>)defaultValue.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: before I'm even done writing this method, this sucks. Needs refactoring at some point
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getIndexedValue(NBTCompound comp, PathSegment segment, Class<T> type) {
|
||||
if (type == String.class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagString) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getStringList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<String> list = comp.getStringList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == int.class || type == Integer.class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagInt) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getIntegerList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<Integer> list = comp.getIntegerList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
} else if(comp.getType(segment.getPath()) == NBTType.NBTTagIntArray) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
int[] array = comp.getIntArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Integer) array[segment.getIndex()];
|
||||
}
|
||||
} else {
|
||||
int[] array = comp.getIntArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Integer) array[array.length + segment.getIndex()];
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == long.class || type == Long.class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagLong) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getLongList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<Long> list = comp.getLongList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
} else if(comp.getType(segment.getPath()) == NBTType.NBTTagLongArray) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
long[] array = comp.getLongArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Long) array[segment.getIndex()];
|
||||
}
|
||||
} else {
|
||||
long[] array = comp.getLongArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Long) array[array.length + segment.getIndex()];
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == float.class || type == Float.class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagFloat) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getFloatList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<Float> list = comp.getFloatList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == double.class || type == Double.class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagDouble) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getDoubleList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<Double> list = comp.getDoubleList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == int[].class) {
|
||||
if (comp.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& comp.getListType(segment.getPath()) == NBTType.NBTTagIntArray) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
return (T) comp.getIntArrayList(segment.getPath()).get(segment.getIndex());
|
||||
} else {
|
||||
List<int[]> list = comp.getIntArrayList(segment.getPath());
|
||||
return (T) list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
if (type == byte.class || type == Byte.class) {
|
||||
if(comp.getType(segment.getPath()) == NBTType.NBTTagByteArray) {
|
||||
if (segment.getIndex() >= 0) {
|
||||
byte[] array = comp.getByteArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Byte) array[segment.getIndex()];
|
||||
}
|
||||
} else {
|
||||
byte[] array = comp.getByteArray(segment.getPath());
|
||||
if(array != null) {
|
||||
return (T)(Byte) array[array.length + segment.getIndex()];
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NbtApiException("No fitting list/array found for " + segment.getPath() + " of type " + type);
|
||||
}
|
||||
throw new NbtApiException("Unable to get indexed value for type " + type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadWriteNBT resolveCompound(String key) {
|
||||
List<PathSegment> keys = PathUtil.splitPath(key);
|
||||
NBTCompound tag = this;
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
PathSegment segment = keys.get(i);
|
||||
if (!segment.hasIndex()) {
|
||||
tag = tag.getCompound(segment.getPath());
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (tag.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& tag.getListType(segment.getPath()) == NBTType.NBTTagCompound) {
|
||||
NBTCompoundList list = tag.getCompoundList(segment.getPath());
|
||||
if (segment.getIndex() >= 0) {
|
||||
tag = list.get(segment.getIndex());
|
||||
} else {
|
||||
tag = list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadWriteNBT resolveOrCreateCompound(String key) {
|
||||
List<PathSegment> keys = PathUtil.splitPath(key);
|
||||
NBTCompound tag = this;
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
PathSegment segment = keys.get(i);
|
||||
if (!segment.hasIndex()) {
|
||||
tag = tag.getOrCreateCompound(segment.getPath());
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (tag.getType(segment.getPath()) == NBTType.NBTTagList
|
||||
&& tag.getListType(segment.getPath()) == NBTType.NBTTagCompound) {
|
||||
NBTCompoundList list = tag.getCompoundList(segment.getPath());
|
||||
if (segment.getIndex() >= 0) {
|
||||
tag = list.get(segment.getIndex());
|
||||
} else {
|
||||
tag = list.get(list.size() + segment.getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a key to the given Enum value. It gets stored as a String. Passing null
|
||||
* as value will call removeKey(key) instead.
|
||||
@@ -1032,6 +1364,16 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(String key, NBTHandler<T> handler) {
|
||||
return handler.get(this, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(String key, T value, NBTHandler<T> handler) {
|
||||
handler.set(this, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
@@ -1077,7 +1419,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
public String asNBTString() {
|
||||
try {
|
||||
readLock.lock();
|
||||
Object comp = NBTReflectionUtil.gettoCompount(getCompound(), this);
|
||||
Object comp = getResolvedObject();
|
||||
if (comp == null)
|
||||
return "{}";
|
||||
if (MinecraftVersion.isForgePresent() && MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
|
||||
@@ -1118,7 +1460,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
|
||||
private static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
|
||||
if (compA.getType(key) != compB.getType(key))
|
||||
return false;
|
||||
switch (compA.getType(key)) {
|
||||
@@ -1126,8 +1468,10 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return compA.getByte(key).equals(compB.getByte(key));
|
||||
case NBTTagByteArray:
|
||||
return Arrays.equals(compA.getByteArray(key), compB.getByteArray(key));
|
||||
case NBTTagCompound:
|
||||
return compA.getCompound(key).equals(compB.getCompound(key));
|
||||
case NBTTagCompound: {
|
||||
NBTCompound tmp = compA.getCompound(key);
|
||||
return tmp != null && tmp.equals(compB.getCompound(key));
|
||||
}
|
||||
case NBTTagDouble:
|
||||
return compA.getDouble(key).equals(compB.getDouble(key));
|
||||
case NBTTagEnd:
|
||||
@@ -1147,6 +1491,8 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return compA.getShort(key).equals(compB.getShort(key));
|
||||
case NBTTagString:
|
||||
return compA.getString(key).equals(compB.getString(key));
|
||||
case NBTTagLongArray:
|
||||
return Arrays.equals(compA.getLongArray(key), compB.getLongArray(key));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public class NBTCompoundList extends NBTList<ReadWriteNBT> implements ReadWriteN
|
||||
* @return
|
||||
*/
|
||||
public NBTCompound addCompound(NBTCompound comp) {
|
||||
if (getParent().isReadOnly()) {
|
||||
throw new NbtApiException("Tried setting data in read only mode!");
|
||||
}
|
||||
try {
|
||||
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
@@ -79,6 +82,9 @@ public class NBTCompoundList extends NBTList<ReadWriteNBT> implements ReadWriteN
|
||||
if (element != null) {
|
||||
throw new NbtApiException("You need to pass null! ListCompounds from other lists won't work.");
|
||||
}
|
||||
if (getParent().isReadOnly()) {
|
||||
throw new NbtApiException("Tried setting data in read only mode!");
|
||||
}
|
||||
try {
|
||||
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
|
||||
@@ -16,6 +16,8 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
public class NBTContainer extends NBTCompound {
|
||||
|
||||
private Object nbt;
|
||||
private boolean closed;
|
||||
private boolean readOnly;
|
||||
|
||||
/**
|
||||
* Creates an empty, standalone NBTCompound
|
||||
@@ -33,7 +35,7 @@ public class NBTContainer extends NBTCompound {
|
||||
public NBTContainer(Object nbt) {
|
||||
super(null, null);
|
||||
if (nbt == null) {
|
||||
throw new NullPointerException("The NBT-Object can't be null!");
|
||||
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().isAssignableFrom(nbt.getClass())) {
|
||||
throw new NbtApiException("The object '" + nbt.getClass() + "' is not a valid NBT-Object!");
|
||||
@@ -79,4 +81,23 @@ public class NBTContainer extends NBTCompound {
|
||||
nbt = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClosed() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
protected boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
protected NBTContainer setReadOnly(boolean readOnly) {
|
||||
this.readOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,10 +3,8 @@ package de.tr7zw.changeme.nbtapi;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import de.tr7zw.annotations.FAUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
|
||||
|
||||
/**
|
||||
* NBT class to access vanilla tags from Entities. Entities don't support custom
|
||||
@@ -21,6 +19,7 @@ public class NBTEntity extends NBTCompound {
|
||||
private final Entity ent;
|
||||
private final boolean readonly;
|
||||
private final Object compound;
|
||||
private boolean closed = false;
|
||||
|
||||
/**
|
||||
* @param entity Any valid Bukkit Entity
|
||||
@@ -41,8 +40,11 @@ public class NBTEntity extends NBTCompound {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: Please use the NBT class
|
||||
*
|
||||
* @param entity Any valid Bukkit Entity
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTEntity(Entity entity) {
|
||||
super(null, null);
|
||||
if (entity == null) {
|
||||
@@ -53,6 +55,21 @@ public class NBTEntity extends NBTCompound {
|
||||
ent = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClosed() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReadOnly() {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
// this runs before async check, since it's just a copy
|
||||
@@ -80,9 +97,8 @@ public class NBTEntity extends NBTCompound {
|
||||
*
|
||||
* @return NBTCompound containing the data of the PersistentDataAPI
|
||||
*/
|
||||
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
|
||||
public NBTCompound getPersistentDataContainer() {
|
||||
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_14_R1);
|
||||
return new NBTPersistentDataContainer(ent.getPersistentDataContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public class NBTFile extends NBTCompound {
|
||||
private Object nbt;
|
||||
|
||||
/**
|
||||
* Creates a NBTFile that uses @param file to store it's data. If this file
|
||||
* Creates a NBTFile that uses @param file to store its data. If this file
|
||||
* exists, the data will be loaded.
|
||||
*
|
||||
* @param file
|
||||
@@ -99,7 +99,7 @@ public class NBTFile extends NBTCompound {
|
||||
if (!file.createNewFile())
|
||||
throw new IOException("Unable to create file at " + file.getAbsolutePath());
|
||||
}
|
||||
NBTReflectionUtil.writeNBT(nbt.getCompound(), Files.newOutputStream(file.toPath()));
|
||||
nbt.writeCompound(Files.newOutputStream(file.toPath()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package de.tr7zw.changeme.nbtapi;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.GameprofileUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
@@ -13,7 +15,11 @@ public class NBTGameProfile {
|
||||
* @param profile
|
||||
* @return A NBTContainer with all the GameProfile data
|
||||
*/
|
||||
@Deprecated
|
||||
public static NBTCompound toNBT(GameProfile profile) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return (NBTCompound) GameprofileUtil.writeGameProfile(NBT.createNBTObject(), profile);
|
||||
}
|
||||
return new NBTContainer(ReflectionMethod.GAMEPROFILE_SERIALIZE.run(null,
|
||||
ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance(), profile));
|
||||
}
|
||||
@@ -24,9 +30,13 @@ public class NBTGameProfile {
|
||||
* @param compound Has to contain GameProfile data
|
||||
* @return The reconstructed GameProfile
|
||||
*/
|
||||
@Deprecated
|
||||
public static GameProfile fromNBT(NBTCompound compound) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return GameprofileUtil.readGameProfile(compound);
|
||||
}
|
||||
return (GameProfile) ReflectionMethod.GAMEPROFILE_DESERIALIZE.run(null,
|
||||
NBTReflectionUtil.gettoCompount(compound.getCompound(), compound));
|
||||
NBTReflectionUtil.getToCompount(compound.getCompound(), compound));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package de.tr7zw.changeme.nbtapi;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
@@ -9,6 +12,7 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteItemNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
@@ -22,15 +26,20 @@ import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
|
||||
private ItemStack bukkitItem;
|
||||
private boolean directApply;
|
||||
private boolean readOnly;
|
||||
private final boolean directApply;
|
||||
private final boolean finalizer;
|
||||
private ItemStack originalSrcStack = null;
|
||||
private Object cachedCompound = null;
|
||||
private boolean closed = false;
|
||||
|
||||
/**
|
||||
* Constructor for NBTItems. The ItemStack will be cloned!
|
||||
* Constructor for NBTItems. The ItemStack will be cloned! Deprecated: Please
|
||||
* use the NBT class to work with items. It's up to 400% faster and provides
|
||||
* less ways to mess up code.
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTItem(ItemStack item) {
|
||||
this(item, false);
|
||||
}
|
||||
@@ -42,14 +51,19 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* Modifying the stack in that case is not valid! Also
|
||||
* overwrites directApply
|
||||
*/
|
||||
protected NBTItem(ItemStack item, boolean directApply, boolean readOnly) {
|
||||
super(null, null);
|
||||
protected NBTItem(ItemStack item, boolean directApply, boolean readOnly, boolean finalizer) {
|
||||
super(null, null, readOnly);
|
||||
if (item == null || item.getType() == Material.AIR || item.getAmount() <= 0) {
|
||||
throw new NullPointerException("ItemStack can't be null/air/amount of 0! This is not a NBTAPI bug!");
|
||||
}
|
||||
if (readOnly) {
|
||||
this.finalizer = finalizer;
|
||||
if (finalizer) {
|
||||
this.bukkitItem = item;
|
||||
this.originalSrcStack = item;
|
||||
this.directApply = false;
|
||||
} else if (readOnly) {
|
||||
bukkitItem = item;
|
||||
this.readOnly = true;
|
||||
this.directApply = false;
|
||||
} else {
|
||||
this.directApply = directApply;
|
||||
bukkitItem = item.clone();
|
||||
@@ -67,11 +81,13 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param item
|
||||
* @param directApply
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTItem(ItemStack item, boolean directApply) {
|
||||
super(null, null);
|
||||
if (item == null || item.getType() == Material.AIR || item.getAmount() <= 0) {
|
||||
throw new NullPointerException("ItemStack can't be null/air/amount of 0! This is not a NBTAPI bug!");
|
||||
}
|
||||
this.finalizer = false;
|
||||
this.directApply = directApply;
|
||||
bukkitItem = item.clone();
|
||||
if (directApply) {
|
||||
@@ -81,20 +97,87 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
if (readOnly && ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(bukkitItem.getClass())) {
|
||||
return NBTReflectionUtil.getItemRootNBTTagCompound(NBTReflectionUtil.getCraftItemHandle(bukkitItem));
|
||||
if (closed) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
if (isReadOnly() && (cachedCompound != null
|
||||
|| ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(bukkitItem.getClass()))) {
|
||||
if (cachedCompound == null) {
|
||||
cachedCompound = NBTReflectionUtil
|
||||
.getItemRootNBTTagCompound(NBTReflectionUtil.getCraftItemHandle(bukkitItem));
|
||||
}
|
||||
return cachedCompound;
|
||||
}
|
||||
if (finalizer) {
|
||||
if (cachedCompound == null) {
|
||||
updateCachedCompound();
|
||||
}
|
||||
return cachedCompound;
|
||||
}
|
||||
return NBTReflectionUtil.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
|
||||
}
|
||||
|
||||
private void updateCachedCompound() {
|
||||
if (finalizer) {
|
||||
cachedCompound = NBTReflectionUtil
|
||||
.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
|
||||
}
|
||||
}
|
||||
|
||||
protected void finalizeChanges() {
|
||||
if (!finalizer || cachedCompound == null) {
|
||||
return;
|
||||
}
|
||||
// There was data, but not anymore, delete the tag from the itemstack
|
||||
if (NBTReflectionUtil.getKeys(this).isEmpty()) {
|
||||
cachedCompound = null;
|
||||
}
|
||||
if (ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(originalSrcStack.getClass())) {
|
||||
Object nmsStack = NBTReflectionUtil.getCraftItemHandle(originalSrcStack);
|
||||
NBTReflectionUtil.setItemStackCompound(nmsStack, cachedCompound);
|
||||
bukkitItem = originalSrcStack;
|
||||
} else {
|
||||
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
|
||||
NBTReflectionUtil.setItemStackCompound(stack, cachedCompound);
|
||||
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
|
||||
originalSrcStack.setItemMeta(bukkitItem.getItemMeta());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClosed() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
if (readOnly) {
|
||||
if (isReadOnly()) {
|
||||
throw new NbtApiException("Tried setting data in read only mode!");
|
||||
}
|
||||
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
|
||||
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, compound);
|
||||
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
|
||||
if (closed) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
if (finalizer) {
|
||||
cachedCompound = compound;
|
||||
return;
|
||||
}
|
||||
if (compound != null && ((Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(compound)).isEmpty()) {
|
||||
compound = null;
|
||||
}
|
||||
if (ClassWrapper.CRAFT_ITEMSTACK.getClazz().isAssignableFrom(bukkitItem.getClass())) {
|
||||
Object nmsStack = NBTReflectionUtil.getCraftItemHandle(bukkitItem);
|
||||
NBTReflectionUtil.setItemStackCompound(nmsStack, compound);
|
||||
} else {
|
||||
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
|
||||
NBTReflectionUtil.setItemStackCompound(stack, compound);
|
||||
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +189,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
@Deprecated
|
||||
public void applyNBT(ItemStack item) {
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
throw new NullPointerException("ItemStack can't be null/Air! This is not a NBTAPI bug!");
|
||||
@@ -120,6 +204,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
@Deprecated
|
||||
public void mergeNBT(ItemStack item) {
|
||||
NBTItem nbti = new NBTItem(item);
|
||||
nbti.mergeCompound(this);
|
||||
@@ -131,10 +216,18 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
@Deprecated
|
||||
public void mergeCustomNBT(ItemStack item) {
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
throw new NullPointerException("ItemStack can't be null/Air!");
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
// 1.20.5+ doesn't have any vanilla tags
|
||||
NBT.modify(item, nbt -> {
|
||||
nbt.mergeCompound(this);
|
||||
});
|
||||
return;
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
NBTReflectionUtil.getUnhandledNBTTags(meta)
|
||||
.putAll(NBTReflectionUtil.getUnhandledNBTTags(bukkitItem.getItemMeta()));
|
||||
@@ -146,7 +239,13 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
*
|
||||
* @return true when custom tags are present
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean hasCustomNbtData() {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
// 1.20.5+ doesn't have any vanilla tags
|
||||
return hasNBTData();
|
||||
}
|
||||
finalizeChanges();
|
||||
ItemMeta meta = bukkitItem.getItemMeta();
|
||||
return !NBTReflectionUtil.getUnhandledNBTTags(meta).isEmpty();
|
||||
}
|
||||
@@ -154,10 +253,18 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
/**
|
||||
* Remove all custom (non-vanilla) NBT tags from the NBTItem.
|
||||
*/
|
||||
@Deprecated
|
||||
public void clearCustomNBT() {
|
||||
finalizeChanges();
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
// 1.20.5+ doesn't have any vanilla tags
|
||||
setCompound(null);
|
||||
return;
|
||||
}
|
||||
ItemMeta meta = bukkitItem.getItemMeta();
|
||||
NBTReflectionUtil.getUnhandledNBTTags(meta).clear();
|
||||
bukkitItem.setItemMeta(meta);
|
||||
updateCachedCompound();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +279,8 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
}
|
||||
|
||||
/**
|
||||
* This may return true even when the NBT is empty.
|
||||
* Returns true if the item has NBT data. This needs to be checked before
|
||||
* calling methods like remove, otherwise the value might be wrong!
|
||||
*
|
||||
* @return Does the ItemStack have a NBTCompound.
|
||||
*/
|
||||
@@ -191,10 +299,16 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param handler
|
||||
*/
|
||||
public void modifyMeta(BiConsumer<ReadableNBT, ItemMeta> handler) {
|
||||
finalizeChanges();
|
||||
ItemMeta meta = bukkitItem.getItemMeta();
|
||||
handler.accept(this, meta);
|
||||
handler.accept(new NBTContainer(getResolvedObject()).setReadOnly(true), meta);
|
||||
bukkitItem.setItemMeta(meta);
|
||||
updateCachedCompound();
|
||||
if (directApply) {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
throw new NbtApiException(
|
||||
"Direct apply mode meta changes don't work anymore in 1.20.5+. Please switch to the modern NBT.modify sytnax!");
|
||||
}
|
||||
applyNBT(originalSrcStack);
|
||||
}
|
||||
}
|
||||
@@ -210,10 +324,12 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param handler
|
||||
*/
|
||||
public <T extends ItemMeta> void modifyMeta(Class<T> type, BiConsumer<ReadableNBT, T> handler) {
|
||||
finalizeChanges();
|
||||
@SuppressWarnings("unchecked")
|
||||
T meta = (T) bukkitItem.getItemMeta();
|
||||
handler.accept(this, meta);
|
||||
handler.accept(new NBTContainer(getResolvedObject()).setReadOnly(true), meta);
|
||||
bukkitItem.setItemMeta(meta);
|
||||
updateCachedCompound();
|
||||
if (directApply) {
|
||||
applyNBT(originalSrcStack);
|
||||
}
|
||||
@@ -226,6 +342,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param item
|
||||
* @return Standalone {@link NBTContainer} with the Item's data
|
||||
*/
|
||||
@Deprecated
|
||||
public static NBTContainer convertItemtoNBT(ItemStack item) {
|
||||
return NBTReflectionUtil.convertNMSItemtoNBTCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, item));
|
||||
}
|
||||
@@ -237,6 +354,8 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param comp
|
||||
* @return ItemStack using the {@link NBTCompound}'s data
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public static ItemStack convertNBTtoItem(NBTCompound comp) {
|
||||
return (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null,
|
||||
NBTReflectionUtil.convertNBTCompoundtoNMSItem(comp));
|
||||
@@ -244,12 +363,13 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
|
||||
/**
|
||||
* Helper method that converts {@link ItemStack}[] to {@link NBTContainer} with
|
||||
* all it data like Material, Damage, Amount and Tags. This is a custom
|
||||
* all its data like Material, Damage, Amount and Tags. This is a custom
|
||||
* implementation and won't work with vanilla code(Shulker content etc).
|
||||
*
|
||||
* @param items
|
||||
* @return Standalone {@link NBTContainer} with the Item's data
|
||||
*/
|
||||
@Deprecated
|
||||
public static NBTContainer convertItemArraytoNBT(ItemStack[] items) {
|
||||
NBTContainer container = new NBTContainer();
|
||||
container.setInteger("size", items.length);
|
||||
@@ -269,7 +389,7 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
/**
|
||||
* Helper method to do the inverse of "convertItemArraytoNBT". Creates an
|
||||
* {@link ItemStack}[] using the {@link NBTCompound}. This is a custom
|
||||
* implementation and won't work with vanilla code(Shulker content etc).
|
||||
* implementation and won't work with vanilla code (Shulker content, etc.).
|
||||
*
|
||||
* Will return null for invalid data. Empty slots in the array are filled with
|
||||
* AIR Stacks!
|
||||
@@ -277,6 +397,8 @@ public class NBTItem extends NBTCompound implements ReadWriteItemNBT {
|
||||
* @param comp
|
||||
* @return ItemStack[] using the {@link NBTCompound}'s data
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public static ItemStack[] convertNBTtoItemArray(NBTCompound comp) {
|
||||
if (!comp.hasTag("size")) {
|
||||
return null;
|
||||
|
||||
@@ -48,7 +48,20 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private void validateClosed() {
|
||||
if (parent.isClosed()) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateWritable() {
|
||||
if (getParent().isReadOnly()) {
|
||||
throw new NbtApiException("Tried setting data in read only mode!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void save() {
|
||||
validateClosed();
|
||||
parent.set(listName, listObject);
|
||||
}
|
||||
|
||||
@@ -56,6 +69,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
validateClosed();
|
||||
validateWritable();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
@@ -74,6 +89,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
validateClosed();
|
||||
validateWritable();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
@@ -91,6 +108,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
validateClosed();
|
||||
validateWritable();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
T prev = get(index);
|
||||
@@ -106,6 +125,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public T remove(int i) {
|
||||
validateClosed();
|
||||
validateWritable();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
T old = get(i);
|
||||
@@ -121,6 +142,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
return (int) ReflectionMethod.LIST_SIZE.run(listObject);
|
||||
@@ -153,6 +175,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
@@ -167,6 +190,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
@@ -181,6 +205,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
@@ -195,6 +220,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
@@ -209,6 +235,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (Object ele : c) {
|
||||
@@ -223,6 +250,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
int index = -1;
|
||||
@@ -238,6 +266,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
@@ -252,6 +281,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
@@ -270,6 +300,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
@@ -375,6 +406,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
Object[] ar = new Object[size()];
|
||||
@@ -389,6 +421,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <E> E[] toArray(E[] a) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
E[] ar = Arrays.copyOf(a, size());
|
||||
@@ -410,6 +443,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex) {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
@@ -428,6 +462,7 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
validateClosed();
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
return listObject.toString();
|
||||
|
||||
@@ -24,13 +24,32 @@ public class NBTListCompound extends NBTCompound {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isClosed() {
|
||||
return owner.getParent().isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReadOnly() {
|
||||
return owner.getParent().isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
if (isClosed()) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
if (isClosed()) {
|
||||
throw new NbtApiException("Tried using closed NBT data!");
|
||||
}
|
||||
if (isReadOnly()) {
|
||||
throw new NbtApiException("Tried setting data in read only mode!");
|
||||
}
|
||||
this.compound = compound;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,23 @@ import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.GsonWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
@@ -34,6 +38,8 @@ public class NBTReflectionUtil {
|
||||
|
||||
private static Field field_unhandledTags = null;
|
||||
private static Field field_handle = null;
|
||||
private static Object type_custom_data = null;
|
||||
private static Object registry_access = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -48,6 +54,21 @@ public class NBTReflectionUtil {
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
||||
}
|
||||
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"));
|
||||
type_custom_data = typeField.get(null);
|
||||
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
||||
|
||||
}
|
||||
try {
|
||||
Object nmsServer = ReflectionMethod.NMSSERVER_GETSERVER.run(Bukkit.getServer());
|
||||
registry_access = ReflectionMethod.NMSSERVER_GETREGISTRYACCESS.run(nmsServer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +100,12 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static Object readNBT(InputStream stream) {
|
||||
try {
|
||||
return ReflectionMethod.NBTFILE_READ.run(null, stream);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R3)) {
|
||||
return ReflectionMethod.NBTFILE_READV2.run(null, stream,
|
||||
ReflectionMethod.NBTACCOUNTER_CREATE_UNLIMITED.run(null));
|
||||
} else {
|
||||
return ReflectionMethod.NBTFILE_READ.run(null, stream);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
stream.close();
|
||||
@@ -127,13 +153,7 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
|
||||
try {
|
||||
Object nbttag = comp.getCompound();
|
||||
if (nbttag == null) {
|
||||
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return;
|
||||
Object workingtag = gettoCompount(nbttag, comp);
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while writing NBT!", e);
|
||||
@@ -149,13 +169,40 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static Object getItemRootNBTTagCompound(Object nmsitem) {
|
||||
try {
|
||||
Object answer = ReflectionMethod.NMSITEM_GETTAG.run(nmsitem);
|
||||
return answer != null ? answer : ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
Object customData = ReflectionMethod.NMSDATACOMPONENTHOLDER_GET.run(nmsitem, type_custom_data);
|
||||
if (customData == null) {
|
||||
return null;
|
||||
}
|
||||
return ReflectionMethod.NMSCUSTOMDATA_GETCOPY.run(customData);
|
||||
} else {
|
||||
Object answer = ReflectionMethod.NMSITEM_GETTAG.run(nmsitem);
|
||||
return answer;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting an Itemstack's NBTCompound!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Compound as the Items NBT or CustomData
|
||||
*
|
||||
* @param nmsItem
|
||||
* @param compound
|
||||
*/
|
||||
public static void setItemStackCompound(Object nmsItem, Object compound) {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
if (compound == null) {
|
||||
ReflectionMethod.NMSITEM_SET.run(nmsItem, new Object[] { type_custom_data, null });
|
||||
} else {
|
||||
ReflectionMethod.NMSITEM_SET.run(nmsItem, type_custom_data,
|
||||
ObjectCreator.NMS_CUSTOMDATA.getInstance(compound));
|
||||
}
|
||||
} else {
|
||||
ReflectionMethod.ITEMSTACK_SET_TAG.run(nmsItem, compound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link NBTCompound} to NMS ItemStacks
|
||||
*
|
||||
@@ -164,8 +211,14 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static Object convertNBTCompoundtoNMSItem(NBTCompound nbtcompound) {
|
||||
try {
|
||||
Object nmsComp = gettoCompount(nbtcompound.getCompound(), nbtcompound);
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_11_R1.getVersionId()) {
|
||||
Object 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());
|
||||
}
|
||||
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);
|
||||
@@ -183,8 +236,13 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static NBTContainer convertNMSItemtoNBTCompound(Object nmsitem) {
|
||||
try {
|
||||
Object answer = ReflectionMethod.NMSITEM_SAVE.run(nmsitem, ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance());
|
||||
return new NBTContainer(answer);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return 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);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while converting NMS ItemStack to NBTCompound!", e);
|
||||
}
|
||||
@@ -197,6 +255,7 @@ public class NBTReflectionUtil {
|
||||
* @return Map containing unhandled (custom) NBT tags
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated
|
||||
public static Map<String, Object> getUnhandledNBTTags(ItemMeta meta) {
|
||||
try {
|
||||
return (Map<String, Object>) field_unhandledTags.get(meta);
|
||||
@@ -258,8 +317,15 @@ public class NBTReflectionUtil {
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
|
||||
}
|
||||
|
||||
if (o == null) {
|
||||
throw new NbtApiException("The passed BlockState(" + tile.getType()
|
||||
+ ") doesn't point to a BlockEntity. Only BlockEntities like Chest/Signs/Furnance/etc have NBT.");
|
||||
}
|
||||
|
||||
Object answer = null;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
|
||||
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);
|
||||
} else {
|
||||
answer = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
@@ -292,7 +358,9 @@ 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_17_R1)) {
|
||||
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);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
Object blockData = ReflectionMethod.TILEENTITY_GET_BLOCKDATA.run(o);
|
||||
@@ -342,7 +410,7 @@ public class NBTReflectionUtil {
|
||||
if (!valideCompound(comp)) {
|
||||
return;
|
||||
}
|
||||
Object workingtag = gettoCompount(nbttag, comp);
|
||||
Object workingtag = getToCompount(nbttag, comp);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_SET.run(workingtag, name,
|
||||
ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance());
|
||||
@@ -358,15 +426,17 @@ public class NBTReflectionUtil {
|
||||
* @param comp
|
||||
* @return true if this is a valide Compound, else false
|
||||
*/
|
||||
public static Boolean valideCompound(NBTCompound comp) {
|
||||
public static boolean valideCompound(NBTCompound comp) {
|
||||
Object root = comp.getCompound();
|
||||
if (root == null) {
|
||||
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
return (gettoCompount(root, comp)) != null;
|
||||
Object tmp = getToCompount(root, comp);
|
||||
comp.setResolvedObject(tmp);
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
protected static Object gettoCompount(Object nbttag, NBTCompound comp) {
|
||||
public static Object getToCompount(Object nbttag, NBTCompound comp) {
|
||||
Deque<String> structure = new ArrayDeque<>();
|
||||
while (comp.getParent() != null) {
|
||||
structure.add(comp.getName());
|
||||
@@ -389,20 +459,17 @@ public class NBTReflectionUtil {
|
||||
* @param nbtcompoundSrc Data to merge
|
||||
*/
|
||||
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompoundSrc) {
|
||||
Object workingtagSrc = nbtcompoundSrc.getResolvedObject();
|
||||
if (workingtagSrc == null) {
|
||||
return;
|
||||
}
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object rootnbttagSrc = nbtcompoundSrc.getCompound();
|
||||
if (rootnbttagSrc == null) {
|
||||
rootnbttagSrc = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(nbtcompoundSrc))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtagSrc = gettoCompount(rootnbttagSrc, nbtcompoundSrc);
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_MERGE.run(workingtag, workingtagSrc);
|
||||
comp.setCompound(rootnbttag);
|
||||
@@ -411,28 +478,6 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content for a given key inside a Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @return Content saved under this key
|
||||
*/
|
||||
public static String getContent(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
return ReflectionMethod.COMPOUND_GET.run(workingtag, key).toString();
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting the Content for key '" + key + "'!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a key in a {@link NBTCompound} to a given value
|
||||
*
|
||||
@@ -452,7 +497,7 @@ public class NBTReflectionUtil {
|
||||
if (!valideCompound(comp)) {
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
}
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_SET.run(workingtag, key, val);
|
||||
comp.setCompound(rootnbttag);
|
||||
@@ -472,13 +517,10 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> NBTList<T> getList(NBTCompound comp, String key, NBTType type, Class<T> clazz) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
if (workingtag == null) {
|
||||
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
|
||||
if (clazz == String.class) {
|
||||
@@ -506,20 +548,23 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
|
||||
public static NBTType getListType(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
if (workingtag == null) {
|
||||
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
String fieldname = "type";
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
fieldname = "w";
|
||||
}
|
||||
Field f = nbt.getClass().getDeclaredField(fieldname);
|
||||
Field f;
|
||||
try {
|
||||
f = nbt.getClass().getDeclaredField(fieldname);
|
||||
} catch (NoSuchFieldException ignore) {
|
||||
// fallback try mojang mapped/legacy
|
||||
f = nbt.getClass().getDeclaredField("type");
|
||||
}
|
||||
f.setAccessible(true);
|
||||
return NBTType.valueOf(f.getByte(nbt));
|
||||
} catch (Exception ex) {
|
||||
@@ -528,16 +573,9 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
|
||||
public static Object getEntry(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
return nbt;
|
||||
return ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while getting an Entry!", ex);
|
||||
}
|
||||
@@ -588,11 +626,11 @@ public class NBTReflectionUtil {
|
||||
public static void remove(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
return;
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
ReflectionMethod.COMPOUND_REMOVE_KEY.run(workingtag, key);
|
||||
comp.setCompound(rootnbttag);
|
||||
}
|
||||
@@ -605,13 +643,10 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Set<String> getKeys(NBTCompound comp) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
if (workingtag == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
return (Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(workingtag);
|
||||
}
|
||||
|
||||
@@ -634,11 +669,13 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
type.run(workingtag, key, data);
|
||||
comp.setCompound(rootnbttag);
|
||||
}
|
||||
|
||||
private final static NBTContainer dummyNBT = new NBTContainer();
|
||||
|
||||
/**
|
||||
* Gets data from the Compound
|
||||
*
|
||||
@@ -648,13 +685,11 @@ public class NBTReflectionUtil {
|
||||
* @return The value or default fallback from NMS
|
||||
*/
|
||||
public static Object getData(NBTCompound comp, ReflectionMethod type, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
return null;
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
// return default behavior data as if there was a compound
|
||||
if (workingtag == null) {
|
||||
workingtag = dummyNBT.getCompound();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
return type.run(workingtag, key);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,8 @@ package de.tr7zw.changeme.nbtapi;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import de.tr7zw.annotations.FAUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.AvailableSince;
|
||||
import de.tr7zw.changeme.nbtapi.utils.annotations.CheckUtil;
|
||||
|
||||
/**
|
||||
* NBT class to access vanilla tags from TileEntities. TileEntities don't
|
||||
@@ -22,12 +20,13 @@ public class NBTTileEntity extends NBTCompound {
|
||||
private final BlockState tile;
|
||||
private final boolean readonly;
|
||||
private final Object compound;
|
||||
private boolean closed = false;
|
||||
|
||||
/**
|
||||
* @param tile BlockState from any TileEntity
|
||||
* @param readonly Readonly makes a copy at init, only reading from that copy
|
||||
*/
|
||||
public NBTTileEntity(BlockState tile, boolean readonly) {
|
||||
protected NBTTileEntity(BlockState tile, boolean readonly) {
|
||||
super(null, null);
|
||||
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
|
||||
throw new NullPointerException("Tile can't be null/not placed!");
|
||||
@@ -42,8 +41,11 @@ public class NBTTileEntity extends NBTCompound {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: Please use the NBT class
|
||||
*
|
||||
* @param tile BlockState from any TileEntity
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTTileEntity(BlockState tile) {
|
||||
super(null, null);
|
||||
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
|
||||
@@ -54,6 +56,21 @@ public class NBTTileEntity extends NBTCompound {
|
||||
this.tile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClosed() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReadOnly() {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
// this runs before async check, since it's just a copy
|
||||
@@ -81,9 +98,8 @@ public class NBTTileEntity extends NBTCompound {
|
||||
*
|
||||
* @return NBTCompound containing the data of the PersistentDataAPI
|
||||
*/
|
||||
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
|
||||
public NBTCompound getPersistentDataContainer() {
|
||||
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_14_R1);
|
||||
if (hasTag("PublicBukkitValues")) {
|
||||
return getCompound("PublicBukkitValues");
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@ package de.tr7zw.changeme.nbtapi;
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum NBTType {
|
||||
NBTTagEnd(0), NBTTagByte(1), NBTTagShort(2), NBTTagInt(3), NBTTagLong(4), NBTTagFloat(5), NBTTagDouble(6),
|
||||
NBTTagByteArray(7), NBTTagIntArray(11), NBTTagString(8), NBTTagList(9), NBTTagCompound(10);
|
||||
NBTTagByteArray(7), NBTTagString(8), NBTTagList(9), NBTTagCompound(10), NBTTagIntArray(11), NBTTagLongArray(12);
|
||||
|
||||
NBTType(int i) {
|
||||
id = i;
|
||||
|
||||
@@ -29,16 +29,6 @@ public class NbtApiException extends RuntimeException {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param cause
|
||||
* @param enableSuppression
|
||||
* @param writableStackTrace
|
||||
*/
|
||||
public NbtApiException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(generateMessage(message), cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param cause
|
||||
@@ -65,12 +55,12 @@ public class NbtApiException extends RuntimeException {
|
||||
if (message == null)
|
||||
return null;
|
||||
if (confirmedBroken == null) {
|
||||
return "[?]" + message;
|
||||
return "[?][" + MinecraftVersion.getNBTAPIVersion() + "]" + message;
|
||||
} else if (confirmedBroken == false) {
|
||||
return "[Selfchecked]" + message;
|
||||
return "[Selfchecked][" + MinecraftVersion.getNBTAPIVersion() + "]" + message;
|
||||
}
|
||||
|
||||
return "[" + MinecraftVersion.getVersion()
|
||||
return "[" + MinecraftVersion.getVersion() + "][" + MinecraftVersion.getNBTAPIVersion()
|
||||
+ "]There were errors detected during the server self-check! Please, make sure that NBT-API is up to date. Error message: "
|
||||
+ message;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package de.tr7zw.changeme.nbtapi.handler;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
|
||||
public class NBTHandlers {
|
||||
|
||||
public static final NBTHandler<ItemStack> ITEM_STACK = new NBTHandler<ItemStack>() {
|
||||
|
||||
@Override
|
||||
public boolean fuzzyMatch(Object obj) {
|
||||
return obj instanceof ItemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ReadWriteNBT nbt, String key, ItemStack value) {
|
||||
nbt.removeKey(key);
|
||||
ReadWriteNBT tag = nbt.getOrCreateCompound(key);
|
||||
tag.mergeCompound(NBT.itemStackToNBT(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get(ReadableNBT nbt, String key) {
|
||||
ReadableNBT tag = nbt.getCompound(key);
|
||||
if (tag != null) {
|
||||
return NBT.itemStackFromNBT(tag);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final NBTHandler<ReadableNBT> STORE_READABLE_TAG = new NBTHandler<ReadableNBT>() {
|
||||
|
||||
@Override
|
||||
public boolean fuzzyMatch(Object obj) {
|
||||
return obj instanceof ReadableNBT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ReadWriteNBT nbt, String key, ReadableNBT value) {
|
||||
nbt.removeKey(key);
|
||||
nbt.getOrCreateCompound(key).mergeCompound(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableNBT get(ReadableNBT nbt, String key) {
|
||||
ReadableNBT tag = nbt.getCompound(key);
|
||||
if (tag != null) {
|
||||
ReadWriteNBT value = NBT.createNBTObject();
|
||||
value.mergeCompound(tag);
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final NBTHandler<ReadWriteNBT> STORE_READWRITE_TAG = new NBTHandler<ReadWriteNBT>() {
|
||||
|
||||
@Override
|
||||
public boolean fuzzyMatch(Object obj) {
|
||||
return obj instanceof ReadWriteNBT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ReadWriteNBT nbt, String key, ReadWriteNBT value) {
|
||||
nbt.removeKey(key);
|
||||
nbt.getOrCreateCompound(key).mergeCompound(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadWriteNBT get(ReadableNBT nbt, String key) {
|
||||
ReadableNBT tag = nbt.getCompound(key);
|
||||
if (tag != null) {
|
||||
ReadWriteNBT value = NBT.createNBTObject();
|
||||
value.mergeCompound(tag);
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.tr7zw.changeme.nbtapi.iface;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface NBTHandler<T> {
|
||||
|
||||
public default boolean fuzzyMatch(Object obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void set(@Nonnull ReadWriteNBT nbt, @Nonnull String key, @Nonnull T value);
|
||||
|
||||
public T get(@Nonnull ReadableNBT nbt, @Nonnull String key);
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
|
||||
public interface ReadWriteItemNBT extends ReadWriteNBT {
|
||||
public interface ReadWriteItemNBT extends ReadWriteNBT, ReadableItemNBT {
|
||||
|
||||
/**
|
||||
* True, if the item has any tags now known for this item type.
|
||||
|
||||
@@ -2,6 +2,8 @@ package de.tr7zw.changeme.nbtapi.iface;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface ReadWriteNBT extends ReadableNBT {
|
||||
@@ -86,6 +88,16 @@ public interface ReadWriteNBT extends ReadableNBT {
|
||||
*/
|
||||
void setIntArray(String key, int[] value);
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* Requires 1.16+
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
void setLongArray(String key, long[] value);
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
@@ -135,8 +147,29 @@ public interface ReadWriteNBT extends ReadableNBT {
|
||||
* @param name
|
||||
* @return The Compound instance or null
|
||||
*/
|
||||
@Nullable
|
||||
ReadWriteNBT getCompound(String name);
|
||||
|
||||
/**
|
||||
* Returns the resolved and creates compounds as required.
|
||||
* <p>
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* be escaped with a backslash.
|
||||
* @return The resolved compound.
|
||||
*/
|
||||
ReadWriteNBT resolveOrCreateCompound(String key);
|
||||
|
||||
/**
|
||||
* Set an Object to a key via the provided handler.
|
||||
*
|
||||
* @param <T>
|
||||
* @param key
|
||||
* @param value
|
||||
* @param handler
|
||||
*/
|
||||
<T> void set(String key, T value, NBTHandler<T> handler);
|
||||
|
||||
/**
|
||||
* Set a key to the given Enum value. It gets stored as a String. Passing null
|
||||
* as value will call removeKey(key) instead.
|
||||
@@ -170,6 +203,10 @@ public interface ReadWriteNBT extends ReadableNBT {
|
||||
|
||||
@Override
|
||||
ReadWriteNBTCompoundList getCompoundList(String name);
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
ReadWriteNBT resolveCompound(String key);
|
||||
|
||||
/**
|
||||
* Remove all keys from this compound
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.tr7zw.changeme.nbtapi.iface;
|
||||
|
||||
public interface ReadableItemNBT extends ReadableNBT {
|
||||
|
||||
/**
|
||||
* Returns true if the item has NBT data.
|
||||
*
|
||||
* @return Does the ItemStack have a NBTCompound.
|
||||
*/
|
||||
public boolean hasNBTData();
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import java.io.OutputStream;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
@@ -84,6 +86,7 @@ public interface ReadableNBT {
|
||||
* @param key The key to use to retrieve the value.
|
||||
* @return A byte array.
|
||||
*/
|
||||
@Nullable
|
||||
byte[] getByteArray(String key);
|
||||
|
||||
/**
|
||||
@@ -93,8 +96,21 @@ public interface ReadableNBT {
|
||||
* @param key The key of the value you want to get.
|
||||
* @return An array of integers.
|
||||
*/
|
||||
@Nullable
|
||||
int[] getIntArray(String key);
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given key as an array of longs, or null
|
||||
* if the key does not exist.
|
||||
*
|
||||
* Requires 1.16+
|
||||
*
|
||||
* @param key The key of the value you want to get.
|
||||
* @return An array of integers.
|
||||
*/
|
||||
@Nullable
|
||||
long[] getLongArray(String key);
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given key, or false if the key is not
|
||||
* found.
|
||||
@@ -111,6 +127,7 @@ public interface ReadableNBT {
|
||||
* @param key The key of the itemstack you want to get.
|
||||
* @return An ItemStack
|
||||
*/
|
||||
@Nullable
|
||||
ItemStack getItemStack(String key);
|
||||
|
||||
/**
|
||||
@@ -121,6 +138,7 @@ public interface ReadableNBT {
|
||||
* @return The stored {@link ItemStack} array, or null if stored data wasn't
|
||||
* found
|
||||
*/
|
||||
@Nullable
|
||||
ItemStack[] getItemStackArray(String key);
|
||||
|
||||
/**
|
||||
@@ -129,6 +147,7 @@ public interface ReadableNBT {
|
||||
* @param key The key to get the value from
|
||||
* @return A UUID object.
|
||||
*/
|
||||
@Nullable
|
||||
UUID getUUID(String key);
|
||||
|
||||
/**
|
||||
@@ -159,6 +178,7 @@ public interface ReadableNBT {
|
||||
* @param name
|
||||
* @return The Compound instance or null
|
||||
*/
|
||||
@Nullable
|
||||
ReadableNBT getCompound(String name);
|
||||
|
||||
/**
|
||||
@@ -209,6 +229,7 @@ public interface ReadableNBT {
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
NBTType getListType(String name);
|
||||
|
||||
/**
|
||||
@@ -221,8 +242,8 @@ public interface ReadableNBT {
|
||||
* Returns the stored value if exists, or provided value otherwise.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double, byte[], int[]},
|
||||
* {@link String}, {@link UUID}
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue default non-null value
|
||||
@@ -235,16 +256,69 @@ public interface ReadableNBT {
|
||||
* Returns the stored value if exists, or null.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code Byte, Short, Integer, Long, Float, Double, byte[], int[]},
|
||||
* {@link String}, {@link UUID}
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key key
|
||||
* @param type data type
|
||||
* @param <T> value type
|
||||
* @return Stored or provided value
|
||||
*/
|
||||
@Nullable
|
||||
<T> T getOrNull(String key, Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns the resolved value if exists, or null.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* be escaped with a backslash.
|
||||
* @param type data type
|
||||
* @param <T> value type
|
||||
* @return resolved or provided value
|
||||
*/
|
||||
@Nullable
|
||||
<T> T resolveOrNull(String key, Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns the resolved value if exists, or provided value otherwise.
|
||||
* <p>
|
||||
* Supported types:
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz".
|
||||
* Dots can be escaped with a backslash.
|
||||
* @param defaultValue default non-null value
|
||||
* @param <T> value type
|
||||
* @return resolved or provided value
|
||||
*/
|
||||
<T> T resolveOrDefault(String key, T defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the resolved Compound if exists, or null.
|
||||
* <p>
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* be escaped with a backslash.
|
||||
* @return The resolved value if exists, or null.
|
||||
*/
|
||||
@Nullable
|
||||
ReadableNBT resolveCompound(String key);
|
||||
|
||||
/**
|
||||
* Get the object at the specified key via the handler.
|
||||
*
|
||||
* @param <T>
|
||||
* @param key
|
||||
* @param handler
|
||||
* @return
|
||||
*/
|
||||
<T> T get(String key, NBTHandler<T> handler);
|
||||
|
||||
/**
|
||||
* Get an Enum value that has been set via setEnum or setString(key,
|
||||
* value.name()). Passing null/invalid keys will return null.
|
||||
@@ -254,6 +328,7 @@ public interface ReadableNBT {
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
<E extends Enum<E>> E getEnum(String key, Class<E> type);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static de.tr7zw.changeme.nbtapi.utils.MinecraftVersion.getLogger;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*
|
||||
* This class is modified by tr7zw to work when the api is shaded into other
|
||||
* peoples plugins.
|
||||
*/
|
||||
public class ApiMetricsLite {
|
||||
|
||||
private static final String PLUGINNAME = "ItemNBTAPI"; // DO NOT CHANGE THE NAME! else it won't link the data on
|
||||
// bStats
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The version of the NBT-Api bStats
|
||||
public static final int NBT_BSTATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private Plugin plugin;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
*/
|
||||
public ApiMetricsLite() {
|
||||
|
||||
// The register method just uses any enabled plugin it can find to register.
|
||||
// This *shouldn't* cause any problems, since the plugin isn't used any other
|
||||
// way.
|
||||
// Register our service
|
||||
for (Plugin plug : Bukkit.getPluginManager().getPlugins()) {
|
||||
plugin = plug;
|
||||
if (plugin != null)
|
||||
break;
|
||||
}
|
||||
if (plugin == null) {
|
||||
return;// Didn't find any plugin that could work
|
||||
}
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n"
|
||||
+ "To honor their work, you should not disable it.\n"
|
||||
+ "This has nearly no effect on the server performance!\n"
|
||||
+ "Check out https://bStats.org/ to learn more :)")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("NBT_BSTATS_VERSION"); // Create only one instance of the nbt-api bstats.
|
||||
return;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
boolean fFound = found;
|
||||
// Register our service
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!fFound) {
|
||||
getLogger()
|
||||
.info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!fFound) {
|
||||
getLogger().info(
|
||||
"[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to
|
||||
// use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the
|
||||
// stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000l * 60l * 5l, 1000l * 60l * 30l);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other
|
||||
// plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be
|
||||
// blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data. This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JsonObject getPluginData() {
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("pluginName", PLUGINNAME); // Append the name of the plugin
|
||||
data.addProperty("pluginVersion", MinecraftVersion.VERSION); // Append the version of the plugin
|
||||
data.add("customCharts", new JsonArray());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JsonObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
String bukkitName = Bukkit.getName();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bukkitVersion", bukkitVersion);
|
||||
data.addProperty("bukkitName", bukkitName);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JsonObject data = getServerData();
|
||||
|
||||
JsonArray pluginData = new JsonArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
} else { // old bstats version compatibility
|
||||
try {
|
||||
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
|
||||
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
|
||||
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
|
||||
jsonStringGetter.setAccessible(true);
|
||||
String jsonString = (String) jsonStringGetter.invoke(plugin);
|
||||
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
pluginData.add(object);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// minecraft version 1.14+
|
||||
if (logFailedRequests) {
|
||||
getLogger().log(Level.WARNING,
|
||||
"[NBTAPI][BSTATS] Encountered exception while posting request!", e);
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the
|
||||
// NBT-Api most of the time
|
||||
// this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception
|
||||
// ", e);
|
||||
}
|
||||
continue; // continue looping since we cannot do any other thing.
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException
|
||||
| InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
data.add("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
getLogger().log(Level.WARNING,
|
||||
"[NBTAPI][BSTATS] Could not submit plugin stats of " + plugin.getName(), e);
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the
|
||||
// NBT-Api most of the time
|
||||
// plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " +
|
||||
// plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
MinecraftVersion.getLogger().info("[NBTAPI][BSTATS] Sending data to bStats: " + data.toString());
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the
|
||||
// NBT-Api most of the time
|
||||
// plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
getLogger().info("[NBTAPI][BSTATS] Sent data to bStats and received response: " + builder.toString());
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the
|
||||
// NBT-Api most of the time
|
||||
// plugin.getLogger().info("Sent data to bStats and received response: " +
|
||||
// builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
|
||||
public class CheckUtil {
|
||||
|
||||
private CheckUtil() {
|
||||
// util
|
||||
}
|
||||
|
||||
public static void assertAvailable(MinecraftVersion version) {
|
||||
if (!MinecraftVersion.isAtLeastVersion(version))
|
||||
throw new NbtApiException(
|
||||
"This Method is only avaliable for the version " + version.name() + " and above!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import com.mojang.datafixers.DSL.TypeReference;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
|
||||
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
|
||||
public static final int VERSION1_12_2 = 1343;
|
||||
public static final int VERSION1_16_5 = 2586;
|
||||
public static final int VERSION1_17_1 = 2730;
|
||||
public static final int VERSION1_18_2 = 2975;
|
||||
public static final int VERSION1_19_2 = 3120;
|
||||
public static final int VERSION1_19_4 = 3337;
|
||||
public static final int VERSION1_20_1 = 3465;
|
||||
public static final int VERSION1_20_2 = 3578;
|
||||
public static final int VERSION1_20_4 = 3700;
|
||||
public static final int VERSION1_20_5 = 3837;
|
||||
public static final int VERSION1_21 = 3953;
|
||||
|
||||
@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);
|
||||
Dynamic<Object> fixed = dataFixer.update(itemStackReference, new Dynamic<Object>(nbtOps, nbt), fromVersion,
|
||||
toVersion);
|
||||
return fixed.getValue();
|
||||
}
|
||||
|
||||
public static ReadWriteNBT fixUpItemData(ReadWriteNBT nbt, int fromVersion, int toVersion)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
return new NBTContainer(fixUpRawItemData(
|
||||
NBTReflectionUtil.getToCompount(((NBTCompound) nbt).getCompound(), ((NBTCompound) nbt)), fromVersion,
|
||||
toVersion));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the closest or exact version number of the current
|
||||
* server. Only contains versions people will reasonably use and will fail with
|
||||
* an exception, when the target version is before 1.12.2. (Assuming no one will
|
||||
* update 1.8 items to 1.11, if so, provide the version numbers to the converter
|
||||
* method directly)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static int getCurrentVersion() {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R1)) {
|
||||
return VERSION1_21;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return VERSION1_20_5;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R3)) {
|
||||
return VERSION1_20_4;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R1)) {
|
||||
return VERSION1_20_1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R3)) {
|
||||
return VERSION1_19_4;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R1)) {
|
||||
return VERSION1_19_2;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
|
||||
return VERSION1_18_2;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
return VERSION1_17_1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
return VERSION1_16_5;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)) {
|
||||
return VERSION1_12_2;
|
||||
}
|
||||
throw new NbtApiException(
|
||||
"Trying to update data *to* a version before 1.12.2? Something is probably going wrong, contact the plugin author.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
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.iface.ReadableNBTList;
|
||||
|
||||
public class GameprofileUtil {
|
||||
|
||||
@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");
|
||||
}
|
||||
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);
|
||||
if (arg.hasTag("Properties") && arg.getType("Properties") == NBTType.NBTTagCompound) {
|
||||
ReadableNBT compoundTag = arg.getCompound("Properties");
|
||||
for (String string2 : compoundTag.getKeys()) {
|
||||
ReadableNBTList<ReadWriteNBT> listTag = compoundTag.getCompoundList(string);
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
ReadableNBT compoundTag2 = listTag.get(i);
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ReadWriteNBT writeGameProfile(ReadWriteNBT arg, GameProfile gameProfile) {
|
||||
if (!(gameProfile.getName() == null || gameProfile.getName().isEmpty())) {
|
||||
arg.setString("Name", gameProfile.getName());
|
||||
}
|
||||
if (gameProfile.getId() != null) {
|
||||
arg.setUUID("Id", gameProfile.getId());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,881 @@
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
*
|
||||
* IMPORTANT: You are not allowed to modify this class, except changing the package.
|
||||
*
|
||||
* Disallowed modifications include but are not limited to:
|
||||
* - Remove the option for users to opt-out
|
||||
* - Change the frequency for data submission
|
||||
* - Obfuscate the code (every obfuscator should allow you to make an exception for specific files)
|
||||
* - Reformat the code (if you use a linter, add an exception)
|
||||
*
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param plugin Your plugin instance.
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.isSet("serverUuid")) {
|
||||
config.addDefault("enabled", true);
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
config.addDefault("logFailedRequests", false);
|
||||
config.addDefault("logSentData", false);
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
// Inform the server owners about bStats
|
||||
config
|
||||
.options()
|
||||
.header(
|
||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
+ "anonymous.")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
boolean enabled = config.getBoolean("enabled", true);
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
||||
boolean logSentData = config.getBoolean("logSentData", false);
|
||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
metricsBase =
|
||||
new MetricsBase(
|
||||
"bukkit",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
plugin::isEnabled,
|
||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText);
|
||||
}
|
||||
|
||||
/** Shuts down the underlying scheduler service. */
|
||||
public void shutdown() {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
||||
builder.appendField("bukkitName", Bukkit.getName());
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
// Just use the new method if the reflection failed
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsBase {
|
||||
|
||||
/** The version of the Metrics class. */
|
||||
public static final String METRICS_VERSION = "3.0.2";
|
||||
|
||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private final String platform;
|
||||
|
||||
private final String serverUuid;
|
||||
|
||||
private final int serviceId;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
||||
|
||||
private final Consumer<Runnable> submitTaskConsumer;
|
||||
|
||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
||||
|
||||
private final BiConsumer<String, Throwable> errorLogger;
|
||||
|
||||
private final Consumer<String> infoLogger;
|
||||
|
||||
private final boolean logErrors;
|
||||
|
||||
private final boolean logSentData;
|
||||
|
||||
private final boolean logResponseStatusText;
|
||||
|
||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Creates a new MetricsBase class instance.
|
||||
*
|
||||
* @param platform The platform of the service.
|
||||
* @param serviceId The id of the service.
|
||||
* @param serverUuid The server uuid.
|
||||
* @param enabled Whether or not data sending is enabled.
|
||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all platform-specific data.
|
||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all service-specific data.
|
||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
||||
* concurrency. Can be {@code null}.
|
||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
||||
* @param errorLogger A consumer that accepts log message and an error.
|
||||
* @param infoLogger A consumer that accepts info log messages.
|
||||
* @param logErrors Whether or not errors should be logged.
|
||||
* @param logSentData Whether or not the sent data should be logged.
|
||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
||||
*/
|
||||
public MetricsBase(
|
||||
String platform,
|
||||
String serverUuid,
|
||||
int serviceId,
|
||||
boolean enabled,
|
||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
||||
Consumer<Runnable> submitTaskConsumer,
|
||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
||||
BiConsumer<String, Throwable> errorLogger,
|
||||
Consumer<String> infoLogger,
|
||||
boolean logErrors,
|
||||
boolean logSentData,
|
||||
boolean logResponseStatusText) {
|
||||
ScheduledThreadPoolExecutor scheduler =
|
||||
new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics"));
|
||||
// We want delayed tasks (non-periodic) that will execute in the future to be
|
||||
// cancelled when the scheduler is shutdown.
|
||||
// Otherwise, we risk preventing the server from shutting down even when
|
||||
// MetricsBase#shutdown() is called
|
||||
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
this.scheduler = scheduler;
|
||||
this.platform = platform;
|
||||
this.serverUuid = serverUuid;
|
||||
this.serviceId = serviceId;
|
||||
this.enabled = enabled;
|
||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
||||
this.submitTaskConsumer = submitTaskConsumer;
|
||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
||||
this.errorLogger = errorLogger;
|
||||
this.infoLogger = infoLogger;
|
||||
this.logErrors = logErrors;
|
||||
this.logSentData = logSentData;
|
||||
this.logResponseStatusText = logResponseStatusText;
|
||||
checkRelocation();
|
||||
if (enabled) {
|
||||
// WARNING: Removing the option to opt-out will get your plugin banned from
|
||||
// bStats
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
this.customCharts.add(chart);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
final Runnable submitTask =
|
||||
() -> {
|
||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
||||
// Submitting data or service is disabled
|
||||
scheduler.shutdown();
|
||||
return;
|
||||
}
|
||||
if (submitTaskConsumer != null) {
|
||||
submitTaskConsumer.accept(this::submitData);
|
||||
} else {
|
||||
this.submitData();
|
||||
}
|
||||
};
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven
|
||||
// distribution of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into
|
||||
// the initial and second delay.
|
||||
// WARNING: You must not modify and part of this Metrics class, including the
|
||||
// submit delay or frequency!
|
||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just
|
||||
// don't do it!
|
||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
scheduler.scheduleAtFixedRate(
|
||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
||||
JsonObjectBuilder.JsonObject[] chartData =
|
||||
customCharts.stream()
|
||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
||||
serviceJsonBuilder.appendField("id", serviceId);
|
||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
||||
scheduler.execute(
|
||||
() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
||||
if (logSentData) {
|
||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
||||
}
|
||||
String url = String.format(REPORT_URL, platform);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks that the class was properly relocated. */
|
||||
private void checkRelocation() {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null
|
||||
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D
|
||||
final String defaultPackage =
|
||||
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
|
||||
final String examplePackage =
|
||||
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure no one just copy & pastes the example and uses the wrong
|
||||
// package names
|
||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given string.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped string.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class CustomChart {
|
||||
|
||||
private final String chartId;
|
||||
|
||||
protected CustomChart(String chartId) {
|
||||
if (chartId == null) {
|
||||
throw new IllegalArgumentException("chartId must not be null");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
||||
builder.appendField("chartId", chartId);
|
||||
try {
|
||||
JsonObjectBuilder.JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
builder.appendField("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
||||
}
|
||||
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extremely simple JSON builder.
|
||||
*
|
||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
||||
* for its use-case.
|
||||
*/
|
||||
public static class JsonObjectBuilder {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean hasAtLeastOneField = false;
|
||||
|
||||
public JsonObjectBuilder() {
|
||||
builder.append("{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a null field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendNull(String key) {
|
||||
appendFieldUnescaped(key, "null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("JSON value must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int value) {
|
||||
appendFieldUnescaped(key, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param object The object.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("JSON object must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, object.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The string array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values)
|
||||
.map(value -> "\"" + escape(value) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a field to the object.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param escapedValue The escaped value of the field.
|
||||
*/
|
||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("JSON key must not be null");
|
||||
}
|
||||
if (hasAtLeastOneField) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
||||
hasAtLeastOneField = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JSON string and invalidates this builder.
|
||||
*
|
||||
* @return The built JSON string.
|
||||
*/
|
||||
public JsonObject build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
||||
builder = null;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
||||
*
|
||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
||||
*
|
||||
* @param value The value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
private static String escape(String value) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '"') {
|
||||
builder.append("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.append("\\\\");
|
||||
} else if (c <= '\u000F') {
|
||||
builder.append("\\u000").append(Integer.toHexString(c));
|
||||
} else if (c <= '\u001F') {
|
||||
builder.append("\\u00").append(Integer.toHexString(c));
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple representation of a JSON object.
|
||||
*
|
||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
||||
* JsonObject)}.
|
||||
*/
|
||||
public static class JsonObject {
|
||||
|
||||
private final String value;
|
||||
|
||||
private JsonObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
|
||||
@@ -19,11 +23,13 @@ public enum MinecraftVersion {
|
||||
MC1_7_R4(174), MC1_8_R3(183), MC1_9_R1(191), MC1_9_R2(192), MC1_10_R1(1101), MC1_11_R1(1111), MC1_12_R1(1121),
|
||||
MC1_13_R1(1131), MC1_13_R2(1132), MC1_14_R1(1141), MC1_15_R1(1151), MC1_16_R1(1161), MC1_16_R2(1162),
|
||||
MC1_16_R3(1163), MC1_17_R1(1171), MC1_18_R1(1181, true), MC1_18_R2(1182, true), MC1_19_R1(1191, true),
|
||||
MC1_19_R2(1192, true), MC1_19_R3(1193, true), MC1_20_R1(1201, 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);
|
||||
|
||||
private static MinecraftVersion version;
|
||||
private static Boolean hasGsonSupport;
|
||||
private static Boolean isForgePresent;
|
||||
private static Boolean isFoliaPresent;
|
||||
private static boolean bStatsDisabled = false;
|
||||
private static boolean disablePackageWarning = false;
|
||||
private static boolean updateCheckDisabled = true;
|
||||
@@ -33,11 +39,26 @@ public enum MinecraftVersion {
|
||||
private static Logger logger = Logger.getLogger("NBTAPI");
|
||||
|
||||
// NBT-API Version
|
||||
protected static final String VERSION = "2.12.0-SNAPSHOT";
|
||||
protected static final String VERSION = "2.13.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);
|
||||
this.put("1.20.1", MC1_20_R1);
|
||||
this.put("1.20.2", MC1_20_R2);
|
||||
this.put("1.20.3", MC1_20_R3);
|
||||
this.put("1.20.4", MC1_20_R3);
|
||||
this.put("1.20.5", MC1_20_R4);
|
||||
this.put("1.20.6", MC1_20_R4);
|
||||
this.put("1.21", MC1_21_R1);
|
||||
}
|
||||
};
|
||||
|
||||
MinecraftVersion(int versionId) {
|
||||
this(versionId, false);
|
||||
}
|
||||
@@ -70,7 +91,11 @@ public enum MinecraftVersion {
|
||||
*/
|
||||
public String getPackageName() {
|
||||
if (this == UNKNOWN) {
|
||||
return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
} catch (Exception ex) {
|
||||
// ignore, paper without remap, will fail
|
||||
}
|
||||
}
|
||||
return this.name().replace("MC", "v");
|
||||
}
|
||||
@@ -105,28 +130,44 @@ public enum MinecraftVersion {
|
||||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
final String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
logger.info("[NBTAPI] Found Spigot: " + ver + "! Trying to find NMS support");
|
||||
try {
|
||||
final String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
logger.info("[NBTAPI] Found Minecraft: " + ver + "! Trying to find NMS support");
|
||||
version = MinecraftVersion.valueOf(ver.replace("v", "MC"));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
version = MinecraftVersion.UNKNOWN;
|
||||
} 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);
|
||||
}
|
||||
if (version != UNKNOWN) {
|
||||
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
|
||||
} else {
|
||||
logger.warning("[NBTAPI] This Server-Version(" + ver + ") is not supported by this NBT-API Version("
|
||||
+ VERSION + ") located in " + VersionChecker.getPlugin()
|
||||
logger.warning("[NBTAPI] This Server-Version(" + Bukkit.getServer().getBukkitVersion()
|
||||
+ ") is not supported by this NBT-API Version(" + VERSION + ") located in "
|
||||
+ VersionChecker.getPlugin()
|
||||
+ ". The NBT-API will try to work as good as it can! Some functions may not work!");
|
||||
}
|
||||
init();
|
||||
return version;
|
||||
}
|
||||
|
||||
public static String getNBTAPIVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
try {
|
||||
if (hasGsonSupport() && !bStatsDisabled)
|
||||
new ApiMetricsLite();
|
||||
if (hasGsonSupport() && !bStatsDisabled) {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(VersionChecker.getPlugin());
|
||||
if (plugin != null && plugin instanceof JavaPlugin) {
|
||||
getLogger()
|
||||
.info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
new Metrics((JavaPlugin) plugin, 1058);
|
||||
} else if (plugin == null) {
|
||||
getLogger().info("[NBTAPI] Unable to create a bStats instance!!");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "[NBTAPI] Error enabling Metrics!", ex);
|
||||
}
|
||||
@@ -143,6 +184,8 @@ public enum MinecraftVersion {
|
||||
// little "trick" ... :D (from bStats)
|
||||
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
|
||||
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
final String reservedPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'n', 'b',
|
||||
't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
if (!disablePackageWarning && MinecraftVersion.class.getPackage().getName().equals(defaultPackage)) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
@@ -156,17 +199,32 @@ public enum MinecraftVersion {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
}
|
||||
if (!disablePackageWarning && !"NBTAPI".equals(VersionChecker.getPlugin())
|
||||
&& MinecraftVersion.class.getPackage().getName().equals("de.tr7zw.nbtapi.utils")) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
logger.warning(
|
||||
"The NBT-API inside " + VersionChecker.getPlugin() + " is located at 'de.tr7zw.nbtapi.utils'!");
|
||||
logger.warning(
|
||||
"This package name is reserved for the official NBTAPI plugin, and not intended to be used for shading!");
|
||||
logger.warning("Please change the relocate to something else. For example: com.example.util.nbtapi");
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
if (!disablePackageWarning && !"NBTAPI".equals(VersionChecker.getPlugin())) { // we are not the nbtapi, check
|
||||
// for common shading errors
|
||||
if (!"de.tr7zw.nbtapi.utils".equals(reservedPackage)) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
logger.warning(
|
||||
"The NBT-API inside " + VersionChecker.getPlugin() + " is the plugin version, not the API!");
|
||||
logger.warning(
|
||||
"The plugin itself should never be shaded! Remove the `-plugin` from the dependency and fix your shading setup.");
|
||||
logger.warning(
|
||||
"For more info check: https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven#option-2-shading-the-nbt-api-into-your-plugin");
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
return; // don't also print the second error
|
||||
}
|
||||
if (MinecraftVersion.class.getPackage().getName().equals("de.tr7zw.nbtapi.utils")) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
logger.warning(
|
||||
"The NBT-API inside " + VersionChecker.getPlugin() + " is located at 'de.tr7zw.nbtapi.utils'!");
|
||||
logger.warning(
|
||||
"This package name is reserved for the official NBTAPI plugin, and not intended to be used for shading!");
|
||||
logger.warning("Please change the relocate to something else. For example: com.example.util.nbtapi");
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +236,7 @@ public enum MinecraftVersion {
|
||||
return hasGsonSupport;
|
||||
}
|
||||
try {
|
||||
logger.info("[NBTAPI] Found Gson: " + Class.forName("com.google.gson.Gson"));
|
||||
Class.forName("com.google.gson.Gson");
|
||||
hasGsonSupport = true;
|
||||
} catch (Exception ex) {
|
||||
logger.info("[NBTAPI] Gson not found! This will not allow the usage of some methods!");
|
||||
@@ -205,6 +263,22 @@ public enum MinecraftVersion {
|
||||
return isForgePresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if Folia is present
|
||||
*/
|
||||
public static boolean isFoliaPresent() {
|
||||
if (isFoliaPresent != null) {
|
||||
return isFoliaPresent;
|
||||
}
|
||||
try {
|
||||
logger.info("[NBTAPI] Found Folia: " + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"));
|
||||
isFoliaPresent = true;
|
||||
} catch (Exception ex) {
|
||||
isFoliaPresent = false;
|
||||
}
|
||||
return isFoliaPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function before the NBT-Api is used will disable bStats stats
|
||||
* collection. Please consider not to do that, since it won't affect your plugin
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class PathUtil {
|
||||
|
||||
private static final Pattern pattern = Pattern.compile("[^\\\\](\\.)");
|
||||
private static final Pattern indexPattern = Pattern.compile(".*\\[(-?[0-9]+)\\]");
|
||||
|
||||
public static List<PathSegment> splitPath(String path) {
|
||||
List<PathSegment> list = new ArrayList<>();
|
||||
Matcher matcher = pattern.matcher(path);
|
||||
int startIndex = 0;
|
||||
while (matcher.find(startIndex)) {
|
||||
list.add(new PathSegment(path.substring(startIndex, matcher.end() - 1).replace("\\.", ".")));
|
||||
startIndex = matcher.end();
|
||||
}
|
||||
list.add(new PathSegment(path.substring(startIndex).replace("\\.", ".")));
|
||||
return list;
|
||||
}
|
||||
|
||||
public static class PathSegment {
|
||||
|
||||
private final String path;
|
||||
private final Integer index;
|
||||
|
||||
private PathSegment(String path) {
|
||||
Matcher matcher = indexPattern.matcher(path);
|
||||
if (matcher.find()) {
|
||||
this.path = path.substring(0, path.indexOf("["));
|
||||
this.index = Integer.parseInt(matcher.group(1));
|
||||
} else {
|
||||
this.path = path;
|
||||
this.index = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public boolean hasIndex() {
|
||||
return index != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathSegment [path=" + path + ", index=" + index + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils.annotations;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@Target({ METHOD })
|
||||
public @interface AvailableSince {
|
||||
|
||||
MinecraftVersion version();
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils.annotations;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
|
||||
public class CheckUtil {
|
||||
|
||||
public static boolean isAvaliable(Method method) {
|
||||
if (MinecraftVersion.getVersion().getVersionId() < method.getAnnotation(AvailableSince.class).version()
|
||||
.getVersionId())
|
||||
throw new NbtApiException("The Method '" + method.getName() + "' is only avaliable for the Versions "
|
||||
+ method.getAnnotation(AvailableSince.class).version() + "+, but still got called!");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -4,6 +4,8 @@ import static de.tr7zw.changeme.nbtapi.utils.MinecraftVersion.getLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
|
||||
/**
|
||||
@@ -18,6 +20,7 @@ public enum ClassWrapper {
|
||||
CRAFT_METAITEM(PackageWrapper.CRAFTBUKKIT, "inventory.CraftMetaItem", null, null),
|
||||
CRAFT_ENTITY(PackageWrapper.CRAFTBUKKIT, "entity.CraftEntity", null, null),
|
||||
CRAFT_WORLD(PackageWrapper.CRAFTBUKKIT, "CraftWorld", null, null),
|
||||
CRAFT_SERVER(PackageWrapper.CRAFTBUKKIT, "CraftServer", null, null),
|
||||
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"),
|
||||
@@ -62,6 +65,26 @@ public enum ClassWrapper {
|
||||
"net.minecraft.nbt.NbtUtils"),
|
||||
NMS_IBLOCKDATA(PackageWrapper.NMS, "IBlockData", MinecraftVersion.MC1_8_R3, null,
|
||||
"net.minecraft.world.level.block.state", "net.minecraft.world.level.block.state.BlockState"),
|
||||
NMS_NBTACCOUNTER(PackageWrapper.NMS, "NBTReadLimiter", MinecraftVersion.MC1_20_R3, null, "net.minecraft.nbt",
|
||||
"net.minecraft.nbt.NbtAccounter"),
|
||||
NMS_CUSTOMDATA(PackageWrapper.NMS, "CustomData", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.world.item.component", "net.minecraft.world.item.component.CustomData"),
|
||||
NMS_DATACOMPONENTTYPE(PackageWrapper.NMS, "DataComponentType", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.core.component", "net.minecraft.core.component.DataComponentType"),
|
||||
NMS_DATACOMPONENTS(PackageWrapper.NMS, "DataComponents", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.core.component", "net.minecraft.core.component.DataComponents"),
|
||||
NMS_DATACOMPONENTHOLDER(PackageWrapper.NMS, "DataComponentHolder", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.core.component", "net.minecraft.core.component.DataComponentHolder"),
|
||||
NMS_PROVIDER(PackageWrapper.NMS, "HolderLookup$a", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.core", "net.minecraft.core.HolderLookup$Provider"),
|
||||
NMS_SERVER(PackageWrapper.NMS, "MinecraftServer", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.server", "net.minecraft.server.MinecraftServer"),
|
||||
NMS_DATAFIXERS(PackageWrapper.NMS, "DataConverterRegistry", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.util.datafix", "net.minecraft.util.datafix.DataFixers"),
|
||||
NMS_REFERENCES(PackageWrapper.NMS, "DataConverterTypes", MinecraftVersion.MC1_20_R4, null,
|
||||
"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"),
|
||||
GAMEPROFILE(PackageWrapper.NONE, "com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3, null);
|
||||
|
||||
private Class<?> clazz;
|
||||
@@ -83,6 +106,15 @@ public enum ClassWrapper {
|
||||
}
|
||||
enabled = true;
|
||||
try {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1) && mojangName != null) {
|
||||
// check for Mojmapped enviroment
|
||||
try {
|
||||
clazz = Class.forName(mojangName);
|
||||
return;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// ignored, not mojang mapped
|
||||
}
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1) && mojangMap != null) {
|
||||
clazz = Class.forName(mojangMap + "." + clazzName);
|
||||
} else if (packageId == PackageWrapper.NONE) {
|
||||
@@ -90,7 +122,11 @@ public enum ClassWrapper {
|
||||
} else if (MinecraftVersion.isForgePresent() && MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4
|
||||
&& Forge1710Mappings.getClassMappings().get(this.name()) != null) {
|
||||
clazz = Class.forName(clazzName = Forge1710Mappings.getClassMappings().get(this.name()));
|
||||
} else if (packageId == PackageWrapper.CRAFTBUKKIT) {
|
||||
// this also works for un-remapped Paper 1.20+
|
||||
clazz = Class.forName(Bukkit.getServer().getClass().getPackage().getName() + "." + clazzName);
|
||||
} else {
|
||||
// fallback for old versions pre mojmap and in the nms package
|
||||
String version = MinecraftVersion.getVersion().getPackageName();
|
||||
clazz = Class.forName(packageId.getUri() + "." + version + "." + clazzName);
|
||||
}
|
||||
|
||||
+81
-3
@@ -3,10 +3,12 @@ package de.tr7zw.changeme.nbtapi.utils.nmsmappings;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Temporary solution to hold Mojang to unmapped Spigot mappings.
|
||||
* Note years later: nothing is more permanent than a temporary solution.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
@@ -28,10 +30,12 @@ public class MojangToMapping {
|
||||
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "n");
|
||||
put("net.minecraft.nbt.CompoundTag#remove(java.lang.String)", "r");
|
||||
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "c");
|
||||
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#merge(net.minecraft.nbt.CompoundTag)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#putBoolean(java.lang.String,boolean)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getTagType(java.lang.String)", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#putLong(java.lang.String,long)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#putLongArray(java.lang.String,long[])", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "o");
|
||||
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "l");
|
||||
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "h");
|
||||
put("net.minecraft.nbt.CompoundTag#putString(java.lang.String,java.lang.String)", "a");
|
||||
@@ -118,11 +122,83 @@ public class MojangToMapping {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_20R2 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_20R1);
|
||||
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bu");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_20R3 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_20R2);
|
||||
|
||||
put("net.minecraft.nbt.NbtIo#readCompressed(java.io.InputStream,net.minecraft.nbt.NbtAccounter)", "a");
|
||||
put("net.minecraft.nbt.NbtAccounter#unlimitedHeap()", "a");
|
||||
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");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_20R4 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_20R3);
|
||||
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bC");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "n");
|
||||
put("net.minecraft.core.component.DataComponents#CUSTOM_DATA", "b");
|
||||
put("net.minecraft.core.component.DataComponentHolder#get(net.minecraft.core.component.DataComponentType)", "a");
|
||||
put("net.minecraft.world.item.component.CustomData#copyTag()", "c");
|
||||
put("net.minecraft.world.item.ItemStack#set(net.minecraft.core.component.DataComponentType,java.lang.Object)", "b");
|
||||
put("net.minecraft.world.item.ItemStack#save(net.minecraft.core.HolderLookup$Provider)", "a");
|
||||
put("net.minecraft.server.MinecraftServer#registryAccess()", "bc");
|
||||
put("net.minecraft.world.item.ItemStack#parseOptional(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)", "a");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId(net.minecraft.core.HolderLookup$Provider)", "c");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#loadWithComponents(net.minecraft.nbt.CompoundTag,net.minecraft.core.HolderLookup$Provider)", "c");
|
||||
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");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R1 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_20R4);
|
||||
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bD");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
public static Map<String, String> getMapping() {
|
||||
switch (MinecraftVersion.getVersion()) {
|
||||
case MC1_21_R1:
|
||||
return MC1_21R1;
|
||||
case MC1_20_R4:
|
||||
return MC1_20R4;
|
||||
case MC1_20_R3:
|
||||
return MC1_20R3;
|
||||
case MC1_20_R2:
|
||||
return MC1_20R2;
|
||||
case MC1_20_R1:
|
||||
return MC1_20R1;
|
||||
case MC1_19_R3:
|
||||
return MC1_19R2;
|
||||
case MC1_19_R2:
|
||||
return MC1_19R2;
|
||||
case MC1_19_R1:
|
||||
@@ -131,9 +207,11 @@ public class MojangToMapping {
|
||||
return MC1_18R2;
|
||||
case MC1_18_R1:
|
||||
return MC1_18R1;
|
||||
case UNKNOWN:
|
||||
return MC1_20R2; // assume it's a future version, so try the latest known mappings
|
||||
default:
|
||||
return MC1_19R2;// throw new NbtApiException("This version of the NBTAPI is not compatible with
|
||||
// this server version!");
|
||||
// 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!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -17,8 +17,9 @@ import static de.tr7zw.changeme.nbtapi.utils.MinecraftVersion.getLogger;
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum ObjectCreator {
|
||||
NMS_NBTTAGCOMPOUND(null, null, ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),
|
||||
NMS_CUSTOMDATA(MinecraftVersion.MC1_20_R4, null, ClassWrapper.NMS_CUSTOMDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),
|
||||
NMS_BLOCKPOSITION(null, null, ClassWrapper.NMS_BLOCKPOSITION.getClazz(), int.class, int.class, int.class),
|
||||
NMS_COMPOUNDFROMITEM(MinecraftVersion.MC1_11_R1, null, ClassWrapper.NMS_ITEMSTACK.getClazz(),
|
||||
NMS_COMPOUNDFROMITEM(MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_20_R3, ClassWrapper.NMS_ITEMSTACK.getClazz(),
|
||||
ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),;
|
||||
|
||||
private Constructor<?> construct;
|
||||
|
||||
+61
-20
@@ -5,7 +5,6 @@ import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
@@ -36,6 +35,9 @@ public enum ReflectionMethod {
|
||||
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int[].class },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putIntArray(java.lang.String,int[])")),
|
||||
COMPOUND_SET_LONGARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, long[].class },
|
||||
MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putLongArray(java.lang.String,long[])")),
|
||||
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, long.class },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putLong(java.lang.String,long)")),
|
||||
@@ -56,7 +58,7 @@ public enum ReflectionMethod {
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putUUID(java.lang.String,java.util.UUID)")),
|
||||
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "merge(net.minecraft.nbt.CompoundTag)")),
|
||||
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, ClassWrapper.NMS_NBTBASE.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
|
||||
@@ -85,6 +87,9 @@ public enum ReflectionMethod {
|
||||
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getIntArray"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getIntArray(java.lang.String)")),
|
||||
COMPOUND_GET_LONGARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1,
|
||||
new Since(MinecraftVersion.MC1_16_R1, "getLongArray"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getLongArray(java.lang.String)")),
|
||||
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getLong"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getLong(java.lang.String)")),
|
||||
@@ -107,10 +112,10 @@ public enum ReflectionMethod {
|
||||
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,
|
||||
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() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "save"),
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R3, new Since(MinecraftVersion.MC1_7_R4, "save"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "save(net.minecraft.nbt.CompoundTag)")),
|
||||
NMSITEM_CREATESTACK(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_10_R1, new Since(MinecraftVersion.MC1_7_R4, "createStack")),
|
||||
@@ -156,7 +161,7 @@ public enum ReflectionMethod {
|
||||
new Since(MinecraftVersion.MC1_13_R1, "get"), new Since(MinecraftVersion.MC1_18_R1, "get(int)")),
|
||||
|
||||
ITEMSTACK_SET_TAG(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTag"),
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R3, new Since(MinecraftVersion.MC1_7_R4, "setTag"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "setTag(net.minecraft.nbt.CompoundTag)")),
|
||||
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK, new Class[] { ItemStack.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
|
||||
@@ -168,10 +173,6 @@ public enum ReflectionMethod {
|
||||
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[] { ClassWrapper.NMS_BLOCKPOSITION.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "getTileEntity"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getBlockEntity(net.minecraft.core.BlockPos)")),
|
||||
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER,
|
||||
new Class[] { ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_16_R3,
|
||||
new Since(MinecraftVersion.MC1_8_R3, "setTileEntity")),
|
||||
NMS_WORLD_REMOVE_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[] { ClassWrapper.NMS_BLOCKPOSITION.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_8_R3, "t"),
|
||||
new Since(MinecraftVersion.MC1_9_R1, "s"), new Since(MinecraftVersion.MC1_13_R1, "n"),
|
||||
@@ -180,11 +181,9 @@ public enum ReflectionMethod {
|
||||
NMS_WORLD_GET_TILEENTITY_1_7_10(ClassWrapper.NMS_WORLDSERVER, new Class[] { int.class, int.class, int.class },
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getTileEntity")),
|
||||
// FIXME: No Spigot mapping!
|
||||
TILEENTITY_LOAD_LEGACY191(ClassWrapper.NMS_TILEENTITY,
|
||||
new Class[] { ClassWrapper.NMS_MINECRAFTSERVER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_9_R1, MinecraftVersion.MC1_9_R1, new Since(MinecraftVersion.MC1_9_R1, "a")),
|
||||
// FIXME: No Spigot mapping!
|
||||
TILEENTITY_LOAD_LEGACY183(ClassWrapper.NMS_TILEENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_9_R2, new Since(MinecraftVersion.MC1_8_R3, "c"),
|
||||
new Since(MinecraftVersion.MC1_9_R1, "a"), new Since(MinecraftVersion.MC1_9_R2, "c")),
|
||||
@@ -201,7 +200,7 @@ public enum ReflectionMethod {
|
||||
TILEENTITY_GET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_7_R4, "b"),
|
||||
new Since(MinecraftVersion.MC1_9_R1, "save")),
|
||||
TILEENTITY_GET_NBT_1181(ClassWrapper.NMS_TILEENTITY, new Class[] {}, MinecraftVersion.MC1_18_R1,
|
||||
TILEENTITY_GET_NBT_1181(ClassWrapper.NMS_TILEENTITY, new Class[] {}, MinecraftVersion.MC1_18_R1, MinecraftVersion.MC1_20_R3,
|
||||
new Since(MinecraftVersion.MC1_18_R1, "saveWithId()")),
|
||||
TILEENTITY_SET_NBT_LEGACY1151(ClassWrapper.NMS_TILEENTITY,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() }, MinecraftVersion.MC1_7_R4,
|
||||
@@ -211,7 +210,7 @@ public enum ReflectionMethod {
|
||||
new Class[] { ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_16_R1, "load")),
|
||||
TILEENTITY_SET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_16_R1, "load"),
|
||||
MinecraftVersion.MC1_17_R1, MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_16_R1, "load"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
|
||||
TILEENTITY_GET_BLOCKDATA(ClassWrapper.NMS_TILEENTITY, new Class[] {}, MinecraftVersion.MC1_16_R1,
|
||||
new Since(MinecraftVersion.MC1_16_R1, "getBlock"),
|
||||
@@ -231,8 +230,19 @@ public enum ReflectionMethod {
|
||||
new Since(MinecraftVersion.MC1_14_R1, "getSaveID"), new Since(MinecraftVersion.MC1_18_R1, "getEncodeId()")),
|
||||
|
||||
NBTFILE_READ(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS, new Class[] { InputStream.class },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"),
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R2, new Since(MinecraftVersion.MC1_7_R4, "a"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "readCompressed(java.io.InputStream)")),
|
||||
|
||||
NBTFILE_READV2(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS,
|
||||
new Class[] { InputStream.class, ClassWrapper.NMS_NBTACCOUNTER.getClazz() }, MinecraftVersion.MC1_20_R3,
|
||||
new Since(MinecraftVersion.MC1_20_R3,
|
||||
"readCompressed(java.io.InputStream,net.minecraft.nbt.NbtAccounter)")),
|
||||
|
||||
NBTACCOUNTER_CREATE_UNLIMITED(ClassWrapper.NMS_NBTACCOUNTER,
|
||||
new Class[] {}, MinecraftVersion.MC1_20_R3,
|
||||
new Since(MinecraftVersion.MC1_20_R3,
|
||||
"unlimitedHeap()")),
|
||||
|
||||
NBTFILE_WRITE(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), OutputStream.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "a"),
|
||||
@@ -246,10 +256,8 @@ public enum ReflectionMethod {
|
||||
MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
|
||||
REGISTRY_GET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] { Object.class }, MinecraftVersion.MC1_11_R1,
|
||||
MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "get")),
|
||||
// FIXME: No Spigot mapping!
|
||||
REGISTRY_SET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] { Object.class, Object.class },
|
||||
MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "a")),
|
||||
// FIXME: No Spigot mapping!
|
||||
REGISTRY_GET_INVERSE(ClassWrapper.NMS_REGISTRYMATERIALS, new Class[] { Object.class }, MinecraftVersion.MC1_11_R1,
|
||||
MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "b")),
|
||||
REGISTRYMATERIALS_KEYSET(ClassWrapper.NMS_REGISTRYMATERIALS, new Class[] {}, MinecraftVersion.MC1_13_R1,
|
||||
@@ -260,12 +268,12 @@ public enum ReflectionMethod {
|
||||
MinecraftVersion.MC1_13_R2, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R2, "getKey")),
|
||||
|
||||
GAMEPROFILE_DESERIALIZE(ClassWrapper.NMS_GAMEPROFILESERIALIZER,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() }, MinecraftVersion.MC1_7_R4,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() }, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R3,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "deserialize"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "readGameProfile(net.minecraft.nbt.CompoundTag)")),
|
||||
GAMEPROFILE_SERIALIZE(ClassWrapper.NMS_GAMEPROFILESERIALIZER,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.GAMEPROFILE.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "serialize"),
|
||||
MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_20_R3, new Since(MinecraftVersion.MC1_8_R3, "serialize"),
|
||||
new Since(MinecraftVersion.MC1_18_R1,
|
||||
"writeGameProfile(net.minecraft.nbt.CompoundTag,com.mojang.authlib.GameProfile)")),
|
||||
|
||||
@@ -275,7 +283,29 @@ public enum ReflectionMethod {
|
||||
MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "getRaw")),
|
||||
CRAFT_PERSISTENT_DATA_CONTAINER_PUT_ALL(ClassWrapper.CRAFT_PERSISTENTDATACONTAINER,
|
||||
new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() }, MinecraftVersion.MC1_14_R1,
|
||||
new Since(MinecraftVersion.MC1_14_R1, "putAll")),;
|
||||
new Since(MinecraftVersion.MC1_14_R1, "putAll")),
|
||||
// 1.20.5+ Stuff
|
||||
NMSDATACOMPONENTHOLDER_GET(ClassWrapper.NMS_DATACOMPONENTHOLDER, new Class[] {ClassWrapper.NMS_DATACOMPONENTTYPE.getClazz()}, MinecraftVersion.MC1_20_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "get(net.minecraft.core.component.DataComponentType)")),
|
||||
NMSCUSTOMDATA_GETCOPY(ClassWrapper.NMS_CUSTOMDATA, new Class[] {}, MinecraftVersion.MC1_20_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "copyTag()")),
|
||||
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)")),
|
||||
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)")),
|
||||
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,
|
||||
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)")),
|
||||
GET_DATAFIXER(ClassWrapper.NMS_DATAFIXERS, new Class[] {}, MinecraftVersion.MC1_20_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "getDataFixer()")),
|
||||
;
|
||||
|
||||
private MinecraftVersion removedAfter;
|
||||
private Since targetVersion;
|
||||
@@ -310,6 +340,17 @@ public enum ReflectionMethod {
|
||||
if (MinecraftVersion.isForgePresent() && MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
|
||||
targetMethodName = Forge1710Mappings.getMethodMapping().getOrDefault(this.name(), targetMethodName);
|
||||
} else if (targetVersion.version.isMojangMapping()) {
|
||||
try {
|
||||
// check for mojang mapped method
|
||||
String name = targetVersion.name.split("\\(")[0];
|
||||
method = targetClass.getClazz().getMethod(name, args);
|
||||
method.setAccessible(true);
|
||||
loaded = true;
|
||||
methodName = name;
|
||||
return;
|
||||
} catch (NoSuchMethodException ignore) {
|
||||
// not mojang mapped
|
||||
}
|
||||
targetMethodName = MojangToMapping.getMapping().getOrDefault(
|
||||
targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
|
||||
}
|
||||
@@ -355,7 +396,7 @@ 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()), ex);
|
||||
+ ", Enum: " + this + ", Passed Class: " + (target == null ? "null" : target.getClass()) + " Args: " + (args == null ? "null" : Arrays.toString(args)), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public enum Casing {
|
||||
camelCase(s -> {
|
||||
if (s.length() < 2) {
|
||||
return s.toLowerCase();
|
||||
}
|
||||
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
|
||||
}), snake_case(s -> {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// Convert the first letter to lowercase
|
||||
result.append(Character.toLowerCase(s.charAt(0)));
|
||||
// Iterate through the rest of the string
|
||||
for (int i = 1; i < s.length(); i++) {
|
||||
char currentChar = s.charAt(i);
|
||||
// Convert uppercase letters to lowercase and add underscore
|
||||
if (Character.isUpperCase(currentChar)) {
|
||||
result.append('_').append(Character.toLowerCase(currentChar));
|
||||
} else {
|
||||
result.append(currentChar);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}), PascalCase(s -> {
|
||||
if (s.length() < 2) {
|
||||
return s.toUpperCase();
|
||||
}
|
||||
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
|
||||
}), lowercase(String::toLowerCase), UPPERCASE(String::toUpperCase);
|
||||
|
||||
private UnaryOperator<String> convert;
|
||||
|
||||
Casing(UnaryOperator<String> function) {
|
||||
this.convert = function;
|
||||
}
|
||||
|
||||
public String convertString(String str) {
|
||||
return convert.apply(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
|
||||
class DefaultMethodInvoker {
|
||||
|
||||
private static Method invokeDefaultMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
invokeDefaultMethod = InvocationHandler.class.getDeclaredMethod("invokeDefault",
|
||||
new Class[] { Object.class, Method.class, Object[].class });
|
||||
invokeDefaultMethod.setAccessible(true);
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
// we are in java 8, use the fallback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using reflections to access reflections, since some are still on java 8.
|
||||
*
|
||||
* @param target
|
||||
* @param method
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static Object invokeDefault(Class<?> srcInt, Object target, Method method, Object[] args) {
|
||||
if (invokeDefaultMethod != null) { // java 9+
|
||||
try {
|
||||
return invokeDefaultMethod.invoke(null, target, method, args);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Error while trying to invoke a default method for Java 9+. " + target + " "
|
||||
+ method + " " + Arrays.toString(args), e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(srcInt).in(srcInt).unreflectSpecial(method, srcInt).bindTo(target)
|
||||
.invokeWithArguments(args);
|
||||
} catch (Throwable e) {
|
||||
throw new NbtApiException("Error while trying to invoke a default method for Java 8. " + target + " "
|
||||
+ method + " " + Arrays.toString(args), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
|
||||
|
||||
public interface NBTProxy {
|
||||
|
||||
final Map<Class<?>, NBTHandler<Object>> handlers = new HashMap<>();
|
||||
|
||||
public default void init() {
|
||||
|
||||
}
|
||||
|
||||
public default Casing getCasing() {
|
||||
return Casing.PascalCase;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public default <T> NBTHandler<T> getHandler(Class<T> clazz) {
|
||||
return (NBTHandler<T>) handlers.get(clazz);
|
||||
}
|
||||
|
||||
public default Collection<NBTHandler<Object>> getHandlers() {
|
||||
return handlers.values();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public default <T> void registerHandler(Class<T> clazz, NBTHandler<T> handler) {
|
||||
handlers.put(clazz, (NBTHandler<Object>) handler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@Target(METHOD)
|
||||
public @interface NBTTarget {
|
||||
public String value();
|
||||
|
||||
public Type type() default Type.AUTOMATIC;
|
||||
|
||||
public enum Type {
|
||||
AUTOMATIC, GET, SET, HAS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
|
||||
|
||||
class ProxiedList<E extends NBTProxy> implements ProxyList<E> {
|
||||
|
||||
private final ReadWriteNBTCompoundList nbt;
|
||||
private final Class<E> proxy;
|
||||
|
||||
public ProxiedList(ReadWriteNBTCompoundList nbt, Class<E> proxyClass) {
|
||||
this.nbt = nbt;
|
||||
this.proxy = proxyClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
ReadWriteNBT tag = nbt.get(index);
|
||||
return new ProxyBuilder<E>(tag,proxy).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return nbt.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(int index) {
|
||||
nbt.remove(index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Itr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E addCompound() {
|
||||
ReadWriteNBT tag = nbt.addCompound();
|
||||
return new ProxyBuilder<E>(tag,proxy).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return nbt.isEmpty();
|
||||
}
|
||||
|
||||
private class Itr implements Iterator<E> {
|
||||
/**
|
||||
* Index of element to be returned by subsequent call to next.
|
||||
*/
|
||||
int cursor = 0;
|
||||
|
||||
/**
|
||||
* Index of element returned by most recent call to next or
|
||||
* previous. Reset to -1 if this element is deleted by a call
|
||||
* to remove.
|
||||
*/
|
||||
int lastRet = -1;
|
||||
|
||||
public boolean hasNext() {
|
||||
return cursor != size();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
try {
|
||||
int i = cursor;
|
||||
E next = get(i);
|
||||
lastRet = i;
|
||||
cursor = i + 1;
|
||||
return next;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (lastRet < 0)
|
||||
throw new IllegalStateException();
|
||||
|
||||
try {
|
||||
ProxiedList.this.remove(lastRet);
|
||||
if (lastRet < cursor)
|
||||
cursor--;
|
||||
lastRet = -1;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.NBTHandler;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
|
||||
|
||||
public class ProxyBuilder<T extends NBTProxy> implements InvocationHandler {
|
||||
|
||||
private static final Map<Method, Function<Arguments, Object>> METHOD_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private final Class<T> target;
|
||||
private final ReadWriteNBT nbt;
|
||||
private boolean readOnly;
|
||||
|
||||
public ProxyBuilder(ReadWriteNBT nbt, Class<T> target) {
|
||||
if (!target.isInterface()) {
|
||||
throw new NbtApiException("A proxy can only be built from an interface! Check the wiki for examples.");
|
||||
}
|
||||
this.target = target;
|
||||
this.nbt = nbt;
|
||||
}
|
||||
|
||||
public ProxyBuilder<T> readOnly() {
|
||||
this.readOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T build() {
|
||||
T inst = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { target }, this);
|
||||
inst.init();
|
||||
return inst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
METHOD_CACHE.computeIfAbsent(method, m -> ProxyBuilder.createFunction((NBTProxy) proxy, m));
|
||||
return METHOD_CACHE.get(method).apply(new Arguments(target, (NBTProxy) proxy, readOnly, nbt, args));
|
||||
}
|
||||
|
||||
private static class Arguments {
|
||||
Class<?> target;
|
||||
NBTProxy proxy;
|
||||
ReadWriteNBT nbt;
|
||||
Object[] args;
|
||||
boolean readOnly;
|
||||
|
||||
public Arguments(Class<?> target, NBTProxy proxy, boolean readOnly, ReadWriteNBT nbt, Object[] args) {
|
||||
this.target = target;
|
||||
this.proxy = proxy;
|
||||
this.nbt = nbt;
|
||||
this.args = args;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static Function<Arguments, Object> createFunction(NBTProxy proxy, Method method) {
|
||||
if ("toString".equals(method.getName()) && method.getParameterCount() == 0
|
||||
&& method.getReturnType() == String.class) {
|
||||
return (arguments) -> arguments.nbt.toString();
|
||||
}
|
||||
if (method.isDefault()) {
|
||||
return (arguments) -> DefaultMethodInvoker.invokeDefault(arguments.target, arguments.proxy, method,
|
||||
arguments.args);
|
||||
}
|
||||
Type action = getAction(method);
|
||||
if (action == Type.SET) {
|
||||
String fieldName = getNBTName(proxy.getCasing(), method);
|
||||
return (arguments) -> {
|
||||
if (arguments.readOnly) {
|
||||
throw new NbtApiException("Tried calling a set method on a read only object.");
|
||||
}
|
||||
return setNBT(arguments.nbt, arguments.proxy, fieldName, arguments.args[0]);
|
||||
};
|
||||
}
|
||||
if (action == Type.GET) {
|
||||
Class<?> retType = method.getReturnType();
|
||||
String fieldName = getNBTName(proxy.getCasing(), method);
|
||||
// Allow stacking of proxies
|
||||
if (retType.isInterface() && NBTProxy.class.isAssignableFrom(retType)) {
|
||||
return (arguments) -> {
|
||||
if (arguments.nbt.hasTag(fieldName) && arguments.nbt.getType(fieldName) != NBTType.NBTTagCompound) {
|
||||
throw new NbtApiException("Tried getting a '" + retType + "' proxy from the field '" + fieldName
|
||||
+ "', but it's not a TagCompound!");
|
||||
}
|
||||
return new ProxyBuilder<NBTProxy>(arguments.nbt.getOrCreateCompound(fieldName),
|
||||
(Class<NBTProxy>) retType).build();
|
||||
};
|
||||
}
|
||||
if (retType == ProxyList.class) {
|
||||
|
||||
Class<?> parameterType = (Class<?>) ((ParameterizedType) method.getGenericReturnType())
|
||||
.getActualTypeArguments()[0];
|
||||
if (parameterType != null && parameterType.isInterface()
|
||||
&& NBTProxy.class.isAssignableFrom(parameterType)) {
|
||||
return (arguments) -> {
|
||||
return new ProxiedList(arguments.nbt.getCompoundList(fieldName), parameterType);
|
||||
};
|
||||
}
|
||||
}
|
||||
NBTHandler<Object> handler = (NBTHandler<Object>) proxy.getHandler(retType);
|
||||
if (handler != null) {
|
||||
return (arguments) -> handler.get(arguments.nbt, fieldName);
|
||||
}
|
||||
return (arguments) -> arguments.nbt.getOrNull(fieldName, retType);
|
||||
}
|
||||
if (action == Type.HAS) {
|
||||
String fieldName = getNBTName(proxy.getCasing(), method);
|
||||
return (arguments) -> arguments.nbt.hasTag(fieldName);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"The method '" + method.getName() + "' in '" + method.getDeclaringClass().getName()
|
||||
+ "' can not be handled by the NBT-API. Please check the Wiki for examples!");
|
||||
}
|
||||
|
||||
private static Type getAction(Method method) {
|
||||
NBTTarget target = method.getAnnotation(NBTTarget.class);
|
||||
if (target != null) {
|
||||
if (target.type() == Type.HAS && method.getParameterCount() == 0
|
||||
&& method.getReturnType() == boolean.class) {
|
||||
return Type.HAS;
|
||||
}
|
||||
if (target.type() == Type.GET && method.getParameterCount() == 0) {
|
||||
return Type.GET;
|
||||
}
|
||||
if (target.type() == Type.SET && method.getParameterCount() == 1) {
|
||||
return Type.SET;
|
||||
}
|
||||
}
|
||||
if (method.getName().startsWith("set") && method.getParameterCount() == 1) {
|
||||
return Type.SET;
|
||||
}
|
||||
if (method.getName().startsWith("get") && method.getParameterCount() == 0) {
|
||||
return Type.GET;
|
||||
}
|
||||
if (method.getName().startsWith("has") && method.getParameterCount() == 0
|
||||
&& method.getReturnType() == boolean.class) {
|
||||
return Type.HAS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getNBTName(Casing casing, Method method) {
|
||||
NBTTarget target = method.getAnnotation(NBTTarget.class);
|
||||
if (target != null) {
|
||||
return target.value();
|
||||
}
|
||||
return casing.convertString(method.getName().substring(3));
|
||||
}
|
||||
|
||||
private static Object setNBT(ReadWriteNBT nbt, NBTProxy proxy, String key, Object value) {
|
||||
// welcome to the "I wish we all could use java 17" method. Thanks, legacy mc
|
||||
// versions
|
||||
if (value == null) {
|
||||
nbt.removeKey(key);
|
||||
} else if (value instanceof Boolean) {
|
||||
nbt.setBoolean(key, (Boolean) value);
|
||||
} else if (value instanceof Byte) {
|
||||
nbt.setByte(key, (Byte) value);
|
||||
} else if (value instanceof Short) {
|
||||
nbt.setShort(key, (Short) value);
|
||||
} else if (value instanceof Integer) {
|
||||
nbt.setInteger(key, (Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
nbt.setLong(key, (Long) value);
|
||||
} else if (value instanceof Float) {
|
||||
nbt.setFloat(key, (Float) value);
|
||||
} else if (value instanceof Double) {
|
||||
nbt.setDouble(key, (Double) value);
|
||||
} else if (value instanceof byte[]) {
|
||||
nbt.setByteArray(key, (byte[]) value);
|
||||
} else if (value instanceof int[]) {
|
||||
nbt.setIntArray(key, (int[]) value);
|
||||
} else if (value instanceof long[]) {
|
||||
nbt.setLongArray(key, (long[]) value);
|
||||
} else if (value instanceof String) {
|
||||
nbt.setString(key, (String) value);
|
||||
} else if (value instanceof UUID) {
|
||||
nbt.setUUID(key, (UUID) value);
|
||||
} else if (value.getClass().isEnum()) {
|
||||
nbt.setEnum(key, (Enum<?>) value);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
NBTHandler<Object> handler = (NBTHandler<Object>) proxy.getHandler(value.getClass());
|
||||
if (handler != null) {
|
||||
handler.set(nbt, key, value);
|
||||
} else {
|
||||
for (NBTHandler<Object> nbth : proxy.getHandlers()) {
|
||||
if (nbth.fuzzyMatch(value)) {
|
||||
nbth.set(nbt, key, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Tried setting an object of type '" + value.getClass().getName()
|
||||
+ "'. This is not a supported NBT value. Please check the Wiki for examples!");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package de.tr7zw.changeme.nbtapi.wrapper;
|
||||
|
||||
public interface ProxyList<T extends NBTProxy> extends Iterable<T> {
|
||||
|
||||
/**
|
||||
* Adds a new compound tag to the current compound tag
|
||||
*
|
||||
* @return A new instance of the class.
|
||||
*/
|
||||
T addCompound();
|
||||
|
||||
/**
|
||||
* Returns the number of elements in this collection. If this collection
|
||||
* contains more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE.
|
||||
*
|
||||
* @return the number of elements in this collection
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Returns true if this collection contains no elements.
|
||||
*
|
||||
* @return true if this collection contains no elements
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Get the object with the given id.
|
||||
*
|
||||
* @param id The id of the object to get.
|
||||
* @return The object with the given id.
|
||||
*/
|
||||
T get(int id);
|
||||
|
||||
/**
|
||||
* Removes the element at the specified position in this list
|
||||
*
|
||||
* @param i The index of the element to remove.
|
||||
*/
|
||||
void remove(int i);
|
||||
|
||||
}
|
||||
+8
-14
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
</parent>
|
||||
<artifactId>item-nbt-api-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -12,21 +12,15 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>nbt-injector</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>nbt-data-api</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>datafixerupper</artifactId>
|
||||
<version>4.0.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
@@ -39,7 +33,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<relocations>
|
||||
|
||||
@@ -26,28 +26,30 @@ import de.tr7zw.nbtapi.plugin.tests.compounds.GetterSetterTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.InterfaceTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.IteratorTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.ListTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.LongArrayTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.MergeTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.ModernSubCompoundsTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.RemovingKeys;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.ResolveTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.StreamTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.SubCompoundsTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.TypeTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.data.WorldDataTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.entities.EntityCustomNbtPersistentTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.entities.EntityTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.injector.EntityCustomNbtInjectorTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.injector.MergeTileSubCompoundTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.injector.SpawnEntityCustomNbtInjectorTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.injector.TilesCustomNBTInjectorTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ComponentsTest;
|
||||
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.tiles.TileTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.tiles.TilesCustomNBTPersistentTest;
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemMergingTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemStackConversionTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.LegacyItemTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.MetaTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.NBTModifyItemTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.SmuggleTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.proxy.SimpleProxyTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.tiles.TileTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.tiles.TilesCustomNBTPersistentTest;
|
||||
|
||||
public class NBTAPI extends JavaPlugin {
|
||||
|
||||
@@ -64,7 +66,6 @@ public class NBTAPI extends JavaPlugin {
|
||||
public void onLoad() {
|
||||
|
||||
getConfig().options().copyDefaults(true);
|
||||
getConfig().addDefault("nbtInjector.enabled", false);
|
||||
getConfig().addDefault("bStats.enabled", true);
|
||||
getConfig().addDefault("updateCheck.enabled", true);
|
||||
getConfig().addDefault("silentquickstart", false);
|
||||
@@ -82,25 +83,13 @@ public class NBTAPI extends JavaPlugin {
|
||||
MinecraftVersion.enableUpdateCheck();
|
||||
}
|
||||
|
||||
// Disabled by default since 2.1. Enable it yourself by calling
|
||||
// NBTInjector.inject(); during onLoad/config
|
||||
if (getConfig().getBoolean("nbtInjector.enabled")) {
|
||||
getLogger().info("Injecting custom NBT");
|
||||
try {
|
||||
NBTInjector.inject();
|
||||
getLogger().info("Injected!");
|
||||
} catch (Throwable ex) { // NOSONAR
|
||||
getLogger().log(Level.SEVERE, "Error while Injecting custom Tile/Entity classes!", ex);
|
||||
compatible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// NBTCompounds
|
||||
apiTests.add(new GetterSetterTest());
|
||||
apiTests.add(new TypeTest());
|
||||
apiTests.add(new RemovingKeys());
|
||||
apiTests.add(new EnumTest());
|
||||
apiTests.add(new InterfaceTest());
|
||||
apiTests.add(new ResolveTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 list support is not complete at all
|
||||
apiTests.add(new ListTest());
|
||||
apiTests.add(new SubCompoundsTest());
|
||||
@@ -112,18 +101,29 @@ public class NBTAPI extends JavaPlugin {
|
||||
apiTests.add(new EqualsTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 list support is not complete at all
|
||||
apiTests.add(new IteratorTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1))
|
||||
apiTests.add(new LongArrayTest());
|
||||
|
||||
// Items
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) { // 1.7.10 not a thing
|
||||
apiTests.add(new ItemConversionTest());
|
||||
apiTests.add(new ItemStackConversionTest());
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
apiTests.add(new LegacyItemTest());
|
||||
}
|
||||
apiTests.add(new ComponentsTest());
|
||||
apiTests.add(new EmptyItemTest());
|
||||
apiTests.add(new SmuggleTest());
|
||||
apiTests.add(new MetaTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) { // 1.7.10 not a thing
|
||||
apiTests.add(new ItemMergingTest());
|
||||
apiTests.add(new DirectApplyTest());
|
||||
apiTests.add(new DirectApplyMetaTest());
|
||||
apiTests.add(new NBTModifyItemTest());
|
||||
}
|
||||
// Proxies
|
||||
apiTests.add(new SimpleProxyTest());
|
||||
|
||||
// Entity
|
||||
apiTests.add(new EntityTest());
|
||||
@@ -142,15 +142,6 @@ public class NBTAPI extends JavaPlugin {
|
||||
// Files
|
||||
apiTests.add(new NBTFileTest());
|
||||
|
||||
// Data
|
||||
apiTests.add(new WorldDataTest());
|
||||
|
||||
// Injector
|
||||
apiTests.add(new TilesCustomNBTInjectorTest());
|
||||
apiTests.add(new MergeTileSubCompoundTest());
|
||||
apiTests.add(new EntityCustomNbtInjectorTest());
|
||||
apiTests.add(new SpawnEntityCustomNbtInjectorTest());
|
||||
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3))
|
||||
apiTests.add(new GameprofileTest());
|
||||
|
||||
@@ -165,10 +156,7 @@ public class NBTAPI extends JavaPlugin {
|
||||
VersionChecker.hideOk = true;
|
||||
return;
|
||||
}
|
||||
getLogger().info("Adding listeners...");
|
||||
Bukkit.getPluginManager().registerEvents(new ReloadListener(), this);
|
||||
getLogger().info("Gson:");
|
||||
MinecraftVersion.hasGsonSupport();
|
||||
MinecraftVersion.hasGsonSupport(); // init gson(if it hasn't already)
|
||||
getLogger().info("Checking bindings...");
|
||||
MinecraftVersion.getVersion();
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
|
||||
/**
|
||||
* This listener class tries to prevent people from reloading while the
|
||||
* NBTInjector is enabled.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class ReloadListener implements Listener {
|
||||
|
||||
/**
|
||||
* Console handler
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onCommand(org.bukkit.event.server.ServerCommandEvent event) {
|
||||
if (event.getCommand().toLowerCase().startsWith("reload") && NBTInjector.isInjected()) {
|
||||
event.setCancelled(true);
|
||||
event.getSender().sendMessage(
|
||||
"[NBTAPI] The NBTInjector is currently enabled. Reloading will turn the server into an unstable state and data-loss may accure. Please do a clean restart. Canceled reload!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player handler
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onCommand(org.bukkit.event.player.PlayerCommandPreprocessEvent event) {
|
||||
if (event.getMessage().toLowerCase().startsWith("/reload") && NBTInjector.isInjected()) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage(
|
||||
"[NBTAPI] The NBTInjector is currently enabled. Reloading will turn the server into an unstable state and data-loss may accure. Please do a clean restart. Canceled reload!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,9 @@ public class NBTFileTest implements Test {
|
||||
NBTFile file = new NBTFile(testFile);
|
||||
file.addCompound("testcomp").setString("test1", "ok");
|
||||
NBTCompound comp = file.getCompound("testcomp");
|
||||
if (comp == null) {
|
||||
throw new NbtApiException("Error getting compound!");
|
||||
}
|
||||
comp.setString("test2", "ok");
|
||||
file.setLong("time", System.currentTimeMillis());
|
||||
file.setString("test", "test");
|
||||
|
||||
+7
-4
@@ -1,7 +1,8 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.compounds;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class GetterSetterTest implements Test {
|
||||
@@ -30,7 +31,7 @@ public class GetterSetterTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
NBTContainer comp = new NBTContainer();
|
||||
ReadWriteNBT comp = NBT.createNBTObject();
|
||||
|
||||
comp.setString(STRING_TEST_KEY, STRING_TEST_VALUE);
|
||||
comp.setInteger(INT_TEST_KEY, INT_TEST_VALUE);
|
||||
@@ -46,13 +47,15 @@ public class GetterSetterTest implements Test {
|
||||
if (!comp.hasTag(STRING_TEST_KEY)) {
|
||||
throw new NbtApiException("Wasn't able to check a key! The Item-NBT-API may not work!");
|
||||
}
|
||||
int[] intArray = comp.getIntArray(INTARRAY_TEST_KEY);
|
||||
byte[] byteArray = comp.getByteArray(BYTEARRAY_TEST_KEY);
|
||||
if (!(STRING_TEST_VALUE).equals(comp.getString(STRING_TEST_KEY))
|
||||
|| comp.getInteger(INT_TEST_KEY) != INT_TEST_VALUE
|
||||
|| comp.getDouble(DOUBLE_TEST_KEY) != DOUBLE_TEST_VALUE
|
||||
|| comp.getByte(BYTE_TEST_KEY) != BYTE_TEST_VALUE || comp.getShort(SHORT_TEST_KEY) != SHORT_TEST_VALUE
|
||||
|| comp.getFloat(FLOAT_TEST_KEY) != FLOAT_TEST_VALUE || comp.getLong(LONG_TEST_KEY) != LONG_TEST_VALUE
|
||||
|| comp.getIntArray(INTARRAY_TEST_KEY).length != (INTARRAY_TEST_VALUE).length
|
||||
|| comp.getByteArray(BYTEARRAY_TEST_KEY).length != (BYTEARRAY_TEST_VALUE).length
|
||||
|| (intArray != null && intArray.length != (INTARRAY_TEST_VALUE).length)
|
||||
|| (byteArray != null && byteArray.length != (BYTEARRAY_TEST_VALUE).length)
|
||||
|| !comp.getBoolean(BOOLEAN_TEST_KEY).equals(BOOLEAN_TEST_VALUE)) {
|
||||
throw new NbtApiException("One key does not equal the original value! The Item-NBT-API may not work!");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import de.tr7zw.changeme.nbtapi.NBTList;
|
||||
import de.tr7zw.changeme.nbtapi.NBTListCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class ListTest implements Test {
|
||||
@@ -50,20 +51,22 @@ public class ListTest implements Test {
|
||||
taglist = comp.getCompoundList("complist");
|
||||
if (taglist.size() == 1) {
|
||||
lcomp = taglist.get(0);
|
||||
ReadWriteNBT sublist = lcomp.getCompound("listsubkey");
|
||||
if (lcomp.getKeys().size() != 4) {
|
||||
throw new NbtApiException("Wrong key amount in Taglist (" + lcomp.getKeys().size() + ")!");
|
||||
} else if (sublist == null) {
|
||||
throw new NbtApiException("The sublist is null! The Item-NBT-API may not work!");
|
||||
} else if (!(lcomp.getDouble("double1") == 0.3333 && lcomp.getInteger("int1") == 42
|
||||
&& lcomp.getString("test2").equals("test2") && !lcomp.hasTag("test1"))) {
|
||||
throw new NbtApiException("One key in the Taglist changed! The Item-NBT-API may not work!");
|
||||
} else if (lcomp.getCompound("listsubkey") == null
|
||||
|| !"String".equals(lcomp.getCompound("listsubkey").getString("deep"))) {
|
||||
} else if (!"String".equals(sublist.getString("deep"))) {
|
||||
throw new NbtApiException(
|
||||
"The Compound nested in the listcompound was not correct! The Item-NBT-API may not work!");
|
||||
} else if (lcomp.getCompound("listsubkey").getType("deep") != NBTType.NBTTagString) {
|
||||
} else if (sublist.getType("deep") != NBTType.NBTTagString) {
|
||||
throw new NbtApiException("The nested key's type wasn't correct! The Item-NBT-API may not work!");
|
||||
} else if (lcomp.getCompound("listsubkey").getType("deeplist") != NBTType.NBTTagList) {
|
||||
throw new NbtApiException("The nested list's type wasn't correct '"
|
||||
+ lcomp.getCompound("listsubkey").getType("deeplist") + "'! The Item-NBT-API may not work!");
|
||||
} else if (sublist.getType("deeplist") != NBTType.NBTTagList) {
|
||||
throw new NbtApiException("The nested list's type wasn't correct '" + sublist.getType("deeplist")
|
||||
+ "'! The Item-NBT-API may not work!");
|
||||
}
|
||||
} else {
|
||||
throw new NbtApiException("Taglist is empty! The Item-NBT-API may not work!");
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.compounds;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class LongArrayTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
NBTContainer comp = new NBTContainer();
|
||||
|
||||
comp.setLongArray("test", new long[] { 1, 2, 3, 4, Long.MAX_VALUE });
|
||||
|
||||
if (!comp.hasTag("test")) {
|
||||
throw new NbtApiException("Wasn't able to check a key! The Item-NBT-API may not work!");
|
||||
}
|
||||
if (!Arrays.equals(comp.getLongArray("test"), new long[] { 1, 2, 3, 4, Long.MAX_VALUE })) {
|
||||
throw new NbtApiException("The long key does not equal the original value! The Item-NBT-API may not work!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.compounds;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
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;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class ResolveTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ReadWriteNBT src = NBT.createNBTObject();
|
||||
src.resolveOrCreateCompound("foo.bar.baz").setInteger("test", 42);
|
||||
if (src.resolveOrDefault("foo.bar.baz.test", 0) != 42) {
|
||||
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
|
||||
}
|
||||
if (!Integer.valueOf(42).equals(src.resolveOrNull("foo.bar.baz.test", Integer.class))) {
|
||||
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
|
||||
}
|
||||
src.resolveOrCreateCompound("foo.some\\.key.baz").setInteger("other", 123);
|
||||
if (src.getOrCreateCompound("foo").getOrCreateCompound("some.key").getOrCreateCompound("baz")
|
||||
.getInteger("other") != 123) {
|
||||
throw new NbtApiException("Wasn't able to check the value! The Item-NBT-API may not work!");
|
||||
}
|
||||
ReadWriteNBT comp = src.resolveOrCreateCompound("foo.bar.baz");
|
||||
comp.setIntArray("intarray", new int[] {1,2,3,4,5,6});
|
||||
comp.getStringList("strlist").addAll(Arrays.asList("Hello", "World!"));
|
||||
ReadWriteNBTCompoundList list = comp.getCompoundList("somelist");
|
||||
list.addCompound().setInteger("id", 1);
|
||||
list.addCompound().setInteger("id", 2);
|
||||
list.addCompound().setInteger("id", 3);
|
||||
list.addCompound().setInteger("id", 4);
|
||||
list.addCompound().setInteger("id", 5);
|
||||
list.addCompound().setInteger("id", 6);
|
||||
ReadableNBT listEntry = src.resolveCompound("foo.bar.baz.somelist[0]");
|
||||
if(listEntry == null || listEntry.getInteger("id") != 1) {
|
||||
throw new NbtApiException("The value is not what was expected! The Item-NBT-API may not work!");
|
||||
}
|
||||
ReadableNBT lastEntry = src.resolveOrCreateCompound("foo.bar.baz.somelist[-1]");
|
||||
if(lastEntry == null || lastEntry.getInteger("id") != 6) {
|
||||
throw new NbtApiException("The value is not what was expected! The Item-NBT-API may not work!");
|
||||
}
|
||||
if(src.resolveOrDefault("foo.bar.baz.intarray[1]", 0) != 2) {
|
||||
throw new NbtApiException("The value is not what was expected! The Item-NBT-API may not work!");
|
||||
}
|
||||
if(src.resolveOrNull("foo.bar.baz.intarray[2]", Integer.class) != (Integer)3) {
|
||||
throw new NbtApiException("The value is not what was expected! The Item-NBT-API may not work!");
|
||||
}
|
||||
if(!"World!".equals(src.resolveOrNull("foo.bar.baz.strlist[1]", String.class))) {
|
||||
throw new NbtApiException("The value is not what was expected! The Item-NBT-API may not work!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -12,13 +12,13 @@ public class StreamTest implements Test {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
NBTContainer base = new NBTContainer();
|
||||
base.addCompound("sub").setString("hello", "world");
|
||||
base.getOrCreateCompound("sub").setString("hello", "world");
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
base.getCompound("sub").writeCompound(outStream);
|
||||
base.getOrCreateCompound("sub").writeCompound(outStream);
|
||||
byte[] data = outStream.toByteArray();
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
|
||||
NBTContainer container = new NBTContainer(inputStream);
|
||||
if (!container.toString().equals(base.getCompound("sub").toString())) {
|
||||
if (!container.toString().equals(base.getOrCreateCompound("sub").toString())) {
|
||||
throw new NbtApiException("Component content did not match! " + base.getCompound("sub") + " " + container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.data;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.data.NBTData;
|
||||
import de.tr7zw.changeme.nbtapi.data.WorldData;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class WorldDataTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
if (!new File(world.getWorldFolder(), "level.dat").exists())
|
||||
continue;
|
||||
WorldData data = NBTData.getWorldData(world);
|
||||
if (data.getWorldName() == null || data.getSpawnPosition() == null)
|
||||
throw new NbtApiException("Got Null");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
|
||||
public class EntityCustomNbtInjectorTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (!NBTInjector.isInjected())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
if (!world.getEntitiesByClasses(Animals.class, Monster.class).isEmpty()) {
|
||||
Entity ent = world.getEntitiesByClasses(Animals.class, Monster.class).iterator().next();
|
||||
ent = NBTInjector.patchEntity(ent);
|
||||
NBTCompound comp = NBTInjector.getNbtData(ent);
|
||||
comp.setString("Hello", "World");
|
||||
NBTEntity nbtent = new NBTEntity(ent);
|
||||
if (!nbtent.toString().contains("__extraData:{Hello:\"World\"}")) {
|
||||
throw new NbtApiException("Custom Data did not save to the Entity!");
|
||||
}
|
||||
comp.removeKey("Hello");
|
||||
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTEntities!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
|
||||
public class MergeTileSubCompoundTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (!NBTInjector.isInjected())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
boolean failed = false;
|
||||
Block block = world.getBlockAt(world.getSpawnLocation().getBlockX(), 255,
|
||||
world.getSpawnLocation().getBlockZ());
|
||||
if (block.getType() == Material.AIR) {
|
||||
block.setType(Material.CHEST);
|
||||
NBTCompound comp = NBTInjector.getNbtData(block.getState());
|
||||
comp.addCompound("subcomp").setString("hello", "world");
|
||||
NBTContainer cont = new NBTContainer();
|
||||
cont.mergeCompound(comp.getCompound("subcomp"));
|
||||
if (!(cont.hasTag("hello") && "world".equals(cont.getString("hello")))) {
|
||||
failed = true;
|
||||
}
|
||||
block.setType(Material.AIR);
|
||||
if (failed) {
|
||||
throw new NbtApiException("Data was not correct! " + cont);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTTiles!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NBTEntity;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
|
||||
public class SpawnEntityCustomNbtInjectorTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (!NBTInjector.isInjected())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
Entity entity = world.spawnEntity(new Location(world, 0, 0, 0), EntityType.ARMOR_STAND);
|
||||
entity = NBTInjector.patchEntity(entity);
|
||||
NBTCompound comp = NBTInjector.getNbtData(entity);
|
||||
comp.setString("Hello", "World");
|
||||
NBTEntity nbtent = new NBTEntity(entity);
|
||||
if (!nbtent.toString().contains("__extraData:{Hello:\"World\"}")) {
|
||||
throw new NbtApiException("Custom Data did not save to the Entity!");
|
||||
}
|
||||
comp.removeKey("Hello");
|
||||
entity.remove();
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTEntities!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
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.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
import de.tr7zw.nbtinjector.NBTInjector;
|
||||
|
||||
public class TilesCustomNBTInjectorTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (!NBTInjector.isInjected())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
Block block = world.getBlockAt(world.getSpawnLocation().getBlockX(), 255,
|
||||
world.getSpawnLocation().getBlockZ());
|
||||
if (block.getType() == Material.AIR) {
|
||||
block.setType(Material.CHEST);
|
||||
NBTCompound comp = NBTInjector.getNbtData(block.getState());
|
||||
comp.setString("Foo", "Bar");
|
||||
if (!new NBTTileEntity(block.getState()).toString().contains("__extraData:{Foo:\"Bar\"}")) {
|
||||
block.setType(Material.AIR);
|
||||
throw new NbtApiException("Custom Data did not save to the Tile!");
|
||||
}
|
||||
block.setType(Material.AIR);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTTiles!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class ComponentsTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if(!MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return;
|
||||
}
|
||||
ItemStack item = new ItemStack(Material.STICK);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName("test");
|
||||
item.setItemMeta(meta);
|
||||
String comp = NBT.modifyComponents(item, n -> {
|
||||
return n.toString();
|
||||
});
|
||||
if(!comp.contains("test")) {
|
||||
throw new NbtApiException("ReadComponent didn't work!");
|
||||
}
|
||||
NBT.modifyComponents(item, nbt -> {
|
||||
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
|
||||
});
|
||||
if(!item.getItemMeta().getDisplayName().equals("foobar")) {
|
||||
throw new NbtApiException("ModifyComponent didn't work!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+6
-1
@@ -4,22 +4,27 @@ import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class DirectApplyMetaTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return; // skip
|
||||
}
|
||||
ItemStack baseItem = new ItemStack(Material.STONE);
|
||||
NBTItem nbti = new NBTItem(baseItem, true);
|
||||
nbti.setString("SomeKey", "SomeValue");
|
||||
nbti.modifyMeta(this::modifyMeta);
|
||||
|
||||
if (!new NBTItem(baseItem).hasTag("SomeKey") || !"SomeValue".equals(baseItem.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("The item was not modified correctly!");
|
||||
throw new NbtApiException("The item was not modified correctly! " + NBT.itemStackToNBT(baseItem));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -25,7 +25,9 @@ public class DirectApplyTest implements Test {
|
||||
});
|
||||
baseItem = NBT.itemStackFromNBT(NBT.itemStackToNBT(baseItem)); // trick to force the item to be "real", not a
|
||||
// Spigot only item
|
||||
String outside = NBT.get(baseItem, nbt -> nbt.getString("SomeKey"));
|
||||
String outside = NBT.get(baseItem, nbt -> {
|
||||
return nbt.getString("SomeKey");
|
||||
});
|
||||
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
|
||||
throw new NbtApiException("The data was not applied!");
|
||||
}
|
||||
|
||||
+46
-1
@@ -3,6 +3,7 @@ package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
@@ -13,8 +14,27 @@ public class EmptyItemTest implements Test {
|
||||
public void test() throws Exception {
|
||||
ItemStack item = new ItemStack(Material.STONE);
|
||||
NBTItem nbti = new NBTItem(item);
|
||||
if (nbti.getBoolean("test") == null || nbti.getString("test") == null)
|
||||
if (nbti.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have data");
|
||||
}
|
||||
if (nbti.getBoolean("test") == null || nbti.getString("test") == null || nbti.getFloatList("test") == null) {
|
||||
throw new NbtApiException("Getters return null instead of the default value");
|
||||
}
|
||||
if (!nbti.getKeys().isEmpty()) {
|
||||
throw new NbtApiException("getKeys() returned keys!");
|
||||
}
|
||||
nbti.removeKey("test");
|
||||
if (nbti.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have data after remove");
|
||||
}
|
||||
nbti.setString("test", "test");
|
||||
if (!nbti.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have no data");
|
||||
}
|
||||
nbti.removeKey("test");
|
||||
if (nbti.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have data after deletion");
|
||||
}
|
||||
|
||||
try {
|
||||
Material barrel = Material.valueOf("BARREL");
|
||||
@@ -23,6 +43,31 @@ public class EmptyItemTest implements Test {
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// old version
|
||||
}
|
||||
|
||||
item = new ItemStack(Material.STONE);
|
||||
NBT.modify(item, nbt -> {
|
||||
nbt.setString("test", "test");
|
||||
});
|
||||
NBT.get(item, nbt -> {
|
||||
if (!nbt.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have no data");
|
||||
}
|
||||
return nbt.hasNBTData();
|
||||
});
|
||||
NBT.modify(item, nbt -> {
|
||||
nbt.removeKey("test");
|
||||
});
|
||||
NBT.get(item, nbt -> {
|
||||
if (nbt.hasNBTData()) {
|
||||
throw new NbtApiException("Item reported to have data after deletion");
|
||||
}
|
||||
return nbt.hasNBTData();
|
||||
});
|
||||
|
||||
ItemStack testItem = new ItemStack(Material.STONE);
|
||||
NBTItem nbt = new NBTItem(testItem, true);
|
||||
nbt.removeKey("not there");
|
||||
nbt.setBoolean("test", true);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ public class ItemConversionTest implements Test {
|
||||
|
||||
NBTContainer cont = new NBTContainer();
|
||||
cont.setItemStack("testItem", item);
|
||||
if (!cont.getItemStack("testItem").isSimilar(item))
|
||||
if (!item.isSimilar(cont.getItemStack("testItem")))
|
||||
throw new NbtApiException("Rebuilt item did not match the original!");
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,16 @@ import org.bukkit.inventory.meta.BookMeta;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class ItemMergingTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return; // skip, there is no vanilla nbt
|
||||
}
|
||||
ItemStack item = new ItemStack(Material.WRITTEN_BOOK);
|
||||
BookMeta bookMeta = (BookMeta) item.getItemMeta();
|
||||
bookMeta.setAuthor("Author");
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ public class ItemStackConversionTest implements Test {
|
||||
new ItemStack(Material.AIR), new ItemStack(Material.STONE) };
|
||||
NBTCompound comp = NBTItem.convertItemArraytoNBT(src);
|
||||
ItemStack[] recreated = NBTItem.convertNBTtoItemArray(comp);
|
||||
if (src.length != recreated.length) {
|
||||
if (recreated == null || src.length != recreated.length) {
|
||||
throw new NbtApiException("Size did not match!");
|
||||
}
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
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.changeme.nbtapi.utils.DataFixerUtil;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class LegacyItemTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ItemStack item = NBT
|
||||
.itemStackFromNBT(NBT.parseNBT("{id:cobblestone,Count:42,tag:{Enchantments:[{lvl:3,id:unbreaking}]}}"));
|
||||
if (item.getType() != Material.COBBLESTONE || item.getAmount() != 42
|
||||
|| item.getEnchantmentLevel(Enchantment.DURABILITY) != 3) {
|
||||
throw new NbtApiException("1.20 item didn't load correctly! " + item);
|
||||
}
|
||||
ReadWriteNBT nbt = NBT
|
||||
.parseNBT("{id:cobblestone,Count:42,tag:{display:{Name:\"test\"},ench:[{lvl:3,id:34}]}}");
|
||||
nbt = DataFixerUtil.fixUpItemData(nbt, DataFixerUtil.VERSION1_12_2, DataFixerUtil.getCurrentVersion());
|
||||
item = NBT.itemStackFromNBT(nbt);
|
||||
if (item.getType() != Material.COBBLESTONE || item.getAmount() != 42
|
||||
|| item.getEnchantmentLevel(Enchantment.DURABILITY) != 3
|
||||
|| !"test".equals(item.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("1.12.2 item didn't load correctly! " + item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class MetaTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return; // skip
|
||||
}
|
||||
ItemStack item = new ItemStack(Material.STONE);
|
||||
NBT.modify(item, nbt -> {
|
||||
nbt.setInteger("HideFlags", 1);
|
||||
nbt.modifyMeta((rnbt, meta) -> {
|
||||
if (!meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS) || rnbt.getInteger("HideFlags") != 1) {
|
||||
throw new NbtApiException("The meta did not correctly update or read! " + rnbt);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class NBTModifyItemTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ItemStack baseItem = new ItemStack(Material.STONE);
|
||||
NBT.modify(baseItem, nbti -> {
|
||||
nbti.setString("SomeKey", "SomeValue");
|
||||
});
|
||||
if (baseItem.equals(new ItemStack(Material.STONE)) || !new NBTItem(baseItem).hasTag("SomeKey")) {
|
||||
throw new NbtApiException("The item's where not equal!");
|
||||
}
|
||||
baseItem = new ItemStack(Material.STONE);
|
||||
// test using a "real" item
|
||||
baseItem = NBT.itemStackFromNBT(NBT.itemStackToNBT(baseItem));
|
||||
String inside = NBT.modify(baseItem, nbt -> {
|
||||
nbt.setString("SomeKey", "SomeValue");
|
||||
nbt.modifyMeta((r, meta) -> {
|
||||
meta.setDisplayName("Test");
|
||||
});
|
||||
nbt.getOrCreateCompound("sub").setInteger("val", 42);
|
||||
return nbt.getString("SomeKey");
|
||||
});
|
||||
String outside = NBT.get(baseItem, nbt -> {
|
||||
return nbt.getString("SomeKey");
|
||||
});
|
||||
if (baseItem == null) {
|
||||
throw new NbtApiException("Base item was null!");
|
||||
}
|
||||
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
|
||||
throw new NbtApiException("The data was not applied!");
|
||||
}
|
||||
if (new NBTItem(baseItem).getOrCreateCompound("sub").getInteger("val") != 42) {
|
||||
throw new NbtApiException("The sub value was not applied!");
|
||||
}
|
||||
if (!"SomeValue".equals(inside)) {
|
||||
throw new NbtApiException("Inside returned the wrong value!");
|
||||
}
|
||||
if (!"SomeValue".equals(outside)) {
|
||||
throw new NbtApiException("Outside returned the wrong value!");
|
||||
}
|
||||
if (!"Test".equals(baseItem.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("The display name was not applied!");
|
||||
}
|
||||
baseItem = new ItemStack(Material.STONE);
|
||||
// test using a "fake" item
|
||||
inside = NBT.modify(baseItem, nbt -> {
|
||||
nbt.setString("SomeKey", "SomeValue");
|
||||
nbt.modifyMeta((r, meta) -> {
|
||||
meta.setDisplayName("Test");
|
||||
});
|
||||
nbt.getOrCreateCompound("sub").setInteger("val", 42);
|
||||
return nbt.getString("SomeKey");
|
||||
});
|
||||
outside = NBT.get(baseItem, nbt -> (String) nbt.getString("SomeKey"));
|
||||
if (!new NBTItem(baseItem).hasTag("SomeKey")) {
|
||||
throw new NbtApiException("The data was not applied!");
|
||||
}
|
||||
if (new NBTItem(baseItem).getOrCreateCompound("sub").getInteger("val") != 42) {
|
||||
throw new NbtApiException("The sub value was not applied!");
|
||||
}
|
||||
if (!"SomeValue".equals(inside)) {
|
||||
throw new NbtApiException("Inside returned the wrong value!");
|
||||
}
|
||||
if (!"SomeValue".equals(outside)) {
|
||||
throw new NbtApiException("Outside returned the wrong value!");
|
||||
}
|
||||
if (!"Test".equals(baseItem.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("The display name was not applied!");
|
||||
}
|
||||
// other ordering
|
||||
baseItem = new ItemStack(Material.STONE);
|
||||
NBT.modify(baseItem, nbti -> {
|
||||
nbti.setString("a", "SomeValue");
|
||||
});
|
||||
NBT.modify(baseItem, nbt -> {
|
||||
nbt.modifyMeta((r, meta) -> {
|
||||
meta.setDisplayName("Test");
|
||||
});
|
||||
nbt.setInteger("b", 12);
|
||||
});
|
||||
if (!new NBTItem(baseItem).hasTag("a") || !new NBTItem(baseItem).hasTag("b")
|
||||
|| !"Test".equals(baseItem.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("The data was not applied: " + new NBTItem(baseItem).toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTList;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class SmuggleTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ItemStack item = new ItemStack(Material.STONE);
|
||||
Exception error = null;
|
||||
try {
|
||||
ReadWriteNBTList<Long> list = NBT.modify(item, nbt -> {
|
||||
nbt.getLongList("test").add(123l);
|
||||
return nbt.getLongList("test");
|
||||
});
|
||||
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
|
||||
} catch (Exception e) {
|
||||
// this is the excpected behavior
|
||||
}
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
ReadableNBTList<Long> list = NBT.get(item, nbt -> {
|
||||
return nbt.getLongList("test");
|
||||
});
|
||||
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
|
||||
} catch (Exception e) {
|
||||
// this is the excpected behavior
|
||||
}
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
AtomicReference<ReadableNBTList<Long>> list = new AtomicReference<>();
|
||||
try {
|
||||
NBT.get(item, nbt -> {
|
||||
list.set(nbt.getLongList("test")); // smuggling the nbt out of the context
|
||||
return null;
|
||||
});
|
||||
if (list.get().size() == 1) {
|
||||
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
|
||||
}
|
||||
error = new NbtApiException("Managed to smuggle nbt out of the context: " + list);
|
||||
} catch (Exception e) {
|
||||
// this is the excpected behavior
|
||||
}
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
//package de.tr7zw.nbtapi.plugin.tests.proxy;
|
||||
//
|
||||
//import org.bukkit.Material;
|
||||
//import org.bukkit.inventory.ItemStack;
|
||||
//import org.bukkit.inventory.meta.ItemMeta;
|
||||
//
|
||||
//import de.tr7zw.changeme.nbtapi.NBT;
|
||||
//import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
//import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
//import de.tr7zw.changeme.nbtapi.data.proxy.NBTItemMeta;
|
||||
//import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
//import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
//import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
//
|
||||
//public class DataItemProxyTest implements Test {
|
||||
//
|
||||
// @Override
|
||||
// public void test() throws Exception {
|
||||
// if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
// return; // skip
|
||||
// }
|
||||
// ItemStack item = new ItemStack(Material.STONE);
|
||||
// ItemMeta meta = item.getItemMeta();
|
||||
// meta.setDisplayName("Test");
|
||||
// item.setItemMeta(meta);
|
||||
// NBT.modify(item, NBTItemMeta.class, nmeta -> {
|
||||
// nmeta.setCustomModelData(123);
|
||||
// nmeta.setUnbreakable(true);
|
||||
// if (!nmeta.getDisplayData().getRawName().contains("Test")) {
|
||||
// throw new NbtApiException("Raw name didn't containg the expected String: " + nmeta);
|
||||
// }
|
||||
// ReadWriteNBT container = NBT.createNBTObject();
|
||||
// container.setString("foo", "bar");
|
||||
// nmeta.setBlockStateTag(container);
|
||||
// if (!container.equals(nmeta.getBlockStateTag())) {
|
||||
// throw new NbtApiException("BlockStateTag did not match! " + nmeta);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
// meta = item.getItemMeta();
|
||||
// if (!meta.hasCustomModelData() || meta.getCustomModelData() != 123) {
|
||||
// throw new NbtApiException("Custom Model Data did not match! " + new NBTItem(item));
|
||||
// }
|
||||
// if (!meta.isUnbreakable()) {
|
||||
// throw new NbtApiException("Unbreakable did not set!");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.proxy;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.handler.NBTHandlers;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.ProxyList;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class SimpleProxyTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ItemStack item = new ItemStack(Material.STONE);
|
||||
NBT.modify(item, TestInterface.class, ti -> {
|
||||
if (ti.hasKills()) {
|
||||
throw new NbtApiException("Item reported to have kills before setting data!");
|
||||
}
|
||||
ti.setKills(42);
|
||||
ti.addKill();
|
||||
if (!ti.hasKills()) {
|
||||
throw new NbtApiException("Item reported to not have kills after setting data!");
|
||||
}
|
||||
if (!"{Kills:43}".equals(ti.toString())) {
|
||||
throw new NbtApiException("ToString returned the wrong string. " + ti.toString());
|
||||
}
|
||||
});
|
||||
if (new NBTItem(item).getInteger("Kills") != 43) {
|
||||
throw new NbtApiException("The item was not modified correctly by the proxy!");
|
||||
}
|
||||
NBT.modify(item, TestInterface.class, ti -> {
|
||||
if (ti.theKillsWithADifferentMethodNameAndNoGet() != 43) {
|
||||
throw new NbtApiException("The annotation didn't work correctly!");
|
||||
}
|
||||
Statistic jumps = ti.getJumps();
|
||||
jumps.setPoints(9000);
|
||||
jumps.addPoint();
|
||||
if (ti.getJumps().getPoints() != 9001) {
|
||||
throw new NbtApiException("The stacked proxy didn't work correctly!");
|
||||
}
|
||||
ItemStack stack = new ItemStack(Material.STONE, 42);
|
||||
ti.setItem(stack);
|
||||
if (!stack.equals(ti.getItem())) {
|
||||
throw new NbtApiException("The handler in the proxy didn't work correctly!");
|
||||
}
|
||||
});
|
||||
NBT.modify(item, TestInterface.class, ti -> {
|
||||
Statistic a = ti.getStatistics().addCompound();
|
||||
a.setPoints(1);
|
||||
a.addPoint();
|
||||
a.addPoint();
|
||||
ti.getStatistics().addCompound();
|
||||
if (ti.getStatistics().size() != 2) {
|
||||
throw new NbtApiException("List size not as expected!");
|
||||
}
|
||||
ti.getStatistics().remove(1);
|
||||
if (ti.getStatistics().size() != 1) {
|
||||
throw new NbtApiException("List size not as expected!");
|
||||
}
|
||||
if (ti.getStatistics().iterator().next().getPoints() != 3) {
|
||||
throw new NbtApiException("Points not as expected!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface TestInterface extends NBTProxy {
|
||||
|
||||
@Override
|
||||
default void init() {
|
||||
registerHandler(ItemStack.class, NBTHandlers.ITEM_STACK);
|
||||
}
|
||||
|
||||
public boolean hasKills();
|
||||
|
||||
public void setKills(int amount);
|
||||
|
||||
public int getKills();
|
||||
|
||||
public default void addKill() {
|
||||
setKills(getKills() + 1);
|
||||
}
|
||||
|
||||
@NBTTarget(value = "Kills", type = Type.GET)
|
||||
public int theKillsWithADifferentMethodNameAndNoGet();
|
||||
|
||||
public Statistic getJumps();
|
||||
|
||||
public ItemStack getItem();
|
||||
|
||||
public void setItem(ItemStack item);
|
||||
|
||||
public ProxyList<Statistic> getStatistics();
|
||||
|
||||
}
|
||||
|
||||
public interface Statistic extends NBTProxy {
|
||||
public void setPoints(int amount);
|
||||
|
||||
public int getPoints();
|
||||
|
||||
public default void addPoint() {
|
||||
setPoints(getPoints() + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,12 +14,15 @@ public class TileTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (MinecraftVersion.isFoliaPresent()) {
|
||||
return;
|
||||
}
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
Block block = world.getBlockAt(world.getSpawnLocation().getBlockX(), 254,
|
||||
world.getSpawnLocation().getBlockZ());
|
||||
if (block.getType() == Material.AIR) {
|
||||
if (block.getChunk().isLoaded() && block.getType() == Material.AIR) {
|
||||
block.setType(Material.CHEST);
|
||||
NBTTileEntity tile = new NBTTileEntity(block.getState());
|
||||
if (!MinecraftVersion.isNewerThan(MinecraftVersion.MC1_17_R1)) {
|
||||
|
||||
+3
@@ -17,6 +17,9 @@ public class TilesCustomNBTPersistentTest implements Test {
|
||||
public void test() throws Exception {
|
||||
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_14_R1.getVersionId())
|
||||
return;
|
||||
if (MinecraftVersion.isFoliaPresent()) {
|
||||
return;
|
||||
}
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
|
||||
Whitespace-only changes.
@@ -3,4 +3,5 @@ name: NBTAPI
|
||||
version: ${project.version}
|
||||
author: tr7zw
|
||||
loadbefore: [ItemNBTAPI]
|
||||
api-version: 1.13
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.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.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -3,13 +3,13 @@ package dev.tr7zw.mappingsparser;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
@@ -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.20.txt");
|
||||
File input = new File("minecraft_server.1.21.txt");
|
||||
List<String> lines = Files.readAllLines(input.toPath());
|
||||
|
||||
Map<String, ClassWrapper> classes = new HashMap<>();
|
||||
@@ -40,6 +40,9 @@ public class MappingsParser {
|
||||
ClassWrapper nmsWrapper = null;
|
||||
Map<ReflectionMethod, String> mappedNames = null;
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (!line.startsWith(" ")) {
|
||||
if (nmsWrapper != null && mappedNames != null) {
|
||||
proccessMapping(nmsWrapper, mappedNames);
|
||||
@@ -59,13 +62,15 @@ public class MappingsParser {
|
||||
mojangName = line.trim().split(" ")[1];
|
||||
unmappedName = line.trim().split(" ")[3];
|
||||
classes.remove(currentClass);
|
||||
for (ReflectionMethod method : mappedNames.keySet()) {
|
||||
if (method.getSelectedVersionInfo() == null)
|
||||
continue;
|
||||
if (!mojangName.contains("("))
|
||||
continue;
|
||||
if (mojangName.equals(method.getSelectedVersionInfo().name)) {
|
||||
mappedNames.put(method, unmappedName);
|
||||
if (mappedNames != null) {
|
||||
for (ReflectionMethod method : mappedNames.keySet()) {
|
||||
if (method.getSelectedVersionInfo() == null)
|
||||
continue;
|
||||
if (!mojangName.contains("("))
|
||||
continue;
|
||||
if (mojangName.equals(method.getSelectedVersionInfo().name)) {
|
||||
mappedNames.put(method, unmappedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println(currentClass + ": " + mojangName + " -> " + unmappedName);
|
||||
@@ -79,6 +84,8 @@ public class MappingsParser {
|
||||
System.out.println(
|
||||
"Missing mapping in class " + nmsWrapper.getMojangName() + " method " + entry.getKey().name());
|
||||
} else {
|
||||
if(!entry.getValue().equals(MojangToMapping.getMapping().get(nmsWrapper.getMojangName() + "#"
|
||||
+ entry.getKey().getSelectedVersionInfo().name)))
|
||||
builder.append("put(\"" + nmsWrapper.getMojangName() + "#"
|
||||
+ entry.getKey().getSelectedVersionInfo().name + "\", \"" + entry.getValue() + "\");\n");
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nbt-data-api</artifactId>
|
||||
@@ -14,7 +14,7 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -37,20 +37,23 @@ public class WorldData {
|
||||
}
|
||||
|
||||
public String getWorldName() {
|
||||
return file.getCompound("Data").getString("LevelName");
|
||||
return file.resolveOrNull("Data.LevelName", String.class);
|
||||
}
|
||||
|
||||
public void setWorldName(String name) {
|
||||
file.getCompound("Data").setString("LevelName", name);
|
||||
file.getOrCreateCompound("Data").setString("LevelName", name);
|
||||
}
|
||||
|
||||
public Vector getSpawnPosition() {
|
||||
NBTCompound data = file.getCompound("Data");
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return new Vector(data.getInteger("SpawnX"), data.getInteger("SpawnY"), data.getInteger("SpawnZ"));
|
||||
}
|
||||
|
||||
public void setSpawnPosition(Vector vec) {
|
||||
NBTCompound data = file.getCompound("Data");
|
||||
NBTCompound data = file.getOrCreateCompound("Data");
|
||||
data.setInteger("SpawnX", vec.getBlockX());
|
||||
data.setInteger("SpawnY", vec.getBlockY());
|
||||
data.setInteger("SpawnZ", vec.getBlockZ());
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.tr7zw.changeme.nbtapi.data.proxy;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.handler.NBTHandlers;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTProxy;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget;
|
||||
import de.tr7zw.changeme.nbtapi.wrapper.NBTTarget.Type;
|
||||
|
||||
public interface NBTItemMeta extends NBTProxy {
|
||||
|
||||
@Override
|
||||
default void init() {
|
||||
registerHandler(ReadableNBT.class, NBTHandlers.STORE_READABLE_TAG);
|
||||
registerHandler(ReadWriteNBT.class, NBTHandlers.STORE_READWRITE_TAG);
|
||||
}
|
||||
|
||||
public int getCustomModelData();
|
||||
|
||||
public void setCustomModelData(int customModelData);
|
||||
|
||||
@NBTTarget(type = Type.GET, value = "Unbreakable")
|
||||
public boolean isUnbreakable();
|
||||
|
||||
public void setUnbreakable(boolean unbreakable);
|
||||
|
||||
public ReadWriteNBT getBlockStateTag();
|
||||
|
||||
public void setBlockStateTag(ReadableNBT blockState);
|
||||
|
||||
@NBTTarget(value = "display")
|
||||
public DisplayData getDisplayData();
|
||||
|
||||
public interface DisplayData extends NBTProxy {
|
||||
|
||||
@NBTTarget(value = "Name")
|
||||
public void setRawName(String rawName);
|
||||
|
||||
@NBTTarget(value = "Name")
|
||||
public String getRawName();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.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.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
<organization>
|
||||
@@ -19,8 +19,6 @@
|
||||
</licenses>
|
||||
<modules>
|
||||
<module>item-nbt-api</module>
|
||||
<module>nbt-injector</module>
|
||||
<module>nbt-data-api</module>
|
||||
<module>item-nbt-plugin</module>
|
||||
<module>mappings-parser</module>
|
||||
</modules>
|
||||
@@ -90,7 +88,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
@@ -99,7 +97,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
@@ -112,7 +110,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
</configuration>
|
||||
@@ -138,29 +136,29 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>allcontributors</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>update-contributors</id>
|
||||
<goals>
|
||||
<goal>update</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<version>2.4.0</version>
|
||||
<configuration>
|
||||
<licenseName>mit</licenseName>
|
||||
<licenseFile>LICENSE</licenseFile>
|
||||
<force>true</force>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>dev.tr7zw.nojitpack.maven</groupId>
|
||||
<artifactId>no-jitpack-maven-plugin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>antijitpack</id>
|
||||
<goals>
|
||||
<goal>antijitpack</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
@@ -169,17 +167,31 @@
|
||||
<formats>
|
||||
<format>
|
||||
<includes>
|
||||
<include>*.java</include>
|
||||
<include>.gitignore</include>
|
||||
</includes>
|
||||
<trimTrailingWhitespace></trimTrailingWhitespace>
|
||||
<endWithNewline></endWithNewline>
|
||||
</format>
|
||||
</formats>
|
||||
<markdown>
|
||||
<includes> <!-- You have to set the target manually -->
|
||||
<include>**/*.md</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>target/**</exclude>
|
||||
</excludes>
|
||||
|
||||
<flexmark/> <!-- has its own section below -->
|
||||
</markdown>
|
||||
<!-- define a language-specific format -->
|
||||
<java>
|
||||
<!-- no need to specify files, inferred automatically, but you can if you want -->
|
||||
|
||||
<excludes>
|
||||
<exclude>Metrics.java</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>*.java</include>
|
||||
</includes>
|
||||
<eclipse>
|
||||
<file>formatter.xml</file>
|
||||
</eclipse>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[B][SIZE=6](Item/Entity/Tile) NBT API[/SIZE][/B]
|
||||
[URL='https://ci.codemc.io/job/Tr7zw/job/Item-NBT-API/'][IMG]https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API[/IMG][/URL] [URL='https://sonarcloud.io/dashboard?id=de.tr7zw%3Aitem-nbt-parent'][IMG]https://sonarcloud.io/api/project_badges/measure?project=de.tr7zw%3Aitem-nbt-parent&metric=ncloc[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic[/IMG] [/URL][URL='https://discordapp.com/invite/yk4caxM'][IMG]https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white[/IMG][/URL]
|
||||
[URL='https://ci.codemc.io/job/Tr7zw/job/Item-NBT-API/'][IMG]https://ci.codemc.org/buildStatus/icon?job=Tr7zw%2FItem-NBT-API[/IMG][/URL] [URL='https://sonarcloud.io/dashboard?id=tr7zw_Item-NBT-API'][IMG]https://sonarcloud.io/api/project_badges/measure?project=tr7zw_Item-NBT-API&metric=ncloc[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/servers/1058.svg?color=green&label=OnlineServers&style=plastic[/IMG][/URL] [URL='https://bstats.org/plugin/bukkit/ItemNBTAPI'][IMG]https://img.shields.io/bstats/players/1058.svg?color=green&label=OnlinePlayers&style=plastic[/IMG] [/URL][URL='https://discordapp.com/invite/yk4caxM'][IMG]https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white[/IMG][/URL]
|
||||
The NBT API allows you to add custom NBT tags to Itemstacks, TileEntities and Entities, or modify excisting ones!
|
||||
It completely uses reflections to interact with NMS code and works with all the latest versions. On server start the plugin checks all reflections.
|
||||
|
||||
@@ -112,7 +112,7 @@ Just use the Plugin as Maven dependency and remember to add "NBTAPI" as dependen
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api-plugin</artifactId>
|
||||
<version>2.12.0-SNAPSHOT</version>
|
||||
<version>2.13.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.12.0-SNAPSHOT</version>
|
||||
<version>2.13.1</version>
|
||||
</dependency>
|
||||
...
|
||||
<plugins>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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)
|
||||
|
||||
## Converting Minecraft Objects into NBT
|
||||
|
||||
```java
|
||||
// Items
|
||||
ReadWriteNBT nbt = NBT.itemStackToNBT(itemStack);
|
||||
ReadWriteNBT nbt = NBT.itemStackArrayToNBT(itemStacks);
|
||||
// Entity
|
||||
NBTEntity nbte = new NBTEntity(entity);
|
||||
// Tiles
|
||||
NBTTileEntity nbtt= new NBTTileEntity(block.getState());
|
||||
// Gameprofiles
|
||||
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
|
||||
// NBT Files
|
||||
NBTFile file = new NBTFile(testFile);
|
||||
```
|
||||
|
||||
## Converting NBT to a String and the String back to NBT
|
||||
|
||||
```java
|
||||
// to String(works with any NBT object)
|
||||
String str = nbt.toString();
|
||||
// String back to NBT
|
||||
ReadWriteNBT nbt = NBT.parseNBT(json);
|
||||
```
|
||||
|
||||
## Recreating the Minecraft Objects from NBT
|
||||
|
||||
```java
|
||||
// Items
|
||||
ItemStack itemStack = NBT.nbtToItemStack(nbt);
|
||||
ItemStack[] itemStacks = NBT.nbtToItemStackArray(nbt);
|
||||
// Entity/Tiles
|
||||
In general "new NBTEntity(entity).mergeCompound(nbt)", but you might want to remove some data from the nbt first like the Location, uuid and entityId
|
||||
// Gameprofile
|
||||
GameProfile profile = NBT.nbtToGameProfile(nbt);
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
## Set a skull's skin
|
||||
|
||||
#### We will be using [this head](https://minecraft-heads.com/custom-heads/head/28194-cup-of-soda) as example.
|
||||
|
||||
```java
|
||||
// This is the base64 texture value from the bottom of the previously mentioned website.
|
||||
final String textureValue = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQyY2M5MjAzYzkwYjg5YmRhYzFkZjI4NDE2NzI2NmI5NTNkZmViZjNjNDY5MGE3Y2QwYjE1NzkxYTYyZTU4MiJ9fX0=";
|
||||
|
||||
|
||||
// Creating ItemStack
|
||||
|
||||
// For Minecraft 1.12.2 and below
|
||||
final ItemStack item = new ItemStack(Material.SKULL_ITEM);
|
||||
item.setDurability((short) 3);
|
||||
|
||||
// For Minecraft 1.13 and newer
|
||||
final ItemStack item = new ItemStack(Material.PLAYER_HEAD);
|
||||
|
||||
|
||||
// Applying nbt
|
||||
|
||||
// For Minecraft 1.20.4 and below
|
||||
NBT.modify(item, nbt -> {
|
||||
ReadWriteNBT skullOwnerCompound = nbt.getOrCreateCompound("SkullOwner");
|
||||
|
||||
// The owner UUID. Note that skulls with the same UUID but different textures will misbehave and only one texture will load.
|
||||
// They will share the texture. To avoid this limitation, it is recommended to use a random UUID.
|
||||
skullOwnerCompound.setUUID("Id", UUID.randomUUID());
|
||||
|
||||
skullOwnerCompound.getOrCreateCompound("Properties")
|
||||
.getCompoundList("textures")
|
||||
.addCompound()
|
||||
.setString("Value", textureValue);
|
||||
});
|
||||
|
||||
// Workaround for Minecraft 1.20.5+
|
||||
NBT.modifyComponents(item, nbt -> {
|
||||
ReadWriteNBT profileNbt = nbt.getOrCreateCompound("minecraft:profile");
|
||||
profileNbt.setUUID("id", uuid);
|
||||
ReadWriteNBT propertiesNbt = profileNbt.getCompoundList("properties").addCompound();
|
||||
propertiesNbt.setString("name", "textures");
|
||||
propertiesNbt.setString("value", textureValue);
|
||||
});
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If you are using Paper API on 1.12.2+, you may use the following code to create textured skulls
|
||||
|
||||
```java
|
||||
SkullMeta meta = (SkullMeta) item.getItemMeta();
|
||||
PlayerProfile playerProfile = Bukkit.createProfile(uuid);
|
||||
playerProfile.setProperty(new ProfileProperty("textures", textureValue));
|
||||
meta.setPlayerProfile(playerProfile);
|
||||
// You can also use item.editMeta(SkullMeta.class, meta -> {}); on 1.17+
|
||||
item.setItemMeta(meta);
|
||||
```
|
||||
|
||||
## Zombie that can pick up loot and does 0.5 hearts of damage
|
||||
|
||||
This code should serve only as a reference, the nbt structure might change between versions.
|
||||
|
||||
```java
|
||||
Zombie zombie = location.getWorld().spawn(location, Zombie.class);
|
||||
|
||||
String attributeName = "minecraft:generic.attack_damage"; // Or generic.attackDamage prior to 1.16
|
||||
double damageValue = 0.5;
|
||||
|
||||
// Modify vanilla data
|
||||
NBT.modify(zombie, nbt -> {
|
||||
nbt.setBoolean("CanPickUpLoot", true);
|
||||
|
||||
ReadWriteNBTCompoundList list = nbt.getCompoundList("Attributes");
|
||||
|
||||
// Check if zombie already has attribute set. If so, modify it
|
||||
for (ReadWriteNBT listEntryNbt : list) {
|
||||
if (!listEntryNbt.getString("Name").equals(attributeName)) continue;
|
||||
|
||||
listEntryNbt.setDouble("Base", damageValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Attribute is missing, add it instead
|
||||
ReadWriteNBT listEntryNbt = list.addCompound();
|
||||
listEntryNbt.setString("Name", attributeName);
|
||||
listEntryNbt.setDouble("Base", damageValue);
|
||||
});
|
||||
|
||||
// Modify custom data
|
||||
NBT.modifyPersistentData(zombie, nbt -> {
|
||||
// Let's mark our zombie as a custom one
|
||||
nbt.setBoolean("custom_zombie", true);
|
||||
});
|
||||
```
|
||||
|
||||
## Reading world data
|
||||
|
||||
```java
|
||||
// Get main world's folder
|
||||
File worldDataFolder = Bukkit.getWorlds().getFirst().getWorldFolder();
|
||||
|
||||
// Read level data
|
||||
NBTFile levelNbtFile = new NBTFile(new File(worldDataFolder, "level.dat"));
|
||||
|
||||
// Obtain world name
|
||||
String worldName = levelNbtFile.resolveOrNull("Data.LevelName", String.class);
|
||||
|
||||
// Read some player's data
|
||||
UUID playerUuid;
|
||||
File playerFile = new File(worldDataFolder, "playerdata/" + playerUuid + ".dat");
|
||||
if (!playerFile.exists()) {
|
||||
// No offline player data for provided uuid
|
||||
return;
|
||||
}
|
||||
|
||||
NBTFile playerNbtFile = new NBTFile(playerFile);
|
||||
|
||||
// Change player's health
|
||||
float health = playerNbtFile.getFloat("Health");
|
||||
playerNbtFile.setFloat("Health", health + 5);
|
||||
```
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
## Frequently Asked Questions
|
||||
|
||||
### I've updated NBT-API, but it keeps saying that it's outdated
|
||||
|
||||
Some other plugin on your server has a shaded version of NBT-API. Try looking at the logs to find out which plugin is it, or add/remove your plugins until you find a culprit.
|
||||
|
||||
### I've installed NBT-API, but the plugin keeps asking for ItemNBTAPI
|
||||
|
||||
The plugin uses a very outdated version of NBT-API. In this case, download the version 1.8.3 from the versions tab. The outdated "ItemNBTAPI" and "NBTAPI" can be used both at the same time.
|
||||
|
||||
### 1.7 support
|
||||
|
||||
- Use 1.7.10.
|
||||
- NBTLists may not work.
|
||||
- NBTTypes don't work as 1.7.x is missing this feature.
|
||||
- TLDR: 1.7.10 is a bit broken and not everything will work! Also, it's not supported anymore!
|
||||
|
||||
### Where did NBTInjector go?
|
||||
|
||||
The experimental NBTInjector became unsupported since Minecraft 1.14 and was removed in 2.13.0 with Minecraft 1.21 release.
|
||||
|
||||
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).
|
||||
+9
-14
@@ -1,22 +1,17 @@
|
||||
# Welcome to the NBT API wiki
|
||||
# Welcome to the NBT-API wiki
|
||||
|
||||
NBT API has been tested on Spigot
|
||||
1.7.10-1.19+
|
||||
NBT-API supports Spigot and Paper versions starting at 1.8. It runs on 1.7.10 too, but some features might not work (see [1.7 support](https://github.com/tr7zw/Item-NBT-API/wiki/FAQ#17-support)).
|
||||
|
||||
Full JavaDoc can be found [here](https://tr7zw.github.io/Item-NBT-API/v2-api/)!
|
||||
Feel free to seek for support in [](https://discordapp.com/invite/yk4caxM).
|
||||
|
||||
### What do I have to do as a server owner?
|
||||
Just download the jar and drop it in the plugins folder. Done.
|
||||
Note that outdated plugins may ask for "ItemNBTAPI". In this case, download version 1.8.3 from the versions tab. The outdated "ItemNBTAPI" and "NBTAPI" can be used at the same time.
|
||||
|
||||
### 1.7 Notes
|
||||
* Use 1.7.10
|
||||
* NBTLists may not work
|
||||
* NBTTypes don't work as 1.7.x is missing this feature.
|
||||
* TLDR: 1.7.10 is a bit broken and not everything will work! Also it's not supported anymore!
|
||||
Just download the jar and drop it in the plugins folder. Done!
|
||||
|
||||
### Don't reload the NBT-Injector
|
||||
Reloading in general is a horrible thing and it will break the NBTInjector in horrible ways! When updating plugins/changing configs always restart the server normally!
|
||||
You might also want to check out some [plugins that use NBT-API](https://github.com/tr7zw/Item-NBT-API/wiki/Plugins).
|
||||
|
||||
### How can I use the API as a developer?
|
||||
See [Using the NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API)
|
||||
|
||||
Import the API using [Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven) or [Gradle](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Gradle), then see the [basic usage](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API) or code examples like [working with Skulls](https://github.com/tr7zw/Item-NBT-API/wiki/Example-Usages#set-a-skulls-skin) to familiarize yourself with the API.
|
||||
|
||||
Full Javadoc can be found [here](https://tr7zw.github.io/Item-NBT-API/v2-api/)!
|
||||
@@ -1,22 +0,0 @@
|
||||
# Merging Compounds
|
||||
You can "merge" the data from any given Compound onto some other Compound. This will overwrite tags with the same name.
|
||||
|
||||
Example:
|
||||
|
||||
`Tag1: {Num1:1, Num2:2} Tag2: {Num1:7, Num3:3}`
|
||||
|
||||
After merging Tag2 on Tag1:
|
||||
|
||||
```java
|
||||
tag1.mergeCompound(tag2);
|
||||
```
|
||||
|
||||
`Tag1: {Num1:7, Num2:2, Num3:3} Tag2: {Num1:7, Num3:3}`
|
||||
|
||||
## Simulate the "/data merge" command
|
||||
|
||||
```java
|
||||
NBT.modify(zombie, nbt -> {
|
||||
nbt.mergeCompound(NBT.parseNBT("{Silent:1b,Invulnerable:1b,Glowing:1b,IsBaby:1b}"));
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
## List of some plugins on GitHub that utilize NBT-API
|
||||
|
||||
- [IllegalStack](https://github.com/dniym/IllegalStack)
|
||||
- [Prison](https://github.com/PrisonTeam/Prison)
|
||||
- [CommandAPI](https://github.com/JorelAli/CommandAPI)
|
||||
- [SkBee](https://github.com/ShaneBeee/SkBee/)
|
||||
- [Monumenta](https://github.com/TeamMonumenta/monumenta-plugins-public/)
|
||||
|
||||
___
|
||||
|
||||
Want your plugin added to this list? Submit a PR editing this file located in `~/wiki/Plugins.md`!
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
# This is an old example and needs to be updated! Proceed with caution!
|
||||
|
||||
We'll be using [this head](https://minecraft-heads.com/custom-heads/decoration/32112-dragonsbreath-opal "Dragonsbreath Opal") for this demonstration.
|
||||
```java
|
||||
String textureValue = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWY1MjQxNjZmN2NlODhhNTM3MTU4NzY2YTFjNTExZTMyMmE5M2E1ZTExZGJmMzBmYTZlODVlNzhkYTg2MWQ4In19fQ=="; // Pulled from the head link, scroll to the bottom and the "Other Value" field has this texture id.
|
||||
|
||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1); // Creating the ItemStack, your input may vary.
|
||||
NBTItem nbti = new NBTItem(head); // Creating the wrapper.
|
||||
|
||||
NBTCompound disp = nbti.addCompound("display");
|
||||
disp.setString("Name", "Testitem"); // Setting the name of the Item
|
||||
|
||||
NBTList l = disp.getStringList("Lore");
|
||||
l.add("Some lore"); // Adding a bit of lore.
|
||||
|
||||
NBTCompound skull = nbti.addCompound("SkullOwner"); // Getting the compound, that way we can set the skin information
|
||||
skull.setString("Name", "Dragonsbreath Opal"); // Owner's name
|
||||
skull.setString("Id", "fce0323d-7f50-4317-9720-5f6b14cf78ea");
|
||||
// The UUID, note that skulls with the same UUID but different textures will misbehave and only one texture will load
|
||||
// (They'll share it), if skulls have different UUIDs and same textures they won't stack. See UUID.randomUUID();
|
||||
|
||||
NBTListCompound texture = skull.addCompound("Properties").getCompoundList("textures").addCompound();
|
||||
texture.setString("Value", textureValue);
|
||||
|
||||
head = nbti.getItem(); // Refresh the ItemStack
|
||||
```
|
||||
+24
-7
@@ -1,10 +1,17 @@
|
||||
To start using NB-API, you either need to depend on its plugin version, or shade (include) it inside your plugin.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
|
||||
|
||||
# Option 1) NBT-API as a dependency
|
||||
Add the following Entries to your Gradle build at the correct location:
|
||||
|
||||
Add the following entries to your Gradle build at the correct locations:
|
||||
|
||||
```groovy
|
||||
compileOnly("de.tr7zw:item-nbt-api-plugin:VERSION")
|
||||
```
|
||||
(Get the current Version from [here](https://www.spigotmc.org/resources/nbt-api.7939/))
|
||||
|
||||
(Get the current ``VERSION`` from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
@@ -17,15 +24,18 @@ repositories {
|
||||
}
|
||||
```
|
||||
|
||||
Finally, add the API as dependency to your ``plugin.yml``
|
||||
Add the API as dependency to your ``plugin.yml``:
|
||||
|
||||
```yml
|
||||
depend: [NBTAPI]
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin
|
||||
|
||||
To include NBT-API directly in your plugin, you can use the gradle shadow plugin.
|
||||
|
||||
Add the plugin to the build configuration, as shown here:
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow") version "VERSION"
|
||||
@@ -33,13 +43,17 @@ plugins {
|
||||
```
|
||||
|
||||
The latest version of the shadow plugin can be found [here](https://github.com/johnrengelman/shadow/releases).
|
||||
<br/>
|
||||
|
||||
Add NBT-API to your dependencies:
|
||||
|
||||
```groovy
|
||||
implementation("de.tr7zw:item-nbt-api:VERSION")
|
||||
```
|
||||
(Get the current Version from [here](https://www.spigotmc.org/resources/nbt-api.7939/))
|
||||
|
||||
(Get the current ``VERSION`` from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure you're using ``item-nbt-api`` as ``artifactId``, never shade the ``-plugin`` artifact!
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
@@ -52,16 +66,19 @@ repositories {
|
||||
}
|
||||
```
|
||||
|
||||
After this you can add the shadowjar configuration to relocate the api package:
|
||||
After this you can add the shadowJar configuration to relocate the API package:
|
||||
|
||||
```groovy
|
||||
shadowJar {
|
||||
relocate("de.tr7zw.changeme.nbtapi", "YOUR PACKAGE WHERE THE API SHOULD END UP")
|
||||
}
|
||||
```
|
||||
|
||||
If you want to run the shadowJar task when the build task is executed, you can write like this:
|
||||
If you want to run the shadowJar task when the build task is executed, you can use this:
|
||||
|
||||
```groovy
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
```
|
||||
|
||||
+70
-35
@@ -1,6 +1,13 @@
|
||||
To start using NB-API, you either need to depend on its plugin version, or shade (include) it inside your plugin.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
|
||||
|
||||
# Option 1) NBT-API as a dependency
|
||||
Add the following Entries to your pom at the correct location:
|
||||
```
|
||||
|
||||
Add the following entries to your pom at the correct locations:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api-plugin</artifactId>
|
||||
@@ -8,8 +15,10 @@ Add the following Entries to your pom at the correct location:
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
(Get the current Version from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
```
|
||||
|
||||
(Get the current ``VERSION`` from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
|
||||
```xml
|
||||
<repositories>
|
||||
...
|
||||
<!-- CodeMC -->
|
||||
@@ -21,22 +30,73 @@ Add the following Entries to your pom at the correct location:
|
||||
...
|
||||
</repositories>
|
||||
```
|
||||
Add the API as dependency to your plugin.yml
|
||||
```
|
||||
|
||||
Add the API as dependency to your ``plugin.yml``:
|
||||
|
||||
```yaml
|
||||
depend: [NBTAPI]
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin
|
||||
Add the following Entries to your pom at the correct location:
|
||||
|
||||
To include NBT-API directly in your plugin, you can use the maven shade plugin.
|
||||
|
||||
Add the plugin to the build configuration, as shown here:
|
||||
|
||||
```xml
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>YOUR PACKAGE WHERE THE API SHOULD END UP</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
```
|
||||
|
||||
The latest version of the shade plugin can be found [here](https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-shade-plugin).
|
||||
|
||||
Replace ``YOUR PACKAGE WHERE THE API SHOULD END UP`` with your own unique package. For example:
|
||||
|
||||
```xml
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>com.yourname.pluginname.nbtapi</shadedPattern>
|
||||
</relocation>
|
||||
```
|
||||
|
||||
Then, add NBT-API to your dependencies by including the following entries to your pom at the correct locations:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
(Get the current Version from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
```
|
||||
|
||||
(Get the current ``VERSION`` from [here](https://modrinth.com/plugin/nbtapi/versions))
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure you're using ``item-nbt-api`` as ``artifactId``, never shade the ``-plugin`` artifact!
|
||||
|
||||
```xml
|
||||
<repositories>
|
||||
...
|
||||
<!-- CodeMC -->
|
||||
@@ -48,29 +108,4 @@ Add the following Entries to your pom at the correct location:
|
||||
...
|
||||
</repositories>
|
||||
```
|
||||
```
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>YOUR PACKAGE WHERE THE API SHOULD END UP</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
```
|
||||
|
||||
+396
-60
@@ -1,87 +1,423 @@
|
||||
## General usage
|
||||
|
||||
## Using the ``NBT`` class (prefered)
|
||||
### 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!
|
||||
|
||||
#### Basic getting and setting of data
|
||||
|
||||
[ReadableNBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/iface/ReadableNBT.html) and [ReadWriteNBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/iface/ReadWriteNBT.html) are used to read/write nbt:
|
||||
|
||||
```java
|
||||
// Setting data on ItemStacks/Entities/BlockEntities
|
||||
// Creating a new empty NBT tag
|
||||
ReadWriteNBT nbt = NBT.createNBTObject();
|
||||
|
||||
// Setting nbt
|
||||
nbt.setString("Stringtest", "Teststring");
|
||||
nbt.setInteger("Inttest", 42);
|
||||
nbt.setDouble("Doubletest", 1.5);
|
||||
nbt.setBoolean("Booleantest", true);
|
||||
// More are available! Ask your IDE, or see Javadoc for suggestions!
|
||||
|
||||
// Getting nbt
|
||||
String s1 = nbt.getString("Stringtest");
|
||||
int i1 = nbt.getInteger("Inttest");
|
||||
double d = nbt.getDouble("Doubletest");
|
||||
boolean b = nbt.getBoolean("Booleantest");
|
||||
// Or, alternatively
|
||||
String s2 = nbt.getOrDefault("Stringtest", "fallback_value");
|
||||
Integer i2 = nbt.getOrNull("Inttest", Integer.class);
|
||||
|
||||
// Keys manipulation
|
||||
nbt.getKeys(); // Get all Tags
|
||||
nbt.hasTag("key"); // Check whether the Tag exists
|
||||
nbt.hasTag("key", NbtType.NBTTagString); // Check whether the Tag exists and matches the type
|
||||
nbt.removeKey("key"); // Remove the Tag
|
||||
NBTType nbtType = nbt.getType("key"); // Get the type of the Tag
|
||||
|
||||
// Subtag compounds
|
||||
nbt.getCompound("subtag"); // Get a subtag, or null
|
||||
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
|
||||
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();
|
||||
// Turn back into nbt again
|
||||
ReadWriteNBT nbt2 = NBT.parseNBT(json);
|
||||
```
|
||||
|
||||
#### Merging Compounds
|
||||
|
||||
You can "merge" the data from any given Compound onto some other Compound. This will overwrite the tags with the same name.
|
||||
|
||||
Example:
|
||||
|
||||
`Tag1: {Num1:1, Num2:2} Tag2: {Num1:7, Num3:3}`
|
||||
|
||||
After merging Tag2 on Tag1:
|
||||
|
||||
```java
|
||||
tag1.mergeCompound(tag2);
|
||||
```
|
||||
|
||||
`Tag1: {Num1:7, Num2:2, Num3:3} Tag2: {Num1:7, Num3:3}`
|
||||
|
||||
#### Resolving data
|
||||
|
||||
To simplify working with deeply nested NBT, resolve methods allow directly getting or working with these nested tags.
|
||||
|
||||
Compounds are separated by dots ``.`` In case you need a ``.`` inside a key, it can be escaped with a backslash ``\``.
|
||||
|
||||
```java
|
||||
// For example, the following code:
|
||||
nbt.resolveOrCreateCompound("foo.bar.baz");
|
||||
// Will get/create the same subtag compound as:
|
||||
nbt.getOrCreateCompound("foo").getOrCreateCompound("bar").getOrCreateCompound("baz");
|
||||
|
||||
// Get compound if exists, or null otherwise
|
||||
nbt.resolveCompound("foo.some.key.baz");
|
||||
|
||||
// Sets foo/bar/baz/test to 42
|
||||
nbt.resolveOrCreateCompound("foo.bar.baz").setInteger("test", 42);
|
||||
|
||||
// Example of a key with a . in it. Sets the key foo/some.key/baz/other to 123
|
||||
nbt.resolveOrCreateCompound("foo.some\\.key.baz").setInteger("other", 123);
|
||||
|
||||
// Similarly, you may also fetch values from nested compounds
|
||||
String s = nbt.resolveOrDefault("foo.bar.Stringtest", "fallback_value");
|
||||
Integer i = nbt.resolveOrNull("foo\\.bar.baz.Inttest", Integer.class);
|
||||
```
|
||||
|
||||
#### Accessing/creating lists and their data
|
||||
|
||||
> [!NOTE]
|
||||
> The lists will be created if they do not exist, there are no setters for lists!
|
||||
|
||||
```java
|
||||
// Get or create a string list
|
||||
ReadWriteNBTList<String> stringList = nbt.getStringList("list_key");
|
||||
stringList.add("value");
|
||||
|
||||
// Get the list type, NBTTagString in this case
|
||||
NBTType type = nbt.getListType("list_key");
|
||||
|
||||
// You can obtain the value back just like with normal Lists
|
||||
nbt.getStringList("list_key").get(0);
|
||||
// Or by fetching it
|
||||
nbt.resolveOrNull("list_key[0]", String.class); // Get the first value in list
|
||||
// You can also use negative number to get the value from the end of the list/array
|
||||
nbt.resolveOrDefault("list_key[-1]", "fallback_value"); // Get the last value in list, or use the default
|
||||
|
||||
|
||||
// NBT compound lists
|
||||
ReadWriteNBTCompoundList nbtList = nbt.getOrCreateCompound("foo").getCompoundList("other_key");
|
||||
ReadWriteNBT nbtListEntry = addCompound();
|
||||
nbtListEntry.setBoolean("bar", true);
|
||||
|
||||
// You can fetch compounds in lists
|
||||
nbt.resolveCompound("foo.other_key[0]"); // Get the first compound in list, or null
|
||||
nbt.resolveOrCreateCompound("foo.other_key[1]"); // Get the second compound in list, or create it
|
||||
// Or fetch data from them
|
||||
boolean bar = nbt.resolveOrDefault("foo.other_key[0].bar", false);
|
||||
```
|
||||
|
||||
### Working with items
|
||||
|
||||
```java
|
||||
// Setting data
|
||||
NBT.modify(itemStack, nbt -> {
|
||||
nbt.setString("Stringtest", "Teststring");
|
||||
nbt.setInteger("Inttest", 42);
|
||||
nbt.setDouble("Doubletest", 1.5d);
|
||||
nbt.setBoolean("Booleantest", true);
|
||||
//More are available! Ask your IDE/Javadocs for suggestions!
|
||||
// More are available! Ask your IDE, or see Javadoc for suggestions!
|
||||
});
|
||||
// Getting data from ItemStacks
|
||||
String string = NBT.get(itemStack, nbt -> nbt.getString("Stringtest"));
|
||||
|
||||
// Reading data
|
||||
NBT.get(itemStack, nbt -> {
|
||||
String stringTest = nbt.getString("Stringtest");
|
||||
int intTest = nbt.getOrDefault("Inttest", 0);
|
||||
// Do more stuff
|
||||
});
|
||||
|
||||
// Getting data
|
||||
String string = NBT.get(itemStack, nbt -> (String) nbt.getString("Stringtest"));
|
||||
|
||||
// Modifying and getting data
|
||||
int someValue = NBT.modify(itemStack, nbt -> {
|
||||
int i = nbt.getOrDefault("key", 0) + 1;
|
||||
nbt.setInteger(i);
|
||||
return i;
|
||||
});
|
||||
|
||||
// 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((meta, readOnlyNbt) -> { // Do not modify the nbt while modifying the meta!
|
||||
meta.setDisplayName("Kills: " + readOnlyNbt.getOrDefault("kills", 0));
|
||||
});
|
||||
//Do more stuff
|
||||
// Do more stuff
|
||||
});
|
||||
```
|
||||
|
||||
// Gameprofile to NBT
|
||||
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
|
||||
// NBT to Gameprofile
|
||||
GameProfile profile = NBT.nbtToGameProfile(nbt);
|
||||
// ItemStack to NBT
|
||||
###### Changing vanilla item nbt on 1.20.5+
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Since Minecraft 1.20.5 ItemStacks no longer have vanilla nbt during runtime.
|
||||
> Any calls like ``NBT.get`` or ``NBT.modify`` will access only the item's ``custom_data`` component.
|
||||
>
|
||||
> As a workaround, you may use the following code:
|
||||
|
||||
```java
|
||||
// NOTE: This code is only for 1.20.5+!
|
||||
NBT.modifyComponents(item, nbt -> {
|
||||
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
|
||||
});
|
||||
```
|
||||
|
||||
### Working with (block-)entities
|
||||
|
||||
Working with entities and block entities is similar to working items.
|
||||
|
||||
#### Accessing vanilla nbt
|
||||
|
||||
The example code for entities is applicable for block entities too.
|
||||
|
||||
```java
|
||||
// Obtain data
|
||||
boolean silent = NBT.get(entity, nbt -> (boolean) nbt.getBoolean("Silent"));
|
||||
// Modify data
|
||||
NBT.modify(entity, nbt -> {
|
||||
nbt.setBoolean("Silent", true);
|
||||
nbt.setByte("CanPickUpLoot", (byte) 1);
|
||||
});
|
||||
```
|
||||
|
||||
#### Accessing custom data
|
||||
|
||||
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.
|
||||
>
|
||||
> For example, you may not be able to add data to a chest in `BlockPlaceEvent` because the chest hasn't been placed yet. In such case, you can delay your actions by one tick or set the block to chest manually.
|
||||
|
||||
```java
|
||||
// Obtain data
|
||||
boolean test = NBT.getPersistentData(entity, nbt -> (boolean) nbt.getBoolean("custom_key"));
|
||||
// Modify data
|
||||
NBT.modifyPersistentData(entity, nbt -> {
|
||||
nbt.setBoolean("custom_key", true);
|
||||
nbt.setByte("custom_byte", (byte) 1);
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you plan to store some data for players, you might also consider using an external storage instead to not clutter the players' data files inside the world folder. Since any data written to the persistent storage is there forever, you can leave redundant data behind when your plugin is removed, but it's perfectly fine to store data in there otherwise.
|
||||
>
|
||||
> Storage solutions like per-player [NBT files](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#nbt-files) might be more than enough (and is basically what vanilla does, but you'll have your own file instead of using the one inside the world). Cache the file's data on join and save on quit/server shutdown, optionally adding auto saving.
|
||||
|
||||
#### Simulate the "/data merge" command
|
||||
|
||||
Applicable for items/block entities/entities/etc.
|
||||
|
||||
```java
|
||||
NBT.modify(zombie, nbt -> {
|
||||
nbt.mergeCompound(NBT.parseNBT("{Silent:1b,Invulnerable:1b,Glowing:1b,IsBaby:1b}"));
|
||||
});
|
||||
```
|
||||
|
||||
### Working with blocks
|
||||
|
||||
You can store data in block entities (block entities like chest, furnace, etc.) using the examples from the section above, but normal blocks do not have the nbt data.
|
||||
|
||||
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``:
|
||||
|
||||
```java
|
||||
ReadWriteNBT nbt = new NBTChunk(chunk).getPersistentDataContainer();
|
||||
```
|
||||
|
||||
Similarly, there is ``NBTBlock``, which allows you to store block data inside chunk's data.
|
||||
|
||||
```java
|
||||
// Block's data will be stored in Chunk's data in "blocks.x_y_z" subtag
|
||||
ReadWriteNBT nbt = new NBTBlock(block).getData();
|
||||
```
|
||||
|
||||
**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!
|
||||
|
||||
Moreover, since the data is stored inside a Chunk, this will increase the chunk's size on the disk.
|
||||
|
||||
## Extras
|
||||
|
||||
> [!TIP]
|
||||
> Besides this page, you may also take a look at some code examples at [example usages](https://github.com/tr7zw/Item-NBT-API/wiki/Example-Usages).
|
||||
|
||||
### 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();
|
||||
|
||||
// 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);
|
||||
```
|
||||
|
||||
### 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)
|
||||
|
||||
#### Items
|
||||
|
||||
While ``NBT.get/modify`` allows you to modify item's direct nbt (or only the ``custom_data`` container since 1.20.5), the ItemStack object nbt represents the item's serialized data. It includes item type, amount and all extra item tags.
|
||||
|
||||
For example, when ``NBT.get`` looks like this:
|
||||
|
||||
`{foo:"bar",points:12,test:1b}`
|
||||
|
||||
The ItemStack object nbt from ``NBT.itemStackToNBT`` may look like this:
|
||||
|
||||
`{components:{"minecraft:custom_data":{foo:"bar",points:12,test:1b}},count:2,id:"minecraft:stone"}`
|
||||
|
||||
Or like this in versions prior to 1.20.5:
|
||||
|
||||
`{Count:2b,id:"minecraft:stone",tag:{foo:"bar",points:12,test:1b}}`
|
||||
|
||||
###### Serializing/deserializing items
|
||||
|
||||
```java
|
||||
// Saving
|
||||
ReadWriteNBT nbt = NBT.itemStackToNBT(itemStack);
|
||||
// NBT to ItemStack
|
||||
ItemStack itemStack = NBT.nbtToItemStack(nbt);
|
||||
// ItemStack Array to NBT
|
||||
ReadWriteNBT nbt = NBT.itemStackArrayToNBT(itemStacks);
|
||||
// NBT to ItemStack Array
|
||||
ItemStack[] itemStacks = NBT.nbtToItemStackArray(nbt);
|
||||
// Creating a new NBT tag
|
||||
ReadWriteNBT nbt = NBT.createNBTObject();
|
||||
// Parsing a Mojang-Json String to NBT
|
||||
// Restoring
|
||||
ItemStack itemStack = NBT.itemStackFromNBT(nbt);
|
||||
ItemStack[] itemStacks = NBT.itemStackArrayFromNBT(nbt);
|
||||
// Reminder (NBT <-> String)
|
||||
String json = nbt.toString();
|
||||
ReadWriteNBT nbt = NBT.parseNBT(json);
|
||||
|
||||
```
|
||||
|
||||
## Old style of creating a NBT Wrapper
|
||||
#### (Block-)Entities
|
||||
|
||||
```java
|
||||
NBTItem nbti = new NBTItem(ItemStack);
|
||||
|
||||
NBTEntity nbtent = new NBTEntity(Entity); // Vanilla tags only!
|
||||
|
||||
NBTTileEntity tent = new NBTTileEntity(block.getState()); // Vanilla tags only!
|
||||
|
||||
NBTFile file = new NBTFile(new File("directory"), "test.nbt")); // Will be created at ./directory/test.nbt
|
||||
|
||||
NBTContainer container = new NBTContainer(json); // Parse in json
|
||||
|
||||
NBTContainer container = new NBTContainer(); // Empty NBTCompound
|
||||
// Saving
|
||||
ReadWriteNBT entityNbt = NBT.createNBTObject();
|
||||
NBT.get(entity, entityNbt::mergeCompound);
|
||||
// Restoring
|
||||
NBT.modify(entity, nbt -> {
|
||||
// You might also want to filter out entityNbt first,
|
||||
// e.g. remove some data like location, uuid, entityId, etc.
|
||||
nbt.mergeCompound(entityNbt);
|
||||
});
|
||||
```
|
||||
|
||||
## Using the wrapper(NBTCompound/)
|
||||
#### Game profiles
|
||||
|
||||
```java
|
||||
//Set
|
||||
nbti.setString("Stringtest", "Teststring");
|
||||
nbti.setInteger("Inttest", 42);
|
||||
nbti.setDouble("Doubletest", 1.5d);
|
||||
nbti.setBoolean("Booleantest", true);
|
||||
//More are available! Ask your IDE for suggestions! Don't use deprecated methods!
|
||||
|
||||
//Access/create lists
|
||||
nbtent.getCompoundList("Attributes");
|
||||
nbtent.getStringList("Attributes");
|
||||
//The list will be created if it doesn't exist, there is no setter for lists!
|
||||
|
||||
//Get
|
||||
nbti.getString("Stringtest");
|
||||
nbti.getInteger("Inttest");
|
||||
nbti.getDouble("Doubletest");
|
||||
nbti.getBoolean("Booleantest");
|
||||
nbti.getKeys(); //Get all Tags
|
||||
nbti.hasTag("Key");
|
||||
nbti.removeKey("Key");
|
||||
|
||||
nbti.getOrCreateCompound("subtag"); // Get or create a subtag
|
||||
|
||||
NBTCompound comp = nbti.getCompound("subtag"); // Get the tag or null
|
||||
B.mergeCompound(A); // Merge the data from compound A into B (Like the /data merge command)
|
||||
comp.toString(); // Get the nbt as a Mojang-Json string
|
||||
// Saving
|
||||
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
|
||||
// Restoring
|
||||
GameProfile profile = NBT.gameProfileFromNBT(nbt);
|
||||
```
|
||||
|
||||
### Interface proxies
|
||||
|
||||
You may define your own interfaces extending ``NBTProxy`` to create wrappers around nbt.
|
||||
|
||||
Methods starting with ``has``/``get``/``set`` will be interpreted as their respective calls.
|
||||
|
||||
```java
|
||||
interface TestInterface extends NBTProxy {
|
||||
// Runs: return nbt.hasTag("kills");
|
||||
boolean hasKills();
|
||||
// Runs: nbt.setInteger("kills", amount);
|
||||
void setKills(int amount);
|
||||
// Runs: return nbt.getInteger("kills");
|
||||
int getKills();
|
||||
|
||||
// Also supported
|
||||
default void addKill() {
|
||||
setKills(getKills() + 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Getters are also allowed to return other interfaces extending ``NBTProxy``.
|
||||
|
||||
And by using ``@NBTTarget`` annotation you may have a more gradual control over data. It allows setting the type of the method (``has``/``get``/``set``) and the data key.
|
||||
|
||||
```java
|
||||
interface TestInterface extends NBTProxy {
|
||||
// Will get PointsInterface from data in "other" key
|
||||
@NBTTarget(type = Type.GET, value = "other")
|
||||
PointsInterface getOtherInterface();
|
||||
}
|
||||
|
||||
interface PointsInterface extends NBTProxy {
|
||||
int getPoints();
|
||||
void setPoints(int points);
|
||||
}
|
||||
```
|
||||
|
||||
To support other data types like ItemStacks, you need to override the init method and register appropriate handlers.
|
||||
|
||||
```java
|
||||
interface TestInterface extends NBTProxy {
|
||||
@Override
|
||||
default void init() {
|
||||
registerHandler(ItemStack.class, NBTHandlers.ITEM_STACK);
|
||||
registerHandler(ReadableNBT.class, NBTHandlers.STORE_READABLE_TAG);
|
||||
registerHandler(ReadWriteNBT.class, NBTHandlers.STORE_READWRITE_TAG);
|
||||
}
|
||||
|
||||
ItemStack getItem();
|
||||
void setItem(ItemStack item);
|
||||
|
||||
ReadWriteNBT getBlockStateTag();
|
||||
void setBlockStateTag(ReadableNBT blockState);
|
||||
}
|
||||
```
|
||||
|
||||
``NBTHandlers`` class contains some pre-defined handlers.
|
||||
|
||||
###### Custom handlers
|
||||
|
||||
If you need to support custom data types that aren't available in ``NBTHandlers``, you can write your own by creating a new ``NBTHandler``.
|
||||
|
||||
You can refer to [NBTHandlers](https://github.com/tr7zw/Item-NBT-API/blob/master/item-nbt-api/src/main/java/de/tr7zw/changeme/nbtapi/handler/NBTHandlers.java) class to see how the default implementations are done.
|
||||
|
||||
### Data fixer utils
|
||||
|
||||
``DataFixerUtil`` allows updating nbt from older versions to more recent ones.
|
||||
|
||||
For example, given the input from 1.12.2:
|
||||
|
||||
`{Count:42,id:"cobblestone",tag:{display:{Name:"test"},ench:[{id:34,lvl:3}]}}`
|
||||
|
||||
You can update it to 1.20.6:
|
||||
|
||||
`{components:{"minecraft:custom_name":'{"text":"test"}',"minecraft:enchantments":{levels:{"minecraft:unbreaking":3}}},count:42,id:"minecraft:cobblestone"}`
|
||||
|
||||
By using the following code:
|
||||
|
||||
```java
|
||||
DataFixerUtil.fixUpItemData(nbt, DataFixerUtil.VERSION1_12_2, DataFixerUtil.VERSION1_20_6);
|
||||
```
|
||||
|
||||
You can also use ``DataFixerUtil.getCurrentVersion()`` to update the data to whatever version the server is running.
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
```java
|
||||
Zombie zombie = (Zombie) b.getWorld().spawnEntity(b.getLocation().add(0, 1, 0), EntityType.ZOMBIE);
|
||||
NBT.modify(zombie, nbt -> {
|
||||
nbt.setByte("CanPickUpLoot", (byte) 1);
|
||||
ReadWriteNBTCompoundList list = nbt.getCompoundList("Attributes");
|
||||
for(int i = 0; i < list.size(); i++){
|
||||
ReadWriteNBT lc = list.get(i);
|
||||
if(lc.getString("Name").equals("generic.attackDamage")){
|
||||
lc.setDouble("Base", 0.5d);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
[tr7zw](https://github.com/tr7zw)
|
||||
[SpigotMC](https://www.spigotmc.org/resources/nbt-api.7939/)
|
||||
[Modrinth](https://modrinth.com/plugin/nbtapi)
|
||||
[Bukkit](https://dev.bukkit.org/projects/nbt-api)
|
||||
[Hangar](https://hangar.papermc.io/tr7zw/NBTAPI)
|
||||
[SpigotMC](https://www.spigotmc.org/resources/nbt-api.7939/)
|
||||
[Bukkit](https://dev.bukkit.org/projects/nbt-api)
|
||||
+17
-7
@@ -1,14 +1,24 @@
|
||||
[Home](https://github.com/tr7zw/Item-NBT-API/wiki)
|
||||
|
||||
[Using the NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API)
|
||||
[Using Maven](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Maven) /
|
||||
[Using Gradle](https://github.com/tr7zw/Item-NBT-API/wiki/Using-Gradle)
|
||||
|
||||
[FAQ](https://github.com/tr7zw/Item-NBT-API/wiki/FAQ)
|
||||
|
||||
## Code Examples
|
||||
|
||||
### General
|
||||
* [Converting Minecraft Objects to NBT and Strings](https://github.com/tr7zw/Item-NBT-API/wiki/Converting-Minecraft-Objects-to-NBT-and-Strings)
|
||||
* [Merging Compounds](https://github.com/tr7zw/Item-NBT-API/wiki/Merging-Compounds-(Copy-the-data-from-one-onto-another-compound))
|
||||
### Items
|
||||
* [Set a skull's skin using NBT API](https://github.com/tr7zw/Item-NBT-API/wiki/Set-a-skull's-skin-using-NBT-API)
|
||||
### Entities
|
||||
* [Zombie that can pick up loot and does 0.5 hearths of damage](https://github.com/tr7zw/Item-NBT-API/wiki/Zombie-that-can-pick-up-loot-and-does-0.5-hearths-of-damage)
|
||||
|
||||
* [Basics](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#basics-overview)
|
||||
* [Items](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#working-with-items)
|
||||
* [Block-Entities/Entities](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#working-with-(block-)entities)
|
||||
* [Blocks](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#working-with-blocks)
|
||||
|
||||
### Extras
|
||||
|
||||
* [Example usages](https://github.com/tr7zw/Item-NBT-API/wiki/Example-Usages)
|
||||
* [NBT files](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#nbt-files)
|
||||
* [NBT objects](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#converting-minecraft-objects-to-nbt-and-strings)
|
||||
* [Interface proxies](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#interface-proxies)
|
||||
* [Data fixer utils](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#data-fixer-utils)
|
||||
|
||||
Reference in new issue
Block a user