From c1a6528a05e2dd171cc18813d69b3d5fc78c9c38 Mon Sep 17 00:00:00 2001 From: Tnze Date: Sun, 18 Dec 2022 18:28:08 +0800 Subject: [PATCH] Add BlockEntity support --- internal/generateutils/utils.go | 7 + level/biome/list.go | 142 ++++--- level/block/block_entities.nbt | Bin 0 -> 1211 bytes level/block/blockentities.go | 397 ++++++++++++++++++ level/block/blockentity.go | 59 +++ .../generator/{Main.java => GenBlocks.java} | 36 +- .../blockentities/blockentities.go.tmpl | 31 ++ level/block/generator/blockentities/main.go | 77 ++++ level/chunk.go | 114 ++--- level/palette.go | 2 +- 10 files changed, 712 insertions(+), 153 deletions(-) create mode 100644 level/block/block_entities.nbt create mode 100644 level/block/blockentities.go create mode 100644 level/block/blockentity.go rename level/block/generator/{Main.java => GenBlocks.java} (71%) create mode 100644 level/block/generator/blockentities/blockentities.go.tmpl create mode 100644 level/block/generator/blockentities/main.go diff --git a/internal/generateutils/utils.go b/internal/generateutils/utils.go index 226bc6d..f27ce0b 100644 --- a/internal/generateutils/utils.go +++ b/internal/generateutils/utils.go @@ -21,3 +21,10 @@ func ToGoTypeName(name string) string { } return strings.Join(words, "") } + +func ToFuncReceiverName(name string) string { + if len(name) > 0 { + name = string(unicode.ToLower([]rune(name)[0])) + } + return name +} diff --git a/level/biome/list.go b/level/biome/list.go index f240f53..33f7b6c 100644 --- a/level/biome/list.go +++ b/level/biome/list.go @@ -2,74 +2,82 @@ package biome import "math/bits" -var BitsPerBiome int +type Type int -var biomesNames = []string{ - "the_void", - "plains", - "sunflower_plains", - "snowy_plains", - "ice_spikes", - "desert", - "swamp", - "mangrove_swamp", - "forest", - "flower_forest", - "birch_forest", - "dark_forest", - "old_growth_birch_forest", - "old_growth_pine_taiga", - "old_growth_spruce_taiga", - "taiga", - "snowy_taiga", - "savanna", - "savanna_plateau", - "windswept_hills", - "windswept_gravelly_hills", - "windswept_forest", - "windswept_savanna", - "jungle", - "sparse_jungle", - "bamboo_jungle", - "badlands", - "eroded_badlands", - "wooded_badlands", - "meadow", - "grove", - "snowy_slopes", - "frozen_peaks", - "jagged_peaks", - "stony_peaks", - "river", - "frozen_river", - "beach", - "snowy_beach", - "stony_shore", - "warm_ocean", - "lukewarm_ocean", - "deep_lukewarm_ocean", - "ocean", - "deep_ocean", - "cold_ocean", - "deep_cold_ocean", - "frozen_ocean", - "deep_frozen_ocean", - "mushroom_fields", - "dripstone_caves", - "lush_caves", - "deep_dark", - "nether_wastes", - "warped_forest", - "crimson_forest", - "soul_sand_valley", - "basalt_deltas", - "the_end", - "end_highlands", - "end_midlands", - "small_end_islands", - "end_barrens", -} +var ( + BitsPerBiome int + BiomesIDs map[string]Type + BiomesNames = []string{ + "the_void", + "plains", + "sunflower_plains", + "snowy_plains", + "ice_spikes", + "desert", + "swamp", + "mangrove_swamp", + "forest", + "flower_forest", + "birch_forest", + "dark_forest", + "old_growth_birch_forest", + "old_growth_pine_taiga", + "old_growth_spruce_taiga", + "taiga", + "snowy_taiga", + "savanna", + "savanna_plateau", + "windswept_hills", + "windswept_gravelly_hills", + "windswept_forest", + "windswept_savanna", + "jungle", + "sparse_jungle", + "bamboo_jungle", + "badlands", + "eroded_badlands", + "wooded_badlands", + "meadow", + "grove", + "snowy_slopes", + "frozen_peaks", + "jagged_peaks", + "stony_peaks", + "river", + "frozen_river", + "beach", + "snowy_beach", + "stony_shore", + "warm_ocean", + "lukewarm_ocean", + "deep_lukewarm_ocean", + "ocean", + "deep_ocean", + "cold_ocean", + "deep_cold_ocean", + "frozen_ocean", + "deep_frozen_ocean", + "mushroom_fields", + "dripstone_caves", + "lush_caves", + "deep_dark", + "nether_wastes", + "warped_forest", + "crimson_forest", + "soul_sand_valley", + "basalt_deltas", + "the_end", + "end_highlands", + "end_midlands", + "small_end_islands", + "end_barrens", + } +) func init() { - BitsPerBiome = bits.Len(uint(len(biomesNames))) + BitsPerBiome = bits.Len(uint(len(BiomesNames))) + BiomesIDs = make(map[string]Type, len(BiomesNames)) + for i, v := range BiomesNames { + BiomesIDs[v] = Type(i) + } } diff --git a/level/block/block_entities.nbt b/level/block/block_entities.nbt new file mode 100644 index 0000000000000000000000000000000000000000..96c7840960ef9340690bb665503a8349f6691c0d GIT binary patch literal 1211 zcmV;s1VsBEiwFP!00000|BYC|cAYj5B~2Ddd*a5f-8e5!o3uIoh%Wm9U3SY6AcKsM zIFc}Z{rUaXqZn6#e8lX z_JAa~p+5t?D%NQ!oc&{L--19XMxrKp?z8abEd*4fBcfLFnMUcY0J>5^x@D!QRWP4U~7VpoS*AV34=2pjg4QgAFGiP;U@`@T-8o8dc zOFl;eYJvw(1RTk@lwD600m+~sLh~8QX>6FGc|pW9AK)`a+3B+aK)4;LsW4^$fKFmt>;6v05h<=JY0^!eSTN3oXHxNDT8kE51E+w$J8y9nmJWji& zCGz#1OGHLH{kZR80(6ap;D@lADbl&60K_f@7loZ^u!uJH0c8edWvlJ89Ea3#H(-Q>oQ+d%NT3~-NA%+YE2*(p8K|CMpM5I4_ z5ZxVL_(XpZ^B@I z8(8B#+H4eAiEehyoosh;Qz!Fn)XW`$yFo+G<<1Lp_HTgDb-A$V`f7R$-?;e89dy9H zfvbTT validBlocks = blockEntity.validBlocks; + for (Block validBlock : validBlocks){ + validBlocksList.add(StringTag.valueOf(BuiltInRegistries.BLOCK.getKey(validBlock).toString())); + } + CompoundTag be = new CompoundTag(); + be.putString("Name", BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity).toString()); + be.putString("ValidBlocks", BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity).toString()); + + list.add(be); + } + return list; + } } diff --git a/level/block/generator/blockentities/blockentities.go.tmpl b/level/block/generator/blockentities/blockentities.go.tmpl new file mode 100644 index 0000000..c4dd202 --- /dev/null +++ b/level/block/generator/blockentities/blockentities.go.tmpl @@ -0,0 +1,31 @@ +// Code generated by {{Generator}}; DO NOT EDIT. + +package block + +{{/* type ( +{{- range .}} + {{.Name | ToGoTypeName}}Entity struct {} +{{- end}} +) */}} + +var EntityList = [...]Entity{ + {{- range .}} + {{.Name | ToGoTypeName}}Entity{}, + {{- end}} +} + +{{- range .}} +func ({{.Name | ToGoTypeName}}Entity) ID() string { return {{.Name | printf "%q"}} } +{{- end}} + +{{range .}} +func ({{.Name | ToFuncReceiverName}} {{.Name | ToGoTypeName}}Entity) IsValidBlock(block Block) bool { + {{if eq 1 (len .ValidBlocks)}}return block.ID() == {{index .ValidBlocks 0 | printf "%q"}}{{else}}switch block.ID() { + case {{index .ValidBlocks 0 | printf "%q"}}{{range slice .ValidBlocks 1}}, + {{. | printf "%q"}}{{end}}: + return true + default: + return false + }{{end}} +} +{{end}} diff --git a/level/block/generator/blockentities/main.go b/level/block/generator/blockentities/main.go new file mode 100644 index 0000000..413d090 --- /dev/null +++ b/level/block/generator/blockentities/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "bytes" + "compress/gzip" + _ "embed" + "go/format" + "log" + "os" + "text/template" + + "github.com/Tnze/go-mc/internal/generateutils" + "github.com/Tnze/go-mc/nbt" +) + +//go:embed blockentities.go.tmpl +var tempSource string + +var temp = template.Must(template. + New("block_template"). + Funcs(template.FuncMap{ + "UpperTheFirst": generateutils.UpperTheFirst, + "ToGoTypeName": generateutils.ToGoTypeName, + "ToFuncReceiverName": generateutils.ToFuncReceiverName, + "Generator": func() string { return "generator/blockentities/main.go" }, + }). + Parse(tempSource), +) + +type BlockEntity struct { + Name string + ValidBlocks []string +} + +func main() { + var states []BlockEntity + readBlockEntities(&states) + + // generate go source file + genSourceFile(states) +} + +func readBlockEntities(states *[]BlockEntity) { + f, err := os.Open("block_entities.nbt") + if err != nil { + log.Panic(err) + } + defer f.Close() + + r, err := gzip.NewReader(f) + if err != nil { + log.Panic(err) + } + + // parse the nbt format + if _, err := nbt.NewDecoder(r).Decode(states); err != nil { + log.Panic(err) + } +} + +func genSourceFile(states []BlockEntity) { + var source bytes.Buffer + if err := temp.Execute(&source, states); err != nil { + log.Panic(err) + } + + formattedSource, err := format.Source(source.Bytes()) + if err != nil { + panic(err) + } + + err = os.WriteFile("blockentities.go", formattedSource, 0o666) + if err != nil { + panic(err) + } + log.Print("Generated blockentities.go") +} diff --git a/level/chunk.go b/level/chunk.go index b7233c1..6f22984 100644 --- a/level/chunk.go +++ b/level/chunk.go @@ -2,11 +2,14 @@ package level import ( "bytes" + "errors" "fmt" "io" "math/bits" + "strconv" "strings" + "github.com/Tnze/go-mc/level/biome" "github.com/Tnze/go-mc/level/block" "github.com/Tnze/go-mc/nbt" pk "github.com/Tnze/go-mc/net/packet" @@ -62,81 +65,6 @@ func EmptyChunk(secs int) *Chunk { } } -var biomesIDs map[string]BiomesState - -var biomesNames = []string{ - "the_void", - "plains", - "sunflower_plains", - "snowy_plains", - "ice_spikes", - "desert", - "swamp", - "mangrove_swamp", - "forest", - "flower_forest", - "birch_forest", - "dark_forest", - "old_growth_birch_forest", - "old_growth_pine_taiga", - "old_growth_spruce_taiga", - "taiga", - "snowy_taiga", - "savanna", - "savanna_plateau", - "windswept_hills", - "windswept_gravelly_hills", - "windswept_forest", - "windswept_savanna", - "jungle", - "sparse_jungle", - "bamboo_jungle", - "badlands", - "eroded_badlands", - "wooded_badlands", - "meadow", - "grove", - "snowy_slopes", - "frozen_peaks", - "jagged_peaks", - "stony_peaks", - "river", - "frozen_river", - "beach", - "snowy_beach", - "stony_shore", - "warm_ocean", - "lukewarm_ocean", - "deep_lukewarm_ocean", - "ocean", - "deep_ocean", - "cold_ocean", - "deep_cold_ocean", - "frozen_ocean", - "deep_frozen_ocean", - "mushroom_fields", - "dripstone_caves", - "lush_caves", - "deep_dark", - "nether_wastes", - "warped_forest", - "crimson_forest", - "soul_sand_valley", - "basalt_deltas", - "the_end", - "end_highlands", - "end_midlands", - "small_end_islands", - "end_barrens", -} - -func init() { - biomesIDs = make(map[string]BiomesState, len(biomesNames)) - for i, v := range biomesNames { - biomesIDs[v] = BiomesState(i) - } -} - // ChunkFromSave convert save.Chunk to level.Chunk. func ChunkFromSave(c *save.Chunk) (*Chunk, error) { secs := len(c.Sections) @@ -159,6 +87,25 @@ func ChunkFromSave(c *save.Chunk) (*Chunk, error) { sections[i].BlockLight = v.BlockLight } + blockEntities := make([]BlockEntity, len(c.BlockEntities)) + for i, v := range c.BlockEntities { + var tmp struct { + ID string `nbt:"id"` + X int32 `nbt:"x"` + Y int32 `nbt:"y"` + Z int32 `nbt:"z"` + } + if err := v.Unmarshal(&tmp); err != nil { + return nil, err + } + blockEntities[i].Data = v + if x, z := int(tmp.X-c.XPos<<4), int(tmp.Z-c.ZPos<<4); !blockEntities[i].PackXZ(x, z) { + return nil, errors.New("Packing a XZ(" + strconv.Itoa(x) + ", " + strconv.Itoa(z) + ") out of bound") + } + blockEntities[i].Y = int16(tmp.Y) + blockEntities[i].Type = block.EntityTypes[tmp.ID] + } + motionBlocking := c.Heightmaps.MotionBlocking motionBlockingNoLeaves := c.Heightmaps.MotionBlockingNoLeaves oceanFloor := c.Heightmaps.OceanFloor @@ -173,7 +120,8 @@ func ChunkFromSave(c *save.Chunk) (*Chunk, error) { OceanFloor: NewBitStorage(bitsForHeight, 16*16, oceanFloor), WorldSurface: NewBitStorage(bitsForHeight, 16*16, worldSurface), }, - Status: ChunkStatus(c.Status), + BlockEntity: blockEntities, + Status: ChunkStatus(c.Status), }, nil } @@ -206,7 +154,7 @@ func readBiomesPalette(palette []string, data []uint64) (*PaletteContainer[Biome biomesRawPalette := make([]BiomesState, len(palette)) var ok bool for i, v := range palette { - biomesRawPalette[i], ok = biomesIDs[strings.TrimPrefix(v, "minecraft:")] + biomesRawPalette[i], ok = biome.BiomesIDs[strings.TrimPrefix(v, "minecraft:")] if !ok { return nil, fmt.Errorf("unknown biomes: %s", v) } @@ -264,7 +212,7 @@ func writeBiomesPalette(paletteData *PaletteContainer[BiomesState]) (palette []s rawPalette := paletteData.palette.export() palette = make([]string, len(rawPalette)) for i, v := range rawPalette { - palette[i] = biomesNames[v] + palette[i] = biome.BiomesNames[v] } data = append(data, paletteData.data.Raw()...) @@ -367,7 +315,7 @@ type HeightMaps struct { type BlockEntity struct { XZ int8 Y int16 - Type int32 + Type block.EntityType Data nbt.RawMessage } @@ -375,6 +323,14 @@ func (b BlockEntity) UnpackXZ() (X, Z int) { return int((uint8(b.XZ) >> 4) & 0xF), int(uint8(b.XZ) & 0xF) } +func (b *BlockEntity) PackXZ(X, Z int) bool { + if X > 0xF || Z > 0xF || X < 0 || Z < 0 { + return false + } + b.XZ = int8(X<<4 | Z) + return true +} + func (b BlockEntity) WriteTo(w io.Writer) (n int64, err error) { return pk.Tuple{ pk.Byte(b.XZ), diff --git a/level/palette.go b/level/palette.go index 5d301e3..99a98ac 100644 --- a/level/palette.go +++ b/level/palette.go @@ -15,7 +15,7 @@ type State interface { } type ( BlocksState = block.StateID - BiomesState int + BiomesState = biome.Type ) type PaletteContainer[T State] struct {