Coder Social home page Coder Social logo

foreverzer0 / sharpnbt Goto Github PK

View Code? Open in Web Editor NEW
25.0 25.0 9.0 444 KB

A pure CLS-compliant C# implementation of the Named Binary Tag (NBT) format specification commonly used with Minecraft applications, allowing easy reading/writing streams and serialization to other formats.

License: MIT License

C# 100.00%
c-sharp clscompilant csharp dotnet minecraft minecraft-bedrock minecraft-java named-binary-tag nbt netstandard serialization snbt stream stringify

sharpnbt's People

Contributors

arcus92 avatar foreverzer0 avatar sitterr avatar vfrz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

sharpnbt's Issues

Unable to install Nuget package in framework 4.7.2 project

When I tried to install the package into the project (framework 4.7.2), the installation failed with an error.

Error Could not install package 'SharpNBT 1.2.0'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.7.2', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

Implement JSON output with modern Utf8JsonWriter

Leverage modern runtime features with the new(-ish) Utf8JsonWriter class. The current implementation uses the ISerializable interface, which is not near as flexible, nor convenient to implement in a way that uses the same structure as the NBT.

SNBT Parser cannot parse negative numbers in IntArrays

Title...

Attempting to parse an integer array fails with a SyntaxError if the array contains a negative number.
This effectively means that any SNBT containing a UUID fails as those are length 4 Integer Arrays and UUID format guarantees a negative number here.

A simple check to confirm:

StringNbt.Parse("{uuid:[I; 0, 0, 0, -1]}");

throws

System.Data.SyntaxErrorException: 'Syntax error at index 19: Invalid character '-' in integer array.'

Consolidate and remove superfluous abstractions

There is a few types in he library that seem to be abstractions for the sake of abstraction, and do not provide anything beneficial. While they do provide a common "parent" to classes like Compound/List, the actual need for such an abstraction is dubious in practical application, and an existing interface such as IEnumerable would suffice if such a need existed.

Being tied to a common concrete class makes both types less flexible.

Error when using SharpNBT in a little dotnet core project: Unable to cast SharpNBT.StringTag to type SharpNBT.CompoundTag

Hello,

I've just created a little console application to see whether SharpNBT suits my needs, I need a small utility to extract nbt tags from a bedrock level.dat file (and be able to edit and write back). I'm still not sure whether SharpNBT is able to read the information from that file format...

But in short, I've found the following problem:
An unhandled exception of type 'System.InvalidCastException' occurred in System.Private.CoreLib.dll: 'Unable to cast object of type 'SharpNBT.StringTag' to type 'SharpNBT.CompoundTag'.'

The code which caused this unhandled exception is pretty simple:

using SharpNBT;

namespace NbtTeset 
{
    class Program
    {
        static void Main(string[] args)
        {
            string file = "/home/david/Downloads/20220828/level.dat";
            var tag = NbtFile.Read(file, FormatOptions.BedrockFile, CompressionType.AutoDetect);
            Console.WriteLine(tag.PrettyPrinted());
        }
    }
}

The exception is thrown at the line where tag is declared and initialized with the result of NbtFile.Read(). I don't know if I am doing something wrong in here, if SharpNBT is unable to handle bedrock worlds or if there is a bug in SharpNBT - that's why I created this issue and hope you may be able to tell me what I'm doing wrong or whether it's really an issue :)

One additional info regarding the file I want to read: it's a world from Bedrock Dedicated Server..

CompoundTag should be backed with a Dictionary

