Files
mc/server/world/block_state.go
T

117 lines
2.5 KiB
Go
Raw Normal View History

2026-07-09 08:33:57 +08:00
package world
import (
"bytes"
_ "embed"
"fmt"
"maps"
"math"
"slices"
"strings"
"unsafe"
"github.com/sandertv/gophertunnel/minecraft/nbt"
)
var (
//go:embed block_states.nbt
blockStateData []byte
)
func init() {
dec := nbt.NewDecoder(bytes.NewBuffer(blockStateData))
// Register all block states present in the block_states.nbt file. These are all possible options registered
// blocks may encode to.
for {
var s BlockState
if err := dec.Decode(&s); err != nil {
break
}
DefaultBlockRegistry.RegisterBlockState(s)
}
}
// BlockState holds a combination of a name and properties, together with a version.
type BlockState struct {
Name string `nbt:"name"`
Properties map[string]any `nbt:"states"`
Version int32 `nbt:"version"`
}
// unknownBlock represents a block that has not yet been implemented. It is used for registering block
// states that haven't yet been added.
type unknownBlock struct {
BlockState
data map[string]any
}
// EncodeBlock ...
func (b unknownBlock) EncodeBlock() (string, map[string]any) {
return b.Name, b.Properties
}
// Model ...
func (unknownBlock) Model() BlockModel {
return unknownModel{}
}
// Hash ...
func (b unknownBlock) Hash() (uint64, uint64) {
return 0, math.MaxUint64
}
// EncodeNBT ...
func (b unknownBlock) EncodeNBT() map[string]any {
return maps.Clone(b.data)
}
// DecodeNBT ...
func (b unknownBlock) DecodeNBT(data map[string]any) any {
b.data = maps.Clone(data)
return b
}
// stateHash is a struct that may be used as a map key for block states. It contains the name of the block state
// and an encoded version of the properties.
type stateHash struct {
name, properties string
}
// hashProperties produces a hash for the block properties held by the BlockState.
func hashProperties(properties map[string]any) string {
if properties == nil {
return ""
}
keys := make([]string, 0, len(properties))
for k := range properties {
keys = append(keys, k)
}
slices.Sort(keys)
var b strings.Builder
for _, k := range keys {
switch v := properties[k].(type) {
case bool:
if v {
b.WriteByte(1)
} else {
b.WriteByte(0)
}
case uint8:
b.WriteByte(v)
case int32:
a := *(*[4]byte)(unsafe.Pointer(&v))
b.Write(a[:])
case string:
b.WriteString(v)
default:
// If block encoding is broken, we want to find out as soon as possible. This saves a lot of time
// debugging in-game.
panic(fmt.Sprintf("invalid block property type %T for property %v", v, k))
}
}
return b.String()
}