From 1ebe963b2dffc0293ab3a160108aff5905f927e0 Mon Sep 17 00:00:00 2001 From: Eccentric Date: Wed, 31 Jan 2024 00:16:57 +0000 Subject: [PATCH] Friends rereimplemented + add mutex to stop concurrent writes to webscoket --- aid/syncer.go | 12 +++++++ fortnite/person.go | 55 ++++++++++++++++-------------- fortnite/shop.go | 10 +++--- handlers/friends.go | 6 ++-- handlers/socket.go | 4 +-- main.go | 7 +++- person/person.go | 4 +-- person/relationships.go | 24 ++++++++++--- socket/jabber.go | 75 ++++++++++++++++++++++++++++------------- socket/socket.go | 10 ++++++ 10 files changed, 142 insertions(+), 65 deletions(-) diff --git a/aid/syncer.go b/aid/syncer.go index f5cae4f..913fc1a 100644 --- a/aid/syncer.go +++ b/aid/syncer.go @@ -43,4 +43,16 @@ func (s *GenericSyncMap[T]) Range(f func(key string, value *T) bool) { func (s *GenericSyncMap[T]) Has(key string) bool { _, ok := s.Get(key) return ok +} + +// change the key of a value in the map + +func (s *GenericSyncMap[T]) ChangeKey(oldKey, newKey string) { + v, ok := s.Get(oldKey) + if !ok { + return + } + + s.Set(newKey, v) + s.Delete(oldKey) } \ No newline at end of file diff --git a/fortnite/person.go b/fortnite/person.go index f2822b3..414ff61 100644 --- a/fortnite/person.go +++ b/fortnite/person.go @@ -7,6 +7,7 @@ import ( "github.com/ectrc/snow/aid" p "github.com/ectrc/snow/person" "github.com/ectrc/snow/storage" + "github.com/google/uuid" ) var ( @@ -24,7 +25,35 @@ var ( ) func NewFortnitePerson(displayName string, everything bool) *p.Person { - person := p.NewPerson() + return NewFortnitePersonWithId(uuid.New().String(), displayName, everything) +} + +func GiveEverything(person *p.Person) { + items := make([]storage.DB_Item, 0) + + for _, item := range Cosmetics.Items { + if strings.Contains(strings.ToLower(item.ID), "random") { + continue + } + + has := person.AthenaProfile.Items.GetItemByTemplateID(item.ID) + if has != nil { + continue + } + + item := p.NewItem(item.Type.BackendValue + ":" + item.ID, 1) + item.HasSeen = true + person.AthenaProfile.Items.AddItem(item) + + items = append(items, *item.ToDatabase(person.AthenaProfile.ID)) + } + + storage.Repo.BulkCreateItems(&items) + person.Save() +} + +func NewFortnitePersonWithId(id string, displayName string, everything bool) *p.Person { + person := p.NewPersonWithCustomID(id) person.DisplayName = displayName for _, item := range defaultAthenaItems { @@ -122,28 +151,4 @@ func NewFortnitePerson(displayName string, everything bool) *p.Person { person.Save() return person -} - -func GiveEverything(person *p.Person) { - items := make([]storage.DB_Item, 0) - - for _, item := range Cosmetics.Items { - if strings.Contains(strings.ToLower(item.ID), "random") { - continue - } - - has := person.AthenaProfile.Items.GetItemByTemplateID(item.ID) - if has != nil { - continue - } - - item := p.NewItem(item.Type.BackendValue + ":" + item.ID, 1) - item.HasSeen = true - person.AthenaProfile.Items.AddItem(item) - - items = append(items, *item.ToDatabase(person.AthenaProfile.ID)) - } - - storage.Repo.BulkCreateItems(&items) - person.Save() } \ No newline at end of file diff --git a/fortnite/shop.go b/fortnite/shop.go index b74e16d..a9aced5 100644 --- a/fortnite/shop.go +++ b/fortnite/shop.go @@ -10,7 +10,7 @@ import ( var ( Rarities = map[string]map[string]int{ - "EFortRarity::Legendary": map[string]int{ + "EFortRarity::Legendary": { "AthenaCharacter": 2000, "AthenaBackpack": 1500, "AthenaPickaxe": 1500, @@ -18,7 +18,7 @@ var ( "AthenaDance": 500, "AthenaItemWrap": 800, }, - "EFortRarity::Epic": map[string]int{ + "EFortRarity::Epic": { "AthenaCharacter": 1500, "AthenaBackpack": 1200, "AthenaPickaxe": 1200, @@ -26,7 +26,7 @@ var ( "AthenaDance": 800, "AthenaItemWrap": 800, }, - "EFortRarity::Rare": map[string]int{ + "EFortRarity::Rare": { "AthenaCharacter": 1200, "AthenaBackpack": 800, "AthenaPickaxe": 800, @@ -34,7 +34,7 @@ var ( "AthenaDance": 500, "AthenaItemWrap": 600, }, - "EFortRarity::Uncommon": map[string]int{ + "EFortRarity::Uncommon": { "AthenaCharacter": 800, "AthenaBackpack": 200, "AthenaPickaxe": 500, @@ -42,7 +42,7 @@ var ( "AthenaDance": 200, "AthenaItemWrap": 300, }, - "EFortRarity::Common": map[string]int{ + "EFortRarity::Common": { "AthenaCharacter": 500, "AthenaBackpack": 200, "AthenaPickaxe": 500, diff --git a/handlers/friends.go b/handlers/friends.go index 82def37..e439f1d 100644 --- a/handlers/friends.go +++ b/handlers/friends.go @@ -34,22 +34,24 @@ func PostCreateFriend(c *fiber.Ctx) error { return c.Status(400).JSON(aid.ErrorBadRequest(err.Error())) } - from, found := socket.GetJabberSocketByPersonID(relationship.From.ID) + from, found := socket.JabberSockets.Get(relationship.From.ID) if found { from.JabberSendMessageToPerson(aid.JSON{ "type": "com.epicgames.friends.core.apiobjects.Friend", "timestamp": time.Now().Format(time.RFC3339), "payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeFromPerson), }) + from.JabberNotifyFriends() } - towards, found := socket.GetJabberSocketByPersonID(relationship.Towards.ID) + towards, found := socket.JabberSockets.Get(relationship.Towards.ID) if found { towards.JabberSendMessageToPerson(aid.JSON{ "type": "com.epicgames.friends.core.apiobjects.Friend", "timestamp": time.Now().Format(time.RFC3339), "payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeTowardsPerson), }) + towards.JabberNotifyFriends() } return c.SendStatus(204) diff --git a/handlers/socket.go b/handlers/socket.go index 8efda12..8ff3330 100644 --- a/handlers/socket.go +++ b/handlers/socket.go @@ -44,9 +44,9 @@ func WebsocketConnection(c *websocket.Conn) { } func GetConnectedSockets(c *fiber.Ctx) error { - jabber := []socket.Socket[socket.JabberData]{} + jabber := map[string]socket.Socket[socket.JabberData]{} socket.JabberSockets.Range(func(key string, value *socket.Socket[socket.JabberData]) bool { - jabber = append(jabber, *value) + jabber[key] = *value return true }) diff --git a/main.go b/main.go index 05bb62c..e8b087a 100644 --- a/main.go +++ b/main.go @@ -46,9 +46,13 @@ func init() { fortnite.GeneratePlaylistImages() if found := person.FindByDisplay("god"); found == nil { - god := fortnite.NewFortnitePerson("god", true) + god := fortnite.NewFortnitePersonWithId("god", "god", true) god.AddPermission("all") + + angel := fortnite.NewFortnitePersonWithId("angel", "angel", true) + angel.AddPermission("all") } + } func main() { r := fiber.New(fiber.Config{ @@ -57,6 +61,7 @@ func main() { JSONDecoder: json.Unmarshal, }) + r.Use(aid.FiberLogger()) r.Use(aid.FiberLimiter()) r.Use(aid.FiberCors()) diff --git a/person/person.go b/person/person.go index b4e4534..10b2591 100644 --- a/person/person.go +++ b/person/person.go @@ -84,7 +84,7 @@ func FindShallow(personId string) *Person { return nil } - return findHelper(person, true, true) + return findHelper(person, true, false) } func FindByDisplay(displayName string) *Person { @@ -120,7 +120,7 @@ func FindByDisplayShallow(displayName string) *Person { return nil } - return findHelper(person, true, true) + return findHelper(person, true, false) } func FindByDiscord(discordId string) *Person { diff --git a/person/relationships.go b/person/relationships.go index 84fb3d4..2d2e171 100644 --- a/person/relationships.go +++ b/person/relationships.go @@ -39,13 +39,15 @@ func (r *Relationship) GenerateFortniteFriendEntry(t RelationshipGenerateType) a switch t { case GenerateTypeFromPerson: - result["direction"] = "OUTBOUND" + result["direction"] = RelationshipOutboundDirection result["accountId"] = r.Towards.ID case GenerateTypeTowardsPerson: - result["direction"] = "INBOUND" + result["direction"] = RelationshipInboundDirection result["accountId"] = r.From.ID } + aid.PrintJSON(result) + return result } @@ -65,7 +67,7 @@ func (p *Person) LoadRelationships() { incoming := storage.Repo.Storage.GetIncomingRelationships(p.ID) for _, entry := range incoming { relationship := &Relationship{ - From: Find(entry.FromPersonID), + From: FindShallow(entry.FromPersonID), Towards: p, Status: entry.Status, Direction: RelationshipInboundDirection, @@ -73,6 +75,18 @@ func (p *Person) LoadRelationships() { p.Relationships.Set(entry.FromPersonID, relationship) } + + outgoing := storage.Repo.Storage.GetOutgoingRelationships(p.ID) + for _, entry := range outgoing { + relationship := &Relationship{ + From: p, + Towards: FindShallow(entry.TowardsPersonID), + Status: entry.Status, + Direction: RelationshipOutboundDirection, + } + + p.Relationships.Set(entry.FromPersonID, relationship) + } } func (p *Person) CreateRelationship(personId string) (*Relationship, error) { @@ -95,7 +109,7 @@ func (p *Person) CreateRelationship(personId string) (*Relationship, error) { func (p *Person) createOutboundRelationship(towards string) (*Relationship, error) { relationship := &Relationship{ From: p, - Towards: Find(towards), + Towards: FindShallow(towards), Status: "PENDING", Direction: RelationshipOutboundDirection, } @@ -104,7 +118,7 @@ func (p *Person) createOutboundRelationship(towards string) (*Relationship, erro func (p *Person) createAcceptInboundRelationship(towards string) (*Relationship, error) { relationship := &Relationship{ - From: Find(towards), + From: FindShallow(towards), Towards: p, Status: "ACCEPTED", Direction: RelationshipInboundDirection, diff --git a/socket/jabber.go b/socket/jabber.go index c8f1759..0f7fb6e 100644 --- a/socket/jabber.go +++ b/socket/jabber.go @@ -12,11 +12,14 @@ import ( type JabberData struct { JabberID string + LastPresence string } var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error { "open": jabberOpenHandler, "iq": jabberIqRootHandler, + "presence": jabberPresenceHandler, + "message": jabberMessageHandler, } func HandleNewJabberSocket(identifier string) { @@ -24,7 +27,7 @@ func HandleNewJabberSocket(identifier string) { if !ok { return } - defer JabberSockets.Delete(identifier) + defer JabberSockets.Delete(socket.ID) for { _, message, failed := socket.Connection.ReadMessage() @@ -49,8 +52,8 @@ func HandleNewJabberSocket(identifier string) { } func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error { - socket.Connection.WriteMessage(websocket.TextMessage, []byte(``)) - socket.Connection.WriteMessage(websocket.TextMessage, []byte(``)) + socket.Write([]byte(``)) + socket.Write([]byte(``)) return nil } @@ -81,32 +84,30 @@ func jabberIqSetHandler(socket *Socket[JabberData], parsed *etree.Document) erro return fmt.Errorf("person not found") } - socket.Data.JabberID = snowId + "@prod.ol.epicgames.com/" + parsed.FindElement("/iq/query/resource").Text() + JabberSockets.ChangeKey(socket.ID, person.ID) + socket.ID = person.ID socket.Person = person + socket.Data.JabberID = snowId + "@prod.ol.epicgames.com/" + parsed.FindElement("/iq/query/resource").Text() - socket.Connection.WriteMessage(websocket.TextMessage, []byte(``)) + socket.Write([]byte(``)) return nil } - func jabberIqGetHandler(socket *Socket[JabberData], parsed *etree.Document) error { - socket.Connection.WriteMessage(websocket.TextMessage, []byte(``)) + socket.Write([]byte(``)) + socket.JabberNotifyFriends() return nil } -func GetJabberSocketByPersonID(id string) (*Socket[JabberData], bool) { - var found *Socket[JabberData] +func jabberPresenceHandler(socket *Socket[JabberData], parsed *etree.Document) error { + socket.Data.LastPresence = parsed.FindElement("/presence/status").Text() + socket.JabberNotifyFriends() + return nil +} - JabberSockets.Range(func(key string, socket *Socket[JabberData]) bool { - if socket.Person.ID == id { - found = socket - return false - } +func jabberMessageHandler(socket *Socket[JabberData], parsed *etree.Document) error { - return true - }) - - return found, found != nil + return nil } func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) { @@ -120,11 +121,39 @@ func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) { return } - aid.Print(` - `+ string(data.ToBytes()) +` - `) - - s.Connection.WriteMessage(1, []byte(` + s.Write([]byte(` `+ string(data.ToBytes()) +` `)) +} + +func (s *Socket[T]) JabberNotifyFriends() { + if reflect.TypeOf(s.Data) != reflect.TypeOf(&JabberData{}) { + return + } + + jabberSocket, ok := JabberSockets.Get(s.ID) + if !ok { + aid.Print("jabber socket not found even though it should be") + return + } + + s.Person.Relationships.Range(func(key string, value *person.Relationship) bool { + friendSocket, found := JabberSockets.Get(value.From.ID) + if value.Direction == person.RelationshipOutboundDirection { + friendSocket, found = JabberSockets.Get(value.Towards.ID) + } + if !found { + return true + } + + friendSocket.Write([]byte(` + `+ jabberSocket.Data.LastPresence +` + `)) + + jabberSocket.Write([]byte(` + `+ friendSocket.Data.LastPresence +` + `)) + + return true + }) } \ No newline at end of file diff --git a/socket/socket.go b/socket/socket.go index 989149f..99e6628 100644 --- a/socket/socket.go +++ b/socket/socket.go @@ -1,6 +1,8 @@ package socket import ( + "sync" + "github.com/ectrc/snow/aid" "github.com/ectrc/snow/person" "github.com/gofiber/contrib/websocket" @@ -17,6 +19,14 @@ type Socket[T JabberData | MatchmakerData] struct { Connection *websocket.Conn Data *T Person *person.Person + mutex sync.Mutex +} + +func (s *Socket[T]) Write(payload []byte) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.Connection.WriteMessage(websocket.TextMessage, payload) } func newSocket[T JabberData | MatchmakerData](conn *websocket.Conn, data ...T) *Socket[T] {