All block states' properties are parsed and all enums represented as byte

This commit is contained in:
Tnze
2022-03-11 14:02:21 +08:00
parent 442993d3b1
commit a05f8e0a33
17 changed files with 1954 additions and 760 deletions

View File

@ -2,7 +2,7 @@ package block
import (
"bytes"
"compress/zlib"
"compress/gzip"
_ "embed"
"fmt"
"math/bits"
@ -14,7 +14,7 @@ type Block interface {
ID() string
}
// This file stores all possible block states into a TAG_List with zlib compressed.
// This file stores all possible block states into a TAG_List with gzip compressed.
//go:embed block_states.nbt
var blockStates []byte
@ -33,7 +33,7 @@ type State struct {
func init() {
var states []State
// decompress
z, err := zlib.NewReader(bytes.NewReader(blockStates))
z, err := gzip.NewReader(bytes.NewReader(blockStates))
if err != nil {
panic(err)
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,7 +1,10 @@
package main
import (
"bytes"
"compress/gzip"
_ "embed"
"go/format"
"log"
"os"
"strings"
@ -20,7 +23,7 @@ var temp = template.Must(template.
"UpperTheFirst": UpperTheFirst,
"ToGoTypeName": ToGoTypeName,
"GetType": GetType,
"Generator": func() string { return "generator/main.go" },
"Generator": func() string { return "generator/blocks/main.go" },
}).
Parse(tempSource))
@ -45,25 +48,31 @@ func readBlockStates(states *[]State) {
}
defer f.Close()
r, err := gzip.NewReader(f)
if err != nil {
log.Panic(err)
}
// parse the nbt format
if _, err := nbt.NewDecoder(f).Decode(states); err != nil {
if _, err := nbt.NewDecoder(r).Decode(states); err != nil {
log.Panic(err)
}
}
func genSourceFile(states []State) {
file, err := os.Create("blocks.go")
if err != nil {
var source bytes.Buffer
if err := temp.Execute(&source, states); err != nil {
log.Panic(err)
}
defer file.Close()
// clean up the file
if err := file.Truncate(0); err != nil {
return
}
if err := temp.Execute(file, states); err != nil {
log.Panic(err)
formattedSource, err := format.Source(source.Bytes())
if err != nil {
panic(err)
}
err = os.WriteFile("blocks.go", formattedSource, 0666)
if err != nil {
panic(err)
}
}
@ -79,7 +88,6 @@ func ToGoTypeName(name string) string {
var typeMaps = map[string]string{
"BooleanProperty": "Boolean",
"DirectionProperty": "Direction",
"EnumProperty": "string",
"IntegerProperty": "Integer",
}

View File

@ -0,0 +1,97 @@
package main
import (
"bytes"
_ "embed"
"go/format"
"log"
"os"
"strings"
"text/template"
"unicode"
)
type EnumProperty struct {
Name string
TrimPrefix bool
Values []string
}
var EnumProperties = []EnumProperty{
{Name: "AttachFace", Values: []string{"floor", "wall", "ceiling"}},
{Name: "BambooLeaves", Values: []string{"none", "small", "large"}},
{Name: "BedPart", Values: []string{"head", "foot"}},
{Name: "BellAttachType", Values: []string{"floor", "ceiling", "single_wall", "double_wall"}},
{Name: "ChestType", Values: []string{"single", "left", "right"}},
{Name: "ComparatorMode", Values: []string{"compare", "subtract"}},
{Name: "Direction", TrimPrefix: true, Values: []string{"down", "up", "north", "south", "west", "east"}},
{Name: "Axis", TrimPrefix: true, Values: []string{"x", "y", "z"}},
{Name: "DoorHingeSide", Values: []string{"left", "right"}},
{Name: "DoubleBlockHalf", Values: []string{"upper", "lower"}},
{Name: "DripstoneThickness", Values: []string{"tip_merge", "tip", "frustum", "middle", "base"}},
{Name: "Half", TrimPrefix: true, Values: []string{"top", "bottom"}},
{Name: "NoteBlockInstrument", Values: []string{
"harp", "basedrum", "snare", "hat",
"bass", "flute", "bell", "guitar",
"chime", "xylophone", "iron_xylophone", "cow_bell",
"didgeridoo", "bit", "banjo", "pling",
}},
{Name: "PistonType", Values: []string{"normal", "sticky"}},
{Name: "RailShape", Values: []string{
"north_south", "east_west",
"ascending_east", "ascending_west", "ascending_north", "ascending_south",
"south_east", "south_west", "north_west", "north_east",
}},
{Name: "RedstoneSide", Values: []string{"up", "side", "none"}},
{Name: "SculkSensorPhase", Values: []string{"inactive", "active", "cooldown"}},
{Name: "SlabType", Values: []string{"top", "bottom", "double"}},
{Name: "StairsShape", Values: []string{"straight", "inner_left", "inner_right", "outer_left", "outer_right"}},
{Name: "StructureMode", Values: []string{"save", "load", "corner", "data"}},
{Name: "Tilt", Values: []string{"none", "unstable", "partial", "full"}},
{Name: "WallSide", Values: []string{"none", "low", "tall"}},
{Name: "FrontAndTop", TrimPrefix: true, Values: []string{
"down_east", "down_north", "down_south", "down_west",
"up_east", "up_north", "up_south", "up_west",
"west_up", "east_up", "north_up", "south_up",
}},
}
//go:embed properties_enum.go.tmpl
var tempSource string
func main() {
var source bytes.Buffer
err := template.Must(template.
New("properties_enum").
Funcs(template.FuncMap{
"UpperTheFirst": UpperTheFirst,
"ToLower": strings.ToLower,
"Generator": func() string { return "generator/properties/main.go" },
}).
Parse(tempSource)).
Execute(&source, EnumProperties)
if err != nil {
log.Panic(err)
}
formattedSource, err := format.Source(source.Bytes())
if err != nil {
log.Panic(err)
}
err = os.WriteFile("properties_enum.go", formattedSource, 0666)
if err != nil {
log.Panic(err)
}
}
func UpperTheFirst(word string) string {
var sb strings.Builder
for _, word := range strings.Split(word, "_") {
runes := []rune(word)
if len(runes) > 0 {
runes[0] = unicode.ToUpper(runes[0])
}
sb.WriteString(string(runes))
}
return sb.String()
}

View File

@ -0,0 +1,45 @@
// Code generated by {{Generator}}; DO NOT EDIT.
package block
import (
"errors"
"strconv"
)
{{range $prop := .}}
type {{.Name}} byte
const (
{{- range $index, $element := .Values}}
{{if not $prop.TrimPrefix}}{{$prop.Name}}{{end}}{{$element | UpperTheFirst}}{{if eq $index 0 }} {{$prop.Name}} = iota{{end}}
{{- end}}
)
{{- $v := slice (.Name | ToLower) 0 1 }}
var str{{.Name}} = [...]string{ {{- range $index, $elem := .Values }}{{$elem | printf "%q"}}{{if ne $index (len $prop.Values)}}, {{end}}{{end -}} }
func ({{$v}} {{.Name}}) String() string {
if int({{$v}}) < len(str{{.Name}}) {
return str{{.Name}}[{{$v}}]
}
return "invalid {{.Name}}"
}
func ({{$v}} {{.Name}}) MarshalText() (text []byte, err error) {
if int({{$v}}) < len(str{{.Name}}) {
return []byte(str{{.Name}}[{{$v}}]), nil
}
return nil, errors.New("invalid {{.Name}}: " + strconv.Itoa(int({{$v}})))
}
func ({{$v}} *{{.Name}}) UnmarshalText(text []byte) error {
switch str := string(text); str {
{{- range .Values}}
case {{. | printf "%q"}}:
*{{$v}} = {{if not $prop.TrimPrefix}}{{$prop.Name}}{{end}}{{. | UpperTheFirst}}
{{- end}}
default:
return errors.New("unknown {{.Name}}: " + str)
}
return nil
}
{{end}}

View File

@ -1,14 +1,8 @@
package block
import (
"strconv"
)
import "strconv"
type (
Boolean bool
Direction string
Integer int
)
type Boolean bool
func (b Boolean) MarshalText() (text []byte, err error) {
return []byte(strconv.FormatBool(bool(b))), nil
@ -19,14 +13,7 @@ func (b *Boolean) UnmarshalText(text []byte) (err error) {
return
}
func (d Direction) MarshalText() (text []byte, err error) {
return []byte(d), nil
}
func (d *Direction) UnmarshalText(text []byte) error {
*d = Direction(text)
return nil
}
type Integer int
func (i Integer) MarshalText() (text []byte, err error) {
return []byte(strconv.Itoa(int(i))), nil
@ -36,3 +23,34 @@ func (i *Integer) UnmarshalText(text []byte) (err error) {
*((*int)(i)), err = strconv.Atoi(string(text))
return
}
func (f FrontAndTop) Directions() (front, top Direction) {
switch f {
case DownEast:
return Down, East
case DownNorth:
return Down, North
case DownSouth:
return Down, South
case DownWest:
return Down, West
case UpEast:
return Up, East
case UpNorth:
return Up, North
case UpSouth:
return Up, South
case UpWest:
return Up, West
case WestUp:
return West, Up
case EastUp:
return East, Up
case NorthUp:
return North, Up
case SouthUp:
return South, Up
default:
panic("invalid FrontAndTop")
}
}

View File

@ -0,0 +1,980 @@
// Code generated by generator/properties/main.go; DO NOT EDIT.
package block
import (
"errors"
"strconv"
)
type AttachFace byte
const (
AttachFaceFloor AttachFace = iota
AttachFaceWall
AttachFaceCeiling
)
var strAttachFace = [...]string{"floor", "wall", "ceiling"}
func (a AttachFace) String() string {
if int(a) < len(strAttachFace) {
return strAttachFace[a]
}
return "invalid AttachFace"
}
func (a AttachFace) MarshalText() (text []byte, err error) {
if int(a) < len(strAttachFace) {
return []byte(strAttachFace[a]), nil
}
return nil, errors.New("invalid AttachFace: " + strconv.Itoa(int(a)))
}
func (a *AttachFace) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "floor":
*a = AttachFaceFloor
case "wall":
*a = AttachFaceWall
case "ceiling":
*a = AttachFaceCeiling
default:
return errors.New("unknown AttachFace: " + str)
}
return nil
}
type BambooLeaves byte
const (
BambooLeavesNone BambooLeaves = iota
BambooLeavesSmall
BambooLeavesLarge
)
var strBambooLeaves = [...]string{"none", "small", "large"}
func (b BambooLeaves) String() string {
if int(b) < len(strBambooLeaves) {
return strBambooLeaves[b]
}
return "invalid BambooLeaves"
}
func (b BambooLeaves) MarshalText() (text []byte, err error) {
if int(b) < len(strBambooLeaves) {
return []byte(strBambooLeaves[b]), nil
}
return nil, errors.New("invalid BambooLeaves: " + strconv.Itoa(int(b)))
}
func (b *BambooLeaves) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "none":
*b = BambooLeavesNone
case "small":
*b = BambooLeavesSmall
case "large":
*b = BambooLeavesLarge
default:
return errors.New("unknown BambooLeaves: " + str)
}
return nil
}
type BedPart byte
const (
BedPartHead BedPart = iota
BedPartFoot
)
var strBedPart = [...]string{"head", "foot"}
func (b BedPart) String() string {
if int(b) < len(strBedPart) {
return strBedPart[b]
}
return "invalid BedPart"
}
func (b BedPart) MarshalText() (text []byte, err error) {
if int(b) < len(strBedPart) {
return []byte(strBedPart[b]), nil
}
return nil, errors.New("invalid BedPart: " + strconv.Itoa(int(b)))
}
func (b *BedPart) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "head":
*b = BedPartHead
case "foot":
*b = BedPartFoot
default:
return errors.New("unknown BedPart: " + str)
}
return nil
}
type BellAttachType byte
const (
BellAttachTypeFloor BellAttachType = iota
BellAttachTypeCeiling
BellAttachTypeSingleWall
BellAttachTypeDoubleWall
)
var strBellAttachType = [...]string{"floor", "ceiling", "single_wall", "double_wall"}
func (b BellAttachType) String() string {
if int(b) < len(strBellAttachType) {
return strBellAttachType[b]
}
return "invalid BellAttachType"
}
func (b BellAttachType) MarshalText() (text []byte, err error) {
if int(b) < len(strBellAttachType) {
return []byte(strBellAttachType[b]), nil
}
return nil, errors.New("invalid BellAttachType: " + strconv.Itoa(int(b)))
}
func (b *BellAttachType) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "floor":
*b = BellAttachTypeFloor
case "ceiling":
*b = BellAttachTypeCeiling
case "single_wall":
*b = BellAttachTypeSingleWall
case "double_wall":
*b = BellAttachTypeDoubleWall
default:
return errors.New("unknown BellAttachType: " + str)
}
return nil
}
type ChestType byte
const (
ChestTypeSingle ChestType = iota
ChestTypeLeft
ChestTypeRight
)
var strChestType = [...]string{"single", "left", "right"}
func (c ChestType) String() string {
if int(c) < len(strChestType) {
return strChestType[c]
}
return "invalid ChestType"
}
func (c ChestType) MarshalText() (text []byte, err error) {
if int(c) < len(strChestType) {
return []byte(strChestType[c]), nil
}
return nil, errors.New("invalid ChestType: " + strconv.Itoa(int(c)))
}
func (c *ChestType) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "single":
*c = ChestTypeSingle
case "left":
*c = ChestTypeLeft
case "right":
*c = ChestTypeRight
default:
return errors.New("unknown ChestType: " + str)
}
return nil
}
type ComparatorMode byte
const (
ComparatorModeCompare ComparatorMode = iota
ComparatorModeSubtract
)
var strComparatorMode = [...]string{"compare", "subtract"}
func (c ComparatorMode) String() string {
if int(c) < len(strComparatorMode) {
return strComparatorMode[c]
}
return "invalid ComparatorMode"
}
func (c ComparatorMode) MarshalText() (text []byte, err error) {
if int(c) < len(strComparatorMode) {
return []byte(strComparatorMode[c]), nil
}
return nil, errors.New("invalid ComparatorMode: " + strconv.Itoa(int(c)))
}
func (c *ComparatorMode) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "compare":
*c = ComparatorModeCompare
case "subtract":
*c = ComparatorModeSubtract
default:
return errors.New("unknown ComparatorMode: " + str)
}
return nil
}
type Direction byte
const (
Down Direction = iota
Up
North
South
West
East
)
var strDirection = [...]string{"down", "up", "north", "south", "west", "east"}
func (d Direction) String() string {
if int(d) < len(strDirection) {
return strDirection[d]
}
return "invalid Direction"
}
func (d Direction) MarshalText() (text []byte, err error) {
if int(d) < len(strDirection) {
return []byte(strDirection[d]), nil
}
return nil, errors.New("invalid Direction: " + strconv.Itoa(int(d)))
}
func (d *Direction) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "down":
*d = Down
case "up":
*d = Up
case "north":
*d = North
case "south":
*d = South
case "west":
*d = West
case "east":
*d = East
default:
return errors.New("unknown Direction: " + str)
}
return nil
}
type Axis byte
const (
X Axis = iota
Y
Z
)
var strAxis = [...]string{"x", "y", "z"}
func (a Axis) String() string {
if int(a) < len(strAxis) {
return strAxis[a]
}
return "invalid Axis"
}
func (a Axis) MarshalText() (text []byte, err error) {
if int(a) < len(strAxis) {
return []byte(strAxis[a]), nil
}
return nil, errors.New("invalid Axis: " + strconv.Itoa(int(a)))
}
func (a *Axis) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "x":
*a = X
case "y":
*a = Y
case "z":
*a = Z
default:
return errors.New("unknown Axis: " + str)
}
return nil
}
type DoorHingeSide byte
const (
DoorHingeSideLeft DoorHingeSide = iota
DoorHingeSideRight
)
var strDoorHingeSide = [...]string{"left", "right"}
func (d DoorHingeSide) String() string {
if int(d) < len(strDoorHingeSide) {
return strDoorHingeSide[d]
}
return "invalid DoorHingeSide"
}
func (d DoorHingeSide) MarshalText() (text []byte, err error) {
if int(d) < len(strDoorHingeSide) {
return []byte(strDoorHingeSide[d]), nil
}
return nil, errors.New("invalid DoorHingeSide: " + strconv.Itoa(int(d)))
}
func (d *DoorHingeSide) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "left":
*d = DoorHingeSideLeft
case "right":
*d = DoorHingeSideRight
default:
return errors.New("unknown DoorHingeSide: " + str)
}
return nil
}
type DoubleBlockHalf byte
const (
DoubleBlockHalfUpper DoubleBlockHalf = iota
DoubleBlockHalfLower
)
var strDoubleBlockHalf = [...]string{"upper", "lower"}
func (d DoubleBlockHalf) String() string {
if int(d) < len(strDoubleBlockHalf) {
return strDoubleBlockHalf[d]
}
return "invalid DoubleBlockHalf"
}
func (d DoubleBlockHalf) MarshalText() (text []byte, err error) {
if int(d) < len(strDoubleBlockHalf) {
return []byte(strDoubleBlockHalf[d]), nil
}
return nil, errors.New("invalid DoubleBlockHalf: " + strconv.Itoa(int(d)))
}
func (d *DoubleBlockHalf) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "upper":
*d = DoubleBlockHalfUpper
case "lower":
*d = DoubleBlockHalfLower
default:
return errors.New("unknown DoubleBlockHalf: " + str)
}
return nil
}
type DripstoneThickness byte
const (
DripstoneThicknessTipMerge DripstoneThickness = iota
DripstoneThicknessTip
DripstoneThicknessFrustum
DripstoneThicknessMiddle
DripstoneThicknessBase
)
var strDripstoneThickness = [...]string{"tip_merge", "tip", "frustum", "middle", "base"}
func (d DripstoneThickness) String() string {
if int(d) < len(strDripstoneThickness) {
return strDripstoneThickness[d]
}
return "invalid DripstoneThickness"
}
func (d DripstoneThickness) MarshalText() (text []byte, err error) {
if int(d) < len(strDripstoneThickness) {
return []byte(strDripstoneThickness[d]), nil
}
return nil, errors.New("invalid DripstoneThickness: " + strconv.Itoa(int(d)))
}
func (d *DripstoneThickness) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "tip_merge":
*d = DripstoneThicknessTipMerge
case "tip":
*d = DripstoneThicknessTip
case "frustum":
*d = DripstoneThicknessFrustum
case "middle":
*d = DripstoneThicknessMiddle
case "base":
*d = DripstoneThicknessBase
default:
return errors.New("unknown DripstoneThickness: " + str)
}
return nil
}
type Half byte
const (
Top Half = iota
Bottom
)
var strHalf = [...]string{"top", "bottom"}
func (h Half) String() string {
if int(h) < len(strHalf) {
return strHalf[h]
}
return "invalid Half"
}
func (h Half) MarshalText() (text []byte, err error) {
if int(h) < len(strHalf) {
return []byte(strHalf[h]), nil
}
return nil, errors.New("invalid Half: " + strconv.Itoa(int(h)))
}
func (h *Half) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "top":
*h = Top
case "bottom":
*h = Bottom
default:
return errors.New("unknown Half: " + str)
}
return nil
}
type NoteBlockInstrument byte
const (
NoteBlockInstrumentHarp NoteBlockInstrument = iota
NoteBlockInstrumentBasedrum
NoteBlockInstrumentSnare
NoteBlockInstrumentHat
NoteBlockInstrumentBass
NoteBlockInstrumentFlute
NoteBlockInstrumentBell
NoteBlockInstrumentGuitar
NoteBlockInstrumentChime
NoteBlockInstrumentXylophone
NoteBlockInstrumentIronXylophone
NoteBlockInstrumentCowBell
NoteBlockInstrumentDidgeridoo
NoteBlockInstrumentBit
NoteBlockInstrumentBanjo
NoteBlockInstrumentPling
)
var strNoteBlockInstrument = [...]string{"harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling"}
func (n NoteBlockInstrument) String() string {
if int(n) < len(strNoteBlockInstrument) {
return strNoteBlockInstrument[n]
}
return "invalid NoteBlockInstrument"
}
func (n NoteBlockInstrument) MarshalText() (text []byte, err error) {
if int(n) < len(strNoteBlockInstrument) {
return []byte(strNoteBlockInstrument[n]), nil
}
return nil, errors.New("invalid NoteBlockInstrument: " + strconv.Itoa(int(n)))
}
func (n *NoteBlockInstrument) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "harp":
*n = NoteBlockInstrumentHarp
case "basedrum":
*n = NoteBlockInstrumentBasedrum
case "snare":
*n = NoteBlockInstrumentSnare
case "hat":
*n = NoteBlockInstrumentHat
case "bass":
*n = NoteBlockInstrumentBass
case "flute":
*n = NoteBlockInstrumentFlute
case "bell":
*n = NoteBlockInstrumentBell
case "guitar":
*n = NoteBlockInstrumentGuitar
case "chime":
*n = NoteBlockInstrumentChime
case "xylophone":
*n = NoteBlockInstrumentXylophone
case "iron_xylophone":
*n = NoteBlockInstrumentIronXylophone
case "cow_bell":
*n = NoteBlockInstrumentCowBell
case "didgeridoo":
*n = NoteBlockInstrumentDidgeridoo
case "bit":
*n = NoteBlockInstrumentBit
case "banjo":
*n = NoteBlockInstrumentBanjo
case "pling":
*n = NoteBlockInstrumentPling
default:
return errors.New("unknown NoteBlockInstrument: " + str)
}
return nil
}
type PistonType byte
const (
PistonTypeNormal PistonType = iota
PistonTypeSticky
)
var strPistonType = [...]string{"normal", "sticky"}
func (p PistonType) String() string {
if int(p) < len(strPistonType) {
return strPistonType[p]
}
return "invalid PistonType"
}
func (p PistonType) MarshalText() (text []byte, err error) {
if int(p) < len(strPistonType) {
return []byte(strPistonType[p]), nil
}
return nil, errors.New("invalid PistonType: " + strconv.Itoa(int(p)))
}
func (p *PistonType) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "normal":
*p = PistonTypeNormal
case "sticky":
*p = PistonTypeSticky
default:
return errors.New("unknown PistonType: " + str)
}
return nil
}
type RailShape byte
const (
RailShapeNorthSouth RailShape = iota
RailShapeEastWest
RailShapeAscendingEast
RailShapeAscendingWest
RailShapeAscendingNorth
RailShapeAscendingSouth
RailShapeSouthEast
RailShapeSouthWest
RailShapeNorthWest
RailShapeNorthEast
)
var strRailShape = [...]string{"north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south", "south_east", "south_west", "north_west", "north_east"}
func (r RailShape) String() string {
if int(r) < len(strRailShape) {
return strRailShape[r]
}
return "invalid RailShape"
}
func (r RailShape) MarshalText() (text []byte, err error) {
if int(r) < len(strRailShape) {
return []byte(strRailShape[r]), nil
}
return nil, errors.New("invalid RailShape: " + strconv.Itoa(int(r)))
}
func (r *RailShape) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "north_south":
*r = RailShapeNorthSouth
case "east_west":
*r = RailShapeEastWest
case "ascending_east":
*r = RailShapeAscendingEast
case "ascending_west":
*r = RailShapeAscendingWest
case "ascending_north":
*r = RailShapeAscendingNorth
case "ascending_south":
*r = RailShapeAscendingSouth
case "south_east":
*r = RailShapeSouthEast
case "south_west":
*r = RailShapeSouthWest
case "north_west":
*r = RailShapeNorthWest
case "north_east":
*r = RailShapeNorthEast
default:
return errors.New("unknown RailShape: " + str)
}
return nil
}
type RedstoneSide byte
const (
RedstoneSideUp RedstoneSide = iota
RedstoneSideSide
RedstoneSideNone
)
var strRedstoneSide = [...]string{"up", "side", "none"}
func (r RedstoneSide) String() string {
if int(r) < len(strRedstoneSide) {
return strRedstoneSide[r]
}
return "invalid RedstoneSide"
}
func (r RedstoneSide) MarshalText() (text []byte, err error) {
if int(r) < len(strRedstoneSide) {
return []byte(strRedstoneSide[r]), nil
}
return nil, errors.New("invalid RedstoneSide: " + strconv.Itoa(int(r)))
}
func (r *RedstoneSide) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "up":
*r = RedstoneSideUp
case "side":
*r = RedstoneSideSide
case "none":
*r = RedstoneSideNone
default:
return errors.New("unknown RedstoneSide: " + str)
}
return nil
}
type SculkSensorPhase byte
const (
SculkSensorPhaseInactive SculkSensorPhase = iota
SculkSensorPhaseActive
SculkSensorPhaseCooldown
)
var strSculkSensorPhase = [...]string{"inactive", "active", "cooldown"}
func (s SculkSensorPhase) String() string {
if int(s) < len(strSculkSensorPhase) {
return strSculkSensorPhase[s]
}
return "invalid SculkSensorPhase"
}
func (s SculkSensorPhase) MarshalText() (text []byte, err error) {
if int(s) < len(strSculkSensorPhase) {
return []byte(strSculkSensorPhase[s]), nil
}
return nil, errors.New("invalid SculkSensorPhase: " + strconv.Itoa(int(s)))
}
func (s *SculkSensorPhase) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "inactive":
*s = SculkSensorPhaseInactive
case "active":
*s = SculkSensorPhaseActive
case "cooldown":
*s = SculkSensorPhaseCooldown
default:
return errors.New("unknown SculkSensorPhase: " + str)
}
return nil
}
type SlabType byte
const (
SlabTypeTop SlabType = iota
SlabTypeBottom
SlabTypeDouble
)
var strSlabType = [...]string{"top", "bottom", "double"}
func (s SlabType) String() string {
if int(s) < len(strSlabType) {
return strSlabType[s]
}
return "invalid SlabType"
}
func (s SlabType) MarshalText() (text []byte, err error) {
if int(s) < len(strSlabType) {
return []byte(strSlabType[s]), nil
}
return nil, errors.New("invalid SlabType: " + strconv.Itoa(int(s)))
}
func (s *SlabType) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "top":
*s = SlabTypeTop
case "bottom":
*s = SlabTypeBottom
case "double":
*s = SlabTypeDouble
default:
return errors.New("unknown SlabType: " + str)
}
return nil
}
type StairsShape byte
const (
StairsShapeStraight StairsShape = iota
StairsShapeInnerLeft
StairsShapeInnerRight
StairsShapeOuterLeft
StairsShapeOuterRight
)
var strStairsShape = [...]string{"straight", "inner_left", "inner_right", "outer_left", "outer_right"}
func (s StairsShape) String() string {
if int(s) < len(strStairsShape) {
return strStairsShape[s]
}
return "invalid StairsShape"
}
func (s StairsShape) MarshalText() (text []byte, err error) {
if int(s) < len(strStairsShape) {
return []byte(strStairsShape[s]), nil
}
return nil, errors.New("invalid StairsShape: " + strconv.Itoa(int(s)))
}
func (s *StairsShape) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "straight":
*s = StairsShapeStraight
case "inner_left":
*s = StairsShapeInnerLeft
case "inner_right":
*s = StairsShapeInnerRight
case "outer_left":
*s = StairsShapeOuterLeft
case "outer_right":
*s = StairsShapeOuterRight
default:
return errors.New("unknown StairsShape: " + str)
}
return nil
}
type StructureMode byte
const (
StructureModeSave StructureMode = iota
StructureModeLoad
StructureModeCorner
StructureModeData
)
var strStructureMode = [...]string{"save", "load", "corner", "data"}
func (s StructureMode) String() string {
if int(s) < len(strStructureMode) {
return strStructureMode[s]
}
return "invalid StructureMode"
}
func (s StructureMode) MarshalText() (text []byte, err error) {
if int(s) < len(strStructureMode) {
return []byte(strStructureMode[s]), nil
}
return nil, errors.New("invalid StructureMode: " + strconv.Itoa(int(s)))
}
func (s *StructureMode) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "save":
*s = StructureModeSave
case "load":
*s = StructureModeLoad
case "corner":
*s = StructureModeCorner
case "data":
*s = StructureModeData
default:
return errors.New("unknown StructureMode: " + str)
}
return nil
}
type Tilt byte
const (
TiltNone Tilt = iota
TiltUnstable
TiltPartial
TiltFull
)
var strTilt = [...]string{"none", "unstable", "partial", "full"}
func (t Tilt) String() string {
if int(t) < len(strTilt) {
return strTilt[t]
}
return "invalid Tilt"
}
func (t Tilt) MarshalText() (text []byte, err error) {
if int(t) < len(strTilt) {
return []byte(strTilt[t]), nil
}
return nil, errors.New("invalid Tilt: " + strconv.Itoa(int(t)))
}
func (t *Tilt) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "none":
*t = TiltNone
case "unstable":
*t = TiltUnstable
case "partial":
*t = TiltPartial
case "full":
*t = TiltFull
default:
return errors.New("unknown Tilt: " + str)
}
return nil
}
type WallSide byte
const (
WallSideNone WallSide = iota
WallSideLow
WallSideTall
)
var strWallSide = [...]string{"none", "low", "tall"}
func (w WallSide) String() string {
if int(w) < len(strWallSide) {
return strWallSide[w]
}
return "invalid WallSide"
}
func (w WallSide) MarshalText() (text []byte, err error) {
if int(w) < len(strWallSide) {
return []byte(strWallSide[w]), nil
}
return nil, errors.New("invalid WallSide: " + strconv.Itoa(int(w)))
}
func (w *WallSide) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "none":
*w = WallSideNone
case "low":
*w = WallSideLow
case "tall":
*w = WallSideTall
default:
return errors.New("unknown WallSide: " + str)
}
return nil
}
type FrontAndTop byte
const (
DownEast FrontAndTop = iota
DownNorth
DownSouth
DownWest
UpEast
UpNorth
UpSouth
UpWest
WestUp
EastUp
NorthUp
SouthUp
)
var strFrontAndTop = [...]string{"down_east", "down_north", "down_south", "down_west", "up_east", "up_north", "up_south", "up_west", "west_up", "east_up", "north_up", "south_up"}
func (f FrontAndTop) String() string {
if int(f) < len(strFrontAndTop) {
return strFrontAndTop[f]
}
return "invalid FrontAndTop"
}
func (f FrontAndTop) MarshalText() (text []byte, err error) {
if int(f) < len(strFrontAndTop) {
return []byte(strFrontAndTop[f]), nil
}
return nil, errors.New("invalid FrontAndTop: " + strconv.Itoa(int(f)))
}
func (f *FrontAndTop) UnmarshalText(text []byte) error {
switch str := string(text); str {
case "down_east":
*f = DownEast
case "down_north":
*f = DownNorth
case "down_south":
*f = DownSouth
case "down_west":
*f = DownWest
case "up_east":
*f = UpEast
case "up_north":
*f = UpNorth
case "up_south":
*f = UpSouth
case "up_west":
*f = UpWest
case "west_up":
*f = WestUp
case "east_up":
*f = EastUp
case "north_up":
*f = NorthUp
case "south_up":
*f = SouthUp
default:
return errors.New("unknown FrontAndTop: " + str)
}
return nil
}

