Introduction to rotating shop items
Overview
This article walks you through how to modify the Extend Override app template for rotating shop item and transform it into your own app that fits your requirements.
Contract functions
There are two functions on the contract as shown in the snippet below.
service SectionService {
/**
GetRotationItems: get current rotation items, this method will be called by rotation type is CUSTOM
*/
rpc GetRotationItems(GetRotationItemsRequest) returns (GetRotationItemsResponse);
/**
Backfill method trigger condition:
1. Rotation type is FIXED_PERIOD
2. Bulkfill type is CUSTOM
3. User already owned any one of current rotation items.
*/
rpc Backfill(BackfillRequest) returns (BackfillResponse);
}
GetRotationItems
This method will be called by the AGS platform Commerce service for a store display section with rotation type CUSTOM. The function is called when users are trying to fetch active display sections. The developer can implement logic that determines what to show for that specified in-game store section for the particular user's request.
In this example, we'll implement the logic for GetRotationItems
to return different items that will be rotated every hour.
- Go
- C#
- Java
- Python
In the app, the following function can be found in 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
}
In the app, the following function can be found in 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);
}
In the app, the following function can be found in 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();
}
In the app, the following function can be found in src/app/services/section_service.py
.
async def GetRotationItems(self, request : GetRotationItemsRequest, context):
"""*
GetRotationItems: get current rotation items, this method will be called by rotation type is 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 is called when the section rotation type is FIXED_PERIOD
, the backfill type is CUSTOM, and there's already an owned item in the current item rotation. One of the use cases for this is you can implement the replacement logic for those owned items.
In this example, we'll replace items that are already owned by a new random item ID in its current index.
- Go
- C#
- Java
- Python
In the app, the following function can be found in 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 {
// if an item is owned by user, then replace it with new item id.
// item id will be generated randomly for example purpose.
newItem := &pb.BackfilledItemObject{
ItemId: strings.ReplaceAll(uuid.NewString(), "-", ""),
Index: item.Index,
}
newItems = append(newItems, newItem)
}
}
resp := &pb.BackfillResponse{BackfilledItems: newItems}
return resp, nil
}
In the app, the following function can be found in 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);
}
In the app, the following function can be found in 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();
}
In the app, the following function can be found in src/app/services/section_service.py
.
async def Backfill(self, request : BackfillRequest, context):
"""*
Backfill method trigger condition:
1. Rotation type is FIXED_PERIOD
2. Backfill type is CUSTOM
3. User already owned any one of current rotation items.
"""
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