Gifting support

This commit is contained in:
Eccentric 2024-01-31 21:40:47 +00:00
parent 85383e4865
commit 3f5adcba6c
6 changed files with 137 additions and 43 deletions

View File

@ -296,12 +296,13 @@ func (e *Entry) GenerateResponse(p *person.Person) aid.JSON {
"itemGrants": []aid.JSON{},
"metaInfo": e.Meta,
"meta": aid.JSON{},
"displayAssetPath": e.DisplayAssetPath,
"title": e.Title,
"displayAssetPath": e.DisplayAssetPath,
"shortDescription": e.ShortDescription,
}
grants := []aid.JSON{}
requirements := []aid.JSON{}
purchaseRequirements := []aid.JSON{}
meta := []aid.JSON{}
for _, templateId := range e.Grants {
@ -316,6 +317,12 @@ func (e *Entry) GenerateResponse(p *person.Person) aid.JSON {
"requiredId": item.ID,
"minQuantity": 1,
})
purchaseRequirements = append(purchaseRequirements, aid.JSON{
"requirementType": "DenyOnItemOwnership",
"requiredId": item.ID,
"minQuantity": 1,
})
}
}
@ -331,6 +338,13 @@ func (e *Entry) GenerateResponse(p *person.Person) aid.JSON {
json["itemGrants"] = grants
json["requirements"] = requirements
json["metaInfo"] = meta
json["giftInfo"] = aid.JSON{
"bIsEnabled": true,
"forcedGiftBoxTemplateId": "",
"purchaseRequirements": purchaseRequirements,
"giftRecordIds": []any{},
}
return json
}

View File

