mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 19:17:38 +08:00
feat: stash client functions
when we restart a client, it takes too long to sync the friend & group lists. This stash functions will help us to reduce sync time when in development. DON'T USE THIS, this is not checked.
This commit is contained in:
parent
e50f46a111
commit
6e33d2fc34
303
client/stash.go
Normal file
303
client/stash.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
// UNCHECKED
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stash will store the data for the client, this will speed up booting
|
||||||
|
// the QQ client but some data may not sync with the server. So it is
|
||||||
|
// recommended to use this in development mode only.
|
||||||
|
|
||||||
|
//go:generate stringer -type=syncMarker -trimprefix=syncMarker
|
||||||
|
type syncMarker int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
syncMarkerNone syncMarker = iota
|
||||||
|
syncMarkerFriendList
|
||||||
|
syncMarkerFriendInfo
|
||||||
|
syncMarkerGroupList
|
||||||
|
syncMarkerGroupInfo
|
||||||
|
syncMarkerGroupMemberList
|
||||||
|
syncMarkerGroupMemberInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
type StashSyncMarkerError struct {
|
||||||
|
marker syncMarker
|
||||||
|
expected syncMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StashSyncMarkerError) Error() string {
|
||||||
|
return "stash sync marker error: expected " + e.expected.String() + ", got " + e.marker.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteStash will write the stash to the given writer.
|
||||||
|
func WriteStash(client *QQClient, writer io.Writer) {
|
||||||
|
var out intWriter
|
||||||
|
w := stashWriter{
|
||||||
|
stringIndex: make(map[string]uint64),
|
||||||
|
}
|
||||||
|
|
||||||
|
w.friendList(client.FriendList)
|
||||||
|
w.groupList(client.GroupList)
|
||||||
|
|
||||||
|
out.uvarint(uint64(w.strings.Len()))
|
||||||
|
out.uvarint(uint64(w.data.Len()))
|
||||||
|
_, _ = io.Copy(&out, &w.strings)
|
||||||
|
_, _ = io.Copy(&out, &w.data)
|
||||||
|
_, _ = io.Copy(writer, &out)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stashWriter struct {
|
||||||
|
data intWriter
|
||||||
|
strings intWriter
|
||||||
|
stringIndex map[string]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stashWriter) string(s string) {
|
||||||
|
off, ok := w.stringIndex[s]
|
||||||
|
if !ok {
|
||||||
|
off = uint64(w.strings.Len())
|
||||||
|
w.strings.uvarint(uint64(len(s)))
|
||||||
|
_, _ = w.strings.WriteString(s)
|
||||||
|
w.stringIndex[s] = off
|
||||||
|
}
|
||||||
|
w.uvarint(off)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stashWriter) sync(marker syncMarker) { w.data.uvarint(uint64(marker)) }
|
||||||
|
func (w *stashWriter) uvarint(v uint64) { w.data.uvarint(v) }
|
||||||
|
func (w *stashWriter) svarint(v int64) { w.data.svarint(v) }
|
||||||
|
|
||||||
|
func (w *stashWriter) int64(v int64) {
|
||||||
|
var buf [8]byte
|
||||||
|
binary.LittleEndian.PutUint64(buf[:], uint64(v))
|
||||||
|
_, _ = w.data.Write(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stashWriter) friendList(list []*FriendInfo) {
|
||||||
|
w.sync(syncMarkerFriendList)
|
||||||
|
w.uvarint(uint64(len(list)))
|
||||||
|
for _, friend := range list {
|
||||||
|
w.sync(syncMarkerFriendInfo)
|
||||||
|
w.int64(friend.Uin)
|
||||||
|
w.string(friend.Nickname)
|
||||||
|
w.string(friend.Remark)
|
||||||
|
w.svarint(int64(friend.FaceId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stashWriter) groupList(list []*GroupInfo) {
|
||||||
|
w.sync(syncMarkerGroupList)
|
||||||
|
w.uvarint(uint64(len(list)))
|
||||||
|
for _, group := range list {
|
||||||
|
w.sync(syncMarkerGroupInfo)
|
||||||
|
w.int64(group.Uin)
|
||||||
|
w.int64(group.Code)
|
||||||
|
w.string(group.Name)
|
||||||
|
w.string(group.Memo)
|
||||||
|
w.int64(group.OwnerUin)
|
||||||
|
w.uvarint(uint64(group.GroupCreateTime))
|
||||||
|
w.uvarint(uint64(group.MemberCount))
|
||||||
|
w.uvarint(uint64(group.MaxMemberCount))
|
||||||
|
w.svarint(group.LastMsgSeq)
|
||||||
|
|
||||||
|
w.groupMemberList(group.Members)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stashWriter) groupMemberList(list []*GroupMemberInfo) {
|
||||||
|
w.sync(syncMarkerGroupMemberList)
|
||||||
|
w.uvarint(uint64(len(list)))
|
||||||
|
for _, member := range list {
|
||||||
|
w.sync(syncMarkerGroupMemberInfo)
|
||||||
|
w.int64(member.Uin)
|
||||||
|
w.uvarint(uint64(member.Gender))
|
||||||
|
w.string(member.Nickname)
|
||||||
|
w.string(member.CardName)
|
||||||
|
w.uvarint(uint64(member.Level))
|
||||||
|
w.int64(member.JoinTime)
|
||||||
|
w.int64(member.LastSpeakTime)
|
||||||
|
w.string(member.SpecialTitle)
|
||||||
|
w.svarint(member.SpecialTitleExpireTime)
|
||||||
|
w.svarint(member.ShutUpTimestamp)
|
||||||
|
w.uvarint(uint64(member.Permission))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type intWriter struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *intWriter) svarint(x int64) {
|
||||||
|
w.uvarint(uint64(x)<<1 ^ uint64(x>>63))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *intWriter) uvarint(x uint64) {
|
||||||
|
var buf [binary.MaxVarintLen64]byte
|
||||||
|
n := binary.PutUvarint(buf[:], x)
|
||||||
|
_, _ = w.Write(buf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadStash will read the stash from the given reader and apply to the given QQClient.
|
||||||
|
func ReadStash(client *QQClient, data string) (err error) {
|
||||||
|
in := newIntReader(data)
|
||||||
|
sl := int64(in.uvarint())
|
||||||
|
dl := int64(in.uvarint())
|
||||||
|
whence, err := in.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sData := data[whence : whence+sl]
|
||||||
|
dData := data[whence+sl : whence+sl+dl]
|
||||||
|
|
||||||
|
r := stashReader{
|
||||||
|
data: newIntReader(dData),
|
||||||
|
strings: newIntReader(sData),
|
||||||
|
stringIndex: make(map[uint64]string),
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if e, ok := r.(error); ok {
|
||||||
|
err = e
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
client.FriendList = r.friendList()
|
||||||
|
client.GroupList = r.groupList(client)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type stashReader struct {
|
||||||
|
data intReader
|
||||||
|
strings intReader
|
||||||
|
stringIndex map[uint64]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) string() string {
|
||||||
|
off := r.data.uvarint()
|
||||||
|
if off == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if s, ok := r.stringIndex[off]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
_, _ = r.strings.Seek(int64(off), io.SeekStart)
|
||||||
|
l := int64(r.strings.uvarint())
|
||||||
|
whence, _ := r.strings.Seek(0, io.SeekCurrent)
|
||||||
|
s := r.strings.data[whence : whence+l]
|
||||||
|
r.stringIndex[off] = s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) sync(marker syncMarker) {
|
||||||
|
got := syncMarker(r.data.uvarint())
|
||||||
|
if got != marker {
|
||||||
|
panic(&StashSyncMarkerError{
|
||||||
|
marker: got,
|
||||||
|
expected: marker,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) friendList() []*FriendInfo {
|
||||||
|
r.sync(syncMarkerFriendList)
|
||||||
|
l := r.uvarint()
|
||||||
|
list := make([]*FriendInfo, l)
|
||||||
|
for i := uint64(0); i < l; i++ {
|
||||||
|
r.sync(syncMarkerFriendInfo)
|
||||||
|
list[i] = &FriendInfo{
|
||||||
|
Uin: r.int64(),
|
||||||
|
Nickname: r.string(),
|
||||||
|
Remark: r.string(),
|
||||||
|
FaceId: int16(r.svarint()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) groupList(client *QQClient) []*GroupInfo {
|
||||||
|
r.sync(syncMarkerGroupList)
|
||||||
|
l := r.uvarint()
|
||||||
|
list := make([]*GroupInfo, l)
|
||||||
|
for i := uint64(0); i < l; i++ {
|
||||||
|
r.sync(syncMarkerGroupInfo)
|
||||||
|
list[i] = &GroupInfo{
|
||||||
|
Uin: r.int64(),
|
||||||
|
Code: r.int64(),
|
||||||
|
Name: r.string(),
|
||||||
|
Memo: r.string(),
|
||||||
|
OwnerUin: r.int64(),
|
||||||
|
GroupCreateTime: uint32(r.uvarint()),
|
||||||
|
GroupLevel: uint32(r.uvarint()),
|
||||||
|
MemberCount: uint16(r.uvarint()),
|
||||||
|
MaxMemberCount: uint16(r.uvarint()),
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
list[i].Members = r.groupMemberList(list[i])
|
||||||
|
list[i].LastMsgSeq = r.svarint()
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) groupMemberList(group *GroupInfo) []*GroupMemberInfo {
|
||||||
|
r.sync(syncMarkerGroupMemberList)
|
||||||
|
l := r.uvarint()
|
||||||
|
list := make([]*GroupMemberInfo, l)
|
||||||
|
for i := uint64(0); i < l; i++ {
|
||||||
|
r.sync(syncMarkerGroupMemberInfo)
|
||||||
|
list[i] = &GroupMemberInfo{
|
||||||
|
Group: group,
|
||||||
|
Uin: r.int64(),
|
||||||
|
Gender: byte(r.uvarint()),
|
||||||
|
Nickname: r.string(),
|
||||||
|
CardName: r.string(),
|
||||||
|
Level: uint16(r.uvarint()),
|
||||||
|
JoinTime: r.int64(),
|
||||||
|
LastSpeakTime: r.int64(),
|
||||||
|
SpecialTitle: r.string(),
|
||||||
|
SpecialTitleExpireTime: r.svarint(),
|
||||||
|
ShutUpTimestamp: r.svarint(),
|
||||||
|
Permission: MemberPermission(r.uvarint()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stashReader) uvarint() uint64 { return r.data.uvarint() }
|
||||||
|
func (r *stashReader) svarint() int64 { return r.data.svarint() }
|
||||||
|
|
||||||
|
func (r *stashReader) int64() int64 {
|
||||||
|
var buf [8]byte
|
||||||
|
_, _ = r.data.Read(buf[:])
|
||||||
|
return int64(binary.LittleEndian.Uint64(buf[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
type intReader struct {
|
||||||
|
data string
|
||||||
|
*strings.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIntReader(s string) intReader {
|
||||||
|
return intReader{
|
||||||
|
data: s,
|
||||||
|
Reader: strings.NewReader(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *intReader) svarint() int64 {
|
||||||
|
i, _ := binary.ReadVarint(r)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *intReader) uvarint() uint64 {
|
||||||
|
i, _ := binary.ReadUvarint(r)
|
||||||
|
return i
|
||||||
|
}
|
29
client/syncmarker_string.go
Normal file
29
client/syncmarker_string.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Code generated by "stringer -type=syncMarker -trimprefix=syncMarker"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[syncMarkerNone-0]
|
||||||
|
_ = x[syncMarkerFriendList-1]
|
||||||
|
_ = x[syncMarkerFriendInfo-2]
|
||||||
|
_ = x[syncMarkerGroupList-3]
|
||||||
|
_ = x[syncMarkerGroupInfo-4]
|
||||||
|
_ = x[syncMarkerGroupMemberList-5]
|
||||||
|
_ = x[syncMarkerGroupMemberInfo-6]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _syncMarker_name = "NoneFriendListFriendInfoGroupListGroupInfoGroupMemberListGroupMemberInfo"
|
||||||
|
|
||||||
|
var _syncMarker_index = [...]uint8{0, 4, 14, 24, 33, 42, 57, 72}
|
||||||
|
|
||||||
|
func (i syncMarker) String() string {
|
||||||
|
if i < 0 || i >= syncMarker(len(_syncMarker_index)-1) {
|
||||||
|
return "syncMarker(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _syncMarker_name[_syncMarker_index[i]:_syncMarker_index[i+1]]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user