Equip Item; Refactor Loadouts; Move Cache System; ...

Change up attribrutes again.
Fix indent bug.
This commit is contained in:
eccentric 2023-11-05 01:58:00 +00:00
parent 113c68a38d
commit db9f92bd91
23 changed files with 727 additions and 478 deletions

View File

@ -1,6 +1,7 @@
package aid
import (
"encoding/json"
"os"
"os/signal"
"syscall"
@ -11,3 +12,14 @@ func WaitForExit() {
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-sc
}
func JSONStringify(input interface{}) string {
json, _ := json.Marshal(input)
return string(json)
}
func JSONParse(input string) interface{} {
var output interface{}
json.Unmarshal([]byte(input), &output)
return output
}

View File

@ -2,6 +2,8 @@ package aid
import (
"os"
"strconv"
"strings"
"gopkg.in/ini.v1"
)
@ -22,6 +24,10 @@ type CS struct {
JWT struct {
Secret string
}
Fortnite struct {
Season int
Build float64
}
}
var (
@ -74,4 +80,28 @@ func LoadConfig() {
if Config.JWT.Secret == "" {
panic("JWT Secret is empty")
}
build, err := cfg.Section("fortnite").Key("build").Float64()
if err != nil {
panic("Fortnite Build is empty")
}
Config.Fortnite.Build = build
buildStr := strconv.FormatFloat(build, 'f', -1, 64)
if buildStr == "" {
panic("Fortnite Build is empty")
}
buildInfo := strings.Split(buildStr, ".")
if len(buildInfo) < 2 {
panic("Fortnite Build is invalid")
}
parsedSeason, err := strconv.Atoi(buildInfo[0])
if err != nil {
panic("Fortnite Season is invalid")
}
Config.Fortnite.Season = parsedSeason
}

15
aid/time.go Normal file
View File

@ -0,0 +1,15 @@
package aid
import "time"
func TimeStartOfDay() string {
return time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()).Format("2006-01-02T15:04:05.999Z")
}
func TimeEndOfDay() string {
return time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 23, 59, 59, 999999999, time.Now().Location()).Format("2006-01-02T15:04:05.999Z")
}
func TimeEndOfWeekString() string {
return time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 23, 59, 59, 999999999, time.Now().Location()).AddDate(0, 0, 7).Format("2006-01-02T15:04:05.999Z")
}

View File

@ -23,3 +23,7 @@ host="0.0.0.0"
[jwt]
; secret for jwt signing
secret="secret"
[fortnite]
; the game build to use
build=2.5

View File

@ -34,3 +34,45 @@ func GetVersionCheck(c *fiber.Ctx) error {
"type": "NO_UPDATE",
})
}
func GetContentPages(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"battlepassaboutmessages": aid.JSON{
"news": aid.JSON{
"messages": []aid.JSON{},
},
"lastModified": "0000-00-00T00:00:00.000Z",
},
"subgameselectdata": aid.JSON{
"saveTheWorldUnowned": aid.JSON{
"message": aid.JSON{
"title": "Co-op PvE",
"body": "Cooperative PvE storm-fighting adventure!",
"spotlight": false,
"hidden": true,
"messagetype": "normal",
},
},
"battleRoyale": aid.JSON{
"message": aid.JSON{
"title": "100 Player PvP",
"body": "100 Player PvP Battle Royale.\n\nPvE progress does not affect Battle Royale.",
"spotlight": false,
"hidden": true,
"messagetype": "normal",
},
},
"creative": aid.JSON{
"message": aid.JSON{
"title": "New Featured Islands!",
"body": "Your Island. Your Friends. Your Rules.\n\nDiscover new ways to play Fortnite, play community made games with friends and build your dream island.",
"spotlight": false,
"hidden": true,
"messagetype": "normal",
},
},
"lastModified": "0000-00-00T00:00:00.000Z",
},
"lastModified": "0000-00-00T00:00:00.000Z",
})
}

View File

@ -1,6 +1,9 @@
package handlers
import (
"strconv"
"time"
"github.com/ectrc/snow/aid"
"github.com/gofiber/fiber/v2"
)
@ -12,3 +15,50 @@ func GetLightswitchBulkStatus(c *fiber.Ctx) error {
"banned": false,
}})
}
func GetTimelineCalendar(c *fiber.Ctx) error {
events := []aid.JSON{
{
"activeUntil": aid.TimeEndOfWeekString(),
"activeSince": "0001-01-01T00:00:00Z",
"activeEventId": "EventFlag.Season" + strconv.Itoa(aid.Config.Fortnite.Season),
},
{
"activeUntil": aid.TimeEndOfWeekString(),
"activeSince": "0001-01-01T00:00:00Z",
"activeEventId": "EventFlag.LobbySeason" + strconv.Itoa(aid.Config.Fortnite.Season),
},
}
state := aid.JSON{
"seasonNumber": aid.Config.Fortnite.Season,
"seasonTemplateId": "AthenaSeason:AthenaSeason" + strconv.Itoa(aid.Config.Fortnite.Season),
"seasonBegin": time.Now().Add(-time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
"seasonEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
"seasonDisplayedEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
"activeStorefronts": []aid.JSON{},
"dailyStoreEnd": aid.TimeEndOfDay(),
"weeklyStoreEnd": aid.TimeEndOfWeekString(),
"sectionStoreEnds": aid.JSON{},
"stwEventStoreEnd": aid.TimeEndOfWeekString(),
"stwWeeklyStoreEnd": aid.TimeEndOfWeekString(),
}
client := aid.JSON{
"states": []aid.JSON{{
"activeEvents": events,
"state": state,
"validFrom": "0001-01-01T00:00:00Z",
}},
"cacheExpire": "9999-12-31T23:59:59.999Z",
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"channels": aid.JSON{
"client-events": client,
},
"currentTime": time.Now().Format("2006-01-02T15:04:05.000Z"),
"cacheIntervalMins": 5,
"eventsTimeOffsetHrs": 0,
})
}

