github.com/df-mc/dragonfly@v0.9.13/server/internal/nbtconv/read.go (about)

     1  package nbtconv
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"github.com/df-mc/dragonfly/server/block/cube"
     7  	"github.com/df-mc/dragonfly/server/item"
     8  	"github.com/df-mc/dragonfly/server/world"
     9  	"github.com/go-gl/mathgl/mgl64"
    10  	"golang.org/x/exp/constraints"
    11  	"time"
    12  )
    13  
    14  // Bool reads a uint8 value from a map at key k and returns true if it equals 1.
    15  func Bool(m map[string]any, k string) bool {
    16  	return Uint8(m, k) == 1
    17  }
    18  
    19  // Uint8 reads a uint8 value from a map at key k.
    20  func Uint8(m map[string]any, k string) uint8 {
    21  	v, _ := m[k].(uint8)
    22  	return v
    23  }
    24  
    25  // String reads a string value from a map at key k.
    26  func String(m map[string]any, k string) string {
    27  	v, _ := m[k].(string)
    28  	return v
    29  }
    30  
    31  // Int16 reads an int16 value from a map at key k.
    32  func Int16(m map[string]any, k string) int16 {
    33  	v, _ := m[k].(int16)
    34  	return v
    35  }
    36  
    37  // Int32 reads an int32 value from a map at key k.
    38  func Int32(m map[string]any, k string) int32 {
    39  	v, _ := m[k].(int32)
    40  	return v
    41  }
    42  
    43  // Int64 reads an int16 value from a map at key k.
    44  func Int64(m map[string]any, k string) int64 {
    45  	v, _ := m[k].(int64)
    46  	return v
    47  }
    48  
    49  // TickDuration reads a uint8/int16/in32 value from a map at key k and converts
    50  // it from ticks to a time.Duration.
    51  func TickDuration[T constraints.Integer](m map[string]any, k string) time.Duration {
    52  	var v time.Duration
    53  	switch any(*new(T)).(type) {
    54  	case uint8:
    55  		v = time.Duration(Uint8(m, k))
    56  	case int16:
    57  		v = time.Duration(Int16(m, k))
    58  	case int32:
    59  		v = time.Duration(Int32(m, k))
    60  	default:
    61  		panic("invalid tick duration value type")
    62  	}
    63  	return v * time.Millisecond * 50
    64  }
    65  
    66  // Float32 reads a float32 value from a map at key k.
    67  func Float32(m map[string]any, k string) float32 {
    68  	v, _ := m[k].(float32)
    69  	return v
    70  }
    71  
    72  // Rotation reads a cube.Rotation from the map passed.
    73  func Rotation(m map[string]any) cube.Rotation {
    74  	return cube.Rotation{float64(Float32(m, "Yaw")), float64(Float32(m, "Pitch"))}
    75  }
    76  
    77  // Float64 reads a float64 value from a map at key k.
    78  func Float64(m map[string]any, k string) float64 {
    79  	v, _ := m[k].(float64)
    80  	return v
    81  }
    82  
    83  // Slice reads a []any value from a map at key k.
    84  func Slice(m map[string]any, k string) []any {
    85  	v, _ := m[k].([]any)
    86  	return v
    87  }
    88  
    89  // Vec3 converts x, y and z values in an NBT map to an mgl64.Vec3.
    90  func Vec3(x map[string]any, k string) mgl64.Vec3 {
    91  	if i, ok := x[k].([]any); ok {
    92  		if len(i) != 3 {
    93  			return mgl64.Vec3{}
    94  		}
    95  		var v mgl64.Vec3
    96  		for index, f := range i {
    97  			f32, _ := f.(float32)
    98  			v[index] = float64(f32)
    99  		}
   100  		return v
   101  	} else if i, ok := x[k].([]float32); ok {
   102  		if len(i) != 3 {
   103  			return mgl64.Vec3{}
   104  		}
   105  		return mgl64.Vec3{float64(i[0]), float64(i[1]), float64(i[2])}
   106  	}
   107  	return mgl64.Vec3{}
   108  }
   109  
   110  // Vec3ToFloat32Slice converts an mgl64.Vec3 to a []float32 with 3 elements.
   111  func Vec3ToFloat32Slice(x mgl64.Vec3) []float32 {
   112  	return []float32{float32(x[0]), float32(x[1]), float32(x[2])}
   113  }
   114  
   115  // Pos converts x, y and z values in an NBT map to a cube.Pos.
   116  func Pos(x map[string]any, k string) cube.Pos {
   117  	if i, ok := x[k].([]any); ok {
   118  		if len(i) != 3 {
   119  			return cube.Pos{}
   120  		}
   121  		var v cube.Pos
   122  		for index, f := range i {
   123  			f32, _ := f.(int32)
   124  			v[index] = int(f32)
   125  		}
   126  		return v
   127  	} else if i, ok := x[k].([]int32); ok {
   128  		if len(i) != 3 {
   129  			return cube.Pos{}
   130  		}
   131  		return cube.Pos{int(i[0]), int(i[1]), int(i[2])}
   132  	}
   133  	return cube.Pos{}
   134  }
   135  
   136  // PosToInt32Slice converts a cube.Pos to a []int32 with 3 elements.
   137  func PosToInt32Slice(x cube.Pos) []int32 {
   138  	return []int32{int32(x[0]), int32(x[1]), int32(x[2])}
   139  }
   140  
   141  // MapItem converts an item's name, count, damage (and properties when it is a block) in a map obtained by decoding NBT
   142  // to a world.Item.
   143  func MapItem(x map[string]any, k string) item.Stack {
   144  	if m, ok := x[k].(map[string]any); ok {
   145  		tag, ok := m["tag"].(map[string]any)
   146  		if !ok {
   147  			tag = map[string]any{}
   148  		}
   149  
   150  		s := readItemStack(m, tag)
   151  		readDamage(tag, &s, true)
   152  		readEnchantments(tag, &s)
   153  		readDisplay(tag, &s)
   154  		readDragonflyData(tag, &s)
   155  		return s
   156  	}
   157  	return item.Stack{}
   158  }
   159  
   160  // Item decodes the data of an item into an item stack.
   161  func Item(data map[string]any, s *item.Stack) item.Stack {
   162  	disk, tag := s == nil, data
   163  	if disk {
   164  		t, ok := data["tag"].(map[string]any)
   165  		if !ok {
   166  			t = map[string]any{}
   167  		}
   168  		tag = t
   169  
   170  		a := readItemStack(data, tag)
   171  		s = &a
   172  	}
   173  
   174  	readAnvilCost(tag, s)
   175  	readDamage(tag, s, disk)
   176  	readDisplay(tag, s)
   177  	readDragonflyData(tag, s)
   178  	readEnchantments(tag, s)
   179  	return *s
   180  }
   181  
   182  // Block decodes the data of a block into a world.Block.
   183  func Block(m map[string]any, k string) world.Block {
   184  	if mk, ok := m[k].(map[string]any); ok {
   185  		name, _ := mk["name"].(string)
   186  		properties, _ := mk["states"].(map[string]any)
   187  		b, _ := world.BlockByName(name, properties)
   188  		return b
   189  	}
   190  	return nil
   191  }
   192  
   193  // readItemStack reads an item.Stack from the NBT in the map passed.
   194  func readItemStack(m, t map[string]any) item.Stack {
   195  	var it world.Item
   196  	if blockItem, ok := Block(m, "Block").(world.Item); ok {
   197  		it = blockItem
   198  	}
   199  	if v, ok := world.ItemByName(String(m, "Name"), Int16(m, "Damage")); ok {
   200  		it = v
   201  	}
   202  	if it == nil {
   203  		return item.Stack{}
   204  	}
   205  	if n, ok := it.(world.NBTer); ok {
   206  		it = n.DecodeNBT(t).(world.Item)
   207  	}
   208  	return item.NewStack(it, int(Uint8(m, "Count")))
   209  }
   210  
   211  // readDamage reads the damage value stored in the NBT with the Damage tag and saves it to the item.Stack passed.
   212  func readDamage(m map[string]any, s *item.Stack, disk bool) {
   213  	if disk {
   214  		*s = s.Damage(int(Int16(m, "Damage")))
   215  		return
   216  	}
   217  	*s = s.Damage(int(Int32(m, "Damage")))
   218  }
   219  
   220  // readAnvilCost ...
   221  func readAnvilCost(m map[string]any, s *item.Stack) {
   222  	*s = s.WithAnvilCost(int(Int32(m, "RepairCost")))
   223  }
   224  
   225  // readEnchantments reads the enchantments stored in the ench tag of the NBT passed and stores it into an item.Stack.
   226  func readEnchantments(m map[string]any, s *item.Stack) {
   227  	enchantments, ok := m["ench"].([]map[string]any)
   228  	if !ok {
   229  		for _, e := range Slice(m, "ench") {
   230  			if v, ok := e.(map[string]any); ok {
   231  				enchantments = append(enchantments, v)
   232  			}
   233  		}
   234  	}
   235  	for _, ench := range enchantments {
   236  		if t, ok := item.EnchantmentByID(int(Int16(ench, "id"))); ok {
   237  			*s = s.WithEnchantments(item.NewEnchantment(t, int(Int16(ench, "lvl"))))
   238  		}
   239  	}
   240  }
   241  
   242  // readDisplay reads the display data present in the display field in the NBT. It includes a custom name of the item
   243  // and the lore.
   244  func readDisplay(m map[string]any, s *item.Stack) {
   245  	if display, ok := m["display"].(map[string]any); ok {
   246  		if name, ok := display["Name"].(string); ok {
   247  			// Only add the custom name if actually set.
   248  			*s = s.WithCustomName(name)
   249  		}
   250  		if lore, ok := display["Lore"].([]string); ok {
   251  			*s = s.WithLore(lore...)
   252  		} else if lore, ok := display["Lore"].([]any); ok {
   253  			loreLines := make([]string, 0, len(lore))
   254  			for _, l := range lore {
   255  				loreLines = append(loreLines, l.(string))
   256  			}
   257  			*s = s.WithLore(loreLines...)
   258  		}
   259  	}
   260  }
   261  
   262  // readDragonflyData reads data written to the dragonflyData field in the NBT of an item and adds it to the item.Stack
   263  // passed.
   264  func readDragonflyData(m map[string]any, s *item.Stack) {
   265  	if customData, ok := m["dragonflyData"]; ok {
   266  		d, ok := customData.([]byte)
   267  		if !ok {
   268  			if itf, ok := customData.([]any); ok {
   269  				for _, v := range itf {
   270  					b, _ := v.(byte)
   271  					d = append(d, b)
   272  				}
   273  			}
   274  		}
   275  		var values []mapValue
   276  		if err := gob.NewDecoder(bytes.NewBuffer(d)).Decode(&values); err != nil {
   277  			panic("error decoding item user data: " + err.Error())
   278  		}
   279  		for _, val := range values {
   280  			*s = s.WithValue(val.K, val.V)
   281  		}
   282  	}
   283  }