2021 iThome 鐵人賽

Software Development

徵坦補! 新手可! Open-Match 配對框架系列 第 6

Day6 Director & Match Function

首先我們會介紹 Director 是如何實作的,並介紹 Director 跟 Open-Match 核心互動的方式。而 Director 在完成配對指派的過程中,向核心調用 Match Function 所需完成的邏輯與接口,會一併於本編介紹。


Director 透過持續向 Open-Match Backend 呼叫,取出 Matches(配對組合) ,並且將符合配對邏輯的 Matches 與 DGS(遊戲伺服器)介接,可對照 demo director.go 狀態 "Connecting to backend" 至 "Match Match: Sending Request" 的部分。


MatchProfile (配對剖析)用來定義一場配對的條件或規格,藉由這些限制來決定哪一些玩家可以參與本次配對。MatchProfile 被傳遞至 Match Function 參與配對邏輯,以 MatchProfile 的條件搭配上 Match Function 的邏輯篩選,完成後產出配對結果。

type MatchProfile struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	// Name of this match profile.
	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	// Set of pools to be queried when generating a match for this MatchProfile.
	Pools []*Pool `protobuf:"bytes,3,rep,name=pools,proto3" json:"pools,omitempty"`
	// Customized information not inspected by Open Match, to be used by the match
	// making function, evaluator, and components making calls to Open Match.
	// Optional, depending on the requirements of the connected systems.
	Extensions map[string]*any.Any `protobuf:"bytes,5,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`

實作時需注意 Pools 將決定取用含有哪些條件的配對池

req := &pb.FetchMatchesRequest{
	Config: &pb.FunctionConfig{
		Host: "",
		Port: 50502,
		Type: pb.FunctionConfig_GRPC,

	//Pools 將決定取用含有哪些條件的配對池
	Profile: &pb.MatchProfile{
		Name: "1v1",
		Pools: []*pb.Pool{
				Name: "Everyone",

stream, err := be.FetchMatches(ds.Ctx, req)
if err != nil {


透過給定 Pool.DoubleRangeFilters, Pool.StringEqualsFilters, Pool.TagPresentFilters 可決定哪些條件可進入配對池。在上面的 demo 的例子來說,由於沒有指定 Pool filter 內容,所以所有的條件都可以進入配對。

type Pool struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	// A developer-chosen human-readable name for this Pool.
	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	// Set of Filters indicating the filtering criteria. Selected tickets must
	// match every Filter.
	DoubleRangeFilters  []*DoubleRangeFilter  `protobuf:"bytes,2,rep,name=double_range_filters,json=doubleRangeFilters,proto3" json:"double_range_filters,omitempty"`
	StringEqualsFilters []*StringEqualsFilter `protobuf:"bytes,4,rep,name=string_equals_filters,json=stringEqualsFilters,proto3" json:"string_equals_filters,omitempty"`
	TagPresentFilters   []*TagPresentFilter   `protobuf:"bytes,5,rep,name=tag_present_filters,json=tagPresentFilters,proto3" json:"tag_present_filters,omitempty"`
	// If specified, only Tickets created before the specified time are selected.
	CreatedBefore *timestamp.Timestamp `protobuf:"bytes,6,opt,name=created_before,json=createdBefore,proto3" json:"created_before,omitempty"`
	// If specified, only Tickets created after the specified time are selected.
	CreatedAfter *timestamp.Timestamp `protobuf:"bytes,7,opt,name=created_after,json=createdAfter,proto3" json:"created_after,omitempty"`

Match Function

Match Function (配對函式)於配對過程中被核心所調用,開發者需依據遊戲的需求,實作符合各種標籤屬性的配對方法,透過 MatchProfiles(配對剖析) 裡的 Tickets 產出每一場 Matches(配對組合)。


Run 為滿足 Open-Match 所需要 interface 的方法,可直接參考範例做法將配對池取出後,透過自訂義的 makeMatches (配對邏輯)完成期望的配對請求。

// 滿足 interface
// Run is this match function's implementation of the gRPC call defined in api/matchfunction.proto.
func (s *matchFunctionService) Run(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
	// Fetch tickets for the pools specified in the Match Profile.
	log.Printf("Generating proposals for function %v", req.GetProfile().GetName())

	poolTickets, err := matchfunction.QueryPools(stream.Context(), s.queryServiceClient, req.GetProfile().GetPools())
	if err != nil {
		log.Printf("Failed to query tickets for the given pools, got %s", err.Error())
		return err

	// 自定義配對邏輯
	proposals, err := makeMatches(poolTickets)
	if err != nil {
		log.Printf("Failed to generate matches, got %s", err.Error())
		return err

	log.Printf("Streaming %v proposals to Open Match", len(proposals))
	// Stream the generated proposals back to Open Match.
	for _, proposal := range proposals {
		if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil {
			log.Printf("Failed to stream proposals to Open Match, got %s", err.Error())
			return err

	return nil

func makeMatches(poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
	//TODO 做一些條件篩選

	return matches, nil

Day5 Game Frontend
Day7 配對條件範例 (角色,人數上限)
徵坦補! 新手可! Open-Match 配對框架30