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

     1  package block
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/df-mc/dragonfly/server/block/cube"
     6  	"github.com/df-mc/dragonfly/server/internal/nbtconv"
     7  	"github.com/df-mc/dragonfly/server/item"
     8  	"github.com/df-mc/dragonfly/server/item/inventory"
     9  	"github.com/df-mc/dragonfly/server/world"
    10  	"github.com/df-mc/dragonfly/server/world/sound"
    11  	"github.com/go-gl/mathgl/mgl64"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  )
    16  
    17  // Barrel is a fisherman's job site block, used to store items. It functions like a single chest, although
    18  // it requires no airspace above it to be opened.
    19  type Barrel struct {
    20  	solid
    21  	bass
    22  
    23  	// Facing is the direction that the barrel is facing.
    24  	Facing cube.Face
    25  	// Open is whether the barrel is open or not.
    26  	Open bool
    27  	// CustomName is the custom name of the barrel. This name is displayed when the barrel is opened, and may
    28  	// include colour codes.
    29  	CustomName string
    30  
    31  	inventory *inventory.Inventory
    32  	viewerMu  *sync.RWMutex
    33  	viewers   map[ContainerViewer]struct{}
    34  }
    35  
    36  // NewBarrel creates a new initialised barrel. The inventory is properly initialised.
    37  func NewBarrel() Barrel {
    38  	m := new(sync.RWMutex)
    39  	v := make(map[ContainerViewer]struct{}, 1)
    40  	return Barrel{
    41  		inventory: inventory.New(27, func(slot int, _, item item.Stack) {
    42  			m.RLock()
    43  			defer m.RUnlock()
    44  			for viewer := range v {
    45  				viewer.ViewSlotChange(slot, item)
    46  			}
    47  		}),
    48  		viewerMu: m,
    49  		viewers:  v,
    50  	}
    51  }
    52  
    53  // Inventory returns the inventory of the barrel. The size of the inventory will be 27.
    54  func (b Barrel) Inventory() *inventory.Inventory {
    55  	return b.inventory
    56  }
    57  
    58  // WithName returns the barrel after applying a specific name to the block.
    59  func (b Barrel) WithName(a ...any) world.Item {
    60  	b.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n")
    61  	return b
    62  }
    63  
    64  // open opens the barrel, displaying the animation and playing a sound.
    65  func (b Barrel) open(w *world.World, pos cube.Pos) {
    66  	b.Open = true
    67  	w.PlaySound(pos.Vec3Centre(), sound.BarrelOpen{})
    68  	w.SetBlock(pos, b, nil)
    69  }
    70  
    71  // close closes the barrel, displaying the animation and playing a sound.
    72  func (b Barrel) close(w *world.World, pos cube.Pos) {
    73  	b.Open = false
    74  	w.PlaySound(pos.Vec3Centre(), sound.BarrelClose{})
    75  	w.SetBlock(pos, b, nil)
    76  }
    77  
    78  // AddViewer adds a viewer to the barrel, so that it is updated whenever the inventory of the barrel is changed.
    79  func (b Barrel) AddViewer(v ContainerViewer, w *world.World, pos cube.Pos) {
    80  	b.viewerMu.Lock()
    81  	defer b.viewerMu.Unlock()
    82  	if len(b.viewers) == 0 {
    83  		b.open(w, pos)
    84  	}
    85  	b.viewers[v] = struct{}{}
    86  }
    87  
    88  // RemoveViewer removes a viewer from the barrel, so that slot updates in the inventory are no longer sent to
    89  // it.
    90  func (b Barrel) RemoveViewer(v ContainerViewer, w *world.World, pos cube.Pos) {
    91  	b.viewerMu.Lock()
    92  	defer b.viewerMu.Unlock()
    93  	if len(b.viewers) == 0 {
    94  		return
    95  	}
    96  	delete(b.viewers, v)
    97  	if len(b.viewers) == 0 {
    98  		b.close(w, pos)
    99  	}
   100  }
   101  
   102  // Activate ...
   103  func (b Barrel) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool {
   104  	if opener, ok := u.(ContainerOpener); ok {
   105  		opener.OpenBlockContainer(pos)
   106  		return true
   107  	}
   108  	return false
   109  }
   110  
   111  // UseOnBlock ...
   112  func (b Barrel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) {
   113  	pos, _, used = firstReplaceable(w, pos, face, b)
   114  	if !used {
   115  		return
   116  	}
   117  	//noinspection GoAssignmentToReceiver
   118  	b = NewBarrel()
   119  	b.Facing = calculateFace(user, pos)
   120  
   121  	place(w, pos, b, user, ctx)
   122  	return placed(ctx)
   123  }
   124  
   125  // BreakInfo ...
   126  func (b Barrel) BreakInfo() BreakInfo {
   127  	return newBreakInfo(2.5, alwaysHarvestable, axeEffective, oneOf(b))
   128  }
   129  
   130  // FlammabilityInfo ...
   131  func (b Barrel) FlammabilityInfo() FlammabilityInfo {
   132  	return newFlammabilityInfo(0, 0, true)
   133  }
   134  
   135  // FuelInfo ...
   136  func (Barrel) FuelInfo() item.FuelInfo {
   137  	return newFuelInfo(time.Second * 15)
   138  }
   139  
   140  // DecodeNBT ...
   141  func (b Barrel) DecodeNBT(data map[string]any) any {
   142  	facing := b.Facing
   143  	//noinspection GoAssignmentToReceiver
   144  	b = NewBarrel()
   145  	b.Facing = facing
   146  	b.CustomName = nbtconv.String(data, "CustomName")
   147  	nbtconv.InvFromNBT(b.inventory, nbtconv.Slice(data, "Items"))
   148  	return b
   149  }
   150  
   151  // EncodeNBT ...
   152  func (b Barrel) EncodeNBT() map[string]any {
   153  	if b.inventory == nil {
   154  		facing, customName := b.Facing, b.CustomName
   155  		//noinspection GoAssignmentToReceiver
   156  		b = NewBarrel()
   157  		b.Facing, b.CustomName = facing, customName
   158  	}
   159  	m := map[string]any{
   160  		"Items": nbtconv.InvToNBT(b.inventory),
   161  		"id":    "Barrel",
   162  	}
   163  	if b.CustomName != "" {
   164  		m["CustomName"] = b.CustomName
   165  	}
   166  	return m
   167  }
   168  
   169  // EncodeBlock ...
   170  func (b Barrel) EncodeBlock() (string, map[string]any) {
   171  	return "minecraft:barrel", map[string]any{"open_bit": boolByte(b.Open), "facing_direction": int32(b.Facing)}
   172  }
   173  
   174  // EncodeItem ...
   175  func (b Barrel) EncodeItem() (name string, meta int16) {
   176  	return "minecraft:barrel", 0
   177  }
   178  
   179  // allBarrels ...
   180  func allBarrels() (b []world.Block) {
   181  	for i := cube.Face(0); i < 6; i++ {
   182  		b = append(b, Barrel{Facing: i})
   183  		b = append(b, Barrel{Facing: i, Open: true})
   184  	}
   185  	return
   186  }