Introduction to the loot box roll function
Extend is in Open Beta for AGS Premium Clients! This means Extend available for you to try on your development environment. You can submit your feedback through this link.
Extend is currently available for AGS Starter Closed Beta partners only.
Overview
AccelByte Gaming Services (AGS) has the capability to customize the behavior of the loot box roll function via Extend. In this guide, we will present the contract of the customization with an example of how to implement the roll function that will return a random reward item.
There's only one function on 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 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'll generate 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.
Code example:
- Go
- Python
- C#
- Java
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
}
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
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);
}
@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();
}