From 77857a1a8504c4b02756d229d3d90058b81ed245 Mon Sep 17 00:00:00 2001 From: Tnze Date: Fri, 30 Dec 2022 01:09:08 +0800 Subject: [PATCH] Add playerinfo recorder for chat message --- bot/msg/chat.go | 9 ++- bot/playerlist/playerlist.go | 137 ++++++++++++++++++++++++++++++++++ examples/autofish/autofish.go | 7 +- examples/daze/daze.go | 5 +- server/auth/auth.go | 40 +++------- yggdrasil/user/auth.go | 47 ++++++++++++ 6 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 bot/playerlist/playerlist.go create mode 100644 yggdrasil/user/auth.go diff --git a/bot/msg/chat.go b/bot/msg/chat.go index 24b4f03..5dd0ebd 100644 --- a/bot/msg/chat.go +++ b/bot/msg/chat.go @@ -9,6 +9,7 @@ import ( "github.com/Tnze/go-mc/bot" "github.com/Tnze/go-mc/bot/basic" + "github.com/Tnze/go-mc/bot/playerlist" "github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/chat/sign" "github.com/Tnze/go-mc/data/packetid" @@ -20,12 +21,12 @@ type Manager struct { p *basic.Player } -func New(c *bot.Client, p *basic.Player, events EventsHandler) *Manager { - attachPlayerMsg(c, p, events.PlayerChatMessage) +func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events EventsHandler) *Manager { + attachPlayerMsg(c, p, pl, events.PlayerChatMessage) return &Manager{c, p} } -func attachPlayerMsg(c *bot.Client, p *basic.Player, handler func(msg chat.Message) error) { +func attachPlayerMsg(c *bot.Client, p *basic.Player, _ *playerlist.PlayerList, handler func(msg chat.Message) error) { c.Events.AddListener( bot.PacketHandler{ Priority: 64, ID: packetid.ClientboundPlayerChat, @@ -85,3 +86,5 @@ func (m *Manager) SendMessage(msg string) error { )) return err } + +var InvalidChatPacket = errors.New("invalid chat packet") diff --git a/bot/playerlist/playerlist.go b/bot/playerlist/playerlist.go new file mode 100644 index 0000000..298ee51 --- /dev/null +++ b/bot/playerlist/playerlist.go @@ -0,0 +1,137 @@ +package playerlist + +import ( + "bytes" + "errors" + + "github.com/google/uuid" + + "github.com/Tnze/go-mc/bot" + "github.com/Tnze/go-mc/chat" + "github.com/Tnze/go-mc/data/packetid" + pk "github.com/Tnze/go-mc/net/packet" + "github.com/Tnze/go-mc/yggdrasil/user" +) + +type PlayerList struct { + PlayerInfos map[uuid.UUID]*PlayerInfo +} + +func New(c *bot.Client) *PlayerList { + pl := PlayerList{ + PlayerInfos: make(map[uuid.UUID]*PlayerInfo), + } + c.Events.AddListener( + bot.PacketHandler{ + Priority: 64, ID: packetid.ClientboundPlayerInfoUpdate, + F: pl.handlePlayerInfoUpdatePacket, + }, + bot.PacketHandler{ + Priority: 64, ID: packetid.ClientboundPlayerInfoRemove, + F: pl.handlePlayerInfoRemovePacket, + }, + ) + return &pl +} + +func (pl *PlayerList) handlePlayerInfoUpdatePacket(p pk.Packet) error { + r := bytes.NewReader(p.Data) + + action := pk.NewFixedBitSet(6) + if _, err := action.ReadFrom(r); err != nil { + return err + } + + var length pk.VarInt + if _, err := length.ReadFrom(r); err != nil { + return err + } + + for i := 0; i < int(length); i++ { + var id pk.UUID + if _, err := id.ReadFrom(r); err != nil { + return err + } + + player, ok := pl.PlayerInfos[uuid.UUID(id)] + if !ok { // create new player info if not exist + player = new(PlayerInfo) + pl.PlayerInfos[uuid.UUID(id)] = player + } + + // add player + if action.Get(0) { + var name pk.String + var properties []user.Property + if _, err := (pk.Tuple{&name, pk.Array(&properties)}).ReadFrom(r); err != nil { + return err + } + player.GameProfile = GameProfile{ + ID: uuid.UUID(id), + Name: string(name), + Properties: properties, + } + } + // initialize chat + if action.Get(1) { + return errors.New("unsupported initialize chat yet") + } + // update gamemode + if action.Get(2) { + var gamemode pk.VarInt + if _, err := gamemode.ReadFrom(r); err != nil { + return err + } + player.Gamemode = int32(gamemode) + } + // update listed + if action.Get(3) { + var listed pk.Boolean + if _, err := listed.ReadFrom(r); err != nil { + return err + } + player.Listed = bool(listed) + } + // update latency + if action.Get(4) { + var latency pk.VarInt + if _, err := latency.ReadFrom(r); err != nil { + return err + } + player.Latency = int32(latency) + } + // display name + if action.Get(5) { + var displayName pk.Option[chat.Message, *chat.Message] + if _, err := displayName.ReadFrom(r); err != nil { + return err + } + if displayName.Has { + player.DisplayName = &displayName.Val + } else { + player.DisplayName = nil + } + } + } + + return nil +} + +func (pl *PlayerList) handlePlayerInfoRemovePacket(p pk.Packet) error { + return nil +} + +type PlayerInfo struct { + GameProfile + // chatSession + Gamemode int32 + Latency int32 + Listed bool + DisplayName *chat.Message +} + +type GameProfile struct { + ID uuid.UUID + Name string + Properties []user.Property +} diff --git a/examples/autofish/autofish.go b/examples/autofish/autofish.go index 70f8fe6..4325019 100644 --- a/examples/autofish/autofish.go +++ b/examples/autofish/autofish.go @@ -8,7 +8,6 @@ import ( "github.com/Tnze/go-mc/bot" "github.com/Tnze/go-mc/bot/basic" - "github.com/Tnze/go-mc/bot/msg" "github.com/Tnze/go-mc/chat" _ "github.com/Tnze/go-mc/data/lang/en-us" "github.com/Tnze/go-mc/data/packetid" @@ -18,9 +17,8 @@ import ( const timeout = 45 var ( - c *bot.Client - p *basic.Player - bc *msg.Manager + c *bot.Client + p *basic.Player watch chan time.Time ) @@ -34,7 +32,6 @@ func main() { Disconnect: onDisconnect, Death: onDeath, }) - bc = msg.New(c, p, msg.EventsHandler{PlayerChatMessage: onChatMsg}) // Register event handlers diff --git a/examples/daze/daze.go b/examples/daze/daze.go index 7e13aa5..8b143b7 100644 --- a/examples/daze/daze.go +++ b/examples/daze/daze.go @@ -13,6 +13,7 @@ import ( "github.com/Tnze/go-mc/bot" "github.com/Tnze/go-mc/bot/basic" "github.com/Tnze/go-mc/bot/msg" + "github.com/Tnze/go-mc/bot/playerlist" "github.com/Tnze/go-mc/bot/screen" "github.com/Tnze/go-mc/bot/world" "github.com/Tnze/go-mc/chat" @@ -31,6 +32,7 @@ var ( var ( client *bot.Client player *basic.Player + playerList *playerlist.PlayerList chatHandler *msg.Manager worldManager *world.World screenManager *screen.Manager @@ -52,7 +54,8 @@ func main() { HealthChange: onHealthChange, Death: onDeath, }) - chatHandler = msg.New(client, player, msg.EventsHandler{ + playerList = playerlist.New(client) + chatHandler = msg.New(client, player, playerList, msg.EventsHandler{ PlayerChatMessage: onPlayerMsg, }) worldManager = world.NewWorld(client, player, world.EventsListener{ diff --git a/server/auth/auth.go b/server/auth/auth.go index 97997c3..cfec992 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -15,11 +15,13 @@ import ( "net/http" "strings" + "github.com/google/uuid" + "github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/net" "github.com/Tnze/go-mc/net/CFB8" pk "github.com/Tnze/go-mc/net/packet" - "github.com/google/uuid" + "github.com/Tnze/go-mc/yggdrasil/user" ) const verifyTokenLen = 16 @@ -171,42 +173,20 @@ func twosComplement(p []byte) []byte { return p } +type ( + Texture = user.Texture + Property = user.Property +) + // Resp is the response of authentication type Resp struct { Name string ID uuid.UUID - Properties []Property -} - -type Property struct { - Name, Value, Signature string -} - -func (p Property) WriteTo(w io.Writer) (n int64, err error) { - return pk.Tuple{ - pk.String(p.Name), - pk.String(p.Value), - pk.Option[pk.String, *pk.String]{ - Has: p.Signature != "", - Val: pk.String(p.Signature), - }, - }.WriteTo(w) -} - -// Texture includes player's skin and cape -type Texture struct { - TimeStamp int64 `json:"timestamp"` - ID uuid.UUID `json:"profileId"` - Name string `json:"profileName"` - Textures struct { - SKIN, CAPE struct { - URL string `json:"url"` - } - } `json:"textures"` + Properties []user.Property } // Texture unmarshal the base64 encoded texture of Resp -func (r *Resp) Texture() (t Texture, err error) { +func (r *Resp) Texture() (t user.Texture, err error) { var texture []byte texture, err = base64.StdEncoding.DecodeString(r.Properties[0].Value) if err != nil { diff --git a/yggdrasil/user/auth.go b/yggdrasil/user/auth.go new file mode 100644 index 0000000..e0268c5 --- /dev/null +++ b/yggdrasil/user/auth.go @@ -0,0 +1,47 @@ +package user + +import ( + "io" + + "github.com/google/uuid" + + pk "github.com/Tnze/go-mc/net/packet" +) + +type Property struct { + Name, Value, Signature string +} + +func (p Property) WriteTo(w io.Writer) (n int64, err error) { + return pk.Tuple{ + pk.String(p.Name), + pk.String(p.Value), + pk.Option[pk.String, *pk.String]{ + Has: p.Signature != "", + Val: pk.String(p.Signature), + }, + }.WriteTo(w) +} + +func (p *Property) ReadFrom(r io.Reader) (n int64, err error) { + var signature pk.Option[pk.String, *pk.String] + n, err = pk.Tuple{ + (*pk.String)(&p.Name), + (*pk.String)(&p.Value), + &signature, + }.ReadFrom(r) + p.Signature = string(signature.Val) + return +} + +// Texture includes player's skin and cape +type Texture struct { + TimeStamp int64 `json:"timestamp"` + ID uuid.UUID `json:"profileId"` + Name string `json:"profileName"` + Textures struct { + SKIN, CAPE struct { + URL string `json:"url"` + } + } `json:"textures"` +}