View File

@ -1,6 +1,8 @@
package handlers
import (
"strconv"
"strings"
"time"
"github.com/ectrc/snow/aid"
@ -13,6 +15,8 @@ var (
profileActions = map[string]func(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
"QueryProfile": PostQueryProfileAction,
"ClientQuestLogin": PostQueryProfileAction,
"MarkItemSeen": PostMarkItemSeenAction,
"EquipBattleRoyaleCustomization": PostEquipBattleRoyaleCustomizationAction,
}
)
@ -21,27 +25,33 @@ func PostProfileAction(c *fiber.Ctx) error {
if person == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Account Found"))
}
defer person.Save()
profile := person.GetProfileFromType(c.Query("profileId"))
if profile == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Profile Found"))
}
defer profile.ClearProfileChanges()
snapshot := profile.Snapshot()
before := profile.Snapshot()
if action, ok := profileActions[c.Params("action")]; ok {
err := action(c, person, profile)
if err != nil {
if err := action(c, person, profile); err != nil {
return err
}
}
profile.Diff(snapshot)
profile.Revision++
changes := profile.Diff(before)
aid.Print("Changes: " + strconv.Itoa(len(changes)))
aid.PrintJSON(changes)
revision, _ := strconv.Atoi(c.Query("rvn"))
if revision == -1 {
revision = profile.Revision
}
revision++
return c.Status(200).JSON(aid.JSON{
"profileId": profile.Type,
"profileRevision": profile.Revision,
"profileCommandRevision": profile.Revision,
"profileChangesBaseRevision": profile.Revision - 1,
"profileRevision": revision,
"profileCommandRevision": revision,
"profileChangesBaseRevision": revision - 1,
"profileChanges": profile.Changes,
"multiUpdate": []aid.JSON{},
"notifications": []aid.JSON{},
@ -51,7 +61,66 @@ func PostProfileAction(c *fiber.Ctx) error {
}
func PostQueryProfileAction(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
profile.Changes = []interface{}{}
profile.CreateFullProfileUpdateChange()
return nil
}
func PostMarkItemSeenAction(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
var body struct {
ItemIds []string `json:"itemIds"`
}
err := c.BodyParser(&body)
if err != nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Body"))
}
for _, itemId := range body.ItemIds {
item := profile.Items.GetItem(itemId)
if item == nil {
continue
}
item.HasSeen = true
}
return nil
}
func PostEquipBattleRoyaleCustomizationAction(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
var body struct {
SlotName string `json:"slotName"`
ItemToSlot string `json:"itemToSlot"`
IndexWithinSlot int `json:"indexWithinSlot"`
}
err := c.BodyParser(&body)
if err != nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Body"))
}
item := profile.Items.GetItem(body.ItemToSlot)
if item == nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Item not found"))
}
attr := profile.Attributes.GetAttributeByKey("favorite_" + strings.ToLower(body.SlotName))
if attr == nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Attribute not found"))
}
switch body.SlotName {
case "Dance":
value := aid.JSONParse(attr.ValueJSON)
value.([]any)[body.IndexWithinSlot] = item.ID
attr.ValueJSON = aid.JSONStringify(value)
case "ItemWrap":
value := aid.JSONParse(attr.ValueJSON)
value.([]any)[body.IndexWithinSlot] = item.ID
attr.ValueJSON = aid.JSONStringify(value)
default:
attr.ValueJSON = aid.JSONStringify(item.ID)
}
return nil
}

View File

@ -17,7 +17,7 @@ func GetCloudStorageConfig(c *fiber.Ctx) error {
"epicAppName": "Live",
"isAuthenticated": true,
"disableV2": true,
"lastUpdated": "2021-01-01T00:00:00Z",
"lastUpdated": "0000-00-00T00:00:00.000Z",
"transports": []string{},
})
}
@ -30,3 +30,15 @@ func GetCloudStorageFile(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{})
}
func GetUserStorageFiles(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]aid.JSON{})
}
func GetUserStorageFile(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{})
}
func PutUserStorageFile(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{})
}

12
main.go
View File

