Files
go-mc/server/gameplay.go
2022-05-28 13:48:19 +08:00

136 lines
2.8 KiB
Go

package server
import (
"context"
_ "embed"
"sync"
"time"
"github.com/google/uuid"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/server/ecs"
)
type GamePlay interface {
// AcceptPlayer handle everything after "LoginSuccess" is sent.
//
// Note: the connection will be closed after this function returned.
// You don't need to close the connection, but to keep not returning while the player is playing.
AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn)
}
type Game struct {
*ecs.World
*ecs.Dispatcher
WorldLocker sync.Mutex
handlers map[int32][]*PacketHandler
components []Component
}
type PacketHandler struct {
ID int32
F packetHandlerFunc
}
type packetHandlerFunc func(client *Client, player *Player, packet Packet758) error
type Component interface {
Init(g *Game)
Run(ctx context.Context)
ClientJoin(c *Client, p *Player)
ClientLeft(c *Client, p *Player, reason error)
}
func NewGame(components ...Component) *Game {
g := &Game{
World: ecs.NewWorld(),
Dispatcher: ecs.NewDispatcher(),
handlers: make(map[int32][]*PacketHandler),
components: components,
}
for _, c := range components {
c.Init(g)
}
ecs.Register[Client, *ecs.HashMapStorage[Client]](g.World)
ecs.Register[Player, *ecs.HashMapStorage[Player]](g.World)
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) {
for _, c := range g.components {
go c.Run(ctx)
}
ticker := time.NewTicker(time.Second / 20)
for {
select {
case <-ticker.C:
g.WorldLocker.Lock()
g.Dispatcher.Run(g.World)
g.WorldLocker.Unlock()
case <-ctx.Done():
return
}
}
}
func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) {
g.WorldLocker.Lock()
eid := g.CreateEntity(
Client{
Conn: conn,
Protocol: protocol,
packetQueue: NewPacketQueue(),
errChan: make(chan error, 1),
},
Player{
UUID: id,
Name: name,
},
)
c := ecs.GetComponent[Client](g.World).GetValue(eid)
p := ecs.GetComponent[Player](g.World).GetValue(eid)
g.WorldLocker.Unlock()
defer c.packetQueue.Close()
go func() {
for {
packet, ok := c.packetQueue.Pull()
if !ok {
break
}
err := c.Conn.WritePacket(packet)
if err != nil {
c.PutErr(err)
break
}
}
}()
var err error
for _, component := range g.components {
component.ClientJoin(c, p)
defer func(cmp Component) { cmp.ClientLeft(c, p, err) }(component)
}
var packet pk.Packet
for {
if err = c.ReadPacket(&packet); err != nil {
return
}
for _, ph := range g.handlers[packet.ID] {
if err = ph.F(c, p, Packet758(packet)); err != nil {
return
}
if err = c.GetErr(); err != nil {
return
}
}
}
}