170 lines
3.5 KiB
Go
170 lines
3.5 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.OpenWindow, F: m.onOpenScreen},
|
|
bot.PacketHandler{Priority: 0, ID: packetid.WindowItems, F: m.onSetContentPacket},
|
|
bot.PacketHandler{Priority: 0, ID: packetid.CloseWindowClientbound, F: m.onCloseScreen},
|
|
bot.PacketHandler{Priority: 0, ID: packetid.SetSlot, 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
|
|
Count pk.Short
|
|
SlotData []Slot
|
|
)
|
|
if err := p.Scan(
|
|
&ContainerID,
|
|
&Count, pk.Ary{
|
|
Len: &Count,
|
|
Ary: &SlotData,
|
|
}); 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
|
|
}
|