Module: CraftBook::NBT

Defined in:
lib/craftbook/nbt.rb,
lib/craftbook/nbt/tag.rb,
lib/craftbook/nbt/int_tag.rb,
lib/craftbook/nbt/version.rb,
lib/craftbook/nbt/byte_tag.rb,
lib/craftbook/nbt/list_tag.rb,
lib/craftbook/nbt/long_tag.rb,
lib/craftbook/nbt/float_tag.rb,
lib/craftbook/nbt/short_tag.rb,
lib/craftbook/nbt/snbt/snbt.rb,
lib/craftbook/nbt/value_tag.rb,
lib/craftbook/nbt/double_tag.rb,
lib/craftbook/nbt/snbt/lexer.rb,
lib/craftbook/nbt/string_tag.rb,
lib/craftbook/nbt/tag_builder.rb,
lib/craftbook/nbt/compound_tag.rb,
lib/craftbook/nbt/container_tag.rb,
lib/craftbook/nbt/int_array_tag.rb,
lib/craftbook/nbt/byte_array_tag.rb,
lib/craftbook/nbt/enumerable_tag.rb,
lib/craftbook/nbt/long_array_tag.rb

Overview

Top-level namespace for the independent Named Binary Tag (NBT) module of the CraftBook API, providing classes and for reading and writing NBT tags used by the Java editions of Minecraft.

Author:

  • Eric "ForeverZer0" Freed

Defined Under Namespace

Classes: ByteArrayTag, ByteTag, CompoundTag, ContainerTag, DoubleTag, EnumerableTag, FloatTag, IntArrayTag, IntTag, ListTag, LongArrayTag, LongTag, ParseError, ShortTag, StringTag, Tag, TagBuilder, Tokenizer, ValueTag

Constant Summary collapse

ENCODING =

The encoding used for all strings.

Encoding::UTF_8
VERSION =

The version of the craftbook-nbt Gem.

"1.0.0"

Class Method Summary collapse

Class Method Details

.parse_snbt(string_nbt) ⇒ CompoundTag

Note:

This method is not safe to call in parallel from multiple threads.

Parses a stringified NBT string and creates a CompoundTag from it.

Parameters:

  • string_nbt (String)

    The stringified NBT code to parse.

Returns:

Raises:

  • (SyntaxError)

    When the source string_nbt is not valid S-NBT.

  • (ParseError)

    When a an incorrect value is specified for the type of tag it represents.

  • (ArgumentError)

    When string_nbt is nil

See Also:



21
22
23
24
25
26
27
28
# File 'lib/craftbook/nbt/snbt/snbt.rb', line 21

def self.parse_snbt(string_nbt)
  raise(ArgumentError, "input string cannot be nil or empty") if string_nbt.nil? || string_nbt.empty?
  @pos = 0
  @depth = 0
  lexer = Tokenizer.new
  @tokens = lexer.tokenize(string_nbt)
  parse_object(@tokens.first)
end

.read(io) ⇒ Tag

Deserializes a Tag instance from the specified IO-like object.

Parameters:

  • io (IO, #read)

    A IO-like object that responds to #read.

Returns:

  • (Tag)

    The deserialized tag object.



129
130
131
132
133
134
135
# File 'lib/craftbook/nbt.rb', line 129

def self.read(io)
  unless io.is_a?(IO) || io.respond_to?(:read)
    raise(ArgumentError, "object must be an IO instance or respond to #read")
  end
  type = io.readbyte
  read_type(io, type, read_string(io))
end

.read_file(path) ⇒ Tag

Note:

Compression formats supported by the specification (GZip, ZLib) will be detected and handled automatically.

Reads and deserializes a Tag from a file stored at the specified path.

Parameters:

  • path (String)

    The path to a file to read from.

Returns:

  • (Tag)

    The deserialized tag object.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/craftbook/nbt.rb', line 144

def self.read_file(path)

  File.open(path, 'rb') do |io|
    byte = io.readbyte
    io.seek(0, IO::SEEK_SET)

    stream = case byte
    when 0x78 then StringIO.new(Zlib::Inflate.inflate(io.read))
    when 0x1F then Zlib::GzipReader.new(io)
    when 0x0A then io
    else raise(ParseError, 'invalid NBT format')
    end
    read(stream)
  end
end

.write(io, tag) ⇒ Integer

Serializes and writes the specified Tag to an IO-like object.

Parameters:

  • io (IO, #write)

    An IO-like object that responds to #write

  • tag (Tag)

    A Tag instance to write. If io represents a file stream, the specification expects this to be a CompoundTag.

Returns:

  • (Integer)

    The number of bytes written.



56
57
58
59
60
61
# File 'lib/craftbook/nbt.rb', line 56

def self.write(io, tag)
  unless io.is_a?(IO) || io.respond_to?(:write)
    raise(ArgumentError, "object must be an IO instance or respond to #write")
  end
  write_tag(io, tag, false)
end

.write_file(path, compound_tag, **opts) ⇒ Integer

Serializes and writes the specified Tag to a file at the specified path. If file already exists at that location, it will be overwritten.

Parameters:

  • path (String)

    The path to the file to write to.

  • compound_tag (Tag)

    A CompoundTag instance to write.

  • opts (Hash{Symbol => Symbol})

    Options hash.

Options Hash (**opts):

  • :compression (Symbol) — default: :gzip

    The type of compression to use when writing, if any. Valid values include:

    • :none No compression
    • :gzip GZip compression
    • :zlib ZLib compression (DEFLATE with 2 byte header and post-fixed CRC checksum)
  • :level (Symbol) — default: :default

    The level of compression to use, ignored when no compression is specified. Valid values include:

    • :default The default compression employed by the specified algorithm.
    • :none No compression. Compressions formats will still include their additional meta-data.
    • :optimal Favor high compression-rate over speed.
    • :fastest Favor speed over compression-rate.

Returns:

  • (Integer)

    The number of bytes written.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/craftbook/nbt.rb', line 88

def self.write_file(path, compound_tag, **opts)

  compression = opts[:compression] || :gzip
  level = case opts[:level]
  when nil then Zlib::DEFAULT_COMPRESSION
  when :default then Zlib::DEFAULT_COMPRESSION
  when :none then Zlib::NO_COMPRESSION
  when :optimal then Zlib::BEST_COMPRESSION
  when :fastest then Zlib::BEST_SPEED
  else raise(ArgumentError, "invalid compression level specified: #{opts[:level]}")
  end

  written = 0
  File.open(path, 'wb') do |io|

    case compression
    when :none then written = write(io, compound_tag)
    when :gzip
      gzip = Zlib::GzipWriter.new(io, level)
      #noinspection RubyMismatchedParameterType
      written = write(gzip, compound_tag)
      gzip.finish
    when :zlib
      buffer = StringIO.new
      #noinspection RubyMismatchedParameterType
      write(buffer, compound_tag)
      compressed = Zlib::Deflate.deflate(buffer.string, level)
      written = io.write(compressed)
    else
      raise(ArgumentError, "invalid compression specified: #{compression}")
    end
  end

  written
end