Files
strom/mc/level/litematic/main.go
T
2026-06-27 13:54:54 +02:00

154 lines
3.0 KiB
Go

package litematic
import (
"errors"
"io"
"os"
"strings"
"unsafe"
data2 "github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/level"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/util"
"github.com/go-viper/mapstructure/v2"
)
var OutOfBoundsErr = errors.New("out of bounds")
const mcNamespaced = "minecraft:"
type Position struct {
X, Y, Z int32
}
func (p Position) FixNegatives() Position {
if p.X < 0 {
p.X *= -1
}
if p.Y < 0 {
p.Y *= -1
}
if p.Z < 0 {
p.Z *= -1
}
return p
}
type MetaData struct {
Author string
Description string
Name string
TimeCreated int64
TimeModified int64
TotalBlocks int32
TotalVolume int32
RegionCount int32
EnclosingSize Position
}
type BlockState struct {
Name string
Properties map[string]string
}
type Region struct {
Position Position
Size Position
BlockStatePalette []BlockState
BlockStates []int64
*level.Storage
}
func (s *Structure) GetBlockAt(region string, x, y, z int32) (stateId int32, err error) {
r := s.Regions[region]
if r.Size.X < 0 {
r.Size.X *= -1
}
if r.Size.Y < 0 {
r.Size.Y *= -1
}
if r.Size.Z < 0 {
r.Size.Z *= -1
}
if x >= r.Size.X || y >= r.Size.Y || z >= r.Size.Z || x < 0 || y < 0 || z < 0 {
err = OutOfBoundsErr
return
}
i := x + z*r.Size.X + y*r.Size.X*r.Size.Z
stateId, err = r.Get(i)
return
}
type File struct {
Version int32
SubVersion int32
MinecraftDataVersion int32
Metadata MetaData
Regions map[string]Region
}
type Structure struct {
File
Version string
}
func LoadFromPath(path string) (s *Structure, err error) {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
s, err = LoadFromFile(f)
return
}
func LoadFromFile(r io.Reader) (s *Structure, err error) {
n, err := nbt.ReadUnstructuredFile(r)
if err != nil {
return
}
return Load(n.Value)
}
func Load(v any) (s *Structure, err error) {
s = &Structure{}
err = mapstructure.Decode(v, &s.File)
if err != nil {
return
}
ret, err := data2.LookUpVersionByDataVersion(s.MinecraftDataVersion)
if err != nil {
return
}
s.Version = ret.MinecraftVersion
for k, region := range s.Regions {
palette := make([]int32, len(region.BlockStatePalette))
for i, b := range region.BlockStatePalette {
var paletteEntry int32
paletteEntry, err = data2.StateIdFromBlocKAndStateMap(s.Version, strings.TrimPrefix(b.Name, mcNamespaced), b.Properties)
if err != nil {
return
}
palette[i] = paletteEntry
}
sizeFixed := region.Size.FixNegatives()
l := sizeFixed.X * sizeFixed.Y * sizeFixed.Z
bpe := util.BpeByNum(float64(len(region.BlockStatePalette)))
uintSlice := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.SliceData(region.BlockStates))), len(region.BlockStates))
region.Storage, err = level.StorageFormat{
AvailableBpes: []uint8{bpe},
BiggestDirect: false,
Len: l,
}.Import(uintSlice, bpe, palette)
if err != nil {
return
}
s.Regions[k] = region
}
return
}