aternos/nbt

PHP库,用于解析、修改和创建NBT对象

v1.12.0 2024-08-13 15:32 UTC

README

Minecraft的命名二进制标签(NBT)格式的完整PHP实现。

与其他实现相比,此库支持64位类型,包括相对较新的TAG_Long_Array

此外,NBT格式的三个版本(Java版本、基岩版/小端和基岩版/VarInt)都得到了支持。

安装

composer require aternos/nbt

用法

读取NBT数据

要读取现有的NBT数据,需要一个Reader对象。此库实现了不同的读者,用于从字符串中读取NBT数据。

//Read uncompressed NBT data
$reader = new \Aternos\Nbt\IO\Reader\StringReader("...nbtData...", \Aternos\Nbt\NbtFormat::BEDROCK_EDITION);

//Read gzip compressed NBT data
$gzipReader = new \Aternos\Nbt\IO\Reader\GZipCompressedStringReader("...compressedNbtData...", \Aternos\Nbt\NbtFormat::JAVA_EDITION);

//Read zlib compressed NBT data
$zlibReader = new \Aternos\Nbt\IO\Reader\ZLibCompressedStringReader("...compressedNbtData...", \Aternos\Nbt\NbtFormat::BEDROCK_EDITION_NETWORK);

请注意,读者对象也用于指定NBT格式版本。可用的有\Aternos\Nbt\NbtFormat::JAVA_EDITION\Aternos\Nbt\NbtFormat::BEDROCK_EDITION\Aternos\Nbt\NbtFormat::BEDROCK_EDITION_NETWORK

可以通过实现\Aternos\Nbt\IO\Reader\Reader接口或扩展\Aternos\Nbt\IO\Reader\AbstractReader类来创建更高级的读者。

读者对象可以用来加载NBT标签。

$reader = new \Aternos\Nbt\IO\Reader\StringReader("...nbtData...", \Aternos\Nbt\NbtFormat::BEDROCK_EDITION);

$tag = \Aternos\Nbt\Tag\Tag::load($reader);

理论上,任何类型的NBT标签都可能被返回,但在现实中,所有NBT文件都是以复合标签或列表标签开始的。

操作NBT结构

类型为TAG_ByteTAG_ShortTAG_IntTAG_LongTAG_FloatTAG_DoubleTAG_String的标签值可以通过它们的getValue()setValue()函数访问。

$myInt new \Aternos\Nbt\Tag\IntTag();

$myInt->setValue(42);
echo $myInt->getValue(); // 42

对于字符串标签,getValue()setValue()使用UTF-8编码,并在序列化时根据所选的NBT版本进行字符串转换。

复合标签、列表标签和数组标签实现了ArrayAccessCountableIterator接口,因此可以像数组一样访问。

$myCompound = new \Aternos\Nbt\Tag\CompoundTag();

$myCompound["myInt"] = (new \Aternos\Nbt\Tag\IntTag())->setValue(42);
$myCompound["myFloat"] = (new \Aternos\Nbt\Tag\IntTag())->setValue(42.42);
echo count($myCompound); // 2

//Manually setting a list's type is not strictly necessary,
//since it's type will be set automatically when the first element is added
$myList = (new \Aternos\Nbt\Tag\ListTag())->setContentTag(\Aternos\Nbt\Tag\TagType::TAG_String);

$myList[] = (new \Aternos\Nbt\Tag\StringTag())->setValue("Hello");
$myList[] = (new \Aternos\Nbt\Tag\StringTag())->setValue("World");

也可以通过getter/setter函数访问复合标签。这在使用新的PHP null安全操作符时特别有用。

/** @var \Aternos\Nbt\Tag\CompoundTag $playerDat */
$playerDat = \Aternos\Nbt\Tag\Tag::load($reader);

$playerDat->set("foo", (new \Aternos\Nbt\Tag\StringTag())->setValue("bar")); //Set a value
$playerDat->delete("foo"); //Delete a value

$playerName = $playerDat->getCompound("bukkit")?->getString("lastKnownName")?->getValue();
echo $playerName ?? "Unknown player name";

序列化NBT结构

与读取NBT数据的读者对象类似,需要一个写入对象来写入NBT数据。

//Write uncompressed NBT data
$writer = (new \Aternos\Nbt\IO\Writer\StringWriter())->setFormat(\Aternos\Nbt\NbtFormat::BEDROCK_EDITION);

//Write gzip compressed NBT data
$gzipWriter = (new \Aternos\Nbt\IO\Writer\GZipCompressedStringWriter())->setFormat(\Aternos\Nbt\NbtFormat::JAVA_EDITION);

//Write zlib compressed NBT data
$gzipWriter = (new \Aternos\Nbt\IO\Writer\ZLibCompressedStringWriter())->setFormat(\Aternos\Nbt\NbtFormat::BEDROCK_EDITION_NETWORK);

写入对象使用的NBT版本可能与最初用于读取NBT结构的读者对象使用的版本不同。因此,可以使用此库在不同格式之间转换NBT结构。

可以通过实现\Aternos\Nbt\IO\Writer\Writer接口或扩展\Aternos\Nbt\IO\Writer\AbstractWriter类来创建更高级的写入对象。

写入对象可以用来写入/序列化NBT结构。

$writer = (new \Aternos\Nbt\IO\Writer\StringWriter())->setFormat(\Aternos\Nbt\NbtFormat::BEDROCK_EDITION);

$tag->write($writer);
file_put_contents("data.nbt", $writer->getStringData());

基岩版level.dat

虽然基岩版的level.dat文件是一个未压缩的NBT文件,但其NBT数据前面有两个32位小端整数。

第一个似乎是基岩版存储工具的版本,它也存储在NBT结构的StorageVersion标签中。

第二个数字是文件NBT结构的大小(不包括前面的两个整数)。

基岩版level.dat文件可以这样读取:

$data = file_get_contents("level.dat");

$version = unpack("V", $data)[1];
$dataLength = unpack("V", $data, 4)[1];

if($dataLength !== strlen($data) - 8) {
    throw new Exception("Invalid level.dat data length");
}
$tag = \Aternos\Nbt\Tag\Tag::load(new \Aternos\Nbt\IO\Reader\StringReader(substr($data, 8), \Aternos\Nbt\NbtFormat::BEDROCK_EDITION));