Compare commits

...

10 Commits

Author SHA1 Message Date
Ling ca2d7f49e1 增强输入处理和文件保存功能
在 `Scanner.cs` 中添加了对 `System.Diagnostics` 的引用,并优化了 `MoveNext` 方法以简化空白字符处理和错误处理逻辑。

在 `StringNbt.cs` 中引入了 `System.IO` 和 `System.Numerics`,并修改了解析逻辑以增强输入处理和错误检查。新增了 `IsLineEnd` 方法,并简化了 `MoveNext` 的调用。添加了 `SaveToFile` 和 `SaveToStream` 方法,以支持将 `CompoundTag` 保存为 SNBT 格式。
2025-06-22 21:16:35 +08:00
ForeverZer0 760c75cadc
Create dotnet-9.yml 2024-12-22 15:18:33 -05:00
ForeverZer0 bd80810e2c
Merge pull request #33 from vfrz/master
Fixed bug where top-level compound tags without a name would be skipped by the TagWriter.
2024-05-17 15:21:44 -04:00
vfrz ad4dcf63f3 Allow empty name only for root compound tag 2024-05-17 19:05:02 +02:00
Valentin Fritz 7f020e1c51
Allow compound tag with empty name 2024-05-17 00:42:51 +02:00
ForeverZer0 d6e0075b74
Merge pull request #29 from Sitterr/master
Wrapping deflated streams in BufferedStream when reading
2024-03-14 20:55:53 -04:00
Sitterr 964821d1fc Wrapped GZipStream and ZlibStream in BufferedStream when reading 2024-03-15 00:33:06 +02:00
ForeverZer0 190b694668
Removed console debug message from TagBuilder 2024-02-08 06:57:32 -05:00
Eric Freed 82399f243b fixed bug with Stringify giving ToString output 2023-09-01 01:54:21 -04:00
Eric Freed f22ae236b6 Updated README and the CODE_OF_CONDUCT 2023-08-27 16:38:03 -04:00
26 changed files with 225 additions and 208 deletions

28
.github/workflows/dotnet-9.yml vendored Normal file
View File

@ -0,0 +1,28 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal

View File

@ -1,75 +1,16 @@
# Contributor Covenant Code of Conduct
# This is Just a Code Repo Code of Conduct
## Our Pledge
This is:
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
* A public repository to share code and track issues
## Our Standards
This is NOT:
Examples of behavior that contributes to creating a positive environment
include:
* Your priest
* Your parent
* Your moral guide
* Anything that has any authority over you whatsoever
* Use commonsense, which by definition does not need explanation
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Not feeling compelled to try and use a "group" you choose to identify with to elevate your position
As such, it pure silliness to issue a "code of conduct", but alas, GitHub really wants us to put them here. Conduct yourself however you see fit to conduct yourself, the same as you do in every other interaction in life.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Pushing your political ideologies onto others
* Trolling, insulting/derogatory comments, and personal attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at efreed09@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
If the very idea that a code repository can be considered "unsafe", then I strongly suggest you not interact with it. I don't even know what that means, nor do I wish to adopt the modern-era madness of pretending I do. I can assure you of one thing: in all of history, a code repository has never hurt anyone. I don't believe that this one will be an exception.

View File

