Add other locker (s12+)

This commit is contained in:
Eccentric 2024-02-03 02:33:54 +00:00
parent b3394099a8
commit c33abe7d6f
7 changed files with 258 additions and 38 deletions

View File

@ -138,7 +138,7 @@ func NewFortnitePersonWithId(id string, displayName string, everything bool) *p.
person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("allowed_to_send_gifts", true)).Save() person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("allowed_to_send_gifts", true)).Save()
person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("gift_history", aid.JSON{})).Save() person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("gift_history", aid.JSON{})).Save()
loadout := p.NewLoadout("sandbox_loadout", person.AthenaProfile) loadout := p.NewLoadoutWithID("sandbox_loadout", "", person.AthenaProfile)
person.AthenaProfile.Loadouts.AddLoadout(loadout).Save() person.AthenaProfile.Loadouts.AddLoadout(loadout).Save()
person.AthenaProfile.Attributes.AddAttribute(p.NewAttribute("loadouts", []string{loadout.ID})).Save() person.AthenaProfile.Attributes.AddAttribute(p.NewAttribute("loadouts", []string{loadout.ID})).Save()
person.AthenaProfile.Attributes.AddAttribute(p.NewAttribute("last_applied_loadout", loadout.ID)).Save() person.AthenaProfile.Attributes.AddAttribute(p.NewAttribute("last_applied_loadout", loadout.ID)).Save()

View File

@ -9,6 +9,7 @@ import (
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
"github.com/ectrc/snow/fortnite" "github.com/ectrc/snow/fortnite"
p "github.com/ectrc/snow/person" p "github.com/ectrc/snow/person"
"github.com/google/uuid"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -23,6 +24,9 @@ var (
"SetBattleRoyaleBanner": clientSetBattleRoyaleBannerAction, "SetBattleRoyaleBanner": clientSetBattleRoyaleBannerAction,
"SetCosmeticLockerSlot": clientSetCosmeticLockerSlotAction, "SetCosmeticLockerSlot": clientSetCosmeticLockerSlotAction,
"SetCosmeticLockerBanner": clientSetCosmeticLockerBannerAction, "SetCosmeticLockerBanner": clientSetCosmeticLockerBannerAction,
"SetCosmeticLockerName": clientSetCosmeticLockerNameAction,
"CopyCosmeticLoadout": clientCopyCosmeticLoadoutAction,
"DeleteCosmeticLoadout": clientDeleteCosmeticLoadoutAction,
"PurchaseCatalogEntry": clientPurchaseCatalogEntryAction, "PurchaseCatalogEntry": clientPurchaseCatalogEntryAction,
"GiftCatalogEntry": clientGiftCatalogEntryAction, "GiftCatalogEntry": clientGiftCatalogEntryAction,
"RemoveGiftBox": clientRemoveGiftBoxAction, "RemoveGiftBox": clientRemoveGiftBoxAction,
@ -188,8 +192,8 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
default: default:
attr.ValueJSON = aid.JSONStringify(item.ID) attr.ValueJSON = aid.JSONStringify(item.ID)
} }
go attr.Save() go attr.Save()
return nil return nil
} }
@ -225,11 +229,9 @@ func clientSetBattleRoyaleBannerAction(c *fiber.Ctx, person *p.Person, profile *
iconAttr.ValueJSON = aid.JSONStringify(strings.Split(iconItem.TemplateID, ":")[1]) iconAttr.ValueJSON = aid.JSONStringify(strings.Split(iconItem.TemplateID, ":")[1])
colorAttr.ValueJSON = aid.JSONStringify(strings.Split(colorItem.TemplateID, ":")[1]) colorAttr.ValueJSON = aid.JSONStringify(strings.Split(colorItem.TemplateID, ":")[1])
iconAttr.Save()
colorAttr.Save()
go func() {
iconAttr.Save()
colorAttr.Save()
}()
return nil return nil
} }
@ -321,6 +323,7 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
} }
go currentLocker.Save() go currentLocker.Save()
return nil return nil
} }
@ -351,11 +354,194 @@ func clientSetCosmeticLockerBannerAction(c *fiber.Ctx, person *p.Person, profile
if currentLocker == nil { if currentLocker == nil {
return fmt.Errorf("current locker not found") return fmt.Errorf("current locker not found")
} }
currentLocker.BannerColorID = color.ID currentLocker.BannerColorID = color.ID
currentLocker.BannerID = icon.ID currentLocker.BannerID = icon.ID
go currentLocker.Save() go currentLocker.Save()
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")
}
lastAppliedLoadout := profile.Loadouts.GetLoadout(aid.JSONParse(lastAppliedLoadoutAttribute.ValueJSON).(string))
if lastAppliedLoadout == nil {
return fmt.Errorf("last applied loadout not found")
}
if body.TargetIndex >= len(loadouts) {
clone := lastAppliedLoadout.Copy()
clone.ID = uuid.New().String()
clone.LockerName = body.OptNewNameForTarget
profile.Loadouts.AddLoadout(&clone).Save()
lastAppliedLoadout.CopyFrom(sandboxLoadout)
go lastAppliedLoadout.Save()
sandboxLoadout.CopyFrom(&clone)
go sandboxLoadout.Save()
loadouts = append(loadouts, clone.ID)
loadoutsAttribute.ValueJSON = aid.JSONStringify(loadouts)
go loadoutsAttribute.Save()
lastAppliedLoadoutAttribute.ValueJSON = aid.JSONStringify(clone.ID)
activeLoadoutIndexAttribute.ValueJSON = aid.JSONStringify(body.TargetIndex)
go lastAppliedLoadoutAttribute.Save()
go activeLoadoutIndexAttribute.Save()
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()
lastAppliedLoadoutAttribute.ValueJSON = aid.JSONStringify(sourceLoadout.ID)
activeLoadoutIndexAttribute.ValueJSON = aid.JSONStringify(body.SourceIndex)
go lastAppliedLoadoutAttribute.Save()
go activeLoadoutIndexAttribute.Save()
return nil
}
activeLoadout := profile.Loadouts.GetLoadout(loadouts[body.TargetIndex])
if activeLoadout == nil {
return fmt.Errorf("target loadout not found")
}
sandboxLoadout.CopyFrom(activeLoadout)
go sandboxLoadout.Save()
if len(profile.Changes) == 0{
// dance ids and item wrap ids are registered as changes in the client so force a fix
profile.CreateLoadoutChangedChange(sandboxLoadout, "DanceID")
}
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 return nil
} }
@ -394,12 +580,9 @@ func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p
} }
vbucks.Quantity -= body.ExpectedTotalPrice vbucks.Quantity -= body.ExpectedTotalPrice
profile0Vbucks.Quantity = vbucks.Quantity
go func() { vbucks.Save()
profile0Vbucks.Quantity = vbucks.Quantity profile0Vbucks.Save()
vbucks.Save()
profile0Vbucks.Save()
}()
if offer.ProfileType != "athena" { if offer.ProfileType != "athena" {
return fmt.Errorf("save the world not implemeted yet") return fmt.Errorf("save the world not implemeted yet")
@ -481,12 +664,9 @@ func clientGiftCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Pro
} }
vbucks.Quantity -= price vbucks.Quantity -= price
profile0Vbucks.Quantity = price
go func() { vbucks.Save()
profile0Vbucks.Quantity = price profile0Vbucks.Save()
vbucks.Save()
profile0Vbucks.Save()
}()
for _, receiverAccountId := range body.ReceiverAccountIds { for _, receiverAccountId := range body.ReceiverAccountIds {
receiverPerson := p.Find(receiverAccountId) receiverPerson := p.Find(receiverAccountId)
@ -501,8 +681,7 @@ func clientGiftCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Pro
gift.AddLoot(item) gift.AddLoot(item)
} }
receiverPerson.CommonCoreProfile.Gifts.AddGift(gift) receiverPerson.CommonCoreProfile.Gifts.AddGift(gift).Save()
receiverPerson.CommonCoreProfile.Save()
} }
return nil return nil