@ -24,6 +24,8 @@ var (
"SetCosmeticLockerSlot": clientSetCosmeticLockerSlotAction,
"SetCosmeticLockerBanner": clientSetCosmeticLockerBannerAction,
"PurchaseCatalogEntry": clientPurchaseCatalogEntryAction,
"GiftCatalogEntry": clientGiftCatalogEntryAction,
"RemoveGiftBox": clientRemoveGiftBoxAction,
}
)
@ -70,8 +72,7 @@ func PostClientProfileAction(c *fiber.Ctx) error {
profile.Diff(profileSnapshot)
}
known, _ := strconv.Atoi(c.Query("rvn"))
revision := known
revision, _ := strconv.Atoi(c.Query("rvn"))
if revision == -1 {
revision = profile.Revision
}
@ -81,8 +82,6 @@ func PostClientProfileAction(c *fiber.Ctx) error {
delete(profileSnapshots, profile.Type)
multiUpdate := []aid.JSON{}
if known != -1 {
for key := range profileSnapshots {
profile := person.GetProfileFromType(key)
if profile == nil {
@ -105,7 +104,6 @@ func PostClientProfileAction(c *fiber.Ctx) error {
profile.ClearProfileChanges()
go profile.Save()
}
}
return c.Status(200).JSON(aid.JSON{
"profileId": c.Query("profileId"),
@ -121,11 +119,7 @@ func PostClientProfileAction(c *fiber.Ctx) error {
}
func clientQueryProfileAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
known, _ := strconv.Atoi(c.Query("rvn"))
if known == -1 {
profile.CreateFullProfileUpdateChange()
}
profile.CreateFullProfileUpdateChange()
return nil
}
@ -134,8 +128,7 @@ func clientMarkItemSeenAction(c *fiber.Ctx, person *p.Person, profile *p.Profile
ItemIds []string `json:"itemIds"`
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -159,8 +152,7 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
IndexWithinSlot int `json:"indexWithinSlot"`
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -207,8 +199,7 @@ func clientSetBattleRoyaleBannerAction(c *fiber.Ctx, person *p.Person, profile *
HomebaseBannerIconID string `json:"homebaseBannerIconId" binding:"required"`
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -248,8 +239,7 @@ func clientSetItemFavoriteStatusBatchAction(c *fiber.Ctx, person *p.Person, prof
Favorite []bool `json:"itemFavStatus" binding:"required"`
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -275,8 +265,7 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
VariantUpdates []aid.JSON `json:"variantUpdates" binding:"required"` // variant updates
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -342,8 +331,7 @@ func clientSetCosmeticLockerBannerAction(c *fiber.Ctx, person *p.Person, profile
BannerIconTemplateName string `json:"bannerIconTemplateName" binding:"required"` // template id
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -372,14 +360,13 @@ func clientSetCosmeticLockerBannerAction(c *fiber.Ctx, person *p.Person, profile
}
func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error {
var body struct{
var body struct {
OfferID string `json:"offerId" binding:"required"`
PurchaseQuantity int `json:"purchaseQuantity" binding:"required"`
ExpectedTotalPrice int `json:"expectedTotalPrice" binding:"required"`
}
err := c.BodyParser(&body)
if err != nil {
if err := c.BodyParser(&body); err != nil {
return fmt.Errorf("invalid Body")
}
@ -409,7 +396,7 @@ func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p
vbucks.Quantity -= body.ExpectedTotalPrice
go func() {
profile0Vbucks.Quantity = vbucks.Quantity // for season 2 and lower
profile0Vbucks.Quantity = vbucks.Quantity
vbucks.Save()
profile0Vbucks.Save()
}()
@ -451,3 +438,95 @@ func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p
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
go func() {
profile0Vbucks.Quantity = price
vbucks.Save()
profile0Vbucks.Save()
}()
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)
}
receiverPerson.CommonCoreProfile.Gifts.AddGift(gift)
receiverPerson.CommonCoreProfile.Save()
}
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
}

View File

@ -38,11 +38,11 @@ func GetCloudStorageFiles(c *fiber.Ctx) error {
})
}
return c.Status(fiber.StatusOK).JSON(result)
return c.Status(200).JSON(result)
}
func GetCloudStorageConfig(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{
return c.Status(200).JSON(aid.JSON{
"enumerateFilesPath": "/api/cloudstorage/system",
"enableMigration": true,
"enableWrites": true,
@ -55,15 +55,17 @@ func GetCloudStorageConfig(c *fiber.Ctx) error {
}
func GetCloudStorageFile(c *fiber.Ctx) error {
c.Set("Content-Type", "application/octet-stream")
switch c.Params("fileName") {
case "DefaultEngine.ini":
c.Set("Content-Type", "application/octet-stream")
c.Status(fiber.StatusOK)
c.Send(storage.GetDefaultEngine())
return nil
return c.Status(200).Send(storage.GetDefaultEngine())
case "DefaultGame.ini":
return c.Status(200).Send(storage.GetDefaultGame())
case "DefaultRuntimeOptions.ini":
return c.Status(200).Send(storage.GetDefaultRuntime())
}
return c.Status(400).JSON(aid.ErrorBadRequest)
return c.Status(404).JSON(aid.ErrorBadRequest("File not found"))
}
func GetUserStorageFiles(c *fiber.Ctx) error {

View File

@ -2,6 +2,7 @@ package person
import (
"fmt"
"time"
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/storage"
@ -33,7 +34,7 @@ func (r *Relationship) ToDatabase() *storage.DB_Relationship {
func (r *Relationship) GenerateFortniteFriendEntry(t RelationshipGenerateType) aid.JSON {
result := aid.JSON{
"status": r.Status,
"created": "0000-00-00T00:00:00.000Z",
"created": time.Now().Add(-time.Hour * 24 * 3).Format(time.RFC3339),
"favorite": false,
}
@ -46,8 +47,6 @@ func (r *Relationship) GenerateFortniteFriendEntry(t RelationshipGenerateType) a
result["accountId"] = r.From.ID
}
aid.PrintJSON(result)
return result
}

View File

@ -13,9 +13,10 @@ Performance first, universal Fortnite private server backend written in Go.
## What's next?
- Gifting, Matchmaker and Battle Pass support.
- Interact with external Services like Amazon S3 Buckets to save player data externally.
- Refactor the XMPP solution to use [melium/xmpp](https://github.com/mellium/xmpp)
- More profile actions like `RefundMtxPurchase` and `CopyCosmeticLoadout`.
- Integrating matchmaking with a hoster to smartly put players into games and know when servers become available.
- Interact with external services like Amazon S3 or Cloudflare R2 to save player data externally.
- Refactor the XMPP solution to use [melium/xmpp](https://github.com/mellium/xmpp).
## Version Support

View File

@ -53,7 +53,7 @@ bShouldCheckIfPlatformAllowed=false`)
}
func GetDefaultRuntime() []byte {
return []byte(`
return []byte(`
[/Script/FortniteGame.FortRuntimeOptions]
bEnableGlobalChat=true
bDisableGifting=false
@ -61,6 +61,5 @@ bDisableGiftingPC=false
bDisableGiftingPS4=false
bDisableGiftingXB=false
!ExperimentalCohortPercent=ClearArray
+ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=20)
`)
+ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=20)`)
}