@ -16,15 +16,7 @@ A CLS-compliant implementation of the Named Binary Tag (NBT) specifications (Jav
* **Ease-of-use:** An intuitive API design, following the style and conventions of the .NET runtime, with full Intellisense for every member: Spend more time being productive and less time digging through documentation.
* **Performance:** Leverages the power of modern C# language features, including `Span` with `stackalloc`, `MemoryMarshal`, etc. This allows for a type-safe way to reinterpret raw buffers without pointers or making unnecessary copies of buffers, a common pitfall with serialization in type-safe languages.
* **Concurrency:** Includes standard async/await concurrency patterns for reading and writing.
* **Cross-platform and cross-language support:** Fully CLR compliant and build against .NET Standard 2.1, allowing support for any CLR language (i.e. C#, Visual Basic, F#, etc.) for the following runtime versions or greater:
* .NET Standard 2.1
* .NET 5.0
* .NET Core 3.0
* Mono 6.4
* Xamarin.iOS 12.16
* Xamarin.Mac 5.16
* Xamarin.Android 10.0
* Unity 2021.2.0b6
* **Cross-language support:** Fully CLR compliant and build against .NET 7.0, allowing support for any CLR language (i.e. C#, Visual Basic, F#, etc.) with the supported runtime version.
* **Callbacks:** Can subscribe to events that get a callback as the parser steps through a stream to get immediate feedback without waiting for document completion. This allows subscribers to even parse the payload themselves and handle the event entirely.
* **String NBT**: Supports both generating and parsing arbitrary SNBT strings.

View File

@ -175,8 +175,8 @@ public static class NbtFile
return compression switch
{
CompressionType.None => stream,
CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress, false),
CompressionType.ZLib => new ZLibStream(stream, CompressionMode.Decompress),
CompressionType.GZip => new BufferedStream(new GZipStream(stream, CompressionMode.Decompress, false)),
CompressionType.ZLib => new BufferedStream(new ZLibStream(stream, CompressionMode.Decompress)),
_ => throw new ArgumentOutOfRangeException(nameof(compression), compression, null)
};
}

1
SharpNBT/README.md Symbolic link
View File

@ -0,0 +1 @@
../README.md

View File

@ -1,5 +1,6 @@
using System;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
@ -44,12 +45,26 @@ internal ref struct Scanner
SyntaxError("Unexpected end of input.");
return false;
}
if (skipWhitespace && char.IsWhiteSpace(Current))
//Debug.Write(Current);
if (skipWhitespace && string.IsNullOrWhiteSpace(Current.ToString()))
goto ReadChar;
return true;
}
public bool MoveNext(bool fail)
{
ReadChar:
Position++;
if (Position >= Source.Length)
{
if (fail)
SyntaxError("Unexpected end of input.");
return false;
}
//Debug.Write(Current);
if (char.IsWhiteSpace(Current) && Current != '\n')
goto ReadChar;
return true;
}
public void AssertChar(char c)
{
if (Current != c)

View File

@ -1,10 +1,11 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Text;
using JetBrains.Annotations;
namespace SharpNBT.SNBT;
@ -83,6 +84,12 @@ public static class StringNbt
while (!scanner.IsEndOfInput)
{
// Closing brace encountered, break loop.
if (scanner.Current == '}')
{
// scanner.MoveNext(true, false);
break;
}
// Read the name of the tag
var childName = ParseString(ref scanner, out _);
@ -94,22 +101,22 @@ public static class StringNbt
scanner.MoveNext(true, true);
var tag = ParseTag(childName, ref scanner);
result.Add(tag);
scanner.MoveNext(true, true);
scanner.MoveNext( true);
// Comma encountered, read another tag.
if (scanner.Current == ',')
if (IsLineEnd(scanner.Current))
{
scanner.MoveNext(true, true);
continue;
}
// Closing brace encountered, break loop.
if (scanner.Current == '}')
{
// scanner.MoveNext(true, false);
break;
}
// Invalid character
scanner.SyntaxError($"Expected ',' or '}}', got '{scanner.Current}'.");
}
@ -309,24 +316,28 @@ public static class StringNbt
var list = new List<Tag>();
while (true)
{
var child = ParseTag(null, ref scanner);
list.Add(child);
scanner.MoveNext(true, true);
// Comma encountered, read another tag.
if (scanner.Current == ',')
{
scanner.MoveNext(true, true);
continue;
}
// Closing brace encountered, break loop.
if (scanner.Current == ']')
{
break;
}
var child = ParseTag(null, ref scanner);
list.Add(child);
scanner.MoveNext(true);
// Comma encountered, read another tag.
if (IsLineEnd(scanner.Current))
{
scanner.MoveNext(true, true);
continue;
}
if (scanner.Current == ']')
{
break;
}
// Invalid character
scanner.SyntaxError($"Expected ',' or ']', got '{scanner.Current}'.");
}
@ -349,7 +360,7 @@ public static class StringNbt
var c = char.ToLowerInvariant(scanner.Current);
if (c == ']')
break;
if (char.IsNumber(c) || c == ',')
if (char.IsNumber(c) ||c == ',' || c == '-')
continue;
if (c is not ('b' or 'l'))
scanner.SyntaxError($"Invalid character '{c}' in integer array.");
@ -366,5 +377,62 @@ public static class StringNbt
}
private const StringSplitOptions SplitOpts = StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries;
private static readonly char[] SplitSeparators = new[] { ',', 'b', 'B', 'l', 'L' };
private static readonly char[] SplitSeparators = new[] { ',', 'b', 'B', 'l', 'L','\n' };
private static bool IsLineEnd(char c)
{
return c == ',' || c == '\n' || c == '\r';
}
/// <summary>
/// Saves a <see cref="CompoundTag"/> to a file in SNBT format.
/// </summary>
/// <param name="tag">The <see cref="CompoundTag"/> to save.</param>
/// <param name="filePath">The path to the file where the SNBT data will be saved.</param>
/// <param name="prettyPrint">Whether to format the output in a human-readable format.</param>
/// <exception cref="ArgumentNullException">When <paramref name="tag"/> is <see langword="null"/>.</exception>
/// <exception cref="IOException">When there is an error writing to the file.</exception>
public static void SaveToFile(CompoundTag tag, string filePath, bool prettyPrint = false)
{
if (tag == null)
throw new ArgumentNullException(nameof(tag));
string snbt = prettyPrint ? tag.PrettyPrinted() : tag.Stringify(true);
try
{
File.WriteAllText(filePath, snbt, Encoding.UTF8);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException)
{
throw new IOException($"Failed to write SNBT to file '{filePath}': {ex.Message}", ex);
}
}
/// <summary>
/// Saves a <see cref="CompoundTag"/> to a file in SNBT format.
/// </summary>
/// <param name="tag">The <see cref="CompoundTag"/> to save.</param>
/// <param name="stream">The stream where the SNBT data will be written.</param>
/// <param name="prettyPrint">Whether to format the output in a human-readable format.</param>
/// <param name="encoding">The text encoding to use. Defaults to UTF-8 if null.</param>
/// <exception cref="ArgumentNullException">When <paramref name="tag"/> or <paramref name="stream"/> is <see langword="null"/>.</exception>
/// <exception cref="IOException">When there is an error writing to the stream.</exception>
public static void SaveToStream(CompoundTag tag, Stream stream, bool prettyPrint = false, Encoding? encoding = null)
{
if (tag == null)
throw new ArgumentNullException(nameof(tag));
if (stream == null)
throw new ArgumentNullException(nameof(stream));
if (!stream.CanWrite)
throw new IOException("Cannot write to the specified stream.");
string snbt = prettyPrint ? tag.PrettyPrinted() : tag.Stringify(true);
encoding ??= Encoding.UTF8;
using var writer = new StreamWriter(stream, encoding, leaveOpen: true);
writer.Write(snbt);
writer.Flush();
}
}