17
main.go
View File

@ -45,14 +45,19 @@ func init() {
fortnite.GenerateRandomStorefront() fortnite.GenerateRandomStorefront()
fortnite.GeneratePlaylistImages() fortnite.GeneratePlaylistImages()
if found := person.FindByDisplay("god"); found == nil { for _, username := range aid.Config.Accounts.Gods {
god := fortnite.NewFortnitePersonWithId("god", "god", true) found := person.FindByDisplay(username)
god.AddPermission("all") if found == nil {
found = fortnite.NewFortnitePersonWithId(username, username, true)
}
angel := fortnite.NewFortnitePersonWithId("angel", "angel", true) for _, perm := range found.Permissions {
angel.AddPermission("all") found.RemovePermission(perm)
}
found.AddPermission("all")
aid.Print("(snow) max account " + username + " loaded")
} }
} }
func main() { func main() {
r := fiber.New(fiber.Config{ r := fiber.New(fiber.Config{

View File

@ -54,4 +54,18 @@ func (a *Attribute) Save() {
return return
} }
storage.Repo.SaveAttribute(a.ToDatabase(a.ProfileID)) storage.Repo.SaveAttribute(a.ToDatabase(a.ProfileID))
}
func AttributeConvertToSlice[T any](attribute *Attribute) []T {
valuesRaw := aid.JSONParse(attribute.ValueJSON).([]interface{})
values := make([]T, len(valuesRaw))
for i, value := range valuesRaw {
values[i] = value.(T)
}
return values
}
func AttributeConvert[T any](attribute *Attribute) T {
return aid.JSONParse(attribute.ValueJSON).(T)
} }

View File

@ -68,7 +68,8 @@ func NewLoadout(name string, athena *Profile) *Loadout {
return &Loadout{ return &Loadout{
ID: uuid.New().String(), ID: uuid.New().String(),
PersonID: athena.ID, PersonID: athena.PersonID,
ProfileID: athena.ID,
TemplateID: "CosmeticLocker:CosmeticLocker_Athena", TemplateID: "CosmeticLocker:CosmeticLocker_Athena",
LockerName: name, LockerName: name,
CharacterID: aid.JSONParse(character.ValueJSON).(string), CharacterID: aid.JSONParse(character.ValueJSON).(string),
@ -85,6 +86,12 @@ func NewLoadout(name string, athena *Profile) *Loadout {
} }
} }
func NewLoadoutWithID(id string, name string, athena *Profile) *Loadout {
loadout := NewLoadout(name, athena)
loadout.ID = id
return loadout
}
func FromDatabaseLoadout(loadout *storage.DB_Loadout) *Loadout { func FromDatabaseLoadout(loadout *storage.DB_Loadout) *Loadout {
return &Loadout{ return &Loadout{
ID: loadout.ID, ID: loadout.ID,
@ -229,14 +236,7 @@ func (l *Loadout) GetItemsSlotData(itemIds []string) aid.JSON {
items := json["items"].([]string) items := json["items"].([]string)
items[pos] = item.TemplateID items[pos] = item.TemplateID
activeVariants := json["activeVariants"].([]aid.JSON)
activeVariants[pos] = aid.JSON{
"variants": []aid.JSON{},
}
json["items"] = items json["items"] = items
json["activeVariants"] = activeVariants
} }
return json return json
@ -268,4 +268,25 @@ func (l *Loadout) ToDatabase(profileId string) *storage.DB_Loadout {
func (q *Loadout) Save() { func (q *Loadout) Save() {
storage.Repo.SaveLoadout(q.ToDatabase(q.ProfileID)) storage.Repo.SaveLoadout(q.ToDatabase(q.ProfileID))
}
func (l *Loadout) Copy() Loadout {
return *l
}
func (l *Loadout) CopyFrom(loadout *Loadout) {
l.ProfileID = loadout.ProfileID
l.BannerID = loadout.BannerID
l.BannerColorID = loadout.BannerColorID
l.CharacterID = loadout.CharacterID
l.PickaxeID = loadout.PickaxeID
l.BackpackID = loadout.BackpackID
l.GliderID = loadout.GliderID
copy(l.DanceID, loadout.DanceID)
copy(l.ItemWrapID, loadout.ItemWrapID)
l.ContrailID = loadout.ContrailID
l.LoadingScreenID = loadout.LoadingScreenID
l.MusicPackID = loadout.MusicPackID
l.Save()
} }

View File

@ -255,11 +255,11 @@ func (m *LoadoutMutex) AddLoadout(loadout *Loadout) *Loadout {
loadout.PersonID = m.PersonID loadout.PersonID = m.PersonID
loadout.ProfileID = m.ProfileID loadout.ProfileID = m.ProfileID
m.Store(loadout.ID, loadout) m.Store(loadout.ID, loadout)
// storage.Repo.SaveLoadout(loadout.ToDatabase(m.ProfileID)) storage.Repo.SaveLoadout(loadout.ToDatabase(m.ProfileID))
return loadout return loadout
} }
func (m *LoadoutMutex) DeleteItem(id string) { func (m *LoadoutMutex) DeleteLoadout(id string) {
loadout := m.GetLoadout(id) loadout := m.GetLoadout(id)
if loadout == nil { if loadout == nil {
return return

View File

@ -13,7 +13,7 @@ Performance first, universal Fortnite private server backend written in Go.
## What's next? ## What's next?
- More profile actions like `RefundMtxPurchase` and `CopyCosmeticLoadout`. - More profile actions like `RefundMtxPurchase` and more.
- Integrating matchmaking with a hoster to smartly put players into games and know when servers become available. - 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. - 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). - Refactor the XMPP solution to use [melium/xmpp](https://github.com/mellium/xmpp).
@ -23,6 +23,7 @@ Performance first, universal Fortnite private server backend written in Go.
### Supported ### Supported
- **_Chapter 1 Season 2_** `Fortnite+Release-2.5-CL-3889387-Windows` - **_Chapter 1 Season 2_** `Fortnite+Release-2.5-CL-3889387-Windows`
- **_Chapter 1 Season 3_** `Fortnite+Release-3.6-CL-4019403-Windows`
- **_Chapter 1 Season 5_** `Fortnite+Release-5.41-CL-4363240-Windows` - **_Chapter 1 Season 5_** `Fortnite+Release-5.41-CL-4363240-Windows`
- **_Chapter 1 Season 8_** `Fortnite+Release-8.51-CL-6165369-Windows` - **_Chapter 1 Season 8_** `Fortnite+Release-8.51-CL-6165369-Windows`
- **_Chapter 2 Season 2_** `Fortnite+Release-12.41-CL-12905909-Windows` - **_Chapter 2 Season 2_** `Fortnite+Release-12.41-CL-12905909-Windows`