From e12871a0469f65cda24b115b65369f13718a74c9 Mon Sep 17 00:00:00 2001 From: FDUTCH Date: Thu, 13 Feb 2025 17:10:43 +0100 Subject: [PATCH 1/3] creative items changes --- server/conf.go | 4 ++++ server/item/creative/creative.go | 34 +++++++++++++----------------- server/server.go | 1 + server/session/handler_crafting.go | 5 ++--- server/session/player.go | 6 +++--- server/session/session.go | 5 ++++- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/server/conf.go b/server/conf.go index f0e1c027d..542af48b6 100644 --- a/server/conf.go +++ b/server/conf.go @@ -5,6 +5,7 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/internal/packbuilder" + "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/player/playerdb" @@ -102,6 +103,8 @@ type Config struct { // may be added to the Server's worlds. If no entity types are registered, // Entities will be set to entity.DefaultRegistry. Entities world.EntityRegistry + // CreativeItems is an items that would be shown in creative menu of players. + CreativeItems []creative.Item } // New creates a Server using fields of conf. The Server's worlds are created @@ -251,6 +254,7 @@ func (uc UserConfig) Config(log *slog.Logger) (Config, error) { MaxPlayers: uc.Players.MaxCount, MaxChunkRadius: uc.Players.MaximumChunkRadius, DisableResourceBuilding: !uc.Resources.AutoBuildPack, + CreativeItems: creative.Items(), } if !uc.Server.DisableJoinQuitMessages { conf.JoinMessage, conf.QuitMessage = chat.MessageJoin, chat.MessageQuit diff --git a/server/item/creative/creative.go b/server/item/creative/creative.go index 847990b14..410ec045d 100644 --- a/server/item/creative/creative.go +++ b/server/item/creative/creative.go @@ -50,13 +50,19 @@ func RegisterGroup(group Group) { // Items returns a list with all items that have been registered as a creative item. These items will // be accessible by players in-game who have creative mode enabled. -func Items() []Item { - return creativeItemStacks -} +func Items() (out []Item) { + for _, data := range items { + if data.GroupIndex >= int32(len(creativeGroups)) { + panic(fmt.Errorf("invalid group index %v for item %v", data.GroupIndex, data.Name)) + } + st, ok := itemStackFromEntry(data) + if !ok { + continue + } + out = append(out, Item{st, creativeGroups[data.GroupIndex].Name}) + } -// RegisterItem registers an item as a creative item, exposing it in the creative inventory. -func RegisterItem(item Item) { - creativeItemStacks = append(creativeItemStacks, item) + return out } var ( @@ -66,9 +72,8 @@ var ( // creativeGroups holds a list of all groups that were registered to the creative inventory using // RegisterGroup. creativeGroups []Group - // creativeItemStacks holds a list of all item stacks that were registered to the creative inventory using - // RegisterItem. - creativeItemStacks []Item + // items holds a list of all item stacks that were registered to the creative inventory. + items []creativeItemEntry ) // creativeGroupEntry holds data of a creative group as present in the creative inventory. @@ -97,6 +102,7 @@ func init() { if err := nbt.Unmarshal(creativeItemData, &m); err != nil { panic(err) } + items = m.Items for i, group := range m.Groups { name := group.Name if name == "" { @@ -106,16 +112,6 @@ func init() { c := Category{category(group.Category)} RegisterGroup(Group{Category: c, Name: name, Icon: st}) } - for _, data := range m.Items { - if data.GroupIndex >= int32(len(creativeGroups)) { - panic(fmt.Errorf("invalid group index %v for item %v", data.GroupIndex, data.Name)) - } - st, ok := itemStackFromEntry(data) - if !ok { - continue - } - RegisterItem(Item{st, creativeGroups[data.GroupIndex].Name}) - } } func itemStackFromEntry(data creativeItemEntry) (item.Stack, bool) { diff --git a/server/server.go b/server/server.go index 28d20bec8..3dd015a39 100644 --- a/server/server.go +++ b/server/server.go @@ -539,6 +539,7 @@ func (srv *Server) createPlayer(id uuid.UUID, conn session.Conn, conf player.Con MaxChunkRadius: srv.conf.MaxChunkRadius, JoinMessage: srv.conf.JoinMessage, QuitMessage: srv.conf.QuitMessage, + CreativeItems: srv.conf.CreativeItems, HandleStop: srv.handleSessionClose, }.New(conn) diff --git a/server/session/handler_crafting.go b/server/session/handler_crafting.go index 606bea3ef..68b86c3ca 100644 --- a/server/session/handler_crafting.go +++ b/server/session/handler_crafting.go @@ -3,7 +3,6 @@ package session import ( "fmt" "github.com/df-mc/dragonfly/server/item" - "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/item/recipe" "github.com/df-mc/dragonfly/server/world" @@ -160,10 +159,10 @@ func (h *ItemStackRequestHandler) handleCreativeCraft(a *protocol.CraftCreativeS return fmt.Errorf("can only craft creative items in gamemode creative/spectator") } index := a.CreativeItemNetworkID - 1 - if int(index) >= len(creative.Items()) { + if int(index) >= len(s.conf.CreativeItems) { return fmt.Errorf("creative item with network ID %v does not exist", index) } - it := creative.Items()[index].Stack + it := s.conf.CreativeItems[index].Stack it = it.Grow(it.MaxCount() - 1) return h.createResults(s, tx, it) } diff --git a/server/session/player.go b/server/session/player.go index 09e276a21..48326bf8c 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -834,7 +834,7 @@ func stacksToIngredientItems(inputs []recipe.Item) []protocol.ItemDescriptorCoun } // creativeContent returns all creative groups, and creative inventory items as protocol item stacks. -func creativeContent() ([]protocol.CreativeGroup, []protocol.CreativeItem) { +func creativeContent(creativeItems []creative.Item) ([]protocol.CreativeGroup, []protocol.CreativeItem) { groups := make([]protocol.CreativeGroup, 0, len(creative.Groups())) for _, group := range creative.Groups() { groups = append(groups, protocol.CreativeGroup{ @@ -844,8 +844,8 @@ func creativeContent() ([]protocol.CreativeGroup, []protocol.CreativeItem) { }) } - it := make([]protocol.CreativeItem, 0, len(creative.Items())) - for index, i := range creative.Items() { + it := make([]protocol.CreativeItem, 0, len(creativeItems)) + for index, i := range creativeItems { group := slices.IndexFunc(creative.Groups(), func(group creative.Group) bool { return group.Name == i.Group }) diff --git a/server/session/session.go b/server/session/session.go index 53a5a870e..4f27235a4 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/cmd" + "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/item/recipe" "github.com/df-mc/dragonfly/server/player/chat" @@ -133,6 +134,8 @@ type Config struct { JoinMessage, QuitMessage chat.Translation + CreativeItems []creative.Item + HandleStop func(*world.Tx, Controllable) } @@ -173,7 +176,7 @@ func (conf Config) New(conn Conn) *Session { s.currentLines.Store(&scoreboardLines) s.registerHandlers() - groups, items := creativeContent() + groups, items := creativeContent(conf.CreativeItems) s.writePacket(&packet.CreativeContent{Groups: groups, Items: items}) s.sendRecipes() s.sendArmourTrimData() From 18fddca396bca6e42e220bc420f82add1928ea41 Mon Sep 17 00:00:00 2001 From: FDUTCH Date: Wed, 26 Feb 2025 03:06:00 +0100 Subject: [PATCH 2/3] changes --- server/conf.go | 6 ++--- server/item/creative/creative.go | 39 +++++++++--------------------- server/server.go | 2 +- server/session/handler_crafting.go | 23 +++++++++++++++--- server/session/player.go | 32 +++++++++++------------- server/session/session.go | 4 +-- 6 files changed, 51 insertions(+), 55 deletions(-) diff --git a/server/conf.go b/server/conf.go index 542af48b6..b5f3b7e96 100644 --- a/server/conf.go +++ b/server/conf.go @@ -103,8 +103,8 @@ type Config struct { // may be added to the Server's worlds. If no entity types are registered, // Entities will be set to entity.DefaultRegistry. Entities world.EntityRegistry - // CreativeItems is an items that would be shown in creative menu of players. - CreativeItems []creative.Item + // CreativeGroups is an item groups that would be shown in creative menu of players. + CreativeGroups []creative.Group } // New creates a Server using fields of conf. The Server's worlds are created @@ -254,7 +254,7 @@ func (uc UserConfig) Config(log *slog.Logger) (Config, error) { MaxPlayers: uc.Players.MaxCount, MaxChunkRadius: uc.Players.MaximumChunkRadius, DisableResourceBuilding: !uc.Resources.AutoBuildPack, - CreativeItems: creative.Items(), + CreativeGroups: creative.Groups(), } if !uc.Server.DisableJoinQuitMessages { conf.JoinMessage, conf.QuitMessage = chat.MessageJoin, chat.MessageQuit diff --git a/server/item/creative/creative.go b/server/item/creative/creative.go index 410ec045d..35ec29b92 100644 --- a/server/item/creative/creative.go +++ b/server/item/creative/creative.go @@ -13,16 +13,6 @@ import ( "github.com/sandertv/gophertunnel/minecraft/nbt" ) -// Item represents a registered item in the creative inventory. It holds a stack of the item and a group that -// the item is part of. -type Item struct { - // Stack is the stack of the item that is registered in the creative inventory. - Stack item.Stack - // Group is the name of the group that the item is part of. If two groups are registered with the same - // name, the item will always reside in the first group that was registered. - Group string -} - // Group represents a group of items in the creative inventory. Each group has a category, a name and an icon. // If either the name or icon is empty, the group is considered an 'anonymous group' and will not group its // contents together in the creative inventory. @@ -34,11 +24,23 @@ type Group struct { Name string // Icon is the item that will be displayed as the icon of the group in the creative inventory. Icon item.Stack + // Items are all items that are that belong to this Group. + Items []item.Stack } // Groups returns a list with all groups that have been registered as a creative group. These groups will be // accessible by players in-game who have creative mode enabled. func Groups() []Group { + for _, it := range items { + if it.GroupIndex >= int32(len(creativeGroups)) { + panic(fmt.Errorf("invalid group index %v for item %v", it.GroupIndex, it.Name)) + } + st, ok := itemStackFromEntry(it) + if !ok { + continue + } + creativeGroups[it.GroupIndex].Items = append(creativeGroups[it.GroupIndex].Items, st) + } return creativeGroups } @@ -48,23 +50,6 @@ func RegisterGroup(group Group) { creativeGroups = append(creativeGroups, group) } -// Items returns a list with all items that have been registered as a creative item. These items will -// be accessible by players in-game who have creative mode enabled. -func Items() (out []Item) { - for _, data := range items { - if data.GroupIndex >= int32(len(creativeGroups)) { - panic(fmt.Errorf("invalid group index %v for item %v", data.GroupIndex, data.Name)) - } - st, ok := itemStackFromEntry(data) - if !ok { - continue - } - out = append(out, Item{st, creativeGroups[data.GroupIndex].Name}) - } - - return out -} - var ( //go:embed creative_items.nbt creativeItemData []byte diff --git a/server/server.go b/server/server.go index 3dd015a39..ce3dea82f 100644 --- a/server/server.go +++ b/server/server.go @@ -539,7 +539,7 @@ func (srv *Server) createPlayer(id uuid.UUID, conn session.Conn, conf player.Con MaxChunkRadius: srv.conf.MaxChunkRadius, JoinMessage: srv.conf.JoinMessage, QuitMessage: srv.conf.QuitMessage, - CreativeItems: srv.conf.CreativeItems, + CreativeGroups: srv.conf.CreativeGroups, HandleStop: srv.handleSessionClose, }.New(conn) diff --git a/server/session/handler_crafting.go b/server/session/handler_crafting.go index 68b86c3ca..53382a22c 100644 --- a/server/session/handler_crafting.go +++ b/server/session/handler_crafting.go @@ -3,6 +3,7 @@ package session import ( "fmt" "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/item/recipe" "github.com/df-mc/dragonfly/server/world" @@ -158,15 +159,29 @@ func (h *ItemStackRequestHandler) handleCreativeCraft(a *protocol.CraftCreativeS if !c.GameMode().CreativeInventory() { return fmt.Errorf("can only craft creative items in gamemode creative/spectator") } - index := a.CreativeItemNetworkID - 1 - if int(index) >= len(s.conf.CreativeItems) { - return fmt.Errorf("creative item with network ID %v does not exist", index) + index := int(a.CreativeItemNetworkID - 1) + + it, err := itemByIndex(s.conf.CreativeGroups, index) + if err != nil { + return err } - it := s.conf.CreativeItems[index].Stack + it = it.Grow(it.MaxCount() - 1) return h.createResults(s, tx, it) } +// itemByIndex returns creative item by index. +func itemByIndex(groups []creative.Group, index int) (item.Stack, error) { + offset := 0 + for _, group := range groups { + if offset+len(group.Items) > index { + return group.Items[index-offset], nil + } + offset += len(group.Items) + } + return item.Stack{}, fmt.Errorf("creative item with network ID %v does not exist", index) +} + // craftingSize gets the crafting size based on the opened container ID. func (s *Session) craftingSize() uint32 { if s.openedContainerID.Load() == 1 { diff --git a/server/session/player.go b/server/session/player.go index 48326bf8c..acdb80997 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -20,7 +20,6 @@ import ( "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "math" "net" - "slices" "time" _ "unsafe" // Imported for compiler directives. ) @@ -834,30 +833,27 @@ func stacksToIngredientItems(inputs []recipe.Item) []protocol.ItemDescriptorCoun } // creativeContent returns all creative groups, and creative inventory items as protocol item stacks. -func creativeContent(creativeItems []creative.Item) ([]protocol.CreativeGroup, []protocol.CreativeItem) { - groups := make([]protocol.CreativeGroup, 0, len(creative.Groups())) - for _, group := range creative.Groups() { +func creativeContent(creativeGroups []creative.Group) ([]protocol.CreativeGroup, []protocol.CreativeItem) { + groups := make([]protocol.CreativeGroup, 0, len(creativeGroups)) + it := make([]protocol.CreativeItem, 0, len(creativeGroups)*10) + + var itemIdx uint32 + for index, group := range creativeGroups { groups = append(groups, protocol.CreativeGroup{ Category: int32(group.Category.Uint8()), Name: group.Name, Icon: deleteDamage(stackFromItem(group.Icon)), }) - } - - it := make([]protocol.CreativeItem, 0, len(creativeItems)) - for index, i := range creativeItems { - group := slices.IndexFunc(creative.Groups(), func(group creative.Group) bool { - return group.Name == i.Group - }) - if group < 0 { - continue + for _, stack := range group.Items { + itemIdx++ + it = append(it, protocol.CreativeItem{ + CreativeItemNetworkID: itemIdx, + Item: deleteDamage(stackFromItem(stack)), + GroupIndex: uint32(index), + }) } - it = append(it, protocol.CreativeItem{ - CreativeItemNetworkID: uint32(index) + 1, - Item: deleteDamage(stackFromItem(i.Stack)), - GroupIndex: uint32(group), - }) } + return groups, it } diff --git a/server/session/session.go b/server/session/session.go index 4f27235a4..8924fb23c 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -134,7 +134,7 @@ type Config struct { JoinMessage, QuitMessage chat.Translation - CreativeItems []creative.Item + CreativeGroups []creative.Group HandleStop func(*world.Tx, Controllable) } @@ -176,7 +176,7 @@ func (conf Config) New(conn Conn) *Session { s.currentLines.Store(&scoreboardLines) s.registerHandlers() - groups, items := creativeContent(conf.CreativeItems) + groups, items := creativeContent(conf.CreativeGroups) s.writePacket(&packet.CreativeContent{Groups: groups, Items: items}) s.sendRecipes() s.sendArmourTrimData() From 8d87c458b98f5b13999a4ee1de1a34635a1a7276 Mon Sep 17 00:00:00 2001 From: fdutch Date: Sun, 23 Mar 2025 22:24:04 +0000 Subject: [PATCH 3/3] fix register new items --- server/conf.go | 4 ---- server/item/creative/creative.go | 9 ++++++--- server/server.go | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/server/conf.go b/server/conf.go index b5f3b7e96..f0e1c027d 100644 --- a/server/conf.go +++ b/server/conf.go @@ -5,7 +5,6 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/internal/packbuilder" - "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/player/playerdb" @@ -103,8 +102,6 @@ type Config struct { // may be added to the Server's worlds. If no entity types are registered, // Entities will be set to entity.DefaultRegistry. Entities world.EntityRegistry - // CreativeGroups is an item groups that would be shown in creative menu of players. - CreativeGroups []creative.Group } // New creates a Server using fields of conf. The Server's worlds are created @@ -254,7 +251,6 @@ func (uc UserConfig) Config(log *slog.Logger) (Config, error) { MaxPlayers: uc.Players.MaxCount, MaxChunkRadius: uc.Players.MaximumChunkRadius, DisableResourceBuilding: !uc.Resources.AutoBuildPack, - CreativeGroups: creative.Groups(), } if !uc.Server.DisableJoinQuitMessages { conf.JoinMessage, conf.QuitMessage = chat.MessageJoin, chat.MessageQuit diff --git a/server/item/creative/creative.go b/server/item/creative/creative.go index 35ec29b92..60f775ee5 100644 --- a/server/item/creative/creative.go +++ b/server/item/creative/creative.go @@ -4,6 +4,8 @@ import ( _ "embed" "fmt" "github.com/df-mc/dragonfly/server/internal/nbtconv" + "slices" + // The following four imports are essential for this package: They make sure this package is loaded after // all these imports. This ensures that all blocks and items are registered before the creative items are // registered in the init function in this package. @@ -31,17 +33,18 @@ type Group struct { // Groups returns a list with all groups that have been registered as a creative group. These groups will be // accessible by players in-game who have creative mode enabled. func Groups() []Group { + groups := slices.Clone(creativeGroups) for _, it := range items { - if it.GroupIndex >= int32(len(creativeGroups)) { + if it.GroupIndex >= int32(len(groups)) { panic(fmt.Errorf("invalid group index %v for item %v", it.GroupIndex, it.Name)) } st, ok := itemStackFromEntry(it) if !ok { continue } - creativeGroups[it.GroupIndex].Items = append(creativeGroups[it.GroupIndex].Items, st) + groups[it.GroupIndex].Items = append(groups[it.GroupIndex].Items, st) } - return creativeGroups + return groups } // RegisterGroup registers a group as a creative group, exposing it in the creative inventory. It can then diff --git a/server/server.go b/server/server.go index ce3dea82f..6f25a2c52 100644 --- a/server/server.go +++ b/server/server.go @@ -10,6 +10,7 @@ import ( "github.com/df-mc/dragonfly/server/internal/iteminternal" "github.com/df-mc/dragonfly/server/internal/sliceutil" _ "github.com/df-mc/dragonfly/server/item" // Imported for maintaining correct initialisation order. + "github.com/df-mc/dragonfly/server/item/creative" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/player/skin" @@ -539,7 +540,7 @@ func (srv *Server) createPlayer(id uuid.UUID, conn session.Conn, conf player.Con MaxChunkRadius: srv.conf.MaxChunkRadius, JoinMessage: srv.conf.JoinMessage, QuitMessage: srv.conf.QuitMessage, - CreativeGroups: srv.conf.CreativeGroups, + CreativeGroups: creative.Groups(), HandleStop: srv.handleSessionClose, }.New(conn)