117 lines
2.5 KiB
Go
117 lines
2.5 KiB
Go
|
|
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()
|
||
|
|
}
|