mirror of
https://github.com/tr7zw/Item-NBT-API.git
synced 2026-11-05 01:00:10 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bdddc134a | ||
|
|
733b871c09 | ||
|
|
bc48239fdd | ||
|
|
f182204b2f | ||
|
|
a424ce1e85 | ||
|
|
c881566c14 | ||
|
|
2d6aa142de | ||
|
|
cc1436cd09 | ||
|
|
4506dd37da | ||
|
|
bbc02e1299 | ||
|
|
ca9d5ad140 | ||
|
|
25c82a5ad0 | ||
|
|
17d40445d2 | ||
|
|
47ad3591df | ||
|
|
b3652ecf7d | ||
|
|
46a9116613 | ||
|
|
5776be4a35 | ||
|
|
9fbee68966 | ||
|
|
b277f0b01d | ||
|
|
e4793a0d46 | ||
|
|
7c34542b86 | ||
|
|
856bf5ed6d | ||
|
|
123da87a3a | ||
|
|
f3d7545c6b | ||
|
|
3b29fe399e | ||
|
|
c368318bb6 | ||
|
|
c1473dfa97 | ||
|
|
a360ca50f4 | ||
|
|
8b1ca8447e | ||
|
|
b66353d0d0 | ||
|
|
5bec1371d2 | ||
|
|
8cbb6af964 | ||
|
|
309455ecfc | ||
|
|
22c0b0385b | ||
|
|
bc6bd56ce9 | ||
|
|
fcdf0929f9 | ||
|
|
e1c6ce2575 | ||
|
|
2fcc1d82ab | ||
|
|
eec2c0980e | ||
|
|
3ddf3e53dc | ||
|
|
70d5ab12eb | ||
|
|
a63e9ba8f4 | ||
|
|
d8a59d6383 | ||
|
|
943c7d23d6 | ||
|
|
6c70ce2ccc | ||
|
|
c1b7854fbb | ||
|
|
f9f1c204b6 | ||
|
|
789f1fea96 | ||
|
|
0e40144f09 | ||
|
|
1c6315e014 | ||
|
|
91f345adf2 | ||
|
|
71783a8dc9 | ||
|
|
5d06ae9275 | ||
|
|
835fdf48cd | ||
|
|
31d77d25f9 | ||
|
|
6cb08ac919 | ||
|
|
08a92ac06c | ||
|
|
6bcd00d114 | ||
|
|
bc93986392 | ||
|
|
aa646e35ae | ||
|
|
1054d16996 | ||
|
|
9351a86774 | ||
|
|
56a43d246b | ||
|
|
2b895fd4ef | ||
|
|
8f8bbfd034 | ||
|
|
df231c2324 | ||
|
|
b068284b6e | ||
|
|
9e9b85f46a | ||
|
|
3baed4b1e7 | ||
|
|
432b40b3bc | ||
|
|
fdf3638948 | ||
|
|
b240e888cd | ||
|
|
ccc096e2d4 | ||
|
|
35f541d3db | ||
|
|
d4ee3b998c | ||
|
|
2902dca5a5 | ||
|
|
1c1e3c8664 | ||
|
|
7f8f021ced | ||
|
|
98ed1850f6 | ||
|
|
764e01503d | ||
|
|
43d8e2a351 | ||
|
|
689cd4fa3b | ||
|
|
c8f68f8b3b | ||
|
|
e52ba209e6 | ||
|
|
418f5bea07 | ||
|
|
1392e72a8a | ||
|
|
dc39d1d8ef | ||
|
|
a974142a55 | ||
|
|
f014e71020 | ||
|
|
da6be72d29 | ||
|
|
d9a93e9306 | ||
|
|
ba374fe69c | ||
|
|
aa35ee8333 | ||
|
|
3bb0bdd74c | ||
|
|
f3af749b11 | ||
|
|
d81c2b4e8e | ||
|
|
94192feb8f | ||
|
|
af3d9c8502 | ||
|
|
ae98859524 | ||
|
|
70f0bb62f9 | ||
|
|
505e172955 | ||
|
|
d2b2b6a506 | ||
|
|
e2e8aad70a | ||
|
|
4874366e30 | ||
|
|
d2de90ee25 | ||
|
|
a0f93e575e | ||
|
|
b339c9e9c2 | ||
|
|
3425a6672d | ||
|
|
67616266da | ||
|
|
b08e4aa12f | ||
|
|
27411e90da |
No files matched your search
@@ -1,13 +1,14 @@
|
||||
# Item-NBT-API
|
||||
|
||||
[](https://modrinth.com/plugin/nbtapi)
|
||||
[](https://discordapp.com/invite/yk4caxM)
|
||||
|
||||
Add custom NBT tags to Items/Tiles/Entities without NMS! Modify NBT and store it in Files, other NBT, or as String in yaml/json/SQL/Redis.
|
||||
[Server Owner/Developer Wiki](https://github.com/tr7zw/Item-NBT-API/wiki)
|
||||
|
||||
### Build/Maven/Sonar Status
|
||||
|
||||
[](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
|
||||
|
||||
|
||||
+15
-3
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
@@ -18,6 +18,12 @@
|
||||
<version>4.0.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -32,9 +38,15 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.6.2</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>de.tr7zw.changeme.nbtapi.utils.metrics</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>LICENSE</resource>
|
||||
|
||||
@@ -9,6 +9,9 @@ import java.util.logging.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -727,4 +730,204 @@ public class NBT {
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads block‑specific data that was previously stored in the chunk's
|
||||
* persistent data container.
|
||||
* <p>
|
||||
* The data is stored inside the {@code blocks} compound of the chunk's
|
||||
* persistent data container, using a key formed as {@code X_Y_Z} where
|
||||
* X, Y, Z are the block's coordinates.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param block the block whose stored data is to be read
|
||||
* @param consumer the consumer that receives a {@link ReadableNBT} view of the data
|
||||
*/
|
||||
public static void readChunkPDC(Block block, Consumer<ReadableNBT> consumer) {
|
||||
processBlockChunkPDC(block, false, nbt -> {
|
||||
consumer.accept(nbt);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads block‑specific data from the chunk's persistent data container and
|
||||
* applies a function to produce a result.
|
||||
* <p>
|
||||
* The data is stored inside the {@code blocks} compound of the chunk's
|
||||
* persistent data container, using a key formed as {@code X_Y_Z} where
|
||||
* X, Y, Z are the block's coordinates.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param block the block whose stored data is to be read
|
||||
* @param function the function that processes the {@link ReadableNBT} and
|
||||
* returns a value
|
||||
* @param <T> the type of the returned value
|
||||
* @return the result of the function applied to the data
|
||||
*/
|
||||
public static <T> T readAndGetChunkPDC(Block block, Function<ReadableNBT, T> function) {
|
||||
return processBlockChunkPDC(block, false, function::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies block‑specific data stored in the chunk's persistent data container.
|
||||
* <p>
|
||||
* The data is stored inside the {@code blocks} compound of the chunk's
|
||||
* persistent data container, using a key formed as {@code X_Y_Z} where
|
||||
* X, Y, Z are the block's coordinates.
|
||||
* <p>
|
||||
* <b>Caution:</b> the data remains even if the block is broken, replaced, moved,
|
||||
* or exploded, etc. You should manually remove the data when it is no longer needed.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param block the block whose data is to be modified
|
||||
* @param consumer the consumer that receives the {@link ReadWriteNBT} and
|
||||
* applies modifications
|
||||
*/
|
||||
public static void modifyChunkPDC(Block block, Consumer<ReadWriteNBT> consumer) {
|
||||
processBlockChunkPDC(block, true, nbt -> {
|
||||
consumer.accept(nbt);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies block‑specific data in the chunk's persistent data container and
|
||||
* returns a result computed from the modified data.
|
||||
* <p>
|
||||
* The data is stored inside the {@code blocks} compound of the chunk's
|
||||
* persistent data container, using a key formed as {@code X_Y_Z} where
|
||||
* X, Y, Z are the block's coordinates.
|
||||
* <p>
|
||||
* <b>Caution:</b> the data remains even if the block is broken, replaced, moved,
|
||||
* or exploded, etc. You should manually remove the data when it is no longer needed.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param block the block whose data is to be modified
|
||||
* @param function the function that receives the {@link ReadWriteNBT}, modifies
|
||||
* it, and returns a value
|
||||
* @param <T> the type of the returned value
|
||||
* @return the result of the function after modification
|
||||
*/
|
||||
public static <T> T modifyAndGetChunkPDC(Block block, Function<ReadWriteNBT, T> function) {
|
||||
return processBlockChunkPDC(block, true, function::apply);
|
||||
}
|
||||
|
||||
private static <T> T processBlockChunkPDC(Block block, boolean createIfAbsent, Function<NBTCompound, T> action) {
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
|
||||
|
||||
String blockKey = block.getX() + "_" + block.getY() + "_" + block.getZ();
|
||||
|
||||
NBTCompound chunkData = new NBTPersistentDataContainer(block.getChunk().getPersistentDataContainer(), !createIfAbsent);
|
||||
|
||||
NBTCompound blocksData = createIfAbsent
|
||||
? chunkData.getOrCreateCompound("blocks")
|
||||
: chunkData.getCompound("blocks");
|
||||
|
||||
NBTCompound blockData;
|
||||
if (createIfAbsent) {
|
||||
blockData = blocksData.getOrCreateCompound(blockKey);
|
||||
} else {
|
||||
blockData = blocksData == null ? null : blocksData.getCompound(blockKey);
|
||||
if (blockData == null) {
|
||||
blockData = new NBTContainer().setReadOnly(true);
|
||||
}
|
||||
}
|
||||
|
||||
T result = action.apply(blockData);
|
||||
|
||||
if (createIfAbsent) {
|
||||
if (blockData.isEmpty()) {
|
||||
blocksData.removeKey(blockKey);
|
||||
}
|
||||
if (blocksData.isEmpty()) {
|
||||
chunkData.removeKey("blocks");
|
||||
}
|
||||
}
|
||||
|
||||
if (result instanceof ReadableNBT || result instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the chunk's persistent data container.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param chunk the chunk whose persistent data is to be read
|
||||
* @param consumer the consumer that receives a {@link ReadableNBT} view of the data
|
||||
*/
|
||||
public static void readChunkPDC(Chunk chunk, Consumer<ReadableNBT> consumer) {
|
||||
processChunkPDC(chunk, false, nbt -> {
|
||||
consumer.accept(nbt);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the chunk's persistent data container and applies a function to produce a result.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param chunk the chunk whose persistent data is to be read
|
||||
* @param function the function that processes the {@link ReadableNBT} and
|
||||
* returns a value
|
||||
* @param <T> the type of the returned value
|
||||
* @return the result of the function applied to the data
|
||||
*/
|
||||
public static <T> T readAndGetChunkPDC(Chunk chunk, Function<ReadableNBT, T> function) {
|
||||
return processChunkPDC(chunk, false, function::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the chunk's persistent data container.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param chunk the chunk whose persistent data is to be modified
|
||||
* @param consumer the consumer that receives the {@link ReadWriteNBT} and
|
||||
* applies modifications
|
||||
*/
|
||||
public static void modifyChunkPDC(Chunk chunk, Consumer<ReadWriteNBT> consumer) {
|
||||
processChunkPDC(chunk, true, nbt -> {
|
||||
consumer.accept(nbt);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the chunk's persistent data container and returns a result computed from the modified data.
|
||||
* <p>
|
||||
* <b>Minecraft version:</b> this method requires <b>1.16.4 or newer</b>.
|
||||
*
|
||||
* @param chunk the chunk whose persistent data is to be modified
|
||||
* @param function the function that receives the {@link ReadWriteNBT}, modifies
|
||||
* it, and returns a value
|
||||
* @param <T> the type of the returned value
|
||||
* @return the result of the function after modification
|
||||
*/
|
||||
public static <T> T modifyAndGetChunkPDC(Chunk chunk, Function<ReadWriteNBT, T> function) {
|
||||
return processChunkPDC(chunk, true, function::apply);
|
||||
}
|
||||
|
||||
private static <T> T processChunkPDC(Chunk chunk, boolean createIfAbsent, Function<NBTCompound, T> action) {
|
||||
CheckUtil.assertAvailable(MinecraftVersion.MC1_16_R3);
|
||||
|
||||
NBTCompound chunkData = new NBTPersistentDataContainer(chunk.getPersistentDataContainer(), !createIfAbsent);
|
||||
|
||||
T result = action.apply(chunkData);
|
||||
|
||||
if (result instanceof ReadableNBT || result instanceof ReadableNBTList<?>) {
|
||||
throw new NbtApiException("Tried returning part of the NBT to outside of the NBT scope!");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,16 +7,17 @@ import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
/**
|
||||
* Helper class to store NBT data to Block Locations. Use getData() to get the
|
||||
* NBT instance. Important notes:
|
||||
*
|
||||
* <p>
|
||||
* - Non BlockEntities can not have NBT data. This stores the data to the chunk
|
||||
* instead!
|
||||
*
|
||||
* <p>
|
||||
* - The data is really just on the location. If the block gets
|
||||
* broken/changed/exploded/moved etc., the data is still on that location!
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
* @deprecated use methods in {@link NBT} class to read/modify block's NBT
|
||||
*/
|
||||
@Deprecated
|
||||
public class NBTBlock {
|
||||
|
||||
private final Block block;
|
||||
@@ -30,6 +31,13 @@ public class NBTBlock {
|
||||
nbtChunk = new NBTChunk(block.getChunk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block NBT data stored within the chunk's PDC.
|
||||
*
|
||||
* @return block NBT data
|
||||
* @deprecated use methods in {@link NBT} class to read/modify block's NBT
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTCompound getData() {
|
||||
return nbtChunk.getPersistentDataContainer().getOrCreateCompound("blocks")
|
||||
.getOrCreateCompound(block.getX() + "_" + block.getY() + "_" + block.getZ());
|
||||
|
||||
@@ -5,6 +5,12 @@ import org.bukkit.Chunk;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Helper class to store NBT data to {@link Chunk}'s PDC (persistent data container).
|
||||
*
|
||||
* @deprecated use methods in {@link NBT} class to read/modify chunk's nbt
|
||||
*/
|
||||
@Deprecated
|
||||
public class NBTChunk {
|
||||
|
||||
private final Chunk chunk;
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -19,6 +20,7 @@ import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.CheckUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.PathUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.UUIDUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.PathUtil.PathSegment;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.Forge1710Mappings;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
@@ -98,10 +100,13 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return readOnlyCache;
|
||||
}
|
||||
Object rootnbttag = getCompound();
|
||||
if (rootnbttag instanceof Optional<?>) {
|
||||
rootnbttag = ((Optional<Object>) rootnbttag).orElse(null);
|
||||
}
|
||||
if (rootnbttag == null) {
|
||||
return null;
|
||||
}
|
||||
if (!NBTReflectionUtil.valideCompound(this))
|
||||
if (!NBTReflectionUtil.validCompound(this))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = NBTReflectionUtil.getToCompount(rootnbttag, this);
|
||||
if (readOnly) {
|
||||
@@ -657,7 +662,9 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
public void setUUID(String key, UUID value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
setIntArray(key, UUIDUtil.uuidToIntArray(value));
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_UUID, key, value);
|
||||
} else {
|
||||
setString(key, value.toString());
|
||||
@@ -678,10 +685,13 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
public UUID getUUID(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)
|
||||
&& getType(key) == NBTType.NBTTagIntArray) {
|
||||
NBTType type = getType(key);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4) && type == NBTType.NBTTagIntArray) {
|
||||
return UUIDUtil.uuidFromIntArray(getIntArray(key));
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)
|
||||
&& type == NBTType.NBTTagIntArray) {
|
||||
return (UUID) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_UUID, key);
|
||||
} else if (getType(key) == NBTType.NBTTagString) {
|
||||
} else if (type == NBTType.NBTTagString) {
|
||||
try {
|
||||
return UUID.fromString(getString(key));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
@@ -786,7 +796,7 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
if (getType(name) != NBTType.NBTTagCompound)
|
||||
return null;
|
||||
NBTCompound next = new NBTCompound(this, name, readOnly);
|
||||
if (NBTReflectionUtil.valideCompound(next))
|
||||
if (NBTReflectionUtil.validCompound(next))
|
||||
return next;
|
||||
return null;
|
||||
} finally {
|
||||
@@ -1343,7 +1353,13 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
Object nbtbase = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
|
||||
if (nbtbase == null)
|
||||
return null;
|
||||
return NBTType.valueOf((byte) ReflectionMethod.COMPOUND_OWN_TYPE.run(nbtbase));
|
||||
return NBTType.valueOf((byte) ReflectionMethod.COMPOUND_OWN_TYPE_LEGACY.run(nbtbase));
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
|
||||
if (o == null)
|
||||
return null;
|
||||
return NBTType.fromName((String)ReflectionMethod.TAGTYPE_GET_NAME.run(ReflectionMethod.TAGTYPE_OWN_TYPE.run(o)));
|
||||
}
|
||||
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_TYPE, name);
|
||||
if (o == null)
|
||||
@@ -1460,6 +1476,106 @@ public class NBTCompound implements ReadWriteNBT {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTCompound extractDifference(ReadableNBT other) {
|
||||
if (this == other)
|
||||
return new NBTContainer();
|
||||
|
||||
if (other instanceof NBTCompound) {
|
||||
return saveDiff(new NBTContainer(), this, (NBTCompound) other);
|
||||
} else {
|
||||
throw new NbtApiException("Unknown NBT object: " + other);
|
||||
}
|
||||
}
|
||||
|
||||
private static NBTCompound saveDiff(NBTCompound saveTo, NBTCompound compA, NBTCompound compB) {
|
||||
for (String key : compA.getKeys()) {
|
||||
saveDiff(saveTo, compA, compB, key);
|
||||
}
|
||||
return saveTo;
|
||||
}
|
||||
|
||||
private static void saveDiff(NBTCompound saveTo, NBTCompound compA, NBTCompound compB, String key) {
|
||||
boolean typeMismatch = compA.getType(key) != compB.getType(key);
|
||||
switch (compA.getType(key)) {
|
||||
case NBTTagByte:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setByte(key, compA.getByte(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagByteArray:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setByteArray(key, compA.getByteArray(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagCompound: {
|
||||
NBTCompound tmp1 = compA.getCompound(key);
|
||||
if (tmp1 == null) return;
|
||||
if (typeMismatch) {
|
||||
saveTo.addCompound(key).mergeCompound(tmp1);
|
||||
} else {
|
||||
NBTCompound tmp2 = compB.getCompound(key);
|
||||
if (tmp2 == null) {
|
||||
saveTo.addCompound(key).mergeCompound(tmp1);
|
||||
return;
|
||||
}
|
||||
NBTCompound tmpDiff = tmp1.extractDifference(tmp2);
|
||||
if (!tmpDiff.getKeys().isEmpty()) {
|
||||
saveTo.addCompound(key).mergeCompound(tmpDiff);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case NBTTagDouble:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setDouble(key, compA.getDouble(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagEnd:
|
||||
return; // ??
|
||||
case NBTTagFloat:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setFloat(key, compA.getFloat(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagInt:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setInteger(key, compA.getInteger(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagIntArray:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setIntArray(key, compA.getIntArray(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagList:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.set(key, NBTReflectionUtil.getEntry(compA, key));
|
||||
}
|
||||
return;
|
||||
case NBTTagLong:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setLong(key, compA.getLong(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagShort:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setShort(key, compA.getShort(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagString:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setString(key, compA.getString(key));
|
||||
}
|
||||
return;
|
||||
case NBTTagLongArray:
|
||||
if (typeMismatch || !isEqual(compA, compB, key)) {
|
||||
saveTo.setLongArray(key, compA.getLongArray(key));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
|
||||
if (compA.getType(key) != compB.getType(key))
|
||||
return false;
|
||||
|
||||
@@ -21,6 +21,8 @@ public class NBTContainer extends NBTCompound {
|
||||
|
||||
/**
|
||||
* Creates an empty, standalone NBTCompound
|
||||
*
|
||||
* @deprecated use {@link NBT#createNBTObject()}
|
||||
*/
|
||||
@Deprecated
|
||||
public NBTContainer() {
|
||||
|
||||
@@ -304,8 +304,8 @@ public abstract class NBTList<T> implements List<T>, ReadWriteNBTList<T> {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
int id = -1;
|
||||
while ((id = indexOf(o)) != -1) {
|
||||
int id = indexOf(o);
|
||||
if (id != -1) {
|
||||
remove(id);
|
||||
}
|
||||
return size != size();
|
||||
|
||||
@@ -11,7 +11,11 @@ public class NBTPersistentDataContainer extends NBTCompound {
|
||||
private final PersistentDataContainer container;
|
||||
|
||||
public NBTPersistentDataContainer(PersistentDataContainer container) {
|
||||
super(null, null);
|
||||
this(container, false);
|
||||
}
|
||||
|
||||
protected NBTPersistentDataContainer(PersistentDataContainer container, boolean readOnly) {
|
||||
super(null, null, readOnly);
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.BlockState;
|
||||
@@ -18,11 +20,15 @@ import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.GsonWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.ReflectionUtil;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.CodecHelper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ObjectCreator;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
@@ -40,6 +46,10 @@ public class NBTReflectionUtil {
|
||||
private static Field field_handle = null;
|
||||
private static Object type_custom_data = null;
|
||||
private static Object registry_access = null;
|
||||
public static Codec<Object> itemstack_codec = null;
|
||||
public static DynamicOps<Object> nbtOps = null;
|
||||
public static DynamicOps<Object> nbtRegistryOps = null;
|
||||
public static Object problemReporter = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -56,15 +66,22 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
try {
|
||||
Field typeField = ClassWrapper.NMS_DATACOMPONENTS.getClazz().getDeclaredField(
|
||||
MojangToMapping.getMapping().get("net.minecraft.core.component.DataComponents#CUSTOM_DATA"));
|
||||
Field typeField = ReflectionUtil.getMappedField(ClassWrapper.NMS_DATACOMPONENTS.getClazz(), "net.minecraft.core.component.DataComponents#CUSTOM_DATA");
|
||||
type_custom_data = typeField.get(null);
|
||||
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
||||
|
||||
} catch (Exception e) {
|
||||
MinecraftVersion.getLogger().log(Level.WARNING,
|
||||
"Unable to find DataComponents#CUSTOM_DATA, NBTApi will not be able to read/write custom data on 1.20+",
|
||||
e);
|
||||
}
|
||||
try {
|
||||
Object nmsServer = ReflectionMethod.NMSSERVER_GETSERVER.run(Bukkit.getServer());
|
||||
registry_access = ReflectionMethod.NMSSERVER_GETREGISTRYACCESS.run(nmsServer);
|
||||
itemstack_codec = (Codec<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_ITEMSTACK.getClazz(), "net.minecraft.world.item.ItemStack#CODEC").get(null);
|
||||
nbtOps = (DynamicOps<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_NBTOPS.getClazz(), "net.minecraft.nbt.NbtOps#INSTANCE").get(null);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
nbtRegistryOps = (DynamicOps<Object>) ReflectionMethod.GET_SERIALIZATION_CONTEXT.run(registry_access, nbtOps);
|
||||
problemReporter = ReflectionUtil.getMappedField(ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), "net.minecraft.util.ProblemReporter#DISCARDING").get(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -154,7 +171,7 @@ public class NBTReflectionUtil {
|
||||
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
|
||||
try {
|
||||
Object workingtag = comp.getResolvedObject();
|
||||
if(workingtag == null) {
|
||||
if (workingtag == null) {
|
||||
workingtag = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
}
|
||||
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
|
||||
@@ -213,21 +230,35 @@ public class NBTReflectionUtil {
|
||||
* @return NMS ItemStack
|
||||
*/
|
||||
public static Object convertNBTCompoundtoNMSItem(NBTCompound nbtcompound) {
|
||||
Object nmsComp = null;
|
||||
try {
|
||||
Object nmsComp = getToCompount(nbtcompound.getCompound(), nbtcompound);
|
||||
nmsComp = getToCompount(nbtcompound.getCompound(), nbtcompound);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
if (nbtcompound.hasTag("tag") || nbtcompound.hasTag("Count")) {
|
||||
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, DataFixerUtil.VERSION1_20_4,
|
||||
DataFixerUtil.getCurrentVersion());
|
||||
if (nbtcompound.hasTag("DataVersion", NBTType.NBTTagInt)) {
|
||||
int dataVersion = nbtcompound.getInteger("DataVersion");
|
||||
int currentVersion = DataFixerUtil.getCurrentVersion();
|
||||
if (dataVersion < currentVersion) {
|
||||
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, dataVersion, currentVersion);
|
||||
}
|
||||
} else if (nbtcompound.hasTag("tag") || nbtcompound.hasTag("Count")) {
|
||||
nmsComp = DataFixerUtil.fixUpRawItemData(nmsComp, DataFixerUtil.VERSION1_20_4, DataFixerUtil.getCurrentVersion());
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
return CodecHelper.convertNbtToItemStack(nmsComp);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
Optional<Object> opt = (Optional<Object>) ReflectionMethod.NMSITEM_LOAD_MODERN.run(null,
|
||||
registry_access, nmsComp);
|
||||
return opt.orElse(null);
|
||||
} else {
|
||||
return ReflectionMethod.NMSITEM_LOAD.run(null, registry_access, nmsComp);
|
||||
}
|
||||
return ReflectionMethod.NMSITEM_LOAD.run(null, registry_access, nmsComp);
|
||||
} else if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_11_R1.getVersionId()) {
|
||||
return ObjectCreator.NMS_COMPOUNDFROMITEM.getInstance(nmsComp);
|
||||
} else {
|
||||
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nmsComp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack!", e);
|
||||
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack! " + nmsComp, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,13 +270,20 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static NBTContainer convertNMSItemtoNBTCompound(Object nmsitem) {
|
||||
try {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return new NBTContainer(ReflectionMethod.NMSITEM_SAVE_MODERN.run(nmsitem, registry_access));
|
||||
NBTContainer container;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
container = new NBTContainer(CodecHelper.convertItemStackToNbt(nmsitem));
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
container = new NBTContainer(ReflectionMethod.NMSITEM_SAVE_MODERN.run(nmsitem, registry_access));
|
||||
} else {
|
||||
Object answer = ReflectionMethod.NMSITEM_SAVE.run(nmsitem,
|
||||
ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance());
|
||||
return new NBTContainer(answer);
|
||||
container = new NBTContainer(answer);
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)) {
|
||||
container.setInteger("DataVersion", DataFixerUtil.getCurrentVersion());
|
||||
}
|
||||
return container;
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while converting NMS ItemStack to NBTCompound!", e);
|
||||
}
|
||||
@@ -276,7 +314,16 @@ public class NBTReflectionUtil {
|
||||
public static Object getEntityNBTTagCompound(Object nmsEntity) {
|
||||
try {
|
||||
Object nbt = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
Object answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
|
||||
Object answer;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
Object output = ReflectionMethod.NMS_GET_TAG_VALUE_OUTPUT.run(null, problemReporter, registry_access);
|
||||
|
||||
ReflectionMethod.NMS_ENTITY_GET_NBT_1216.run(nmsEntity, output);
|
||||
|
||||
answer = ReflectionMethod.NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND.run(output);
|
||||
} else {
|
||||
answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
|
||||
}
|
||||
if (answer == null)
|
||||
answer = nbt;
|
||||
return answer;
|
||||
@@ -294,7 +341,14 @@ public class NBTReflectionUtil {
|
||||
*/
|
||||
public static Object setEntityNBTTag(Object nbtTag, Object nmsEntity) {
|
||||
try {
|
||||
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
Object valueInputTag = ReflectionMethod.NMS_GET_TAG_VALUE_INPUT.run(null, problemReporter, registry_access, nbtTag);
|
||||
|
||||
ReflectionMethod.NMS_ENTITY_SET_NBT_1216.run(nmsEntity, valueInputTag);
|
||||
} else {
|
||||
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
|
||||
}
|
||||
|
||||
return nmsEntity;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while setting the NBTCompound of an Entity", ex);
|
||||
@@ -326,7 +380,13 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
|
||||
Object answer = null;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
Object output = ReflectionMethod.NMS_GET_TAG_VALUE_OUTPUT.run(null, problemReporter, registry_access);
|
||||
|
||||
ReflectionMethod.TILEENTITY_GET_NBT_1216.run(o, output);
|
||||
|
||||
answer = ReflectionMethod.NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND.run(output);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
answer = ReflectionMethod.TILEENTITY_GET_NBT_1205.run(o, registry_access);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
|
||||
answer = ReflectionMethod.TILEENTITY_GET_NBT_1181.run(o);
|
||||
@@ -361,7 +421,11 @@ public class NBTReflectionUtil {
|
||||
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
Object valueInput = ReflectionMethod.NMS_GET_TAG_VALUE_INPUT.run(null, problemReporter, registry_access, comp);
|
||||
|
||||
ReflectionMethod.TILEENTITY_SET_NBT_1216.run(o, valueInput);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
ReflectionMethod.TILEENTITY_SET_NBT_1205.run(o, comp, registry_access);
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
ReflectionMethod.TILEENTITY_SET_NBT.run(o, comp);
|
||||
@@ -386,7 +450,11 @@ public class NBTReflectionUtil {
|
||||
public static Object getSubNBTTagCompound(Object compound, String name) {
|
||||
try {
|
||||
if ((boolean) ReflectionMethod.COMPOUND_HAS_KEY.run(compound, name)) {
|
||||
return ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
|
||||
Object comp = ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
|
||||
if (comp instanceof Optional) {
|
||||
return ((Optional<?>) comp).orElse(null);
|
||||
}
|
||||
return comp;
|
||||
} else {
|
||||
throw new NbtApiException("Tried getting invalid compound '" + name + "' from '" + compound + "'!");
|
||||
}
|
||||
@@ -410,7 +478,7 @@ public class NBTReflectionUtil {
|
||||
if (nbttag == null) {
|
||||
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp)) {
|
||||
if (!validCompound(comp)) {
|
||||
return;
|
||||
}
|
||||
Object workingtag = getToCompount(nbttag, comp);
|
||||
@@ -427,10 +495,13 @@ public class NBTReflectionUtil {
|
||||
* Checks if the Compound is correctly linked to it's roots
|
||||
*
|
||||
* @param comp
|
||||
* @return true if this is a valide Compound, else false
|
||||
* @return true if this is a valid Compound, else false
|
||||
*/
|
||||
public static boolean valideCompound(NBTCompound comp) {
|
||||
public static boolean validCompound(NBTCompound comp) {
|
||||
Object root = comp.getCompound();
|
||||
if (root instanceof Optional) {
|
||||
root = ((Optional<?>) root).orElse(null);
|
||||
}
|
||||
if (root == null) {
|
||||
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
@@ -445,9 +516,15 @@ public class NBTReflectionUtil {
|
||||
structure.add(comp.getName());
|
||||
comp = comp.getParent();
|
||||
}
|
||||
if (nbttag instanceof Optional) {
|
||||
nbttag = ((Optional<?>) nbttag).orElse(null);
|
||||
}
|
||||
while (!structure.isEmpty()) {
|
||||
String target = structure.pollLast();
|
||||
nbttag = getSubNBTTagCompound(nbttag, target);
|
||||
if (nbttag instanceof Optional) {
|
||||
nbttag = ((Optional<?>) nbttag).orElse(null);
|
||||
}
|
||||
if (nbttag == null) {
|
||||
throw new NbtApiException("Unable to find tag '" + target + "' in " + nbttag);
|
||||
}
|
||||
@@ -470,7 +547,7 @@ public class NBTReflectionUtil {
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
if (!validCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
try {
|
||||
@@ -497,7 +574,7 @@ public class NBTReflectionUtil {
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp)) {
|
||||
if (!validCompound(comp)) {
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
}
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
@@ -525,7 +602,18 @@ public class NBTReflectionUtil {
|
||||
workingtag = dummyNBT.getCompound(); // it creates a new ListTag if needed, but its unlinked
|
||||
}
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
|
||||
Object nbt = null;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key);
|
||||
if (nbt instanceof Optional) {
|
||||
nbt = ((Optional<?>) nbt).orElse(null);
|
||||
}
|
||||
if (nbt == null) {
|
||||
nbt = ClassWrapper.NMS_NBTTAGLIST.getClazz().newInstance();
|
||||
}
|
||||
} else {
|
||||
nbt = ReflectionMethod.COMPOUND_GET_LIST_LEGACY.run(workingtag, key, type.getId());
|
||||
}
|
||||
if (clazz == String.class) {
|
||||
return (NBTList<T>) new NBTStringList(comp, key, type, nbt);
|
||||
} else if (clazz == NBTListCompound.class) {
|
||||
@@ -557,6 +645,26 @@ public class NBTReflectionUtil {
|
||||
}
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
if (nbt instanceof Optional) {
|
||||
nbt = ((Optional<?>) nbt).orElse(null);
|
||||
}
|
||||
if (nbt == null) {
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
if (new NBTStringList(comp, key, NBTType.NBTTagString, nbt).isEmpty()) {
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
Object compound = ReflectionMethod.LIST_GET.run(nbt, 0);
|
||||
if (compound instanceof Optional) {
|
||||
compound = ((Optional<?>) compound).orElse(null);
|
||||
}
|
||||
if (compound == null) {
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
return NBTType.fromName((String) ReflectionMethod.TAGTYPE_GET_NAME
|
||||
.run(ReflectionMethod.TAGTYPE_OWN_TYPE.run(compound)));
|
||||
}
|
||||
String fieldname = "type";
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
fieldname = "w";
|
||||
@@ -631,7 +739,7 @@ public class NBTReflectionUtil {
|
||||
if (rootnbttag == null) {
|
||||
return;
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
if (!validCompound(comp))
|
||||
return;
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
ReflectionMethod.COMPOUND_REMOVE_KEY.run(workingtag, key);
|
||||
@@ -670,7 +778,7 @@ public class NBTReflectionUtil {
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
if (!validCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = getToCompount(rootnbttag, comp);
|
||||
type.run(workingtag, key, data);
|
||||
@@ -693,7 +801,41 @@ public class NBTReflectionUtil {
|
||||
if (workingtag == null) {
|
||||
workingtag = dummyNBT.getCompound();
|
||||
}
|
||||
return type.run(workingtag, key);
|
||||
if (workingtag instanceof Optional<?>) {
|
||||
workingtag = ((Optional<Object>) workingtag).orElseGet(() -> dummyNBT.getCompound());
|
||||
}
|
||||
Object obj = type.run(workingtag, key);
|
||||
if (obj instanceof Optional<?>) {
|
||||
return ((Optional<Object>) obj).orElseGet(() -> getDefaultValue(type));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static Object getDefaultValue(ReflectionMethod type) {
|
||||
if (type == ReflectionMethod.COMPOUND_GET_STRING) {
|
||||
return "";
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_BYTE) {
|
||||
return (byte) 0;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_SHORT) {
|
||||
return (short) 0;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_BOOLEAN) {
|
||||
return false;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_INT) {
|
||||
return 0;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_LONG) {
|
||||
return 0L;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_FLOAT) {
|
||||
return 0.0f;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_DOUBLE) {
|
||||
return 0.0d;
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_BYTEARRAY) {
|
||||
return new byte[0];
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_INTARRAY) {
|
||||
return new int[0];
|
||||
} else if (type == ReflectionMethod.COMPOUND_GET_LONGARRAY) {
|
||||
return new long[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package de.tr7zw.changeme.nbtapi;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Optional;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
@@ -21,7 +22,11 @@ public class NBTStringList extends NBTList<String> {
|
||||
@Override
|
||||
public String get(int index) {
|
||||
try {
|
||||
return (String) ReflectionMethod.LIST_GET_STRING.run(listObject, index);
|
||||
Object ret = ReflectionMethod.LIST_GET_STRING.run(listObject, index);
|
||||
if (ret instanceof Optional<?>) {
|
||||
return ((Optional<String>) ret).orElse("");
|
||||
}
|
||||
return (String) ret;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@ package de.tr7zw.changeme.nbtapi;
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum NBTType {
|
||||
NBTTagEnd(0), NBTTagByte(1), NBTTagShort(2), NBTTagInt(3), NBTTagLong(4), NBTTagFloat(5), NBTTagDouble(6),
|
||||
NBTTagByteArray(7), NBTTagString(8), NBTTagList(9), NBTTagCompound(10), NBTTagIntArray(11), NBTTagLongArray(12);
|
||||
NBTTagEnd(0, ""), NBTTagByte(1, "BYTE"), NBTTagShort(2, "SHORT"), NBTTagInt(3, "INT"), NBTTagLong(4, "LONG"), NBTTagFloat(5, "FLOAT"), NBTTagDouble(6, "DOUBLE"),
|
||||
NBTTagByteArray(7, "BYTE[]"), NBTTagString(8, "STRING"), NBTTagList(9, "LIST"), NBTTagCompound(10, "COMPOUND"), NBTTagIntArray(11, "INT[]"), NBTTagLongArray(12, "LONG[]");
|
||||
|
||||
NBTType(int i) {
|
||||
NBTType(int i, String name) {
|
||||
id = i;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* @return Id used by Minecraft internally
|
||||
@@ -23,6 +25,13 @@ public enum NBTType {
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of the NBTType
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id Internal Minecraft id
|
||||
@@ -35,4 +44,11 @@ public enum NBTType {
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
|
||||
public static NBTType fromName(String name) {
|
||||
for (NBTType t : values())
|
||||
if (t.getName().equals(name))
|
||||
return t;
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -174,6 +174,15 @@ public interface ReadableNBT {
|
||||
*/
|
||||
Set<String> getKeys();
|
||||
|
||||
/**
|
||||
* Checks whether this NBT is empty (has no stored data).
|
||||
*
|
||||
* @return whether this NBT is empty
|
||||
*/
|
||||
default boolean isEmpty() {
|
||||
return getKeys().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The Compound instance or null
|
||||
@@ -274,7 +283,7 @@ public interface ReadableNBT {
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* @param key Path key, separated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* be escaped with a backslash.
|
||||
* @param type data type
|
||||
* @param <T> value type
|
||||
@@ -290,7 +299,7 @@ public interface ReadableNBT {
|
||||
* {@code Boolean, Byte, Short, Integer, Long, Float, Double, byte[], int[], long[]},
|
||||
* {@link String}, {@link UUID}, and {@link Enum}
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz".
|
||||
* @param key Path key, separated by '.'. For example: "foo.bar.baz".
|
||||
* Dots can be escaped with a backslash.
|
||||
* @param defaultValue default non-null value
|
||||
* @param <T> value type
|
||||
@@ -302,7 +311,7 @@ public interface ReadableNBT {
|
||||
* Returns the resolved Compound if exists, or null.
|
||||
* <p>
|
||||
*
|
||||
* @param key Path key, seperated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* @param key Path key, separated by '.'. For example: "foo.bar.baz". Dots can
|
||||
* be escaped with a backslash.
|
||||
* @return The resolved value if exists, or null.
|
||||
*/
|
||||
@@ -344,6 +353,17 @@ public interface ReadableNBT {
|
||||
*/
|
||||
void writeCompound(OutputStream stream);
|
||||
|
||||
/**
|
||||
* Compares this readable nbt to the other one
|
||||
* and returns the difference.
|
||||
* <p>Note: the result will only contain data that is present
|
||||
* in this readable nbt and is missing/different in the provided one.
|
||||
*
|
||||
* @param other other readable nbt
|
||||
* @return difference between readable nbts
|
||||
*/
|
||||
ReadWriteNBT extractDifference(ReadableNBT other);
|
||||
|
||||
/**
|
||||
* @return The NBT as printable NBT-Json.
|
||||
*/
|
||||
|
||||
@@ -5,42 +5,76 @@ import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NBTReflectionUtil;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
public class DataFixerUtil {
|
||||
|
||||
// these values can be found in DetectedVersion inside mc
|
||||
// Finding pre 1.12.2 values is left as an exercise for the reader,
|
||||
// as DetectedVersion is not a thing there
|
||||
// https://minecraft.wiki/w/Data_version
|
||||
public static final int VERSION1_12R1 = 1343;
|
||||
public static final int VERSION1_16R1 = 2586;
|
||||
public static final int VERSION1_17R1 = 2730;
|
||||
public static final int VERSION1_18R1 = 2975;
|
||||
public static final int VERSION1_19R1 = 3120;
|
||||
public static final int VERSION1_19R3 = 3337;
|
||||
public static final int VERSION1_20R1 = 3465;
|
||||
public static final int VERSION1_20R2 = 3578;
|
||||
public static final int VERSION1_20R3 = 3700;
|
||||
public static final int VERSION1_20R4 = 3837;
|
||||
public static final int VERSION1_21R1 = 3953;
|
||||
public static final int VERSION1_21R2 = 4080;
|
||||
public static final int VERSION1_21R3 = 4189;
|
||||
public static final int VERSION1_21R4 = 4323;
|
||||
public static final int VERSION1_21R5 = 4435;
|
||||
public static final int VERSION1_21R6 = 4554;
|
||||
public static final int VERSION1_21R7 = 4671;
|
||||
public static final int VERSION_26_1 = 4786;
|
||||
public static final int VERSION_26_2 = 4903;
|
||||
public static final int VERSION_26_3 = 5023;
|
||||
// There was a mixup between version numbers and revisions, kept for compatibility, use the above revision numbers
|
||||
@Deprecated
|
||||
public static final int VERSION1_12_2 = 1343;
|
||||
@Deprecated
|
||||
public static final int VERSION1_16_5 = 2586;
|
||||
@Deprecated
|
||||
public static final int VERSION1_17_1 = 2730;
|
||||
@Deprecated
|
||||
public static final int VERSION1_18_2 = 2975;
|
||||
@Deprecated
|
||||
public static final int VERSION1_19_2 = 3120;
|
||||
@Deprecated
|
||||
public static final int VERSION1_19_4 = 3337;
|
||||
@Deprecated
|
||||
public static final int VERSION1_20_1 = 3465;
|
||||
@Deprecated
|
||||
public static final int VERSION1_20_2 = 3578;
|
||||
@Deprecated
|
||||
public static final int VERSION1_20_4 = 3700;
|
||||
@Deprecated
|
||||
public static final int VERSION1_20_5 = 3837;
|
||||
// the 1.21 versions use Revision instead of patch versions, unlike the other above. kept for compatibility
|
||||
@Deprecated
|
||||
public static final int VERSION1_21 = 3953;
|
||||
@Deprecated
|
||||
public static final int VERSION1_21_2 = 4080;
|
||||
@Deprecated
|
||||
public static final int VERSION1_21_3 = 4189;
|
||||
@Deprecated
|
||||
public static final int VERSION1_21_4 = 4323;
|
||||
@Deprecated
|
||||
public static final int VERSION1_21_5 = 4435;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Object fixUpRawItemData(Object nbt, int fromVersion, int toVersion)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
DataFixer dataFixer = (DataFixer) ReflectionMethod.GET_DATAFIXER.run(null);
|
||||
TypeReference itemStackReference = (TypeReference) ClassWrapper.NMS_REFERENCES.getClazz()
|
||||
.getField(MojangToMapping.getMapping().get("net.minecraft.util.datafix.fixes.References#ITEM_STACK"))
|
||||
.get(null);
|
||||
DynamicOps<Object> nbtOps = (DynamicOps<Object>) ClassWrapper.NMS_NBTOPS.getClazz()
|
||||
.getField(MojangToMapping.getMapping().get("net.minecraft.nbt.NbtOps#INSTANCE")).get(null);
|
||||
TypeReference itemStackReference = (TypeReference) ReflectionUtil.getMappedField(ClassWrapper.NMS_REFERENCES.getClazz(),"net.minecraft.util.datafix.fixes.References#ITEM_STACK").get(null);
|
||||
DynamicOps<Object> nbtOps = (DynamicOps<Object>) ReflectionUtil.getMappedField(ClassWrapper.NMS_NBTOPS.getClazz(), "net.minecraft.nbt.NbtOps#INSTANCE").get(null);
|
||||
Dynamic<Object> fixed = dataFixer.update(itemStackReference, new Dynamic<Object>(nbtOps, nbt), fromVersion,
|
||||
toVersion);
|
||||
return fixed.getValue();
|
||||
@@ -48,9 +82,16 @@ public class DataFixerUtil {
|
||||
|
||||
public static ReadWriteNBT fixUpItemData(ReadWriteNBT nbt, int fromVersion, int toVersion)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
return new NBTContainer(fixUpRawItemData(
|
||||
ReadWriteNBT tag = NBT.wrapNMSTag(fixUpRawItemData(
|
||||
NBTReflectionUtil.getToCompount(((NBTCompound) nbt).getCompound(), ((NBTCompound) nbt)), fromVersion,
|
||||
toVersion));
|
||||
tag.setInteger("DataVersion", toVersion);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static ReadWriteNBT fixUpItemData(ReadWriteNBT nbt, int fromVersion)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
return fixUpItemData(nbt, fromVersion, getCurrentVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,28 +104,44 @@ public class DataFixerUtil {
|
||||
* @return
|
||||
*/
|
||||
public static int getCurrentVersion() {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R2)) {
|
||||
return VERSION1_21_2;
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_3)) {
|
||||
return VERSION_26_3;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_2)) {
|
||||
return VERSION_26_2;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC26_1)) {
|
||||
return VERSION_26_1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R7)) {
|
||||
return VERSION1_21R7;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
return VERSION1_21R6;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R5)) {
|
||||
return VERSION1_21R5;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
return VERSION1_21R4;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R3)) {
|
||||
return VERSION1_21R3;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R2)) {
|
||||
return VERSION1_21R2;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R1)) {
|
||||
return VERSION1_21;
|
||||
return VERSION1_21R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
return VERSION1_20_5;
|
||||
return VERSION1_20R4;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R3)) {
|
||||
return VERSION1_20_4;
|
||||
return VERSION1_20R3;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R1)) {
|
||||
return VERSION1_20_1;
|
||||
return VERSION1_20R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R3)) {
|
||||
return VERSION1_19_4;
|
||||
return VERSION1_19R3;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_19_R1)) {
|
||||
return VERSION1_19_2;
|
||||
return VERSION1_19R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
|
||||
return VERSION1_18_2;
|
||||
return VERSION1_18R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
return VERSION1_17_1;
|
||||
return VERSION1_17R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
return VERSION1_16_5;
|
||||
return VERSION1_16R1;
|
||||
} else if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)) {
|
||||
return VERSION1_12_2;
|
||||
return VERSION1_12R1;
|
||||
}
|
||||
throw new NbtApiException(
|
||||
"Trying to update data *to* a version before 1.12.2? Something is probably going wrong, contact the plugin author.");
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
@@ -14,15 +18,42 @@ import de.tr7zw.changeme.nbtapi.iface.ReadableNBTList;
|
||||
|
||||
public class GameprofileUtil {
|
||||
|
||||
private static Method GET_NAME = null;
|
||||
private static Method GET_ID = null;
|
||||
private static Method GET_VALUE = null;
|
||||
private static Method GET_SIGNATURE = null;
|
||||
private static Method GET_PROPERTY_NAME = null;
|
||||
private static Method GET_PROPERTIES = null;
|
||||
|
||||
static {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
GET_NAME = GameProfile.class.getDeclaredMethod("name");
|
||||
GET_ID = GameProfile.class.getDeclaredMethod("id");
|
||||
GET_VALUE = com.mojang.authlib.properties.Property.class.getDeclaredMethod("value");
|
||||
GET_SIGNATURE = com.mojang.authlib.properties.Property.class.getDeclaredMethod("signature");
|
||||
GET_PROPERTY_NAME = com.mojang.authlib.properties.Property.class.getDeclaredMethod("name");
|
||||
GET_PROPERTIES = GameProfile.class.getDeclaredMethod("properties");
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GameProfile readGameProfile(ReadableNBT arg) {
|
||||
String string = null;
|
||||
UUID uUID = null;
|
||||
if (arg.hasTag("Name") && arg.getType("Name") == NBTType.NBTTagString) {
|
||||
string = arg.getString("Name");
|
||||
} else if (arg.hasTag("name") && arg.getType("name") == NBTType.NBTTagString) {
|
||||
string = arg.getString("name");
|
||||
}
|
||||
if (arg.hasTag("Id") && arg.getType("Id") == NBTType.NBTTagIntArray && arg.getIntArray("Id").length == 4) {
|
||||
uUID = arg.getUUID("Id");
|
||||
} else if (arg.hasTag("id") && arg.getType("id") == NBTType.NBTTagIntArray
|
||||
&& arg.getIntArray("id").length == 4) {
|
||||
uUID = arg.getUUID("id");
|
||||
}
|
||||
try {
|
||||
GameProfile gameProfile = new GameProfile(uUID, string);
|
||||
@@ -43,6 +74,21 @@ public class GameprofileUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (arg.getType("properties") == NBTType.NBTTagList) { // 1.20.5+
|
||||
ReadableNBTList<ReadWriteNBT> listTag = arg.getCompoundList("properties");
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
ReadableNBT compoundTag2 = listTag.get(i);
|
||||
String string2 = compoundTag2.getString("name");
|
||||
String string3 = compoundTag2.getString("value");
|
||||
if (compoundTag2.hasTag("signature")
|
||||
&& compoundTag2.getType("signature") == NBTType.NBTTagString) {
|
||||
gameProfile.getProperties().put(string2, new com.mojang.authlib.properties.Property(string2,
|
||||
string3, compoundTag2.getString("signature")));
|
||||
} else {
|
||||
gameProfile.getProperties().put(string2,
|
||||
new com.mojang.authlib.properties.Property(string2, string3));
|
||||
}
|
||||
}
|
||||
}
|
||||
return gameProfile;
|
||||
} catch (Throwable var11) {
|
||||
@@ -51,26 +97,115 @@ public class GameprofileUtil {
|
||||
}
|
||||
|
||||
public static ReadWriteNBT writeGameProfile(ReadWriteNBT arg, GameProfile gameProfile) {
|
||||
if (!(gameProfile.getName() == null || gameProfile.getName().isEmpty())) {
|
||||
arg.setString("Name", gameProfile.getName());
|
||||
String name = getName(gameProfile);
|
||||
if (!(name == null || name.isEmpty())) {
|
||||
String nameKey = MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) ? "name" : "Name";
|
||||
arg.setString(nameKey, getName(gameProfile));
|
||||
}
|
||||
if (gameProfile.getId() != null) {
|
||||
arg.setUUID("Id", gameProfile.getId());
|
||||
UUID id = getId(gameProfile);
|
||||
if (id != null) {
|
||||
String idKey = MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) ? "id" : "Id";
|
||||
arg.setUUID(idKey, id);
|
||||
}
|
||||
if (!gameProfile.getProperties().isEmpty()) {
|
||||
ReadWriteNBT compoundTag = arg.getOrCreateCompound("Properties");
|
||||
for (String string : gameProfile.getProperties().keySet()) {
|
||||
ReadWriteNBTCompoundList list = compoundTag.getCompoundList(string);
|
||||
for (com.mojang.authlib.properties.Property property : gameProfile.getProperties().get(string)) {
|
||||
ReadWriteNBT tag = list.addCompound();
|
||||
tag.setString("Value", property.getValue());
|
||||
if (property.hasSignature()) {
|
||||
tag.setString("Signature", property.getSignature());
|
||||
PropertyMap properties = getProperties(gameProfile);
|
||||
if (!properties.isEmpty()) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
ReadWriteNBTCompoundList list = arg.getCompoundList("properties");
|
||||
for (com.mojang.authlib.properties.Property property : properties.values()) {
|
||||
ReadWriteNBT tag = list.addCompound();
|
||||
tag.setString("name", getPropertyName(property));
|
||||
tag.setString("value", getValue(property));
|
||||
if (property.hasSignature()) {
|
||||
tag.setString("signature", getSignature(property));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ReadWriteNBT compoundTag = arg.getOrCreateCompound("Properties");
|
||||
for (String string : gameProfile.getProperties().keySet()) {
|
||||
ReadWriteNBTCompoundList list = compoundTag.getCompoundList(string);
|
||||
for (com.mojang.authlib.properties.Property property : properties.get(string)) {
|
||||
ReadWriteNBT tag = list.addCompound();
|
||||
tag.setString("Value", getValue(property));
|
||||
if (property.hasSignature()) {
|
||||
tag.setString("Signature", getSignature(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static String getName(GameProfile profile) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return GET_NAME.invoke(profile).toString();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get GameProfile name via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return profile.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static UUID getId(GameProfile profile) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return (UUID) GET_ID.invoke(profile);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get GameProfile id via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return profile.getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getValue(com.mojang.authlib.properties.Property property) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return GET_VALUE.invoke(property).toString();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get Property value via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return property.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSignature(com.mojang.authlib.properties.Property property) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return GET_SIGNATURE.invoke(property).toString();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get Property signature via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return property.getSignature();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPropertyName(com.mojang.authlib.properties.Property property) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return GET_PROPERTY_NAME.invoke(property).toString();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get Property name via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return property.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static PropertyMap getProperties(GameProfile profile) {
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R6)) {
|
||||
try {
|
||||
return (PropertyMap) GET_PROPERTIES.invoke(profile);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new NbtApiException("Failed to get GameProfile properties via reflection", e);
|
||||
}
|
||||
} else {
|
||||
return profile.getProperties();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,881 +0,0 @@
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
*
|
||||
* IMPORTANT: You are not allowed to modify this class, except changing the package.
|
||||
*
|
||||
* Disallowed modifications include but are not limited to:
|
||||
* - Remove the option for users to opt-out
|
||||
* - Change the frequency for data submission
|
||||
* - Obfuscate the code (every obfuscator should allow you to make an exception for specific files)
|
||||
* - Reformat the code (if you use a linter, add an exception)
|
||||
*
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param plugin Your plugin instance.
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.isSet("serverUuid")) {
|
||||
config.addDefault("enabled", true);
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
config.addDefault("logFailedRequests", false);
|
||||
config.addDefault("logSentData", false);
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
// Inform the server owners about bStats
|
||||
config
|
||||
.options()
|
||||
.header(
|
||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
+ "anonymous.")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
boolean enabled = config.getBoolean("enabled", true);
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
||||
boolean logSentData = config.getBoolean("logSentData", false);
|
||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
metricsBase =
|
||||
new MetricsBase(
|
||||
"bukkit",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
plugin::isEnabled,
|
||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText);
|
||||
}
|
||||
|
||||
/** Shuts down the underlying scheduler service. */
|
||||
public void shutdown() {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
||||
builder.appendField("bukkitName", Bukkit.getName());
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
// Just use the new method if the reflection failed
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsBase {
|
||||
|
||||
/** The version of the Metrics class. */
|
||||
public static final String METRICS_VERSION = "3.0.2";
|
||||
|
||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private final String platform;
|
||||
|
||||
private final String serverUuid;
|
||||
|
||||
private final int serviceId;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
||||
|
||||
private final Consumer<Runnable> submitTaskConsumer;
|
||||
|
||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
||||
|
||||
private final BiConsumer<String, Throwable> errorLogger;
|
||||
|
||||
private final Consumer<String> infoLogger;
|
||||
|
||||
private final boolean logErrors;
|
||||
|
||||
private final boolean logSentData;
|
||||
|
||||
private final boolean logResponseStatusText;
|
||||
|
||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Creates a new MetricsBase class instance.
|
||||
*
|
||||
* @param platform The platform of the service.
|
||||
* @param serviceId The id of the service.
|
||||
* @param serverUuid The server uuid.
|
||||
* @param enabled Whether or not data sending is enabled.
|
||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all platform-specific data.
|
||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all service-specific data.
|
||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
||||
* concurrency. Can be {@code null}.
|
||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
||||
* @param errorLogger A consumer that accepts log message and an error.
|
||||
* @param infoLogger A consumer that accepts info log messages.
|
||||
* @param logErrors Whether or not errors should be logged.
|
||||
* @param logSentData Whether or not the sent data should be logged.
|
||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
||||
*/
|
||||
public MetricsBase(
|
||||
String platform,
|
||||
String serverUuid,
|
||||
int serviceId,
|
||||
boolean enabled,
|
||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
||||
Consumer<Runnable> submitTaskConsumer,
|
||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
||||
BiConsumer<String, Throwable> errorLogger,
|
||||
Consumer<String> infoLogger,
|
||||
boolean logErrors,
|
||||
boolean logSentData,
|
||||
boolean logResponseStatusText) {
|
||||
ScheduledThreadPoolExecutor scheduler =
|
||||
new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics"));
|
||||
// We want delayed tasks (non-periodic) that will execute in the future to be
|
||||
// cancelled when the scheduler is shutdown.
|
||||
// Otherwise, we risk preventing the server from shutting down even when
|
||||
// MetricsBase#shutdown() is called
|
||||
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
this.scheduler = scheduler;
|
||||
this.platform = platform;
|
||||
this.serverUuid = serverUuid;
|
||||
this.serviceId = serviceId;
|
||||
this.enabled = enabled;
|
||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
||||
this.submitTaskConsumer = submitTaskConsumer;
|
||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
||||
this.errorLogger = errorLogger;
|
||||
this.infoLogger = infoLogger;
|
||||
this.logErrors = logErrors;
|
||||
this.logSentData = logSentData;
|
||||
this.logResponseStatusText = logResponseStatusText;
|
||||
checkRelocation();
|
||||
if (enabled) {
|
||||
// WARNING: Removing the option to opt-out will get your plugin banned from
|
||||
// bStats
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
this.customCharts.add(chart);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
final Runnable submitTask =
|
||||
() -> {
|
||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
||||
// Submitting data or service is disabled
|
||||
scheduler.shutdown();
|
||||
return;
|
||||
}
|
||||
if (submitTaskConsumer != null) {
|
||||
submitTaskConsumer.accept(this::submitData);
|
||||
} else {
|
||||
this.submitData();
|
||||
}
|
||||
};
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven
|
||||
// distribution of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into
|
||||
// the initial and second delay.
|
||||
// WARNING: You must not modify and part of this Metrics class, including the
|
||||
// submit delay or frequency!
|
||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just
|
||||
// don't do it!
|
||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
scheduler.scheduleAtFixedRate(
|
||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
||||
JsonObjectBuilder.JsonObject[] chartData =
|
||||
customCharts.stream()
|
||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
||||
serviceJsonBuilder.appendField("id", serviceId);
|
||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
||||
scheduler.execute(
|
||||
() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
||||
if (logSentData) {
|
||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
||||
}
|
||||
String url = String.format(REPORT_URL, platform);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks that the class was properly relocated. */
|
||||
private void checkRelocation() {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null
|
||||
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D
|
||||
final String defaultPackage =
|
||||
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
|
||||
final String examplePackage =
|
||||
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure no one just copy & pastes the example and uses the wrong
|
||||
// package names
|
||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given string.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped string.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class CustomChart {
|
||||
|
||||
private final String chartId;
|
||||
|
||||
protected CustomChart(String chartId) {
|
||||
if (chartId == null) {
|
||||
throw new IllegalArgumentException("chartId must not be null");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
||||
builder.appendField("chartId", chartId);
|
||||
try {
|
||||
JsonObjectBuilder.JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
builder.appendField("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
||||
}
|
||||
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extremely simple JSON builder.
|
||||
*
|
||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
||||
* for its use-case.
|
||||
*/
|
||||
public static class JsonObjectBuilder {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean hasAtLeastOneField = false;
|
||||
|
||||
public JsonObjectBuilder() {
|
||||
builder.append("{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a null field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendNull(String key) {
|
||||
appendFieldUnescaped(key, "null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("JSON value must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int value) {
|
||||
appendFieldUnescaped(key, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param object The object.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("JSON object must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, object.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The string array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values)
|
||||
.map(value -> "\"" + escape(value) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a field to the object.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param escapedValue The escaped value of the field.
|
||||
*/
|
||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("JSON key must not be null");
|
||||
}
|
||||
if (hasAtLeastOneField) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
||||
hasAtLeastOneField = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JSON string and invalidates this builder.
|
||||
*
|
||||
* @return The built JSON string.
|
||||
*/
|
||||
public JsonObject build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
||||
builder = null;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
||||
*
|
||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
||||
*
|
||||
* @param value The value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
private static String escape(String value) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '"') {
|
||||
builder.append("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.append("\\\\");
|
||||
} else if (c <= '\u000F') {
|
||||
builder.append("\\u000").append(Integer.toHexString(c));
|
||||
} else if (c <= '\u001F') {
|
||||
builder.append("\\u00").append(Integer.toHexString(c));
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple representation of a JSON object.
|
||||
*
|
||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
||||
* JsonObject)}.
|
||||
*/
|
||||
public static class JsonObject {
|
||||
|
||||
private final String value;
|
||||
|
||||
private JsonObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,20 @@ package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.DrilldownPie;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
|
||||
* other classes,registers bStats and checks rather Maven shading was done
|
||||
@@ -24,11 +31,15 @@ public enum MinecraftVersion {
|
||||
MC1_13_R1(1131), MC1_13_R2(1132), MC1_14_R1(1141), MC1_15_R1(1151), MC1_16_R1(1161), MC1_16_R2(1162),
|
||||
MC1_16_R3(1163), MC1_17_R1(1171), MC1_18_R1(1181, true), MC1_18_R2(1182, true), MC1_19_R1(1191, true),
|
||||
MC1_19_R2(1192, true), MC1_19_R3(1193, true), MC1_20_R1(1201, true), MC1_20_R2(1202, true), MC1_20_R3(1203, true),
|
||||
MC1_20_R4(1204, true), MC1_21_R1(1211, true), MC1_21_R2(1212, true);
|
||||
MC1_20_R4(1204, true), MC1_21_R1(1211, true), MC1_21_R2(1212, true), MC1_21_R3(1213, true), MC1_21_R4(1214, true),
|
||||
MC1_21_R5(1215, true), MC1_21_R6(1216, true), MC1_21_R7(1217, true), MC26_1(260100, true), MC26_2(260200, true),
|
||||
MC26_3(260300, true);
|
||||
|
||||
private static MinecraftVersion version;
|
||||
private static Boolean hasGsonSupport;
|
||||
private static Boolean isForgePresent;
|
||||
private static Boolean isNeoForgePresent;
|
||||
private static Boolean isFabricPresent;
|
||||
private static Boolean isFoliaPresent;
|
||||
private static boolean bStatsDisabled = false;
|
||||
private static boolean disablePackageWarning = false;
|
||||
@@ -39,13 +50,12 @@ public enum MinecraftVersion {
|
||||
private static Logger logger = Logger.getLogger("NBTAPI");
|
||||
|
||||
// NBT-API Version
|
||||
protected static final String VERSION = "2.14.0";
|
||||
protected static final String VERSION = "2.16.1";
|
||||
|
||||
private final int versionId;
|
||||
private final boolean mojangMapping;
|
||||
|
||||
// TODO: not nice
|
||||
@SuppressWarnings("serial")
|
||||
private static final Map<String, MinecraftVersion> VERSION_TO_REVISION = new HashMap<String, MinecraftVersion>() {
|
||||
{
|
||||
this.put("1.20", MC1_20_R1);
|
||||
@@ -59,6 +69,17 @@ public enum MinecraftVersion {
|
||||
this.put("1.21.1", MC1_21_R1);
|
||||
this.put("1.21.2", MC1_21_R2);
|
||||
this.put("1.21.3", MC1_21_R2);
|
||||
this.put("1.21.4", MC1_21_R3);
|
||||
this.put("1.21.5", MC1_21_R4);
|
||||
this.put("1.21.6", MC1_21_R5);
|
||||
this.put("1.21.7", MC1_21_R5);
|
||||
this.put("1.21.8", MC1_21_R5);
|
||||
this.put("1.21.9", MC1_21_R6);
|
||||
this.put("1.21.10", MC1_21_R6);
|
||||
this.put("1.21.11", MC1_21_R7);
|
||||
this.put("26.1", MC26_1);
|
||||
this.put("26.2", MC26_2);
|
||||
this.put("26.3", MC26_3);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,8 +161,22 @@ public enum MinecraftVersion {
|
||||
} catch (Exception ex) {
|
||||
logger.info("[NBTAPI] Found Minecraft: " + Bukkit.getServer().getBukkitVersion().split("-")[0]
|
||||
+ "! Trying to find NMS support");
|
||||
version = VERSION_TO_REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0],
|
||||
MinecraftVersion.UNKNOWN);
|
||||
version = VERSION_TO_REVISION.get(Bukkit.getServer().getBukkitVersion().split("-")[0]);
|
||||
if (version == null) {
|
||||
// check for modern versions with the new versioning scheme, also the new paper version format
|
||||
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0].split(".build")[0];
|
||||
for (Entry<String, MinecraftVersion> entry : VERSION_TO_REVISION.entrySet()) {
|
||||
if (versionString.startsWith(entry.getKey()) && version == null) {
|
||||
version = entry.getValue();
|
||||
// pick the highest revision that matches the version string, in case 26.1.3 is somehow different to 26.1
|
||||
} else if (versionString.startsWith(entry.getKey()) && entry.getValue().getVersionId() > version.getVersionId()) {
|
||||
version = entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (version == null) {
|
||||
version = UNKNOWN;
|
||||
}
|
||||
}
|
||||
if (version != UNKNOWN) {
|
||||
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
|
||||
@@ -160,13 +195,70 @@ public enum MinecraftVersion {
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D (from bStats)
|
||||
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
|
||||
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
final String reservedPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'n', 'b',
|
||||
't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
try {
|
||||
if (hasGsonSupport() && !bStatsDisabled) {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(VersionChecker.getPlugin());
|
||||
if (plugin != null && plugin instanceof JavaPlugin) {
|
||||
getLogger()
|
||||
.info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
new Metrics((JavaPlugin) plugin, 1058);
|
||||
Metrics metrics = new Metrics((JavaPlugin) plugin, 1058);
|
||||
metrics.addCustomChart(new SimplePie("nbtapi_version", () -> {
|
||||
return VERSION;
|
||||
}));
|
||||
metrics.addCustomChart(new DrilldownPie("nms_version", () -> {
|
||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
Map<String, Integer> entry = new HashMap<>();
|
||||
entry.put(Bukkit.getName(), 1);
|
||||
map.put(getVersion().name(), entry);
|
||||
return map;
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("shaded", () -> {
|
||||
return Boolean.toString(!"NBTAPI".equals(VersionChecker.getPlugin()));
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("server_software", () -> {
|
||||
return Bukkit.getName();
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("parent_plugin", () -> {
|
||||
return VersionChecker.getPluginforBStats();
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("parent_plugin_type", () -> {
|
||||
return VersionChecker.getPluginType();
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("special_environment", () -> {
|
||||
if (isFoliaPresent()) {
|
||||
return "Folia";
|
||||
} else if (isForgePresent()) {
|
||||
return "Forge";
|
||||
} else if (isFabricPresent()) {
|
||||
return "Fabric";
|
||||
} else if (isNeoForgePresent()) {
|
||||
return "NeoForge";
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
}));
|
||||
metrics.addCustomChart(new SimplePie("bindings_check", () -> {
|
||||
|
||||
boolean failedBinding = false;
|
||||
for (ClassWrapper c : ClassWrapper.values()) {
|
||||
if (c.isEnabled() && c.getClazz() == null) {
|
||||
failedBinding = true;
|
||||
}
|
||||
}
|
||||
for (ReflectionMethod method : ReflectionMethod.values()) {
|
||||
if (method.isCompatible() && !method.isLoaded()) {
|
||||
failedBinding = true;
|
||||
}
|
||||
}
|
||||
|
||||
return failedBinding ? "Failed" : "Pass";
|
||||
}));
|
||||
} else if (plugin == null) {
|
||||
getLogger().info("[NBTAPI] Unable to create a bStats instance!!");
|
||||
}
|
||||
@@ -183,12 +275,6 @@ public enum MinecraftVersion {
|
||||
logger.log(Level.WARNING, "[NBTAPI] Error while checking for updates! Error: " + ex.getMessage());
|
||||
}
|
||||
}).start();
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D (from bStats)
|
||||
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
|
||||
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
final String reservedPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'n', 'b',
|
||||
't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
if (!disablePackageWarning && MinecraftVersion.class.getPackage().getName().equals(defaultPackage)) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
@@ -248,6 +334,22 @@ public enum MinecraftVersion {
|
||||
return hasGsonSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if Fabric is present
|
||||
*/
|
||||
public static boolean isFabricPresent() {
|
||||
if (isFabricPresent != null) {
|
||||
return isFabricPresent;
|
||||
}
|
||||
try {
|
||||
logger.info("[NBTAPI] Found Fabric: " + Class.forName("net.fabricmc.api.ModInitializer"));
|
||||
isFabricPresent = true;
|
||||
} catch (Exception ex) {
|
||||
isFabricPresent = false;
|
||||
}
|
||||
return isFabricPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if Forge is present
|
||||
*/
|
||||
@@ -265,6 +367,22 @@ public enum MinecraftVersion {
|
||||
}
|
||||
return isForgePresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if NeoForge is present
|
||||
*/
|
||||
public static boolean isNeoForgePresent() {
|
||||
if (isNeoForgePresent != null) {
|
||||
return isNeoForgePresent;
|
||||
}
|
||||
try {
|
||||
logger.info("[NBTAPI] Found NeoForge: " + Class.forName("net.neoforged.neoforge.common.NeoForge"));
|
||||
isNeoForgePresent = true;
|
||||
} catch (Exception ex) {
|
||||
isNeoForgePresent = false;
|
||||
}
|
||||
return isNeoForgePresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if Folia is present
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ClassWrapper;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
public class NBTJsonUtil {
|
||||
|
||||
/**
|
||||
* 1.20.3-1.21.4 only. Used to convert items into Json, used in Chat Hover Components.
|
||||
*
|
||||
* @param itemStack
|
||||
* @return
|
||||
* @throws NbtApiException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JsonElement itemStackToJson(ItemStack itemStack) {
|
||||
try {
|
||||
Codec<Object> itemStackCodec = (Codec<Object>) ClassWrapper.NMS_ITEMSTACK.getClazz()
|
||||
.getField(MojangToMapping.getMapping().get("net.minecraft.world.item.ItemStack#CODEC")).get(null);
|
||||
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, itemStack);
|
||||
DataResult<JsonElement> result = itemStackCodec.encode(stack, JsonOps.INSTANCE,
|
||||
JsonOps.INSTANCE.emptyMap());
|
||||
Optional<JsonElement> opt = (Optional<JsonElement>) result.getClass().getMethod("result").invoke(result);
|
||||
return opt.orElse(null);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Error trying to get Json of an ItemStack.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +1,24 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.nmsmappings.MojangToMapping;
|
||||
|
||||
public final class ReflectionUtil {
|
||||
|
||||
private static Field field_modifiers;
|
||||
|
||||
static {
|
||||
|
||||
public static Field getMappedField(Class<?> clazz, String mapping) {
|
||||
String mojmapName = mapping.split("#")[1];
|
||||
try {
|
||||
field_modifiers = Field.class.getDeclaredField("modifiers");
|
||||
field_modifiers.setAccessible(true);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
try {
|
||||
// This hacky workaround is for newer jdk versions 11+?
|
||||
Method fieldGetter = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
|
||||
fieldGetter.setAccessible(true);
|
||||
Field[] fields = (Field[]) fieldGetter.invoke(Field.class, false);
|
||||
for (Field f : fields)
|
||||
if (f.getName().equals("modifiers")) {
|
||||
field_modifiers = f;
|
||||
field_modifiers.setAccessible(true);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException(e);
|
||||
}
|
||||
return clazz.getField(mojmapName);
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
// not Mojamp, try remapped
|
||||
}
|
||||
if (field_modifiers == null) {
|
||||
throw new NbtApiException("Unable to init the modifiers Field.");
|
||||
try {
|
||||
return clazz.getDeclaredField(MojangToMapping.getMapping().get(mapping));
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Unable to find field " + mapping + " in class " + clazz.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field makeNonFinal(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
int mods = field.getModifiers();
|
||||
if (Modifier.isFinal(mods)) {
|
||||
field_modifiers.set(field, mods & ~Modifier.FINAL);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public static void setFinal(Object obj, Field field, Object newValue)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
field.setAccessible(true);
|
||||
field = makeNonFinal(field);
|
||||
field.set(obj, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -109,7 +109,20 @@ public class VersionChecker {
|
||||
|
||||
protected static String getPlugin() {
|
||||
ClassLoader classLoader = VersionChecker.class.getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream("plugin.yml");
|
||||
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
|
||||
if (inputStream != null) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
|
||||
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
|
||||
return pluginYml.getString("name");
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This can happen if the paper-plugin.yml is not formatted correctly
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading paper-plugin.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
inputStream = classLoader.getResourceAsStream("plugin.yml");
|
||||
|
||||
if (inputStream != null) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
|
||||
@@ -117,9 +130,67 @@ public class VersionChecker {
|
||||
return pluginYml.getString("name");
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This can happen if the plugin.yml is not formatted correctly
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading plugin.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return NBTItem.class.getPackage().getName();
|
||||
}
|
||||
|
||||
protected static String getPluginforBStats() {
|
||||
ClassLoader classLoader = VersionChecker.class.getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
|
||||
if (inputStream != null) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
|
||||
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
|
||||
return pluginYml.getString("name");
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This can happen if the paper-plugin.yml is not formatted correctly
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading paper-plugin.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
inputStream = classLoader.getResourceAsStream("plugin.yml");
|
||||
|
||||
if (inputStream != null) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream)) {
|
||||
YamlConfiguration pluginYml = YamlConfiguration.loadConfiguration(reader);
|
||||
return pluginYml.getString("name");
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This can happen if the plugin.yml is not formatted correctly
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Error reading plugin.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return "UnknownPlugin";
|
||||
}
|
||||
|
||||
protected static String getPluginType() {
|
||||
ClassLoader classLoader = VersionChecker.class.getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream("paper-plugin.yml");
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
}
|
||||
return "PaperPlugin";
|
||||
}
|
||||
|
||||
inputStream = classLoader.getResourceAsStream("plugin.yml");
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
}
|
||||
return "SpigotPlugin";
|
||||
}
|
||||
return "UnknownPlugin";
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -24,6 +24,7 @@ public enum ClassWrapper {
|
||||
CRAFT_PERSISTENTDATACONTAINER(PackageWrapper.CRAFTBUKKIT, "persistence.CraftPersistentDataContainer",
|
||||
MinecraftVersion.MC1_14_R1, null),
|
||||
NMS_NBTBASE(PackageWrapper.NMS, "NBTBase", null, null, "net.minecraft.nbt", "net.minecraft.nbt.Tag"),
|
||||
NMS_TAGTYPE(PackageWrapper.NMS, "NBTTagType", MinecraftVersion.MC1_21_R4, null, "net.minecraft.nbt", "net.minecraft.nbt.TagType"),
|
||||
NMS_NBTTAGSTRING(PackageWrapper.NMS, "NBTTagString", null, null, "net.minecraft.nbt",
|
||||
"net.minecraft.nbt.StringTag"),
|
||||
NMS_NBTTAGINT(PackageWrapper.NMS, "NBTTagInt", null, null, "net.minecraft.nbt", "net.minecraft.nbt.IntTag"),
|
||||
@@ -85,6 +86,18 @@ public enum ClassWrapper {
|
||||
"net.minecraft.util.datafix.fixes", "net.minecraft.util.datafix.fixes.References"),
|
||||
NMS_NBTOPS(PackageWrapper.NMS, "DynamicOpsNBT", MinecraftVersion.MC1_20_R4, null,
|
||||
"net.minecraft.nbt", "net.minecraft.nbt.NbtOps"),
|
||||
NMS_PROBLEM_REPORTER(PackageWrapper.NMS, "ProblemReporter", MinecraftVersion.MC1_21_R5, null,
|
||||
"net.minecraft.util", "net.minecraft.util.ProblemReporter"),
|
||||
NMS_TAG_VALUE_INPUT(PackageWrapper.NMS, "TagValueInput", MinecraftVersion.MC1_21_R5, null,
|
||||
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.TagValueInput"),
|
||||
NMS_VALUE_INPUT(PackageWrapper.NMS, "ValueInput", MinecraftVersion.MC1_21_R5, null,
|
||||
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.ValueInput"),
|
||||
NMS_TAG_VALUE_OUTPUT(PackageWrapper.NMS, "TagValueOutput", MinecraftVersion.MC1_21_R5, null,
|
||||
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.TagValueOutput"),
|
||||
NMS_VALUE_OUTPUT(PackageWrapper.NMS, "ValueOutput", MinecraftVersion.MC1_21_R5, null,
|
||||
"net.minecraft.world.level.storage", "net.minecraft.world.level.storage.ValueOutput"),
|
||||
NMS_DYNAMICOPS(PackageWrapper.NONE, "DynamicOps", MinecraftVersion.MC1_21_R5, null,
|
||||
"com.mojang.serialization", "com.mojang.serialization.DynamicOps"),
|
||||
GAMEPROFILE(PackageWrapper.NONE, "com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3, null);
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils.nmsmappings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTReflectionUtil;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
|
||||
public class CodecHelper {
|
||||
|
||||
public static Object convertItemStackToNbt(Object itemStack) {
|
||||
Object result = null;
|
||||
try {
|
||||
// FIXME caching, this has 0 exception handling
|
||||
result = NBTReflectionUtil.itemstack_codec.encodeStart(NBTReflectionUtil.nbtRegistryOps, itemStack);
|
||||
Objects.requireNonNull(result);
|
||||
return ((Optional<Object>)result.getClass().getMethod("result").invoke(result)).get();
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Failed to convert ItemStack to NBT. " + result + " " + itemStack, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object convertNbtToItemStack(Object nbt) {
|
||||
Object result = null;
|
||||
try {
|
||||
// FIXME caching, this has 0 exception handling
|
||||
result = NBTReflectionUtil.itemstack_codec.parse(NBTReflectionUtil.nbtRegistryOps, nbt);
|
||||
Objects.requireNonNull(result);
|
||||
return ((Optional<Object>)result.getClass().getMethod("result").invoke(result)).get();
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Failed to convert NBT to ItemStack. " + result + " " + nbt, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -56,8 +56,8 @@ public class Forge1710Mappings {
|
||||
methodMap.put("COMPOUND_MERGE", "merge");// Only present on Crucible
|
||||
methodMap.put("COMPOUND_SET", "func_74782_a");
|
||||
methodMap.put("COMPOUND_GET", "func_74781_a");
|
||||
methodMap.put("COMPOUND_GET_LIST", "func_150295_c");
|
||||
methodMap.put("COMPOUND_OWN_TYPE", "func_74732_a");
|
||||
methodMap.put("COMPOUND_GET_LIST_LEGACY", "func_150295_c");
|
||||
methodMap.put("COMPOUND_OWN_TYPE_LEGACY", "func_74732_a");
|
||||
methodMap.put("COMPOUND_GET_FLOAT", "func_74760_g");
|
||||
methodMap.put("COMPOUND_GET_STRING", "func_74779_i");
|
||||
methodMap.put("COMPOUND_GET_INT", "func_74762_e");
|
||||
|
||||
+135
-2
@@ -1,5 +1,6 @@
|
||||
package de.tr7zw.changeme.nbtapi.utils.nmsmappings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -145,6 +146,7 @@ public class MojangToMapping {
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bw");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId()", "p");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "r");
|
||||
put("net.minecraft.world.item.ItemStack#CODEC", "a");
|
||||
}
|
||||
|
||||
};
|
||||
@@ -169,6 +171,7 @@ public class MojangToMapping {
|
||||
put("net.minecraft.util.datafix.DataFixers#getDataFixer()", "a");
|
||||
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "t");
|
||||
put("net.minecraft.nbt.NbtOps#INSTANCE", "a");
|
||||
put("net.minecraft.world.item.ItemStack#CODEC", "b");
|
||||
}
|
||||
|
||||
};
|
||||
@@ -193,14 +196,144 @@ public class MojangToMapping {
|
||||
put("net.minecraft.server.MinecraftServer#registryAccess()", "ba");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bK");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "m");
|
||||
put("net.minecraft.world.item.ItemStack#CODEC", "a");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R3 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_21R2);
|
||||
|
||||
put("net.minecraft.world.item.component.CustomData#copyTag()", "d");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R4 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_21R3);
|
||||
|
||||
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "i");
|
||||
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "b");
|
||||
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "h");
|
||||
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "j");
|
||||
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "g");
|
||||
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "f");
|
||||
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "l");
|
||||
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "e");
|
||||
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "k");
|
||||
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "m");
|
||||
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "c");
|
||||
put("net.minecraft.nbt.ListTag#getString(int)", "m");
|
||||
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.nbt.CompoundTag)", "h");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bI");
|
||||
put("net.minecraft.world.entity.Entity#load(net.minecraft.nbt.CompoundTag)", "i");
|
||||
put("net.minecraft.nbt.Tag#getType()", "c");
|
||||
put("net.minecraft.nbt.TagType#getName()", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String)", "o");
|
||||
put("net.minecraft.world.item.ItemStack#parse(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.Tag)", "a");
|
||||
put("net.minecraft.world.item.ItemStack#CODEC", "b");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R5 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_21R3);
|
||||
put("net.minecraft.core.HolderLookup$Provider#createSerializationContext(com.mojang.serialization.DynamicOps)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "j");
|
||||
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "h");
|
||||
put("net.minecraft.nbt.CompoundTag#keySet()", "e");
|
||||
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "f");
|
||||
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String)", "o");
|
||||
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "e");
|
||||
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "m");
|
||||
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "c");
|
||||
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "k");
|
||||
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "i");
|
||||
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "g");
|
||||
put("net.minecraft.nbt.CompoundTag#getLongArray(java.lang.String)", "l");
|
||||
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "b");
|
||||
put("net.minecraft.nbt.ListTag#getString(int)", "m");
|
||||
put("net.minecraft.nbt.Tag#getType()", "c");
|
||||
put("net.minecraft.nbt.TagParser#parseCompoundFully(java.lang.String)", "a");
|
||||
put("net.minecraft.nbt.TagType#getName()", "a");
|
||||
put("net.minecraft.world.entity.Entity#load(net.minecraft.world.level.storage.ValueInput)", "e");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bN");
|
||||
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.world.level.storage.ValueOutput)", "d");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId(net.minecraft.world.level.storage.ValueOutput)", "d");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#loadWithComponents(net.minecraft.world.level.storage.ValueInput)", "b");
|
||||
put("net.minecraft.world.level.storage.TagValueInput#create(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)", "a");
|
||||
put("net.minecraft.world.level.storage.TagValueOutput#createWithContext(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider)", "a");
|
||||
put("net.minecraft.world.level.storage.TagValueOutput#buildResult()", "b");
|
||||
put("net.minecraft.world.item.ItemStack#CODEC", "b");
|
||||
put("net.minecraft.util.ProblemReporter#DISCARDING", "a");
|
||||
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "u");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R6 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_21R5);
|
||||
|
||||
put("net.minecraft.server.MinecraftServer#registryAccess()", "bg");
|
||||
put("net.minecraft.world.entity.Entity#load(net.minecraft.world.level.storage.ValueInput)", "d");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bW");
|
||||
put("net.minecraft.world.item.component.CustomData#copyTag()", "b");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "o");
|
||||
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "v");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_21R7 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
putAll(MC1_21R6);
|
||||
|
||||
put("net.minecraft.nbt.NbtAccounter#unlimitedHeap()", "c");
|
||||
put("net.minecraft.server.MinecraftServer#registryAccess()", "bc");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "ca");
|
||||
put("net.minecraft.util.datafix.fixes.References#ITEM_STACK", "x");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
public static Map<String, String> getMapping() {
|
||||
// No mappings after 26.1
|
||||
if(MinecraftVersion.getVersion() != MinecraftVersion.UNKNOWN && MinecraftVersion.getVersion().isAtLeastVersion(MinecraftVersion.MC26_1)) {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
switch (MinecraftVersion.getVersion()) {
|
||||
case MC1_21_R7:
|
||||
return MC1_21R7;
|
||||
case MC1_21_R6:
|
||||
return MC1_21R6;
|
||||
case MC1_21_R5:
|
||||
return MC1_21R5;
|
||||
case MC1_21_R4:
|
||||
return MC1_21R4;
|
||||
case MC1_21_R3:
|
||||
return MC1_21R3;
|
||||
case MC1_21_R2:
|
||||
return MC1_21R2;
|
||||
case MC1_21_R1:
|
||||
@@ -224,7 +357,7 @@ public class MojangToMapping {
|
||||
case MC1_18_R1:
|
||||
return MC1_18R1;
|
||||
case UNKNOWN:
|
||||
return MC1_20R2; // assume it's a future version, so try the latest known mappings
|
||||
return MC1_21R7; // assume it's a future version, so try the latest known mappings
|
||||
default:
|
||||
// this should never happen, unless a version is forgotten here(like 1.19R3 which uses the 1.19R2 mappings)
|
||||
throw new NbtApiException("No fitting mapping found for version " + MinecraftVersion.getVersion() + ". This is a bug!");
|
||||
|
||||
+63
-20
@@ -5,6 +5,8 @@ import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
@@ -54,7 +56,7 @@ public enum ReflectionMethod {
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putBoolean(java.lang.String,boolean)")),
|
||||
COMPOUND_SET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, UUID.class },
|
||||
MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"),
|
||||
MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_16_R1, "a"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "putUUID(java.lang.String,java.util.UUID)")),
|
||||
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"),
|
||||
@@ -65,12 +67,18 @@ public enum ReflectionMethod {
|
||||
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "get"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "get(java.lang.String)")),
|
||||
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int.class },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList"),
|
||||
COMPOUND_GET_LIST_LEGACY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class, int.class },
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_7_R4, "getList"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getList(java.lang.String,int)")),
|
||||
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_21_R4,
|
||||
new Since(MinecraftVersion.MC1_21_R4, "getList(java.lang.String)")),
|
||||
// Only needed for 1.7.10 getType
|
||||
COMPOUND_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4,
|
||||
COMPOUND_OWN_TYPE_LEGACY(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getTypeId")),
|
||||
TAGTYPE_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[] {}, MinecraftVersion.MC1_21_R4,
|
||||
new Since(MinecraftVersion.MC1_21_R4, "getType()")),
|
||||
TAGTYPE_GET_NAME(ClassWrapper.NMS_TAGTYPE, new Class[] {}, MinecraftVersion.MC1_21_R4,
|
||||
new Since(MinecraftVersion.MC1_21_R4, "getName()")),
|
||||
|
||||
COMPOUND_GET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getFloat"),
|
||||
@@ -105,13 +113,14 @@ public enum ReflectionMethod {
|
||||
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getBoolean"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getBoolean(java.lang.String)")),
|
||||
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1,
|
||||
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_21_R3,
|
||||
new Since(MinecraftVersion.MC1_16_R1, "a"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getUUID(java.lang.String)")),
|
||||
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getCompound"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getCompound(java.lang.String)")),
|
||||
|
||||
|
||||
|
||||
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK, new Class[] {}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_20_R3,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getTag"), new Since(MinecraftVersion.MC1_18_R1, "getTag()")),
|
||||
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
@@ -126,17 +135,17 @@ public enum ReflectionMethod {
|
||||
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "hasKey"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "contains(java.lang.String)")),
|
||||
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_8_R3,
|
||||
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] { String.class }, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_21_R4,
|
||||
new Since(MinecraftVersion.MC1_8_R3, "b"), new Since(MinecraftVersion.MC1_9_R1, "d"),
|
||||
new Since(MinecraftVersion.MC1_15_R1, "e"), new Since(MinecraftVersion.MC1_16_R1, "d"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getTagType(java.lang.String)")),
|
||||
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()"), new Since(MinecraftVersion.MC1_21_R5, "keySet()")),
|
||||
// FIXME ?!?
|
||||
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
|
||||
// LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[] {}, MinecraftVersion.MC1_7_R4,
|
||||
// new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"),
|
||||
// new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
|
||||
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST, new Class[] { int.class }, MinecraftVersion.MC1_8_R3,
|
||||
new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "remove(int)")),
|
||||
@@ -166,7 +175,8 @@ public enum ReflectionMethod {
|
||||
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK, new Class[] { ItemStack.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
|
||||
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK, new Class[] { ClassWrapper.NMS_ITEMSTACK.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
|
||||
MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, resolveItemStackBukkitMirrorMethodName())),
|
||||
|
||||
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD, new Class[] {}, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
|
||||
@@ -219,11 +229,11 @@ public enum ReflectionMethod {
|
||||
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY, new Class[] {}, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
|
||||
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "f"),
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_7_R4, "f"),
|
||||
new Since(MinecraftVersion.MC1_16_R1, "load"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
|
||||
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "e"),
|
||||
MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_7_R4, "e"),
|
||||
new Since(MinecraftVersion.MC1_12_R1, "save"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "saveWithoutId(net.minecraft.nbt.CompoundTag)")),
|
||||
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY, new Class[] {}, MinecraftVersion.MC1_14_R1,
|
||||
@@ -251,7 +261,7 @@ public enum ReflectionMethod {
|
||||
|
||||
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER, new Class[] { String.class }, MinecraftVersion.MC1_7_R4,
|
||||
new Since(MinecraftVersion.MC1_7_R4, "parse"),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)")),
|
||||
new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)"), new Since(MinecraftVersion.MC1_21_R5, "parseCompoundFully(java.lang.String)")),
|
||||
REGISTRY_KEYSET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] {}, MinecraftVersion.MC1_11_R1,
|
||||
MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
|
||||
REGISTRY_GET(ClassWrapper.NMS_REGISTRYSIMPLE, new Class[] { Object.class }, MinecraftVersion.MC1_11_R1,
|
||||
@@ -292,19 +302,41 @@ public enum ReflectionMethod {
|
||||
NMSITEM_SET(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_DATACOMPONENTTYPE.getClazz(), Object.class}, MinecraftVersion.MC1_20_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "set(net.minecraft.core.component.DataComponentType,java.lang.Object)")),
|
||||
NMSITEM_SAVE_MODERN(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz() },
|
||||
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "save(net.minecraft.core.HolderLookup$Provider)")),
|
||||
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "save(net.minecraft.core.HolderLookup$Provider)")),
|
||||
NMSITEM_LOAD(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz() },
|
||||
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "parseOptional(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
|
||||
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R3, new Since(MinecraftVersion.MC1_20_R4, "parseOptional(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
|
||||
NMSITEM_LOAD_MODERN(ClassWrapper.NMS_ITEMSTACK, new Class[] { ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTBASE.getClazz() },
|
||||
MinecraftVersion.MC1_21_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "parse(net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.Tag)")),
|
||||
NMSSERVER_GETREGISTRYACCESS(ClassWrapper.NMS_SERVER, new Class[] {},
|
||||
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "registryAccess()")),
|
||||
NMSSERVER_GETSERVER(ClassWrapper.CRAFT_SERVER, new Class[] {},
|
||||
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "getServer()")),
|
||||
TILEENTITY_GET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_20_R4,
|
||||
TILEENTITY_GET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "saveWithId(net.minecraft.core.HolderLookup$Provider)")),
|
||||
TILEENTITY_SET_NBT_1205(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz() },
|
||||
MinecraftVersion.MC1_20_R4, new Since(MinecraftVersion.MC1_20_R4, "loadWithComponents(net.minecraft.nbt.CompoundTag,net.minecraft.core.HolderLookup$Provider)")),
|
||||
MinecraftVersion.MC1_20_R4, MinecraftVersion.MC1_21_R4, new Since(MinecraftVersion.MC1_20_R4, "loadWithComponents(net.minecraft.nbt.CompoundTag,net.minecraft.core.HolderLookup$Provider)")),
|
||||
GET_DATAFIXER(ClassWrapper.NMS_DATAFIXERS, new Class[] {}, MinecraftVersion.MC1_20_R4,
|
||||
new Since(MinecraftVersion.MC1_20_R4, "getDataFixer()")),
|
||||
// 1.21.6 Stuff
|
||||
GET_SERIALIZATION_CONTEXT(ClassWrapper.NMS_PROVIDER, new Class[] {ClassWrapper.NMS_DYNAMICOPS.getClazz()}, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "createSerializationContext(com.mojang.serialization.DynamicOps)")),
|
||||
NMS_GET_TAG_VALUE_INPUT(ClassWrapper.NMS_TAG_VALUE_INPUT, new Class[] {ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "create(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider,net.minecraft.nbt.CompoundTag)")),
|
||||
NMS_GET_TAG_VALUE_OUTPUT(ClassWrapper.NMS_TAG_VALUE_OUTPUT, new Class[] {ClassWrapper.NMS_PROBLEM_REPORTER.getClazz(), ClassWrapper.NMS_PROVIDER.getClazz()}, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "createWithContext(net.minecraft.util.ProblemReporter,net.minecraft.core.HolderLookup$Provider)")),
|
||||
NMS_TAG_VALUE_OUTPUT_TO_TAG_COMPOUND(ClassWrapper.NMS_TAG_VALUE_OUTPUT, new Class[] {}, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "buildResult()")),
|
||||
NMS_ENTITY_SET_NBT_1216(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_VALUE_INPUT.getClazz() }, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "load(net.minecraft.world.level.storage.ValueInput)")
|
||||
),
|
||||
NMS_ENTITY_GET_NBT_1216(ClassWrapper.NMS_ENTITY, new Class[] { ClassWrapper.NMS_VALUE_OUTPUT.getClazz() }, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "saveWithoutId(net.minecraft.world.level.storage.ValueOutput)")),
|
||||
|
||||
TILEENTITY_GET_NBT_1216(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_VALUE_OUTPUT.getClazz()}, MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "saveWithId(net.minecraft.world.level.storage.ValueOutput)")),
|
||||
TILEENTITY_SET_NBT_1216(ClassWrapper.NMS_TILEENTITY, new Class[] {ClassWrapper.NMS_VALUE_INPUT.getClazz() },
|
||||
MinecraftVersion.MC1_21_R5,
|
||||
new Since(MinecraftVersion.MC1_21_R5, "loadWithComponents(net.minecraft.world.level.storage.ValueInput)")),
|
||||
;
|
||||
|
||||
private MinecraftVersion removedAfter;
|
||||
@@ -382,6 +414,16 @@ public enum ReflectionMethod {
|
||||
this(targetClass, args, addedSince, null, methodnames);
|
||||
}
|
||||
|
||||
private static String resolveItemStackBukkitMirrorMethodName() {
|
||||
try {
|
||||
ClassWrapper.CRAFT_ITEMSTACK.getClazz().getDeclaredMethod("asCraftMirror",
|
||||
ClassWrapper.NMS_ITEMSTACK.getClazz());
|
||||
return "asCraftMirror";
|
||||
} catch (NullPointerException | NoSuchMethodException | SecurityException ignored) {
|
||||
return "asBukkitMirror";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the method on a given target object using the given args.
|
||||
*
|
||||
@@ -396,7 +438,8 @@ public enum ReflectionMethod {
|
||||
return method.invoke(target, args);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Error while calling the method '" + methodName + "', loaded: " + loaded
|
||||
+ ", Enum: " + this + ", Passed Class: " + (target == null ? "null" : target.getClass()) + " Args: " + (args == null ? "null" : Arrays.toString(args)), ex);
|
||||
+ ", Enum: " + this + ", Passed Class: " + (target == null ? "null" : target.getClass()) + " Args: " + (args == null ? "null" : Arrays.toString(args)) +
|
||||
" Classes: " + (args == null ? "null" : Arrays.asList(args).stream().map(a -> a == null ? "NULL" : a.getClass().getName()).collect(Collectors.toList())), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-11
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</parent>
|
||||
<artifactId>item-nbt-api-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -12,7 +12,7 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -21,6 +21,13 @@
|
||||
<version>4.0.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<!-- Overwrite to provided -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
@@ -33,7 +40,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.6.2</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<relocations>
|
||||
@@ -41,14 +48,6 @@
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>de.tr7zw.nbtapi</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>javassist</pattern>
|
||||
<shadedPattern>de.tr7zw.nbtinjector.javassist</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.annotations</pattern>
|
||||
<shadedPattern>de.tr7zw.nbtapi.utils.annotations</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
|
||||
@@ -7,7 +7,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
@@ -19,6 +18,7 @@ import de.tr7zw.nbtapi.plugin.tests.GameprofileTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.NBTFileTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.blocks.BlockNBTTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.chunks.ChunkNBTPersistentTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.CompoundDifferenceTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.EnumTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.EqualsTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.compounds.ForEachTest;
|
||||
@@ -41,6 +41,7 @@ import de.tr7zw.nbtapi.plugin.tests.items.DirectApplyMetaTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.DirectApplyTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.EmptyItemTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemConversionTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemJsonTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemMergingTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.ItemStackConversionTest;
|
||||
import de.tr7zw.nbtapi.plugin.tests.items.LegacyItemTest;
|
||||
@@ -99,6 +100,7 @@ public class NBTAPI extends JavaPlugin {
|
||||
apiTests.add(new ForEachTest());
|
||||
apiTests.add(new StreamTest());
|
||||
apiTests.add(new EqualsTest());
|
||||
apiTests.add(new CompoundDifferenceTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3)) // 1.7.10 list support is not complete at all
|
||||
apiTests.add(new IteratorTest());
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1))
|
||||
@@ -112,6 +114,9 @@ public class NBTAPI extends JavaPlugin {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4)) {
|
||||
apiTests.add(new LegacyItemTest());
|
||||
}
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_20_R4) && !MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
apiTests.add(new ItemJsonTest());
|
||||
}
|
||||
apiTests.add(new ComponentsTest());
|
||||
apiTests.add(new EmptyItemTest());
|
||||
apiTests.add(new SmuggleTest());
|
||||
@@ -195,7 +200,7 @@ public class NBTAPI extends JavaPlugin {
|
||||
} catch (Exception ex) {
|
||||
results.put(test, ex);
|
||||
getLogger().log(Level.WARNING, "Error during '" + test.getClass().getSimpleName() + "' test!", ex);
|
||||
} catch (NoSuchFieldError th) { // NOSONAR
|
||||
} catch (Throwable th) { // NOSONAR
|
||||
getLogger().log(Level.SEVERE, "Servere error during '" + test.getClass().getSimpleName() + "' test!");
|
||||
getLogger().warning(
|
||||
"WARNING! This version of Item-NBT-API seems to be broken with your Spigot version! Canceled the other tests!");
|
||||
|
||||
@@ -15,9 +15,8 @@ public class GameprofileTest implements Test {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
GameProfile profile = new GameProfile(uuid, "random");
|
||||
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
|
||||
profile = null;
|
||||
profile = NBT.gameProfileFromNBT(nbt);
|
||||
if (profile == null || !profile.getId().equals(uuid)) {
|
||||
GameProfile profile2 = NBT.gameProfileFromNBT(nbt);
|
||||
if (profile == null || !profile.equals(profile2)) {
|
||||
throw new NbtApiException("Error when converting a GameProfile from/to NBT!");
|
||||
}
|
||||
}
|
||||
|
||||
+56
-19
@@ -1,40 +1,77 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.blocks;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTBlock;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BlockNBTTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_16_R3.getVersionId())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
if (world.getLoadedChunks().length > 1) {
|
||||
Chunk chunk = world.getLoadedChunks()[0];
|
||||
Block block = chunk.getBlock(0, 254, 0);
|
||||
NBTBlock comp = new NBTBlock(block);
|
||||
comp.getData().removeKey("Too");
|
||||
if (comp.getData().hasTag("Too")) {
|
||||
throw new NbtApiException("Unable to remove key from Block!");
|
||||
}
|
||||
comp.getData().setString("Too", "Bar");
|
||||
if (!new NBTBlock(block).getData().getString("Too").equals("Bar")) {
|
||||
throw new NbtApiException("Custom Data did not save to a Block!");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTBlocks!", ex);
|
||||
|
||||
List<World> loadedWorlds = Bukkit.getWorlds();
|
||||
if (loadedWorlds.isEmpty())
|
||||
return;
|
||||
|
||||
World world = loadedWorlds.get(0);
|
||||
Chunk[] loadedChunks = world.getLoadedChunks();
|
||||
if (loadedChunks.length == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
Chunk chunk = loadedChunks[0];
|
||||
Block block = chunk.getBlock(0, 254, 0);
|
||||
String blockKey = block.getX() + "_" + block.getY() + "_" + block.getZ();
|
||||
|
||||
NBT.modifyChunkPDC(block, nbt -> nbt.setString("Too", "Bar"));
|
||||
boolean storedDataPresent = NBT.readAndGetChunkPDC(chunk, nbt -> {
|
||||
if (!nbt.hasTag("blocks", NBTType.NBTTagCompound)) return false;
|
||||
|
||||
ReadableNBT blocksNbt = nbt.getCompound("blocks");
|
||||
if (blocksNbt == null || !blocksNbt.hasTag(blockKey, NBTType.NBTTagCompound)) return false;
|
||||
|
||||
ReadableNBT blockNbt = blocksNbt.getCompound(blockKey);
|
||||
return blockNbt != null && blockNbt.hasTag("Too", NBTType.NBTTagString) && "Bar".equals(blockNbt.getString("Too"));
|
||||
});
|
||||
if (!storedDataPresent) {
|
||||
throw new NbtApiException("Custom Data did not save to a Block!");
|
||||
}
|
||||
if (!NBT.readAndGetChunkPDC(block, nbt -> nbt.hasTag("Too"))) {
|
||||
throw new NbtApiException("Custom Data did not save to a Block!");
|
||||
}
|
||||
|
||||
boolean blockHadOnlyTestKey = NBT.readAndGetChunkPDC(block, nbt -> nbt.getKeys().size() == 1);
|
||||
boolean chunkHadOnlyOneBlock = NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getCompound("blocks").getKeys().size() == 1);
|
||||
|
||||
NBT.modifyChunkPDC(block, nbt -> nbt.removeKey("Too"));
|
||||
|
||||
if (NBT.readAndGetChunkPDC(block, nbt -> nbt.hasTag("Too"))) {
|
||||
throw new NbtApiException("Unable to remove key from Block!");
|
||||
}
|
||||
|
||||
if (chunkHadOnlyOneBlock) {
|
||||
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("blocks"))) {
|
||||
throw new NbtApiException("Chunk data wasn't cleaned up (hanging \"blocks\" compound)!");
|
||||
}
|
||||
} else if (blockHadOnlyTestKey) {
|
||||
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getCompound("blocks").hasTag(blockKey))) {
|
||||
throw new NbtApiException("Chunk data wasn't cleaned up (hanging block location compound)!");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTBlocks!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-20
@@ -1,40 +1,46 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.chunks;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTChunk;
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChunkNBTPersistentTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
if (MinecraftVersion.getVersion().getVersionId() < MinecraftVersion.MC1_16_R3.getVersionId())
|
||||
return;
|
||||
if (!Bukkit.getWorlds().isEmpty()) {
|
||||
World world = Bukkit.getWorlds().get(0);
|
||||
try {
|
||||
if (world.getLoadedChunks().length > 1) {
|
||||
Chunk chunk = world.getLoadedChunks()[0];
|
||||
NBTChunk comp = new NBTChunk(chunk);
|
||||
NBTCompound persistentData = comp.getPersistentDataContainer();
|
||||
persistentData.removeKey("Foo");
|
||||
if (persistentData.hasTag("Foo")) {
|
||||
throw new NbtApiException("Unable to remove key from Chunk!");
|
||||
}
|
||||
persistentData.setString("Foo", "Bar");
|
||||
if (!new NBTChunk(chunk).getPersistentDataContainer().getString("Foo").equals("Bar")) {
|
||||
throw new NbtApiException("Custom Data did not save to the Chunk!");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTChunks!", ex);
|
||||
|
||||
List<World> loadedWorlds = Bukkit.getWorlds();
|
||||
if (loadedWorlds.isEmpty())
|
||||
return;
|
||||
|
||||
World world = loadedWorlds.get(0);
|
||||
Chunk[] loadedChunks = world.getLoadedChunks();
|
||||
if (loadedChunks.length == 0)
|
||||
return;
|
||||
|
||||
Chunk chunk = loadedChunks[0];
|
||||
try {
|
||||
NBT.modifyChunkPDC(chunk, nbt -> nbt.setString("Foo", "Bar"));
|
||||
if (!NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("Foo", NBTType.NBTTagString) && "Bar".equals(nbt.getString("Foo")))) {
|
||||
throw new NbtApiException("Custom Data did not save to the Chunk!");
|
||||
}
|
||||
|
||||
NBT.modifyChunkPDC(chunk, nbt -> nbt.removeKey("Foo"));
|
||||
if (NBT.readAndGetChunkPDC(chunk, nbt -> nbt.hasTag("Foo"))) {
|
||||
throw new NbtApiException("Unable to remove key from Chunk!");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Wasn't able to use NBTChunks!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.compounds;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CompoundDifferenceTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ReadWriteNBT nbt1 = NBT.createNBTObject();
|
||||
nbt1.setInteger("intTag", 1);
|
||||
ReadWriteNBT tmp = NBT.createNBTObject();
|
||||
tmp.setInteger("foo", 1);
|
||||
nbt1.getCompoundList("compoundList1").addCompound(tmp);
|
||||
nbt1.setIntArray("intArray", new int[]{1, 2, 3});
|
||||
tmp = NBT.createNBTObject();
|
||||
tmp.setIntArray("foo", new int[]{1, 2, 3});
|
||||
nbt1.getCompoundList("compoundList2").addCompound(tmp);
|
||||
nbt1.getOrCreateCompound("compoundTag").setFloat("floatTag", 20F);
|
||||
nbt1.getOrCreateCompound("compoundTag").setBoolean("booleanTag", true);
|
||||
nbt1.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D));
|
||||
nbt1.getOrCreateCompound("compoundTag").getFloatList("floatList").addAll(Arrays.asList(1F, 2F));
|
||||
nbt1.getOrCreateCompound("compoundTagTwo").setString("stringTag", "string");
|
||||
ReadWriteNBT nbt2 = NBT.createNBTObject();
|
||||
nbt2.setInteger("intTag", 1);
|
||||
tmp = NBT.createNBTObject();
|
||||
tmp.setInteger("foo", 1);
|
||||
nbt2.getCompoundList("compoundList1").addCompound(tmp);
|
||||
nbt2.setInteger("alsoIntTag", 2);
|
||||
tmp = NBT.createNBTObject();
|
||||
tmp.setInteger("foo", 2);
|
||||
nbt2.getCompoundList("compoundList2").addCompound(tmp);
|
||||
nbt2.getOrCreateCompound("compoundTag").setFloat("floatTag", 20F);
|
||||
nbt2.getOrCreateCompound("compoundTag").setBoolean("booleanTag", false);
|
||||
nbt2.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D, 4D));
|
||||
nbt2.getOrCreateCompound("compoundTag").getFloatList("floatList").addAll(Arrays.asList(1F, 2F));
|
||||
nbt2.getOrCreateCompound("compoundTagTwo").setString("stringTag", "string");
|
||||
|
||||
ReadWriteNBT expectedDiff1 = NBT.createNBTObject();
|
||||
tmp = NBT.createNBTObject();
|
||||
tmp.setIntArray("foo", new int[]{1, 2, 3});
|
||||
expectedDiff1.getCompoundList("compoundList2").addCompound(tmp);
|
||||
expectedDiff1.setIntArray("intArray", new int[]{1, 2, 3});
|
||||
expectedDiff1.getOrCreateCompound("compoundTag").setBoolean("booleanTag", true);
|
||||
expectedDiff1.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D));
|
||||
ReadWriteNBT expectedDiff2 = NBT.createNBTObject();
|
||||
tmp = NBT.createNBTObject();
|
||||
tmp.setInteger("foo", 2);
|
||||
expectedDiff2.getCompoundList("compoundList2").addCompound(tmp);
|
||||
expectedDiff2.setInteger("alsoIntTag", 2);
|
||||
expectedDiff2.getOrCreateCompound("compoundTag").setBoolean("booleanTag", false);
|
||||
expectedDiff2.getOrCreateCompound("compoundTag").getDoubleList("doubleList").addAll(Arrays.asList(1D, 2D, 3D, 4D));
|
||||
|
||||
ReadWriteNBT diff1 = nbt1.extractDifference(nbt2);
|
||||
ReadWriteNBT diff2 = nbt2.extractDifference(nbt1);
|
||||
|
||||
if (!expectedDiff1.equals(diff1)) {
|
||||
throw new NbtApiException("Diff1: Compounds did not match! " + expectedDiff1 + " " + diff1);
|
||||
}
|
||||
if (!expectedDiff2.equals(diff2)) {
|
||||
throw new NbtApiException("Diff2: Compounds did not match! " + expectedDiff2 + " " + diff2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class ListTest implements Test {
|
||||
}
|
||||
|
||||
if (comp.getListType("complist") != NBTType.NBTTagCompound) {
|
||||
throw new NbtApiException("complist had the wrong type! The Item-NBT-API may not work!");
|
||||
throw new NbtApiException("complist had the wrong type(" + comp.getListType("complist") + ")! The Item-NBT-API may not work!");
|
||||
}
|
||||
|
||||
// Integer
|
||||
|
||||
+3
-3
@@ -7,21 +7,21 @@ import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class StreamTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
NBTContainer base = new NBTContainer();
|
||||
ReadWriteNBT base = NBT.createNBTObject();
|
||||
base.getOrCreateCompound("sub").setString("hello", "world");
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
base.getOrCreateCompound("sub").writeCompound(outStream);
|
||||
byte[] data = outStream.toByteArray();
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
|
||||
NBTContainer container = new NBTContainer(inputStream);
|
||||
ReadWriteNBT container = NBT.readNBT(inputStream);
|
||||
if (!container.toString().equals(base.getOrCreateCompound("sub").toString())) {
|
||||
throw new NbtApiException("Component content did not match! " + base.getCompound("sub") + " " + container);
|
||||
}
|
||||
|
||||
+5
-1
@@ -27,7 +27,11 @@ public class ComponentsTest implements Test {
|
||||
throw new NbtApiException("ReadComponent didn't work!");
|
||||
}
|
||||
NBT.modifyComponents(item, nbt -> {
|
||||
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_21_R4)) {
|
||||
nbt.mergeCompound(NBT.parseNBT("{\"minecraft:custom_name\":[{\"text\":\"foobar\",\"italic\":false}]}"));
|
||||
} else {
|
||||
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
|
||||
}
|
||||
});
|
||||
if(!item.getItemMeta().getDisplayName().equals("foobar")) {
|
||||
throw new NbtApiException("ModifyComponent didn't work!");
|
||||
|
||||
+16
-7
@@ -1,13 +1,15 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.NBTType;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTContainer;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
@@ -19,14 +21,21 @@ public class ItemConversionTest implements Test {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setLore(Lists.newArrayList("Firest Line", "Second Line"));
|
||||
item.setItemMeta(meta);
|
||||
String nbt = NBTItem.convertItemtoNBT(item).toString();
|
||||
if (!nbt.contains("Firest Line") || !nbt.contains("Second Line"))
|
||||
throw new NbtApiException("The Item nbt '" + nbt + "' didn't contain the lore");
|
||||
ItemStack rebuild = NBTItem.convertNBTtoItem(new NBTContainer(nbt));
|
||||
|
||||
ReadWriteNBT nbt = NBT.itemStackToNBT(item);
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_12_R1)
|
||||
&& !nbt.hasTag("DataVersion", NBTType.NBTTagInt)) {
|
||||
throw new NbtApiException("The item nbt '" + nbt + "' didn't contain the data version");
|
||||
}
|
||||
|
||||
String nbtString = nbt.toString();
|
||||
if (!nbtString.contains("Firest Line") || !nbtString.contains("Second Line"))
|
||||
throw new NbtApiException("The Item nbt '" + nbtString + "' didn't contain the lore");
|
||||
ItemStack rebuild = NBT.itemStackFromNBT(NBT.parseNBT(nbtString));
|
||||
if (!item.isSimilar(rebuild))
|
||||
throw new NbtApiException("Rebuilt item did not match the original!");
|
||||
|
||||
NBTContainer cont = new NBTContainer();
|
||||
ReadWriteNBT cont = NBT.createNBTObject();
|
||||
cont.setItemStack("testItem", item);
|
||||
if (!item.isSimilar(cont.getItemStack("testItem")))
|
||||
throw new NbtApiException("Rebuilt item did not match the original!");
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.tr7zw.nbtapi.plugin.tests.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NbtApiException;
|
||||
import de.tr7zw.changeme.nbtapi.utils.NBTJsonUtil;
|
||||
import de.tr7zw.nbtapi.plugin.tests.Test;
|
||||
|
||||
public class ItemJsonTest implements Test {
|
||||
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ItemStack item = new ItemStack(Material.STONE);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName("test");
|
||||
meta.setUnbreakable(true);
|
||||
item.setItemMeta(meta);
|
||||
JsonElement elem = NBTJsonUtil.itemStackToJson(item);
|
||||
if (elem == null) {
|
||||
throw new NbtApiException("Getting the Json didn't work correctly! " + item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,15 @@ public class LegacyItemTest implements Test {
|
||||
|| !"test".equals(item.getItemMeta().getDisplayName())) {
|
||||
throw new NbtApiException("1.12.2 item didn't load correctly! " + item);
|
||||
}
|
||||
|
||||
ItemStack item2 = NBT.itemStackFromNBT(NBT.parseNBT("{DataVersion:" + DataFixerUtil.VERSION1_12_2 + ",id:cobblestone,Count:42,tag:{display:{Name:\"test\"},ench:[{lvl:3,id:34}]}}"));
|
||||
if (!item.equals(item2))
|
||||
throw new NbtApiException("Data-fixed 1.12.2 item didn't load correctly! " + item2);
|
||||
|
||||
ReadWriteNBT testNBT = NBT.parseNBT("{id:cobblestone,Count:42,tag:{display:{Name:\"test\"},ench:[{lvl:3,id:34}]},DataVersion:1343}");
|
||||
testNBT = DataFixerUtil.fixUpItemData(testNBT, DataFixerUtil.VERSION1_12R1);
|
||||
if (testNBT.getInteger("DataVersion") != DataFixerUtil.getCurrentVersion())
|
||||
throw new NbtApiException("DataVersion wasn't updated correctly! " + testNBT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</parent>
|
||||
<artifactId>mappings-parser</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -12,7 +12,7 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -24,7 +24,7 @@ public class MappingsParser {
|
||||
public static StringBuilder builder = new StringBuilder();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File input = new File("minecraft_server.1.21.2.txt");
|
||||
File input = new File("minecraft_server.1.21.9.txt");
|
||||
List<String> lines = Files.readAllLines(input.toPath());
|
||||
|
||||
Map<String, ClassWrapper> classes = new HashMap<>();
|
||||
@@ -75,6 +75,33 @@ public class MappingsParser {
|
||||
}
|
||||
// System.out.println(currentClass + ": " + mojangName + " -> " + unmappedName);
|
||||
}
|
||||
for(String type : MojangToMapping.getMapping().keySet()) {
|
||||
if(type.contains("(")) {
|
||||
continue;
|
||||
}
|
||||
// Field
|
||||
String clazz = type.split("#")[0];
|
||||
String fieldName = type.split("#")[1];
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (!line.startsWith(" ")) {
|
||||
currentClass = line.split(" ")[0];
|
||||
continue;
|
||||
}
|
||||
mojangName = line.trim().split(" ")[1];
|
||||
unmappedName = line.trim().split(" ")[3];
|
||||
if (currentClass.equals(clazz) && mojangName.equals(fieldName)) {
|
||||
if (MojangToMapping.getMapping().get(type) == null) {
|
||||
System.out.println("Missing mapping for " + type);
|
||||
} else if (!MojangToMapping.getMapping().get(type)
|
||||
.equals(unmappedName)) {
|
||||
builder.append("put(\"" + type + "\", \"" + unmappedName + "\");\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nbt-data-api</artifactId>
|
||||
@@ -14,7 +14,7 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-parent</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</parent>
|
||||
<artifactId>nbt-injector</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -12,7 +12,7 @@
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.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.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
<organization>
|
||||
@@ -88,7 +88,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.16.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
@@ -97,7 +97,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<version>3.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
@@ -110,7 +110,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
<version>3.12.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
</configuration>
|
||||
@@ -139,7 +139,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<version>2.7.1</version>
|
||||
<configuration>
|
||||
<licenseName>mit</licenseName>
|
||||
<licenseFile>LICENSE</licenseFile>
|
||||
|
||||
@@ -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.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</dependency>
|
||||
[/CODE]
|
||||
[/SPOILER]
|
||||
@@ -122,7 +122,7 @@ Using this method the API gets copied into your plugin. Server owners don't have
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.16.1</version>
|
||||
</dependency>
|
||||
...
|
||||
<plugins>
|
||||
|
||||
@@ -100,7 +100,7 @@ NBT.modifyPersistentData(zombie, nbt -> {
|
||||
File worldDataFolder = Bukkit.getWorlds().getFirst().getWorldFolder();
|
||||
|
||||
// Read level data
|
||||
NBTFile levelNbtFile = new NBTFile(new File(worldDataFolder, "level.dat"));
|
||||
NBTFileHandle levelNbtFile = NBT.getFileHandle(new File(worldDataFolder, "level.dat"));
|
||||
|
||||
// Obtain world name
|
||||
String worldName = levelNbtFile.resolveOrNull("Data.LevelName", String.class);
|
||||
@@ -113,7 +113,7 @@ if (!playerFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NBTFile playerNbtFile = new NBTFile(playerFile);
|
||||
NBTFileHandle playerNbtFile = NBT.getFileHandle(playerFile);
|
||||
|
||||
// Change player's health
|
||||
float health = playerNbtFile.getFloat("Health");
|
||||
|
||||
+1
-3
@@ -21,6 +21,4 @@ The experimental NBTInjector became unsupported since Minecraft 1.14 and was rem
|
||||
|
||||
NBTInjector is incompatible with reloads and may break things, and thus is not recommended.
|
||||
|
||||
If you're using Minecraft 1.14+, you should switch to the persistent storage API instead.
|
||||
|
||||
In versions prior to 1.14, you may use a workaround like storing data inside an item's nbt that the entity is wearing (e.g. having a button in mob's helmet, and storing data on that button).
|
||||
See [this wiki section](https://github.com/tr7zw/Item-NBT-API/wiki/Using-the-NBT-API#accessing-custom-data) on how to handle custom entity nbt data without NBTInjector.
|
||||
+3
-1
@@ -2,9 +2,11 @@
|
||||
|
||||
- [IllegalStack](https://github.com/dniym/IllegalStack)
|
||||
- [Prison](https://github.com/PrisonTeam/Prison)
|
||||
- [CommandAPI](https://github.com/JorelAli/CommandAPI)
|
||||
- [CommandAPI](https://github.com/CommandAPI/CommandAPI)
|
||||
- [SkBee](https://github.com/ShaneBeee/SkBee/)
|
||||
- [Monumenta](https://github.com/TeamMonumenta/monumenta-plugins-public/)
|
||||
- [SummonFriendlyMobs](https://github.com/ArmyFury/SummonFriendlyMobs)
|
||||
- [EvenMoreFish](https://github.com/Oheers/EvenMoreFish)
|
||||
|
||||
___
|
||||
|
||||
|
||||
+27
-5
@@ -2,8 +2,11 @@ To start using NB-API, you either need to depend on its plugin version, or shade
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
|
||||
>
|
||||
> [!IMPORTANT]
|
||||
> Alternative ways of loading the api like Libby are not supported on Discord/Github issues.
|
||||
|
||||
# Option 1) NBT-API as a dependency
|
||||
# Option 1) NBT-API as a dependency (recommended)
|
||||
|
||||
Add the following entries to your Gradle build at the correct locations:
|
||||
|
||||
@@ -30,19 +33,38 @@ Add the API as dependency to your ``plugin.yml``:
|
||||
depend: [NBTAPI]
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin
|
||||
Or, if you are using ``paper-plugin.yml``:
|
||||
|
||||
To include NBT-API directly in your plugin, you can use the gradle shadow plugin.
|
||||
```yml
|
||||
dependencies:
|
||||
server:
|
||||
NBTAPI:
|
||||
load: BEFORE
|
||||
required: true
|
||||
join-classpath: true
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin (Not recommended)
|
||||
|
||||
> [!WARNING]
|
||||
> Due to the increasing amount of pitfalls in modern Paper, shading is not recommended. Please test with the normal plugin dependency before reporting issues.
|
||||
|
||||
To include NBT-API directly in your plugin, you can use the [Gradle Shadow Plugin](https://gradleup.com/shadow/).
|
||||
|
||||
Create an empty ``.mojang-mapped`` file in the META-INF folder of your plugin for 1.20+ Paper support.
|
||||
|
||||
> [!WARNING]
|
||||
> Not adding this flag will make the shaded api not work on the latest Paper. It might also break other reflections/nms logic!
|
||||
|
||||
Add the plugin to the build configuration, as shown here:
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow") version "VERSION"
|
||||
id("com.gradleup.shadow") version "VERSION"
|
||||
}
|
||||
```
|
||||
|
||||
The latest version of the shadow plugin can be found [here](https://github.com/johnrengelman/shadow/releases).
|
||||
The latest version of the shadow plugin can be found [here](https://plugins.gradle.org/plugin/com.gradleup.shadow).
|
||||
|
||||
Add NBT-API to your dependencies:
|
||||
|
||||
|
||||
+24
-2
@@ -2,8 +2,11 @@ To start using NB-API, you either need to depend on its plugin version, or shade
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Plugin and shaded versions have different ``artifactId``. Make sure to correctly choose the one you need!
|
||||
>
|
||||
> [!IMPORTANT]
|
||||
> Alternative ways of loading the api like Libby are not supported on Discord/Github issues.
|
||||
|
||||
# Option 1) NBT-API as a dependency
|
||||
# Option 1) NBT-API as a dependency (recommended)
|
||||
|
||||
Add the following entries to your pom at the correct locations:
|
||||
|
||||
@@ -37,10 +40,29 @@ Add the API as dependency to your ``plugin.yml``:
|
||||
depend: [NBTAPI]
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin
|
||||
Or, if you are using ``paper-plugin.yml``:
|
||||
|
||||
```yml
|
||||
dependencies:
|
||||
server:
|
||||
NBTAPI:
|
||||
load: BEFORE
|
||||
required: true
|
||||
join-classpath: true
|
||||
```
|
||||
|
||||
# Option 2) Shading the NBT-API into your plugin (Not recommended)
|
||||
|
||||
> [!WARNING]
|
||||
> Due to the increasing amount of pitfalls in modern Paper, shading is not recommended. Please test with the normal plugin dependency before reporting issues.
|
||||
|
||||
To include NBT-API directly in your plugin, you can use the maven shade plugin.
|
||||
|
||||
Create an empty ``.mojang-mapped`` file in the META-INF folder of your plugin for 1.20+ Paper support.
|
||||
|
||||
> [!WARNING]
|
||||
> Not adding this flag will make the shaded api not work on the latest Paper. It might also break other reflections/nms logic!
|
||||
|
||||
Add the plugin to the build configuration, as shown here:
|
||||
|
||||
```xml
|
||||
|
||||
+101
-30
@@ -2,7 +2,7 @@
|
||||
|
||||
### Basics overview
|
||||
|
||||
Most of the things are exposed inside the [NBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/NBT.html) class. Make sure to explore the Javadoc, or ask your IDE for suggestions!
|
||||
Most of the things are exposed inside the [NBT](https://tr7zw.github.io/Item-NBT-API/v2-api/de/tr7zw/changeme/nbtapi/NBT.html) class. Your specific use-case might not yet be covered by the wiki, so make sure to explore the [Javadoc](https://tr7zw.github.io/Item-NBT-API/v2-api/), or ask your IDE for suggestions!
|
||||
|
||||
#### Basic getting and setting of data
|
||||
|
||||
@@ -43,14 +43,34 @@ nbt.getOrCreateCompound("subtag"); // Get or create a subtag
|
||||
#### Converting NBT to String and the String back to NBT
|
||||
|
||||
```java
|
||||
// Parse Mojang-Json string to nbt
|
||||
// Parse SNBT to nbt
|
||||
ReadWriteNBT nbt = NBT.parseNBT("{Health:20.0f,Motion:[0.0d,10.0d,0.0d],Silent:1b}");
|
||||
// Get the NBT back as a Mojang-Json string (works with any NBT object)
|
||||
String json = nbt.toString();
|
||||
// Get the NBT back as a SNBT (works with any NBT object)
|
||||
String snbt = nbt.toString();
|
||||
// Turn back into nbt again
|
||||
ReadWriteNBT nbt2 = NBT.parseNBT(json);
|
||||
ReadWriteNBT nbt2 = NBT.parseNBT(snbt);
|
||||
```
|
||||
|
||||
#### Comparing NBT data
|
||||
|
||||
As with other objects in Java, you may compare nbt using `equals`.
|
||||
|
||||
To see what exactly mismatched, you can use `ReadableNBT#extractDifference`:
|
||||
|
||||
```java
|
||||
ReadWriteNBT nbt1 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:1b}},intArray:[I;1,2]");
|
||||
ReadWriteNBT nbt2 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:0b}},intArray:[I;1,2,3],alsoIntTag:2");
|
||||
|
||||
ReadWriteNBT diff1 = nbt1.extractDifference(nbt2);
|
||||
ReadWriteNBT diff2 = nbt2.extractDifference(nbt1);
|
||||
```
|
||||
|
||||
For the example above, `ReadableNBT#toString` will produce:
|
||||
|
||||
diff1: `{compoundTag:{booleanTag:1b},intArray:[I;1,2]}`
|
||||
|
||||
diff2: `{compoundTag:{booleanTag:0b},intArray:[I;1,2,3],alsoIntTag:2}`
|
||||
|
||||
#### Merging Compounds
|
||||
|
||||
You can "merge" the data from any given Compound onto some other Compound. This will overwrite the tags with the same name.
|
||||
@@ -115,9 +135,13 @@ nbt.resolveOrDefault("list_key[-1]", "fallback_value"); // Get the last value in
|
||||
|
||||
|
||||
// NBT compound lists
|
||||
// Get the list (will create it if needed)
|
||||
ReadWriteNBTCompoundList nbtList = nbt.getOrCreateCompound("foo").getCompoundList("other_key");
|
||||
ReadWriteNBT nbtListEntry = addCompound();
|
||||
// Add new compound to the list
|
||||
ReadWriteNBT nbtListEntry = nbtList.addCompound();
|
||||
nbtListEntry.setBoolean("bar", true);
|
||||
// Add existing nbt compound to the list
|
||||
ReadWriteNBT otherNbtListEntry = nbtList.addCompound(NBT.parseNBT("{foo_bar:1b}"));
|
||||
|
||||
// You can fetch compounds in lists
|
||||
nbt.resolveCompound("foo.other_key[0]"); // Get the first compound in list, or null
|
||||
@@ -162,12 +186,33 @@ int someValue = NBT.modify(itemStack, nbt -> {
|
||||
|
||||
> [!WARNING]
|
||||
> Never mix the usage of ItemMeta and NBT.
|
||||
> Setting ItemMeta will override any changes made to item's nbt.
|
||||
|
||||
```java
|
||||
// WRONG, do not do this!
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
meta.setDisplayName("Modified!");
|
||||
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
|
||||
itemStack.setItemMeta(meta); // Will undo all changes to nbt!
|
||||
|
||||
|
||||
// Correct, both ItemMeta and NBT will be applied
|
||||
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
|
||||
// Taking ItemMeta snapshot after changes to nbt
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
meta.setDisplayName("Modified!");
|
||||
itemStack.setItemMeta(meta);
|
||||
// If making any further changes with NBT & ItemMeta,
|
||||
// re-take the item's ItemMeta after changing the nbt!
|
||||
```
|
||||
|
||||
Alternatively, you may safely modify ItemMeta inside the NBT scope:
|
||||
|
||||
```java
|
||||
// Updating ItemMeta using NBT
|
||||
NBT.modify(itemStack, nbt -> {
|
||||
nbt.setInteger("kills", nbt.getOrDefault("kills", 0) + 1);
|
||||
nbt.modifyMeta((meta, readOnlyNbt) -> { // Do not modify the nbt while modifying the meta!
|
||||
nbt.modifyMeta((readOnlyNbt, meta) -> { // Do not modify the nbt while modifying the meta!
|
||||
meta.setDisplayName("Kills: " + readOnlyNbt.getOrDefault("kills", 0));
|
||||
});
|
||||
// Do more stuff
|
||||
@@ -184,6 +229,15 @@ NBT.modify(itemStack, nbt -> {
|
||||
|
||||
```java
|
||||
// NOTE: This code is only for 1.20.5+!
|
||||
|
||||
// Only reading vanilla data
|
||||
NBT.getComponents(item, nbt -> {
|
||||
if (nbt.hasTag("minecraft:custom_name")) {
|
||||
String customName = nbt.getString("minecraft:custom_name");
|
||||
}
|
||||
});
|
||||
|
||||
// Modifying vanilla data
|
||||
NBT.modifyComponents(item, nbt -> {
|
||||
nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
|
||||
});
|
||||
@@ -209,7 +263,12 @@ NBT.modify(entity, nbt -> {
|
||||
|
||||
#### Accessing custom data
|
||||
|
||||
For reading/storing custom data on (block-)entities, you should use methods that end with PersistentData.
|
||||
> [!WARNING]
|
||||
> Persistent data storage for custom (block-)entity nbt is only available in 1.14+.
|
||||
>
|
||||
> If you need to support versions below that, for entities you may use a workaround like storing data inside an item's nbt that the entity is wearing (e.g. having a button in mob's helmet, and storing data on that button).
|
||||
|
||||
For reading/storing custom data on (block-)entities, you should use methods that end with ``PersistentData``.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When working with block entities, make sure that the block entity exists in the world.
|
||||
@@ -247,20 +306,26 @@ You can store data in block entities (block entities like chest, furnace, etc.)
|
||||
|
||||
Thus, you have to use your own block data storage to store custom block data.
|
||||
|
||||
You can store data inside Chunks since 1.16.4, and NBT-API allows you to do so by using ``NBTChunk``:
|
||||
You can store data inside Chunks using their PDC (Persistent Data Container) since Minecraft **1.16.4**. The NBT‑API provides read and modify methods in the `NBT` class to interact with it:
|
||||
|
||||
```java
|
||||
ReadWriteNBT nbt = new NBTChunk(chunk).getPersistentDataContainer();
|
||||
// Read the entire PDC or a specific value
|
||||
NBT.readChunkPDC(chunk, nbt -> { /* ... */ });
|
||||
String str = NBT.readAndGetChunkPDC(chunk, nbt -> nbt.getOrNull("key", String.class));
|
||||
```
|
||||
|
||||
Similarly, there is ``NBTBlock``, which allows you to store block data inside chunk's data.
|
||||
For convenience, you can also pass Blocks into `NBT#read/modify[AndGet]ChunkPDC` methods to store block data inside Chunk's PDC.
|
||||
Blocks are stored inside their chunk's PDC under a `blocks` compound, using the block's location (`X_Y_Z`) as the key for a subcompound.
|
||||
|
||||
```java
|
||||
// Block's data will be stored in Chunk's data in "blocks.x_y_z" subtag
|
||||
ReadWriteNBT nbt = new NBTBlock(block).getData();
|
||||
// Modify and retrieve data for a specific block
|
||||
boolean bool = NBT.modifyAndGetChunkPDC(block, nbt -> {
|
||||
nbt.setString("owner_name", "Player123");
|
||||
return nbt.getOrDefault("key", false);
|
||||
});
|
||||
```
|
||||
|
||||
**However**, keep in mind that this data is linked only to the location, and if the block gets broken/changed/exploded/moved/etc., the data will still be on that location unless manually cleared/moved!
|
||||
**However**, keep in mind that the block's data is linked only to the location, and if the block gets broken/changed/exploded/moved/etc., the data will still be on that location unless manually cleared/moved!
|
||||
|
||||
Moreover, since the data is stored inside a Chunk, this will increase the chunk's size on the disk.
|
||||
|
||||
@@ -271,26 +336,32 @@ Moreover, since the data is stored inside a Chunk, this will increase the chunk'
|
||||
|
||||
### NBT files
|
||||
|
||||
```java
|
||||
// Creating nbt file
|
||||
// NBTFile will automatically create the file if it does not exist
|
||||
NBTFile file = new NBTFile(new File("directory"), "test.nbt"); // Will be created at ./directory/test.nbt
|
||||
// Setting data
|
||||
file.setString("foo", "bar");
|
||||
// Saving file
|
||||
file.save();
|
||||
NBTFileHandle allows working with nbt files.
|
||||
|
||||
// Alternatively, you may use helper methods in NBTFile class
|
||||
// To read the file without creating it if does not exist:
|
||||
ReadWriteNBT nbt = NBTFile.readFrom(file);
|
||||
// To save nbt to the file:
|
||||
NBTFile.saveTo(file, nbt);
|
||||
```java
|
||||
// Will automatically create the file if it does not exist
|
||||
NBTFileHandle nbtFile = NBT.getFileHandle(new File("directory", "test.nbt"));
|
||||
// Setting data
|
||||
nbtFile.setString("foo", "bar");
|
||||
// Saving the file after applying changes
|
||||
nbtFile.save();
|
||||
```
|
||||
|
||||
Alternatively, you may read the nbt file without maintaining the link to it.
|
||||
Unlike NBTFileHandle, the file won't be automatically created if it does not exist.
|
||||
|
||||
```java
|
||||
File file = new File("directory", "test.nbt");
|
||||
// Reading data from file (will return an empty compound if the file does not exist)
|
||||
ReadWriteNBT nbt = NBT.readFile(file);
|
||||
// Saving nbt to file
|
||||
NBT.writeFile(file, nbt);
|
||||
```
|
||||
|
||||
### Converting Minecraft Objects to NBT and Strings
|
||||
|
||||
The basic idea is that you can convert data into different representations:
|
||||
Minecraft Objects <-> NBT <-> String (Mojang-Json)
|
||||
Minecraft Objects <-> NBT <-> String (SNBT)
|
||||
|
||||
#### Items
|
||||
|
||||
@@ -318,8 +389,8 @@ ReadWriteNBT nbt = NBT.itemStackArrayToNBT(itemStacks);
|
||||
ItemStack itemStack = NBT.itemStackFromNBT(nbt);
|
||||
ItemStack[] itemStacks = NBT.itemStackArrayFromNBT(nbt);
|
||||
// Reminder (NBT <-> String)
|
||||
String json = nbt.toString();
|
||||
ReadWriteNBT nbt = NBT.parseNBT(json);
|
||||
String snbt = nbt.toString();
|
||||
ReadWriteNBT nbt = NBT.parseNBT(snbt);
|
||||
```
|
||||
|
||||
#### Entities and block entities
|
||||
|
||||
Reference in new issue
Block a user