block state
This commit is contained in:
+120
-13
@@ -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
|
||||
}
|
||||
|
||||
@@ -14,3 +14,21 @@ func TestLookupBlockByStateId(t *testing.T) {
|
||||
}
|
||||
fmt.Println(b)
|
||||
}
|
||||
|
||||
func TestBlockStates(t *testing.T) {
|
||||
var stateId int32 = 8301
|
||||
b, stateMap, err := data.FromBlockState("1.21.8", stateId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
t.Logf("Block state map: %v, block: %v", stateMap, b.Name)
|
||||
stateId2, err := b.IdFromStateMap(stateMap)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
if stateId2 != stateId {
|
||||
t.Fatal("state id mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user