Friends rereimplemented + add mutex to stop concurrent writes to webscoket

This commit is contained in:
Eccentric 2024-01-31 00:16:57 +00:00
parent 9275e8fcd4
commit 1ebe963b2d
10 changed files with 142 additions and 65 deletions

View File

@ -44,3 +44,15 @@ func (s *GenericSyncMap[T]) Has(key string) bool {
_, ok := s.Get(key) _, ok := s.Get(key)
return ok 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)
}

View File

@ -7,6 +7,7 @@ import (
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person" p "github.com/ectrc/snow/person"
"github.com/ectrc/snow/storage" "github.com/ectrc/snow/storage"
"github.com/google/uuid"
) )
var ( var (
@ -24,7 +25,35 @@ var (
) )
func NewFortnitePerson(displayName string, everything bool) *p.Person { 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 person.DisplayName = displayName
for _, item := range defaultAthenaItems { for _, item := range defaultAthenaItems {
@ -123,27 +152,3 @@ func NewFortnitePerson(displayName string, everything bool) *p.Person {
return person 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()
}

View File

@ -10,7 +10,7 @@ import (
var ( var (
Rarities = map[string]map[string]int{ Rarities = map[string]map[string]int{
"EFortRarity::Legendary": map[string]int{ "EFortRarity::Legendary": {
"AthenaCharacter": 2000, "AthenaCharacter": 2000,
"AthenaBackpack": 1500, "AthenaBackpack": 1500,
"AthenaPickaxe": 1500, "AthenaPickaxe": 1500,
@ -18,7 +18,7 @@ var (
"AthenaDance": 500, "AthenaDance": 500,
"AthenaItemWrap": 800, "AthenaItemWrap": 800,
}, },
"EFortRarity::Epic": map[string]int{ "EFortRarity::Epic": {
"AthenaCharacter": 1500, "AthenaCharacter": 1500,
"AthenaBackpack": 1200, "AthenaBackpack": 1200,
"AthenaPickaxe": 1200, "AthenaPickaxe": 1200,
@ -26,7 +26,7 @@ var (
"AthenaDance": 800, "AthenaDance": 800,
"AthenaItemWrap": 800, "AthenaItemWrap": 800,
}, },
"EFortRarity::Rare": map[string]int{ "EFortRarity::Rare": {
"AthenaCharacter": 1200, "AthenaCharacter": 1200,
"AthenaBackpack": 800, "AthenaBackpack": 800,
"AthenaPickaxe": 800, "AthenaPickaxe": 800,
@ -34,7 +34,7 @@ var (
"AthenaDance": 500, "AthenaDance": 500,
"AthenaItemWrap": 600, "AthenaItemWrap": 600,
}, },
"EFortRarity::Uncommon": map[string]int{ "EFortRarity::Uncommon": {
"AthenaCharacter": 800, "AthenaCharacter": 800,
"AthenaBackpack": 200, "AthenaBackpack": 200,
"AthenaPickaxe": 500, "AthenaPickaxe": 500,
@ -42,7 +42,7 @@ var (
"AthenaDance": 200, "AthenaDance": 200,
"AthenaItemWrap": 300, "AthenaItemWrap": 300,
}, },
"EFortRarity::Common": map[string]int{ "EFortRarity::Common": {
"AthenaCharacter": 500, "AthenaCharacter": 500,
"AthenaBackpack": 200, "AthenaBackpack": 200,
"AthenaPickaxe": 500, "AthenaPickaxe": 500,

View File

@ -34,22 +34,24 @@ func PostCreateFriend(c *fiber.Ctx) error {
return c.Status(400).JSON(aid.ErrorBadRequest(err.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 { if found {
from.JabberSendMessageToPerson(aid.JSON{ from.JabberSendMessageToPerson(aid.JSON{
"type": "com.epicgames.friends.core.apiobjects.Friend", "type": "com.epicgames.friends.core.apiobjects.Friend",
"timestamp": time.Now().Format(time.RFC3339), "timestamp": time.Now().Format(time.RFC3339),
"payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeFromPerson), "payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeFromPerson),
}) })
from.JabberNotifyFriends()
} }
towards, found := socket.GetJabberSocketByPersonID(relationship.Towards.ID) towards, found := socket.JabberSockets.Get(relationship.Towards.ID)
if found { if found {
towards.JabberSendMessageToPerson(aid.JSON{ towards.JabberSendMessageToPerson(aid.JSON{
"type": "com.epicgames.friends.core.apiobjects.Friend", "type": "com.epicgames.friends.core.apiobjects.Friend",
"timestamp": time.Now().Format(time.RFC3339), "timestamp": time.Now().Format(time.RFC3339),
"payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeTowardsPerson), "payload": relationship.GenerateFortniteFriendEntry(p.GenerateTypeTowardsPerson),
}) })
towards.JabberNotifyFriends()
} }
return c.SendStatus(204) return c.SendStatus(204)

View File

@ -44,9 +44,9 @@ func WebsocketConnection(c *websocket.Conn) {
} }
func GetConnectedSockets(c *fiber.Ctx) error { 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 { socket.JabberSockets.Range(func(key string, value *socket.Socket[socket.JabberData]) bool {
jabber = append(jabber, *value) jabber[key] = *value
return true return true
}) })

View File

@ -46,9 +46,13 @@ func init() {
fortnite.GeneratePlaylistImages() fortnite.GeneratePlaylistImages()
if found := person.FindByDisplay("god"); found == nil { if found := person.FindByDisplay("god"); found == nil {
god := fortnite.NewFortnitePerson("god", true) god := fortnite.NewFortnitePersonWithId("god", "god", true)
god.AddPermission("all") god.AddPermission("all")
angel := fortnite.NewFortnitePersonWithId("angel", "angel", true)
angel.AddPermission("all")
} }
} }
func main() { func main() {
r := fiber.New(fiber.Config{ r := fiber.New(fiber.Config{
@ -57,6 +61,7 @@ func main() {
JSONDecoder: json.Unmarshal, JSONDecoder: json.Unmarshal,
}) })
r.Use(aid.FiberLogger()) r.Use(aid.FiberLogger())
r.Use(aid.FiberLimiter()) r.Use(aid.FiberLimiter())
r.Use(aid.FiberCors()) r.Use(aid.FiberCors())

View File

@ -84,7 +84,7 @@ func FindShallow(personId string) *Person {
return nil return nil
} }
return findHelper(person, true, true) return findHelper(person, true, false)
} }
func FindByDisplay(displayName string) *Person { func FindByDisplay(displayName string) *Person {
@ -120,7 +120,7 @@ func FindByDisplayShallow(displayName string) *Person {
return nil return nil
} }
return findHelper(person, true, true) return findHelper(person, true, false)
} }
func FindByDiscord(discordId string) *Person { func FindByDiscord(discordId string) *Person {

View File

@ -39,13 +39,15 @@ func (r *Relationship) GenerateFortniteFriendEntry(t RelationshipGenerateType) a
switch t { switch t {
case GenerateTypeFromPerson: case GenerateTypeFromPerson:
result["direction"] = "OUTBOUND" result["direction"] = RelationshipOutboundDirection
result["accountId"] = r.Towards.ID result["accountId"] = r.Towards.ID
case GenerateTypeTowardsPerson: case GenerateTypeTowardsPerson:
result["direction"] = "INBOUND" result["direction"] = RelationshipInboundDirection
result["accountId"] = r.From.ID result["accountId"] = r.From.ID
} }
aid.PrintJSON(result)
return result return result
} }
@ -65,7 +67,7 @@ func (p *Person) LoadRelationships() {
incoming := storage.Repo.Storage.GetIncomingRelationships(p.ID) incoming := storage.Repo.Storage.GetIncomingRelationships(p.ID)
for _, entry := range incoming { for _, entry := range incoming {
relationship := &Relationship{ relationship := &Relationship{
From: Find(entry.FromPersonID), From: FindShallow(entry.FromPersonID),
Towards: p, Towards: p,
Status: entry.Status, Status: entry.Status,
Direction: RelationshipInboundDirection, Direction: RelationshipInboundDirection,
@ -73,6 +75,18 @@ func (p *Person) LoadRelationships() {
p.Relationships.Set(entry.FromPersonID, relationship) 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) { 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) { func (p *Person) createOutboundRelationship(towards string) (*Relationship, error) {
relationship := &Relationship{ relationship := &Relationship{
From: p, From: p,
Towards: Find(towards), Towards: FindShallow(towards),
Status: "PENDING", Status: "PENDING",
Direction: RelationshipOutboundDirection, Direction: RelationshipOutboundDirection,
} }
@ -104,7 +118,7 @@ func (p *Person) createOutboundRelationship(towards string) (*Relationship, erro
func (p *Person) createAcceptInboundRelationship(towards string) (*Relationship, error) { func (p *Person) createAcceptInboundRelationship(towards string) (*Relationship, error) {
relationship := &Relationship{ relationship := &Relationship{
From: Find(towards), From: FindShallow(towards),
Towards: p, Towards: p,
Status: "ACCEPTED", Status: "ACCEPTED",
Direction: RelationshipInboundDirection, Direction: RelationshipInboundDirection,

View File

@ -12,11 +12,14 @@ import (
type JabberData struct { type JabberData struct {
JabberID string JabberID string
LastPresence string
} }
var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error { var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error {
"open": jabberOpenHandler, "open": jabberOpenHandler,
"iq": jabberIqRootHandler, "iq": jabberIqRootHandler,
"presence": jabberPresenceHandler,
"message": jabberMessageHandler,
} }
func HandleNewJabberSocket(identifier string) { func HandleNewJabberSocket(identifier string) {
@ -24,7 +27,7 @@ func HandleNewJabberSocket(identifier string) {
if !ok { if !ok {
return return
} }
defer JabberSockets.Delete(identifier) defer JabberSockets.Delete(socket.ID)
for { for {
_, message, failed := socket.Connection.ReadMessage() _, message, failed := socket.Connection.ReadMessage()
@ -49,8 +52,8 @@ func HandleNewJabberSocket(identifier string) {
} }
func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error { func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error {
socket.Connection.WriteMessage(websocket.TextMessage, []byte(`<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" from="prod.ol.epicgames.com" version="1.0" id="`+ socket.ID +`" />`)) socket.Write([]byte(`<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" from="prod.ol.epicgames.com" version="1.0" id="`+ socket.ID +`" />`))
socket.Connection.WriteMessage(websocket.TextMessage, []byte(`<stream:features xmlns:stream="http://etherx.jabber.org/streams" />`)) socket.Write([]byte(`<stream:features xmlns:stream="http://etherx.jabber.org/streams" />`))
return nil return nil
} }
@ -81,32 +84,30 @@ func jabberIqSetHandler(socket *Socket[JabberData], parsed *etree.Document) erro
return fmt.Errorf("person not found") 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.Person = person
socket.Data.JabberID = snowId + "@prod.ol.epicgames.com/" + parsed.FindElement("/iq/query/resource").Text()
socket.Connection.WriteMessage(websocket.TextMessage, []byte(`<iq xmlns="jabber:client" type="result" id="_xmpp_auth1" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`)) socket.Write([]byte(`<iq xmlns="jabber:client" type="result" id="_xmpp_auth1" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`))
return nil return nil
} }
func jabberIqGetHandler(socket *Socket[JabberData], parsed *etree.Document) error { func jabberIqGetHandler(socket *Socket[JabberData], parsed *etree.Document) error {
socket.Connection.WriteMessage(websocket.TextMessage, []byte(`<iq xmlns="jabber:client" type="result" id="`+ parsed.Root().SelectAttr("id").Value +`" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`)) socket.Write([]byte(`<iq xmlns="jabber:client" type="result" id="`+ parsed.Root().SelectAttr("id").Value +`" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`))
socket.JabberNotifyFriends()
return nil return nil
} }
func GetJabberSocketByPersonID(id string) (*Socket[JabberData], bool) { func jabberPresenceHandler(socket *Socket[JabberData], parsed *etree.Document) error {
var found *Socket[JabberData] socket.Data.LastPresence = parsed.FindElement("/presence/status").Text()
socket.JabberNotifyFriends()
JabberSockets.Range(func(key string, socket *Socket[JabberData]) bool { return nil
if socket.Person.ID == id {
found = socket
return false
} }
return true func jabberMessageHandler(socket *Socket[JabberData], parsed *etree.Document) error {
})
return found, found != nil return nil
} }
func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) { func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) {
@ -120,11 +121,39 @@ func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) {
return return
} }
aid.Print(`<message xmlns="jabber:client" from="xmpp-admin@prod.ol.epicgames.com" to="`+ jabberSocket.Data.JabberID +`"> s.Write([]byte(`<message xmlns="jabber:client" from="xmpp-admin@prod.ol.epicgames.com" to="`+ jabberSocket.Data.JabberID +`">
<body>`+ string(data.ToBytes()) +`</body>
</message>`)
s.Connection.WriteMessage(1, []byte(`<message xmlns="jabber:client" from="xmpp-admin@prod.ol.epicgames.com" to="`+ jabberSocket.Data.JabberID +`">
<body>`+ string(data.ToBytes()) +`</body> <body>`+ string(data.ToBytes()) +`</body>
</message>`)) </message>`))
} }
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(`<presence xmlns="jabber:client" type="available" from="`+ jabberSocket.Data.JabberID +`" to="`+ friendSocket.Data.JabberID +`">
<status>`+ jabberSocket.Data.LastPresence +`</status>
</presence>`))
jabberSocket.Write([]byte(`<presence xmlns="jabber:client" type="available" from="`+ friendSocket.Data.JabberID +`" to="`+ jabberSocket.Data.JabberID +`">
<status>`+ friendSocket.Data.LastPresence +`</status>
</presence>`))
return true
})
}

View File

@ -1,6 +1,8 @@
package socket package socket
import ( import (
"sync"
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
"github.com/ectrc/snow/person" "github.com/ectrc/snow/person"
"github.com/gofiber/contrib/websocket" "github.com/gofiber/contrib/websocket"
@ -17,6 +19,14 @@ type Socket[T JabberData | MatchmakerData] struct {
Connection *websocket.Conn Connection *websocket.Conn
Data *T Data *T
Person *person.Person 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] { func newSocket[T JabberData | MatchmakerData](conn *websocket.Conn, data ...T) *Socket[T] {