package config import ( "bytes" "encoding/json" "io/ioutil" "net/http" "time" "github.com/heroiclabs/nakama-common/runtime" ) var ( BackendBaseURL = "http://host.docker.internal:9991/api/internal" // 默认值 InternalAPIKey = "bindbox-internal-secret-2024" // 必须与后端匹配 ) var httpClient = &http.Client{ Timeout: 5 * time.Second, } // MakeInternalRequest 发送带认证的内部API请求的辅助函数 func MakeInternalRequest(method, url string, body []byte) (*http.Response, error) { req, err := http.NewRequest(method, url, bytes.NewBuffer(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Internal-Key", InternalAPIKey) return httpClient.Do(req) } type MinesweeperConfig struct { GridSize int `json:"grid_size"` BombCount int `json:"bomb_count"` ItemMin int `json:"item_min"` ItemMax int `json:"item_max"` HPInit int `json:"hp_init"` MatchPlayerCount int `json:"match_player_count"` EnabledItems map[string]bool `json:"enabled_items"` ItemWeights map[string]int `json:"item_weights"` CharacterHP map[string]int `json:"character_hp"` // 每个角色的独立HP配置 TurnDuration int `json:"turn_duration"` // 每回合的限时(秒) } func GetMinesweeperConfig(logger runtime.Logger) *MinesweeperConfig { url := BackendBaseURL + "/game/minesweeper/config" logger.Info("Fetching minesweeper config from: %s", url) resp, err := MakeInternalRequest("GET", url, nil) if err != nil { logger.Error("Network error fetching config (check BackendBaseURL): %v", err) return nil } defer resp.Body.Close() if resp.StatusCode != 200 { logger.Error("Backend config API returned status: %d (URL: %s)", resp.StatusCode, url) return nil } body, err := ioutil.ReadAll(resp.Body) if err != nil { logger.Error("Failed to read config response body: %v", err) return nil } var config MinesweeperConfig if err := json.Unmarshal(body, &config); err != nil { logger.Error("Failed to parse minesweeper config: %v. Raw body: %s", err, string(body)) return nil } return &config } type VerifyTicketResponse struct { Valid bool `json:"valid"` UserID string `json:"user_id"` RemainingTimes int `json:"remaining_times"` } type SettleGameResponse struct { Success bool `json:"success"` Reward string `json:"reward"` } // SettlePlayerRecord 单个玩家的结算数据 type SettlePlayerRecord struct { UserID int64 `json:"user_id"` Ticket string `json:"ticket"` Win bool `json:"win"` Rank int `json:"rank"` Score int `json:"score"` DamageDealt int `json:"damage_dealt"` DamageTaken int `json:"damage_taken"` Kills int `json:"kills"` ChestsCollected int `json:"chests_collected"` RoundsSurvived int `json:"rounds_survived"` } // SettleGameWithBackend 批量结算整局所有玩家(替换旧版单人结算) func SettleGameWithBackend(logger runtime.Logger, matchID, gameType string, totalRounds int, players []SettlePlayerRecord) { logger.Info("Settling game with backend: match=%s, type=%s, players=%d", matchID, gameType, len(players)) reqBody, _ := json.Marshal(map[string]interface{}{ "match_id": matchID, "game_type": gameType, "total_rounds": totalRounds, "players": players, }) go func() { resp, err := MakeInternalRequest("POST", BackendBaseURL+"/game/settle", reqBody) if err != nil { logger.Error("Failed to call backend settle API: %v", err) return } defer resp.Body.Close() if resp.StatusCode != 200 { logger.Error("Backend settle returned non-200: %d", resp.StatusCode) } else { logger.Info("Game settled successfully with backend") } }() } type GameTokenInfo struct { Valid bool `json:"valid"` UserID int64 `json:"user_id"` Username string `json:"username"` Avatar string `json:"avatar"` GameType string `json:"game_type"` Ticket string `json:"ticket"` Error string `json:"error"` } // ValidateGameToken 向后端验证游戏Token并返回用户信息 func ValidateGameToken(logger runtime.Logger, gameToken string) *GameTokenInfo { logger.Info("Validating game token with backend") reqBody, _ := json.Marshal(map[string]string{ "game_token": gameToken, }) resp, err := MakeInternalRequest("POST", BackendBaseURL+"/game/validate-token", reqBody) if err != nil { logger.Error("Failed to call backend validate-token API: %v", err) return nil } defer resp.Body.Close() if resp.StatusCode != 200 { logger.Error("Backend validate-token returned non-200 status: %d", resp.StatusCode) return nil } body, _ := ioutil.ReadAll(resp.Body) var result GameTokenInfo if err := json.Unmarshal(body, &result); err != nil { logger.Error("Failed to parse validate-token response: %v", err) return nil } if !result.Valid { logger.Warn("Game token invalid: %s", result.Error) return nil } return &result } // GetBackendBaseURL 允许 main.go 从环境变量更新基础URL func SetBackendBaseURL(url string) { BackendBaseURL = url } func GetBackendBaseURL() string { return BackendBaseURL }