snow/handlers/client.go

748 lines
21 KiB
Go
Raw Normal View History

package handlers
import (
2023-11-09 19:35:39 +00:00
"fmt"
"strconv"
"strings"
"time"
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/fortnite"
p "github.com/ectrc/snow/person"
"github.com/gofiber/fiber/v2"
)
var (
2024-01-03 19:51:46 +00:00
clientActions = map[string]func(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
"QueryProfile": clientQueryProfileAction,
"ClientQuestLogin": clientQueryProfileAction,
"MarkItemSeen": clientMarkItemSeenAction,
"SetItemFavoriteStatusBatch": clientSetItemFavoriteStatusBatchAction,
"EquipBattleRoyaleCustomization": clientEquipBattleRoyaleCustomizationAction,
"SetBattleRoyaleBanner": clientSetBattleRoyaleBannerAction,
"SetCosmeticLockerSlot": clientSetCosmeticLockerSlotAction,
"SetCosmeticLockerBanner": clientSetCosmeticLockerBannerAction,
2024-02-03 02:33:54 +00:00
"SetCosmeticLockerName": clientSetCosmeticLockerNameAction,
"CopyCosmeticLoadout": clientCopyCosmeticLoadoutAction,
"DeleteCosmeticLoadout": clientDeleteCosmeticLoadoutAction,
2024-01-03 19:51:46 +00:00
"PurchaseCatalogEntry": clientPurchaseCatalogEntryAction,
2024-01-31 21:40:47 +00:00
"GiftCatalogEntry": clientGiftCatalogEntryAction,
"RemoveGiftBox": clientRemoveGiftBoxAction,
}
)
2024-01-03 19:51:46 +00:00
func PostClientProfileAction(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person)
if person == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Account Found"))
}
profile := person.GetProfileFromType(c.Query("profileId"))
if profile == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Profile Found"))
}
defer profile.ClearProfileChanges()
profileSnapshots := map[string]*p.ProfileSnapshot{
"athena": nil,
"common_core": nil,
"common_public": nil,
}
for key := range profileSnapshots {
profileSnapshots[key] = person.GetProfileFromType(key).Snapshot()
}
notifications := []aid.JSON{}
2024-01-03 19:51:46 +00:00
action, ok := clientActions[c.Params("action")];
if ok && profile != nil {
if err := action(c, person, profile, &notifications); err != nil {
return c.Status(400).JSON(aid.ErrorBadRequest(err.Error()))
}
}
for key, profileSnapshot := range profileSnapshots {
profile := person.GetProfileFromType(key)
if profile == nil {
continue
}
if profileSnapshot == nil {
continue
}
profile.Diff(profileSnapshot)
}
2024-01-31 21:40:47 +00:00
revision, _ := strconv.Atoi(c.Query("rvn"))
if revision == -1 {
revision = profile.Revision
}
revision++
profile.Revision = revision
2024-01-20 23:08:14 +00:00
go profile.Save()
delete(profileSnapshots, profile.Type)
multiUpdate := []aid.JSON{}
2024-01-31 16:20:43 +00:00
for key := range profileSnapshots {
profile := person.GetProfileFromType(key)
if profile == nil {
continue
}
profile.Revision++
if len(profile.Changes) == 0 {
continue
}
multiUpdate = append(multiUpdate, aid.JSON{
"profileId": profile.Type,
"profileRevision": profile.Revision,
"profileCommandRevision": profile.Revision,
"profileChangesBaseRevision": profile.Revision - 1,
"profileChanges": profile.Changes,
})
profile.ClearProfileChanges()
go profile.Save()
}
return c.Status(200).JSON(aid.JSON{
"profileId": c.Query("profileId"),
2024-01-03 19:51:46 +00:00
"profileRevision": profile.Revision,
"profileCommandRevision": profile.Revision,
"profileChangesBaseRevision": profile.Revision - 1,
"profileChanges": profile.Changes,
"multiUpdate": multiUpdate,
"notifications": notifications,
"responseVersion": 1,
"serverTime": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
}
2024-01-03 19:51:46 +00:00
func clientQueryProfileAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
2024-01-31 21:40:47 +00:00
profile.CreateFullProfileUpdateChange()
return nil
}
2024-01-03 19:51:46 +00:00
func clientMarkItemSeenAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
ItemIds []string `json:"itemIds"`
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
2023-11-09 19:35:39 +00:00
return fmt.Errorf("invalid Body")
}
for _, itemId := range body.ItemIds {
item := profile.Items.GetItem(itemId)
if item == nil {
continue
}
item.HasSeen = true
2023-11-05 02:43:21 +00:00
go item.Save()
}
return nil
}
2024-01-03 19:51:46 +00:00
func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
SlotName string `json:"slotName" binding:"required"`
ItemToSlot string `json:"itemToSlot"`
IndexWithinSlot int `json:"indexWithinSlot"`
2024-02-04 01:25:44 +00:00
VariantUpdates []struct{
Active string `json:"active"`
Channel string `json:"channel"`
} `json:"variantUpdates"`
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
2023-11-09 19:35:39 +00:00
return fmt.Errorf("invalid Body")
}
item := profile.Items.GetItem(body.ItemToSlot)
if item == nil {
2023-11-09 19:35:39 +00:00
if body.ItemToSlot != "" && !strings.Contains(strings.ToLower(body.ItemToSlot), "random") {
return fmt.Errorf("item not found")
}
item = &p.Item{
ID: body.ItemToSlot,
}
}
2024-02-04 01:25:44 +00:00
for _, update := range body.VariantUpdates {
channel := item.GetChannel(update.Channel)
if channel == nil {
continue
}
channel.Active = update.Active
go channel.Save()
}
2024-01-20 23:06:36 +00:00
attr := profile.Attributes.GetAttributeByKey("favorite_" + strings.ReplaceAll(strings.ToLower(body.SlotName), "wrap", "wraps"))
if attr == nil {
2023-11-09 19:35:39 +00:00
return fmt.Errorf("attribute not found")
}
switch body.SlotName {
case "Dance":
value := aid.JSONParse(attr.ValueJSON)
value.([]any)[body.IndexWithinSlot] = item.ID
attr.ValueJSON = aid.JSONStringify(value)
case "ItemWrap":
value := aid.JSONParse(attr.ValueJSON)
2024-01-20 23:06:36 +00:00
if body.IndexWithinSlot == -1 {
attr.ValueJSON = aid.JSONStringify([]any{item.ID,item.ID,item.ID,item.ID,item.ID,item.ID,item.ID})
break
}
value.([]any)[body.IndexWithinSlot] = item.ID
attr.ValueJSON = aid.JSONStringify(value)
default:
attr.ValueJSON = aid.JSONStringify(item.ID)
}
2024-02-03 02:33:54 +00:00
2024-02-04 01:25:44 +00:00
go attr.Save()
2023-11-09 19:35:39 +00:00
return nil
}
2024-01-03 19:51:46 +00:00
func clientSetBattleRoyaleBannerAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
2023-11-09 19:35:39 +00:00
var body struct {
HomebaseBannerColorID string `json:"homebaseBannerColorId" binding:"required"`
HomebaseBannerIconID string `json:"homebaseBannerIconId" binding:"required"`
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
2023-11-09 19:35:39 +00:00
return fmt.Errorf("invalid Body")
}
colorItem := person.CommonCoreProfile.Items.GetItemByTemplateID("HomebaseBannerColor:"+body.HomebaseBannerColorID)
if colorItem == nil {
return fmt.Errorf("color item not found")
}
iconItem := person.CommonCoreProfile.Items.GetItemByTemplateID("HomebaseBannerIcon:"+body.HomebaseBannerIconID)
if iconItem == nil {
return fmt.Errorf("icon item not found")
}
iconAttr := profile.Attributes.GetAttributeByKey("banner_icon")
if iconAttr == nil {
return fmt.Errorf("icon attribute not found")
}
colorAttr := profile.Attributes.GetAttributeByKey("banner_color")
if colorAttr == nil {
return fmt.Errorf("color attribute not found")
}
iconAttr.ValueJSON = aid.JSONStringify(strings.Split(iconItem.TemplateID, ":")[1])
colorAttr.ValueJSON = aid.JSONStringify(strings.Split(colorItem.TemplateID, ":")[1])
2024-02-03 02:33:54 +00:00
iconAttr.Save()
colorAttr.Save()
2023-11-09 19:35:39 +00:00
return nil
}
2024-01-03 19:51:46 +00:00
func clientSetItemFavoriteStatusBatchAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
ItemIds []string `json:"itemIds" binding:"required"`
Favorite []bool `json:"itemFavStatus" binding:"required"`
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
for i, itemId := range body.ItemIds {
item := profile.Items.GetItem(itemId)
if item == nil {
continue
}
item.Favorite = body.Favorite[i]
go item.Save()
}
return nil
}
2024-01-03 19:51:46 +00:00
func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
Category string `json:"category" binding:"required"` // item type e.g. Character
ItemToSlot string `json:"itemToSlot" binding:"required"` // template id
LockerItem string `json:"lockerItem" binding:"required"` // locker id
SlotIndex int `json:"slotIndex" binding:"required"` // index of slot
2024-02-04 01:25:44 +00:00
VariantUpdates []struct{
Active string `json:"active"`
Channel string `json:"channel"`
} `json:"variantUpdates" binding:"required"` // variant updates
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
item := profile.Items.GetItemByTemplateID(body.ItemToSlot)
if item == nil {
if body.ItemToSlot != "" && !strings.Contains(strings.ToLower(body.ItemToSlot), "random") {
return fmt.Errorf("item not found")
}
item = &p.Item{
ID: body.ItemToSlot,
}
}
currentLocker := profile.Loadouts.GetLoadout(body.LockerItem)
if currentLocker == nil {
return fmt.Errorf("current locker not found")
}
2024-02-04 01:25:44 +00:00
for _, update := range body.VariantUpdates {
channel := item.GetChannel(update.Channel)
if channel == nil {
continue
}
channel.Active = update.Active
go channel.Save()
}
switch body.Category {
case "Character":
currentLocker.CharacterID = item.ID
case "Backpack":
currentLocker.BackpackID = item.ID
case "Pickaxe":
currentLocker.PickaxeID = item.ID
case "Glider":
currentLocker.GliderID = item.ID
case "ItemWrap":
defer profile.CreateLoadoutChangedChange(currentLocker, "ItemWrapID")
if body.SlotIndex == -1 {
for i := range currentLocker.ItemWrapID {
currentLocker.ItemWrapID[i] = item.ID
}
break
}
currentLocker.ItemWrapID[body.SlotIndex] = item.ID
case "Dance":
defer profile.CreateLoadoutChangedChange(currentLocker, "DanceID")
if body.SlotIndex == -1 {
for i := range currentLocker.DanceID {
currentLocker.DanceID[i] = item.ID
}
break
}
currentLocker.DanceID[body.SlotIndex] = item.ID
case "SkyDiveContrail":
currentLocker.ContrailID = item.ID
case "LoadingScreen":
currentLocker.LoadingScreenID = item.ID
case "MusicPack":
currentLocker.MusicPackID = item.ID
}
2024-02-04 01:25:44 +00:00
go currentLocker.Save()
return nil
}
2024-01-03 19:51:46 +00:00
func clientSetCosmeticLockerBannerAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
LockerItem string `json:"lockerItem" binding:"required"` // locker id
BannerColorTemplateName string `json:"bannerColorTemplateName" binding:"required"` // template id
BannerIconTemplateName string `json:"bannerIconTemplateName" binding:"required"` // template id
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
color := person.CommonCoreProfile.Items.GetItemByTemplateID("HomebaseBannerColor:" + body.BannerColorTemplateName)
if color == nil {
return fmt.Errorf("color item not found")
}
icon := profile.Items.GetItemByTemplateID("HomebaseBannerIcon:" + body.BannerIconTemplateName)
if icon == nil {
icon = &p.Item{
ID: body.BannerIconTemplateName,
}
}
currentLocker := profile.Loadouts.GetLoadout(body.LockerItem)
if currentLocker == nil {
return fmt.Errorf("current locker not found")
}
currentLocker.BannerColorID = color.ID
currentLocker.BannerID = icon.ID
go currentLocker.Save()
2024-02-03 02:33:54 +00:00
return nil
}
func clientSetCosmeticLockerNameAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
LockerItem string `json:"lockerItem" binding:"required"`
Name string `json:"name" binding:"required"`
}
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
loadoutsAttribute := profile.Attributes.GetAttributeByKey("loadouts")
if loadoutsAttribute == nil {
return fmt.Errorf("loadouts not found")
}
loadouts := p.AttributeConvertToSlice[string](loadoutsAttribute)
currentLocker := profile.Loadouts.GetLoadout(body.LockerItem)
if currentLocker == nil {
return fmt.Errorf("current locker not found")
}
if loadouts[0] == currentLocker.ID {
return fmt.Errorf("cannot rename default locker")
}
currentLocker.LockerName = body.Name
go currentLocker.Save()
return nil
}
func clientCopyCosmeticLoadoutAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
OptNewNameForTarget string `json:"optNewNameForTarget" binding:"required"`
SourceIndex int `json:"sourceIndex" binding:"required"`
TargetIndex int `json:"targetIndex" binding:"required"`
}
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
lastAppliedLoadoutAttribute := profile.Attributes.GetAttributeByKey("last_applied_loadout")
if lastAppliedLoadoutAttribute == nil {
return fmt.Errorf("last_applied_loadout not found")
}
activeLoadoutIndexAttribute := profile.Attributes.GetAttributeByKey("active_loadout_index")
if activeLoadoutIndexAttribute == nil {
return fmt.Errorf("active_loadout_index not found")
}
loadoutsAttribute := profile.Attributes.GetAttributeByKey("loadouts")
if loadoutsAttribute == nil {
return fmt.Errorf("loadouts not found")
}
loadouts := p.AttributeConvertToSlice[string](loadoutsAttribute)
if body.SourceIndex >= len(loadouts) {
return fmt.Errorf("source index out of range")
}
sandboxLoadout := profile.Loadouts.GetLoadout("sandbox_loadout")
if sandboxLoadout == nil {
return fmt.Errorf("sandbox loadout not found")
}
2024-02-03 14:58:50 +00:00
lastAppliedLoadout := profile.Loadouts.GetLoadout(p.AttributeConvert[string](lastAppliedLoadoutAttribute))
2024-02-03 02:33:54 +00:00
if lastAppliedLoadout == nil {
return fmt.Errorf("last applied loadout not found")
}
if body.TargetIndex >= len(loadouts) {
2024-02-03 14:58:50 +00:00
newLoadout := p.NewLoadout(body.OptNewNameForTarget, profile)
newLoadout.CopyFrom(lastAppliedLoadout)
profile.Loadouts.AddLoadout(newLoadout)
go newLoadout.Save()
2024-02-03 02:33:54 +00:00
lastAppliedLoadout.CopyFrom(sandboxLoadout)
go lastAppliedLoadout.Save()
2024-02-03 14:58:50 +00:00
lastAppliedLoadoutAttribute.ValueJSON = aid.JSONStringify(newLoadout.ID)
activeLoadoutIndexAttribute.ValueJSON = aid.JSONStringify(body.TargetIndex)
go lastAppliedLoadoutAttribute.Save()
go activeLoadoutIndexAttribute.Save()
2024-02-03 02:33:54 +00:00
2024-02-03 14:58:50 +00:00
loadouts = append(loadouts, newLoadout.ID)
2024-02-03 02:33:54 +00:00
loadoutsAttribute.ValueJSON = aid.JSONStringify(loadouts)
go loadoutsAttribute.Save()
2024-02-03 14:58:50 +00:00
sandboxLoadout.CopyFrom(newLoadout)
go sandboxLoadout.Save()
2024-02-03 14:58:50 +00:00
if len(profile.Changes) == 0 {
profile.CreateLoadoutChangedChange(sandboxLoadout, "DanceID")
}
2024-02-03 02:33:54 +00:00
return nil
}
if body.SourceIndex > 0 {
sourceLoadout := profile.Loadouts.GetLoadout(loadouts[body.SourceIndex])
if sourceLoadout == nil {
return fmt.Errorf("target loadout not found")
}
sandboxLoadout.CopyFrom(sourceLoadout)
go sandboxLoadout.Save()
2024-02-03 14:58:50 +00:00
2024-02-03 02:33:54 +00:00
lastAppliedLoadoutAttribute.ValueJSON = aid.JSONStringify(sourceLoadout.ID)
activeLoadoutIndexAttribute.ValueJSON = aid.JSONStringify(body.SourceIndex)
2024-02-03 14:58:50 +00:00
2024-02-03 02:33:54 +00:00
go lastAppliedLoadoutAttribute.Save()
go activeLoadoutIndexAttribute.Save()
if len(profile.Changes) == 0{
2024-02-03 14:58:50 +00:00
profile.CreateLoadoutChangedChange(sandboxLoadout, "DanceID")
profile.CreateLoadoutChangedChange(sourceLoadout, "DanceID")
}
2024-02-03 02:33:54 +00:00
return nil
}
2024-02-03 14:58:50 +00:00
targetLoadout := profile.Loadouts.GetLoadout(loadouts[body.TargetIndex])
if targetLoadout == nil {
2024-02-03 02:33:54 +00:00
return fmt.Errorf("target loadout not found")
}
2024-02-03 14:58:50 +00:00
sandboxLoadout.CopyFrom(targetLoadout)
2024-02-03 02:33:54 +00:00
go sandboxLoadout.Save()
if len(profile.Changes) == 0{
2024-02-03 14:58:50 +00:00
profile.CreateLoadoutChangedChange(sandboxLoadout, "DanceID")
profile.CreateLoadoutChangedChange(targetLoadout, "DanceID")
2024-02-03 02:33:54 +00:00
}
return nil
}
func clientDeleteCosmeticLoadoutAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
FallbackLoadoutIndex int `json:"fallbackLoadoutIndex" binding:"required"`
LoadoutIndex int `json:"index" binding:"required"`
}
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
lastAppliedLoadoutAttribute := profile.Attributes.GetAttributeByKey("last_applied_loadout")
if lastAppliedLoadoutAttribute == nil {
return fmt.Errorf("last_applied_loadout not found")
}
activeLoadoutIndexAttribute := profile.Attributes.GetAttributeByKey("active_loadout_index")
if activeLoadoutIndexAttribute == nil {
return fmt.Errorf("active_loadout_index not found")
}
loadoutsAttribute := profile.Attributes.GetAttributeByKey("loadouts")
if loadoutsAttribute == nil {
return fmt.Errorf("loadouts not found")
}
loadouts := p.AttributeConvertToSlice[string](loadoutsAttribute)
if body.LoadoutIndex >= len(loadouts) {
return fmt.Errorf("loadout index out of range")
}
if body.LoadoutIndex == 0 {
return fmt.Errorf("cannot delete default loadout")
}
if body.FallbackLoadoutIndex == -1 {
body.FallbackLoadoutIndex = 0
}
fallbackLoadout := profile.Loadouts.GetLoadout(loadouts[body.FallbackLoadoutIndex])
if fallbackLoadout == nil {
return fmt.Errorf("fallback loadout not found")
}
lastAppliedLoadoutAttribute.ValueJSON = aid.JSONStringify(fallbackLoadout.ID)
activeLoadoutIndexAttribute.ValueJSON = aid.JSONStringify(body.FallbackLoadoutIndex)
lastAppliedLoadoutAttribute.Save()
activeLoadoutIndexAttribute.Save()
profile.Loadouts.DeleteLoadout(loadouts[body.LoadoutIndex])
loadouts = append(loadouts[:body.LoadoutIndex], loadouts[body.LoadoutIndex+1:]...)
loadoutsAttribute.ValueJSON = aid.JSONStringify(loadouts)
loadoutsAttribute.Save()
return nil
}
2024-01-03 19:51:46 +00:00
func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
2024-01-31 21:40:47 +00:00
var body struct {
OfferID string `json:"offerId" binding:"required"`
PurchaseQuantity int `json:"purchaseQuantity" binding:"required"`
ExpectedTotalPrice int `json:"expectedTotalPrice" binding:"required"`
}
2024-01-31 21:40:47 +00:00
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
offer := fortnite.StaticCatalog.GetOfferById(body.OfferID)
if offer == nil {
return fmt.Errorf("offer not found")
}
if offer.Price != body.ExpectedTotalPrice {
return fmt.Errorf("invalid price")
}
vbucks := profile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if vbucks == nil {
return fmt.Errorf("vbucks not found")
}
profile0Vbucks := person.Profile0Profile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if profile0Vbucks == nil {
return fmt.Errorf("profile0vbucks not found")
}
if vbucks.Quantity < body.ExpectedTotalPrice {
return fmt.Errorf("not enough vbucks")
}
vbucks.Quantity -= body.ExpectedTotalPrice
2024-02-03 02:33:54 +00:00
profile0Vbucks.Quantity = vbucks.Quantity
vbucks.Save()
profile0Vbucks.Save()
if offer.ProfileType != "athena" {
2023-12-06 22:26:42 +00:00
return fmt.Errorf("save the world not implemeted yet")
}
loot := []aid.JSON{}
for i := 0; i < body.PurchaseQuantity; i++ {
for _, grant := range offer.Grants {
if profile.Items.GetItemByTemplateID(grant) != nil {
2023-12-06 22:26:42 +00:00
item := profile.Items.GetItemByTemplateID(grant)
item.Quantity++
go item.Save()
continue
}
item := p.NewItem(grant, 1)
person.AthenaProfile.Items.AddItem(item)
loot = append(loot, aid.JSON{
"itemType": item.TemplateID,
"itemGuid": item.ID,
"quantity": item.Quantity,
"itemProfile": offer.ProfileType,
})
}
}
*notifications = append(*notifications, aid.JSON{
"type": "CatalogPurchase",
"lootResult": aid.JSON{
"items": loot,
},
"primary": true,
})
2024-01-31 21:40:47 +00:00
return nil
}
func clientGiftCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
Currency string `json:"currency" binding:"required"`
CurrencySubType string `json:"currencySubType" binding:"required"`
ExpectedTotalPrice int `json:"expectedTotalPrice" binding:"required"`
GameContext string `json:"gameContext" binding:"required"`
GiftWrapTemplateId string `json:"giftWrapTemplateId" binding:"required"`
PersonalMessage string `json:"personalMessage" binding:"required"`
ReceiverAccountIds []string `json:"receiverAccountIds" binding:"required"`
OfferId string `json:"offerId" binding:"required"`
}
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
offer := fortnite.StaticCatalog.GetOfferById(body.OfferId)
if offer == nil {
return fmt.Errorf("offer not found")
}
if offer.Price != body.ExpectedTotalPrice {
return fmt.Errorf("invalid price")
}
price := offer.Price * len(body.ReceiverAccountIds)
vbucks := profile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if vbucks == nil {
return fmt.Errorf("vbucks not found")
}
profile0Vbucks := person.Profile0Profile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if profile0Vbucks == nil {
return fmt.Errorf("profile0vbucks not found")
}
if vbucks.Quantity < price {
return fmt.Errorf("not enough vbucks")
}
vbucks.Quantity -= price
2024-02-03 02:33:54 +00:00
profile0Vbucks.Quantity = price
vbucks.Save()
profile0Vbucks.Save()
2024-01-31 21:40:47 +00:00
for _, receiverAccountId := range body.ReceiverAccountIds {
receiverPerson := p.Find(receiverAccountId)
if receiverPerson == nil {
continue
}
gift := p.NewGift(body.GiftWrapTemplateId, 1, person.ID, body.PersonalMessage)
for _, grant := range offer.Grants {
item := p.NewItem(grant, 1)
item.ProfileType = offer.ProfileType
gift.AddLoot(item)
}
2024-02-03 02:33:54 +00:00
receiverPerson.CommonCoreProfile.Gifts.AddGift(gift).Save()
2024-01-31 21:40:47 +00:00
}
return nil
}
func clientRemoveGiftBoxAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct {
GiftBoxItemId string `json:"giftBoxItemId" binding:"required"`
}
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
gift := profile.Gifts.GetGift(body.GiftBoxItemId)
if gift == nil {
return fmt.Errorf("gift not found")
}
for _, item := range gift.Loot {
person.GetProfileFromType(item.ProfileType).Items.AddItem(item).Save()
}
profile.Gifts.DeleteGift(gift.ID)
return nil
}