diff --git a/bot/screen/events.go b/bot/screen/events.go new file mode 100644 index 0000000..e538706 --- /dev/null +++ b/bot/screen/events.go @@ -0,0 +1,7 @@ +package screen + +type EventsListener struct { + Open func(id int) error + SetSlot func(id, index int) error + Close func(id int) error +} diff --git a/bot/screen/inventory.go b/bot/screen/inventory.go index 67f11f1..b19e537 100644 --- a/bot/screen/inventory.go +++ b/bot/screen/inventory.go @@ -1,16 +1,29 @@ package screen -import ( - "fmt" - - "github.com/Tnze/go-mc/data/item" - "github.com/Tnze/go-mc/nbt" -) +import "errors" type Inventory struct { + Slots [46]Slot } -func (inv Inventory) SetSlot(i int, id int32, count byte, NBT nbt.RawMessage) { - // TODO: accept inv data - fmt.Printf("Inventory[%d] = minecraft:%v * %d\n", i, item.ByID[item.ID(id)].Name, count) +func (inv *Inventory) onClose() error { + return nil } + +func (inv *Inventory) onSetSlot(i int, s Slot) error { + if i < 0 || i >= len(inv.Slots) { + return errors.New("slot index out of bounds") + } + inv.Slots[i] = s + return nil +} + +func (inv *Inventory) CraftingOutput() *Slot { return &inv.Slots[0] } +func (inv *Inventory) CraftingInput() []Slot { return inv.Slots[1 : 1+4] } + +// Armor returns to the armor section of the Inventory. +// The length is 4, which are head, chest, legs and feet. +func (inv *Inventory) Armor() []Slot { return inv.Slots[5 : 5+4] } +func (inv *Inventory) Main() []Slot { return inv.Slots[9 : 9+3*9] } +func (inv *Inventory) Hotbar() []Slot { return inv.Slots[36 : 36+9] } +func (inv *Inventory) Offhand() *Slot { return &inv.Slots[45] } diff --git a/bot/screen/screen.go b/bot/screen/screen.go index 375c89a..1b4eb51 100644 --- a/bot/screen/screen.go +++ b/bot/screen/screen.go @@ -1,6 +1,7 @@ package screen import ( + "errors" "io" "github.com/Tnze/go-mc/bot" @@ -11,15 +12,23 @@ import ( ) type Manager struct { - Screens map[byte]Container + Screens map[int]Container + Inventory Inventory + Cursor Slot + events EventsListener } -func NewManager(c *bot.Client) *Manager { - m := &Manager{Screens: make(map[byte]Container)} - m.Screens[0] = &Inventory{} +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: 64, ID: packetid.OpenWindow, F: m.onOpenScreen}, - bot.PacketHandler{Priority: 64, ID: packetid.WindowItems, F: m.onSetContentPacket}, + 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 } @@ -33,7 +42,14 @@ func (m *Manager) onOpenScreen(p pk.Packet) error { 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 } @@ -41,7 +57,7 @@ func (m *Manager) onSetContentPacket(p pk.Packet) error { var ( ContainerID pk.UnsignedByte Count pk.Short - SlotData []slot + SlotData []Slot ) if err := p.Scan( &ContainerID, @@ -51,32 +67,93 @@ func (m *Manager) onSetContentPacket(p pk.Packet) error { }); err != nil { return Error{err} } - container := m.Screens[byte(ContainerID)] + // 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 { - container.SetSlot(i, int32(v.id), byte(v.count), v.nbt) + 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 } -type slot struct { - present pk.Boolean - id pk.VarInt - count pk.Byte - nbt nbt.RawMessage +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 (s *slot) ReadFrom(r io.Reader) (n int64, err error) { +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.Close != nil { + if err := m.events.Close(int(ContainerID)); 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{ - &s.present, pk.Opt{Has: &s.present, + &present, pk.Opt{Has: &present, Field: pk.Tuple{ - &s.id, &s.count, pk.NBT(&s.nbt), + &s.ID, &s.Count, pk.NBT(&s.NBT), }, }, }.ReadFrom(r) } type Container interface { - SetSlot(i int, id int32, count byte, NBT nbt.RawMessage) + onSetSlot(i int, s Slot) error + onClose() error } type Error struct { diff --git a/bot/screen/type.go b/bot/screen/type.go deleted file mode 100644 index de435d7..0000000 --- a/bot/screen/type.go +++ /dev/null @@ -1,29 +0,0 @@ -package screen - -type Info struct { - Name string - Start, End int // Player inventory - Slots int -} - -func (i Info) PlayerInvStart() int { - return i.Start -} - -func (i Info) PlayerInvEnd() int { - return i.End -} - -func (i Info) HotbarIdx(place int) int { - return i.End - (8 - place) -} - -var ByType = map[int]Info{ - -1: {Name: "inventory", Start: 9, End: 44, Slots: 46}, - 0: {Name: "generic_9x1", Start: 1 * 9, End: 1*9 + 35, Slots: 1*9 + 36}, - 1: {Name: "generic_9x2", Start: 2 * 9, End: 2*9 + 35, Slots: 2*9 + 36}, - 2: {Name: "generic_9x3", Start: 3 * 9, End: 3*9 + 35, Slots: 3*9 + 36}, - 3: {Name: "generic_9x4", Start: 4 * 9, End: 4*9 + 35, Slots: 4*9 + 36}, - 4: {Name: "generic_9x5", Start: 5 * 9, End: 5*9 + 35, Slots: 5*9 + 36}, - 5: {Name: "generic_9x6", Start: 6 * 9, End: 6*9 + 35, Slots: 6*9 + 36}, -} diff --git a/chat/chatMsg.go b/chat/chatMsg.go index 08d653c..6956c68 100644 --- a/chat/chatMsg.go +++ b/chat/chatMsg.go @@ -11,15 +11,15 @@ package chat import ( "encoding/json" "fmt" - en_us "github.com/Tnze/go-mc/data/lang/en-us" "io" "regexp" "strings" + en_us "github.com/Tnze/go-mc/data/lang/en-us" pk "github.com/Tnze/go-mc/net/packet" ) -//Message is a message sent by other +// Message is a message sent by other type Message jsonChat type jsonChat struct { @@ -50,7 +50,7 @@ func (m *Message) UnmarshalJSON(jsonMsg []byte) (err error) { return } -//Decode for a ChatMsg packet +// ReadFrom decode Message in a ChatMsg packet func (m *Message) ReadFrom(r io.Reader) (int64, error) { var Len pk.VarInt if n, err := Len.ReadFrom(r); err != nil { @@ -61,7 +61,7 @@ func (m *Message) ReadFrom(r io.Reader) (int64, error) { return int64(Len) - lr.N, err } -//Encode for a ChatMsg packet +// WriteTo encode Message into a ChatMsg packet func (m Message) WriteTo(w io.Writer) (int64, error) { code, err := json.Marshal(m) if err != nil { @@ -174,7 +174,7 @@ func (m Message) ClearString() string { if m.Extra != nil { for i := range m.Extra { - msg.WriteString(Message(m.Extra[i]).ClearString()) + msg.WriteString(m.Extra[i].ClearString()) } } return msg.String() @@ -225,7 +225,7 @@ func (m Message) String() string { if m.Extra != nil { for i := range m.Extra { - msg.WriteString(Message(m.Extra[i]).String()) + msg.WriteString(m.Extra[i].String()) } } diff --git a/examples/daze/daze.go b/examples/daze/daze.go index f3facd4..9c7175a 100644 --- a/examples/daze/daze.go +++ b/examples/daze/daze.go @@ -20,6 +20,7 @@ import ( var address = flag.String("address", "127.0.0.1", "The server address") var client *bot.Client var player *basic.Player +var screenManager *screen.Manager func main() { flag.Parse() @@ -32,7 +33,11 @@ func main() { Disconnect: onDisconnect, Death: onDeath, }.Attach(client) - _ = screen.NewManager(client) + screenManager = screen.NewManager(client, screen.EventsListener{ + Open: nil, + SetSlot: onScreenSlotChange, + Close: nil, + }) //Login err := client.JoinServer(*address) @@ -84,6 +89,18 @@ func onChatMsg(c chat.Message, _ byte, _ uuid.UUID) error { return nil } +func onScreenSlotChange(id, index int) error { + if id == -2 { + log.Printf("Slot change: inventory: %v", screenManager.Inventory.Slots[index]) + } else if id == -1 && index == -1 { + log.Printf("Slot change: cursor: %v", screenManager.Cursor) + } else { + container := screenManager.Screens[id] + log.Printf("Slot change: Screen[%d].Slot[%d]: %T", id, index, container) + } + return nil +} + type DisconnectErr struct { Reason chat.Message }