Files
go-mc/bot/screen/screen.go
2021-12-09 20:24:39 +08:00

175 lines
3.7 KiB
Go

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 {
Screens map[int]Container
Inventory Inventory
Cursor Slot
events EventsListener
}
func NewManager(c *bot.Client, e EventsListener) *Manager {
m := &Manager{
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
}
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 c, ok := m.Screens[byte(ContainerID)]; ok {
// TODO: Create the specified container
//}
if m.events.Open != nil {
if err := m.events.Open(int(ContainerID)); err != nil {
return Error{err}
}
}
return nil
}
func (m *Manager) onSetContentPacket(p pk.Packet) error {
var (
ContainerID pk.UnsignedByte
StateID pk.VarInt
Count pk.VarInt
SlotData []Slot
CarriedItem Slot
)
if err := p.Scan(
&ContainerID,
&StateID,
&Count, pk.Ary{
Len: &Count,
Ary: &SlotData,
},
&CarriedItem,
); err != nil {
return Error{err}
}
// 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
SlotID pk.Short
ItemStack Slot
)
if err := p.Scan(&ContainerID, &SlotID, &ItemStack); err != nil {
return Error{err}
}
if ContainerID == -1 && SlotID == -1 {
m.Cursor = ItemStack
} else if ContainerID == -2 {
err = m.Inventory.onSetSlot(int(SlotID), ItemStack)
} else if c, ok := m.Screens[int(ContainerID)]; ok {
err = c.onSetSlot(int(SlotID), ItemStack)
}
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.Byte
NBT nbt.RawMessage
}
func (s *Slot) ReadFrom(r io.Reader) (n int64, err error) {
var present pk.Boolean
return pk.Tuple{
&present, pk.Opt{Has: &present,
Field: pk.Tuple{
&s.ID, &s.Count, pk.NBT(&s.NBT),
},
},
}.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
}