litematic format
This commit is contained in:
@@ -46,6 +46,7 @@ func LookUpVersionByDataVersion(version int32) (ret VersionInfo, err error) {
|
|||||||
for _, v := range ProtocolVersions {
|
for _, v := range ProtocolVersions {
|
||||||
if v.DataVersion == version {
|
if v.DataVersion == version {
|
||||||
ret = v
|
ret = v
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = UnknownMinecraftVersionError
|
err = UnknownMinecraftVersionError
|
||||||
|
|||||||
+101
-2
@@ -1,7 +1,15 @@
|
|||||||
package main
|
package litematic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/data"
|
||||||
|
"github.com/admin-else/strom/level"
|
||||||
|
"github.com/admin-else/strom/nbt"
|
||||||
"github.com/go-viper/mapstructure/v2"
|
"github.com/go-viper/mapstructure/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,6 +41,38 @@ type Region struct {
|
|||||||
BlockStates []int64
|
BlockStates []int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Region) GetBlockAt(x, y, z int32) (name string, properties map[string]string, err error) {
|
||||||
|
uintSlice := make([]uint64, len(r.BlockStates))
|
||||||
|
for i, v := range r.BlockStates {
|
||||||
|
uintSlice[i] = uint64(v)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
err = OutOfBoundsErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := x + z*r.Size.X + y*r.Size.X*r.Size.Z
|
||||||
|
bpe := int32(math.Ceil(math.Log2(float64(len(r.BlockStatePalette)))))
|
||||||
|
i = level.GetEntryFromLongs(uintSlice, i, bpe)
|
||||||
|
if i >= int32(len(r.BlockStatePalette)) {
|
||||||
|
err = BlockStateNotInPalletErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
block := r.BlockStatePalette[i]
|
||||||
|
name = block.Name
|
||||||
|
properties = block.Properties
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Version int32
|
Version int32
|
||||||
SubVersion int32
|
SubVersion int32
|
||||||
@@ -46,6 +86,24 @@ type Structure struct {
|
|||||||
BlockStatePalletRealized map[string][]int32
|
BlockStatePalletRealized map[string][]int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.ReadFile(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return Load(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
func Load(v any) (s *Structure, err error) {
|
func Load(v any) (s *Structure, err error) {
|
||||||
s = &Structure{}
|
s = &Structure{}
|
||||||
err = mapstructure.Decode(v, &s.File)
|
err = mapstructure.Decode(v, &s.File)
|
||||||
@@ -56,11 +114,12 @@ func Load(v any) (s *Structure, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.BlockStatePalletRealized = make(map[string][]int32)
|
||||||
for k, r := range s.Regions {
|
for k, r := range s.Regions {
|
||||||
s.BlockStatePalletRealized[k] = make([]int32, len(r.BlockStatePalette))
|
s.BlockStatePalletRealized[k] = make([]int32, len(r.BlockStatePalette))
|
||||||
for i, b := range r.BlockStatePalette {
|
for i, b := range r.BlockStatePalette {
|
||||||
var stateId int32
|
var stateId int32
|
||||||
stateId, err = data.StateIdFromBlocKAndStateMap(ret.MinecraftVersion, b.Name, b.Properties)
|
stateId, err = data.StateIdFromBlocKAndStateMap(ret.MinecraftVersion, strings.TrimPrefix(b.Name, "minecraft:"), b.Properties)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -69,3 +128,43 @@ func Load(v any) (s *Structure, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var BlockStateNotInPalletErr = errors.New("block state not in pallet")
|
||||||
|
var OutOfBoundsErr = errors.New("out of bounds")
|
||||||
|
var RegionNotInFileErr = errors.New("region not in file")
|
||||||
|
|
||||||
|
func (s *Structure) GetBlockAt(name string, x, y, z int32) (blockState int32, err error) {
|
||||||
|
r, ok := s.Regions[name]
|
||||||
|
if !ok {
|
||||||
|
err = RegionNotInFileErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uintSlice := make([]uint64, len(r.BlockStates))
|
||||||
|
for i, v := range r.BlockStates {
|
||||||
|
uintSlice[i] = uint64(v)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
err = OutOfBoundsErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := x + z*r.Size.X + y*r.Size.X*r.Size.Z
|
||||||
|
bpe := int32(math.Ceil(math.Log2(float64(len(r.BlockStatePalette)))))
|
||||||
|
i = level.GetEntryFromLongs(uintSlice, i, bpe)
|
||||||
|
mapSlice := s.BlockStatePalletRealized[name]
|
||||||
|
if i >= int32(len(mapSlice)) {
|
||||||
|
err = BlockStateNotInPalletErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
blockState = mapSlice[i]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package litematic_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/data"
|
||||||
|
"github.com/admin-else/strom/level/litematic"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed testdata
|
||||||
|
var testData embed.FS
|
||||||
|
|
||||||
|
func TestLitematic(t *testing.T) {
|
||||||
|
f, err := testData.Open("testdata/place.litematic")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
s, err := litematic.LoadFromFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateId, err := s.GetBlockAt("Cactus", 4, 0, 4)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
version, err := data.LookUpVersionByDataVersion(s.MinecraftDataVersion)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b, state, err := data.FromBlockState(version.MinecraftVersion, stateId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("Block: %v, state: %v", b.Name, state)
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
@@ -75,6 +75,13 @@ func LongsToData(data []uint64, n, bpe int32) (ret []int32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetEntryFromLongs(data []uint64, i, bpe int32) (ret int32) {
|
||||||
|
mask := (uint64(1) << uint32(bpe)) - 1
|
||||||
|
elementsPerLong := 64 / bpe
|
||||||
|
ret = int32((data[i/elementsPerLong] >> ((i % elementsPerLong) * bpe)) & mask)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// UnpackBlockData minecraft does this at net.minecraft.world.chunk.PaletteProvider
|
// UnpackBlockData minecraft does this at net.minecraft.world.chunk.PaletteProvider
|
||||||
func UnpackBlockData(r io.Reader) (blocks []int32, err error) {
|
func UnpackBlockData(r io.Reader) (blocks []int32, err error) {
|
||||||
var bitsPerEntry uint8
|
var bitsPerEntry uint8
|
||||||
|
|||||||
Reference in New Issue
Block a user