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 } } } }