ショップアイテムのローテーション入門
注釈:本資料はAI技術を用いて翻訳されています。
概要
この記事では、ショップアイテムのローテーションとショップセクションアイテムのバックフィルに使用される Extend Override アプリの API コントラクト(Protobuf)について説明します。
service SectionService {
/**
GetRotationItems: 現在のローテーションアイテムを取得します。このメソッドはローテーションタイプが CUSTOM の場合に呼び出されます
*/
rpc GetRotationItems(GetRotationItemsRequest) returns (GetRotationItemsResponse);
/**
Backfill メソッドのトリガー条件:
1. ローテーションタイプが FIXED_PERIOD
2. Bulkfill タイプが CUSTOM
3. ユーザーが現在のローテーションアイテムのいずれかを既に所有している
*/
rpc Backfill(BackfillRequest) returns (BackfillResponse);
}
API コントラクト
GetRotationItems
このメソッドは、ローテーションタイプが CUSTOM のストア表示セクションに対して、AGS プラットフォームの Commerce サービスによって呼び出されます。この関数は、ユーザーがアクティブな表示セクションを取得しようとしたときに呼び出されます。開発者は、特定のユーザーのリクエストに対して、指定されたゲーム内ストアセクションに何を表示するかを決定するロジックを実装できます。
この例では、GetRotationItems のロジックを実装して、1時間ごとにローテーションされる異なるアイテムを返します。
- C#
- Go
- Java
- Python
アプリでは、次の関数が src/AccelByte.PluginArch.ItemRotation.Demo.Server/Services/SectionFunctionService.cs にあります。
public override Task<GetRotationItemsResponse> GetRotationItems(GetRotationItemsRequest request, ServerCallContext context)
{
List<SectionItemObject> items = new List<SectionItemObject>(request.SectionObject.Items);
float inputCount = items.Count;
float currentPoint = DateTime.Now.Hour;
int selectedIndex = (int)Math.Floor((inputCount / _UpperLimit) * currentPoint);
SectionItemObject selectedItem = items[selectedIndex];
GetRotationItemsResponse response = new GetRotationItemsResponse();
response.ExpiredAt = 0;
response.Items.Add(selectedItem);
return Task.FromResult(response);
}
アプリでは、次の関数が pkg/service/section_service.go にあります。
func (s SectionServiceServer) GetRotationItems(ctx context.Context, request *pb.GetRotationItemsRequest) (*pb.GetRotationItemsResponse, error) {
inputCount := len(request.GetSectionObject().GetItems())
currentPoint := time.Now().Hour()
selectedIndex := int(math.Floor((float64(inputCount) / float64(upperLimit)) * float64(currentPoint)))
selectedItem := request.GetSectionObject().GetItems()[selectedIndex]
responseItems := []*pb.SectionItemObject{
{
ItemId: selectedItem.ItemId,
ItemSku: selectedItem.ItemSku,
},
}
resp := pb.GetRotationItemsResponse{
Items: responseItems,
ExpiredAt: 0,
}
return &resp, nil
}
アプリでは、次の関数が src/main/java/net/accelbyte/service/SectionServiceImplementation.java にあります。
@Override
public void getRotationItems(GetRotationItemsRequest request, StreamObserver<GetRotationItemsResponse> responseObserver) {
List<SectionItemObject> items = request.getSectionObject().getItemsList();
final int inputCount = items.size();
final float currentPoint = (float) LocalDateTime.now().getHour();
final int selectedIndex = (int)Math.floor((inputCount/upperLimit)*currentPoint);
SectionItemObject selectedItem = items.get(selectedIndex);
List<SectionItemObject> responseItems = new ArrayList<>();
responseItems.add(selectedItem);
GetRotationItemsResponse response = GetRotationItemsResponse
.newBuilder()
.setExpiredAt(0)
.addAllItems(responseItems)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
アプリでは、次の関数が src/app/services/section_service.py にあります。
async def GetRotationItems(self, request : GetRotationItemsRequest, context):
"""*
GetRotationItems: 現在のローテーションアイテムを取得します。このメソッドはローテーションタイプが CUSTOM の場合に呼び出されます
"""
self.log_payload(f'{self.GetRotationItems.__name__} request: %s', request)
items : List[SectionItemObject] = request.sectionObject.items
input_count : int = len(items)
current_point : float = float(datetime.now().hour)
selected_index : int = int(math.floor((input_count/self.upper_limit)*current_point))
selected_item : SectionItemObject = items[selected_index]
response_items : List[SectionItemObject] = [selected_item]
response : GetRotationItemsResponse = GetRotationItemsResponse(expiredAt=0, items=response_items)
self.log_payload(f'{self.GetRotationItems.__name__} response: %s', response)
return response
Backfill
Backfill は、セクションのローテーションタイプが FIXED_PERIOD、バックフィルタイプが CUSTOM、かつ現在のアイテムローテーションに既に所有しているアイテムがある場合に呼び出されます。このユースケースの1つは、所有しているアイテムの置き換えロジックを実装できることです。
この例では、既に所有しているアイテムを、現在のインデックスにある新しいランダムなアイテム ID で置き換えます。
- C#
- Go
- Java
- Python
アプリでは、次の関数が src/AccelByte.PluginArch.ItemRotation.Demo.Server/Services/SectionFunctionService.cs にあります。
public override Task<BackfillResponse> Backfill(BackfillRequest request, ServerCallContext context)
{
BackfillResponse response = new BackfillResponse();
foreach (var item in request.Items)
{
if (item.Owned)
{
BackfilledItemObject newItem = new BackfilledItemObject()
{
ItemId = Guid.NewGuid().ToString().Replace("-", ""),
Index = item.Index
};
response.BackfilledItems.Add(newItem);
}
}
return Task.FromResult(response);
}
アプリでは、次の関数が pkg/service/section_service.go にあります。
func (s SectionServiceServer) Backfill(ctx context.Context, request *pb.BackfillRequest) (*pb.BackfillResponse, error) {
var newItems []*pb.BackfilledItemObject
for _, item := range request.GetItems() {
if item.Owned {
// ユーザーがアイテムを所有している場合、新しいアイテム ID で置き換えます。
// アイテム ID は例として、ランダムに生成されます。
newItem := &pb.BackfilledItemObject{
ItemId: strings.ReplaceAll(uuid.NewString(), "-", ""),
Index: item.Index,
}
newItems = append(newItems, newItem)
}
}
resp := &pb.BackfillResponse{BackfilledItems: newItems}
return resp, nil
}
アプリでは、次の関数が src/main/java/net/accelbyte/service/SectionServiceImplementation.java にあります。
@Override
public void backfill(BackfillRequest request, StreamObserver<BackfillResponse> responseObserver) {
List<BackfilledItemObject> newItems = new ArrayList<>();
for(RotationItemObject item : request.getItemsList()) {
if (item.getOwned()) {
BackfilledItemObject newItem = BackfilledItemObject
.newBuilder()
.setItemId(UUID.randomUUID().toString().replace("-",""))
.setIndex(item.getIndex())
.build();
newItems.add(newItem);
}
}
BackfillResponse response = BackfillResponse
.newBuilder()
.addAllBackfilledItems(newItems)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
アプリでは、次の関数が src/app/services/section_service.py にあります。
async def Backfill(self, request : BackfillRequest, context):
"""*
Backfill メソッドのトリガー条件:
1. ローテーションタイプが FIXED_PERIOD
2. Backfill タイプが CUSTOM
3. ユーザーが現在のローテーションアイテムのいずれかを既に所有している
"""
self.log_payload(f'{self.Backfill.__name__} request: %s', request)
new_items : List[BackfilledItemObject] = []
item : RotationItemObject
for item in request.items:
if item.owned:
new_item : BackfilledItemObject = BackfilledItemObject(itemId=str(uuid.uuid4()).replace("-",""), index=item.index)
new_items.append(new_item)
response : BackfillResponse = BackfillResponse(backfilledItems=new_items)
self.log_payload(f'{self.Backfill.__name__} response: %s', response)
return response
gRPC リクエスト処理の詳細については、こちらをご覧ください。