From dccbf7ce46b7dadd9a4b833912dc875377f9c6f6 Mon Sep 17 00:00:00 2001 From: Tnze Date: Fri, 24 Dec 2021 11:45:50 +0800 Subject: [PATCH] Unify all components --- examples/frameworkServer/main.go | 9 ++++- server/chat.go | 10 ++--- server/gameplay.go | 61 +++++++++++++++------------- server/keepalive.go | 25 ++++++------ server/player.go | 40 +++++++++++++++++-- server/playerlist.go | 68 ++++++++++++++++++++------------ 6 files changed, 137 insertions(+), 76 deletions(-) diff --git a/examples/frameworkServer/main.go b/examples/frameworkServer/main.go index dc4e687..a0a3e1e 100644 --- a/examples/frameworkServer/main.go +++ b/examples/frameworkServer/main.go @@ -29,8 +29,13 @@ func main() { chunk00 := level.ChunkFromSave(readChunk00(), 256) defaultDimension.LoadChunk(level.ChunkPos{X: 0, Z: 0}, chunk00) - game := server.NewGame(defaultDimension, playerList) - game.Run(context.Background()) + game := server.NewGame( + defaultDimension, + playerList, + server.NewKeepAlive(), + server.NewGlobalChat(), + ) + go game.Run(context.Background()) s := server.Server{ ListPingHandler: serverInfo, diff --git a/server/chat.go b/server/chat.go index 6f83363..becd2c7 100644 --- a/server/chat.go +++ b/server/chat.go @@ -23,8 +23,8 @@ type chatItem struct { text string } -func NewGlobalChat() GlobalChat { - return GlobalChat{ +func NewGlobalChat() *GlobalChat { + return &GlobalChat{ msg: make(chan chatItem), join: make(chan *Player), quit: make(chan *Player), @@ -34,7 +34,7 @@ func NewGlobalChat() GlobalChat { func (g *GlobalChat) AddPlayer(p *Player) { g.join <- p - p.Add(PacketHandler{ + p.AddHandler(PacketHandler{ ID: packetid.ServerboundChat, F: func(packet Packet757) error { var msg pk.String @@ -52,7 +52,7 @@ func (g *GlobalChat) RemovePlayer(p *Player) { g.quit <- p } -func (c chatItem) ToMessage() chat.Message { +func (c chatItem) toMessage() chat.Message { return chat.TranslateMsg( "chat.type.text", chat.Message{ @@ -95,7 +95,7 @@ func (g *GlobalChat) Run(ctx context.Context) { case item := <-g.msg: packet := Packet757(pk.Marshal( packetid.ClientboundChat, - item.ToMessage(), + item.toMessage(), pk.Byte(0), pk.UUID(item.p.UUID), )) diff --git a/server/gameplay.go b/server/gameplay.go index c339f97..3debebc 100644 --- a/server/gameplay.go +++ b/server/gameplay.go @@ -3,11 +3,11 @@ package server import ( "context" _ "embed" + "sync" "sync/atomic" "github.com/google/uuid" - "github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/nbt" "github.com/Tnze/go-mc/net" @@ -23,11 +23,16 @@ type GamePlay interface { } type Game struct { + Dim Level + components []Component + eid int32 - Dim Level - *PlayerList - KeepAlive KeepAlive - GlobalChat GlobalChat +} + +type Component interface { + Run(ctx context.Context) + AddPlayer(p *Player) + RemovePlayer(p *Player) } //go:embed DimensionCodec.snbt @@ -36,30 +41,26 @@ var dimensionCodecSNBT string //go:embed Dimension.snbt var dimensionSNBT string -func NewGame(dim Level, playerList *PlayerList) *Game { +func NewGame(dim Level, components ...Component) *Game { return &Game{ Dim: dim, - PlayerList: playerList, - KeepAlive: NewKeepAlive(), - GlobalChat: NewGlobalChat(), + components: components, } } func (g *Game) Run(ctx context.Context) { - go g.KeepAlive.Run(ctx) - go g.GlobalChat.Run(ctx) + var wg sync.WaitGroup + wg.Add(len(g.components)) + for _, c := range g.components { + go func(c Component) { + defer wg.Done() + c.Run(ctx) + }(c) + } + wg.Wait() } func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) { - remove := g.PlayerList.TryInsert(PlayerSample{Name: name, ID: id}) - if remove == nil { - _ = conn.WritePacket(pk.Marshal( - packetid.ClientboundDisconnect, - chat.TranslateMsg("multiplayer.disconnect.server_full"), - )) - return - } - defer remove() p := &Player{ Conn: conn, Name: name, @@ -67,6 +68,7 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net EntityID: g.newEID(), Gamemode: 1, handlers: make(map[int32][]packetHandlerFunc), + errChan: make(chan error, 1), } dimInfo := g.Dim.Info() err := p.WritePacket(Packet757(pk.Marshal( @@ -94,11 +96,14 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net g.Dim.PlayerJoin(p) defer g.Dim.PlayerQuit(p) - g.KeepAlive.AddPlayer(p) - defer g.KeepAlive.RemovePlayer(p) - - g.GlobalChat.AddPlayer(p) - defer g.GlobalChat.RemovePlayer(p) + for _, c := range g.components { + c.AddPlayer(p) + if err := p.GetErr(); err != nil { + return + } + //goland:noinspection GoDeferInLoop + defer c.RemovePlayer(p) + } var packet pk.Packet for { @@ -108,9 +113,9 @@ func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net } for _, ph := range p.handlers[packet.ID] { err = ph(Packet757(packet)) - } - if err != nil { - return + if err != nil { + return + } } } } diff --git a/server/keepalive.go b/server/keepalive.go index 1211e0c..4e55968 100644 --- a/server/keepalive.go +++ b/server/keepalive.go @@ -35,21 +35,23 @@ type KeepAlive struct { //updatePlayerDelay func(p *Player, delay time.Duration) } -func NewKeepAlive() (k KeepAlive) { - k.join = make(chan *Player) - k.quit = make(chan *Player) - k.tick = make(chan *Player) - k.pingList = list.New() - k.waitList = list.New() - k.listIndex = make(map[uuid.UUID]*list.Element) - k.listTimer = time.NewTimer(keepAliveInterval) - k.waitTimer = time.NewTimer(keepAliveWaitInterval) - return +func NewKeepAlive() (k *KeepAlive) { + return &KeepAlive{ + join: make(chan *Player), + quit: make(chan *Player), + tick: make(chan *Player), + pingList: list.New(), + waitList: list.New(), + listIndex: make(map[uuid.UUID]*list.Element), + listTimer: time.NewTimer(keepAliveInterval), + waitTimer: time.NewTimer(keepAliveWaitInterval), + keepAliveID: 0, + } } func (k *KeepAlive) AddPlayer(p *Player) { k.join <- p - p.Add(PacketHandler{ + p.AddHandler(PacketHandler{ ID: packetid.ServerboundKeepAlive, F: func(packet Packet757) error { var KeepAliveID pk.Long @@ -91,7 +93,6 @@ func (k KeepAlive) pushPlayer(p *Player) { ) } -//goland:noinspection GoDeferInLoop func (k *KeepAlive) removePlayer(p *Player) { elem := k.listIndex[p.UUID] delete(k.listIndex, p.UUID) diff --git a/server/player.go b/server/player.go index ccf01dd..4d8fb8e 100644 --- a/server/player.go +++ b/server/player.go @@ -1,6 +1,7 @@ package server import ( + "strconv" "sync" "github.com/google/uuid" @@ -18,6 +19,8 @@ type Player struct { EntityID int32 Gamemode byte handlers map[int32][]packetHandlerFunc + + errChan chan error } // Packet757 is a packet in protocol 757. @@ -28,7 +31,24 @@ type Packet757 pk.Packet func (p *Player) WritePacket(packet Packet757) error { p.writeLock.Lock() defer p.writeLock.Unlock() - return p.Conn.WritePacket(pk.Packet(packet)) + err := p.Conn.WritePacket(pk.Packet(packet)) + if err != nil { + return WritePacketError{Err: err, ID: packet.ID} + } + return nil +} + +type WritePacketError struct { + Err error + ID int32 +} + +func (s WritePacketError) Error() string { + return "server: send packet " + strconv.FormatInt(int64(s.ID), 16) + " error: " + s.Err.Error() +} + +func (s WritePacketError) Unwrap() error { + return s.Err } type PacketHandler struct { @@ -38,8 +58,7 @@ type PacketHandler struct { type packetHandlerFunc func(packet Packet757) error -func (p *Player) Add(ph PacketHandler) { - +func (p *Player) AddHandler(ph PacketHandler) { if p.handlers == nil { p.handlers = make(map[int32][]packetHandlerFunc) } @@ -47,5 +66,18 @@ func (p *Player) Add(ph PacketHandler) { } func (p *Player) PutErr(err error) { - // TODO: handle errors + select { + case p.errChan <- err: + default: + // previous error exist, ignore this. + } +} + +func (p *Player) GetErr() error { + select { + case err := <-p.errChan: + return err + default: + return nil + } } diff --git a/server/playerlist.go b/server/playerlist.go index 00e1db9..6e958c8 100644 --- a/server/playerlist.go +++ b/server/playerlist.go @@ -1,15 +1,22 @@ package server import ( - "container/list" + "context" + "errors" "sync" + + "github.com/google/uuid" + + "github.com/Tnze/go-mc/chat" + "github.com/Tnze/go-mc/data/packetid" + pk "github.com/Tnze/go-mc/net/packet" ) // PlayerList is a player list based on linked-list. // This struct should not be copied after used. type PlayerList struct { maxPlayer int - players *list.List + players map[uuid.UUID]*Player // Only the linked-list is protected by this Mutex. // Because others field never change after created. playersLock sync.Mutex @@ -19,27 +26,36 @@ type PlayerList struct { func NewPlayerList(maxPlayers int) *PlayerList { return &PlayerList{ maxPlayer: maxPlayers, - players: list.New(), + players: make(map[uuid.UUID]*Player), } } -// TryInsert trying to insert player into PlayerList. -// Return nil if the server is full (length of list larger than maxPlayers), -// otherwise return a function which is used to remove the player from PlayerList -func (p *PlayerList) TryInsert(player PlayerSample) (remove func()) { +func (p *PlayerList) Run(context.Context) {} + +func (p *PlayerList) AddPlayer(player *Player) { p.playersLock.Lock() defer p.playersLock.Unlock() - if p.players.Len() >= p.maxPlayer { - return nil + if len(p.players) >= p.maxPlayer { + err := player.WritePacket(Packet757(pk.Marshal( + packetid.ClientboundDisconnect, + chat.TranslateMsg("multiplayer.disconnect.server_full"), + ))) + if err != nil { + player.PutErr(err) + } else { + player.PutErr(errors.New("playerlist: server full")) + } + return } - elem := p.players.PushBack(player) - return func() { - p.playersLock.Lock() - p.players.Remove(elem) - p.playersLock.Unlock() - } + p.players[player.UUID] = player +} + +func (p *PlayerList) RemovePlayer(player *Player) { + p.playersLock.Lock() + defer p.playersLock.Unlock() + delete(p.players, player.UUID) } func (p *PlayerList) MaxPlayer() int { @@ -49,22 +65,24 @@ func (p *PlayerList) MaxPlayer() int { func (p *PlayerList) OnlinePlayer() int { p.playersLock.Lock() defer p.playersLock.Unlock() - return p.players.Len() + return len(p.players) } func (p *PlayerList) PlayerSamples() (sample []PlayerSample) { p.playersLock.Lock() defer p.playersLock.Unlock() // Up to 10 players can be returned - length := p.players.Len() - if length > 10 { - length = 10 - } - sample = make([]PlayerSample, length) - v := p.players.Front() - for i := 0; i < length; i++ { - sample[i] = v.Value.(PlayerSample) - v = v.Next() + sample = make([]PlayerSample, len(p.players)) + var i int + for _, v := range p.players { + sample[i] = PlayerSample{ + Name: v.Name, + ID: v.UUID, + } + i++ + if i >= len(p.players) { + break + } } return }