package gameprovider import ( "chatapp3-golang/internal/common" "context" "net/http" "sort" "strings" ) // Registry 保存所有已注册的游戏厂商。 type Registry struct { providers map[string]Provider ordered []Provider } var emptyGameListUserIDs = map[int64]struct{}{ 2061394478798794754: {}, } // NewRegistry 创建厂商注册表。 func NewRegistry(providers ...Provider) *Registry { r := &Registry{ providers: make(map[string]Provider, len(providers)), ordered: make([]Provider, 0, len(providers)), } for _, provider := range providers { r.Register(provider) } return r } // Register 注册一个游戏厂商。 func (r *Registry) Register(provider Provider) { if provider == nil { return } key := normalizeKey(provider.Key()) if key == "" { panic("gameprovider: empty provider key") } if _, exists := r.providers[key]; exists { panic("gameprovider: duplicate provider key " + key) } r.providers[key] = provider r.ordered = append(r.ordered, provider) } // Resolve 根据名称解析厂商,名称大小写不敏感。 func (r *Registry) Resolve(key string) (Provider, error) { if r == nil { return nil, common.NewAppError(http.StatusNotFound, "provider_not_found", "game provider not found") } normalizedKey := normalizeKey(key) provider, ok := r.providers[normalizedKey] if !ok { provider, ok = r.providers[normalizeProviderAlias(normalizedKey)] } if !ok { return nil, common.NewAppError(http.StatusNotFound, "provider_not_found", "game provider not found") } return provider, nil } // List 返回已注册厂商列表。 func (r *Registry) List() ProviderListResponse { if r == nil || len(r.ordered) == 0 { return ProviderListResponse{Items: []ProviderSummary{}} } items := make([]ProviderSummary, 0, len(r.ordered)) for _, provider := range r.ordered { items = append(items, ProviderSummary{ Key: provider.Key(), DisplayName: provider.DisplayName(), }) } return ProviderListResponse{Items: items} } // ListShortcutGames 返回聚合后的快捷游戏列表。 func (r *Registry) ListShortcutGames(ctx context.Context, user AuthUser, roomID string) ([]RoomGameListItem, error) { if IsEmptyGameListUser(user.UserID) { return []RoomGameListItem{}, nil } if r == nil || len(r.ordered) == 0 { return []RoomGameListItem{}, nil } items := make([]RoomGameListItem, 0, len(r.ordered)) for _, provider := range r.ordered { resp, err := provider.ListShortcutGames(ctx, user, roomID) if err != nil { return nil, err } items = append(items, resp...) } sortRoomGameListItems(items) if len(items) > 5 { items = items[:5] } return items, nil } // ListRoomGames 返回聚合后的房间游戏列表。 func (r *Registry) ListRoomGames(ctx context.Context, user AuthUser, roomID, category string) (*RoomGameListResponse, error) { if IsEmptyGameListUser(user.UserID) { return &RoomGameListResponse{Items: []RoomGameListItem{}}, nil } if r == nil || len(r.ordered) == 0 { return &RoomGameListResponse{Items: []RoomGameListItem{}}, nil } items := make([]RoomGameListItem, 0, len(r.ordered)) for _, provider := range r.ordered { resp, err := provider.ListRoomGames(ctx, user, roomID, category) if err != nil { return nil, err } if resp != nil { items = append(items, resp.Items...) } } sortRoomGameListItems(items) return &RoomGameListResponse{Items: items}, nil } func IsEmptyGameListUser(userID int64) bool { _, ok := emptyGameListUserIDs[userID] return ok } // ResolveByLaunchRequest 根据启动请求解析内部对应的厂商。 func (r *Registry) ResolveByLaunchRequest(ctx context.Context, user AuthUser, req LaunchRequest) (Provider, error) { if r == nil { return nil, common.NewAppError(http.StatusNotFound, "provider_not_found", "game provider not found") } if req.ID <= 0 && strings.TrimSpace(req.GameID) == "" { return nil, common.NewAppError(http.StatusBadRequest, "missing_game_id", "id or gameId is required") } for _, provider := range r.ordered { ok, err := provider.SupportsLaunch(ctx, user, req) if err != nil { return nil, err } if ok { return provider, nil } } return nil, common.NewAppError(http.StatusNotFound, "game_not_found", "game provider not found for gameId") } // GetRoomState 返回聚合后的房间状态。 func (r *Registry) GetRoomState(ctx context.Context, user AuthUser, roomID string) (*RoomStateResponse, error) { if strings.TrimSpace(roomID) == "" { return nil, common.NewAppError(http.StatusBadRequest, "missing_room_id", "roomId is required") } state, _, err := r.resolveProviderByRoomState(ctx, user, roomID, "") if err != nil { return nil, err } if state != nil { return state, nil } return &RoomStateResponse{RoomID: roomID, State: roomStateIdle}, nil } // LaunchGame 按 gameId 自动分发到内部对应的厂商实现。 func (r *Registry) LaunchGame(ctx context.Context, user AuthUser, req LaunchRequest, clientIP string) (*LaunchResponse, error) { provider, err := r.ResolveByLaunchRequest(ctx, user, req) if err != nil { return nil, err } return provider.LaunchGame(ctx, user, req, clientIP) } // CloseGame 根据当前房间状态自动路由到真实厂商。 func (r *Registry) CloseGame(ctx context.Context, user AuthUser, req CloseRequest) (*RoomStateResponse, error) { if strings.TrimSpace(req.RoomID) == "" { return nil, common.NewAppError(http.StatusBadRequest, "missing_room_id", "roomId is required") } _, provider, err := r.resolveProviderByRoomState(ctx, user, req.RoomID, req.GameSessionID) if err != nil { return nil, err } if provider == nil { return &RoomStateResponse{RoomID: req.RoomID, State: roomStateIdle}, nil } return provider.CloseGame(ctx, user, req) } const roomStateIdle = "IDLE" func (r *Registry) resolveProviderByRoomState(ctx context.Context, user AuthUser, roomID, gameSessionID string) (*RoomStateResponse, Provider, error) { if r == nil { return nil, nil, common.NewAppError(http.StatusNotFound, "provider_not_found", "game provider not found") } sessionID := strings.TrimSpace(gameSessionID) var activeState *RoomStateResponse var activeProvider Provider for _, provider := range r.ordered { state, err := provider.GetRoomState(ctx, user, roomID) if err != nil { return nil, nil, err } if state == nil { continue } if sessionID != "" && strings.EqualFold(strings.TrimSpace(state.GameSessionID), sessionID) { return state, provider, nil } if !isIdleRoomState(state) && activeProvider == nil { activeState = state activeProvider = provider } } return activeState, activeProvider, nil } func isIdleRoomState(state *RoomStateResponse) bool { if state == nil { return true } if strings.TrimSpace(state.GameSessionID) != "" { return false } value := strings.ToUpper(strings.TrimSpace(state.State)) if value == "" { value = roomStateIdle } return value == roomStateIdle } func sortRoomGameListItems(items []RoomGameListItem) { sort.SliceStable(items, func(i, j int) bool { if items[i].Sort != items[j].Sort { return items[i].Sort > items[j].Sort } if items[i].GameID != items[j].GameID { return items[i].GameID < items[j].GameID } return strings.ToUpper(strings.TrimSpace(items[i].GameType)) < strings.ToUpper(strings.TrimSpace(items[j].GameType)) }) } func normalizeKey(key string) string { return strings.ToUpper(strings.TrimSpace(key)) } func normalizeProviderAlias(key string) string { switch normalizeKey(key) { case "LEADER": return "LINGXIAN" default: return normalizeKey(key) } }