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

     1  package session
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/df-mc/dragonfly/server/block"
     6  	"github.com/df-mc/dragonfly/server/entity/effect"
     7  	"github.com/df-mc/dragonfly/server/item"
     8  	"github.com/sandertv/gophertunnel/minecraft/protocol"
     9  )
    10  
    11  // beaconInputSlot is the slot index of the input item in the beacon.
    12  const beaconInputSlot = 0x1b
    13  
    14  // handleBeaconPayment handles the selection of effects in a beacon and the removal of the item used to pay
    15  // for those effects.
    16  func (h *ItemStackRequestHandler) handleBeaconPayment(a *protocol.BeaconPaymentStackRequestAction, s *Session) error {
    17  	slot := protocol.StackRequestSlotInfo{
    18  		ContainerID: protocol.ContainerBeaconPayment,
    19  		Slot:        beaconInputSlot,
    20  	}
    21  	// First check if there actually is a beacon opened.
    22  	if !s.containerOpened.Load() {
    23  		return fmt.Errorf("no beacon container opened")
    24  	}
    25  	pos := s.openedPos.Load()
    26  	beacon, ok := s.c.World().Block(pos).(block.Beacon)
    27  	if !ok {
    28  		return fmt.Errorf("no beacon container opened")
    29  	}
    30  
    31  	// Check if the item present in the beacon slot is valid.
    32  	payment, _ := h.itemInSlot(slot, s)
    33  	if payable, ok := payment.Item().(item.BeaconPayment); !ok || !payable.PayableForBeacon() {
    34  		return fmt.Errorf("item %#v in beacon slot cannot be used as payment", payment)
    35  	}
    36  
    37  	// Check if the effects are valid and allowed for the beacon's level.
    38  	if !h.validBeaconEffect(a.PrimaryEffect, beacon) {
    39  		return fmt.Errorf("primary effect selected is not allowed: %v for level %v", a.PrimaryEffect, beacon.Level())
    40  	} else if !h.validBeaconEffect(a.SecondaryEffect, beacon) || (beacon.Level() < 4 && a.SecondaryEffect != 0) {
    41  		return fmt.Errorf("secondary effect selected is not allowed: %v for level %v", a.SecondaryEffect, beacon.Level())
    42  	}
    43  
    44  	primary, pOk := effect.ByID(int(a.PrimaryEffect))
    45  	secondary, sOk := effect.ByID(int(a.SecondaryEffect))
    46  	if pOk {
    47  		beacon.Primary = primary.(effect.LastingType)
    48  	}
    49  	if sOk {
    50  		beacon.Secondary = secondary.(effect.LastingType)
    51  	}
    52  	s.c.World().SetBlock(pos, beacon, nil)
    53  
    54  	// The client will send a Destroy action after this action, but we can't rely on that because the client
    55  	// could just not send it.
    56  	// We just ignore the next Destroy action and set the item to air here.
    57  	h.setItemInSlot(slot, item.Stack{}, s)
    58  	h.ignoreDestroy = true
    59  	return nil
    60  }
    61  
    62  // validBeaconEffect checks if the ID passed is a valid beacon effect.
    63  func (h *ItemStackRequestHandler) validBeaconEffect(id int32, beacon block.Beacon) bool {
    64  	switch id {
    65  	case 1, 3:
    66  		return beacon.Level() >= 1
    67  	case 8, 11:
    68  		return beacon.Level() >= 2
    69  	case 5:
    70  		return beacon.Level() >= 3
    71  	case 10:
    72  		return beacon.Level() >= 4
    73  	case 0:
    74  		return true
    75  	}
    76  	return false
    77  }