Finished basic party v2 functionality

This commit is contained in:
Eccentric 2024-02-14 22:20:17 +00:00
parent 8021c1d436
commit bb3dbe5718
6 changed files with 96 additions and 53 deletions

View File

@ -117,8 +117,10 @@ func PostPartyCreate(c *fiber.Ctx) error {
party.AddMember(person) party.AddMember(person)
party.UpdateMemberMeta(person, body.JoinInformation.Meta) party.UpdateMemberMeta(person, body.JoinInformation.Meta)
party.UpdateMemberConnection(person, body.JoinInformation.Connection) party.UpdateMemberConnection(person, body.JoinInformation.Connection)
party.ChangeNewCaptain()
socket.EmitPartyMemberJoined(party, party.GetMember(person)) member := party.GetMember(person)
party.PromoteMember(member)
socket.EmitPartyMemberJoined(party, member)
return c.Status(200).JSON(party.GenerateFortniteParty()) return c.Status(200).JSON(party.GenerateFortniteParty())
} }
@ -134,11 +136,9 @@ func PatchPartyUpdateState(c *fiber.Ctx) error {
} `json:"meta"` } `json:"meta"`
} }
if err := c.BodyParser(&body); err != nil { if err := c.BodyParser(&body); err != nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request")) return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request"))
} }
aid.PrintJSON(body)
party, ok := person.Parties.Get(c.Params("partyId")) party, ok := person.Parties.Get(c.Params("partyId"))
if !ok { if !ok {
@ -212,8 +212,8 @@ func DeletePartyMember(c *fiber.Ctx) error {
party.RemoveMember(person) party.RemoveMember(person)
if party.Captain.Person.ID == person.ID && len(party.Members) > 0 { if party.Captain.Person.ID == person.ID && len(party.Members) > 0 {
party.ChangeNewCaptain() party.PromoteMember(party.GetFirstMember())
go socket.EmitPartyNewCaptain(party) socket.EmitPartyNewCaptain(party)
} }
return c.SendStatus(204) return c.SendStatus(204)
@ -266,21 +266,6 @@ func PostPartyJoin(c *fiber.Ctx) error {
return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request")) return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request"))
} }
// joinability, ok := party.Config["joinability"].(string)
// if ok && joinability != "OPEN" {
// invite, ok := person.Invites.Get(c.Params("partyId"))
// if !ok {
// return c.Status(400).JSON(aid.ErrorBadRequest("Invite Not Found"))
// }
// if invite.Party.ID != party.ID {
// return c.Status(400).JSON(aid.ErrorBadRequest("Invite Not Found"))
// }
// person.Invites.Delete(c.Params("partyId"))
// party.RemoveInvite(invite)
// }
party.AddMember(person) party.AddMember(person)
party.UpdateMemberMeta(person, body.Meta) party.UpdateMemberMeta(person, body.Meta)
party.UpdateMemberConnection(person, body.Connection) party.UpdateMemberConnection(person, body.Connection)
@ -295,3 +280,25 @@ func PostPartyJoin(c *fiber.Ctx) error {
"status": "JOINED", "status": "JOINED",
}) })
} }
func PostPartyPromoteMember(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person)
party, ok := person.Parties.Get(c.Params("partyId"))
if !ok {
return c.Status(400).JSON(aid.ErrorBadRequest("Party Not Found"))
}
member := party.GetMember(p.Find(c.Params("accountId")))
if member == nil {
return c.Status(400).JSON(aid.ErrorBadRequest("Member Not Found"))
}
if party.Captain.Person.ID != person.ID {
return c.Status(400).JSON(aid.ErrorBadRequest("Not Captain"))
}
party.PromoteMember(member)
socket.EmitPartyNewCaptain(party)
return c.SendStatus(204)
}

View File

