package server import ( "context" "io" "time" "github.com/google/uuid" "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" ) type PlayerInfo struct { updateDelay chan playerDelayInfo join chan *Player quit chan *Player ticker *time.Ticker } type playerDelayInfo struct { player *Player delay time.Duration } func (p *playerDelayInfo) WriteTo(w io.Writer) (n int64, err error) { return pk.Tuple{ pk.UUID(p.player.UUID), pk.String(p.player.Name), pk.Array([]pk.FieldEncoder{}), pk.VarInt(p.player.Gamemode), pk.VarInt(p.delay), pk.Boolean(false), }.WriteTo(w) } type playerInfoList struct { list map[uuid.UUID]playerDelayInfo } func (p *playerInfoList) WriteTo(w io.Writer) (n int64, err error) { n, err = pk.VarInt(len(p.list)).WriteTo(w) if err != nil { return } var n1 int64 for _, p := range p.list { n1, err = p.WriteTo(w) n += n1 if err != nil { return } } return } type playerDelayUpdate playerDelayInfo func (p playerDelayUpdate) WriteTo(w io.Writer) (n int64, err error) { return pk.Tuple{ pk.UUID(p.player.UUID), pk.VarInt(p.delay.Milliseconds()), }.WriteTo(w) } const ( actionAddPlayer = iota actionUpdateGamemode actionUpdateLatency actionUpdateDisplayName actionRemovePlayer ) type PlayerDelaySource interface { AddPlayerDelayUpdateHandler(f func(p *Player, delay time.Duration)) } func NewPlayerInfo(updateFreq time.Duration, delaySource PlayerDelaySource) *PlayerInfo { p := &PlayerInfo{ updateDelay: make(chan playerDelayInfo), join: make(chan *Player), quit: make(chan *Player), ticker: time.NewTicker(updateFreq), } if delaySource != nil { delaySource.AddPlayerDelayUpdateHandler(p.onPlayerDelayUpdate) } return p } func (p *PlayerInfo) Init(*Game) {} func (p *PlayerInfo) Run(ctx context.Context) { players := &playerInfoList{list: make(map[uuid.UUID]playerDelayInfo)} var delayBuffer []playerDelayUpdate for { select { case player := <-p.join: info := playerDelayInfo{player: player, delay: 0} pack := Packet758(pk.Marshal( packetid.ClientboundPlayerInfo, pk.VarInt(actionAddPlayer), pk.VarInt(1), &info, )) players.list[player.UUID] = info for _, p := range players.list { p.player.WritePacket(pack) } player.WritePacket(Packet758(pk.Marshal( packetid.ClientboundPlayerInfo, pk.VarInt(actionAddPlayer), players, ))) case player := <-p.quit: delete(players.list, player.UUID) pack := Packet758(pk.Marshal( packetid.ClientboundPlayerInfo, pk.VarInt(actionRemovePlayer), pk.VarInt(1), pk.UUID(player.UUID), )) for _, p := range players.list { p.player.WritePacket(pack) } case change := <-p.updateDelay: delayBuffer = append(delayBuffer, playerDelayUpdate(change)) case <-p.ticker.C: pack := Packet758(pk.Marshal( packetid.ClientboundPlayerInfo, pk.VarInt(actionUpdateLatency), pk.Array(&delayBuffer), )) for _, p := range players.list { p.player.WritePacket(pack) } delayBuffer = delayBuffer[:0] case <-ctx.Done(): break } } } func (p *PlayerInfo) AddPlayer(player *Player) { p.join <- player } func (p *PlayerInfo) RemovePlayer(player *Player) { p.quit <- player } func (p *PlayerInfo) onPlayerDelayUpdate(player *Player, delay time.Duration) { p.updateDelay <- playerDelayInfo{player: player, delay: delay} }