View File

@ -13,11 +13,13 @@
<PackageTags>nbt;named binary tag;minecraft;serialization;java;bedrock;pocket edition;varint;varlong;zlib</PackageTags>
<Copyright>Copyright © Eric Freed 2021</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>1.2.0</PackageVersion>
<AssemblyVersion>1.2.0</AssemblyVersion>
<FileVersion>1.2.0</FileVersion>
<PackageVersion>1.3.1</PackageVersion>
<AssemblyVersion>1.3.1</AssemblyVersion>
<FileVersion>1.3.1</FileVersion>
<LangVersion>latestmajor</LangVersion>
<Nullable>enable</Nullable>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>Hotfix to correct bug with Stringified output.</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -25,6 +27,7 @@
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" Visible="true" PackagePath="/"/>
<None Include="icon.png" Pack="true" Visible="true" PackagePath="" />
</ItemGroup>

View File

@ -437,8 +437,7 @@ public class TagBuilder
/// <summary>Closes this context.</summary>
public void Dispose()
{
Console.WriteLine(Tag);
closeHandler.Invoke();
}
}
}
}

View File

@ -306,7 +306,10 @@ public class TagWriter : TagIO
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteTypeAndName(Tag tag)
{
if (tag.Parent is ListTag || string.IsNullOrEmpty(tag.Name))
if (tag.Parent is ListTag)
return;
if (string.IsNullOrEmpty(tag.Name) && !(tag is CompoundTag && tag.Parent is null))
return;
BaseStream.WriteByte((byte) tag.Type);
@ -384,4 +387,4 @@ public class TagWriter : TagIO
else
BaseStream.Write(GetBytes(count), 0, sizeof(int));
}
}
}

