Start on tcp xmpp for seasons 2 <=
This commit is contained in:
parent
a179961138
commit
36fc5f9f5d
1
go.mod
1
go.mod
|
@ -15,7 +15,6 @@ require (
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||||
github.com/google/uuid v1.5.0
|
github.com/google/uuid v1.5.0
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
|
||||||
github.com/r3labs/diff/v3 v3.0.1
|
github.com/r3labs/diff/v3 v3.0.1
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
gorm.io/driver/postgres v1.5.3
|
gorm.io/driver/postgres v1.5.3
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"github.com/ectrc/snow/socket"
|
"github.com/ectrc/snow/socket"
|
||||||
"github.com/gofiber/contrib/websocket"
|
"github.com/gofiber/contrib/websocket"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func MiddlewareWebsocket(c *fiber.Ctx) error {
|
func MiddlewareWebsocket(c *fiber.Ctx) error {
|
||||||
|
@ -22,7 +21,7 @@ func MiddlewareWebsocket(c *fiber.Ctx) error {
|
||||||
protocol = "matchmaking"
|
protocol = "matchmaking"
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Locals("identifier", uuid.New().String())
|
c.Locals("identifier", "ws-"+aid.RandomString(8))
|
||||||
c.Locals("protocol", protocol)
|
c.Locals("protocol", protocol)
|
||||||
|
|
||||||
return c.Next()
|
return c.Next()
|
||||||
|
|
112
handlers/tcp.go
Normal file
112
handlers/tcp.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ectrc/snow/aid"
|
||||||
|
"github.com/ectrc/snow/socket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tcpClient struct {
|
||||||
|
c *net.Conn
|
||||||
|
buffer []byte
|
||||||
|
jabber *socket.Socket[socket.JabberData]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpClient) WriteMessage(messageType int, data []byte) error {
|
||||||
|
_, err := (*t.c).Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpClient) ReadMessage() (messageType int, p []byte, err error) {
|
||||||
|
n, err := (*t.c).Read(t.buffer)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, t.buffer[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpClient) loop() {
|
||||||
|
defer t.close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, p, err := t.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aid.Print("(tcp) received: " + string(p))
|
||||||
|
socket.JabberSocketOnMessage(t.jabber, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpClient) close() error {
|
||||||
|
socket.JabberSockets.Delete(t.jabber.ID)
|
||||||
|
(*t.c).Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tcpServer struct {
|
||||||
|
ln net.Listener
|
||||||
|
port string
|
||||||
|
nope chan string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer() (*tcpServer) {
|
||||||
|
portNumber, err := strconv.Atoi(aid.Config.API.Port[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
portNumber++
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", portNumber))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tcpServer{
|
||||||
|
ln: ln,
|
||||||
|
port: fmt.Sprintf(":%d", portNumber),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpServer) Listen() error {
|
||||||
|
defer t.ln.Close()
|
||||||
|
aid.Print("(tcp) listening on " + aid.Config.API.Host + t.port)
|
||||||
|
|
||||||
|
go t.accept()
|
||||||
|
<-t.nope
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpServer) accept() {
|
||||||
|
for {
|
||||||
|
conn, err := t.ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aid.Print("(tcp) new connection from " + conn.RemoteAddr().String())
|
||||||
|
|
||||||
|
go t.handle(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpServer) handle(conn net.Conn) {
|
||||||
|
tcpClient := &tcpClient{
|
||||||
|
c: &conn,
|
||||||
|
buffer: make([]byte, 1024),
|
||||||
|
}
|
||||||
|
tcpClient.jabber = socket.NewJabberSocket(tcpClient, "tcp-"+aid.RandomString(8), socket.JabberData{})
|
||||||
|
socket.JabberSockets.Set(tcpClient.jabber.ID, tcpClient.jabber)
|
||||||
|
|
||||||
|
tcpClient.loop()
|
||||||
|
}
|
9
main.go
9
main.go
|
@ -192,11 +192,16 @@ func main() {
|
||||||
aid.Print("(fiber) listening on " + aid.Config.API.Host + ":" + ld.Port)
|
aid.Print("(fiber) listening on " + aid.Config.API.Host + ":" + ld.Port)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
r.All("*", func(c *fiber.Ctx) error { return c.Status(fiber.StatusNotFound).JSON(aid.ErrorNotFound) })
|
r.All("*", func(c *fiber.Ctx) error { return c.Status(fiber.StatusNotFound).JSON(aid.ErrorNotFound) })
|
||||||
|
|
||||||
|
if aid.Config.Fortnite.Season <= 2 {
|
||||||
|
t := handlers.NewServer()
|
||||||
|
go t.Listen()
|
||||||
|
}
|
||||||
|
|
||||||
err := r.Listen("0.0.0.0" + aid.Config.API.Port)
|
err := r.Listen("0.0.0.0" + aid.Config.API.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("(fiber) ailed to listen: %v", err))
|
panic(fmt.Sprintf("(fiber) failed to listen: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
13
readme.md
13
readme.md
|
@ -22,6 +22,19 @@ And once battle royale is completed ...
|
||||||
|
|
||||||
- **Save The World**
|
- **Save The World**
|
||||||
|
|
||||||
|
## Feature List
|
||||||
|
|
||||||
|
- **XMPP** From builds 3.6 onwards, season 1 and 2 are planned.
|
||||||
|
- **Friends** On every builds with the ability to add and remove friends.
|
||||||
|
- **Party System V2** This replaces the legacy xmpp driven party system.
|
||||||
|
- **Gifting** Of any item shop entry to any friend.
|
||||||
|
- **Locker Loadouts** On seasons 12 onwards, this allows for the saving and loading of multiple locker presets.
|
||||||
|
- **Item Refunding** Of previous shop purchases, will use a refund ticket if refunded in time.
|
||||||
|
- **Universal Item Shop** Works on all builds and will be updated every 24 hours.
|
||||||
|
- **Client Settings Storage** Uses amazon buckets to store client settings.
|
||||||
|
- **Support A Creator 5%** Use any display name and each purchase will give them 5% of the vbucks spent.
|
||||||
|
- **Discord Bot** Very useful to control players, their inventory and their settings
|
||||||
|
|
||||||
## Supported MCP Actions
|
## Supported MCP Actions
|
||||||
|
|
||||||
`QueryProfile`, `ClientQuestLogin`, `MarkItemSeen`, `SetItemFavoriteStatusBatch`, `EquipBattleRoyaleCustomization`, `SetBattleRoyaleBanner`, `SetCosmeticLockerSlot`, `SetCosmeticLockerBanner`, `SetCosmeticLockerName`, `CopyCosmeticLoadout`, `DeleteCosmeticLoadout`, `PurchaseCatalogEntry`, `GiftCatalogEntry`, `RemoveGiftBox`, `RefundMtxPurchase`, `SetAffiliateName`, `SetReceiveGiftsEnabled`
|
`QueryProfile`, `ClientQuestLogin`, `MarkItemSeen`, `SetItemFavoriteStatusBatch`, `EquipBattleRoyaleCustomization`, `SetBattleRoyaleBanner`, `SetCosmeticLockerSlot`, `SetCosmeticLockerBanner`, `SetCosmeticLockerName`, `CopyCosmeticLoadout`, `DeleteCosmeticLoadout`, `PurchaseCatalogEntry`, `GiftCatalogEntry`, `RemoveGiftBox`, `RefundMtxPurchase`, `SetAffiliateName`, `SetReceiveGiftsEnabled`
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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"
|
||||||
|
@ -17,6 +18,7 @@ type JabberData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error {
|
var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error {
|
||||||
|
"stream": jabberStreamHandler,
|
||||||
"open": jabberOpenHandler,
|
"open": jabberOpenHandler,
|
||||||
"iq": jabberIqRootHandler,
|
"iq": jabberIqRootHandler,
|
||||||
"presence": jabberPresenceRootHandler,
|
"presence": jabberPresenceRootHandler,
|
||||||
|
@ -24,8 +26,11 @@ var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleNewJabberSocket(identifier string) {
|
func HandleNewJabberSocket(identifier string) {
|
||||||
|
aid.Print("new jabber handle: " + identifier)
|
||||||
|
|
||||||
socket, ok := JabberSockets.Get(identifier)
|
socket, ok := JabberSockets.Get(identifier)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
aid.Print("socket not found", identifier)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer JabberSockets.Delete(socket.ID)
|
defer JabberSockets.Delete(socket.ID)
|
||||||
|
@ -33,11 +38,23 @@ func HandleNewJabberSocket(identifier string) {
|
||||||
for {
|
for {
|
||||||
_, message, failed := socket.Connection.ReadMessage()
|
_, message, failed := socket.Connection.ReadMessage()
|
||||||
if failed != nil {
|
if failed != nil {
|
||||||
|
aid.Print("jabber message failed", failed)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JabberSocketOnMessage(socket, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func JabberSocketOnMessage(socket *Socket[JabberData], message []byte) {
|
||||||
|
if strings.Contains(string(message), `">`) {
|
||||||
|
message = []byte(strings.ReplaceAll(string(message), `">`, `"/>`))
|
||||||
|
}
|
||||||
|
|
||||||
|
aid.Print("jabber message", string(message))
|
||||||
parsed := etree.NewDocument()
|
parsed := etree.NewDocument()
|
||||||
if err := parsed.ReadFromBytes(message); err != nil {
|
if err := parsed.ReadFromString(string(message)); err != nil {
|
||||||
|
aid.Print("jabber message failed to parse", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,14 +63,22 @@ func HandleNewJabberSocket(identifier string) {
|
||||||
socket.Connection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
socket.Connection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
aid.Print("jabber message not handled", parsed.Root().Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jabberStreamHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
||||||
|
socket.Write([]byte(`<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" to="prod.ol.epicgames.com" id="`+ socket.ID +`" version="1.0" xml:lang="en">`))
|
||||||
|
socket.Write([]byte(`<stream:features xmlns:stream="http://etherx.jabber.org/streams" />`))
|
||||||
|
socket.Write([]byte(`<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" from="prod.ol.epicgames.com" version="1.0" id="`+ socket.ID +`" />`))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
||||||
socket.Write([]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.Write([]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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,14 @@ type MatchmakerData struct {
|
||||||
Region string
|
Region string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WebSocket interface {
|
||||||
|
WriteMessage(messageType int, data []byte) error
|
||||||
|
ReadMessage() (messageType int, p []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
type Socket[T JabberData | MatchmakerData] struct {
|
type Socket[T JabberData | MatchmakerData] struct {
|
||||||
ID string
|
ID string
|
||||||
Connection *websocket.Conn
|
Connection WebSocket
|
||||||
Data *T
|
Data *T
|
||||||
Person *person.Person
|
Person *person.Person
|
||||||
M sync.Mutex
|
M sync.Mutex
|
||||||
|
@ -29,7 +34,7 @@ func (s *Socket[T]) Write(payload []byte) {
|
||||||
s.Connection.WriteMessage(websocket.TextMessage, payload)
|
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, data ...T) *Socket[T] {
|
||||||
additional := data[0]
|
additional := data[0]
|
||||||
|
|
||||||
return &Socket[T]{
|
return &Socket[T]{
|
||||||
|
@ -39,7 +44,7 @@ func newSocket[T JabberData | MatchmakerData](conn *websocket.Conn, data ...T) *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJabberSocket(conn *websocket.Conn, id string, data JabberData) *Socket[JabberData] {
|
func NewJabberSocket(conn WebSocket, id string, data JabberData) *Socket[JabberData] {
|
||||||
socket := newSocket[JabberData](conn, data)
|
socket := newSocket[JabberData](conn, data)
|
||||||
socket.ID = id
|
socket.ID = id
|
||||||
return socket
|
return socket
|
||||||
|
|
|
@ -1,29 +1,49 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/ectrc/snow/aid"
|
"github.com/ectrc/snow/aid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDefaultEngine() []byte {
|
func GetDefaultEngine() []byte {
|
||||||
/*[OnlineSubsystemMcp]
|
portNumber, err := strconv.Atoi(aid.Config.API.Port[1:])
|
||||||
bUsePartySystemV2=true
|
if err != nil {
|
||||||
|
return nil
|
||||||
[OnlineSubsystemMcp.OnlinePartySystemMcpAdapter]
|
}
|
||||||
bUsePartySystemV2=true*/
|
portNumber++
|
||||||
return []byte(`
|
realPort := fmt.Sprintf("%d", portNumber)
|
||||||
[OnlineSubsystemMcp.Xmpp]
|
|
||||||
bUseSSL=false
|
|
||||||
Protocol=ws
|
|
||||||
ServerAddr="ws://`+ aid.Config.API.Host + aid.Config.API.Port +`/?"
|
|
||||||
|
|
||||||
[OnlineSubsystemMcp.Xmpp Prod]
|
|
||||||
bUseSSL=false
|
|
||||||
Protocol=ws
|
|
||||||
ServerAddr="ws://`+ aid.Config.API.Host + aid.Config.API.Port +`/?"
|
|
||||||
|
|
||||||
|
str := `
|
||||||
[XMPP]
|
[XMPP]
|
||||||
bEnableWebsockets=true
|
bEnableWebsockets=true
|
||||||
|
|
||||||
|
[OnlineSubsystem]
|
||||||
|
bHasVoiceEnabled=true
|
||||||
|
|
||||||
|
[Core.Log]
|
||||||
|
LogHttp=VeryVerbose
|
||||||
|
LogXmpp=VeryVerbose
|
||||||
|
LogBeacon=VeryVerbose
|
||||||
|
LogQos=VeryVerbose
|
||||||
|
LogOnline=VeryVerbose
|
||||||
|
LogOnlineGame=VeryVerbose
|
||||||
|
LogOnlineParty=VeryVerbose
|
||||||
|
LogParty=VeryVerbose
|
||||||
|
LogOnlineChat=VeryVerbose
|
||||||
|
LogGarbage=VeryVerbose
|
||||||
|
LogTemp=VeryVerbose
|
||||||
|
LogSourceControl=VeryVerbose
|
||||||
|
LogLootTables=VeryVerbose
|
||||||
|
LogMatchmakingServiceClient=VeryVerbose
|
||||||
|
LogMatchmakingServiceDedicatedServer=VeryVerbose
|
||||||
|
LogUAC=VeryVerbose
|
||||||
|
LogBattlEye=VeryVerbose
|
||||||
|
LogEasyAntiCheatServer=VeryVerbose
|
||||||
|
LogEasyAntiCheatClient=VeryVerbose
|
||||||
|
LogEasyAntiCheatNetComponent=VeryVerbose
|
||||||
|
|
||||||
[ConsoleVariables]
|
[ConsoleVariables]
|
||||||
n.VerifyPeer=0
|
n.VerifyPeer=0
|
||||||
FortMatchmakingV2.ContentBeaconFailureCancelsMatchmaking=0
|
FortMatchmakingV2.ContentBeaconFailureCancelsMatchmaking=0
|
||||||
|
@ -32,7 +52,36 @@ FortMatchmakingV2.EnableContentBeacon=0
|
||||||
|
|
||||||
[/Script/Qos.QosRegionManager]
|
[/Script/Qos.QosRegionManager]
|
||||||
NumTestsPerRegion=5
|
NumTestsPerRegion=5
|
||||||
PingTimeout=3.0`)
|
PingTimeout=3.0`
|
||||||
|
|
||||||
|
if aid.Config.Fortnite.Season <= 2 {
|
||||||
|
str += `
|
||||||
|
|
||||||
|
[OnlineSubsystemMcp.Xmpp]
|
||||||
|
bUseSSL=false
|
||||||
|
Protocol=tcp
|
||||||
|
ServerAddr="`+ aid.Config.API.Host + `"
|
||||||
|
ServerPort=`+ realPort + `
|
||||||
|
|
||||||
|
[OnlineSubsystemMcp.Xmpp Prod]
|
||||||
|
bUseSSL=false
|
||||||
|
Protocol=tcp
|
||||||
|
ServerAddr="`+ aid.Config.API.Host + `"
|
||||||
|
ServerPort=`+ realPort
|
||||||
|
} else {
|
||||||
|
str += `
|
||||||
|
[OnlineSubsystemMcp.Xmpp]
|
||||||
|
bUseSSL=false
|
||||||
|
Protocol=ws
|
||||||
|
ServerAddr="ws://`+ aid.Config.API.Host + aid.Config.API.Port +`/?"
|
||||||
|
|
||||||
|
[OnlineSubsystemMcp.Xmpp Prod]
|
||||||
|
bUseSSL=false
|
||||||
|
Protocol=ws
|
||||||
|
ServerAddr="ws://`+ aid.Config.API.Host + aid.Config.API.Port +`/?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultGame() []byte {
|
func GetDefaultGame() []byte {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user