Working variants/styles equipping

This commit is contained in:
Eccentric 2024-02-04 01:25:44 +00:00
parent cfd72743f3
commit e00724b645
11 changed files with 218 additions and 43 deletions

View File

@ -2,9 +2,11 @@ package fortnite
import (
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"slices"
"strings"
"github.com/ectrc/snow/aid"
@ -26,6 +28,18 @@ type FAPI_Error struct {
Error string `json:"error"`
}
type FAPI_Cosmetic_Variant struct {
Channel string `json:"channel"`
Type string `json:"type"`
Options []FAPI_Cosmetic_VariantChannel `json:"options"`
}
type FAPI_Cosmetic_VariantChannel struct {
Tag string `json:"tag"`
Name string `json:"name"`
Image string `json:"image"`
}
type FAPI_Cosmetic struct {
ID string `json:"id"`
Name string `json:"name"`
@ -62,15 +76,7 @@ type FAPI_Cosmetic struct {
SmallIcon string `json:"smallIcon"`
Other map[string]string `json:"other"`
} `json:"images"`
Variants []struct {
Channel string `json:"channel"`
Type string `json:"type"`
Options []struct {
Tag string `json:"tag"`
Name string `json:"name"`
Image string `json:"image"`
} `json:"options"`
} `json:"variants"`
Variants []FAPI_Cosmetic_Variant `json:"variants"`
GameplayTags []string `json:"gameplayTags"`
SearchTags []string `json:"searchTags"`
MetaTags []string `json:"metaTags"`
@ -165,7 +171,13 @@ func (c *CosmeticData) GetRandomSet() Set {
return c.GetRandomSet()
}
var EXTRA_NUMERIC_STYLES = []string{"Soccer", "Football", "ScaryBall"}
func (c *CosmeticData) AddItem(item FAPI_Cosmetic) {
if slices.Contains(EXTRA_NUMERIC_STYLES, item.Set.BackendValue) {
item = c.AddNumericVariantChannelToItem(item)
}
c.Items[item.ID] = item
if item.Set.BackendValue != "" {
@ -181,6 +193,23 @@ func (c *CosmeticData) AddItem(item FAPI_Cosmetic) {
}
}
func (c *CosmeticData) AddNumericVariantChannelToItem(item FAPI_Cosmetic) FAPI_Cosmetic {
owned := []FAPI_Cosmetic_VariantChannel{}
for i := 0; i < 100; i++ {
owned = append(owned, FAPI_Cosmetic_VariantChannel{
Tag: fmt.Sprint(i),
})
}
item.Variants = append(item.Variants, FAPI_Cosmetic_Variant{
Channel: "Numeric",
Type: "int",
Options: owned,
})
return item
}
var (
StaticAPI = NewFortniteAPI()
Cosmetics = CosmeticData{

View File

@ -41,11 +41,24 @@ func GiveEverything(person *p.Person) {
continue
}
item := p.NewItem(item.Type.BackendValue + ":" + item.ID, 1)
item.HasSeen = true
person.AthenaProfile.Items.AddItem(item)
new := p.NewItem(item.Type.BackendValue + ":" + item.ID, 1)
new.HasSeen = true
items = append(items, *item.ToDatabase(person.AthenaProfile.ID))
grouped := map[string][]string{}
for _, variant := range item.Variants {
grouped[variant.Channel] = []string{}
for _, option := range variant.Options {
grouped[variant.Channel] = append(grouped[variant.Channel], option.Tag)
}
}
for channel, tags := range grouped {
new.AddChannel(new.NewChannel(channel, tags, tags[0]))
}
person.AthenaProfile.Items.AddItem(new)
items = append(items, *new.ToDatabase(person.AthenaProfile.ID))
}
storage.Repo.BulkCreateItems(&items)

View File

@ -93,6 +93,7 @@ func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error {
access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
"am": "exchange_code",
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
@ -101,6 +102,7 @@ func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error {
refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
"am": "exchange_code",
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
@ -143,6 +145,7 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error {
access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
"am": "password",
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
@ -151,6 +154,7 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error {
refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
"am": "password",
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)

View File

@ -153,6 +153,10 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
SlotName string `json:"slotName" binding:"required"`
ItemToSlot string `json:"itemToSlot"`
IndexWithinSlot int `json:"indexWithinSlot"`
VariantUpdates []struct{
Active string `json:"active"`
Channel string `json:"channel"`
} `json:"variantUpdates"`
}
if err := c.BodyParser(&body); err != nil {
@ -170,6 +174,16 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
}
}
for _, update := range body.VariantUpdates {
channel := item.GetChannel(update.Channel)
if channel == nil {
continue
}
channel.Active = update.Active
go channel.Save()
}
attr := profile.Attributes.GetAttributeByKey("favorite_" + strings.ReplaceAll(strings.ToLower(body.SlotName), "wrap", "wraps"))
if attr == nil {
return fmt.Errorf("attribute not found")
@ -191,8 +205,8 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
default:
attr.ValueJSON = aid.JSONStringify(item.ID)
}
go attr.Save()
go attr.Save()
return nil
}
@ -263,7 +277,10 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
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
VariantUpdates []aid.JSON `json:"variantUpdates" binding:"required"` // variant updates
VariantUpdates []struct{
Active string `json:"active"`
Channel string `json:"channel"`
} `json:"variantUpdates" binding:"required"` // variant updates
}
if err := c.BodyParser(&body); err != nil {
@ -286,6 +303,16 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
return fmt.Errorf("current locker not found")
}
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
@ -322,7 +349,6 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
}
go currentLocker.Save()
return nil
}
@ -434,8 +460,6 @@ func clientCopyCosmeticLoadoutAction(c *fiber.Ctx, person *p.Person, profile *p.
}
if body.TargetIndex >= len(loadouts) {
aid.Print("creating a new loadout with source", body.SourceIndex, "and target", body.TargetIndex)
newLoadout := p.NewLoadout(body.OptNewNameForTarget, profile)
newLoadout.CopyFrom(lastAppliedLoadout)
profile.Loadouts.AddLoadout(newLoadout)
@ -464,8 +488,6 @@ func clientCopyCosmeticLoadoutAction(c *fiber.Ctx, person *p.Person, profile *p.
}
if body.SourceIndex > 0 {
aid.Print("saving source loadout", body.SourceIndex, "to sandbox")
sourceLoadout := profile.Loadouts.GetLoadout(loadouts[body.SourceIndex])
if sourceLoadout == nil {
return fmt.Errorf("target loadout not found")
@ -488,8 +510,6 @@ func clientCopyCosmeticLoadoutAction(c *fiber.Ctx, person *p.Person, profile *p.
return nil
}
aid.Print("loading target loadout", body.TargetIndex, "to sandbox")
targetLoadout := profile.Loadouts.GetLoadout(loadouts[body.TargetIndex])
if targetLoadout == nil {
return fmt.Errorf("target loadout not found")

View File

@ -55,7 +55,7 @@ func init() {
found.RemovePermission(perm)
}
found.AddPermission("all")
found.AddPermission("asdasdasdasa")
aid.Print("(snow) max account " + username + " loaded")
}
}

View File

@ -44,6 +44,7 @@ func FromDatabaseItem(item *storage.DB_Item) *Item {
variants := []*VariantChannel{}
for _, variant := range item.Variants {
variants = append(variants, FromDatabaseVariant(&variant))
}
@ -126,6 +127,7 @@ func (i *Item) DeleteLoot() {
func (i *Item) NewChannel(channel string, owned []string, active string) *VariantChannel {
return &VariantChannel{
ID: uuid.New().String(),
ItemID: i.ID,
Channel: channel,
Owned: owned,
@ -139,12 +141,14 @@ func (i *Item) AddChannel(channel *VariantChannel) {
}
func (i *Item) RemoveChannel(channel *VariantChannel) {
var vId string
for index, c := range i.Variants {
if c.Channel == channel.Channel {
vId = c.ID
i.Variants = append(i.Variants[:index], i.Variants[index+1:]...)
}
}
//storage.Repo.DeleteItemVariant(i.ID, channel)
storage.Repo.DeleteVariant(vId)
}
func (i *Item) GetChannel(channel string) *VariantChannel {
@ -164,6 +168,14 @@ func (i *Item) FillChannels(channels []*VariantChannel) {
}
}
func (i *Item) EquipChannel(channel string, variant string) {
for _, c := range i.Variants {
if c.Channel == channel {
c.Active = variant
}
}
}
func (i *Item) ToDatabase(profileId string) *storage.DB_Item {
variants := []storage.DB_VariantChannel{}
@ -251,5 +263,5 @@ func (v *VariantChannel) ToDatabase() *storage.DB_VariantChannel {
}
func (v *VariantChannel) Save() {
//storage.Repo.SaveItemVariant(v.ToDatabase())
storage.Repo.SaveVariant(v.ToDatabase())
}

View File

@ -1,6 +1,7 @@
package person
import (
"regexp"
"strings"
"github.com/ectrc/snow/aid"
@ -167,7 +168,6 @@ func (l *Loadout) GetAttribute(attribute string) interface{} {
return l.GenerateFortniteLockerSlotsData()
}
return nil
}
@ -187,6 +187,43 @@ func (l *Loadout) GenerateFortniteLockerSlotsData() aid.JSON {
}
}
func (l *Loadout) GetSlotFromItemTemplateID(templateId string) string {
re := regexp.MustCompile(`Athena(.*):`)
match := re.FindStringSubmatch(templateId)
if len(match) > 1 {
return match[1]
}
return ""
}
func (l *Loadout) GetItemFromSlot(slot string) *Item {
person := Find(l.PersonID)
if person == nil {
return nil
}
switch slot {
case "Character":
return person.AthenaProfile.Items.GetItem(l.CharacterID)
case "Backpack":
return person.AthenaProfile.Items.GetItem(l.BackpackID)
case "Pickaxe":
return person.AthenaProfile.Items.GetItem(l.PickaxeID)
case "Glider":
return person.AthenaProfile.Items.GetItem(l.GliderID)
case "SkyDiveContrail":
return person.AthenaProfile.Items.GetItem(l.ContrailID)
case "LoadingScreen":
return person.AthenaProfile.Items.GetItem(l.LoadingScreenID)
case "MusicPack":
return person.AthenaProfile.Items.GetItem(l.MusicPackID)
}
return nil
}
func (l *Loadout) GetItemSlotData(itemId string) aid.JSON {
json := aid.JSON{
"items": []string{},

View File

@ -253,19 +253,24 @@ func (p *Person) Save() {
storage.Repo.SavePerson(dbPerson)
}
func (p *Person) SaveShallow() {
dbPerson := p.ToDatabaseShallow()
storage.Repo.SavePerson(dbPerson)
}
func (p *Person) Ban() {
p.IsBanned = true
p.Save()
p.SaveShallow()
}
func (p *Person) Unban() {
p.IsBanned = false
p.Save()
p.SaveShallow()
}
func (p *Person) AddPermission(permission string) {
p.Permissions = append(p.Permissions, permission)
p.Save()
p.SaveShallow()
}
func (p *Person) RemovePermission(permission string) {
@ -275,7 +280,7 @@ func (p *Person) RemovePermission(permission string) {
break
}
}
p.Save()
p.SaveShallow()
}
func (p *Person) HasPermission(permission Permission) bool {
@ -360,6 +365,24 @@ func (p *Person) ToDatabase() *storage.DB_Person {
return &dbPerson
}
func (p *Person) ToDatabaseShallow() *storage.DB_Person {
dbPerson := storage.DB_Person{
ID: p.ID,
DisplayName: p.DisplayName,
Permissions: p.Permissions,
IsBanned: p.IsBanned,
Profiles: []storage.DB_Profile{},
Stats: []storage.DB_SeasonStat{},
Discord: storage.DB_DiscordPerson{},
}
if p.Discord != nil {
dbPerson.Discord = *p.Discord
}
return &dbPerson
}
func (p *Person) AddAttribute(value *Attribute) {
p.AthenaProfile.Attributes.AddAttribute(value)
}

View File

@ -183,6 +183,8 @@ func (p *Profile) Diff(b *ProfileSnapshot) []diff.Change {
return nil
}
loadout := p.GetActiveLoadout()
for _, change := range changes {
switch change.Path[0] {
case "Items":
@ -199,7 +201,14 @@ func (p *Profile) Diff(b *ProfileSnapshot) []diff.Change {
}
if change.Type == "update" && change.Path[2] != "Quantity" {
p.CreateItemAttributeChangedChange(p.Items.GetItem(change.Path[1]), change.Path[2])
item := p.Items.GetItem(change.Path[1])
p.CreateItemAttributeChangedChange(item, change.Path[2])
slotType := loadout.GetSlotFromItemTemplateID(item.TemplateID)
slotValue := loadout.GetItemFromSlot(slotType)
if slotValue != nil && slotValue.ID == item.ID {
p.CreateLoadoutChangedChange(loadout, slotType + "ID")
}
}
case "Quests":
if change.Type == "create" && change.Path[2] == "ID" {
@ -223,7 +232,12 @@ func (p *Profile) Diff(b *ProfileSnapshot) []diff.Change {
}
if change.Type == "update" && change.Path[2] == "ValueJSON" {
p.CreateStatModifiedChange(p.Attributes.GetAttribute(change.Path[1]))
attribute := p.Attributes.GetAttribute(change.Path[1])
p.CreateStatModifiedChange(attribute)
if attribute.Key == "last_applied_loadout" {
p.CreateLoadoutChangedChange(p.GetActiveLoadout(), "CharacterID")
}
}
case "Loadouts":
if change.Type == "create" && change.Path[2] == "ID" {
@ -243,6 +257,20 @@ func (p *Profile) Diff(b *ProfileSnapshot) []diff.Change {
return changes
}
func (p *Profile) GetActiveLoadout() *Loadout {
lastAppliedLoadoutAttribute := p.Attributes.GetAttributeByKey("last_applied_loadout")
if lastAppliedLoadoutAttribute == nil {
return nil
}
lastAppliedLoadout := p.Loadouts.GetLoadout(AttributeConvert[string](lastAppliedLoadoutAttribute))
if lastAppliedLoadout == nil {
return nil
}
return lastAppliedLoadout
}
func (p *Profile) CreateAttribute(key string, value interface{}) *Attribute {
p.Attributes.AddAttribute(NewAttribute(key, value))
return p.Attributes.GetAttribute(key)

View File

@ -58,11 +58,11 @@ func (s *PostgresStorage) GetPerson(personId string) *DB_Person {
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
// Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Quests").
Preload("Discord").
Where("id = ?", personId).
@ -81,11 +81,11 @@ func (s *PostgresStorage) GetPersonByDisplay(displayName string) *DB_Person {
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
// Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Quests").
Preload("Discord").
Where("display_name = ?", displayName).
@ -104,11 +104,11 @@ func (s *PostgresStorage) GetPersonsByPartialDisplay(displayName string) []*DB_P
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
// Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Quests").
Preload("Discord").
Where("display_name LIKE ?", "%" + displayName + "%").
@ -139,11 +139,11 @@ func (s *PostgresStorage) GetAllPersons() []*DB_Person {
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
// Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Quests").
Preload("Discord").
Find(&dbPersons)
@ -172,11 +172,11 @@ func (s *PostgresStorage) DeletePerson(personId string) {
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
// Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Quests").
Preload("Discord").
Delete(&DB_Person{}, "id = ?", personId)
@ -226,6 +226,10 @@ func (s *PostgresStorage) SaveVariant(variant *DB_VariantChannel) {
s.Postgres.Save(variant)
}
func (s *PostgresStorage) BulkCreateVariants(variants *[]DB_VariantChannel) {
s.Postgres.Create(variants)
}
func (s *PostgresStorage) DeleteVariant(variantId string) {
s.Postgres.Delete(&DB_VariantChannel{}, "id = ?", variantId)
}

View File

@ -31,6 +31,7 @@ type Storage interface {
DeleteItem(itemId string)
SaveVariant(variant *DB_VariantChannel)
BulkCreateVariants(variants *[]DB_VariantChannel)
DeleteVariant(variantId string)
SaveQuest(quest *DB_Quest)
@ -154,6 +155,10 @@ func (r *Repository) DeleteItem(itemId string) {
r.Storage.DeleteItem(itemId)
}
func (r *Repository) BulkCreateVariants(variants *[]DB_VariantChannel) {
r.Storage.BulkCreateVariants(variants)
}
func (r *Repository) SaveVariant(variant *DB_VariantChannel) {
r.Storage.SaveVariant(variant)
}