148 lines
3.4 KiB
Go
148 lines
3.4 KiB
Go
package game
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/gin-contrib/pprof"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/protobuf/proto"
|
|
"minotaur/game/protobuf/protobuf"
|
|
"minotaur/utils/gin/middlewares"
|
|
"minotaur/utils/log"
|
|
"minotaur/utils/timer"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
loginTimeoutTimerName = "player_login_timeout_timer"
|
|
)
|
|
|
|
type OnCreateConnHandleFunc func() Conn
|
|
type gateway struct {
|
|
httpServer *http.Server
|
|
}
|
|
|
|
func (slf *gateway) run(stateMachine *StateMachine, appName string, port int, onCreateConnHandleFunc OnCreateConnHandleFunc) *gateway {
|
|
|
|
// Gin WebSocket
|
|
gin.SetMode(gin.ReleaseMode)
|
|
router := gin.New()
|
|
router.Use(middlewares.Logger(log.Logger), middlewares.Cors())
|
|
pprof.Register(router) // pprof 可视化分析
|
|
|
|
var upgrade = websocket.Upgrader{
|
|
ReadBufferSize: 4096,
|
|
WriteBufferSize: 4096,
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
router.GET(fmt.Sprintf("/%s", appName), func(context *gin.Context) {
|
|
ip := context.GetHeader("X-Real-IP")
|
|
ws, err := upgrade.Upgrade(context.Writer, context.Request, nil)
|
|
if err != nil {
|
|
log.Error("Gateway", zap.Error(err))
|
|
return
|
|
}
|
|
if len(ip) == 0 {
|
|
addr := ws.RemoteAddr().String()
|
|
if index := strings.LastIndex(addr, ":"); index != -1 {
|
|
ip = addr[0:index]
|
|
}
|
|
}
|
|
conn := onCreateConnHandleFunc()
|
|
if err := context.ShouldBind(conn); err != nil {
|
|
log.Error("Gateway", zap.Error(err))
|
|
context.AbortWithStatus(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
playerGuid, err := stateMachine.sonyflake.NextID()
|
|
if err != nil {
|
|
log.Error("Gateway", zap.Error(err))
|
|
context.AbortWithStatus(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
player := &Player{
|
|
guid: int64(playerGuid),
|
|
conn: conn,
|
|
ws: ws,
|
|
ip: ip,
|
|
Manager: timer.GetManager(64),
|
|
GameTimer: stateMachine.Manager,
|
|
}
|
|
|
|
channelId, size := stateMachine.channelStrategy()
|
|
channel := stateMachine.channel(channelId, size)
|
|
player, err = channel.join(player)
|
|
if err != nil {
|
|
player.exit(err.Error())
|
|
return
|
|
}
|
|
|
|
player.channel.push(new(Message).Init(MessageTypeEvent, EventTypeGuestJoin, player))
|
|
if stateMachine.loginTimeout > 0 {
|
|
player.After(loginTimeoutTimerName, stateMachine.loginTimeout, func() {
|
|
player.exit("login timeout")
|
|
})
|
|
}
|
|
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
player.exit()
|
|
}
|
|
}()
|
|
|
|
for {
|
|
if err := player.ws.SetReadDeadline(time.Now().Add(time.Second * 30)); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
_, data, err := player.ws.ReadMessage()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var msg = new(protobuf.Message)
|
|
if err = proto.Unmarshal(data, msg); err != nil {
|
|
continue
|
|
}
|
|
|
|
player.channel.push(new(Message).Init(MessageTypePlayer, player, msg.Code, msg.Data))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// HttpServer
|
|
slf.httpServer = &http.Server{
|
|
Addr: fmt.Sprintf(":%d", port),
|
|
Handler: router,
|
|
}
|
|
log.Info("GatewayStart", zap.String("listen", slf.httpServer.Addr))
|
|
|
|
go func() {
|
|
if err := slf.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
stateMachine.errChannel <- err
|
|
return
|
|
}
|
|
}()
|
|
|
|
return slf
|
|
}
|
|
|
|
func (slf *gateway) shutdown(context context.Context) error {
|
|
log.Info("GatewayShutdown", zap.String("stateMachine", "start"))
|
|
if err := slf.httpServer.Shutdown(context); err != nil {
|
|
return err
|
|
}
|
|
log.Info("GatewayShutdown", zap.String("stateMachine", "normal"))
|
|
return nil
|
|
}
|