github.com/df-mc/dragonfly@v0.9.13/server/world/item.go (about)

     1  package world
     2  
     3  import (
     4  	_ "embed"
     5  	"fmt"
     6  	"github.com/df-mc/dragonfly/server/item/category"
     7  	"github.com/sandertv/gophertunnel/minecraft/nbt"
     8  	"image"
     9  )
    10  
    11  // Item represents an item that may be added to an inventory. It has a method to encode the item to an ID and
    12  // a metadata value.
    13  type Item interface {
    14  	// EncodeItem encodes the item to its Minecraft representation, which consists of a numerical ID and a
    15  	// metadata value.
    16  	EncodeItem() (name string, meta int16)
    17  }
    18  
    19  // CustomItem represents an item that is non-vanilla and requires a resource pack and extra steps to show it
    20  // to the client.
    21  type CustomItem interface {
    22  	Item
    23  	// Name is the name that will be displayed on the item to all clients.
    24  	Name() string
    25  	// Texture is the Image of the texture for this item.
    26  	Texture() image.Image
    27  	// Category is the category the item will be listed under in the creative inventory.
    28  	Category() category.Category
    29  }
    30  
    31  // RegisterItem registers an item with the ID and meta passed. Once registered, items may be obtained from an
    32  // ID and metadata value using itemByID().
    33  // If an item with the ID and meta passed already exists, RegisterItem panics.
    34  func RegisterItem(item Item) {
    35  	name, meta := item.EncodeItem()
    36  	h := itemHash{name: name, meta: meta}
    37  
    38  	if _, ok := items[h]; ok {
    39  		panic(fmt.Sprintf("item registered with name %v and meta %v already exists", name, meta))
    40  	}
    41  	if c, ok := item.(CustomItem); ok {
    42  		nextRID := int32(len(itemNamesToRuntimeIDs))
    43  		itemRuntimeIDsToNames[nextRID] = name
    44  		itemNamesToRuntimeIDs[name] = nextRID
    45  
    46  		customItems = append(customItems, c)
    47  	}
    48  	if _, ok := itemNamesToRuntimeIDs[name]; !ok {
    49  		panic(fmt.Sprintf("item name %v does not have a runtime ID", name))
    50  	}
    51  	items[h] = item
    52  }
    53  
    54  // itemHash is a combination of an item's name and metadata. It is used as a key in hash maps.
    55  type itemHash struct {
    56  	name string
    57  	meta int16
    58  }
    59  
    60  var (
    61  	//go:embed item_runtime_ids.nbt
    62  	itemRuntimeIDData []byte
    63  	// items holds a list of all registered items, indexed using the itemHash created when calling
    64  	// Item.EncodeItem.
    65  	items = map[itemHash]Item{}
    66  	// customItems holds a list of all registered custom items.
    67  	customItems []CustomItem
    68  	// itemRuntimeIDsToNames holds a map to translate item runtime IDs to string IDs.
    69  	itemRuntimeIDsToNames = map[int32]string{}
    70  	// itemNamesToRuntimeIDs holds a map to translate item string IDs to runtime IDs.
    71  	itemNamesToRuntimeIDs = map[string]int32{}
    72  )
    73  
    74  // init reads all item entries from the resource JSON, and sets the according values in the runtime ID maps.
    75  func init() {
    76  	var m map[string]int32
    77  	err := nbt.Unmarshal(itemRuntimeIDData, &m)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  	for name, rid := range m {
    82  		itemNamesToRuntimeIDs[name] = rid
    83  		itemRuntimeIDsToNames[rid] = name
    84  	}
    85  }
    86  
    87  // ItemByName attempts to return an item by a name and a metadata value.
    88  func ItemByName(name string, meta int16) (Item, bool) {
    89  	it, ok := items[itemHash{name: name, meta: meta}]
    90  	if !ok {
    91  		// Also try obtaining the item with a metadata value of 0, for cases with durability.
    92  		it, ok = items[itemHash{name: name}]
    93  	}
    94  	return it, ok
    95  }
    96  
    97  // ItemRuntimeID attempts to return the runtime ID of the Item passed. False is returned if the Item is not
    98  // registered.
    99  func ItemRuntimeID(i Item) (rid int32, meta int16, ok bool) {
   100  	name, meta := i.EncodeItem()
   101  	rid, ok = itemNamesToRuntimeIDs[name]
   102  	return rid, meta, ok
   103  }
   104  
   105  // ItemByRuntimeID attempts to return an Item by the runtime ID passed. If no item with that runtime ID exists,
   106  // false is returned. ItemByRuntimeID also tries to find the item with a metadata value of 0.
   107  func ItemByRuntimeID(rid int32, meta int16) (Item, bool) {
   108  	name, ok := itemRuntimeIDsToNames[rid]
   109  	if !ok {
   110  		return nil, false
   111  	}
   112  	return ItemByName(name, meta)
   113  }
   114  
   115  // Items returns a slice of all registered items.
   116  func Items() []Item {
   117  	m := make([]Item, 0, len(items))
   118  	for _, i := range items {
   119  		m = append(m, i)
   120  	}
   121  	return m
   122  }
   123  
   124  // CustomItems returns a slice of all registered custom items.
   125  func CustomItems() []CustomItem {
   126  	return customItems
   127  }