View File

@ -23,6 +23,9 @@ func Unmarshal(data []byte, v interface{}) error {
// For example, you can decode an NBT value which root tag is TagCompound(0x0a)
// into a struct or map, but not a string.
//
// If v implement Unmarshaler, the method will be called and override the default behavior.
// Else if v implement encoding.TextUnmarshaler, the value will be encoded as TagString.
//
// This method also return tag name of the root tag.
// In real world, it is often empty, but the API should allow you to get it when ever you want.
func (d *Decoder) Decode(v interface{}) (string, error) {

View File

@ -405,72 +405,3 @@ func TestDecoder_Decode_textUnmarshaler(t *testing.T) {
t.Errorf("b should be true")
}
}
func TestRawMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value RawMessage
List RawMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
t.Fatal(tag)
} else {
if tag != "ab" {
t.Fatalf("Decode tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("Decode Key error: want %v, get: %v", 12, container.Key)
}
if !bytes.Equal(container.Value.Data, []byte{
0, 4, 'T', 'n', 'z', 'e',
}) {
t.Fatalf("Decode Key error: get: %v", container.Value)
}
if !bytes.Equal(container.List.Data, []byte{
TagCompound, 0, 0, 0, 2,
0, 0,
}) {
t.Fatalf("Decode List error: get: %v", container.List)
}
}
}
func TestStringifiedMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 5, 'T', 'n', ' ', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value StringifiedMessage
List StringifiedMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
t.Fatal(tag, err)
} else {
if tag != "ab" {
t.Fatalf("UnmarshalNBT tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("UnmarshalNBT Key error: want %v, get: %v", 12, container.Key)
}
if container.Value != `"Tn ze"` {
t.Fatalf("UnmarshalNBT Key error: get: %v", container.Value)
}
if container.List != "[{},{}]" {
t.Fatalf("UnmarshalNBT List error: get: %v", container.List)
}
}
}

