마인크래프트/NBT
NBT(Named Binary Tag)는 마인크래프트에서 게임 데이터를 저장하는 트리 구조의 바이너리 형식이다. Notch가 마인크래프트 초기 개발 단계에서 만든 이 형식은 현재까지도 월드 저장, 플레이어 데이터, 아이템 정보 등 거의 모든 게임 데이터를 저장하는 핵심 시스템으로 사용되고 있다.
NBT는 단순하면서도 강력한 구조를 가지고 있어, 복잡한 게임 데이터를 효율적으로 저장하고 불러올 수 있다. 이름에서 알 수 있듯이 각 태그는 고유한 이름을 가지며, 이를 통해 데이터를 계층적으로 구성할 수 있다. 예를 들어, 플레이어의 인벤토리는 여러 아이템 태그를 포함하는 리스트 태그로 저장되며, 각 아이템은 다시 ID, 수량, 내구도 등의 하위 태그를 포함한다.
태그 타입
NBT는 12가지의 기본 태그 타입을 정의하고 있으며, 각각은 고유한 ID를 가진다.
기본 타입
TAG_End (ID: 0)는 복합 태그나 리스트 태그의 끝을 나타내는 특수한 태그다. 실제 데이터를 저장하지는 않으며, 파일 파싱 시 구조의 끝을 알리는 역할을 한다.
TAG_Byte (ID: 1)는 8비트 부호 있는 정수를 저장한다. 주로 불리언 값이나 작은 숫자를 저장할 때 사용되며, -128부터 127까지의 값을 표현할 수 있다. 예를 들어, 플레이어의 게임 모드나 날씨 상태 같은 간단한 속성을 저장하는 데 사용된다.
TAG_Short (ID: 2)는 16비트 정수를, TAG_Int (ID: 3)는 32비트 정수를, TAG_Long (ID: 4)는 64비트 정수를 저장한다. 이들은 각각 다른 범위의 숫자를 표현할 때 사용되며, 예를 들어 아이템의 내구도는 Short로, 경험치는 Int로, 월드 시간은 Long으로 저장된다.
부동소수점 타입
TAG_Float (ID: 5)와 TAG_Double (ID: 6)은 각각 32비트와 64비트 부동소수점 수를 저장한다. 엔티티의 정확한 좌표나 회전 각도처럼 소수점이 필요한 값들을 저장할 때 사용된다. 플레이어의 위치가 대표적인 예로, X, Y, Z 좌표가 모두 Double 타입으로 저장된다.
배열 타입
TAG_Byte_Array (ID: 7)는 바이트 배열을 저장하며, 주로 청크의 블록 데이터나 바이옴 정보를 저장하는 데 사용된다. TAG_Int_Array (ID: 11)와 TAG_Long_Array (ID: 12)는 각각 정수 배열과 긴 정수 배열을 저장하며, 더 큰 숫자들의 집합을 효율적으로 저장할 때 사용된다.
문자열과 복합 타입
TAG_String (ID: 8)은 UTF-8로 인코딩된 문자열을 저장한다. 아이템 이름, 표지판의 텍스트, 커스텀 이름 등 게임 내의 모든 텍스트가 이 타입으로 저장된다.
TAG_List (ID: 9)는 동일한 타입의 태그들을 순서대로 저장하는 리스트다. 인벤토리의 아이템 목록이나 엔티티 목록처럼 같은 종류의 데이터 여러 개를 저장할 때 사용된다.
TAG_Compound (ID: 10)는 가장 복잡하면서도 중요한 타입으로, 이름을 가진 태그들의 집합을 저장한다. 다른 프로그래밍 언어의 맵이나 딕셔너리와 유사한 구조를 가지며, 플레이어 데이터나 블록 엔티티처럼 여러 속성을 가진 복잡한 객체를 표현할 때 사용된다.
파일 형식
압축 형식
마인크래프트는 NBT 데이터를 저장할 때 대부분 GZip 압축을 사용한다. 이는 텍스트 기반의 데이터가 많은 NBT 구조상 상당한 용량 절감 효과를 가져온다. 월드 파일, 플레이어 데이터, 구조물 파일 등이 모두 압축된 NBT 형식으로 저장된다.
일부 경우에는 압축되지 않은 NBT 형식도 사용된다. 네트워크 패킷이나 메모리 내부에서는 압축/해제 오버헤드를 피하기 위해 비압축 형식을 사용하며, 일부 외부 도구들도 편집의 편의를 위해 비압축 형식을 지원한다.
바이너리 구조
NBT 파일은 루트 태그로 시작하며, 이는 항상 TAG_Compound 타입이다. 각 태그는 타입 ID, 이름 길이, 이름, 페이로드 순서로 저장된다. TAG_End는 이름을 가지지 않으며, TAG_List의 요소들도 개별 이름을 가지지 않는다는 점이 특징이다.
실제 사용 예시
플레이어 데이터
플레이어의 데이터는 복잡한 TAG_Compound 구조로 저장된다. 최상위 레벨에는 체력, 허기, 경험치 같은 기본 속성들이 있고, Inventory 태그 안에는 각 슬롯의 아이템 정보가 리스트로 저장된다. 각 아이템은 다시 ID, Count, tag 등의 하위 태그를 가지며, 인챈트나 커스텀 이름이 있는 경우 tag 안에 추가 정보가 저장된다.
{ "Health": 20.0f, "foodLevel": 20, "Inventory": [ { "Slot": 0b, "id": "minecraft:diamond_sword", "Count": 1b, "tag": { "Enchantments": [ {"id": "minecraft:sharpness", "lvl": 5s} ] } } ] }
블록 엔티티
상자, 화로, 표지판 같은 블록 엔티티들도 NBT로 데이터를 저장한다. 상자의 경우 Items 리스트에 보관된 아이템들이, 표지판의 경우 각 줄의 텍스트가 별도의 태그로 저장된다. 이러한 구조 덕분에 복잡한 데이터를 가진 블록들도 효율적으로 관리될 수 있다.
외부 도구
NBT 편집기
NBTExplorer는 가장 널리 사용되는 NBT 편집 도구로, 직관적인 트리 구조 인터페이스를 제공한다. 월드 파일, 플레이어 데이터, 구조물 파일 등을 열어 직접 수정할 수 있으며, 잘못된 데이터로 인한 월드 손상을 복구하는 데도 유용하다.
webNBT는 웹 기반 NBT 뷰어로, 별도의 프로그램 설치 없이 브라우저에서 NBT 파일을 확인할 수 있다. 주로 간단한 확인 작업이나 구조 분석에 사용된다.
프로그래밍 라이브러리
다양한 프로그래밍 언어에서 NBT를 다룰 수 있는 라이브러리가 제공된다. Java의 경우 마인크래프트 자체 코드나 SpigotAPI를 통해 NBT를 조작할 수 있고, Python에서는 python-nbt, JavaScript에서는 prismarine-nbt 등의 라이브러리를 사용할 수 있다.
SNBT (Stringified NBT)
SNBT는 NBT를 사람이 읽을 수 있는 텍스트 형식으로 표현한 것이다. 주로 명령어에서 NBT 데이터를 지정할 때 사용되며, JSON과 유사하지만 NBT의 타입 정보를 보존하는 특징이 있다. 예를 들어, 숫자 뒤에 b, s, f, d, L 등의 접미사를 붙여 타입을 명시한다.
/give @p minecraft:diamond_sword{Enchantments:[{id:"minecraft:sharpness",lvl:5s}]} 1
이 명령어에서 중괄호로 둘러싸인 부분이 SNBT 형식이며, 게임 내에서 직접 복잡한 NBT 구조를 만들 수 있게 해준다.
제한사항과 주의점
NBT는 강력하지만 몇 가지 제한사항이 있다. 태그 이름은 32,767자를 초과할 수 없고, 문자열 값도 같은 제한을 가진다. 또한 복합 태그의 깊이가 너무 깊어지면 (일반적으로 512 레벨 이상) 게임이 데이터를 읽지 못할 수 있다.
잘못된 NBT 데이터는 월드 손상이나 게임 크래시를 일으킬 수 있으므로, 외부 도구로 NBT를 편집할 때는 항상 백업을 먼저 하는 것이 권장된다. 특히 월드 파일이나 플레이어 데이터를 수정할 때는 더욱 신중해야 한다.