View File

@ -82,10 +82,12 @@ public abstract class ArrayTag<T> : Tag, IReadOnlyList<T> where T : unmanaged, I
/// <returns>A reference to the first value in the underlying array.</returns>
public ref T GetPinnableReference() => ref array[0] ;
private protected string Stringify(char prefix, char? suffix)
private protected string Stringify(bool named, char prefix, char? suffix)
{
var sb = new StringBuilder(32 + array.Length * 4);
sb.Append($"{StringifyName}:[{prefix};");
if (named)
sb.Append($"{StringifyName}:");
sb.Append($"[{prefix};");
for (var i = 0; i < array.Length; i++)
{

View File

@ -49,12 +49,12 @@ public class BoolTag : Tag
/// <param name="tag">The tag to convert.</param>
/// <returns>The tag represented as a <see cref="byte"/>.</returns>
public static implicit operator bool(BoolTag tag) => tag.Value;
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{(Value ? "true" : "false")}";
/// <inheritdoc />
public override string Stringify(bool named = true)
{
var value = Value ? "true" : "false";
return named ? $"{StringifyName}:{value}" : value;
}
}

View File

@ -75,11 +75,7 @@ public class ByteArrayTag : ArrayTag<byte>
var word = Count == 1 ? Strings.WordElement : Strings.WordElements;
return $"TAG_Byte_Array({PrettyName}): [{Count} {word}]";
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => Stringify('B', 'b');
/// <inheritdoc />
public override string Stringify(bool named = true) => Stringify(named, 'B', 'b');
}

View File

@ -132,10 +132,6 @@ public class ByteTag : NumericTag<byte>
[CLSCompliant(false)]
public static implicit operator sbyte(ByteTag tag) => unchecked((sbyte)tag.Value);
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value}B";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}B" : $"{Value}B";
}

View File

@ -242,36 +242,35 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
buffer.AppendLine(space + "}");
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify()
/// <inheritdoc />
public override string Stringify(bool named = true)
{
var sb = new StringBuilder();
sb.Append($"{StringifyName}:{{");
if (named)
sb.Append($"{StringifyName}:");
sb.Append('{');
var i = 0;
foreach (var value in dict.Values)
{
if (i++ > 0)
sb.Append(',');
sb.Append(value);
sb.Append(value.Stringify(true));
}
sb.Append('}');
return sb.ToString();
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <param name="topLevel">Flag indicating if this is the top-level tag that should be wrapped in braces.</param>
/// <param name="named">Flag indicating if the name of the NBT should be written.</param>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public string Stringify(bool topLevel)
public string Stringify(bool topLevel, bool named)
{
var str = Stringify();
var str = Stringify(named);
return topLevel ? $"{{{str}}}" : str;
}

View File

@ -41,10 +41,6 @@ public class DoubleTag : NumericTag<double>
/// <returns>The tag represented as a <see cref="double"/>.</returns>
public static implicit operator double(DoubleTag tag) => tag.Value;
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value:0.0}D";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}D" : $"{Value}D";
}

