From 14cb97cba106a4981f7990c792ad5f8b7405172b Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 30 Oct 2022 16:47:26 +0000 Subject: [PATCH] Sort NBT when writing This should fix the worst cases of #1196. --- .../computercraft/shared/util/NBTUtil.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java index c1686aa77..03aeaa289 100644 --- a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java @@ -18,6 +18,7 @@ import java.io.OutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -181,7 +182,7 @@ public static String getNBTHash( @Nullable CompoundNBT tag ) { MessageDigest digest = MessageDigest.getInstance( "MD5" ); DataOutput output = new DataOutputStream( new DigestOutputStream( digest ) ); - CompressedStreamTools.write( tag, output ); + writeTag( output, "", tag ); byte[] hash = digest.digest(); return ENCODING.encode( hash ); } @@ -192,6 +193,38 @@ public static String getNBTHash( @Nullable CompoundNBT tag ) } } + /** + * An alternative version of {@link CompressedStreamTools#write(CompoundNBT, DataOutput)}, which sorts keys. This + * should make the output slightly more deterministic. + * + * @param output The output to write to. + * @param name The name of the key we're writing. Should be {@code ""} for the root node. + * @param tag The tag to write. + * @throws IOException If the underlying stream throws. + * @see CompressedStreamTools#write(CompoundNBT, DataOutput) + * @see CompoundNBT#write(DataOutput) + */ + private static void writeTag( DataOutput output, String name, INBT tag ) throws IOException + { + output.writeByte( tag.getId() ); + if( tag.getId() == 0 ) return; + output.writeUTF( name ); + + if( tag instanceof CompoundNBT ) + { + CompoundNBT compound = (CompoundNBT) tag; + String[] keys = compound.getAllKeys().toArray( new String[0] ); + Arrays.sort( keys ); + for( String key : keys ) writeTag( output, key, compound.get( key ) ); + + output.writeByte( 0 ); + } + else + { + tag.write( output ); + } + } + private static final class DigestOutputStream extends OutputStream { private final MessageDigest digest;