fix room rocket in-room reward spread

This commit is contained in:
zhx 2026-06-11 17:58:48 +08:00
parent 1123821785
commit b55a01c0d6
2 changed files with 79 additions and 14 deletions

View File

@ -1053,13 +1053,6 @@ func (s *Service) settleRoomRocketRewards(ctx context.Context, now time.Time, cu
})
return nil
}
addRandomReward := func(role string, userID int64, pool []RoomRocketRewardItem) error {
item, ok := selectRoomRocketReward(launch.RocketID, role, userID, pool)
if !ok {
return nil
}
return addRewardItem(role, userID, item)
}
addAllRewards := func(role string, userID int64, pool []RoomRocketRewardItem) error {
for _, item := range pool {
if err := addRewardItem(role, userID, item); err != nil {
@ -1069,9 +1062,14 @@ func (s *Service) settleRoomRocketRewards(ctx context.Context, now time.Time, cu
return nil
}
inRoomRewards := selectRoomRocketRewardsForUsers(launch.RocketID, roomRocketRewardInRoom, inRoomUserIDs, levelCfg.InRoomRewards)
for _, userID := range inRoomUserIDs {
// 在房奖励是大面积发放池,每个在线用户只按权重抽一个奖项,避免房间人数放大单次火箭成本。
if err := addRandomReward(roomRocketRewardInRoom, userID, levelCfg.InRoomRewards); err != nil {
// 在房奖励是大面积发放池,每个在线用户只拿一个奖项;同一枚火箭内按无放回周期分配,避免配置了金币和麦位声波时整房都落到同一个奖项。
item, ok := inRoomRewards[userID]
if !ok {
continue
}
if err := addRewardItem(roomRocketRewardInRoom, userID, item); err != nil {
return nil, err
}
}
@ -1111,7 +1109,56 @@ func (s *Service) grantRoomRocketReward(ctx context.Context, _ time.Time, curren
return resp.GetGrant().GetGrantId(), nil
}
func validRoomRocketRewardPool(pool []RoomRocketRewardItem) []RoomRocketRewardItem {
valid := make([]RoomRocketRewardItem, 0, len(pool))
for _, item := range pool {
if item.Weight <= 0 || item.ResourceGroupID <= 0 {
continue
}
valid = append(valid, item)
}
return valid
}
func selectRoomRocketRewardsForUsers(rocketID string, role string, userIDs []int64, pool []RoomRocketRewardItem) map[int64]RoomRocketRewardItem {
assignments := make(map[int64]RoomRocketRewardItem, len(userIDs))
if len(userIDs) == 0 {
return assignments
}
basePool := validRoomRocketRewardPool(pool)
if len(basePool) == 0 {
return assignments
}
available := append([]RoomRocketRewardItem(nil), basePool...)
cycle := 0
for index, userID := range userIDs {
if userID <= 0 {
continue
}
if len(available) == 0 {
// 一轮内每个配置项最多发一次;房间人数超过奖池大小时再开启下一轮,让大房间仍可继续按配置重复分配。
available = append([]RoomRocketRewardItem(nil), basePool...)
cycle++
}
selectedIndex, ok := selectRoomRocketRewardIndex(rocketID, role, userID, available, fmt.Sprintf("cycle:%d:index:%d", cycle, index))
if !ok {
continue
}
assignments[userID] = available[selectedIndex]
available = append(available[:selectedIndex], available[selectedIndex+1:]...)
}
return assignments
}
func selectRoomRocketReward(rocketID string, role string, userID int64, pool []RoomRocketRewardItem) (RoomRocketRewardItem, bool) {
index, ok := selectRoomRocketRewardIndex(rocketID, role, userID, pool, "")
if !ok {
return RoomRocketRewardItem{}, false
}
return pool[index], true
}
func selectRoomRocketRewardIndex(rocketID string, role string, userID int64, pool []RoomRocketRewardItem, salt string) (int, bool) {
total := int64(0)
for _, item := range pool {
if item.Weight > 0 && item.ResourceGroupID > 0 {
@ -1119,21 +1166,26 @@ func selectRoomRocketReward(rocketID string, role string, userID int64, pool []R
}
}
if total <= 0 {
return RoomRocketRewardItem{}, false
return -1, false
}
hash := sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", rocketID, role, userID)))
hash := sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d|%s", rocketID, role, userID, salt)))
pick := int64(binary.BigEndian.Uint64(hash[:8]) % uint64(total))
cursor := int64(0)
for _, item := range pool {
fallback := -1
for index, item := range pool {
if item.Weight <= 0 || item.ResourceGroupID <= 0 {
continue
}
fallback = index
cursor += item.Weight
if pick < cursor {
return item, true
return index, true
}
}
return pool[len(pool)-1], true
if fallback >= 0 {
return fallback, true
}
return -1, false
}
func roomRocketGrantCommandID(rocketID string, role string, userID int64, rewardItemID string) string {

View File

@ -566,6 +566,9 @@ func TestRoomRocketLaunchStacksAllSpecialPoolsAndRandomInRoomReward(t *testing.T
if countRocketRewards(rewards, igniterID, "in_room") != 1 {
t.Fatalf("top1 igniter must still receive exactly one random in-room reward: %+v", rewards)
}
if countRocketRewardsByRoleAndGroup(rewards, "in_room", 1001) != 1 || countRocketRewardsByRoleAndGroup(rewards, "in_room", 1101) != 1 {
t.Fatalf("in-room rewards must spread across the configured pool before repeating an item: %+v", rewards)
}
if countRocketRewards(rewards, igniterID, "top1") != 2 || countRocketRewards(rewards, igniterID, "igniter") != 2 {
t.Fatalf("top1 igniter must receive every configured special reward: %+v", rewards)
}
@ -828,6 +831,16 @@ func countRocketRewards(rewards []*roomv1.RoomRocketRewardGrant, userID int64, r
return count
}
func countRocketRewardsByRoleAndGroup(rewards []*roomv1.RoomRocketRewardGrant, role string, groupID int64) int {
count := 0
for _, reward := range rewards {
if reward.GetRewardRole() == role && reward.GetResourceGroupId() == groupID {
count++
}
}
return count
}
func rocketRewardUserIDs(rewards []*roomv1.RoomRocketRewardGrant) []int64 {
userIDs := make([]int64, 0, len(rewards))
for _, reward := range rewards {