game/server/config/config.go
2026-04-20 16:07:22 +08:00

181 lines
5.2 KiB
Go
Executable File

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
}