update comments
This commit is contained in:
@ -7,14 +7,13 @@
|
|||||||
// There is 4 kinds of clientbound packet is handled by this package.
|
// There is 4 kinds of clientbound packet is handled by this package.
|
||||||
// - LoginPacket, for cache player info. The player info will be stored in [Player.PlayerInfo].
|
// - LoginPacket, for cache player info. The player info will be stored in [Player.PlayerInfo].
|
||||||
// - KeepAlivePacket, for avoid the client to be kicked by the server.
|
// - KeepAlivePacket, for avoid the client to be kicked by the server.
|
||||||
// - PlayerPosition, is only received when server teleporting the player. And the confirm packet is automatically sent.
|
// - PlayerPosition, is only received when server teleporting the player.
|
||||||
// - Respawn, for updating player info, which may change when player respawned.
|
// - Respawn, for updating player info, which may change when player respawned.
|
||||||
//
|
//
|
||||||
// # [EventsListener]
|
// # [EventsListener]
|
||||||
//
|
//
|
||||||
// Handles some basic event you probably need.
|
// Handles some basic event you probably need.
|
||||||
// - GameStart
|
// - GameStart
|
||||||
// - ChatMsg
|
|
||||||
// - Disconnect
|
// - Disconnect
|
||||||
// - HealthChange
|
// - HealthChange
|
||||||
// - Death
|
// - Death
|
||||||
@ -32,21 +31,22 @@ type Player struct {
|
|||||||
|
|
||||||
PlayerInfo
|
PlayerInfo
|
||||||
WorldInfo
|
WorldInfo
|
||||||
isSpawn bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPlayer create a new Player manager.
|
||||||
func NewPlayer(c *bot.Client, settings Settings, events EventsListener) *Player {
|
func NewPlayer(c *bot.Client, settings Settings, events EventsListener) *Player {
|
||||||
p := &Player{c: c, Settings: settings}
|
p := &Player{c: c, Settings: settings}
|
||||||
c.Events.AddListener(
|
c.Events.AddListener(
|
||||||
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: p.handleLoginPacket},
|
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: p.handleLoginPacket},
|
||||||
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: p.handleKeepAlivePacket},
|
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: p.handleKeepAlivePacket},
|
||||||
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundPlayerPosition, F: p.handlePlayerPosition},
|
|
||||||
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: p.handleRespawnPacket},
|
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: p.handleRespawnPacket},
|
||||||
)
|
)
|
||||||
events.attach(p)
|
events.attach(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Respawn is used to send a respawn packet to the server.
|
||||||
|
// Typically, you should call this method when the player is dead (in the [Death] event handler).
|
||||||
func (p *Player) Respawn() error {
|
func (p *Player) Respawn() error {
|
||||||
const PerformRespawn = 0
|
const PerformRespawn = 0
|
||||||
|
|
||||||
@ -61,6 +61,19 @@ func (p *Player) Respawn() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcceptTeleportation is used to send a teleport confirmation packet to the server.
|
||||||
|
// Typically, you should call this method when received a ClientboundPlayerPosition packet (in the [Teleported] event handler).
|
||||||
|
func (p *Player) AcceptTeleportation(teleportID pk.VarInt) error {
|
||||||
|
err := p.c.Conn.WritePacket(pk.Marshal(
|
||||||
|
packetid.ServerboundAcceptTeleportation,
|
||||||
|
teleportID,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return Error{err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,49 @@ import (
|
|||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EventsListener is a collection of event handlers.
|
||||||
|
// Fill the fields with your handler functions and pass it to [NewPlayer] to create the Player manager.
|
||||||
|
// For the event you don't want to handle, just leave it nil.
|
||||||
type EventsListener struct {
|
type EventsListener struct {
|
||||||
|
// GameStart event is called when the login process is completed and the player is ready to play.
|
||||||
|
//
|
||||||
|
// If you want to do some action when the bot joined the server like sending a chat message,
|
||||||
|
// this event is the right place to do it.
|
||||||
GameStart func() error
|
GameStart func() error
|
||||||
|
|
||||||
|
// Disconnect event is called before the server disconnects your client.
|
||||||
|
// When the server willfully disconnects the client, it will send a ClientboundDisconnect packet and tell you why.
|
||||||
|
// On vanilla client, the reason is displayed in the disconnect screen.
|
||||||
|
//
|
||||||
|
// This information may be very useful for debugging, and generally you should record it into the log.
|
||||||
|
//
|
||||||
|
// If the connection is disconnected due to network reasons or the client's initiative,
|
||||||
|
// this event will not be triggered.
|
||||||
Disconnect func(reason chat.Message) error
|
Disconnect func(reason chat.Message) error
|
||||||
HealthChange func(health float32) error
|
|
||||||
|
// HealthChange event is called when the player's health or food changed.
|
||||||
|
HealthChange func(health float32, foodLevel int32, foodSaturation float32) error
|
||||||
|
|
||||||
|
// Death event is a special case of HealthChange.
|
||||||
|
// It will be called after HealthChange handler called (if it isn't nil)
|
||||||
|
// when the player's health is less than or equal to 0.
|
||||||
|
//
|
||||||
|
// Typically, you should call [Player.Respawn] in this handler.
|
||||||
Death func() error
|
Death func() error
|
||||||
|
|
||||||
|
// Teleported event is called when the server think the player position in the client side is wrong,
|
||||||
|
// and send a ClientboundPlayerPosition packet to correct the client.
|
||||||
|
//
|
||||||
|
// Typically, you need to do two things in this handler:
|
||||||
|
// - Update the player's position and rotation you tracked to the correct position.
|
||||||
|
// - Call [Player.AcceptTeleportation] to send a teleport confirmation packet to the server.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// For more information, see https://wiki.vg/Protocol#Synchronize_Player_Position
|
||||||
|
Teleported func(x, y, z float64, yaw, pitch float32, flags byte, teleportID int32, dismountVehicle bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// attach your event listener to the client.
|
// attach your event listener to the client.
|
||||||
@ -26,6 +64,9 @@ func (e EventsListener) attach(p *Player) {
|
|||||||
if e.HealthChange != nil || e.Death != nil {
|
if e.HealthChange != nil || e.Death != nil {
|
||||||
attachUpdateHealth(p.c, e.HealthChange, e.Death)
|
attachUpdateHealth(p.c, e.HealthChange, e.Death)
|
||||||
}
|
}
|
||||||
|
if e.Teleported != nil {
|
||||||
|
attachPlayerPosition(p.c, e.Teleported)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func attachJoinGameHandler(c *bot.Client, handler func() error) {
|
func attachJoinGameHandler(c *bot.Client, handler func() error) {
|
||||||
@ -50,20 +91,20 @@ func attachDisconnect(c *bot.Client, handler func(reason chat.Message) error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func attachUpdateHealth(c *bot.Client, healthChangeHandler func(health float32) error, deathHandler func() error) {
|
func attachUpdateHealth(c *bot.Client, healthChangeHandler func(health float32, food int32, saturation float32) error, deathHandler func() error) {
|
||||||
c.Events.AddListener(bot.PacketHandler{
|
c.Events.AddListener(bot.PacketHandler{
|
||||||
Priority: 64, ID: packetid.ClientboundSetHealth,
|
Priority: 64, ID: packetid.ClientboundSetHealth,
|
||||||
F: func(p pk.Packet) error {
|
F: func(p pk.Packet) error {
|
||||||
var health pk.Float
|
var health pk.Float
|
||||||
var food pk.VarInt
|
var food pk.VarInt
|
||||||
var foodSaturation pk.Float
|
var saturation pk.Float
|
||||||
|
|
||||||
if err := p.Scan(&health, &food, &foodSaturation); err != nil {
|
if err := p.Scan(&health, &food, &saturation); err != nil {
|
||||||
return Error{err}
|
return Error{err}
|
||||||
}
|
}
|
||||||
var healthChangeErr, deathErr error
|
var healthChangeErr, deathErr error
|
||||||
if healthChangeHandler != nil {
|
if healthChangeHandler != nil {
|
||||||
healthChangeErr = healthChangeHandler(float32(health))
|
healthChangeErr = healthChangeHandler(float32(health), int32(food), float32(saturation))
|
||||||
}
|
}
|
||||||
if deathHandler != nil && health <= 0 {
|
if deathHandler != nil && health <= 0 {
|
||||||
deathErr = deathHandler()
|
deathErr = deathHandler()
|
||||||
@ -76,6 +117,25 @@ 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, dismountVehicle bool) error) {
|
||||||
|
c.Events.AddListener(bot.PacketHandler{
|
||||||
|
Priority: 64, ID: packetid.ClientboundPlayerPosition,
|
||||||
|
F: func(p pk.Packet) error {
|
||||||
|
var (
|
||||||
|
X, Y, Z pk.Double
|
||||||
|
Yaw, Pitch pk.Float
|
||||||
|
Flags pk.Byte
|
||||||
|
TeleportID pk.VarInt
|
||||||
|
DismountVehicle pk.Boolean
|
||||||
|
)
|
||||||
|
if err := p.Scan(&X, &Y, &Z, &Yaw, &Pitch, &Flags, &TeleportID, &DismountVehicle); err != nil {
|
||||||
|
return Error{err}
|
||||||
|
}
|
||||||
|
return handler(float64(X), float64(Y), float64(Z), float32(Yaw), float32(Pitch), byte(Flags), int32(TeleportID), bool(DismountVehicle))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type updateHealthError struct {
|
type updateHealthError struct {
|
||||||
healthChangeErr, deathErr error
|
healthChangeErr, deathErr error
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ type WorldInfo struct {
|
|||||||
MaxPlayers int32 // Was once used by the client to draw the player list, but now is ignored.
|
MaxPlayers int32 // Was once used by the client to draw the player list, but now is ignored.
|
||||||
ViewDistance int32 // Render distance (2-32).
|
ViewDistance int32 // Render distance (2-32).
|
||||||
SimulationDistance int32 // The distance that the client will process specific things, such as entities.
|
SimulationDistance int32 // The distance that the client will process specific things, such as entities.
|
||||||
ReducedDebugInfo bool // If true, a Notchian client shows reduced information on the debug screen. For servers in development, this should almost always be false.
|
ReducedDebugInfo bool // If true, a vanilla client shows reduced information on the debug screen. For servers in development, this should almost always be false.
|
||||||
EnableRespawnScreen bool // Set to false when the doImmediateRespawn gamerule is true.
|
EnableRespawnScreen bool // Set to false when the doImmediateRespawn gamerule is true.
|
||||||
IsDebug bool // True if the world is a debug mode world; debug mode worlds cannot be modified and have predefined blocks.
|
IsDebug bool // True if the world is a debug mode world; debug mode worlds cannot be modified and have predefined blocks.
|
||||||
IsFlat bool // True if the world is a superflat world; flat worlds have different void fog and a horizon at y=0 instead of y=63.
|
IsFlat bool // True if the world is a superflat world; flat worlds have different void fog and a horizon at y=0 instead of y=63.
|
||||||
@ -31,11 +31,6 @@ type PlayerInfo struct {
|
|||||||
PrevGamemode int8 // Previous Gamemode
|
PrevGamemode int8 // Previous Gamemode
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServInfo contains information about the server implementation.
|
|
||||||
type ServInfo struct {
|
|
||||||
Brand string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Player) handleLoginPacket(packet pk.Packet) error {
|
func (p *Player) handleLoginPacket(packet pk.Packet) error {
|
||||||
err := packet.Scan(
|
err := packet.Scan(
|
||||||
(*pk.Int)(&p.EID),
|
(*pk.Int)(&p.EID),
|
||||||
|
@ -20,41 +20,3 @@ func (p *Player) handleKeepAlivePacket(packet pk.Packet) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) handlePlayerPosition(packet pk.Packet) error {
|
|
||||||
var (
|
|
||||||
X, Y, Z pk.Double
|
|
||||||
Yaw, Pitch pk.Float
|
|
||||||
Flags pk.Byte
|
|
||||||
TeleportID pk.VarInt
|
|
||||||
DismountVehicle pk.Boolean
|
|
||||||
)
|
|
||||||
if err := packet.Scan(&X, &Y, &Z, &Yaw, &Pitch, &Flags, &TeleportID, &DismountVehicle); err != nil {
|
|
||||||
return Error{err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Teleport Confirm
|
|
||||||
err := p.c.Conn.WritePacket(pk.Marshal(
|
|
||||||
packetid.ServerboundAcceptTeleportation,
|
|
||||||
TeleportID,
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
return Error{err}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.isSpawn {
|
|
||||||
// PlayerPositionAndRotation to confirm the spawn position
|
|
||||||
err = p.c.Conn.WritePacket(pk.Marshal(
|
|
||||||
packetid.ServerboundMovePlayerPosRot,
|
|
||||||
X, Y-1.62, Z,
|
|
||||||
Yaw, Pitch,
|
|
||||||
pk.Boolean(true),
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
return Error{err}
|
|
||||||
}
|
|
||||||
p.isSpawn = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The Manager is used to receive and send chat messages.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
c *bot.Client
|
c *bot.Client
|
||||||
p *basic.Player
|
p *basic.Player
|
||||||
@ -26,6 +27,7 @@ type Manager struct {
|
|||||||
sign.SignatureCache
|
sign.SignatureCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns a new chat manager.
|
||||||
func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events EventsHandler) *Manager {
|
func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events EventsHandler) *Manager {
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
c: c,
|
c: c,
|
||||||
@ -37,7 +39,7 @@ func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events Event
|
|||||||
if events.SystemChat != nil {
|
if events.SystemChat != nil {
|
||||||
c.Events.AddListener(bot.PacketHandler{
|
c.Events.AddListener(bot.PacketHandler{
|
||||||
Priority: 64, ID: packetid.ClientboundSystemChat,
|
Priority: 64, ID: packetid.ClientboundSystemChat,
|
||||||
F: m.handleSystemMessage,
|
F: m.handleSystemChat,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if events.PlayerChatMessage != nil {
|
if events.PlayerChatMessage != nil {
|
||||||
@ -55,7 +57,7 @@ func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events Event
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) handleSystemMessage(p pk.Packet) error {
|
func (m *Manager) handleSystemChat(p pk.Packet) error {
|
||||||
var msg chat.Message
|
var msg chat.Message
|
||||||
var overlay pk.Boolean
|
var overlay pk.Boolean
|
||||||
if err := p.Scan(&msg, &overlay); err != nil {
|
if err := p.Scan(&msg, &overlay); err != nil {
|
||||||
@ -149,7 +151,7 @@ func (m *Manager) handleDisguisedChat(packet pk.Packet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage send chat message to server.
|
// SendMessage send chat message to server.
|
||||||
// Currently only support offline-mode or "Not Secure" chat
|
// Doesn't support sending message with signature currently.
|
||||||
func (m *Manager) SendMessage(msg string) error {
|
func (m *Manager) SendMessage(msg string) error {
|
||||||
if len(msg) > 256 {
|
if len(msg) > 256 {
|
||||||
return errors.New("message length greater than 256")
|
return errors.New("message length greater than 256")
|
||||||
|
@ -2,8 +2,28 @@ package msg
|
|||||||
|
|
||||||
import "github.com/Tnze/go-mc/chat"
|
import "github.com/Tnze/go-mc/chat"
|
||||||
|
|
||||||
|
// EventsHandler is a collection of event handlers.
|
||||||
|
// Fill the fields with your handler functions and pass this struct to [New] to create the msg manager.
|
||||||
|
// The handler functions will be called when the corresponding event is triggered.
|
||||||
|
// Leave the fields as nil if you don't want to handle the event.
|
||||||
type EventsHandler struct {
|
type EventsHandler struct {
|
||||||
|
// SystemChat handles messages sent by gaming system.
|
||||||
|
//
|
||||||
|
// In vanilla client:
|
||||||
|
// If overlay is false, the message will be displayed in the chat box.
|
||||||
|
// If overlay is true, the message will be displayed on the top of the hot-bar.
|
||||||
SystemChat func(msg chat.Message, overlay bool) error
|
SystemChat func(msg chat.Message, overlay bool) error
|
||||||
|
|
||||||
|
// PlayerChatMessage handles messages sent by players.
|
||||||
|
//
|
||||||
|
// Message signing system is added in 1.19. The message and its context could be signed by the player's private key.
|
||||||
|
// The manager tries to verify the message signature through the player's public key,
|
||||||
|
// and return the result as validated boolean.
|
||||||
PlayerChatMessage func(msg chat.Message, validated bool) error
|
PlayerChatMessage func(msg chat.Message, validated bool) error
|
||||||
|
|
||||||
|
// DisguisedChat handles DisguisedChat message.
|
||||||
|
//
|
||||||
|
// DisguisedChat message used to send system chat.
|
||||||
|
// Now it is used to send messages from "/say" command from server console.
|
||||||
DisguisedChat func(msg chat.Message) error
|
DisguisedChat func(msg chat.Message) error
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
// Package playerlist contains a PlayerList struct that used to manage player information.
|
||||||
|
//
|
||||||
|
// The [PlayerList] contains a list of [PlayerInfo] which is received from server when client join.
|
||||||
|
// The playerlist contains every players' information of name, display name, uuid, gamemode, latency, public key, etc.
|
||||||
|
// And can be used to render the "TAB List". Other packages may also require playerlist to work,
|
||||||
|
// for example, the bot/msg package.
|
||||||
package playerlist
|
package playerlist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
Reference in New Issue
Block a user