Enhance support of screens

This commit is contained in:
Tnze
2021-07-04 12:38:24 +08:00
parent ebc44e7c8b
commit 7cfe5145d2
6 changed files with 148 additions and 63 deletions

7
bot/screen/events.go Normal file
View File

@ -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
}

View File

@ -1,16 +1,29 @@
package screen package screen
import ( import "errors"
"fmt"
"github.com/Tnze/go-mc/data/item"
"github.com/Tnze/go-mc/nbt"
)
type Inventory struct { type Inventory struct {
Slots [46]Slot
} }
func (inv Inventory) SetSlot(i int, id int32, count byte, NBT nbt.RawMessage) { func (inv *Inventory) onClose() error {
// TODO: accept inv data return nil
fmt.Printf("Inventory[%d] = minecraft:%v * %d\n", i, item.ByID[item.ID(id)].Name, count)
} }
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] }

View File

@ -1,6 +1,7 @@
package screen package screen
import ( import (
"errors"
"io" "io"
"github.com/Tnze/go-mc/bot" "github.com/Tnze/go-mc/bot"
@ -11,15 +12,23 @@ import (
) )
type Manager struct { type Manager struct {
Screens map[byte]Container Screens map[int]Container
Inventory Inventory
Cursor Slot
events EventsListener
} }
func NewManager(c *bot.Client) *Manager { func NewManager(c *bot.Client, e EventsListener) *Manager {
m := &Manager{Screens: make(map[byte]Container)} m := &Manager{
m.Screens[0] = &Inventory{} Screens: make(map[int]Container),
events: e,
}
m.Screens[0] = &m.Inventory
c.Events.AddListener( c.Events.AddListener(
bot.PacketHandler{Priority: 64, ID: packetid.OpenWindow, F: m.onOpenScreen}, bot.PacketHandler{Priority: 0, ID: packetid.OpenWindow, F: m.onOpenScreen},
bot.PacketHandler{Priority: 64, ID: packetid.WindowItems, F: m.onSetContentPacket}, 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 return m
} }
@ -33,7 +42,14 @@ func (m *Manager) onOpenScreen(p pk.Packet) error {
if err := p.Scan(&ContainerID, &Type, &Title); err != nil { if err := p.Scan(&ContainerID, &Type, &Title); err != nil {
return Error{err} return Error{err}
} }
//if c, ok := m.Screens[byte(ContainerID)]; ok {
// TODO: Create the specified container // TODO: Create the specified container
//}
if m.events.Open != nil {
if err := m.events.Open(int(ContainerID)); err != nil {
return Error{err}
}
}
return nil return nil
} }
@ -41,7 +57,7 @@ func (m *Manager) onSetContentPacket(p pk.Packet) error {
var ( var (
ContainerID pk.UnsignedByte ContainerID pk.UnsignedByte
Count pk.Short Count pk.Short
SlotData []slot SlotData []Slot
) )
if err := p.Scan( if err := p.Scan(
&ContainerID, &ContainerID,
@ -51,32 +67,93 @@ func (m *Manager) onSetContentPacket(p pk.Packet) error {
}); err != nil { }); err != nil {
return Error{err} 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 { 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 return nil
} }
type slot struct { func (m *Manager) onCloseScreen(p pk.Packet) error {
present pk.Boolean var ContainerID pk.UnsignedByte
id pk.VarInt if err := p.Scan(&ContainerID); err != nil {
count pk.Byte return Error{err}
nbt nbt.RawMessage }
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{ return pk.Tuple{
&s.present, pk.Opt{Has: &s.present, &present, pk.Opt{Has: &present,
Field: pk.Tuple{ Field: pk.Tuple{
&s.id, &s.count, pk.NBT(&s.nbt), &s.ID, &s.Count, pk.NBT(&s.NBT),
}, },
}, },
}.ReadFrom(r) }.ReadFrom(r)
} }
type Container interface { type Container interface {
SetSlot(i int, id int32, count byte, NBT nbt.RawMessage) onSetSlot(i int, s Slot) error
onClose() error
} }
type Error struct { type Error struct {

View File

@ -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},
}

View File

@ -11,11 +11,11 @@ package chat
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
en_us "github.com/Tnze/go-mc/data/lang/en-us"
"io" "io"
"regexp" "regexp"
"strings" "strings"
en_us "github.com/Tnze/go-mc/data/lang/en-us"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
@ -50,7 +50,7 @@ func (m *Message) UnmarshalJSON(jsonMsg []byte) (err error) {
return return
} }
//Decode for a ChatMsg packet // ReadFrom decode Message in a ChatMsg packet
func (m *Message) ReadFrom(r io.Reader) (int64, error) { func (m *Message) ReadFrom(r io.Reader) (int64, error) {
var Len pk.VarInt var Len pk.VarInt
if n, err := Len.ReadFrom(r); err != nil { 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 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) { func (m Message) WriteTo(w io.Writer) (int64, error) {
code, err := json.Marshal(m) code, err := json.Marshal(m)
if err != nil { if err != nil {
@ -174,7 +174,7 @@ func (m Message) ClearString() string {
if m.Extra != nil { if m.Extra != nil {
for i := range m.Extra { for i := range m.Extra {
msg.WriteString(Message(m.Extra[i]).ClearString()) msg.WriteString(m.Extra[i].ClearString())
} }
} }
return msg.String() return msg.String()
@ -225,7 +225,7 @@ func (m Message) String() string {
if m.Extra != nil { if m.Extra != nil {
for i := range m.Extra { for i := range m.Extra {
msg.WriteString(Message(m.Extra[i]).String()) msg.WriteString(m.Extra[i].String())
} }
} }

View File

@ -20,6 +20,7 @@ import (
var address = flag.String("address", "127.0.0.1", "The server address") var address = flag.String("address", "127.0.0.1", "The server address")
var client *bot.Client var client *bot.Client
var player *basic.Player var player *basic.Player
var screenManager *screen.Manager
func main() { func main() {
flag.Parse() flag.Parse()
@ -32,7 +33,11 @@ func main() {
Disconnect: onDisconnect, Disconnect: onDisconnect,
Death: onDeath, Death: onDeath,
}.Attach(client) }.Attach(client)
_ = screen.NewManager(client) screenManager = screen.NewManager(client, screen.EventsListener{
Open: nil,
SetSlot: onScreenSlotChange,
Close: nil,
})
//Login //Login
err := client.JoinServer(*address) err := client.JoinServer(*address)
@ -84,6 +89,18 @@ func onChatMsg(c chat.Message, _ byte, _ uuid.UUID) error {
return nil 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 { type DisconnectErr struct {
Reason chat.Message Reason chat.Message
} }