@@ -2,6 +2,7 @@ package creative
2
2
3
3
import (
4
4
_ "embed"
5
+ "fmt"
5
6
"github.com/df-mc/dragonfly/server/internal/nbtconv"
6
7
// The following four imports are essential for this package: They make sure this package is loaded after
7
8
// all these imports. This ensures that all blocks and items are registered before the creative items are
@@ -12,90 +13,160 @@ import (
12
13
"github.com/sandertv/gophertunnel/minecraft/nbt"
13
14
)
14
15
16
+ // Item represents a registered item in the creative inventory. It holds a stack of the item and a group that
17
+ // the item is part of.
18
+ type Item struct {
19
+ // Stack is the stack of the item that is registered in the creative inventory.
20
+ Stack item.Stack
21
+ // Group is the name of the group that the item is part of. If two groups are registered with the same
22
+ // name, the item will always reside in the first group that was registered.
23
+ Group string
24
+ }
25
+
26
+ // Group represents a group of items in the creative inventory. Each group has a category, a name and an icon.
27
+ // If either the name or icon is empty, the group is considered an 'anonymous group' and will not group its
28
+ // contents together in the creative inventory.
29
+ type Group struct {
30
+ // Category is the category of the group. It determines the tab in which the group will be displayed in the
31
+ // creative inventory.
32
+ Category Category
33
+ // Name is the localised name of the group, i.e. "itemGroup.name.planks".
34
+ Name string
35
+ // Icon is the item that will be displayed as the icon of the group in the creative inventory.
36
+ Icon item.Stack
37
+ }
38
+
39
+ // Groups returns a list with all groups that have been registered as a creative group. These groups will be
40
+ // accessible by players in-game who have creative mode enabled.
41
+ func Groups () []Group {
42
+ return creativeGroups
43
+ }
44
+
45
+ // RegisterGroup registers a group as a creative group, exposing it in the creative inventory. It can then
46
+ // be referenced using its name when calling RegisterItem.
47
+ func RegisterGroup (group Group ) {
48
+ creativeGroups = append (creativeGroups , group )
49
+ }
50
+
15
51
// Items returns a list with all items that have been registered as a creative item. These items will
16
52
// be accessible by players in-game who have creative mode enabled.
17
- func Items () []item. Stack {
53
+ func Items () []Item {
18
54
return creativeItemStacks
19
55
}
20
56
21
57
// RegisterItem registers an item as a creative item, exposing it in the creative inventory.
22
- func RegisterItem (item item. Stack ) {
58
+ func RegisterItem (item Item ) {
23
59
creativeItemStacks = append (creativeItemStacks , item )
24
60
}
25
61
26
62
var (
27
63
//go:embed creative_items.nbt
28
64
creativeItemData []byte
65
+
66
+ // creativeGroups holds a list of all groups that were registered to the creative inventory using
67
+ // RegisterGroup.
68
+ creativeGroups []Group
29
69
// creativeItemStacks holds a list of all item stacks that were registered to the creative inventory using
30
70
// RegisterItem.
31
- creativeItemStacks []item. Stack
71
+ creativeItemStacks []Item
32
72
)
33
73
74
+ // creativeGroupEntry holds data of a creative group as present in the creative inventory.
75
+ type creativeGroupEntry struct {
76
+ Category int32 `nbt:"category"`
77
+ Name string `nbt:"name"`
78
+ Icon creativeItemEntry `nbt:"icon"`
79
+ }
80
+
34
81
// creativeItemEntry holds data of a creative item as present in the creative inventory.
35
82
type creativeItemEntry struct {
36
83
Name string `nbt:"name"`
37
84
Meta int16 `nbt:"meta"`
38
85
NBT map [string ]any `nbt:"nbt,omitempty"`
39
86
BlockProperties map [string ]any `nbt:"block_properties,omitempty"`
87
+ GroupIndex int32 `nbt:"group_index,omitempty"`
40
88
}
41
89
42
90
// init initialises the creative items, registering all creative items that have also been registered as
43
91
// normal items and are present in vanilla.
44
92
func init () {
45
- var m []creativeItemEntry
93
+ var m struct {
94
+ Groups []creativeGroupEntry `nbt:"groups"`
95
+ Items []creativeItemEntry `nbt:"items"`
96
+ }
46
97
if err := nbt .Unmarshal (creativeItemData , & m ); err != nil {
47
98
panic (err )
48
99
}
49
- for _ , data := range m {
50
- var (
51
- it world.Item
52
- ok bool
53
- )
54
- if len (data .BlockProperties ) > 0 {
55
- // Item with a block, try parsing the block, then try asserting that to an item. Blocks no longer
56
- // have their metadata sent, but we still need to get that metadata in order to be able to register
57
- // different block states as different items.
58
- if b , ok := world .BlockByName (data .Name , data .BlockProperties ); ok {
59
- if it , ok = b .(world.Item ); ! ok {
60
- continue
61
- }
62
- }
63
- } else {
64
- if it , ok = world .ItemByName (data .Name , data .Meta ); ! ok {
65
- // The item wasn't registered, so don't register it as a creative item.
66
- continue
67
- }
68
- if _ , resultingMeta := it .EncodeItem (); resultingMeta != data .Meta {
69
- // We found an item registered with that ID and a meta of 0, but we only need items with strictly
70
- // the same meta here.
71
- continue
72
- }
100
+ for i , group := range m .Groups {
101
+ name := group .Name
102
+ if name == "" {
103
+ name = fmt .Sprint ("anon" , i )
104
+ }
105
+ st , _ := itemStackFromEntry (group .Icon )
106
+ c := Category {category (group .Category )}
107
+ RegisterGroup (Group {Category : c , Name : name , Icon : st })
108
+ }
109
+ for _ , data := range m .Items {
110
+ if data .GroupIndex >= int32 (len (creativeGroups )) {
111
+ panic (fmt .Errorf ("invalid group index %v for item %v" , data .GroupIndex , data .Name ))
112
+ }
113
+ st , ok := itemStackFromEntry (data )
114
+ if ! ok {
115
+ continue
73
116
}
117
+ RegisterItem (Item {st , creativeGroups [data .GroupIndex ].Name })
118
+ }
119
+ }
74
120
75
- if n , ok := it .(world.NBTer ); ok {
76
- if len (data .NBT ) > 0 {
77
- it = n .DecodeNBT (data .NBT ).(world.Item )
121
+ func itemStackFromEntry (data creativeItemEntry ) (item.Stack , bool ) {
122
+ var (
123
+ it world.Item
124
+ ok bool
125
+ )
126
+ if len (data .BlockProperties ) > 0 {
127
+ // Item with a block, try parsing the block, then try asserting that to an item. Blocks no longer
128
+ // have their metadata sent, but we still need to get that metadata in order to be able to register
129
+ // different block states as different items.
130
+ if b , ok := world .BlockByName (data .Name , data .BlockProperties ); ok {
131
+ if it , ok = b .(world.Item ); ! ok {
132
+ return item.Stack {}, false
78
133
}
79
134
}
135
+ } else {
136
+ if it , ok = world .ItemByName (data .Name , data .Meta ); ! ok {
137
+ // The item wasn't registered, so don't register it as a creative item.
138
+ return item.Stack {}, false
139
+ }
140
+ if _ , resultingMeta := it .EncodeItem (); resultingMeta != data .Meta {
141
+ // We found an item registered with that ID and a meta of 0, but we only need items with strictly
142
+ // the same meta here.
143
+ return item.Stack {}, false
144
+ }
145
+ }
80
146
81
- st := item . NewStack ( it , 1 )
147
+ if n , ok := it .(world. NBTer ); ok {
82
148
if len (data .NBT ) > 0 {
83
- var invalid bool
84
- for _ , e := range nbtconv .Slice (data .NBT , "ench" ) {
85
- if v , ok := e .(map [string ]any ); ok {
86
- t , ok := item .EnchantmentByID (int (nbtconv .Int16 (v , "id" )))
87
- if ! ok {
88
- invalid = true
89
- break
90
- }
91
- st = st .WithEnchantments (item .NewEnchantment (t , int (nbtconv .Int16 (v , "lvl" ))))
149
+ it = n .DecodeNBT (data .NBT ).(world.Item )
150
+ }
151
+ }
152
+
153
+ st := item .NewStack (it , 1 )
154
+ if len (data .NBT ) > 0 {
155
+ var invalid bool
156
+ for _ , e := range nbtconv .Slice (data .NBT , "ench" ) {
157
+ if v , ok := e .(map [string ]any ); ok {
158
+ t , ok := item .EnchantmentByID (int (nbtconv .Int16 (v , "id" )))
159
+ if ! ok {
160
+ invalid = true
161
+ break
92
162
}
163
+ st = st .WithEnchantments (item .NewEnchantment (t , int (nbtconv .Int16 (v , "lvl" ))))
93
164
}
94
- if invalid {
95
- // Invalid enchantment, skip this item.
96
- continue
97
- }
98
165
}
99
- RegisterItem (st )
166
+ if invalid {
167
+ // Invalid enchantment, skip this item.
168
+ return item.Stack {}, false
169
+ }
100
170
}
171
+ return st , true
101
172
}
0 commit comments