1.21.8 data
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Go / Test (1.22) (push) Has been cancelled
Go / Test (^1.22) (push) Has been cancelled

This commit is contained in:
2025-08-22 06:17:33 +08:00
parent 133e3fab4a
commit 0958972953
173 changed files with 13782 additions and 406 deletions

240
bot/screen/screen.go Normal file
View File

@ -0,0 +1,240 @@
package screen
import (
"errors"
"io"
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/nbt"
pk "github.com/Tnze/go-mc/net/packet"
)
type Manager struct {
c *bot.Client
Screens map[int]Container
Inventory Inventory
Cursor Slot
events EventsListener
// The last received State ID from server
stateID int32
}
func NewManager(c *bot.Client, e EventsListener) *Manager {
m := &Manager{
c: c,
Screens: make(map[int]Container),
events: e,
}
m.Screens[0] = &m.Inventory
c.Events.AddListener(
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundOpenScreen, F: m.onOpenScreen},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundContainerSetContent, F: m.onSetContentPacket},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundContainerClose, F: m.onCloseScreen},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundContainerSetSlot, F: m.onSetSlot},
)
return m
}
type ChangedSlots map[int]*Slot
func (m *Manager) ContainerClick(id int, slot int16, button byte, mode int32, slots ChangedSlots, carried *Slot) error {
return m.c.Conn.WritePacket(pk.Marshal(
packetid.ServerboundContainerClick,
pk.UnsignedByte(id),
pk.VarInt(m.stateID),
pk.Short(slot),
pk.Byte(button),
pk.VarInt(mode),
slots,
carried,
))
}
func (c ChangedSlots) WriteTo(w io.Writer) (n int64, err error) {
n, err = pk.VarInt(len(c)).WriteTo(w)
if err != nil {
return
}
for i, v := range c {
n1, err := pk.Short(i).WriteTo(w)
if err != nil {
return n + n1, err
}
n2, err := v.WriteTo(w)
if err != nil {
return n + n1 + n2, err
}
n += n1 + n2
}
return
}
func (m *Manager) onOpenScreen(p pk.Packet) error {
var (
ContainerID pk.VarInt
Type pk.VarInt
Title chat.Message
)
if err := p.Scan(&ContainerID, &Type, &Title); err != nil {
return Error{err}
}
if _, ok := m.Screens[int(ContainerID)]; !ok {
TypeInt32 := int32(Type)
if TypeInt32 < 6 {
Rows := TypeInt32 + 1
chest := Chest{
Type: TypeInt32,
Slots: make([]Slot, 9*Rows),
Rows: int(Rows),
Title: Title,
}
m.Screens[int(ContainerID)] = &chest
}
} else {
return errors.New("container id already exists in screens")
}
if m.events.Open != nil {
if err := m.events.Open(int(ContainerID), int32(Type), Title); err != nil {
return Error{err}
}
}
return nil
}
func (m *Manager) onSetContentPacket(p pk.Packet) error {
var (
ContainerID pk.UnsignedByte
StateID pk.VarInt
SlotData []Slot
CarriedItem Slot
)
if err := p.Scan(
&ContainerID,
&StateID,
pk.Array(&SlotData),
&CarriedItem,
); err != nil {
return Error{err}
}
m.stateID = int32(StateID)
// copy the slot data to container
container, ok := m.Screens[int(ContainerID)]
if !ok {
return Error{errors.New("setting content of non-exist container")}
}
for i, v := range SlotData {
err := container.onSetSlot(i, v)
if err != nil {
return Error{err}
}
if m.events.SetSlot != nil {
if err := m.events.SetSlot(int(ContainerID), i); err != nil {
return Error{err}
}
}
}
return nil
}
func (m *Manager) onCloseScreen(p pk.Packet) error {
var ContainerID pk.UnsignedByte
if err := p.Scan(&ContainerID); err != nil {
return Error{err}
}
if c, ok := m.Screens[int(ContainerID)]; ok {
delete(m.Screens, int(ContainerID))
if err := c.onClose(); err != nil {
return Error{err}
}
if m.events.Close != nil {
if err := m.events.Close(int(ContainerID)); err != nil {
return Error{err}
}
}
}
return nil
}
func (m *Manager) onSetSlot(p pk.Packet) (err error) {
var (
ContainerID pk.Byte
StateID pk.VarInt
SlotID pk.Short
SlotData Slot
)
if err := p.Scan(&ContainerID, &StateID, &SlotID, &SlotData); err != nil {
return Error{err}
}
m.stateID = int32(StateID)
if ContainerID == -1 && SlotID == -1 {
m.Cursor = SlotData
} else if ContainerID == -2 {
err = m.Inventory.onSetSlot(int(SlotID), SlotData)
} else if c, ok := m.Screens[int(ContainerID)]; ok {
err = c.onSetSlot(int(SlotID), SlotData)
}
if m.events.SetSlot != nil {
if err := m.events.SetSlot(int(ContainerID), int(SlotID)); err != nil {
return Error{err}
}
}
if err != nil {
return Error{err}
}
return nil
}
type Slot struct {
ID pk.VarInt
Count pk.VarInt
NBT nbt.RawMessage
}
func (s *Slot) WriteTo(w io.Writer) (n int64, err error) {
var present pk.Boolean = s != nil
return pk.Tuple{
present, pk.Opt{
Has: present,
Field: pk.Tuple{
&s.ID, &s.Count, pk.NBT(&s.NBT),
},
},
}.WriteTo(w)
}
func (s *Slot) ReadFrom(r io.Reader) (n int64, err error) {
var componentsAdd, componentsRemove pk.VarInt
return pk.Tuple{
&s.Count, pk.Opt{
Has: func() bool { return s.Count > 0 },
Field: pk.Tuple{
&s.ID,
&componentsAdd,
&componentsRemove,
// TODO: Components Ignored
},
},
}.ReadFrom(r)
}
type Container interface {
onSetSlot(i int, s Slot) error
onClose() error
}
type Error struct {
Err error
}
func (e Error) Error() string {
return "bot/screen: " + e.Err.Error()
}
func (e Error) Unwrap() error {
return e.Err
}