-
Notifications
You must be signed in to change notification settings - Fork 113
/
chat.go
182 lines (163 loc) · 4.48 KB
/
chat.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package msg
import (
"crypto/rand"
"encoding/binary"
"errors"
"time"
"github.com/google/uuid"
"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"
pk "github.com/Tnze/go-mc/net/packet"
)
// The Manager is used to receive and send chat messages.
// This implement require c.ConfigHandler set to be *bot.ConfigData
type Manager struct {
c *bot.Client
p *basic.Player
pl *playerlist.PlayerList
events EventsHandler
sign.SignatureCache
}
// New returns a new chat manager.
func New(c *bot.Client, p *basic.Player, pl *playerlist.PlayerList, events EventsHandler) *Manager {
m := &Manager{
c: c,
p: p,
pl: pl,
events: events,
SignatureCache: sign.NewSignatureCache(),
}
if events.SystemChat != nil {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundSystemChat,
F: m.handleSystemChat,
})
}
if events.PlayerChatMessage != nil {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundPlayerChat,
F: m.handlePlayerChat,
})
}
if events.DisguisedChat != nil {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundDisguisedChat,
F: m.handleDisguisedChat,
})
}
return m
}
func (m *Manager) handleSystemChat(p pk.Packet) error {
var msg chat.Message
var overlay pk.Boolean
if err := p.Scan(&msg, &overlay); err != nil {
return err
}
return m.events.SystemChat(msg, bool(overlay))
}
func (m *Manager) handlePlayerChat(packet pk.Packet) error {
var (
sender pk.UUID
index pk.VarInt
signature pk.Option[sign.Signature, *sign.Signature]
body sign.PackedMessageBody
unsignedContent pk.Option[chat.Message, *chat.Message]
filter sign.FilterMask
chatType chat.Type
)
if err := packet.Scan(&sender, &index, &signature, &body, &unsignedContent, &filter, &chatType); err != nil {
return err
}
unpackedMsg, err := body.Unpack(&m.SignatureCache)
if err != nil {
return InvalidChatPacket
}
senderInfo, ok := m.pl.PlayerInfos[uuid.UUID(sender)]
if !ok {
return InvalidChatPacket
}
ct := m.c.ConfigHandler.(*bot.ConfigData).Registries.ChatType.FindByID(chatType.ID)
if ct == nil {
return InvalidChatPacket
}
var message sign.Message
if senderInfo.ChatSession != nil {
message.Prev = sign.Prev{
Index: int(index),
Sender: uuid.UUID(sender),
Session: senderInfo.ChatSession.SessionID,
}
} else {
message.Prev = sign.Prev{
Index: 0,
Sender: uuid.UUID(sender),
Session: uuid.Nil,
}
}
message.Signature = signature.Pointer()
message.MessageBody = unpackedMsg
message.Unsigned = unsignedContent.Pointer()
message.FilterMask = filter
var validated bool
if senderInfo.ChatSession != nil {
if !senderInfo.ChatSession.VerifyAndUpdate(&message) {
return ValidationFailed
}
validated = true
// store signature into signatureCache
m.PopOrInsert(signature.Pointer(), message.LastSeen)
}
var content chat.Message
if unsignedContent.Has {
content = unsignedContent.Val
} else {
content = chat.Text(body.PlainMsg)
}
msg := chatType.Decorate(content, &ct.Chat)
return m.events.PlayerChatMessage(msg, validated)
}
func (m *Manager) handleDisguisedChat(packet pk.Packet) error {
var (
message chat.Message
chatType chat.Type
)
if err := packet.Scan(&message, &chatType); err != nil {
return err
}
ct := m.c.ConfigHandler.(*bot.ConfigData).Registries.ChatType.FindByID(chatType.ID)
if ct == nil {
return InvalidChatPacket
}
msg := chatType.Decorate(message, &ct.Chat)
return m.events.DisguisedChat(msg)
}
// SendMessage send chat message to server.
// Doesn't support sending message with signature currently.
func (m *Manager) SendMessage(msg string) error {
if len(msg) > 256 {
return errors.New("message length greater than 256")
}
var salt int64
if err := binary.Read(rand.Reader, binary.BigEndian, &salt); err != nil {
return err
}
err := m.c.Conn.WritePacket(pk.Marshal(
packetid.ServerboundChat,
pk.String(msg),
pk.Long(time.Now().UnixMilli()),
pk.Long(salt),
pk.Boolean(false), // signature
sign.HistoryUpdate{
Acknowledged: pk.NewFixedBitSet(20),
},
))
return err
}
var (
InvalidChatPacket = errors.New("invalid chat packet")
ValidationFailed error = bot.DisconnectErr(chat.TranslateMsg("multiplayer.disconnect.chat_validation_failed"))
)