Introduction to the loot box roll function
Overview
This article walks you through how to modify the Extend Override app template for the loot box roll function and transform it into your own app that fits your requirements.
Contract function
There is only one function in the contract. It is shown in the snippet below which is a unary function called RollLootBoxRewards
.
service LootBox {
rpc RollLootBoxRewards(RollLootBoxRewardsRequest) returns (RollLootBoxRewardsResponse);
}
RollLootBoxRewards
RollLootBoxRewards
is called by AccelByte Gaming Services (AGS) when a player consumes a loot box, and it is used for implementing the logic to decide which reward items will be given to a specified player from a given loot box rewards list.
In this example, we are generating as many reward item results in the requested quantity.
First, we select rewards randomly based on the total reward weight sum. The bigger the weight, the higher probability for the reward to be selected. We will also get a random item from the selected reward. Finally, we add that item to the final results as the response.
- Go
- C#
- Java
- Python
In the app, the following function can be found in pkg/service/lootbox_service.go
.
func (s *LootboxServiceServer) RollLootBoxRewards(_ context.Context, req *pb.RollLootBoxRewardsRequest) (*pb.RollLootBoxRewardsResponse, error) {
rewards := req.GetItemInfo().GetLootBoxRewards()
rewardWeightSum := 0
for _, r := range rewards {
rewardWeightSum += int(r.Weight)
}
var resultItems []*pb.RewardObject
for i := int32(0); i < req.GetQuantity(); i++ {
selectedIdx := 0
for r := int(random(rewardWeightSum)); selectedIdx < len(rewards); selectedIdx++ {
r -= int(rewards[selectedIdx].GetWeight())
if r <= 0.0 {
break
}
}
selectedReward := rewards[selectedIdx]
selectedRewardItemCount := len(selectedReward.GetItems())
selectedItemIdx := int(math.Round(random(selectedRewardItemCount - 1)))
selectedItem := selectedReward.GetItems()[selectedItemIdx]
resultItems = append(resultItems, &pb.RewardObject{
ItemId: selectedItem.ItemId,
ItemSku: selectedItem.ItemSku,
Count: selectedItem.Count,
})
}
response := &pb.RollLootBoxRewardsResponse{Rewards: resultItems}
return response, nil
}
In the app, the following function can be found in src/AccelByte.PluginArch.LootBox.Demo.Server/Services/LootboxFunctionService.cs
.
public override Task<RollLootBoxRewardsResponse> RollLootBoxRewards(RollLootBoxRewardsRequest request, ServerCallContext context)
{
var rewards = request.ItemInfo.LootBoxRewards;
int rewardWeightSum = 0;
foreach (var reward in rewards)
rewardWeightSum += reward.Weight;
Random rand = new Random();
List<RewardObject> result = new List<RewardObject>();
for (int i = 0; i < request.Quantity; i++)
{
int selectedIdx = 0;
for (double r = rand.NextDouble() * rewardWeightSum; selectedIdx < rewards.Count - 1; selectedIdx++)
{
r -= rewards[selectedIdx].Weight;
if (r <= 0.0)
break;
}
var selectedReward = rewards[selectedIdx];
int itemCount = selectedReward.Items.Count;
int selectedItemIdx = (int)Math.Round(rand.NextDouble() * (double)(itemCount - 1));
BoxItemObject selectedItem = selectedReward.Items[selectedItemIdx];
var rewardObject = new RewardObject()
{
ItemId = selectedItem.ItemId,
ItemSku = selectedItem.ItemSku,
Count = selectedItem.Count
};
result.Add(rewardObject);
}
RollLootBoxRewardsResponse response = new RollLootBoxRewardsResponse();
response.Rewards.AddRange(result);
return Task.FromResult(response);
}
In the app, the following function can be found in src/main/java/net/accelbyte/service/LootboxServiceImplementation.java
.
@Override
public void rollLootBoxRewards(RollLootBoxRewardsRequest request, StreamObserver<RollLootBoxRewardsResponse> responseObserver) {
List<RewardObject> finalItems = new ArrayList<>();
List<LootBoxItemInfo.LootBoxRewardObject> rewards = request.getItemInfo().getLootBoxRewardsList();
int rewardWeightSum = 0;
for (LootBoxItemInfo.LootBoxRewardObject reward: rewards) {
rewardWeightSum += reward.getWeight();
}
for (int i=0;i<request.getQuantity();i++)
{
int selIdx = 0;
for (double r = Math.random() * rewardWeightSum; selIdx < rewards.size() - 1; ++selIdx) {
r -= rewards.get(selIdx).getWeight();
if (r <= 0.0)
break;
}
LootBoxItemInfo.LootBoxRewardObject selReward = rewards.get(selIdx);
int itemCount = selReward.getItemsCount();
int selItemIdx = (int)Math.round(Math.random() * (itemCount - 1));
BoxItemObject selItem = selReward.getItems(selItemIdx);
RewardObject rewardItem = RewardObject
.newBuilder()
.setItemId(selItem.getItemId())
.setItemSku(selItem.getItemSku())
.setCount(selItem.getCount())
.build();
finalItems.add(rewardItem);
}
RollLootBoxRewardsResponse response = RollLootBoxRewardsResponse
.newBuilder()
.addAllRewards(finalItems)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
In the app, the following function can be found in src/app/services/lootbox_service.py
.
async def RollLootBoxRewards(self, request: RollLootBoxRewardsRequest, context):
self.log_payload(f'{self.RollLootBoxRewards.__name__} request: %s', request)
final_items: List[RewardObject] = []
rewards: List[LootBoxItemInfo.LootBoxRewardObject] = request.itemInfo.lootBoxRewards
reward_weight_sum: int = 0
reward_weight_sum = sum(reward.weight for reward in rewards)
for i in range(request.quantity):
for sel_idx in range(len(rewards)):
r = random.random() * reward_weight_sum
r -= rewards[sel_idx].weight
if r <= 0.0:
break
sel_reward: LootBoxItemInfo.LootBoxRewardObject = rewards[sel_idx]
item_count: int = len(sel_reward.items)
sel_item_idx: int = random.randint(0, item_count-1)
sel_item: BoxItemObject = sel_reward.items[sel_item_idx]
reward_item: RewardObject = RewardObject(
itemId=sel_item.itemId,
itemSku=sel_item.itemSku,
count=sel_item.count,
)
final_items.append(reward_item)
response: RollLootBoxRewardsResponse = RollLootBoxRewardsResponse(
rewards=final_items
)
self.log_payload(f'{self.RollLootBoxRewards.__name__} response: %s', response)
return response