View File

@ -2,6 +2,7 @@ package nbt
import (
"bytes"
"encoding"
"errors"
"fmt"
"io"
@ -37,8 +38,8 @@ func NewEncoder(w io.Writer) *Encoder {
// expect `[]int8`, `[]int32`, `[]int64`, `[]uint8`, `[]uint32` and `[]uint64`,
// which TagByteArray, TagIntArray and TagLongArray.
// To force encode them as TagList, add a struct field tag.
// You haven't ability to encode them as TagList as root element at this time,
// issue or pull-request is welcome.
//
//
func (e *Encoder) Encode(v interface{}, tagName string) error {
t, val := getTagType(reflect.ValueOf(v))
return e.marshal(val, t, tagName)
@ -129,10 +130,22 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error {
}
case TagString:
if err := e.writeInt16(int16(val.Len())); err != nil {
var str []byte
if val.NumMethod() > 0 && val.CanInterface() {
if t, ok := val.Interface().(encoding.TextMarshaler); ok {
var err error
str, err = t.MarshalText()
if err != nil {
return err
}
}
} else {
str = []byte(val.String())
}
if err := e.writeInt16(int16(len(str))); err != nil {
return err
}
_, err := e.w.Write([]byte(val.String()))
_, err := e.w.Write(str)
return err
case TagCompound:
@ -205,17 +218,23 @@ func getTagType(v reflect.Value) (byte, reflect.Value) {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(Marshaler); ok {
i := v.Interface()
if u, ok := i.(Marshaler); ok {
return u.TagType(), v
} else if _, ok := i.(encoding.TextMarshaler); ok {
return TagString, v
}
}
v = v.Elem()
}
if v.CanInterface() {
if encoder, ok := v.Interface().(Marshaler); ok {
return encoder.TagType(), v
if v.Type().NumMethod() > 0 && v.CanInterface() {
i := v.Interface()
if u, ok := i.(Marshaler); ok {
return u.TagType(), v
} else if _, ok := i.(encoding.TextMarshaler); ok {
return TagString, v
}
}

View File

@ -208,29 +208,6 @@ func TestEncoder_Encode_map(t *testing.T) {
}
}
func TestRawMessage_Encode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e',
TagEnd,
}
var container struct {
Key int32
Value RawMessage
}
container.Key = 12
container.Value.Type = TagString
container.Value.Data = []byte{0, 4, 'T', 'n', 'z', 'e'}
var buf bytes.Buffer
if err := NewEncoder(&buf).Encode(container, "ab"); err != nil {
t.Fatalf("Encode error: %v", err)
} else if !bytes.Equal(data, buf.Bytes()) {
t.Fatalf("Encode error: want %v, get: %v", data, buf.Bytes())
}
}
func TestEncoder_Encode_interface(t *testing.T) {
data := map[string]interface{}{
"Key": int32(12),
@ -253,3 +230,18 @@ func TestEncoder_Encode_interface(t *testing.T) {
t.Fatalf("want: (%v, %v), but got (%v, %v)", 12, "Tnze", container.Key, container.Value)
}
}
func TestEncoder_Encode_textMarshaler(t *testing.T) {
var b TextBool = true
data, err := Marshal(&b)
if err != nil {
t.Fatal(err)
}
wants := []byte{
TagString, 0, 0,
0, 4, 't', 'r', 'u', 'e',
}
if !bytes.Equal(data, wants) {
t.Errorf("get %v, want %v", data, wants)
}
}

66
nbt/rawmsg_test.go Normal file
View File

@ -0,0 +1,66 @@
package nbt
import (
"bytes"
"testing"
)
func TestRawMessage_Encode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e',
TagEnd,
}
var container struct {
Key int32
Value RawMessage
}
container.Key = 12
container.Value.Type = TagString
container.Value.Data = []byte{0, 4, 'T', 'n', 'z', 'e'}
var buf bytes.Buffer
if err := NewEncoder(&buf).Encode(container, "ab"); err != nil {
t.Fatalf("Encode error: %v", err)
} else if !bytes.Equal(data, buf.Bytes()) {
t.Fatalf("Encode error: want %v, get: %v", data, buf.Bytes())
}
}
func TestRawMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value RawMessage
List RawMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
t.Fatal(tag)
} else {
if tag != "ab" {
t.Fatalf("Decode tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("Decode Key error: want %v, get: %v", 12, container.Key)
}
if !bytes.Equal(container.Value.Data, []byte{
0, 4, 'T', 'n', 'z', 'e',
}) {
t.Fatalf("Decode Key error: get: %v", container.Value)
}
if !bytes.Equal(container.List.Data, []byte{
TagCompound, 0, 0, 0, 2,
0, 0,
}) {
t.Fatalf("Decode List error: get: %v", container.List)
}
}
}

View File

@ -1,3 +1,38 @@
package nbt
//TODO: Test SNBT encode
import (
"bytes"
"testing"
)
func TestStringifiedMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12,
TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 5, 'T', 'n', ' ', 'z', 'e',
TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0,
TagEnd,
}
var container struct {
Key int32
Value StringifiedMessage
List StringifiedMessage
}
if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil {
t.Fatal(tag, err)
} else {
if tag != "ab" {
t.Fatalf("UnmarshalNBT tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("UnmarshalNBT Key error: want %v, get: %v", 12, container.Key)
}
if container.Value != `"Tn ze"` {
t.Fatalf("UnmarshalNBT Key error: get: %v", container.Value)
}
if container.List != "[{},{}]" {
t.Fatalf("UnmarshalNBT List error: get: %v", container.List)
}
}
}

View File

@ -45,10 +45,10 @@ type PacketHandler struct {
type packetHandlerFunc func(player *Player, packet Packet758) error
//go:embed DimensionCodec.snbt
var dimensionCodecSNBT string
var dimensionCodecSNBT nbt.StringifiedMessage
//go:embed Dimension.snbt
var dimensionSNBT string
var dimensionSNBT nbt.StringifiedMessage
func NewGame(dim Level, components ...Component) *Game {
g := &Game{
@ -91,8 +91,8 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net
pk.Array([]pk.Identifier{
pk.Identifier(dimInfo.Name),
}),
pk.NBT(nbt.StringifiedMessage(dimensionCodecSNBT)),
pk.NBT(nbt.StringifiedMessage(dimensionSNBT)),
pk.NBT(dimensionCodecSNBT),
pk.NBT(dimensionSNBT),
pk.Identifier(dimInfo.Name), // World Name
pk.Long(dimInfo.HashedSeed), // Hashed seed
pk.VarInt(0), // Max Players (Ignored by client)