@ -22,7 +22,6 @@ func init() {
}
postgresStorage.Migrate(&storage.DB_Person{}, "Persons")
postgresStorage.Migrate(&storage.DB_Loadout{}, "Loadouts")
postgresStorage.Migrate(&storage.DB_Profile{}, "Profiles")
postgresStorage.Migrate(&storage.DB_Item{}, "Items")
postgresStorage.Migrate(&storage.DB_Gift{}, "Gifts")
@ -35,12 +34,11 @@ func init() {
}
storage.Repo = storage.NewStorage(device)
storage.Cache = storage.NewPersonsCacheMutex()
}
func init() {
if aid.Config.Database.DropAllTables {
person.NewFortnitePerson("ac", "ket")
person.NewFortnitePerson("ac", "1")
}
aid.PrintTime("Loading all persons from database", func() {
@ -49,7 +47,7 @@ func init() {
}
})
go storage.Cache.CacheKiller()
// go storage.Cache.CacheKiller()
}
func main() {
@ -59,6 +57,8 @@ func main() {
r.Use(aid.FiberLimiter())
r.Use(aid.FiberCors())
r.Get("/content/api/pages/fortnite-game", handlers.GetContentPages)
account := r.Group("/account/api")
account.Get("/public/account/:accountId", handlers.GetPublicAccount)
account.Get("/public/account/:accountId/externalAuths", handlers.GetPublicAccountExternalAuths)
@ -68,6 +68,7 @@ func main() {
fortnite := r.Group("/fortnite/api")
fortnite.Get("/receipts/v1/account/:accountId/receipts", handlers.GetAccountReceipts)
fortnite.Get("/versioncheck/*", handlers.GetVersionCheck)
fortnite.Get("/calendar/v1/timeline", handlers.GetTimelineCalendar)
matchmaking := fortnite.Group("/matchmaking")
matchmaking.Get("/session/findPlayer/:accountId", handlers.GetSessionFindPlayer)
@ -76,6 +77,9 @@ func main() {
storage.Get("/system", handlers.GetCloudStorageFiles)
storage.Get("/system/config", handlers.GetCloudStorageConfig)
storage.Get("/system/:fileName", handlers.GetCloudStorageFile)
storage.Get("/user/:accountId", handlers.GetUserStorageFiles)
storage.Get("/user/:accountId/:fileName", handlers.GetUserStorageFile)
storage.Put("/user/:accountId/:fileName", handlers.PutUserStorageFile)
game := fortnite.Group("/game/v2")
game.Post("/tryPlayOnPlatform/account/:accountId", handlers.PostTryPlayOnPlatform)

View File

@ -1,17 +1,18 @@
package person
import (
"encoding/json"
"reflect"
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/storage"
"github.com/google/uuid"
)
type Attribute struct {
ID string
ProfileID string
Key string
Value interface{}
ValueJSON string
Type string
}
@ -19,37 +20,27 @@ func NewAttribute(key string, value interface{}) *Attribute {
return &Attribute{
ID: uuid.New().String(),
Key: key,
Value: value,
ValueJSON: aid.JSONStringify(value),
Type: reflect.TypeOf(value).String(),
}
}
func FromDatabaseAttribute(db *storage.DB_PAttribute) *Attribute {
var value interface{}
err := json.Unmarshal([]byte(db.ValueJSON), &value)
if err != nil {
return nil
}
return &Attribute{
ID: db.ID,
ProfileID: db.ProfileID,
Key: db.Key,
Value: value,
ValueJSON: db.ValueJSON,
Type: db.Type,
}
}
func (a *Attribute) ToDatabase(profileId string) *storage.DB_PAttribute {
value, err := json.Marshal(a.Value)
if err != nil {
return nil
}
return &storage.DB_PAttribute{
ID: a.ID,
ProfileID: profileId,
Key: a.Key,
ValueJSON: string(value),
ValueJSON: a.ValueJSON,
Type: a.Type,
}
}
@ -57,3 +48,11 @@ func (a *Attribute) ToDatabase(profileId string) *storage.DB_PAttribute {
func (a *Attribute) Delete() {
storage.Repo.DeleteAttribute(a.ID)
}
func (a *Attribute) Save() {
if a.ProfileID == "" {
aid.Print("error saving attribute", a.Key, "profile id is empty")
return
}
storage.Repo.SaveAttribute(a.ToDatabase(a.ProfileID))
}

View File

@ -1,12 +1,17 @@
package storage
package person
import (
"fmt"
"sync"
"time"
)
var (
cache *PersonsCache
)
type CacheEntry struct {
Entry interface{}
Entry *Person
LastAccessed time.Time
}
@ -20,15 +25,15 @@ func NewPersonsCacheMutex() *PersonsCache {
func (m *PersonsCache) CacheKiller() {
for {
if Cache.Count() == 0 {
if m.Count() == 0 {
continue
}
Cache.Range(func(key, value interface{}) bool {
m.Range(func(key, value interface{}) bool {
cacheEntry := value.(*CacheEntry)
if time.Since(cacheEntry.LastAccessed) >= 30 * time.Minute {
Cache.Delete(key)
m.Delete(key)
}
return true
@ -38,20 +43,21 @@ func (m *PersonsCache) CacheKiller() {
}
}
func (m *PersonsCache) GetPerson(id string) *DB_Person {
func (m *PersonsCache) GetPerson(id string) *Person {
if p, ok := m.Load(id); ok {
fmt.Println("Cache hit", id)
cacheEntry := p.(*CacheEntry)
return cacheEntry.Entry.(*DB_Person)
return cacheEntry.Entry
}
return nil
}
func (m *PersonsCache) GetPersonByDisplay(displayName string) *DB_Person {
var person *DB_Person
m.Range(func(key, value interface{}) bool {
if value.(*CacheEntry).Entry.(*DB_Person).DisplayName == displayName {
person = value.(*CacheEntry).Entry.(*DB_Person)
func (m *PersonsCache) GetPersonByDisplay(displayName string) *Person {
var person *Person
m.RangeEntry(func(key string, value *CacheEntry) bool {
if value.Entry.DisplayName == displayName {
person = value.Entry
return false
}
@ -61,7 +67,7 @@ func (m *PersonsCache) GetPersonByDisplay(displayName string) *DB_Person {
return person
}
func (m *PersonsCache) SavePerson(p *DB_Person) {
func (m *PersonsCache) SavePerson(p *Person) {
m.Store(p.ID, &CacheEntry{
Entry: p,
LastAccessed: time.Now(),
@ -72,9 +78,9 @@ func (m *PersonsCache) DeletePerson(id string) {
m.Delete(id)
}
func (m *PersonsCache) RangePersons(f func(key string, value *DB_Person) bool) {
func (m *PersonsCache) RangeEntry(f func(key string, value *CacheEntry) bool) {
m.Range(func(key, value interface{}) bool {
return f(key.(string), value.(*CacheEntry).Entry.(*DB_Person))
return f(key.(string), value.(*CacheEntry))
})
}

View File

@ -7,22 +7,14 @@ func NewFortnitePerson(displayName string, key string) {
person.DisplayName = displayName
person.AccessKey = key
character := NewItem("AthenaCharacter:CID_001_Athena_Commando_F_Default", 1)
pickaxe := NewItem("AthenaPickaxe:DefaultPickaxe", 1)
glider := NewItem("AthenaGlider:DefaultGlider", 1)
default_dance := NewItem("AthenaDance:EID_DanceMoves", 1)
person.AthenaProfile.Items.AddItem(character)
person.AthenaProfile.Items.AddItem(pickaxe)
person.AthenaProfile.Items.AddItem(glider)
person.AthenaProfile.Items.AddItem(default_dance)
person.AthenaProfile.Items.AddItem(NewItem("AthenaCharacter:CID_001_Athena_Commando_F_Default", 1))
person.AthenaProfile.Items.AddItem(NewItem("AthenaCharacter:CID_032_Athena_Commando_M_Medieval", 1))
person.AthenaProfile.Items.AddItem(NewItem("AthenaCharacter:CID_033_Athena_Commando_F_Medieval", 1))
person.AthenaProfile.Items.AddItem(NewItem("AthenaPickaxe:DefaultPickaxe", 1))
person.AthenaProfile.Items.AddItem(NewItem("AthenaGlider:DefaultGlider", 1))
person.AthenaProfile.Items.AddItem(NewItem("AthenaDance:EID_DanceMoves", 1))
person.CommonCoreProfile.Items.AddItem(NewItem("Currency:MtxPurchased", 0))
person.Loadout.Character = character.ID
person.Loadout.Pickaxe = pickaxe.ID
person.Loadout.Glider = glider.ID
person.Loadout.Dances[0] = default_dance.ID
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("mfa_reward_claimed", true))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("rested_xp_overflow", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("lifetime_wins", 0))
@ -32,7 +24,7 @@ func NewFortnitePerson(displayName string, key string) {
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("daily_rewards", []aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("competitive_identity", aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("season_update", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("season_num", 2))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("season_num", aid.Config.Fortnite.Season))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("permissions", []aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("loadouts", []aid.JSON{}))
@ -51,17 +43,17 @@ func NewFortnitePerson(displayName string, key string) {
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("book_level", 1))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("book_xp", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_character", person.Loadout.Character))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_backpack", person.Loadout.Backpack))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_pickaxe", person.Loadout.Pickaxe))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_glider", person.Loadout.Glider))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_skydivecontrail", person.Loadout.SkyDiveContrail))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_dance", person.Loadout.Dances))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_itemwraps", person.Loadout.ItemWraps))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_loadingscreen", person.Loadout.LoadingScreen))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_musicpack", person.Loadout.MusicPack))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_icon", person.Loadout.BannerIcon))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_color", person.Loadout.BannerColor))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_character", person.AthenaProfile.Items.GetItemByTemplateID("AthenaCharacter:CID_001_Athena_Commando_F_Default").ID))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_backpack", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_pickaxe", person.AthenaProfile.Items.GetItemByTemplateID("AthenaPickaxe:DefaultPickaxe").ID))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_glider", person.AthenaProfile.Items.GetItemByTemplateID("AthenaGlider:DefaultGlider").ID))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_skydivecontrail", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_dance", make([]string, 6)))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_itemwraps", make([]string, 7)))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_loadingscreen", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_musicpack", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_icon", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_color", ""))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("mfa_enabled", true))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("mtx_affiliate", ""))

View File

@ -10,6 +10,7 @@ import (
type Gift struct {
ID string
ProfileID string
TemplateID string
Quantity int
FromID string
@ -39,6 +40,7 @@ func FromDatabaseGift(gift *storage.DB_Gift) *Gift {
return &Gift{
ID: gift.ID,
ProfileID: gift.ProfileID,
TemplateID: gift.TemplateID,
Quantity: gift.Quantity,
FromID: gift.FromID,
@ -101,8 +103,8 @@ func (g *Gift) ToDatabase(profileId string) *storage.DB_Gift {
}
return &storage.DB_Gift{
ProfileID: profileId,
ID: g.ID,
ProfileID: profileId,
TemplateID: g.TemplateID,
Quantity: g.Quantity,
FromID: g.FromID,
@ -113,7 +115,12 @@ func (g *Gift) ToDatabase(profileId string) *storage.DB_Gift {
}
func (g *Gift) Save() {
//storage.Repo.SaveGift(g.ToDatabase())
if g.ProfileID == "" {
aid.Print("error saving gift", g.ID, "no profile id")
return
}
storage.Repo.SaveGift(g.ToDatabase(g.ProfileID))
}
func (g *Gift) Snapshot() GiftSnapshot {

View File

@ -8,6 +8,7 @@ import (
type Item struct {
ID string
ProfileID string
TemplateID string
Quantity int
Favorite bool
@ -39,7 +40,7 @@ func NewItemWithType(templateID string, quantity int, profile string) *Item {
}
}
func FromDatabaseItem(item *storage.DB_Item, profileType *string) *Item {
func FromDatabaseItem(item *storage.DB_Item) *Item {
variants := []*VariantChannel{}
for _, variant := range item.Variants {
@ -53,7 +54,6 @@ func FromDatabaseItem(item *storage.DB_Item, profileType *string) *Item {
Favorite: item.Favorite,
HasSeen: item.HasSeen,
Variants: variants,
ProfileType: *profileType,
}
}
@ -171,7 +171,12 @@ func (i *Item) ToDatabase(profileId string) *storage.DB_Item {
}
func (i *Item) Save() {
//storage.Repo.SaveItem(i.ToDatabase())
if i.ProfileID == "" {
aid.Print("error saving item", i.ID, "no profile id")
return
}
storage.Repo.SaveItem(i.ToDatabase(i.ProfileID))
}
func (i *Item) ToLootDatabase(giftId string) *storage.DB_Loot {

View File

@ -1,6 +1,8 @@
package person
import (
"time"
"github.com/ectrc/snow/storage"
"github.com/google/uuid"
)
@ -13,7 +15,6 @@ type Person struct {
CommonCoreProfile *Profile
CommonPublicProfile *Profile
Profile0 *Profile
Loadout *Loadout
}
type Option struct {
@ -30,69 +31,52 @@ func NewPerson() *Person {
CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"),
Profile0: NewProfile("profile0"),
Loadout: NewLoadout(),
}
}
func Find(personId string) *Person {
person := storage.Repo.GetPerson(personId)
if cache == nil {
cache = NewPersonsCacheMutex()
}
cachedPerson := cache.GetPerson(personId)
if cachedPerson != nil {
return cachedPerson
}
person := storage.Repo.GetPersonFromDB(personId)
if person == nil {
return nil
}
loadout := FromDatabaseLoadout(&person.Loadout)
athenaProfile := NewProfile("athena")
commonCoreProfile := NewProfile("common_core")
commonPublicProfile := NewProfile("common_public")
profile0 := NewProfile("profile0")
for _, profile := range person.Profiles {
if profile.Type == "athena" {
athenaProfile.ID = profile.ID
athenaProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_core" {
commonCoreProfile.ID = profile.ID
commonCoreProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_public" {
commonPublicProfile.ID = profile.ID
commonPublicProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "profile0" {
profile0.ID = profile.ID
profile0 = FromDatabaseProfile(&profile)
}
}
return &Person{
ID: person.ID,
DisplayName: person.DisplayName,
AccessKey: person.AccessKey,
AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile,
Profile0: profile0,
Loadout: loadout,
}
return findHelper(person)
}
func FindByDisplay(displayName string) *Person {
person := storage.Repo.GetPersonByDisplay(displayName)
if cache == nil {
cache = NewPersonsCacheMutex()
}
person := storage.Repo.GetPersonByDisplayFromDB(displayName)
if person == nil {
return nil
}
loadout := FromDatabaseLoadout(&person.Loadout)
cachedPerson := cache.GetPerson(person.ID)
if cachedPerson != nil {
return cachedPerson
}
return findHelper(person)
}
func findHelper(databasePerson *storage.DB_Person) *Person {
athenaProfile := NewProfile("athena")
commonCoreProfile := NewProfile("common_core")
commonPublicProfile := NewProfile("common_public")
profile0 := NewProfile("profile0")
for _, profile := range person.Profiles {
for _, profile := range databasePerson.Profiles {
if profile.Type == "athena" {
athenaProfile.ID = profile.ID
athenaProfile = FromDatabaseProfile(&profile)
@ -114,16 +98,22 @@ func FindByDisplay(displayName string) *Person {
}
}
return &Person{
ID: person.ID,
DisplayName: person.DisplayName,
AccessKey: person.AccessKey,
person := &Person{
ID: databasePerson.ID,
DisplayName: databasePerson.DisplayName,
AccessKey: databasePerson.AccessKey,
AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile,
Profile0: profile0,
Loadout: loadout,
}
cache.Store(person.ID, &CacheEntry{
Entry: person,
LastAccessed: time.Now(),
})
return person
}
func AllFromDatabase() []*Person {
@ -152,7 +142,8 @@ func (p *Person) GetProfileFromType(profileType string) *Profile {
}
func (p *Person) Save() {
storage.Repo.SavePerson(p.ToDatabase())
dbPerson := p.ToDatabase()
storage.Repo.SavePerson(dbPerson)
}
func (p *Person) ToDatabase() *storage.DB_Person {
@ -160,7 +151,6 @@ func (p *Person) ToDatabase() *storage.DB_Person {
ID: p.ID,
DisplayName: p.DisplayName,
Profiles: []storage.DB_Profile{},
Loadout: *p.Loadout.ToDatabase(),
AccessKey: p.AccessKey,
}
@ -178,7 +168,9 @@ func (p *Person) ToDatabase() *storage.DB_Person {
Type: profileType,
Items: []storage.DB_Item{},
Gifts: []storage.DB_Gift{},
Quests: []storage.DB_Quest{},
Attributes: []storage.DB_PAttribute{},
Revision: profile.Revision,
}
profile.Items.RangeItems(func(id string, item *Item) bool {
@ -225,7 +217,6 @@ func (p *Person) Snapshot() *PersonSnapshot {
ID: p.ID,
DisplayName: p.DisplayName,
AthenaProfile: *p.AthenaProfile.Snapshot(),
CommonCoreProfile:* p.CommonCoreProfile.Snapshot(),
Loadout: *p.Loadout,
CommonCoreProfile: *p.CommonCoreProfile.Snapshot(),
}
}

View File

@ -22,13 +22,14 @@ type Profile struct {
}
func NewProfile(profile string) *Profile {
id := uuid.New().String()
return &Profile{
ID: uuid.New().String(),
ID: id,
PersonID: "",
Items: NewItemMutex(profile),
Gifts: NewGiftMutex(),
Quests: NewQuestMutex(),
Attributes: NewAttributeMutex(),
Items: NewItemMutex(&storage.DB_Profile{ID: id, Type: profile}),
Gifts: NewGiftMutex(&storage.DB_Profile{ID: id, Type: profile}),
Quests: NewQuestMutex(&storage.DB_Profile{ID: id, Type: profile}),
Attributes: NewAttributeMutex(&storage.DB_Profile{ID: id, Type: profile}),
Type: profile,
Revision: 0,
Changes: []interface{}{},
@ -36,13 +37,13 @@ func NewProfile(profile string) *Profile {
}
func FromDatabaseProfile(profile *storage.DB_Profile) *Profile {
items := NewItemMutex(profile.Type)
gifts := NewGiftMutex()
quests := NewQuestMutex()
attributes := NewAttributeMutex()
items := NewItemMutex(profile)
gifts := NewGiftMutex(profile)
quests := NewQuestMutex(profile)
attributes := NewAttributeMutex(profile)
for _, item := range profile.Items {
items.AddItem(FromDatabaseItem(&item, &profile.Type))
items.AddItem(FromDatabaseItem(&item))
}
for _, gift := range profile.Gifts {
@ -50,7 +51,7 @@ func FromDatabaseProfile(profile *storage.DB_Profile) *Profile {
}
for _, quest := range profile.Quests {
quests.AddQuest(FromDatabaseQuest(&quest, &profile.Type))
quests.AddQuest(FromDatabaseQuest(&quest))
}
for _, attribute := range profile.Attributes {
@ -72,6 +73,7 @@ func FromDatabaseProfile(profile *storage.DB_Profile) *Profile {
Attributes: attributes,
Type: profile.Type,
Revision: profile.Revision,
Changes: []interface{}{},
}
}
@ -95,7 +97,7 @@ func (p *Profile) GenerateFortniteProfileEntry() aid.JSON {
})
p.Attributes.RangeAttributes(func(id string, attribute *Attribute) bool {
attributes[attribute.Key] = attribute.Value
attributes[attribute.Key] = aid.JSONParse(attribute.ValueJSON)
return true
})
@ -114,7 +116,7 @@ func (p *Profile) GenerateFortniteProfileEntry() aid.JSON {
}
func (p *Profile) Save() {
//storage.Repo.SaveProfile(p.ToDatabase())
// storage.Repo.SaveProfile(p.ToDatabase())
}
func (p *Profile) Snapshot() *ProfileSnapshot {
@ -154,8 +156,8 @@ func (p *Profile) Snapshot() *ProfileSnapshot {
}
}
func (p *Profile) Diff(snapshot *ProfileSnapshot) []diff.Change {
changes, err := diff.Diff(snapshot, p.Snapshot())
func (p *Profile) Diff(b *ProfileSnapshot) []diff.Change {
changes, err := diff.Diff(*b, *p.Snapshot())
if err != nil {
fmt.Printf("error diffing profile: %v\n", err)
return nil
@ -200,7 +202,7 @@ func (p *Profile) Diff(snapshot *ProfileSnapshot) []diff.Change {
p.CreateStatModifiedChange(p.Attributes.GetAttribute(change.Path[1]))
}
if change.Type == "update" && change.Path[2] == "Value" {
if change.Type == "update" && change.Path[2] == "ValueJSON" {
p.CreateStatModifiedChange(p.Attributes.GetAttribute(change.Path[1]))
}
}
@ -209,6 +211,11 @@ func (p *Profile) Diff(snapshot *ProfileSnapshot) []diff.Change {
return changes
}
func (p *Profile) CreateAttribute(key string, value interface{}) *Attribute {
p.Attributes.AddAttribute(NewAttribute(key, value))
return p.Attributes.GetAttribute(key)
}
func (p *Profile) CreateStatModifiedChange(attribute *Attribute) {
if attribute == nil {
fmt.Println("error getting attribute from profile", attribute.ID)
@ -218,7 +225,7 @@ func (p *Profile) CreateStatModifiedChange(attribute *Attribute) {
p.Changes = append(p.Changes, StatModified{
ChangeType: "statModified",
Name: attribute.Key,
Value: attribute.Value,
Value: aid.JSONParse(attribute.ValueJSON),
})
}
@ -302,78 +309,12 @@ func (p *Profile) CreateItemAttributeChangedChange(item *Item, attribute string)
}
func (p *Profile) CreateFullProfileUpdateChange() {
p.Changes = append(p.Changes, FullProfileUpdate{
p.Changes = []interface{}{FullProfileUpdate{
ChangeType: "fullProfileUpdate",
Profile: p.GenerateFortniteProfileEntry(),
})
}}
}
type Loadout struct {
ID string
Character string
Backpack string
Pickaxe string
Glider string
Dances []string
ItemWraps []string
LoadingScreen string
SkyDiveContrail string
MusicPack string
BannerIcon string
BannerColor string
}
func NewLoadout() *Loadout {
return &Loadout{
ID: uuid.New().String(),
Character: "",
Backpack: "",
Pickaxe: "",
Glider: "",
Dances: make([]string, 6),
ItemWraps: make([]string, 7),
LoadingScreen: "",
SkyDiveContrail: "",
MusicPack: "",
BannerIcon: "",
BannerColor: "",
}
}
func FromDatabaseLoadout(l *storage.DB_Loadout) *Loadout {
return &Loadout{
ID: l.ID,
Character: l.Character,
Backpack: l.Backpack,
Pickaxe: l.Pickaxe,
Glider: l.Glider,
Dances: l.Dances,
ItemWraps: l.ItemWraps,
LoadingScreen: l.LoadingScreen,
SkyDiveContrail: l.SkyDiveContrail,
MusicPack: l.MusicPack,
BannerIcon: l.BannerIcon,
BannerColor: l.BannerColor,
}
}
func (l *Loadout) ToDatabase() *storage.DB_Loadout {
return &storage.DB_Loadout{
ID: l.ID,
Character: l.Character,
Backpack: l.Backpack,
Pickaxe: l.Pickaxe,
Glider: l.Glider,
Dances: l.Dances,
ItemWraps: l.ItemWraps,
LoadingScreen: l.LoadingScreen,
SkyDiveContrail: l.SkyDiveContrail,
MusicPack: l.MusicPack,
BannerIcon: l.BannerIcon,
BannerColor: l.BannerColor,
}
}
func (l *Loadout) Save() {
//storage.Repo.SaveLoadout(l.ToDatabase())
func (p *Profile) ClearProfileChanges() {
p.Changes = []interface{}{}
}

View File

@ -8,6 +8,7 @@ import (
type Quest struct {
ID string
ProfileID string
TemplateID string
State string
Objectives []string
@ -38,9 +39,10 @@ func NewDailyQuest(templateID string) *Quest {
}
}
func FromDatabaseQuest(quest *storage.DB_Quest, profileType *string) *Quest {
func FromDatabaseQuest(quest *storage.DB_Quest) *Quest {
return &Quest{
ID: quest.ID,
ProfileID: quest.ProfileID,
TemplateID: quest.TemplateID,
State: quest.State,
Objectives: quest.Objectives,
@ -126,8 +128,8 @@ func (q *Quest) RemoveObjective(objective string) {
func (q *Quest) ToDatabase(profileId string) *storage.DB_Quest {
return &storage.DB_Quest{
ProfileID: profileId,
ID: q.ID,
ProfileID: profileId,
TemplateID: q.TemplateID,
State: q.State,
Objectives: q.Objectives,
@ -138,5 +140,5 @@ func (q *Quest) ToDatabase(profileId string) *storage.DB_Quest {
}
func (q *Quest) Save() {
//storage.Repo.SaveQuest(q.ToDatabase())
storage.Repo.SaveQuest(q.ToDatabase(q.ProfileID))
}

View File

@ -5,7 +5,6 @@ type PersonSnapshot struct {
DisplayName string
AthenaProfile ProfileSnapshot
CommonCoreProfile ProfileSnapshot
Loadout Loadout
}
type ProfileSnapshot struct {

View File

@ -2,23 +2,28 @@ package person
import (
"sync"
"github.com/ectrc/snow/storage"
)
type ItemMutex struct {
sync.Map
ProfileType string
ProfileID string
}
func NewItemMutex(profile string) *ItemMutex {
func NewItemMutex(profile *storage.DB_Profile) *ItemMutex {
return &ItemMutex{
ProfileType: profile,
ProfileType: profile.Type,
ProfileID: profile.ID,
}
}
func (m *ItemMutex) AddItem(item *Item) {
item.ProfileType = m.ProfileType
item.ProfileID = m.ProfileID
m.Store(item.ID, item)
// storage.Repo.SaveItem(item)
storage.Repo.SaveItem(item.ToDatabase(m.ProfileID))
}
func (m *ItemMutex) DeleteItem(id string) {
@ -29,7 +34,7 @@ func (m *ItemMutex) DeleteItem(id string) {
item.Delete()
m.Delete(id)
// storage.Repo.DeleteItem(id)
storage.Repo.DeleteItem(id)
}
func (m *ItemMutex) GetItem(id string) *Item {
@ -74,20 +79,25 @@ func (m *ItemMutex) Count() int {
type GiftMutex struct {
sync.Map
ProfileType string
ProfileID string
}
func NewGiftMutex() *GiftMutex {
return &GiftMutex{}
func NewGiftMutex(profile *storage.DB_Profile) *GiftMutex {
return &GiftMutex{
ProfileType: profile.Type,
ProfileID: profile.ID,
}
}
func (m *GiftMutex) AddGift(gift *Gift) {
gift.ProfileID = m.ProfileID
m.Store(gift.ID, gift)
// storage.Repo.SaveGift(gift)
storage.Repo.SaveGift(gift.ToDatabase(m.ProfileID))
}
func (m *GiftMutex) DeleteGift(id string) {
m.Delete(id)
// storage.Repo.DeleteGift(id)
storage.Repo.DeleteGift(id)
}
func (m *GiftMutex) GetGift(id string) *Gift {
@ -116,20 +126,26 @@ func (m *GiftMutex) Count() int {
type QuestMutex struct {
sync.Map
ProfileType string
ProfileID string
}
func NewQuestMutex() *QuestMutex {
return &QuestMutex{}
func NewQuestMutex(profile *storage.DB_Profile) *QuestMutex {
return &QuestMutex{
ProfileType: profile.Type,
ProfileID: profile.ID,
}
}
func (m *QuestMutex) AddQuest(quest *Quest) {
quest.ProfileID = m.ProfileID
m.Store(quest.ID, quest)
// storage.Repo.SaveQuest(quest)
storage.Repo.SaveQuest(quest.ToDatabase(m.ProfileID))
}
func (m *QuestMutex) DeleteQuest(id string) {
m.Delete(id)
// storage.Repo.DeleteQuest(id)
storage.Repo.DeleteQuest(id)
}
func (m *QuestMutex) GetQuest(id string) *Quest {
@ -158,20 +174,25 @@ func (m *QuestMutex) Count() int {
type AttributeMutex struct {
sync.Map
ProfileType string
ProfileID string
}
func NewAttributeMutex() *AttributeMutex {
return &AttributeMutex{}
func NewAttributeMutex(profile *storage.DB_Profile) *AttributeMutex {
return &AttributeMutex{
ProfileID: profile.ID,
}
}
func (m *AttributeMutex) AddAttribute(attribute *Attribute) {
attribute.ProfileID = m.ProfileID
m.Store(attribute.ID, attribute)
// storage.Repo.SaveAttribute(key, value)
storage.Repo.SaveAttribute(attribute.ToDatabase(m.ProfileID))
}
func (m *AttributeMutex) DeleteAttribute(id string) {
m.Delete(id)
// storage.Repo.DeleteAttribute(key)
storage.Repo.DeleteAttribute(id)
}
func (m *AttributeMutex) GetAttribute(id string) *Attribute {

View File

@ -88,22 +88,54 @@ func (s *PostgresStorage) SavePerson(person *DB_Person) {
s.Postgres.Save(person)
}
func (s *PostgresStorage) DeletePerson(personId string) {
s.Postgres.Delete(&DB_Person{}, "id = ?", personId)
}
func (s *PostgresStorage) SaveProfile(profile *DB_Profile) {
s.Postgres.Save(profile)
}
func (s *PostgresStorage) DeleteProfile(profileId string) {
s.Postgres.Delete(&DB_Profile{}, "id = ?", profileId)
}
func (s *PostgresStorage) SaveItem(item *DB_Item) {
s.Postgres.Save(item)
}
func (s *PostgresStorage) DeleteItem(itemId string) {
s.Postgres.Delete(&DB_Item{}, "id = ?", itemId)
}
func (s *PostgresStorage) SaveVariant(variant *DB_VariantChannel) {
s.Postgres.Save(variant)
}
func (s *PostgresStorage) DeleteVariant(variantId string) {
s.Postgres.Delete(&DB_VariantChannel{}, "id = ?", variantId)
}
func (s *PostgresStorage) SaveQuest(quest *DB_Quest) {
s.Postgres.Save(quest)
}
func (s *PostgresStorage) DeleteQuest(questId string) {
s.Postgres.Delete(&DB_Quest{}, "id = ?", questId)
}
func (s *PostgresStorage) SaveLoot(loot *DB_Loot) {
s.Postgres.Save(loot)
}
func (s *PostgresStorage) DeleteLoot(lootId string) {
s.Postgres.Delete(&DB_Loot{}, "id = ?", lootId)
}
func (s *PostgresStorage) SaveGift(gift *DB_Gift) {
s.Postgres.Save(gift)
}
func (s *PostgresStorage) DeleteGift(giftId string) {
s.Postgres.Delete(&DB_Gift{}, "id = ?", giftId)
}
@ -111,3 +143,8 @@ func (s *PostgresStorage) DeleteGift(giftId string) {
func (s *PostgresStorage) DeleteAttribute(attributeId string) {
s.Postgres.Delete(&DB_PAttribute{}, "id = ?", attributeId)
}
func (s *PostgresStorage) SaveAttribute(attribute *DB_PAttribute) {
aid.Print("saving attribute", attribute.Key, attribute.ValueJSON)
s.Postgres.Save(attribute)
}

View File

@ -2,7 +2,6 @@ package storage
var (
Repo *Repository
Cache *PersonsCache
)
type Storage interface {
@ -13,11 +12,25 @@ type Storage interface {
GetAllPersons() []*DB_Person
SavePerson(person *DB_Person)
SaveProfile(profile *DB_Profile)
DeleteProfile(profileId string)
SaveItem(item *DB_Item)
DeleteItem(itemId string)
SaveVariant(variant *DB_VariantChannel)
DeleteVariant(variantId string)
SaveQuest(quest *DB_Quest)
DeleteQuest(questId string)
SaveLoot(loot *DB_Loot)
DeleteLoot(lootId string)
SaveGift(gift *DB_Gift)
DeleteGift(giftId string)
SaveAttribute(attribute *DB_PAttribute)
DeleteAttribute(attributeId string)
}
@ -31,30 +44,18 @@ func NewStorage(s Storage) *Repository {
}
}
func (r *Repository) GetPerson(personId string) *DB_Person {
cachePerson := Cache.GetPerson(personId)
if cachePerson != nil {
return cachePerson
}
func (r *Repository) GetPersonFromDB(personId string) *DB_Person {
storagePerson := r.Storage.GetPerson(personId)
if storagePerson != nil {
Cache.SavePerson(storagePerson)
return storagePerson
}
return nil
}
func (r *Repository) GetPersonByDisplay(displayName string) *DB_Person {
cachePerson := Cache.GetPersonByDisplay(displayName)
if cachePerson != nil {
return cachePerson
}
func (r *Repository) GetPersonByDisplayFromDB(displayName string) *DB_Person {
storagePerson := r.Storage.GetPersonByDisplay(displayName)
if storagePerson != nil {
Cache.SavePerson(storagePerson)
return storagePerson
}
@ -66,30 +67,61 @@ func (r *Repository) GetAllPersons() []*DB_Person {
}
func (r *Repository) SavePerson(person *DB_Person) {
Cache.SavePerson(person)
r.Storage.SavePerson(person)
}
func (r *Repository) SaveProfile(profile *DB_Profile) {
r.Storage.SaveProfile(profile)
}
func (r *Repository) DeleteProfile(profileId string) {
r.Storage.DeleteProfile(profileId)
}
func (r *Repository) SaveItem(item *DB_Item) {
r.Storage.SaveItem(item)
}
func (r *Repository) DeleteItem(itemId string) {
r.Storage.DeleteItem(itemId)
}
func (r *Repository) SaveVariant(variant *DB_VariantChannel) {
r.Storage.SaveVariant(variant)
}
func (r *Repository) DeleteVariant(variantId string) {
r.Storage.DeleteVariant(variantId)
}
func (r *Repository) SaveQuest(quest *DB_Quest) {
r.Storage.SaveQuest(quest)
}
func (r *Repository) DeleteQuest(questId string) {
r.Storage.DeleteQuest(questId)
}
func (r *Repository) SaveLoot(loot *DB_Loot) {
r.Storage.SaveLoot(loot)
}
func (r *Repository) DeleteLoot(lootId string) {
r.Storage.DeleteLoot(lootId)
}
func (r *Repository) SaveGift(gift *DB_Gift) {
r.Storage.SaveGift(gift)
}
func (r *Repository) DeleteGift(giftId string) {
r.Storage.DeleteGift(giftId)
}
func (r *Repository) SaveAttribute(attribute *DB_PAttribute) {
r.Storage.SaveAttribute(attribute)
}
func (r *Repository) DeleteAttribute(attributeId string) {
r.Storage.DeleteAttribute(attributeId)
}

View File

@ -11,33 +11,12 @@ type DB_Person struct {
DisplayName string
AccessKey string
Profiles []DB_Profile `gorm:"foreignkey:PersonID"`
Loadout DB_Loadout `gorm:"foreignkey:PersonID"`
}
func (DB_Person) TableName() string {
return "Persons"
}
type DB_Loadout struct {
ID string `gorm:"primary_key"`
PersonID string
Character string
Backpack string
Pickaxe string
Glider string
Dances pq.StringArray `gorm:"type:text[]"`
ItemWraps pq.StringArray `gorm:"type:text[]"`
LoadingScreen string
SkyDiveContrail string
MusicPack string
BannerIcon string
BannerColor string
}
func (DB_Loadout) TableName() string {
return "Loadouts"
}
type DB_Profile struct {
ID string `gorm:"primary_key"`
PersonID string