The current CompoundTag implementation behaves akin to a dictionary, but through layers of abstraction, it is actually backed by a list (see #15 ). This works in practice, but breaks the constant-time lookup speed of the CompoundTag.

This proposal is complete #15 and provide and use an actual dictionary for storing child tags.

Stringify returns incorrect values on CompoundTags

There is flawed logic in the Stringify method of the CompoundTag class, and likely the ListTag as well. When appending children to the StringBuilder, it fails to call Stringify on them, therefore appending the ToString representation.

I want to restore Nbt string how to do?

This is the data I receive

image

如果我直接序列化json,NBT 格式和原来的就不对了

If I serialize json directly, the NBT format is wrong with the original one

JsonConvert.SerializeObject(tag);

image

SharpNBT.SNBT.StringNbt.Parse 之后如何转回原来的?

How do I switch back to the original after SharpNBT.SNBT.StringNbt.Parse?

Support for trailing commas in SNBT

Add support for container-like tags to support trailing commas without error.

"list": ["Hello", "world",]

This can be enabled via a configuration for the scanner, being disabled by default.

Cant get a nested tag

Hi. I am trying to read from the Minecraft level.dat file for a project i am working on. This is my code:

CompoundTag WorldData = NbtFile.Read(WorldFolder + @".\level.dat", FormatOptions.Java, CompressionType.AutoDetect);
var seed = WorldData["Data"]["WorldGenSettings"]["Seed"];

It is giving me the error stating that I cant use [] on the Array.

PrettyPrint should accept a stream argument

Rework the "PrettyPrint" logic to output to a Stream/TextWriter, which defaults to the standard output when not specified. This would provide more flexibility than StringBuilder object.

SNBT Parsing Is Horribly Slow

The SNBT parser appears to rely heavily on Regex but the regexes aren't compiled.

Specifying the RegexOptions.Compiled in the Regex constructor will yield a massive speedup.

Check for leading +/- when determining array type in SNBT

In ParseList, when determining if an container is a ListTag or array of numbers, it checks if the next in the sequence is a number:

if (char.IsNumber(c) || c == ',')

This should also include a check for a number that has a leading + or -.

Immutable types

Apply the immutability to many NBT types.

The following types should be completely immutable after initialization:

  • BoolTag ( #17 )
  • ByteTag
  • ShortTag
  • IntTag
  • LongTag
  • FloatTag
  • Doubletag
  • StringTag

The following types should be of a fixed size, but allow individual elements to be changed.

  • ByteArraytag
  • IntArrayTag
  • LongArraytag

The following will behave normally, allowing insertion/deletion

  • ListTag
  • CompoundTag

Optimize TagReader/TagWriter

The TagReader and TagWriter class still use the naive and legacy method of BitConverter.GetBytes paired with Array.Reverse to get the correct byte-order.

Replace all these method calls with the use of stackalloc spans and employ BinaryPrimitives methods to read/write in the appropriate endian. This will make them free of any heap-allocations in additional to being computationally less expensive.

Problems with ListTags

image

I found some strange things. When parsing, it traverses a ListTag whose type is SharpNBT.TagType.List. Their child item Type is SharpNBT.TagType.Compound, but when I build ListTag

If I

ListTag listTag = new ListTag(NBTEnchants, TagType.List);

Then

   CompoundTag? dicJarrTag = DicToTag(dicJArryItem);

   listTag.Add(dicJarrTag); //An error will be reported here

Exception:

System.ArrayTypeMismatchException: "Incorrect tag type added to this collection."

why is that? Or should my ListTag TagType be set to CompoundTag? So can it still be the same as the original one?

Split TagReader and TagWriter into separate sub-classes

Problem

There are currently three different ways that NBT can be serialized:

Format Endianness VarInt
Java Big None
Bedrock (file) Little None
Bedorck (network) Little Yes. Sometimes they are ZigZag encoded using the ProtoBuf style, other times not.

I will reserve disparaging Microsoft for needlessly complicating one of the most simple serialization formats in existence. It is par for the course, and not even unexpected anymore. I would be more surprised if they didn't mess it up.

Regardless, the TagReader and TagWriter class accounts for all scenarios using the format options passed to its constructor. While this works in practice, it means most methods have to do additional checks to see what the appropriate way to read the data is.

Proposal

Make TagReader and TagWriter abstract, with a factory method to create an instance. Internally these different ways of reading/writing can be split into separate classes that do not require any runtime checks.

Something akin to this psuedo-code:

public static TagReader Create(Stream stream, Format format)
{
    return format switch
    {
        Format.Java => new JavaReader(stream),
        Format.BedrockFile => new BedrockFileReader(stream),
        Format.BedrockNetwork => new BedrockNetworkReader(stream),
        _ => throw new ArgumentOutOfRangeException()
    };
}

The actual implementations can be completely transparent and need not even be part of the public API.

Missing SNBT Boolean Support

The SNBT parser lacks support for the Boolean type with values true or false. Internally this type is mapped to Byte when converting to NBT.

Although I think it is important to somehow preserve the original value came from a Boolean such that when rewriting snbt files, the Boolean fields remain as true or false and don't get rewritten as 1b or 0b. Rewriting to 1b or 0b would cause unnecessary changes to appear when diffing snbts. It also makes manual editing more error prone as one loses any indication that the field has only two valid values.

So perhaps an extra property could be added to ByteTag indicating it is really a Boolean, or a BooleanTag could be introduced that has conversions for ByteTag in order to prevent information loss.

Option to disable writing to console when disposing Context

If you're using the recommended Contexts workflow, you end up getting log spam every time a Context is disposed.

I know there's ways to suppress the Console temporarily yourself, but it'd be nice to have the option to disable this within TagBuilder.
Thanks for an otherwise solid library 🩷

Log spam

image

Compound, List, and Array Tags give false-positives when using ==

When comparing compound tags the bar for equality is set extremely low. Two compound objects are equal if they have the same type (guaranteed) and their names are the same.

This is because the CompoundTag class does not override the Tag.Equals method leading to this bizarre behaviour. A very simple check is this:

using SharpNBT.SNBT;
Console.WriteLine(StringNbt.Parse("{value:385}") == StringNbt.Parse("{data:[1,2,3,4]}"));

This will pass even though the two compounds aren't remotely the same.

BaseStream.Read reads less bytes than expected due to breaking change in .NET 6

There was a breaking change in .NET 6 in DeflateStream and GZipStream. These streams can now read less bytes than requested by the Read methods count parameter. You now have to call the Read method multiple times until your buffer is filled or 0 is returned (end of base stream).

In TagReader there are a few instances where BaseStream.Read is used but the return value is ignored. For example: When reading Minecraft chunk files the NBT data is compressed by GZipStream or ZLibStream (uses DeflateStream). Chunk files can contain really long LongArrayTags that are read in a single Read call. Due to the .NET 6 change it is possible that not all bytes are read in a single call. This will mess up the read data, the stream position and the Crc in ZLibStream that will throw an InvalidDataException.

If required I can upload a region file and an example code to reproduce the problem.

Possible solutions:
Create an extension method e.g. ReadFixedLength with the same signature as Stream.Read but without return value. This will call Stream.Read in a loop until all count bytes are read. This should throw an EndOfStreamException if it couldn't read the required number of bytes from the base steam.

Remove BoolTag

The BoolTag is an addition to this library that does not exist in the spec. This proposal is to remove it completely, and merge its little functionality into ByteTag with additional IsBool and BoolValue properties, as well as a constructor that accepts a boolean.

When reading from binary, it is impossible to differentiate from a ByteTag, so its only purpose is really for display when parsed from SNBT. That small functionality can be easily achieved by the ByteTag class.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.