181 lines
5.2 KiB
Go
Executable File
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
|
|
}
|