View File

@ -31,11 +31,7 @@ public sealed class EndTag : Tag
{
// Do nothing
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => string.Empty;
/// <inheritdoc />
public override string Stringify(bool named = true) => string.Empty;
}

View File

@ -41,10 +41,6 @@ public class FloatTag : NumericTag<float>
/// <returns>The tag represented as a <see cref="float"/>.</returns>
public static implicit operator float(FloatTag tag) => tag.Value;
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value:0.0}F";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}F" : $"{Value}F";
}

View File

@ -73,11 +73,7 @@ public class IntArrayTag : ArrayTag<int>
var word = Count == 1 ? Strings.WordElement : Strings.WordElements;
return $"TAG_Int_Array({PrettyName}): [{Count} {word}]";
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => Stringify('I', null);
/// <inheritdoc />
public override string Stringify(bool named = true) => Stringify(named, 'I', null);
}

View File

@ -69,10 +69,6 @@ public class IntTag : NumericTag<int>
[CLSCompliant(false)]
public static implicit operator uint(IntTag tag) => unchecked((uint)tag.Value);
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value}";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}" : $"{Value}";
}

View File

@ -175,15 +175,21 @@ public class ListTag : Tag, IList<Tag>
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify()
public override string Stringify(bool named = true)
{
var strings = new string[Count];
for (var i = 0; i < strings.Length; i++)
strings[i] = this[i].Stringify();
// TODO: Use StringBuilder
return $"{StringifyName}:[{string.Join(',', strings)}]";
var sb = new StringBuilder();
if (named)
sb.Append($"{StringifyName}:");
sb.Append('[');
for (var i = 0; i < list.Count; i++)
{
if (i > 0)
sb.Append(',');
sb.Append(list[i].Stringify(false));
}
sb.Append(']');
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -71,10 +71,6 @@ public class LongArrayTag : ArrayTag<long>
return $"TAG_Long_Array({PrettyName}): [{Count} {word}]";
}
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => Stringify('L', 'l');
/// <inheritdoc />
public override string Stringify(bool named = true) => Stringify(named, 'L', 'l');
}

View File

@ -69,10 +69,6 @@ public class LongTag : NumericTag<long>
[CLSCompliant(false)]
public static implicit operator ulong(LongTag tag) => unchecked((ulong)tag.Value);
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value}L";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}L" : $"{Value}L";
}

View File

@ -75,10 +75,6 @@ public class ShortTag : NumericTag<short>
[CLSCompliant(false)]
public static implicit operator ushort(ShortTag tag) => unchecked((ushort)tag.Value);
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:{Value}S";
/// <inheritdoc />
public override string Stringify(bool named = true) => named ? $"{StringifyName}:{Value}S" : $"{Value}S";
}

View File

@ -47,13 +47,12 @@ public class StringTag : Tag, IEquatable<StringTag>
/// <param name="tag">The tag to convert.</param>
/// <returns>The tag represented as a <see cref="string"/>.</returns>
public static implicit operator string(StringTag tag) => tag.Value;
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public override string Stringify() => $"{StringifyName}:\"{Value}\""; // TODO: Does this get properly escaped?
/// <inheritdoc />
public override string Stringify(bool named = true)
{
return named ? $"{StringifyName}:\"{Value}\"" : $"\"{Value}\"";
}
/// <inheritdoc />
public bool Equals(StringTag? other)

View File

@ -212,9 +212,10 @@ public abstract class Tag : IEquatable<Tag>, ICloneable
/// <summary>
/// Gets the <i>string</i> representation of this NBT tag (SNBT).
/// </summary>
/// <param name="named">Flag indicating if the name of the tag should be written.</param>
/// <returns>This NBT tag in SNBT format.</returns>
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
public abstract string Stringify();
public abstract string Stringify(bool named = true);
/// <summary>
/// Gets the name in a formatted properly for SNBT.