Optimize PacketHandler performance
This commit is contained in:
@ -14,7 +14,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxPlayer = 20
|
const MaxPlayer = 1024
|
||||||
const IconPath = "./server-icon.png"
|
const IconPath = "./server-icon.png"
|
||||||
|
|
||||||
var motd = chat.Message{Text: "A Minecraft Server ", Extra: []chat.Message{{Text: "Powered by go-mc", Color: "yellow"}}}
|
var motd = chat.Message{Text: "A Minecraft Server ", Extra: []chat.Message{{Text: "Powered by go-mc", Color: "yellow"}}}
|
||||||
@ -40,8 +40,9 @@ func main() {
|
|||||||
s := server.Server{
|
s := server.Server{
|
||||||
ListPingHandler: serverInfo,
|
ListPingHandler: serverInfo,
|
||||||
LoginHandler: &server.MojangLoginHandler{
|
LoginHandler: &server.MojangLoginHandler{
|
||||||
OnlineMode: false,
|
OnlineMode: false,
|
||||||
Threshold: 256,
|
Threshold: 256,
|
||||||
|
LoginChecker: playerList,
|
||||||
},
|
},
|
||||||
GamePlay: game,
|
GamePlay: game,
|
||||||
}
|
}
|
||||||
|
99
examples/pressureTest/main.go
Normal file
99
examples/pressureTest/main.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/bot"
|
||||||
|
"github.com/Tnze/go-mc/bot/basic"
|
||||||
|
"github.com/Tnze/go-mc/chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
var address = flag.String("address", "127.0.0.1", "The server address")
|
||||||
|
var number = flag.Int("number", 1023, "The number of clients")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
log.SetOutput(colorable.NewColorableStdout())
|
||||||
|
|
||||||
|
for i := 0; i < *number; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
for {
|
||||||
|
ind := newIndividual(i, "Player"+strconv.Itoa(i))
|
||||||
|
ind.run(*address)
|
||||||
|
time.Sleep(time.Second * 3)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type individual struct {
|
||||||
|
id int
|
||||||
|
client *bot.Client
|
||||||
|
player *basic.Player
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIndividual(id int, name string) (i *individual) {
|
||||||
|
i = new(individual)
|
||||||
|
i.id = id
|
||||||
|
i.client = bot.NewClient()
|
||||||
|
i.client.Auth.Name = name
|
||||||
|
i.player = basic.NewPlayer(i.client, basic.DefaultSettings)
|
||||||
|
basic.EventsListener{
|
||||||
|
GameStart: i.onGameStart,
|
||||||
|
Death: i.onDeath,
|
||||||
|
Disconnect: onDisconnect,
|
||||||
|
}.Attach(i.client)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *individual) run(address string) {
|
||||||
|
//Login
|
||||||
|
err := i.client.JoinServer(address)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%d]Login fail: %v", i.id, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[%d]Login success", i.id)
|
||||||
|
|
||||||
|
//JoinGame
|
||||||
|
if err = i.client.HandleGame(); err == nil {
|
||||||
|
panic("HandleGame never return nil")
|
||||||
|
}
|
||||||
|
log.Printf("[%d] Handle game error: %v", i.id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *individual) onDeath() error {
|
||||||
|
log.Printf("[%d]Died and Respawned", i.id)
|
||||||
|
// If we exclude Respawn(...) then the player won't press the "Respawn" button upon death
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
err := i.player.Respawn()
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *individual) onGameStart() error {
|
||||||
|
log.Printf("[%d]Game start", i.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisconnectErr struct {
|
||||||
|
Reason chat.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DisconnectErr) Error() string {
|
||||||
|
return "disconnect: " + d.Reason.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func onDisconnect(reason chat.Message) error {
|
||||||
|
return DisconnectErr{Reason: reason}
|
||||||
|
}
|
@ -32,26 +32,51 @@ func NewGlobalChat() *GlobalChat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GlobalChat) AddPlayer(p *Player) {
|
func (g *GlobalChat) Init(game *Game) {
|
||||||
g.join <- p
|
game.AddHandler(&PacketHandler{
|
||||||
p.AddHandler(PacketHandler{
|
|
||||||
ID: packetid.ServerboundChat,
|
ID: packetid.ServerboundChat,
|
||||||
F: func(packet Packet757) error {
|
F: func(player *Player, packet Packet757) error {
|
||||||
var msg pk.String
|
var msg pk.String
|
||||||
if err := pk.Packet(packet).Scan(&msg); err != nil {
|
if err := pk.Packet(packet).Scan(&msg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
text, _ := chat.TransCtrlSeq(string(msg), false)
|
text, _ := chat.TransCtrlSeq(string(msg), false)
|
||||||
g.msg <- chatItem{p: p, text: text}
|
g.msg <- chatItem{p: player, text: text}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GlobalChat) RemovePlayer(p *Player) {
|
func (g *GlobalChat) Run(ctx context.Context) {
|
||||||
g.quit <- p
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case item := <-g.msg:
|
||||||
|
packet := Packet757(pk.Marshal(
|
||||||
|
packetid.ClientboundChat,
|
||||||
|
item.toMessage(),
|
||||||
|
pk.Byte(0),
|
||||||
|
pk.UUID(item.p.UUID),
|
||||||
|
))
|
||||||
|
for _, p := range g.players {
|
||||||
|
err := p.WritePacket(packet)
|
||||||
|
if err != nil {
|
||||||
|
p.PutErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case p := <-g.join:
|
||||||
|
g.players[p.UUID] = p
|
||||||
|
case p := <-g.quit:
|
||||||
|
delete(g.players, p.UUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GlobalChat) AddPlayer(player *Player) { g.join <- player }
|
||||||
|
|
||||||
|
func (g *GlobalChat) RemovePlayer(p *Player) { g.quit <- p }
|
||||||
|
|
||||||
func (c chatItem) toMessage() chat.Message {
|
func (c chatItem) toMessage() chat.Message {
|
||||||
return chat.TranslateMsg(
|
return chat.TranslateMsg(
|
||||||
"chat.type.text",
|
"chat.type.text",
|
||||||
@ -86,29 +111,3 @@ func playerToSNBT(p *Player) string {
|
|||||||
|
|
||||||
return string(s)
|
return string(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GlobalChat) Run(ctx context.Context) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case item := <-g.msg:
|
|
||||||
packet := Packet757(pk.Marshal(
|
|
||||||
packetid.ClientboundChat,
|
|
||||||
item.toMessage(),
|
|
||||||
pk.Byte(0),
|
|
||||||
pk.UUID(item.p.UUID),
|
|
||||||
))
|
|
||||||
for _, p := range g.players {
|
|
||||||
err := p.WritePacket(packet)
|
|
||||||
if err != nil {
|
|
||||||
p.PutErr(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case p := <-g.join:
|
|
||||||
g.players[p.UUID] = p
|
|
||||||
case p := <-g.quit:
|
|
||||||
delete(g.players, p.UUID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -25,16 +25,25 @@ type GamePlay interface {
|
|||||||
type Game struct {
|
type Game struct {
|
||||||
Dim Level
|
Dim Level
|
||||||
components []Component
|
components []Component
|
||||||
|
handlers map[int32][]*PacketHandler
|
||||||
|
|
||||||
eid int32
|
eid int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Component interface {
|
type Component interface {
|
||||||
|
Init(g *Game)
|
||||||
Run(ctx context.Context)
|
Run(ctx context.Context)
|
||||||
AddPlayer(p *Player)
|
AddPlayer(p *Player)
|
||||||
RemovePlayer(p *Player)
|
RemovePlayer(p *Player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketHandler struct {
|
||||||
|
ID int32
|
||||||
|
F packetHandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type packetHandlerFunc func(player *Player, packet Packet757) error
|
||||||
|
|
||||||
//go:embed DimensionCodec.snbt
|
//go:embed DimensionCodec.snbt
|
||||||
var dimensionCodecSNBT string
|
var dimensionCodecSNBT string
|
||||||
|
|
||||||
@ -42,10 +51,19 @@ var dimensionCodecSNBT string
|
|||||||
var dimensionSNBT string
|
var dimensionSNBT string
|
||||||
|
|
||||||
func NewGame(dim Level, components ...Component) *Game {
|
func NewGame(dim Level, components ...Component) *Game {
|
||||||
return &Game{
|
g := &Game{
|
||||||
Dim: dim,
|
Dim: dim,
|
||||||
components: components,
|
components: components,
|
||||||
|
handlers: make(map[int32][]*PacketHandler),
|
||||||
}
|
}
|
||||||
|
for _, v := range components {
|
||||||
|
v.Init(g)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) AddHandler(ph *PacketHandler) {
|
||||||
|
g.handlers[ph.ID] = append(g.handlers[ph.ID], ph)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Run(ctx context.Context) {
|
func (g *Game) Run(ctx context.Context) {
|
||||||
@ -67,7 +85,6 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net
|
|||||||
UUID: id,
|
UUID: id,
|
||||||
EntityID: g.newEID(),
|
EntityID: g.newEID(),
|
||||||
Gamemode: 1,
|
Gamemode: 1,
|
||||||
handlers: make(map[int32][]packetHandlerFunc),
|
|
||||||
errChan: make(chan error, 1),
|
errChan: make(chan error, 1),
|
||||||
}
|
}
|
||||||
dimInfo := g.Dim.Info()
|
dimInfo := g.Dim.Info()
|
||||||
@ -107,13 +124,11 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net
|
|||||||
|
|
||||||
var packet pk.Packet
|
var packet pk.Packet
|
||||||
for {
|
for {
|
||||||
err := p.ReadPacket(&packet)
|
if err := p.ReadPacket(&packet); err != nil {
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, ph := range p.handlers[packet.ID] {
|
for _, ph := range g.handlers[packet.ID] {
|
||||||
err = ph(Packet757(packet))
|
if err := ph.F(p, Packet757(packet)); err != nil {
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,25 +49,22 @@ func NewKeepAlive() (k *KeepAlive) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KeepAlive) AddPlayer(p *Player) {
|
// Init implement Component for KeepAlive
|
||||||
k.join <- p
|
func (k *KeepAlive) Init(g *Game) {
|
||||||
p.AddHandler(PacketHandler{
|
g.AddHandler(&PacketHandler{
|
||||||
ID: packetid.ServerboundKeepAlive,
|
ID: packetid.ServerboundKeepAlive,
|
||||||
F: func(packet Packet757) error {
|
F: func(player *Player, packet Packet757) error {
|
||||||
var KeepAliveID pk.Long
|
var KeepAliveID pk.Long
|
||||||
if err := pk.Packet(packet).Scan(&KeepAliveID); err != nil {
|
if err := pk.Packet(packet).Scan(&KeepAliveID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
k.tick <- p
|
k.tick <- player
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KeepAlive) RemovePlayer(p *Player) {
|
// Run implement Component for KeepAlive
|
||||||
k.quit <- p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KeepAlive) Run(ctx context.Context) {
|
func (k *KeepAlive) Run(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -87,6 +84,12 @@ func (k *KeepAlive) Run(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPlayer implement Component for KeepAlive
|
||||||
|
func (k *KeepAlive) AddPlayer(player *Player) { k.join <- player }
|
||||||
|
|
||||||
|
// RemovePlayer implement Component for KeepAlive
|
||||||
|
func (k *KeepAlive) RemovePlayer(p *Player) { k.quit <- p }
|
||||||
|
|
||||||
func (k KeepAlive) pushPlayer(p *Player) {
|
func (k KeepAlive) pushPlayer(p *Player) {
|
||||||
k.listIndex[p.UUID] = k.pingList.PushBack(
|
k.listIndex[p.UUID] = k.pingList.PushBack(
|
||||||
keepAliveItem{player: p, t: time.Now()},
|
keepAliveItem{player: p, t: time.Now()},
|
||||||
|
@ -18,7 +18,6 @@ type Player struct {
|
|||||||
uuid.UUID
|
uuid.UUID
|
||||||
EntityID int32
|
EntityID int32
|
||||||
Gamemode byte
|
Gamemode byte
|
||||||
handlers map[int32][]packetHandlerFunc
|
|
||||||
|
|
||||||
errChan chan error
|
errChan chan error
|
||||||
}
|
}
|
||||||
@ -51,20 +50,6 @@ func (s WritePacketError) Unwrap() error {
|
|||||||
return s.Err
|
return s.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketHandler struct {
|
|
||||||
ID int32
|
|
||||||
F packetHandlerFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type packetHandlerFunc func(packet Packet757) error
|
|
||||||
|
|
||||||
func (p *Player) AddHandler(ph PacketHandler) {
|
|
||||||
if p.handlers == nil {
|
|
||||||
p.handlers = make(map[int32][]packetHandlerFunc)
|
|
||||||
}
|
|
||||||
p.handlers[ph.ID] = append(p.handlers[ph.ID], ph.F)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Player) PutErr(err error) {
|
func (p *Player) PutErr(err error) {
|
||||||
select {
|
select {
|
||||||
case p.errChan <- err:
|
case p.errChan <- err:
|
||||||
|
@ -30,8 +30,13 @@ func NewPlayerList(maxPlayers int) *PlayerList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init implement Component for PlayerList
|
||||||
|
func (p *PlayerList) Init(*Game) {}
|
||||||
|
|
||||||
|
// Run implement Component for PlayerList
|
||||||
func (p *PlayerList) Run(context.Context) {}
|
func (p *PlayerList) Run(context.Context) {}
|
||||||
|
|
||||||
|
// AddPlayer implement Component for PlayerList
|
||||||
func (p *PlayerList) AddPlayer(player *Player) {
|
func (p *PlayerList) AddPlayer(player *Player) {
|
||||||
p.playersLock.Lock()
|
p.playersLock.Lock()
|
||||||
defer p.playersLock.Unlock()
|
defer p.playersLock.Unlock()
|
||||||
@ -52,12 +57,23 @@ func (p *PlayerList) AddPlayer(player *Player) {
|
|||||||
p.players[player.UUID] = player
|
p.players[player.UUID] = player
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemovePlayer implement Component for PlayerList
|
||||||
func (p *PlayerList) RemovePlayer(player *Player) {
|
func (p *PlayerList) RemovePlayer(player *Player) {
|
||||||
p.playersLock.Lock()
|
p.playersLock.Lock()
|
||||||
defer p.playersLock.Unlock()
|
defer p.playersLock.Unlock()
|
||||||
delete(p.players, player.UUID)
|
delete(p.players, player.UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckPlayer implement LoginChecker for PlayerList
|
||||||
|
func (p *PlayerList) CheckPlayer(name string, id uuid.UUID, protocol int32) (ok bool, reason chat.Message) {
|
||||||
|
p.playersLock.Lock()
|
||||||
|
defer p.playersLock.Unlock()
|
||||||
|
if len(p.players) >= p.maxPlayer {
|
||||||
|
return false, chat.TranslateMsg("multiplayer.disconnect.server_full")
|
||||||
|
}
|
||||||
|
return true, chat.Message{}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PlayerList) MaxPlayer() int {
|
func (p *PlayerList) MaxPlayer() int {
|
||||||
return p.maxPlayer
|
return p.maxPlayer
|
||||||
}
|
}
|
||||||
@ -72,7 +88,11 @@ func (p *PlayerList) PlayerSamples() (sample []PlayerSample) {
|
|||||||
p.playersLock.Lock()
|
p.playersLock.Lock()
|
||||||
defer p.playersLock.Unlock()
|
defer p.playersLock.Unlock()
|
||||||
// Up to 10 players can be returned
|
// Up to 10 players can be returned
|
||||||
sample = make([]PlayerSample, len(p.players))
|
length := len(p.players)
|
||||||
|
if length > 10 {
|
||||||
|
length = 10
|
||||||
|
}
|
||||||
|
sample = make([]PlayerSample, length)
|
||||||
var i int
|
var i int
|
||||||
for _, v := range p.players {
|
for _, v := range p.players {
|
||||||
sample[i] = PlayerSample{
|
sample[i] = PlayerSample{
|
||||||
@ -80,7 +100,7 @@ func (p *PlayerList) PlayerSamples() (sample []PlayerSample) {
|
|||||||
ID: v.UUID,
|
ID: v.UUID,
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
if i >= len(p.players) {
|
if i >= length {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user