package minecraft import ( "coreapp/util" "coreapp/util/ws" "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "google.golang.org/protobuf/proto" "math/rand" "net/http" "strings" "sync" "time" ) type ServerType string const ( ServerTypeUnknown ServerType = "" ServerTypeLobby ServerType = "LOBBY" ServerTypeRoom ServerType = "ROOM" ServerTypeLimbo ServerType = "LIMBO" ServerTypeSystem ServerType = "SYSTEM" ) func (typ ServerType) Prefix() string { switch typ { case ServerTypeLobby: return "L" case ServerTypeRoom: return "mini" case ServerTypeLimbo: return "" case ServerTypeSystem: return "S" case ServerTypeUnknown: default: return "U" } return "U" } type Server struct { Id string `json:"id"` Name string `json:"name"` Group string `json:"group"` Host string `json:"host"` Port int `json:"port"` Type ServerType `json:"type"` Motd string `json:"motd"` Players []string `json:"players"` LastHB time.Time } var servers = sync.Map{} var serverProxyConn *ws.Conn var serverProxyLastHB time.Time func ServerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { if strings.HasSuffix(c.Request.URL.Path, "ws") || (c.Request.Method == "POST" && !strings.HasSuffix(c.Request.URL.Path, "/register")) { if c.GetHeader("Authorization") != "NEXTCRAFT" { c.AbortWithStatusJSON(401, gin.H{ "code": 1000, "message": "Who are you?", }) return } serverId := c.Query("id") if serverId == "" { c.AbortWithStatusJSON(400, gin.H{ "code": -1, "message": "请提供服务器ID", }) return } if serverId != "PROXY" { if _, ok := servers.Load(serverId); !ok { c.AbortWithStatusJSON(404, gin.H{ "code": -1, "message": "服务器不存在", }) return } } c.Set("serverId", serverId) } } } func ServerList(c *gin.Context) { var serverList []Server // 遍历 sync.Map servers.Range(func(key, value interface{}) bool { server := value.(*Server) serverList = append(serverList, *server) return true // 继续遍历 }) c.JSON(200, gin.H{ "code": 0, "message": "success", "data": serverList, }) } type ServerRegisterData struct { Name string `json:"name,omitempty"` Host string `json:"host"` Port int `json:"port"` Type ServerType `json:"type"` Group string `json:"group,omitempty"` Motd string `json:"motd,omitempty"` } func ServerRegister(c *gin.Context) { data := &ServerRegisterData{} if err := c.ShouldBindJSON(data); err != nil { c.JSON(400, gin.H{ "code": -1, "message": "请提供正确的注册信息", }) return } if data.Name == "" { data.Name = "AUTO" } if data.Type == "" { c.JSON(400, gin.H{ "code": -1, "message": "请提供正确的服务器类型{LOBBY, ROOM, SYSTEM, LIMBO, UNKNOWN}", }) return } if data.Host == "" || data.Port == 0 { c.JSON(400, gin.H{ "code": -1, "message": "请提供正确的服务器IP和端口", }) return } if data.Group == "" { data.Group = "default" } server := &Server{ Id: generateServerId(data.Type.Prefix()), Host: data.Host, Port: data.Port, Type: data.Type, Group: data.Group, LastHB: time.Now(), } if data.Name == "AUTO" { server.Name = server.Id } else { server.Name = data.Name } servers.Store(server.Id, server) // 使用 Store 替代原来的 map 写入 newPkt := &ServerNewPacket{ ServerId: server.Id, Name: server.Name, Host: server.Host, Port: int32(server.Port), Group: server.Group, Motd: server.Motd, } if serverProxyConn != nil { buf, err := serverWrapPacket(ServerPacketType_NEW, newPkt) if err != nil { panic(err) } serverProxyConn.WriteChan <- ws.Binary(buf) } c.JSON(200, gin.H{ "code": 0, "message": "success", "data": server, }) } func ServerUnregister(c *gin.Context) { serverId := c.GetString("serverId") deletePkt := &ServerDeletePacket{ ServerId: serverId, } if serverProxyConn != nil { buf, err := serverWrapPacket(ServerPacketType_DELETE, deletePkt) if err != nil { panic(err) } serverProxyConn.WriteChan <- ws.Binary(buf) } servers.Delete(serverId) // 直接删除 c.JSON(200, gin.H{ "code": 0, "message": "success", }) } // ServerMessage 函数用于处理服务器消息 func ServerMessage(c *gin.Context) { conn, err := serverUpgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "code": -666, "message": "WebSocket升级失败", }) panic(c.Error(err)) return } wrapped := ws.Wrap(conn) go wrapped.ReadLoop() go wrapped.WriteLoop() if c.GetString("serverId") == "PROXY" { serverProxyConn = wrapped serverProxyLastHB = time.Now() servers.Range(func(_, value any) bool { server := value.(*Server) newPkt := &ServerNewPacket{ ServerId: server.Id, Name: server.Name, Host: server.Host, Port: int32(server.Port), Group: server.Group, Motd: server.Motd, } buf, err := serverWrapPacket(ServerPacketType_NEW, newPkt) if err != nil { panic(err) } wrapped.WriteChan <- ws.Binary(buf) return true }) go serverProxyReadLoop(wrapped) } else { go serverReadLoop(wrapped, c.GetString("serverId")) } } func serverReadLoop(conn *ws.Conn, serverId string) { defer func() { fmt.Printf("%s closed\n", serverId) }() for { // 使用 Load 读取,避免并发问题 serverVal, ok := servers.Load(serverId) if !ok { conn.Close() return } server := serverVal.(*Server) select { case msg := <-conn.ReadChan: if msg == nil { return } pkt := &ServerPacket{} err := proto.Unmarshal(msg, pkt) if err != nil { panic(err) } switch pkt.GetTyp() { case ServerPacketType_PING: // 更新 LastHB(由于 Server 本身是结构体指针,可以直接修改) server.LastHB = time.Now() resp := &ServerPacket{ Typ: ServerPacketType_PONG, } buf, err := proto.Marshal(resp) if err != nil { panic(err) } conn.WriteChan <- ws.Binary(buf) case ServerPacketType_CONNECT: if serverProxyConn != nil { serverProxyConn.WriteChan <- ws.Binary(msg) } case ServerPacketType_JOIN: var payload ServerPlayerPacket err := proto.Unmarshal(pkt.GetPayload(), &payload) if err != nil { continue } server.Players = append(server.Players, payload.Player) case ServerPacketType_LEAVE: var payload ServerPlayerPacket err := proto.Unmarshal(pkt.GetPayload(), &payload) if err != nil { continue } for i, player := range server.Players { if player == payload.Player { server.Players = append(server.Players[:i], server.Players[i+1:]...) break } } case ServerPacketType_LIST: var payload ServerListPacket err := proto.Unmarshal(pkt.GetPayload(), &payload) if err != nil { panic(err) } reply := &ServerListPacket{ ServerId: payload.ServerId, } if s, ok := servers.Load(payload.ServerId); ok { reply.Players = s.(*Server).Players buf, err := proto.Marshal(reply) if err != nil { panic(err) } conn.WriteChan <- ws.Binary(buf) return } } default: if conn.IsClosed() { return } } } } func serverProxyReadLoop(conn *ws.Conn) { defer func() { serverProxyConn = nil }() for { msg := <-conn.ReadChan if msg == nil { return } pkt := &ServerPacket{} err := proto.Unmarshal(msg, pkt) if err != nil { continue } switch pkt.GetTyp() { case ServerPacketType_PING: serverProxyLastHB = time.Now() resp := &ServerPacket{ Typ: ServerPacketType_PONG, } buf, err := proto.Marshal(resp) if err != nil { panic(err) } conn.WriteChan <- ws.Binary(buf) } } } var serverUpgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func generateServerId(prefix string) string { return prefix + util.RandStringNumber(rand.Intn(2)+1) + strings.ToUpper(util.RandStringAlphabet(rand.Intn(2)+1)) } var serverHBTicker = time.NewTicker(time.Second * 5) func init() { go func() { for range serverHBTicker.C { // 遍历所有服务器,检查心跳 servers.Range(func(key, value interface{}) bool { server := value.(*Server) if time.Since(server.LastHB) > time.Second*10 { deletePkt := &ServerDeletePacket{ ServerId: server.Id, } if serverProxyConn != nil { buf, err := serverWrapPacket(ServerPacketType_DELETE, deletePkt) if err != nil { panic(err) } serverProxyConn.WriteChan <- ws.Binary(buf) } servers.Delete(key) // 超时则删除 } return true // 继续遍历 }) if time.Since(serverProxyLastHB) > time.Second*10 && serverProxyConn != nil { serverProxyConn.Close() } } }() } func serverWrapPacket(typ ServerPacketType, payload proto.Message) ([]byte, error) { pkt := &ServerPacket{ Typ: typ, Payload: nil, } if payload != nil { buf, err := proto.Marshal(payload) if err != nil { return nil, err } pkt.Payload = buf } buf, err := proto.Marshal(pkt) if err != nil { return nil, err } return buf, nil }