From 3dadabf7060fe99744a5bed793c6eae4a89161f1 Mon Sep 17 00:00:00 2001 From: olebeck <31539311+olebeck@users.noreply.github.com> Date: Tue, 4 Apr 2023 17:57:24 +0200 Subject: [PATCH] update nbtconv --- utils/nbtconv/item.go | 4 +- utils/nbtconv/mapread.go | 89 ----------------- utils/nbtconv/read.go | 207 ++++++++++++++++++++++++++++++++++----- utils/nbtconv/write.go | 37 ++++--- 4 files changed, 204 insertions(+), 133 deletions(-) delete mode 100644 utils/nbtconv/mapread.go diff --git a/utils/nbtconv/item.go b/utils/nbtconv/item.go index 7c704aa..1443ddc 100644 --- a/utils/nbtconv/item.go +++ b/utils/nbtconv/item.go @@ -8,11 +8,11 @@ import ( func InvFromNBT(inv *inventory.Inventory, items []any) { for _, itemData := range items { data, _ := itemData.(map[string]any) - it := ReadItem(data, nil) + it := Item(data, nil) if it.Empty() { continue } - _ = inv.SetItem(int(Map[byte](data, "Slot")), it) + _ = inv.SetItem(int(Uint8(data, "Slot")), it) } } diff --git a/utils/nbtconv/mapread.go b/utils/nbtconv/mapread.go deleted file mode 100644 index 5629286..0000000 --- a/utils/nbtconv/mapread.go +++ /dev/null @@ -1,89 +0,0 @@ -package nbtconv - -import ( - "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/item" - "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" -) - -// Map reads a value of the type T from the map passed. Map never panics. If the key was not found in the map -// or if the value was of a different type, the default value of type T is returned. -func Map[T any](m map[string]any, key string) T { - v, _ := m[key].(T) - return v -} - -// MapVec3 converts x, y and z values in an NBT map to an mgl64.Vec3. -func MapVec3(x map[string]any, k string) mgl64.Vec3 { - if i, ok := x[k].([]any); ok { - if len(i) != 3 { - return mgl64.Vec3{} - } - var v mgl64.Vec3 - for index, f := range i { - f32, _ := f.(float32) - v[index] = float64(f32) - } - return v - } else if i, ok := x[k].([]float32); ok { - if len(i) != 3 { - return mgl64.Vec3{} - } - return mgl64.Vec3{float64(i[0]), float64(i[1]), float64(i[2])} - } - return mgl64.Vec3{} -} - -// Vec3ToFloat32Slice converts an mgl64.Vec3 to a []float32 with 3 elements. -func Vec3ToFloat32Slice(x mgl64.Vec3) []float32 { - return []float32{float32(x[0]), float32(x[1]), float32(x[2])} -} - -// MapPos converts x, y and z values in an NBT map to a cube.Pos. -func MapPos(x map[string]any, k string) cube.Pos { - if i, ok := x[k].([]any); ok { - if len(i) != 3 { - return cube.Pos{} - } - var v cube.Pos - for index, f := range i { - f32, _ := f.(int32) - v[index] = int(f32) - } - return v - } else if i, ok := x[k].([]int32); ok { - if len(i) != 3 { - return cube.Pos{} - } - return cube.Pos{int(i[0]), int(i[1]), int(i[2])} - } - return cube.Pos{} -} - -// PosToInt32Slice converts a cube.Pos to a []int32 with 3 elements. -func PosToInt32Slice(x cube.Pos) []int32 { - return []int32{int32(x[0]), int32(x[1]), int32(x[2])} -} - -// MapBlock converts a block's name and properties in a map obtained by decoding NBT to a world.Block. -func MapBlock(x map[string]any, k string) world.Block { - if m, ok := x[k].(map[string]any); ok { - return ReadBlock(m) - } - return nil -} - -// MapItem converts an item's name, count, damage (and properties when it is a block) in a map obtained by decoding NBT -// to a world.Item. -func MapItem(x map[string]any, k string) item.Stack { - if m, ok := x[k].(map[string]any); ok { - s := readItemStack(m) - readDamage(m, &s, true) - readEnchantments(m, &s) - readDisplay(m, &s) - readDragonflyData(m, &s) - return s - } - return item.Stack{} -} diff --git a/utils/nbtconv/read.go b/utils/nbtconv/read.go index 20df0f2..81d6e9d 100644 --- a/utils/nbtconv/read.go +++ b/utils/nbtconv/read.go @@ -3,78 +3,233 @@ package nbtconv import ( "bytes" "encoding/gob" + "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "golang.org/x/exp/constraints" + "time" ) -// ReadItem decodes the data of an item into an item stack. -func ReadItem(data map[string]any, s *item.Stack) item.Stack { - disk := s == nil +// Bool reads a uint8 value from a map at key k and returns true if it equals 1. +func Bool(m map[string]any, k string) bool { + return Uint8(m, k) == 1 +} + +// Uint8 reads a uint8 value from a map at key k. +func Uint8(m map[string]any, k string) uint8 { + v, _ := m[k].(uint8) + return v +} + +// String reads a string value from a map at key k. +func String(m map[string]any, k string) string { + v, _ := m[k].(string) + return v +} + +// Int16 reads an int16 value from a map at key k. +func Int16(m map[string]any, k string) int16 { + v, _ := m[k].(int16) + return v +} + +// Int32 reads an int32 value from a map at key k. +func Int32(m map[string]any, k string) int32 { + v, _ := m[k].(int32) + return v +} + +// Int64 reads an int16 value from a map at key k. +func Int64(m map[string]any, k string) int64 { + v, _ := m[k].(int64) + return v +} + +// TickDuration reads a uint8/int16/in32 value from a map at key k and converts +// it from ticks to a time.Duration. +func TickDuration[T constraints.Integer](m map[string]any, k string) time.Duration { + var v time.Duration + switch any(*new(T)).(type) { + case uint8: + v = time.Duration(Uint8(m, k)) + case int16: + v = time.Duration(Int16(m, k)) + case int32: + v = time.Duration(Int32(m, k)) + default: + panic("invalid tick duration value type") + } + return v * time.Millisecond * 50 +} + +// Float32 reads a float32 value from a map at key k. +func Float32(m map[string]any, k string) float32 { + v, _ := m[k].(float32) + return v +} + +// Float64 reads a float64 value from a map at key k. +func Float64(m map[string]any, k string) float64 { + v, _ := m[k].(float64) + return v +} + +// Slice reads a []any value from a map at key k. +func Slice(m map[string]any, k string) []any { + v, _ := m[k].([]any) + return v +} + +// Vec3 converts x, y and z values in an NBT map to an mgl64.Vec3. +func Vec3(x map[string]any, k string) mgl64.Vec3 { + if i, ok := x[k].([]any); ok { + if len(i) != 3 { + return mgl64.Vec3{} + } + var v mgl64.Vec3 + for index, f := range i { + f32, _ := f.(float32) + v[index] = float64(f32) + } + return v + } else if i, ok := x[k].([]float32); ok { + if len(i) != 3 { + return mgl64.Vec3{} + } + return mgl64.Vec3{float64(i[0]), float64(i[1]), float64(i[2])} + } + return mgl64.Vec3{} +} + +// Vec3ToFloat32Slice converts an mgl64.Vec3 to a []float32 with 3 elements. +func Vec3ToFloat32Slice(x mgl64.Vec3) []float32 { + return []float32{float32(x[0]), float32(x[1]), float32(x[2])} +} + +// Pos converts x, y and z values in an NBT map to a cube.Pos. +func Pos(x map[string]any, k string) cube.Pos { + if i, ok := x[k].([]any); ok { + if len(i) != 3 { + return cube.Pos{} + } + var v cube.Pos + for index, f := range i { + f32, _ := f.(int32) + v[index] = int(f32) + } + return v + } else if i, ok := x[k].([]int32); ok { + if len(i) != 3 { + return cube.Pos{} + } + return cube.Pos{int(i[0]), int(i[1]), int(i[2])} + } + return cube.Pos{} +} + +// PosToInt32Slice converts a cube.Pos to a []int32 with 3 elements. +func PosToInt32Slice(x cube.Pos) []int32 { + return []int32{int32(x[0]), int32(x[1]), int32(x[2])} +} + +// MapItem converts an item's name, count, damage (and properties when it is a block) in a map obtained by decoding NBT +// to a world.Item. +func MapItem(x map[string]any, k string) item.Stack { + if m, ok := x[k].(map[string]any); ok { + tag, ok := m["tag"].(map[string]any) + if !ok { + tag = map[string]any{} + } + + s := readItemStack(m, tag) + readDamage(tag, &s, true) + readEnchantments(tag, &s) + readDisplay(tag, &s) + readDragonflyData(tag, &s) + return s + } + return item.Stack{} +} + +// Item decodes the data of an item into an item stack. +func Item(data map[string]any, s *item.Stack) item.Stack { + disk, tag := s == nil, data if disk { - a := readItemStack(data) + t, ok := data["tag"].(map[string]any) + if !ok { + t = map[string]any{} + } + tag = t + + a := readItemStack(data, tag) s = &a } - readDamage(data, s, disk) - readAnvilCost(data, s) - readDisplay(data, s) - readEnchantments(data, s) - readDragonflyData(data, s) + + readAnvilCost(tag, s) + readDamage(tag, s, disk) + readDisplay(tag, s) + readDragonflyData(tag, s) + readEnchantments(tag, s) return *s } -// ReadBlock decodes the data of a block into a world.Block. -func ReadBlock(m map[string]any) world.Block { - name, _ := m["name"].(string) - properties, _ := m["states"].(map[string]any) - b, _ := world.BlockByName(name, properties) - return b +// Block decodes the data of a block into a world.Block. +func Block(m map[string]any, k string) world.Block { + if mk, ok := m[k].(map[string]any); ok { + name, _ := mk["name"].(string) + properties, _ := mk["states"].(map[string]any) + b, _ := world.BlockByName(name, properties) + return b + } + return nil } // readItemStack reads an item.Stack from the NBT in the map passed. -func readItemStack(m map[string]any) item.Stack { +func readItemStack(m, t map[string]any) item.Stack { var it world.Item - if blockItem, ok := MapBlock(m, "Block").(world.Item); ok { + if blockItem, ok := Block(m, "Block").(world.Item); ok { it = blockItem } - if v, ok := world.ItemByName(Map[string](m, "Name"), Map[int16](m, "Damage")); ok { + if v, ok := world.ItemByName(String(m, "Name"), Int16(m, "Damage")); ok { it = v } if it == nil { return item.Stack{} } if n, ok := it.(world.NBTer); ok { - it = n.DecodeNBT(m).(world.Item) + it = n.DecodeNBT(t).(world.Item) } - return item.NewStack(it, int(Map[byte](m, "Count"))) + return item.NewStack(it, int(Uint8(m, "Count"))) } // readDamage reads the damage value stored in the NBT with the Damage tag and saves it to the item.Stack passed. func readDamage(m map[string]any, s *item.Stack, disk bool) { if disk { - *s = s.Damage(int(Map[int16](m, "Damage"))) + *s = s.Damage(int(Int16(m, "Damage"))) return } - *s = s.Damage(int(Map[int32](m, "Damage"))) + *s = s.Damage(int(Int32(m, "Damage"))) } // readAnvilCost ... func readAnvilCost(m map[string]any, s *item.Stack) { - *s = s.WithAnvilCost(int(Map[int32](m, "RepairCost"))) + *s = s.WithAnvilCost(int(Int32(m, "RepairCost"))) } // readEnchantments reads the enchantments stored in the ench tag of the NBT passed and stores it into an item.Stack. func readEnchantments(m map[string]any, s *item.Stack) { enchantments, ok := m["ench"].([]map[string]any) if !ok { - for _, e := range Map[[]any](m, "ench") { + for _, e := range Slice(m, "ench") { if v, ok := e.(map[string]any); ok { enchantments = append(enchantments, v) } } } for _, ench := range enchantments { - if t, ok := item.EnchantmentByID(int(Map[int16](ench, "id"))); ok { - *s = s.WithEnchantments(item.NewEnchantment(t, int(Map[int16](ench, "lvl")))) + if t, ok := item.EnchantmentByID(int(Int16(ench, "id"))); ok { + *s = s.WithEnchantments(item.NewEnchantment(t, int(Int16(ench, "lvl")))) } } } diff --git a/utils/nbtconv/write.go b/utils/nbtconv/write.go index 37e8f40..4102251 100644 --- a/utils/nbtconv/write.go +++ b/utils/nbtconv/write.go @@ -3,33 +3,35 @@ package nbtconv import ( "bytes" "encoding/gob" - "sort" - "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/chunk" + "sort" ) // WriteItem encodes an item stack into a map that can be encoded using NBT. func WriteItem(s item.Stack, disk bool) map[string]any { - m := make(map[string]any) + tag := make(map[string]any) if nbt, ok := s.Item().(world.NBTer); ok { for k, v := range nbt.EncodeNBT() { - m[k] = v + tag[k] = v } } - if disk { - writeItemStack(m, s) - } - t := make(map[string]any) - writeDamage(t, s, disk) - writeAnvilCost(t, s) - writeDisplay(t, s) - writeEnchantments(t, s) - m["tag"] = t + writeAnvilCost(tag, s) + writeDamage(tag, s, disk) + writeDisplay(tag, s) + writeDragonflyData(tag, s) + writeEnchantments(tag, s) - writeDragonflyData(m, s) - return m + data := make(map[string]any) + if disk { + writeItemStack(data, tag, s) + } else { + for k, v := range tag { + data[k] = v + } + } + return data } // WriteBlock encodes a world.Block into a map that can be encoded using NBT. @@ -43,7 +45,7 @@ func WriteBlock(b world.Block) map[string]any { } // writeItemStack writes the name, metadata value, count and NBT of an item to a map ready for NBT encoding. -func writeItemStack(m map[string]any, s item.Stack) { +func writeItemStack(m, t map[string]any, s item.Stack) { m["Name"], m["Damage"] = s.Item().EncodeItem() if b, ok := s.Item().(world.Block); ok { v := map[string]any{} @@ -51,6 +53,9 @@ func writeItemStack(m map[string]any, s item.Stack) { m["Block"] = v } m["Count"] = byte(s.Count()) + if len(t) > 0 { + m["tag"] = t + } } // writeBlock writes the name, properties and version of a block to a map ready for NBT encoding.