Add other locker (s12+)
This commit is contained in:
parent
b3394099a8
commit
c33abe7d6f
|
@ -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("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.Attributes.AddAttribute(p.NewAttribute("loadouts", []string{loadout.ID})).Save()
|
||||
person.AthenaProfile.Attributes.AddAttribute(p.NewAttribute("last_applied_loadout", loadout.ID)).Save()
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/ectrc/snow/aid"
|
||||
"github.com/ectrc/snow/fortnite"
|
||||
p "github.com/ectrc/snow/person"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
@ -23,6 +24,9 @@ var (
|
|||
"SetBattleRoyaleBanner": clientSetBattleRoyaleBannerAction,
|
||||
"SetCosmeticLockerSlot": clientSetCosmeticLockerSlotAction,
|
||||
"SetCosmeticLockerBanner": clientSetCosmeticLockerBannerAction,
|
||||
"SetCosmeticLockerName": clientSetCosmeticLockerNameAction,
|
||||
"CopyCosmeticLoadout": clientCopyCosmeticLoadoutAction,
|
||||
"DeleteCosmeticLoadout": clientDeleteCosmeticLoadoutAction,
|
||||
"PurchaseCatalogEntry": clientPurchaseCatalogEntryAction,
|
||||
"GiftCatalogEntry": clientGiftCatalogEntryAction,
|
||||
"RemoveGiftBox": clientRemoveGiftBoxAction,
|
||||
|
@ -188,8 +192,8 @@ func clientEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person,
|
|||
default:
|
||||
attr.ValueJSON = aid.JSONStringify(item.ID)
|
||||
}
|
||||
|
||||
go attr.Save()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -225,11 +229,9 @@ func clientSetBattleRoyaleBannerAction(c *fiber.Ctx, person *p.Person, profile *
|
|||
|
||||
iconAttr.ValueJSON = aid.JSONStringify(strings.Split(iconItem.TemplateID, ":")[1])
|
||||
colorAttr.ValueJSON = aid.JSONStringify(strings.Split(colorItem.TemplateID, ":")[1])
|
||||
iconAttr.Save()
|
||||
colorAttr.Save()
|
||||
|
||||
go func() {
|
||||
iconAttr.Save()
|
||||
colorAttr.Save()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -321,6 +323,7 @@ func clientSetCosmeticLockerSlotAction(c *fiber.Ctx, person *p.Person, profile *
|
|||
}
|
||||
|
||||
go currentLocker.Save()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -351,11 +354,194 @@ func clientSetCosmeticLockerBannerAction(c *fiber.Ctx, person *p.Person, profile
|
|||
if currentLocker == nil {
|
||||
return fmt.Errorf("current locker not found")
|
||||
}
|
||||
|
||||
currentLocker.BannerColorID = color.ID
|
||||
currentLocker.BannerID = icon.ID
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -394,12 +580,9 @@ func clientPurchaseCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p
|
|||
}
|
||||
|
||||
vbucks.Quantity -= body.ExpectedTotalPrice
|
||||
|
||||
go func() {
|
||||
profile0Vbucks.Quantity = vbucks.Quantity
|
||||
vbucks.Save()
|
||||
profile0Vbucks.Save()
|
||||
}()
|
||||
profile0Vbucks.Quantity = vbucks.Quantity
|
||||
vbucks.Save()
|
||||
profile0Vbucks.Save()
|
||||
|
||||
if offer.ProfileType != "athena" {
|
||||
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
|
||||
|
||||
go func() {
|
||||
profile0Vbucks.Quantity = price
|
||||
vbucks.Save()
|
||||
profile0Vbucks.Save()
|
||||
}()
|
||||
profile0Vbucks.Quantity = price
|
||||
vbucks.Save()
|
||||
profile0Vbucks.Save()
|
||||
|
||||
for _, receiverAccountId := range body.ReceiverAccountIds {
|
||||
receiverPerson := p.Find(receiverAccountId)
|
||||
|
@ -501,8 +681,7 @@ func clientGiftCatalogEntryAction(c *fiber.Ctx, person *p.Person, profile *p.Pro
|
|||
gift.AddLoot(item)
|
||||
}
|
||||
|
||||
receiverPerson.CommonCoreProfile.Gifts.AddGift(gift)
|
||||
receiverPerson.CommonCoreProfile.Save()
|
||||
receiverPerson.CommonCoreProfile.Gifts.AddGift(gift).Save()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
17
main.go
17
main.go
|
@ -45,14 +45,19 @@ func init() {
|
|||
fortnite.GenerateRandomStorefront()
|
||||
fortnite.GeneratePlaylistImages()
|
||||
|
||||
if found := person.FindByDisplay("god"); found == nil {
|
||||
god := fortnite.NewFortnitePersonWithId("god", "god", true)
|
||||
god.AddPermission("all")
|
||||
for _, username := range aid.Config.Accounts.Gods {
|
||||
found := person.FindByDisplay(username)
|
||||
if found == nil {
|
||||
found = fortnite.NewFortnitePersonWithId(username, username, true)
|
||||
}
|
||||
|
||||
angel := fortnite.NewFortnitePersonWithId("angel", "angel", true)
|
||||
angel.AddPermission("all")
|
||||
for _, perm := range found.Permissions {
|
||||
found.RemovePermission(perm)
|
||||
}
|
||||
|
||||
found.AddPermission("all")
|
||||
aid.Print("(snow) max account " + username + " loaded")
|
||||
}
|
||||
|
||||
}
|
||||
func main() {
|
||||
r := fiber.New(fiber.Config{
|
||||
|
|
|
@ -55,3 +55,17 @@ func (a *Attribute) Save() {
|
|||
}
|
||||
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)
|
||||
}
|
|
@ -68,7 +68,8 @@ func NewLoadout(name string, athena *Profile) *Loadout {
|
|||
|
||||
return &Loadout{
|
||||
ID: uuid.New().String(),
|
||||
PersonID: athena.ID,
|
||||
PersonID: athena.PersonID,
|
||||
ProfileID: athena.ID,
|
||||
TemplateID: "CosmeticLocker:CosmeticLocker_Athena",
|
||||
LockerName: name,
|
||||
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 {
|
||||
return &Loadout{
|
||||
ID: loadout.ID,
|
||||
|
@ -229,14 +236,7 @@ func (l *Loadout) GetItemsSlotData(itemIds []string) aid.JSON {
|
|||
|
||||
items := json["items"].([]string)
|
||||
items[pos] = item.TemplateID
|
||||
|
||||
activeVariants := json["activeVariants"].([]aid.JSON)
|
||||
activeVariants[pos] = aid.JSON{
|
||||
"variants": []aid.JSON{},
|
||||
}
|
||||
|
||||
json["items"] = items
|
||||
json["activeVariants"] = activeVariants
|
||||
}
|
||||
|
||||
return json
|
||||
|
@ -269,3 +269,24 @@ func (l *Loadout) ToDatabase(profileId string) *storage.DB_Loadout {
|
|||
func (q *Loadout) Save() {
|
||||
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()
|
||||
}
|
|
@ -255,11 +255,11 @@ func (m *LoadoutMutex) AddLoadout(loadout *Loadout) *Loadout {
|
|||
loadout.PersonID = m.PersonID
|
||||
loadout.ProfileID = m.ProfileID
|
||||
m.Store(loadout.ID, loadout)
|
||||
// storage.Repo.SaveLoadout(loadout.ToDatabase(m.ProfileID))
|
||||
storage.Repo.SaveLoadout(loadout.ToDatabase(m.ProfileID))
|
||||
return loadout
|
||||
}
|
||||
|
||||
func (m *LoadoutMutex) DeleteItem(id string) {
|
||||
func (m *LoadoutMutex) DeleteLoadout(id string) {
|
||||
loadout := m.GetLoadout(id)
|
||||
if loadout == nil {
|
||||
return
|
||||
|
|
|
@ -13,7 +13,7 @@ Performance first, universal Fortnite private server backend written in Go.
|
|||
|
||||
## 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.
|
||||
- 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).
|
||||
|
@ -23,6 +23,7 @@ Performance first, universal Fortnite private server backend written in Go.
|
|||
### Supported
|
||||
|
||||
- **_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 8_** `Fortnite+Release-8.51-CL-6165369-Windows`
|
||||
- **_Chapter 2 Season 2_** `Fortnite+Release-12.41-CL-12905909-Windows`
|
||||
|
|
Loading…
Reference in New Issue
Block a user