diff --git a/fortnite/interact.go b/fortnite/interact.go index 97a9a62..7e595ac 100644 --- a/fortnite/interact.go +++ b/fortnite/interact.go @@ -82,6 +82,7 @@ type FAPI_Cosmetic struct { Path string `json:"path"` Added string `json:"added"` ShopHistory []string `json:"shopHistory"` + BattlePass bool `json:"battlePass"` } type Set struct { @@ -214,18 +215,24 @@ func (f *FortniteAPI) GetAllCosmetics() ([]FAPI_Cosmetic, error) { } func PreloadCosmetics(max int) error { - aid.Print("Fortnite Assets from", "https://fortnite-api.com") + aid.Print("Fortnite Assets from", StaticAPI.URL) list, err := StaticAPI.GetAllCosmetics() if err != nil { return err } + battlePassSkins := make([]FAPI_Cosmetic, 0) for _, item := range list { if item.Introduction.BackendValue > max { continue } + if len(item.ShopHistory) == 0 && item.Type.Value == "outfit" { + item.BattlePass = true + battlePassSkins = append(battlePassSkins, item) + } + Cosmetics.Items[item.ID] = item if item.Set.BackendValue != "" { @@ -243,14 +250,20 @@ func PreloadCosmetics(max int) error { aid.Print("Preloaded", len(Cosmetics.Items), "cosmetics") aid.Print("Preloaded", len(Cosmetics.Sets), "sets") + aid.Print("Preloaded", len(battlePassSkins), "battle pass skins") - notFound := make([]string, 0) + found := make([]string, 0) + characters := make([]string, 0) for id, item := range Cosmetics.Items { - if item.ItemPreviewHeroPath == "" { + if item.Type.Value == "outfit" { + characters = append(characters, id) + } + + if item.Type.Value != "backpack" { continue } - if item.Type.Value != "AthenaBackpack" { + if item.ItemPreviewHeroPath == "" { continue } @@ -259,19 +272,24 @@ func PreloadCosmetics(max int) error { character, ok := Cosmetics.Items[characterId] if !ok { - notFound = append(notFound, characterId) continue } - character.Backpack = id - Cosmetics.Items[characterId] = character + if _, ok := Cosmetics.Sets[character.Set.BackendValue]; !ok { + Cosmetics.Sets[character.Set.BackendValue] = Set{ + Items: make(map[string]FAPI_Cosmetic), + Name: character.Set.Value, + BackendName: character.Set.BackendValue, + } + } + Cosmetics.Sets[character.Set.BackendValue].Items[characterId] = character + found = append(found, id) } - if len(notFound) > 0 { - aid.Print("Couldn't find", len(notFound), "backpacks for characters:", notFound) - } + // print the perecentage of backpacks that have a character + aid.Print("Preloaded", len(found), "backpacks with characters", "(", float64(len(found))/float64(len(characters))*100, "% )") DAv2 := *storage.Asset("assets.json") if DAv2 == nil { diff --git a/fortnite/person.go b/fortnite/person.go index eaed263..0c616fe 100644 --- a/fortnite/person.go +++ b/fortnite/person.go @@ -53,8 +53,8 @@ func NewFortnitePerson(displayName string, key string) *p.Person { } if item == "Currency:MtxPurchased" { - person.CommonCoreProfile.Items.AddItem(p.NewItem(item, 9999)).Save() - person.Profile0Profile.Items.AddItem(p.NewItem(item, 9999)).Save() + person.CommonCoreProfile.Items.AddItem(p.NewItem(item, 0)).Save() + person.Profile0Profile.Items.AddItem(p.NewItem(item, 0)).Save() continue } diff --git a/fortnite/shop.go b/fortnite/shop.go index 97c7ca6..622e487 100644 --- a/fortnite/shop.go +++ b/fortnite/shop.go @@ -307,7 +307,7 @@ func (e *Entry) GenerateResponse(p *person.Person) aid.JSON { return json } -func GenerateStorefront() { +func GenerateRandomStorefront() { storefront := NewCatalog() daily := NewStorefront("BRDailyStorefront") diff --git a/handlers/profile.go b/handlers/profile.go index 9af3c0b..76a34b8 100644 --- a/handlers/profile.go +++ b/handlers/profile.go @@ -404,13 +404,17 @@ func PostPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.P }() if offer.ProfileType != "athena" { - return fmt.Errorf("save the world not implemeted yet!") + 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 { + item := profile.Items.GetItemByTemplateID(grant) + item.Quantity++ + go item.Save() + continue } diff --git a/main.go b/main.go index 7d4c4c9..b3e91cd 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ func init() { func init() { fortnite.PreloadCosmetics(aid.Config.Fortnite.Season) - fortnite.GenerateStorefront() + fortnite.GenerateRandomStorefront() if aid.Config.Database.DropAllTables { fortnite.NewFortnitePerson("ac", "1") diff --git a/readme.md b/readme.md index 004a280..c1836ac 100644 --- a/readme.md +++ b/readme.md @@ -7,49 +7,12 @@ Performance first, universal Fortnite private server backend written in Go. ## Features - **Blazing Fast** Written in Go and build on Fast HTTP, snow is extremely fast and can handle any profile action in milliseconds with its caching system. -- **Profile Changes** Snow, semi-automatically, keeps track of profile changes exactly like Fortnite does, meaning it is one-to-one with the game. +- **Profile Changes** Snow, automatically, keeps track of profile changes exactly like Fortnite does, meaning it is one-to-one with the game and never desyncs. - **Universal Database** Easily add new storage methods that satisfy the `Storage` interface. This means you can use any database you want. _(example of how to do this coming soon)_ -## Examples of Person Structures - -### Quests - -```golang -schedule := person.NewItem("ChallengeBundleSchedule:Paid_1", 1) -user.AthenaProfile.Items.AddItem(schedule) - -bundle := person.NewItem("ChallengeBundle:Daily_1", 1) -user.AthenaProfile.Items.AddItem(bundle) - -quest := person.NewQuest("Quest:Quest_2", bundle.ID, schedule.ID) -quest.AddObjective("quest_objective_eliminateplayers", 0) -user.AthenaProfile.Quests.AddQuest(quest) - -daily := person.NewDailyQuest("Quest:Quest_3") -daily.AddObjective("quest_objective_place_top10", 0) -user.AthenaProfile.Quests.AddQuest(daily) -``` - -### Profile Changes - -```golang -snapshot := user.CommonCoreProfile.Snapshot() -{ - vbucks := user.CommonCoreProfile.Items.GetItemByTemplateID("Currency:MtxPurchased") - vbucks.Quantity = 200 - vbucks.Favorite = true - - user.CommonCoreProfile.Items.DeleteItem(user.CommonCoreProfile.Items.GetItemByTemplateID("Token:CampaignAccess").ID) - user.CommonCoreProfile.Items.AddItem(person.NewItem("Token:ReceiveMtxCurrency", 1)) -} -user.CommonCoreProfile.Diff(snapshot) -``` - ## What's next? -- Every endpoint that is used by Fortnite. This includes all MCP Operations, extracting data from the telemetry and even the Party Service (maybe party v2). -- Automatic storefront that uses previous data to generate a storefront. This would use item shops from history to make sure there are no blank spots in the storefront. Also battle pass tiers etc. -- Embed game assets into the backend e.g. Battle Pass, Quest Data etc. _This would mean a single binary that can be run anywhere without the need of external files._ +- Final Fortnite Operations, this includes: Battle Pass, Friends, XMPP and Gifting. - Interact with external Services like Amazon S3 Buckets to save player data externally. - A way to interact with accounts outside of the game. This is mainly for a web app and other services to interact with the backend.