block state

This commit is contained in:
adman
2026-03-06 21:24:08 +01:00
parent 5dfd8fc499
commit 73d4e07b06
5 changed files with 167 additions and 27 deletions
+120 -13
View File
@@ -1,6 +1,10 @@
package data
// {
import (
"errors"
)
// {
// "id": 0,
// "name": "air",
// "displayName": "Air",
@@ -35,29 +39,32 @@ type Block struct {
DefaultState int32
MinStateId int32
MaxStateId int32
States []struct {
Name string
Type string
NumValues int
}
Drops []int
BoundingBox string
States []BlockState
Drops []int
BoundingBox string
}
var BlocksCache = make(map[string][]Block)
type BlockState struct {
Name string
Type string
NumValues int32 `json:"num_values"`
Values []string
}
func BlocksForVersion(v string) (ret []Block) {
var BlocksCache = make(map[string][]*Block)
func BlocksForVersion(v string) (ret []*Block) {
var ok bool
if ret, ok = BlocksCache[v]; ok {
return
}
var b []Block
var b []*Block
must(LoadVersionedJson(v, "blocks", &b))
BlocksCache[v] = b
return b
}
func LookupBlockByStateId(version string, stateId int32) (block Block, ok bool) {
func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) {
for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
block = b
@@ -67,7 +74,7 @@ func LookupBlockByStateId(version string, stateId int32) (block Block, ok bool)
return
}
func LookupBlockByName(version string, stateId int32) (block Block, ok bool) {
func LookupBlockByName(version string, stateId int32) (block *Block, ok bool) {
for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
block = b
@@ -76,3 +83,103 @@ func LookupBlockByName(version string, stateId int32) (block Block, ok bool) {
}
return
}
var BlockStateOutOfRangeErr = errors.New("stateId out of range")
var BlockNotFoundErr = errors.New("block not found")
var InvalidBoolValueErr = errors.New("invalid bool value")
var BadBlockStateTypeErr = errors.New("bad block state type")
var BadEnumErr = errors.New("bad enum")
var SupplyAllStatesErr = errors.New("supply all states")
func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]any, err error) {
b, ok := LookupBlockByStateId(version, stateId)
if !ok {
err = BlockNotFoundErr
}
stateData, err = b.StateMapFromId(stateId)
return
}
func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
switch s.Type {
case "int":
v = n
case "enum":
v = s.Values[n]
case "bool":
if n == 0 { // MOJANK: 0 is true, 1 is false
v = true
} else if n == 1 {
v = false
} else {
err = InvalidBoolValueErr
}
default:
err = BadBlockStateTypeErr
}
return
}
func mcDataBlockStateTypeSerialize(v any, s *BlockState) (n int32, err error) {
switch t := v.(type) {
case int32:
n = t
case bool:
if t {
n = 0
} else {
n = 1
}
case string:
for i, enumValue := range s.Values {
if enumValue == t {
return int32(i), nil
}
}
err = BadEnumErr
default:
err = BadBlockStateTypeErr
}
return
}
func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) {
if stateId < b.MinStateId || stateId > b.MaxStateId {
err = BlockStateOutOfRangeErr
return
}
ret = make(map[string]any)
stateId -= b.MinStateId
a := int32(1)
for i := range len(b.States) {
s := b.States[len(b.States)-1-i]
ret[s.Name], err = mcDataBlockStateTypeParse((stateId/a)%s.NumValues, &s)
if err != nil {
return
}
a *= s.NumValues
}
return
}
func (b *Block) IdFromStateMap(m map[string]any) (ret int32, err error) {
a := int32(1)
for i := range len(b.States) {
s := b.States[len(b.States)-1-i]
v, found := m[s.Name]
if !found {
err = SupplyAllStatesErr
return
}
var n int32
n, err = mcDataBlockStateTypeSerialize(v, &s)
if err != nil {
return
}
ret += n * a
a *= s.NumValues
}
ret += b.MinStateId
return
}