From 61449fe2cf39b0c7cdeb3fafbbc96205154dffd3 Mon Sep 17 00:00:00 2001 From: Saurabh Wagh Date: Tue, 17 Sep 2019 17:48:47 -0700 Subject: [PATCH] Implement Roster based MMF that populates roster pools with tickets from the pools supplied. (#806) * update * Implement the Roster based match function for scale tests --- .../golang/rosterbased/mmf/matchfunction.go | 98 +++++++++++++++++-- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/examples/functions/golang/rosterbased/mmf/matchfunction.go b/examples/functions/golang/rosterbased/mmf/matchfunction.go index 2ef1c8535..42d61c394 100644 --- a/examples/functions/golang/rosterbased/mmf/matchfunction.go +++ b/examples/functions/golang/rosterbased/mmf/matchfunction.go @@ -12,28 +12,108 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package mmf provides a sample match function that uses the GRPC harness to set up 1v1 matches. -// This sample is a reference to demonstrate the usage of the GRPC harness and should only be used as -// a starting point for your match function. You will need to modify the -// matchmaking logic in this function based on your game's requirements. package mmf import ( + "fmt" + "time" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" mmfHarness "open-match.dev/open-match/pkg/harness/function/golang" "open-match.dev/open-match/pkg/pb" ) var ( - matchName = "roster-based-matchfunction" - logger = logrus.WithFields(logrus.Fields{ + matchName = "roster-based-matchfunction" + emptyRosterSpot = "EMPTY_ROSTER_SPOT" + logger = logrus.WithFields(logrus.Fields{ "app": "openmatch", "component": "mmf.rosterbased", }) ) -// MakeMatches is where your custom matchmaking logic lives. func MakeMatches(p *mmfHarness.MatchFunctionParams) ([]*pb.Match, error) { - logger.Infof("MakeMatches called for Profile:%v", p.ProfileName) - return []*pb.Match{}, nil + // This roster based match function expects the match profile to have a + // populated roster specifying the empty slots for each pool name and also + // have the ticket pools referenced in the roster. It generates matches by + // populating players from the specified pools into rosters. + wantTickets, err := wantPoolTickets(p.Rosters) + if err != nil { + return nil, err + } + + var matches []*pb.Match + for { + insufficientTickets := false + matchTickets := []*pb.Ticket{} + matchRosters := []*pb.Roster{} + // Loop through each pool wanted in the rosters and pick the number of + // wanted players from the respective Pool. + for poolName, want := range wantTickets { + have, ok := p.PoolNameToTickets[poolName] + if !ok { + // A wanted Pool was not found in the Pools specified in the profile. + insufficientTickets = true + break + } + + if len(have) < want { + // The Pool in the profile has fewer tickets than what the roster needs. + insufficientTickets = true + break + } + + // Populate the wanted tickets from the Tickets in the corresponding Pool. + matchTickets = append(matchTickets, have[0:want]...) + p.PoolNameToTickets[poolName] = have[want:] + var ids []string + for _, ticket := range matchTickets { + ids = append(ids, ticket.Id) + } + + matchRosters = append(matchRosters, &pb.Roster{ + Name: poolName, + TicketIds: ids, + }) + } + + if insufficientTickets { + // Ran out of Tickets. Matches cannot be created from the remaining Tickets. + break + } + + matches = append(matches, &pb.Match{ + MatchId: fmt.Sprintf("profile-%v-time-%v", p.ProfileName, time.Now().Format("2006-01-02T15:04:05.00")), + MatchProfile: p.ProfileName, + MatchFunction: matchName, + Tickets: matchTickets, + Rosters: matchRosters, + }) + } + + return matches, nil +} + +// wantPoolTickets parses the roster to return a map of the Pool name to the +// number of empty roster slots for that Pool. +func wantPoolTickets(rosters []*pb.Roster) (map[string]int, error) { + wantTickets := make(map[string]int) + for _, r := range rosters { + if _, ok := wantTickets[r.Name]; ok { + // We do not expect multiple Roster Pools to have the same name. + logger.Errorf("multiple rosters with same name not supported") + return nil, status.Error(codes.InvalidArgument, "multiple rosters with same name not supported") + } + + wantTickets[r.Name] = 0 + for _, slot := range r.TicketIds { + if slot == emptyRosterSpot { + wantTickets[r.Name] = wantTickets[r.Name] + 1 + } + } + } + + return wantTickets, nil }