Optimize KeepAlive performance

This commit is contained in:
Tnze
2021-12-24 02:00:11 +08:00
parent 0fc4453bf6
commit 51cbe4c648

View File

@ -4,6 +4,7 @@ import (
"container/list" "container/list"
"context" "context"
"errors" "errors"
"github.com/google/uuid"
"time" "time"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
@ -23,13 +24,15 @@ type KeepAlive struct {
pingList *list.List pingList *list.List
waitList *list.List waitList *list.List
listIndex map[uuid.UUID]*list.Element
listTimer *time.Timer listTimer *time.Timer
waitTimer *time.Timer waitTimer *time.Timer
// The Notchian server uses a system-dependent time in milliseconds to generate the keep alive ID value. // The Notchian server uses a system-dependent time in milliseconds to generate the keep alive ID value.
// We don't do that here for security reason. // We don't do that here for security reason.
keepAliveID int64 keepAliveID int64
onPlayerExpire func(p *Player) //onPlayerExpire func(p *Player)
//updatePlayerDelay func(p *Player, delay time.Duration)
} }
func NewKeepAlive() (k KeepAlive) { func NewKeepAlive() (k KeepAlive) {
@ -38,6 +41,7 @@ func NewKeepAlive() (k KeepAlive) {
k.tick = make(chan *Player) k.tick = make(chan *Player)
k.pingList = list.New() k.pingList = list.New()
k.waitList = list.New() k.waitList = list.New()
k.listIndex = make(map[uuid.UUID]*list.Element)
k.listTimer = time.NewTimer(keepAliveInterval) k.listTimer = time.NewTimer(keepAliveInterval)
k.waitTimer = time.NewTimer(keepAliveWaitInterval) k.waitTimer = time.NewTimer(keepAliveWaitInterval)
return return
@ -82,99 +86,75 @@ func (k *KeepAlive) Run(ctx context.Context) {
} }
func (k KeepAlive) pushPlayer(p *Player) { func (k KeepAlive) pushPlayer(p *Player) {
k.pingList.PushBack(keepAliveItem{ k.listIndex[p.UUID] = k.pingList.PushBack(
player: p, keepAliveItem{player: p, t: time.Now()},
lastTick: time.Now(), )
})
} }
//goland:noinspection GoDeferInLoop //goland:noinspection GoDeferInLoop
func (k *KeepAlive) removePlayer(p *Player) { func (k *KeepAlive) removePlayer(p *Player) {
// find player in pingList elem := k.listIndex[p.UUID]
e := k.pingList.Front() delete(k.listIndex, p.UUID)
for e != nil { if elem.Prev() == nil {
if e.Value.(keepAliveItem).player.UUID == p.UUID { // At present, it is difficult to distinguish
if e.Prev() == nil { // which linked list the player is in,
// so both timers will be reset
defer keepAliveSetTimer(k.pingList, k.listTimer, keepAliveInterval) defer keepAliveSetTimer(k.pingList, k.listTimer, keepAliveInterval)
}
k.pingList.Remove(e)
return
}
e = e.Next()
}
// find player in waitList
e = k.waitList.Front()
for e != nil {
if e.Value.(keepAliveItem).player.UUID == p.UUID {
if e.Prev() == nil {
defer keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval) defer keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval)
} }
k.waitList.Remove(e) k.pingList.Remove(elem)
return k.waitList.Remove(elem)
}
e = e.Next()
}
// player not found
panic("keepalive: fail to remove player: " + p.UUID.String() + ": not found")
} }
func (k *KeepAlive) pingPlayer(now time.Time) { func (k *KeepAlive) pingPlayer(now time.Time) {
if elem := k.pingList.Front(); elem != nil { if elem := k.pingList.Front(); elem != nil {
player := elem.Value.(keepAliveItem).player p := k.pingList.Remove(elem).(keepAliveItem).player
// Send Clientbound KeepAlive packet. // Send Clientbound KeepAlive packet.
err := player.WritePacket(Packet757(pk.Marshal( err := p.WritePacket(Packet757(pk.Marshal(
packetid.ClientboundKeepAlive, packetid.ClientboundKeepAlive,
pk.Long(k.keepAliveID), pk.Long(k.keepAliveID),
))) )))
if err != nil { if err != nil {
player.PutErr(err) p.PutErr(err)
return return
} }
k.keepAliveID++ k.keepAliveID++
// Clientbound KeepAlive packet is sent, move the player to waiting list. // Clientbound KeepAlive packet is sent, move the player to waiting list.
k.pingList.Remove(elem) k.listIndex[p.UUID] = k.waitList.PushBack(
k.waitList.PushBack(keepAliveItem{ keepAliveItem{player: p, t: now},
player: player, )
lastTick: now,
})
} }
// Wait for next earliest player // Wait for next earliest player
keepAliveSetTimer(k.pingList, k.listTimer, keepAliveInterval) keepAliveSetTimer(k.pingList, k.listTimer, keepAliveInterval)
} }
func (k *KeepAlive) tickPlayer(p *Player) { func (k *KeepAlive) tickPlayer(p *Player) {
elem := k.waitList.Front() elem, ok := k.listIndex[p.UUID]
for elem != nil { if !ok {
if elem.Value.(keepAliveItem).player.UUID == p.UUID {
k.waitList.Remove(elem)
break
}
elem = elem.Next()
}
if elem == nil {
p.PutErr(errors.New("keepalive: fail to tick player: " + p.UUID.String() + " not found")) p.PutErr(errors.New("keepalive: fail to tick player: " + p.UUID.String() + " not found"))
return return
} }
if elem.Prev() == nil { if elem.Prev() == nil {
if !k.waitTimer.Stop() { if !k.waitTimer.Stop() {
<-k.waitTimer.C <-k.waitTimer.C
} }
defer keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval) defer keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval)
} }
k.waitList.Remove(elem) // update delay of player
k.pingList.PushBack(keepAliveItem{ //t := k.waitList.Remove(elem).(keepAliveItem).t
player: p, now := time.Now()
lastTick: time.Now(), //k.updatePlayerDelay(p, now.Sub(t))
}) // move the player to ping list
k.listIndex[p.UUID] = k.pingList.PushBack(
keepAliveItem{player: p, t: now},
)
} }
func (k *KeepAlive) kickPlayer() { func (k *KeepAlive) kickPlayer() {
if elem := k.waitList.Front(); elem != nil { if elem := k.waitList.Front(); elem != nil {
player := elem.Value.(keepAliveItem).player //player := elem.Value.(keepAliveItem).player
k.waitList.Remove(elem) k.waitList.Remove(elem)
k.onPlayerExpire(player) //k.onPlayerExpire(player)
} }
keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval) keepAliveSetTimer(k.waitList, k.waitTimer, keepAliveWaitInterval)
} }
@ -182,7 +162,7 @@ func (k *KeepAlive) kickPlayer() {
func keepAliveSetTimer(l *list.List, timer *time.Timer, interval time.Duration) { func keepAliveSetTimer(l *list.List, timer *time.Timer, interval time.Duration) {
if first := l.Front(); first != nil { if first := l.Front(); first != nil {
item := first.Value.(keepAliveItem) item := first.Value.(keepAliveItem)
interval -= time.Since(item.lastTick) interval -= time.Since(item.t)
if interval < 0 { if interval < 0 {
interval = 0 interval = 0
} }
@ -193,5 +173,5 @@ func keepAliveSetTimer(l *list.List, timer *time.Timer, interval time.Duration)
type keepAliveItem struct { type keepAliveItem struct {
player *Player player *Player
lastTick time.Time t time.Time
} }