@ -162,12 +162,12 @@ func main() {
party.Post("/parties", handlers.PostPartyCreate) party.Post("/parties", handlers.PostPartyCreate)
party.Post("/parties/:partyId/invites/:accountId", handlers.PostPartyInvite) party.Post("/parties/:partyId/invites/:accountId", handlers.PostPartyInvite)
party.Post("/parties/:partyId/members/:accountId/join", handlers.PostPartyJoin) party.Post("/parties/:partyId/members/:accountId/join", handlers.PostPartyJoin)
party.Post("/parties/:partyId/members/:accountId/promote", handlers.PostPartyPromoteMember)
party.Patch("/parties/:partyId", handlers.PatchPartyUpdateState) party.Patch("/parties/:partyId", handlers.PatchPartyUpdateState)
party.Patch("/parties/:partyId/members/:accountId/meta", handlers.PatchPartyUpdateMemberState) party.Patch("/parties/:partyId/members/:accountId/meta", handlers.PatchPartyUpdateMemberState)
party.Delete("/parties/:partyId/members/:accountId", handlers.DeletePartyMember) party.Delete("/parties/:partyId/members/:accountId", handlers.DeletePartyMember)
// post /parties/:partyId/members/:accountId/conferences/connection (join a voip channel) // post /parties/:partyId/members/:accountId/conferences/connection (join a voip channel)
// delete /parties/:partyId/members/:accountid (remove a person from a party)
// get /user/:accountId/pings/:pinger/friendId/parties (get pings from a friend) // get /user/:accountId/pings/:pinger/friendId/parties (get pings from a friend)
// post /user/:accountId/pings/:pinger/join (join a party from a ping) // post /user/:accountId/pings/:pinger/join (join a party from a ping)

View File

@ -55,6 +55,11 @@ type PartyMember struct{
Role string Role string
JoinedAt time.Time JoinedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Revision int
}
func (pm *PartyMember) IncreaseRevision() {
pm.Revision++
} }
func (pm *PartyMember) GenerateFortnitePartyMember() aid.JSON { func (pm *PartyMember) GenerateFortnitePartyMember() aid.JSON {
@ -67,7 +72,7 @@ func (pm *PartyMember) GenerateFortnitePartyMember() aid.JSON {
"meta": pm.Meta, "meta": pm.Meta,
"joined_at": pm.JoinedAt.Format(time.RFC3339), "joined_at": pm.JoinedAt.Format(time.RFC3339),
"connections": []aid.JSON{conn}, "connections": []aid.JSON{conn},
"revision": 0, "revision": pm.Revision,
} }
} }
@ -79,6 +84,7 @@ type Party struct{
Config map[string]interface{} Config map[string]interface{}
Meta map[string]interface{} Meta map[string]interface{}
CreatedAt time.Time CreatedAt time.Time
Revision int
m sync.Mutex m sync.Mutex
} }
@ -105,6 +111,13 @@ func NewParty() *Party {
return party return party
} }
func (p *Party) IncreaseRevision() {
p.m.Lock()
defer p.m.Unlock()
p.Revision++
}
func (p *Party) GetMember(person *Person) *PartyMember { func (p *Party) GetMember(person *Person) *PartyMember {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -112,16 +125,24 @@ func (p *Party) GetMember(person *Person) *PartyMember {
return p.Members[person.ID] return p.Members[person.ID]
} }
func (p *Party) ChangeNewCaptain() { func (p *Party) PromoteMember(member *PartyMember) {
p.m.Lock()
defer p.m.Unlock()
member.Role = "CAPTAIN"
member.UpdatedAt = time.Now()
p.Captain = member
}
func (p *Party) GetFirstMember() *PartyMember {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
for _, member := range p.Members { for _, member := range p.Members {
p.Captain = member return member
p.Captain.Role = "CAPTAIN"
p.Captain.UpdatedAt = time.Now()
break
} }
return nil
} }
func (p *Party) AddMember(person *Person) { func (p *Party) AddMember(person *Person) {

View File

@ -13,7 +13,6 @@
## What's up next? ## What's up next?
- **Party System V2** Currently it relies on the automatic XMPP solution which is very hard to keep track of.
- Seeded randomization for the **Item Shop** instead of a random number generator. This will ensure that even if the backend is restarted, the same random items will be in the shop during that day. - Seeded randomization for the **Item Shop** instead of a random number generator. This will ensure that even if the backend is restarted, the same random items will be in the shop during that day.
- Purchasing the **Battle Pass**. This will require the Battle Pass Storefront ID for every build. I am yet to think of a solution for this. - Purchasing the **Battle Pass**. This will require the Battle Pass Storefront ID for every build. I am yet to think of a solution for this.
- Interaction with a Game Server to handle **Event Tracking** for player statistics and challenges. This will be a very large task as a new specialised game server will need to be created. - Interaction with a Game Server to handle **Event Tracking** for player statistics and challenges. This will be a very large task as a new specialised game server will need to be created.
@ -33,4 +32,4 @@ And once battle royale is completed ...
## Contributing ## Contributing
Contributions are welcome! Please open an issue or pull request if you would like to contribute. Make sure to follow the same format (2 space indents) and style! Make sure to keep commits concise and readable e.g. do not change formating to mess up code review! Contributions are welcome! Please open an issue or pull request if you would like to contribute. Make sure to follow the same formatting and to keep commits concise and readable e.g. do not change line indents to mess up code review!

View File

@ -14,6 +14,7 @@ func EmitPartyMemberJoined(party *person.Party, joiningMember *person.PartyMembe
continue continue
} }
joiningMember.IncreaseRevision()
s.JabberSendMessageToPerson(aid.JSON{ s.JabberSendMessageToPerson(aid.JSON{
"account_id": joiningMember.Person.ID, "account_id": joiningMember.Person.ID,
"account_dn": joiningMember.Person.DisplayName, "account_dn": joiningMember.Person.DisplayName,
@ -24,7 +25,7 @@ func EmitPartyMemberJoined(party *person.Party, joiningMember *person.PartyMembe
"ns": "Fortnite", "ns": "Fortnite",
"party_id": party.ID, "party_id": party.ID,
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),
"revision": 0, "revision": joiningMember.Revision,
"type": "com.epicgames.social.party.notification.v0.MEMBER_JOINED", "type": "com.epicgames.social.party.notification.v0.MEMBER_JOINED",
}) })
@ -47,20 +48,21 @@ func EmitPartyMemberJoined(party *person.Party, joiningMember *person.PartyMembe
} }
} }
func EmitPartyMemberLeft(party *person.Party, member *person.PartyMember) { func EmitPartyMemberLeft(party *person.Party, leavingMember *person.PartyMember) {
for _, m := range party.Members { for _, m := range party.Members {
s, ok := JabberSockets.Get(m.Person.ID) s, ok := JabberSockets.Get(m.Person.ID)
if !ok { if !ok {
continue continue
} }
leavingMember.IncreaseRevision()
s.JabberSendMessageToPerson(aid.JSON{ s.JabberSendMessageToPerson(aid.JSON{
"account_id": member.Person.ID, "account_id": leavingMember.Person.ID,
"member_state_updated": aid.JSON{}, "member_state_updated": aid.JSON{},
"ns": "Fortnite", "ns": "Fortnite",
"party_id": party.ID, "party_id": party.ID,
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),
"revision": 0, "revision": leavingMember.Revision,
"type": "com.epicgames.social.party.notification.v0.MEMBER_LEFT", "type": "com.epicgames.social.party.notification.v0.MEMBER_LEFT",
}) })
} }
@ -73,6 +75,7 @@ func EmitPartyMemberMetaUpdated(party *person.Party, member *person.PartyMember,
continue continue
} }
member.IncreaseRevision()
s.JabberSendMessageToPerson(aid.JSON{ s.JabberSendMessageToPerson(aid.JSON{
"account_id": member.Person.ID, "account_id": member.Person.ID,
"account_dn": member.Person.DisplayName, "account_dn": member.Person.DisplayName,
@ -84,7 +87,7 @@ func EmitPartyMemberMetaUpdated(party *person.Party, member *person.PartyMember,
"ns": "Fortnite", "ns": "Fortnite",
"party_id": party.ID, "party_id": party.ID,
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),
"revision": 0, "revision": member.Revision,
"type": "com.epicgames.social.party.notification.v0.MEMBER_STATE_UPDATED", "type": "com.epicgames.social.party.notification.v0.MEMBER_STATE_UPDATED",
}) })
} }
@ -97,6 +100,7 @@ func EmitPartyMetaUpdated(party *person.Party, override map[string]interface{},
continue continue
} }
party.IncreaseRevision()
s.JabberSendMessageToPerson(aid.JSON{ s.JabberSendMessageToPerson(aid.JSON{
"captain_id": party.Captain.Person.ID, "captain_id": party.Captain.Person.ID,
"party_state_updated": update, "party_state_updated": update,
@ -113,11 +117,9 @@ func EmitPartyMetaUpdated(party *person.Party, override map[string]interface{},
"ns": "Fortnite", "ns": "Fortnite",
"party_id": party.ID, "party_id": party.ID,
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),
"revision": 0, "revision": party.Revision,
"type": "com.epicgames.social.party.notification.v0.PARTY_UPDATED", "type": "com.epicgames.social.party.notification.v0.PARTY_UPDATED",
}) })
aid.Print("EmitPartyMetaUpdated party", party.ID, "to", m.Person.DisplayName)
} }
} }
@ -128,13 +130,14 @@ func EmitPartyNewCaptain(party *person.Party) {
continue continue
} }
party.IncreaseRevision()
s.JabberSendMessageToPerson(aid.JSON{ s.JabberSendMessageToPerson(aid.JSON{
"account_id": party.Captain.Person.ID, "account_id": party.Captain.Person.ID,
"account_dn": party.Captain.Person.DisplayName, "account_dn": party.Captain.Person.DisplayName,
"ns": "Fortnite", "ns": "Fortnite",
"party_id": party.ID, "party_id": party.ID,
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),
"revision": 0, "revision": party.Revision,
"type": "com.epicgames.social.party.notification.v0.MEMBER_NEW_CAPTAIN", "type": "com.epicgames.social.party.notification.v0.MEMBER_NEW_CAPTAIN",
}) })
} }
@ -154,7 +157,7 @@ func EmitPartyInvite(invite *person.PartyInvite) {
"sent_at": invite.CreatedAt.Format(time.RFC3339), "sent_at": invite.CreatedAt.Format(time.RFC3339),
"updated_at": invite.UpdatedAt.Format(time.RFC3339), "updated_at": invite.UpdatedAt.Format(time.RFC3339),
"friends_ids": []string{}, "friends_ids": []string{},
"members_count": 0, "members_count": len(invite.Party.Members),
"party_id": invite.Party.ID, "party_id": invite.Party.ID,
"ns": "Fortnite", "ns": "Fortnite",
"sent": time.Now().Format(time.RFC3339), "sent": time.Now().Format(time.RFC3339),

View File

@ -3,7 +3,6 @@ package socket
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"strings"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
@ -172,23 +171,37 @@ func jabberPresenceJoinGroupchat(socket *Socket[JabberData], parsed *etree.Docum
} }
func jabberMessageHandler(socket *Socket[JabberData], parsed *etree.Document) error { func jabberMessageHandler(socket *Socket[JabberData], parsed *etree.Document) error {
if parsed.FindElement("/message/body") == nil {
return nil
}
message := parsed.FindElement("/message") message := parsed.FindElement("/message")
target := message.SelectAttr("to").Value if message.FindElement("/body") == nil {
parts := strings.Split(target, "@")
if len(parts) != 2 {
return nil return nil
} }
if reciever, ok := JabberSockets.Get(parts[0]); ok { towards := message.SelectAttr("to")
reciever.Write([]byte(`<message xmlns="jabber:client" from="`+ socket.Data.JabberID +`" to="`+ target +`" id="`+ message.SelectAttr("id").Value +`"> if towards == nil {
<body>`+ message.FindElement("/message/body").Text() +`</body> return nil
</message>`))
} }
redirect := map[string]func(*Socket[JabberData], *etree.Document) error {
"chat": jabberMessageChatHandler,
"groupchat": jabberMessageGroupchatHandler,
}
if handler, ok := redirect[towards.Value]; ok {
if err := handler(socket, parsed); err != nil {
return err
}
}
return nil
}
// TODO: IMPLEMENT WHISPERING
func jabberMessageChatHandler(socket *Socket[JabberData], parsed *etree.Document) error {
return nil
}
func jabberMessageGroupchatHandler(socket *Socket[JabberData], parsed *etree.Document) error {
return nil return nil
} }