package domain import ( "fmt" "time" ) type PreparedProposal struct { Proposal Proposal CasualLineup []CasualSlot } // PrepareProposal is the boundary between matchmaking and proposal state. It // never creates a proposal for a casual formation without one human per team, // or for a ranked formation whose verified identity/arena metadata fails the // ranked admission policy. func PrepareProposal(id string, playlist Playlist, formation MatchFormation, rankedParticipants []RankedParticipant, arena RankedArena, now time.Time) (PreparedProposal, error) { if len(formation.Selection.Players) < 2 || len(formation.Selection.Players) > 6 { return PreparedProposal{}, fmt.Errorf("invalid formed player count") } playerIDs := make([]string, 0, len(formation.Selection.Players)) seen := make(map[string]bool, len(formation.Selection.Players)) for _, player := range formation.Selection.Players { if player.PlayerID == "" || seen[player.PlayerID] { return PreparedProposal{}, fmt.Errorf("invalid formed player identity") } seen[player.PlayerID] = true playerIDs = append(playerIDs, player.PlayerID) } var lineup []CasualSlot switch playlist { case Casual: participants := make([]ConnectParticipant, 0, len(formation.Selection.Players)) for _, player := range formation.Teams.Team0 { participants = append(participants, ConnectParticipant{PlayerID: player.PlayerID, Team: 0}) } for _, player := range formation.Teams.Team1 { participants = append(participants, ConnectParticipant{PlayerID: player.PlayerID, Team: 1}) } var err error lineup, err = BuildCasualLineup(participants) if err != nil { return PreparedProposal{}, err } case Ranked: if len(rankedParticipants) != len(playerIDs) { return PreparedProposal{}, fmt.Errorf("ranked metadata does not match formed players") } metadata := make(map[string]bool, len(rankedParticipants)) for _, participant := range rankedParticipants { metadata[participant.PlayerID] = true } if len(metadata) != len(playerIDs) { return PreparedProposal{}, fmt.Errorf("ranked metadata has duplicate or unknown players") } for _, playerID := range playerIDs { if !metadata[playerID] { return PreparedProposal{}, fmt.Errorf("ranked metadata missing formed player") } } if err := ValidateRankedAdmission(rankedParticipants, arena); err != nil { return PreparedProposal{}, err } default: return PreparedProposal{}, fmt.Errorf("unsupported playlist") } proposal, err := NewProposal(id, playlist, playerIDs, now) if err != nil { return PreparedProposal{}, err } return PreparedProposal{Proposal: proposal, CasualLineup: lineup}, nil }