-
Notifications
You must be signed in to change notification settings - Fork 0
/
write.go
148 lines (135 loc) · 3.92 KB
/
write.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package nbtconv
import (
"bytes"
"encoding/gob"
"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 {
tag := make(map[string]any)
if nbt, ok := s.Item().(world.NBTer); ok {
for k, v := range nbt.EncodeNBT() {
tag[k] = v
}
}
writeAnvilCost(tag, s)
writeDamage(tag, s, disk)
writeDisplay(tag, s)
writeDragonflyData(tag, s)
writeEnchantments(tag, s)
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.
func WriteBlock(b world.Block) map[string]any {
name, properties := b.EncodeBlock()
return map[string]any{
"name": name,
"states": properties,
"version": chunk.CurrentBlockVersion,
}
}
// writeItemStack writes the name, metadata value, count and NBT of an item to a map ready for NBT encoding.
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{}
writeBlock(v, b)
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.
func writeBlock(m map[string]any, b world.Block) {
m["name"], m["states"] = b.EncodeBlock()
m["version"] = chunk.CurrentBlockVersion
}
// writeDragonflyData writes additional data associated with an item.Stack to a map for NBT encoding.
func writeDragonflyData(m map[string]any, s item.Stack) {
if v := s.Values(); len(v) != 0 {
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(mapToSlice(v)); err != nil {
panic("error encoding item user data: " + err.Error())
}
m["dragonflyData"] = buf.Bytes()
}
}
// mapToSlice converts a map to a slice of the type mapValue and orders the slice by the keys in the map to ensure a
// deterministic order.
func mapToSlice(m map[string]any) []mapValue {
values := make([]mapValue, 0, len(m))
for k, v := range m {
values = append(values, mapValue{K: k, V: v})
}
sort.Slice(values, func(i, j int) bool {
return values[i].K < values[j].K
})
return values
}
// mapValue represents a value in a map. It is used to convert maps to a slice and order the slice before encoding to
// NBT to ensure a deterministic output.
type mapValue struct {
K string
V any
}
// writeEnchantments writes the enchantments of an item to a map for NBT encoding.
func writeEnchantments(m map[string]any, s item.Stack) {
if len(s.Enchantments()) != 0 {
var enchantments []map[string]any
for _, e := range s.Enchantments() {
if eType, ok := item.EnchantmentID(e.Type()); ok {
enchantments = append(enchantments, map[string]any{
"id": int16(eType),
"lvl": int16(e.Level()),
})
}
}
m["ench"] = enchantments
}
}
// writeDisplay writes the display name and lore of an item to a map for NBT encoding.
func writeDisplay(m map[string]any, s item.Stack) {
name, lore := s.CustomName(), s.Lore()
v := map[string]any{}
if name != "" {
v["Name"] = name
}
if len(lore) != 0 {
v["Lore"] = lore
}
if len(v) != 0 {
m["display"] = v
}
}
// writeDamage writes the damage to an item.Stack (either an int16 for disk or int32 for network) to a map for NBT
// encoding.
func writeDamage(m map[string]any, s item.Stack, disk bool) {
if v, ok := m["Damage"]; !ok || v.(int16) == 0 {
if _, ok := s.Item().(item.Durable); ok {
if disk {
m["Damage"] = int16(s.MaxDurability() - s.Durability())
} else {
m["Damage"] = int32(s.MaxDurability() - s.Durability())
}
}
}
}
// writeAnvilCost ...
func writeAnvilCost(m map[string]any, s item.Stack) {
if cost := s.AnvilCost(); cost > 0 {
m["RepairCost"] = int32(cost)
}
}