首先我們會介紹 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: "om-function.open-match-demo.svc.cluster.local",
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 {
panic(err)
}
透過給定 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 (配對函式)於配對過程中被核心所調用,開發者需依據遊戲的需求,實作符合各種標籤屬性的配對方法,透過 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
}