Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ examples/genmaps/genmaps
examples/genmaps/*.png
data/packetid/generate.go
.idea/
.vscode/
.vscode/

temp/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Go-MC

![Version](https://img.shields.io/badge/Minecraft-1.21-blue.svg)
![Version](https://img.shields.io/badge/Minecraft-1.21.11-blue.svg)
[![Go Reference](https://pkg.go.dev/badge/github.com/Tnze/go-mc.svg)](https://pkg.go.dev/github.com/Tnze/go-mc)
[![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc)
[![Discord](https://img.shields.io/discord/915805561138860063?label=Discord)](https://discord.gg/A4qh8BT8Ue)
Expand Down
19 changes: 12 additions & 7 deletions bot/basic/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ type EventsListener struct {
// Before you confirm the teleportation, the server will not accept any player motion packets.
//
// The position coordinates and rotation are absolute or relative depends on the flags.
// The flag byte is a bitfield, specifies whether each coordinate value is absolute or relative.
// The flags field is a u32 bitfield (PositionUpdateRelatives) with bits:
// 0x01=X, 0x02=Y, 0x04=Z, 0x08=Yaw, 0x10=Pitch, 0x20=DX, 0x40=DY, 0x80=DZ, 0x100=YawDelta
// For more information, see https://wiki.vg/Protocol#Synchronize_Player_Position
Teleported func(x, y, z float64, yaw, pitch float32, flags byte, teleportID int32) error
Teleported func(x, y, z float64, yaw, pitch float32, flags int32, teleportID int32) error
}

// attach your event listener to the client.
Expand Down Expand Up @@ -117,20 +118,24 @@ func attachUpdateHealth(c *bot.Client, healthChangeHandler func(health float32,
})
}

func attachPlayerPosition(c *bot.Client, handler func(x, y, z float64, yaw, pitch float32, flag byte, teleportID int32) error) {
func attachPlayerPosition(c *bot.Client, handler func(x, y, z float64, yaw, pitch float32, flags int32, teleportID int32) error) {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundPlayerPosition,
F: func(p pk.Packet) error {
var (
TeleportID pk.VarInt
X, Y, Z pk.Double
DX, DY, DZ pk.Double // velocity deltas (1.21.2+)
Yaw, Pitch pk.Float
Flags pk.Byte
TeleportID pk.VarInt
Flags pk.Int // u32 bitflags (PositionUpdateRelatives)
)
if err := p.Scan(&X, &Y, &Z, &Yaw, &Pitch, &Flags, &TeleportID); err != nil {
if err := p.Scan(&TeleportID, &X, &Y, &Z, &DX, &DY, &DZ, &Yaw, &Pitch, &Flags); err != nil {
return Error{err}
}
return handler(float64(X), float64(Y), float64(Z), float32(Yaw), float32(Pitch), byte(Flags), int32(TeleportID))
_ = DX
_ = DY
_ = DZ
return handler(float64(X), float64(Y), float64(Z), float32(Yaw), float32(Pitch), int32(Flags), int32(TeleportID))
},
})
}
Expand Down
1 change: 1 addition & 0 deletions bot/basic/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error {
pk.VarInt(p.Settings.MainHand),
pk.Boolean(p.Settings.EnableTextFiltering),
pk.Boolean(p.Settings.AllowListing),
pk.VarInt(p.Settings.ParticleStatus), // 1.21.4+: 0=all, 1=decreased, 2=minimal
))
if err != nil {
return Error{err}
Expand Down
6 changes: 6 additions & 0 deletions bot/basic/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ type Settings struct {
EnableTextFiltering bool
AllowListing bool

// ParticleStatus controls which particles are shown.
// 0 = all, 1 = decreased, 2 = minimal.
// Added in 1.21.4 (protocol 769).
ParticleStatus int

// The brand string presented to the server.
Brand string
}
Expand All @@ -40,6 +45,7 @@ var DefaultSettings = Settings{

EnableTextFiltering: false,
AllowListing: true,
ParticleStatus: 0, // all

Brand: "vanilla",
}
5 changes: 0 additions & 5 deletions bot/basic/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package basic

import (
"bytes"
"errors"

pk "github.com/Tnze/go-mc/net/packet"
)
Expand All @@ -24,10 +23,6 @@ func (p *Player) handleUpdateTags(packet pk.Packet) error {
}

registry := p.c.Registries.Registry(string(registryID))
if registry == nil {
return Error{errors.New("unknown registry: " + string(registryID))}
}

_, err = registry.ReadTagsFrom(r)
if err != nil {
return Error{err}
Expand Down
4 changes: 0 additions & 4 deletions bot/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package bot

import (
"bytes"
"errors"
"fmt"
"io"

Expand Down Expand Up @@ -150,9 +149,6 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
}

registry := c.Registries.Registry(string(registryID))
if registry == nil {
return ConfigErr{ErrStage, errors.New("unknown registry: " + string(registryID))}
}

_, err = registry.ReadFrom(r)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions bot/ingame.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (c *Client) HandleGame() error {
return err
}

if p.ID == int32(packetid.BundleDelimiter) {
if p.ID == int32(packetid.ClientboundBundleDelimiter) {
err := c.handleBundlePackets()
if err != nil {
return err
Expand Down Expand Up @@ -58,7 +58,7 @@ func (c *Client) handleBundlePackets() (err error) {
return err
}

if p.ID == int32(packetid.BundleDelimiter) {
if p.ID == int32(packetid.ClientboundBundleDelimiter) {
// bundle finished
goto handlePackets
}
Expand Down
2 changes: 1 addition & 1 deletion bot/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (c *Client) joinLogin(conn *net.Conn) error {
}
receiving = "set compression"

case packetid.ClientboundLoginGameProfile: // Login Success
case packetid.ClientboundLoginLoginFinished: // Login Success
err := p.Scan(
(*pk.UUID)(&c.UUID),
(*pk.String)(&c.Name),
Expand Down
2 changes: 1 addition & 1 deletion bot/mcbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

// ProtocolVersion is the protocol version number of minecraft net protocol
const (
ProtocolVersion = 767
ProtocolVersion = 774
DefaultPort = mcnet.DefaultPort
)

Expand Down
3 changes: 2 additions & 1 deletion bot/msg/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (m *Manager) handleSystemChat(p pk.Packet) error {

func (m *Manager) handlePlayerChat(packet pk.Packet) error {
var (
globalIndex pk.VarInt
sender pk.UUID
index pk.VarInt
signature pk.Option[sign.Signature, *sign.Signature]
Expand All @@ -76,7 +77,7 @@ func (m *Manager) handlePlayerChat(packet pk.Packet) error {
filter sign.FilterMask
chatType chat.Type
)
if err := packet.Scan(&sender, &index, &signature, &body, &unsignedContent, &filter, &chatType); err != nil {
if err := packet.Scan(&globalIndex, &sender, &index, &signature, &body, &unsignedContent, &filter, &chatType); err != nil {
return err
}

Expand Down
94 changes: 80 additions & 14 deletions bot/screen/screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package screen

import (
"errors"
"fmt"
"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"
"github.com/Tnze/go-mc/level/component"
pk "github.com/Tnze/go-mc/net/packet"
)

Expand All @@ -34,6 +35,7 @@ func NewManager(c *bot.Client, e EventsListener) *Manager {
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},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundSetPlayerInventory, F: m.onSetPlayerInventory},
)
return m
}
Expand Down Expand Up @@ -189,37 +191,101 @@ func (m *Manager) onSetSlot(p pk.Packet) (err error) {
return nil
}

// onSetPlayerInventory handles ClientboundSetPlayerInventory (1.20.5+).
// Wire: VarInt(slotIndex) + Slot(data). Updates the player inventory directly.
func (m *Manager) onSetPlayerInventory(p pk.Packet) error {
var (
SlotID pk.VarInt
SlotData Slot
)
if err := p.Scan(&SlotID, &SlotData); err != nil {
return Error{err}
}
if err := m.Inventory.onSetSlot(int(SlotID), SlotData); err != nil {
return Error{err}
}
if m.events.SetSlot != nil {
if err := m.events.SetSlot(0, int(SlotID)); err != nil {
return Error{err}
}
}
return nil
}

type Slot struct {
ID pk.VarInt
Count pk.VarInt
NBT nbt.RawMessage
ID pk.VarInt
Count pk.VarInt
ComponentsAdd int32 // number of component patches to add (informational)
ComponentsRemove int32 // number of component patches to remove (informational)
}

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),
},
},
// Post-1.20.5 format: Count (VarInt, 0=empty) → ItemID → ComponentsAdd → ComponentsRemove → data
n, err = s.Count.WriteTo(w)
if err != nil || s.Count <= 0 {
return
}
var n2 int64
n2, err = pk.Tuple{
s.ID,
pk.VarInt(s.ComponentsAdd),
pk.VarInt(s.ComponentsRemove),
// Component data not supported yet — only items with 0 components can be sent
}.WriteTo(w)
return n + n2, err
}

func (s *Slot) ReadFrom(r io.Reader) (n int64, err error) {
var componentsAdd, componentsRemove pk.VarInt
return pk.Tuple{
n, err = 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)
if err != nil {
return
}
s.ComponentsAdd = int32(componentsAdd)
s.ComponentsRemove = int32(componentsRemove)

// Read component data for added components
for i := int32(0); i < s.ComponentsAdd; i++ {
var componentType pk.VarInt
var n2 int64
n2, err = componentType.ReadFrom(r)
n += n2
if err != nil {
return
}
comp := component.NewComponent(int32(componentType))
if comp == nil {
err = fmt.Errorf("unsupported component type %d in slot item %d", componentType, s.ID)
return
}
n2, err = comp.ReadFrom(r)
n += n2
if err != nil {
return
}
}

// Read component IDs for removed components (just VarInt type IDs, no data)
for i := int32(0); i < s.ComponentsRemove; i++ {
var componentType pk.VarInt
var n2 int64
n2, err = componentType.ReadFrom(r)
n += n2
if err != nil {
return
}
}

return
}

type Container interface {
Expand Down
13 changes: 8 additions & 5 deletions chat/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,15 @@ func (p HistoryMessage) WriteTo(w io.Writer) (n int64, err error) {
type HistoryUpdate struct {
Offset pk.VarInt
Acknowledged pk.FixedBitSet // n == 20
Checksum pk.Byte // 0 = ignore checksum
}

func (h HistoryUpdate) WriteTo(w io.Writer) (n int64, err error) {
return pk.Tuple{h.Offset, h.Acknowledged}.WriteTo(w)
return pk.Tuple{h.Offset, h.Acknowledged, h.Checksum}.WriteTo(w)
}

func (h *HistoryUpdate) ReadFrom(r io.Reader) (n int64, err error) {
return pk.Tuple{&h.Offset, &h.Acknowledged}.ReadFrom(r)
return pk.Tuple{&h.Offset, &h.Acknowledged, &h.Checksum}.ReadFrom(r)
}

type Signature [256]byte
Expand Down Expand Up @@ -128,17 +129,19 @@ func (p PackedSignature) WriteTo(w io.Writer) (n int64, err error) {
return n1, err
}

func (p PackedSignature) ReadFrom(r io.Reader) (n int64, err error) {
n1, err := (*pk.VarInt)(&p.ID).ReadFrom(r)
func (p *PackedSignature) ReadFrom(r io.Reader) (n int64, err error) {
var raw pk.VarInt
n1, err := raw.ReadFrom(r)
if err != nil {
return n1, err
}
p.ID = int32(raw) - 1

if p.ID == -1 {
if p.Signature == nil {
p.Signature = new(Signature)
}
n2, err := r.Read(p.Signature[:])
n2, err := io.ReadFull(r, p.Signature[:])
return n1 + int64(n2), err
} else {
p.Signature = nil
Expand Down
11 changes: 3 additions & 8 deletions data/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
## Updating `data`
## data

1. Go to [https://github.com/PrismarineJS/minecraft-data/tree/master/data/pc/{version}](https://github.com/PrismarineJS/minecraft-data/tree/master/data/pc)
2. Update `version` in the following files if there is a new corresponding JSON file available:
- [gen_block.go](block/gen_block.go) - `blocks.json`
- [gen_entity.go](entity/gen_entity.go) - `entities.json`
- [gen_item.go](item/gen_item.go) - `items.json`
3. Update the `URL` in [gen_soundid.go](soundid/gen_soundid.go) (verify the URL returns a response first)
4. Run `go generate ./...`
All data packages are generated by `tools/` from the Minecraft server jar.
See [docs/dev/tools.md](../docs/dev/tools.md) for the extraction and generation pipeline.
Loading