diff --git a/bot/msg/chat.go b/bot/msg/chat.go index a0ab55c..24b4f03 100644 --- a/bot/msg/chat.go +++ b/bot/msg/chat.go @@ -30,20 +30,24 @@ func attachPlayerMsg(c *bot.Client, p *basic.Player, handler func(msg chat.Messa bot.PacketHandler{ Priority: 64, ID: packetid.ClientboundPlayerChat, F: func(packet pk.Packet) error { - var message sign.PlayerMessage - var chatType chat.Type - if err := packet.Scan(&message, &chatType); err != nil { + var ( + sender pk.UUID + index pk.VarInt + signature pk.Option[sign.Signature, *sign.Signature] + body sign.MessageBody + 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 } var content chat.Message - if message.MessageBody.DecoratedMsg != nil { - data, _ := message.MessageBody.DecoratedMsg.MarshalJSON() - if err := content.UnmarshalJSON(data); err != nil { - return err - } + if unsignedContent.Has { + content = unsignedContent.Val } else { - content = chat.Text(message.MessageBody.PlainMsg) + content = chat.Text(body.PlainMsg) } ct := p.WorldInfo.RegistryCodec.ChatType.FindByID(chatType.ID) @@ -74,11 +78,9 @@ func (m *Manager) SendMessage(msg string) error { pk.String(msg), pk.Long(time.Now().UnixMilli()), pk.Long(salt), - pk.ByteArray{}, - pk.Boolean(false), - pk.Array([]sign.HistoryMessage{}), - pk.Option[sign.HistoryMessage, *sign.HistoryMessage]{ - Has: false, + pk.Boolean(false), // signature + sign.HistoryUpdate{ + Acknowledged: pk.NewFixedBitSet(20), }, )) return err diff --git a/chat/sign/sign.go b/chat/sign/sign.go index d7b15b4..3fcbfc3 100644 --- a/chat/sign/sign.go +++ b/chat/sign/sign.go @@ -1,122 +1,122 @@ package sign import ( - "crypto/sha256" - "encoding/binary" - "encoding/json" "io" "time" - "github.com/Tnze/go-mc/chat" pk "github.com/Tnze/go-mc/net/packet" "github.com/google/uuid" ) -type MessageHeader struct { - PrevSignature []byte - Sender uuid.UUID -} - -func (m *MessageHeader) WriteTo(w io.Writer) (n int64, err error) { - hasSignature := pk.Boolean(len(m.PrevSignature) > 0) - n, err = hasSignature.WriteTo(w) - if err != nil { - return - } - if hasSignature { - n2, err := pk.ByteArray(m.PrevSignature).WriteTo(w) - n += n2 - if err != nil { - return n, err - } - } - n3, err := pk.UUID(m.Sender).WriteTo(w) - return n + n3, err -} - -func (m *MessageHeader) ReadFrom(r io.Reader) (n int64, err error) { - var hasSignature pk.Boolean - n, err = hasSignature.ReadFrom(r) - if err != nil { - return - } - if hasSignature { - n2, err := (*pk.ByteArray)(&m.PrevSignature).ReadFrom(r) - n += n2 - if err != nil { - return n, err - } - } - n3, err := (*pk.UUID)(&m.Sender).ReadFrom(r) - return n + n3, err -} - -func (m *MessageHeader) Hash(bodyHash []byte) []byte { - hash := sha256.New() - if m.PrevSignature != nil { - hash.Write(m.PrevSignature) - } - hash.Write(m.Sender[:]) - hash.Write(bodyHash) - return hash.Sum(nil) -} - type MessageBody struct { - PlainMsg string - DecoratedMsg json.RawMessage - Timestamp time.Time - Salt int64 - History []HistoryMessage + PlainMsg string + Timestamp time.Time + Salt int64 + LastSeen []PackedSignature } func (m *MessageBody) WriteTo(w io.Writer) (n int64, err error) { return pk.Tuple{ pk.String(m.PlainMsg), - pk.Boolean(m.DecoratedMsg != nil), - pk.Opt{ - Has: m.DecoratedMsg != nil, - Field: pk.ByteArray(m.DecoratedMsg), - }, pk.Long(m.Timestamp.UnixMilli()), pk.Long(m.Salt), - pk.Array(&m.History), + pk.Array(m.LastSeen), }.WriteTo(w) } func (m *MessageBody) ReadFrom(r io.Reader) (n int64, err error) { - var hasContent pk.Boolean var timestamp pk.Long n, err = pk.Tuple{ (*pk.String)(&m.PlainMsg), - &hasContent, - pk.Opt{ - Has: &hasContent, - Field: (*pk.ByteArray)(&m.DecoratedMsg), - }, ×tamp, (*pk.Long)(&m.Salt), - pk.Array(&m.History), + pk.Array(&m.LastSeen), }.ReadFrom(r) m.Timestamp = time.UnixMilli(int64(timestamp)) return } -func (m *MessageBody) Hash() []byte { - hash := sha256.New() - _ = binary.Write(hash, binary.BigEndian, m.Salt) - _ = binary.Write(hash, binary.BigEndian, m.Timestamp.Unix()) - hash.Write([]byte(m.PlainMsg)) - hash.Write([]byte{70}) - if m.DecoratedMsg != nil { - decorated, _ := m.DecoratedMsg.MarshalJSON() - hash.Write(decorated) +type HistoryMessage struct { + Sender uuid.UUID + Signature []byte +} + +func (p *HistoryMessage) ReadFrom(r io.Reader) (n int64, err error) { + n, err = (*pk.UUID)(&p.Sender).ReadFrom(r) + if err != nil { + return } - for _, v := range m.History { - hash.Write([]byte{70}) - hash.Write(v.Sender[:]) - hash.Write(v.Signature) + n2, err := (*pk.ByteArray)(&p.Signature).ReadFrom(r) + return n + n2, err +} + +func (p HistoryMessage) WriteTo(w io.Writer) (n int64, err error) { + n, err = pk.UUID(p.Sender).WriteTo(w) + if err != nil { + return + } + n2, err := pk.ByteArray(p.Signature).WriteTo(w) + return n + n2, err +} + +type HistoryUpdate struct { + Offset pk.VarInt + Acknowledged pk.FixedBitSet // n == 20 +} + +func (h HistoryUpdate) WriteTo(w io.Writer) (n int64, err error) { + return pk.Tuple{h.Offset, h.Acknowledged}.WriteTo(w) +} + +func (h *HistoryUpdate) ReadFrom(r io.Reader) (n int64, err error) { + return pk.Tuple{&h.Offset, &h.Acknowledged}.ReadFrom(r) +} + +type Signature [256]byte + +func (s Signature) WriteTo(w io.Writer) (n int64, err error) { + n2, err := w.Write(s[:]) + return int64(n2), err +} + +func (s *Signature) ReadFrom(r io.Reader) (n int64, err error) { + n2, err := r.Read(s[:]) + return int64(n2), err +} + +type PackedSignature struct { + ID int32 + *Signature +} + +func (p PackedSignature) WriteTo(w io.Writer) (n int64, err error) { + n1, err := pk.VarInt(p.ID + 1).WriteTo(w) + if err != nil { + return n1, err + } + if p.Signature != nil { + n2, err := w.Write(p.Signature[:]) + return n1 + int64(n2), err + } + return n1, err +} + +func (p PackedSignature) ReadFrom(r io.Reader) (n int64, err error) { + n1, err := (*pk.VarInt)(&p.ID).ReadFrom(r) + if err != nil { + return n1, err + } + + if p.ID == -1 { + if p.Signature == nil { + p.Signature = new(Signature) + } + n2, err := r.Read(p.Signature[:]) + return n1 + int64(n2), err + } else { + p.Signature = nil + return n1, err } - return hash.Sum(nil) } type FilterMask struct { @@ -150,70 +150,3 @@ func (f *FilterMask) ReadFrom(r io.Reader) (n int64, err error) { } return } - -type PlayerMessage struct { - MessageHeader - MessageSignature []byte - MessageBody - UnsignedContent *chat.Message - FilterMask -} - -func (msg *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) { - var hasUnsignedContent pk.Boolean - return pk.Tuple{ - &msg.MessageHeader, - (*pk.ByteArray)(&msg.MessageSignature), - &msg.MessageBody, - &hasUnsignedContent, - pk.Opt{ - Has: &hasUnsignedContent, - Field: func() pk.FieldDecoder { - msg.UnsignedContent = new(chat.Message) - return msg.UnsignedContent - }, - }, - &msg.FilterMask, - }.ReadFrom(r) -} - -func (msg *PlayerMessage) WriteTo(w io.Writer) (n int64, err error) { - return pk.Tuple{ - &msg.MessageHeader, - pk.ByteArray(msg.MessageSignature), - &msg.MessageBody, - pk.Boolean(msg.UnsignedContent != nil), - pk.Opt{ - Has: msg.UnsignedContent != nil, - Field: msg.UnsignedContent, - }, - &msg.FilterMask, - }.WriteTo(w) -} - -func (msg *PlayerMessage) Hash() []byte { - return msg.MessageHeader.Hash(msg.MessageBody.Hash()) -} - -type HistoryMessage struct { - Sender uuid.UUID - Signature []byte -} - -func (p *HistoryMessage) ReadFrom(r io.Reader) (n int64, err error) { - n, err = (*pk.UUID)(&p.Sender).ReadFrom(r) - if err != nil { - return - } - n2, err := (*pk.ByteArray)(&p.Signature).ReadFrom(r) - return n + n2, err -} - -func (p HistoryMessage) WriteTo(w io.Writer) (n int64, err error) { - n, err = pk.UUID(p.Sender).WriteTo(w) - if err != nil { - return - } - n2, err := pk.ByteArray(p.Signature).WriteTo(w) - return n + n2, err -} diff --git a/chat/sign/sign_test.go b/chat/sign/sign_test.go index dd7afe1..b83d2df 100644 --- a/chat/sign/sign_test.go +++ b/chat/sign/sign_test.go @@ -1,55 +1 @@ package sign - -import ( - "bytes" - "crypto" - "crypto/rsa" - "encoding/base64" - "math/big" - "testing" - "time" - - "github.com/google/uuid" -) - -func TestMessageBody_Hash(t *testing.T) { - msg := PlayerMessage{ - MessageHeader: MessageHeader{ - PrevSignature: nil, - Sender: uuid.MustParse("58f6356e-b30c-4811-8bfc-d72a9ee99e73"), - }, - MessageSignature: fromBase64("L+gR1hrubxVdhPAe6J+nQCH4U+jsUvJDJE+dzsL8DJwpHfUwT4dgSwm7mI/u8rVxrjPVial1DU0sPZodaGVqcSqApyK38bJThWpmojYtieT63hcsgAZzhGNG0GUrykEzHIMPvAnO0bEBmOqPKWjp/kDxhUgF1kKgmQmiOb1fTazi9dVxGuepVkFXknhp7aZBvQ4oQ94bRY5lTMoWyvNcs3CUpVdeFuWwIVnRAIn+hQQ5rDBvWTgKpFTOuxcCOf6hbtPOO2HZ7TT0rsM1D1LV0R9oHUqlEe4nB0E/vT3GdcplSQTSWc7dDmwTjB+wFeGxrNjFP3FEt3he6a+8Q1svlw=="), - MessageBody: MessageBody{ - PlainMsg: "123", - DecoratedMsg: nil, - Timestamp: time.Unix(1669990398, 750000000), - Salt: -5503869105027681791, - History: nil, - }, - UnsignedContent: nil, - FilterMask: FilterMask{}, - } - hash := msg.MessageBody.Hash() - want := []byte{ - 0x40, 0x2f, 0x63, 0xf1, 0x41, 0x64, 0x83, 0xea, - 0x64, 0xbc, 0xe1, 0xab, 0x4f, 0xa1, 0xdd, 0xcf, - 0x31, 0x6b, 0xdf, 0x9, 0xd3, 0xe3, 0x0, 0xed, - 0xd9, 0x9d, 0x61, 0xb2, 0xfe, 0xe1, 0x23, 0x38, - } - if !bytes.Equal(hash, want) { - t.Errorf("hash not match: want %v, got: %v", want, hash) - } - N := new(big.Int) - N.SetString("19764335557512872060688171398766153113124870942516110890430525316014258628424563821100465499350547856280308462415364939681918846480558609475927282823778386281239015421014618416397562105532153533222767841904849714784545929150813290491806771374240538006775842793512508846836865954304676946257326405255232806869270545112507181469911882584371001804731679283550070980294409246775152428394531383836676761395396452034288295355452943368136472112108083541314359681019016586668426966809565492641015821429276348346645505643438759550398087660394335140584223095644803819139358171070866744685891950091018380254474327892983538632197", 10) - if err := rsa.VerifyPKCS1v15(&rsa.PublicKey{N: N, E: 65537}, crypto.SHA256, msg.Hash(), msg.MessageSignature); err != nil { - t.Error(err) - } -} - -func fromBase64(s string) []byte { - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - panic(err) - } - return b -} diff --git a/examples/daze/daze.go b/examples/daze/daze.go index 277e78c..7e13aa5 100644 --- a/examples/daze/daze.go +++ b/examples/daze/daze.go @@ -107,9 +107,9 @@ func onDeath() error { func onGameStart() error { log.Println("Game start") - //if err := chatHandler.SendMessage("Hello, world"); err != nil { - // return err - //} + if err := chatHandler.SendMessage("Hello, world"); err != nil { + return err + } return nil // if err isn't nil, HandleGame() will return it. } diff --git a/net/packet/types.go b/net/packet/types.go index 37eb68f..4053e09 100644 --- a/net/packet/types.go +++ b/net/packet/types.go @@ -76,6 +76,9 @@ type ( // BitSet represents Java's BitSet, a list of bits. BitSet []int64 + + // FixedBitSet is a fixed size BitSet + FixedBitSet []byte ) const ( @@ -536,3 +539,34 @@ func (b BitSet) Set(index int, value bool) { b[index/64] &= ^(1 << (index % 64)) } } + +// NewFixedBitSet make a [FixedBitSet] which can store n bits at least. +// If n <= 0, return nil +func NewFixedBitSet(n int64) FixedBitSet { + if n <= 0 { + return nil + } + return make(FixedBitSet, (n-1)/8+1) +} + +func (f FixedBitSet) WriteTo(w io.Writer) (n int64, err error) { + n2, err := w.Write(f) + return int64(n2), err +} + +func (f FixedBitSet) ReadFrom(r io.Reader) (n int64, err error) { + n2, err := r.Read(f) + return int64(n2), err +} + +func (f FixedBitSet) Get(index int) bool { + return (f[index/8] & (1 << (index % 8))) != 0 +} + +func (f FixedBitSet) Set(index int, value bool) { + if value { + f[index/8] |= 1 << (index % 8) + } else { + f[index/8] &= ^(1 << (index % 8)) + } +} diff --git a/server/client.go b/server/client.go index 8b29207..4b19950 100644 --- a/server/client.go +++ b/server/client.go @@ -3,8 +3,8 @@ package server import ( "strconv" - "github.com/Tnze/go-mc/internal/queue" pk "github.com/Tnze/go-mc/net/packet" + "github.com/Tnze/go-mc/net/queue" ) // Packet758 is a packet in protocol 757.