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/google/uuid v1.5.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/r3labs/diff/v3 v3.0.1
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gorm.io/driver/postgres v1.5.3
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"github.com/ectrc/snow/socket"
|
||||
"github.com/gofiber/contrib/websocket"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func MiddlewareWebsocket(c *fiber.Ctx) error {
|
||||
|
@ -22,7 +21,7 @@ func MiddlewareWebsocket(c *fiber.Ctx) error {
|
|||
protocol = "matchmaking"
|
||||
}
|
||||
|
||||
c.Locals("identifier", uuid.New().String())
|
||||
c.Locals("identifier", "ws-"+aid.RandomString(8))
|
||||
c.Locals("protocol", protocol)
|
||||
|
||||
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)
|
||||
return nil
|
||||
})
|
||||
|
||||
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)
|
||||
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**
|
||||
|
||||
## 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
|
||||
|
||||
`QueryProfile`, `ClientQuestLogin`, `MarkItemSeen`, `SetItemFavoriteStatusBatch`, `EquipBattleRoyaleCustomization`, `SetBattleRoyaleBanner`, `SetCosmeticLockerSlot`, `SetCosmeticLockerBanner`, `SetCosmeticLockerName`, `CopyCosmeticLoadout`, `DeleteCosmeticLoadout`, `PurchaseCatalogEntry`, `GiftCatalogEntry`, `RemoveGiftBox`, `RefundMtxPurchase`, `SetAffiliateName`, `SetReceiveGiftsEnabled`
|
||||
|
|
|
@ -3,6 +3,7 @@ package socket
|
|||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
"github.com/ectrc/snow/aid"
|
||||
|
@ -17,6 +18,7 @@ type JabberData struct {
|
|||
}
|
||||
|
||||
var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error {
|
||||
"stream": jabberStreamHandler,
|
||||
"open": jabberOpenHandler,
|
||||
"iq": jabberIqRootHandler,
|
||||
"presence": jabberPresenceRootHandler,
|
||||
|
@ -24,8 +26,11 @@ var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error
|
|||
}
|
||||
|
||||
func HandleNewJabberSocket(identifier string) {
|
||||
aid.Print("new jabber handle: " + identifier)
|
||||
|
||||
socket, ok := JabberSockets.Get(identifier)
|
||||
if !ok {
|
||||
aid.Print("socket not found", identifier)
|
||||
return
|
||||
}
|
||||
defer JabberSockets.Delete(socket.ID)
|
||||
|
@ -33,27 +38,47 @@ func HandleNewJabberSocket(identifier string) {
|
|||
for {
|
||||
_, message, failed := socket.Connection.ReadMessage()
|
||||
if failed != nil {
|
||||
aid.Print("jabber message failed", failed)
|
||||
break
|
||||
}
|
||||
|
||||
parsed := etree.NewDocument()
|
||||
if err := parsed.ReadFromBytes(message); err != nil {
|
||||
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()
|
||||
if err := parsed.ReadFromString(string(message)); err != nil {
|
||||
aid.Print("jabber message failed to parse", err)
|
||||
return
|
||||
}
|
||||
|
||||
if handler, ok := jabberHandlers[parsed.Root().Tag]; ok {
|
||||
if err := handler(socket, parsed); err != nil {
|
||||
socket.Connection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if handler, ok := jabberHandlers[parsed.Root().Tag]; ok {
|
||||
if err := handler(socket, parsed); err != nil {
|
||||
socket.Connection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
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 {
|
||||
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" />`))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,14 @@ type MatchmakerData struct {
|
|||
Region string
|
||||
}
|
||||
|
||||
type WebSocket interface {
|
||||
WriteMessage(messageType int, data []byte) error
|
||||
ReadMessage() (messageType int, p []byte, err error)
|
||||
}
|
||||
|
||||
type Socket[T JabberData | MatchmakerData] struct {
|
||||
ID string
|
||||
Connection *websocket.Conn
|
||||
Connection WebSocket
|
||||
Data *T
|
||||
Person *person.Person
|
||||
M sync.Mutex
|
||||
|
@ -29,7 +34,7 @@ func (s *Socket[T]) Write(payload []byte) {
|
|||
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]
|
||||
|
||||
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.ID = id
|
||||
return socket
|
||||
|
|
|
@ -1,29 +1,49 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/ectrc/snow/aid"
|
||||
)
|
||||
|
||||
func GetDefaultEngine() []byte {
|
||||
/*[OnlineSubsystemMcp]
|
||||
bUsePartySystemV2=true
|
||||
|
||||
[OnlineSubsystemMcp.OnlinePartySystemMcpAdapter]
|
||||
bUsePartySystemV2=true*/
|
||||
return []byte(`
|
||||
[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 +`/?"
|
||||
portNumber, err := strconv.Atoi(aid.Config.API.Port[1:])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
portNumber++
|
||||
realPort := fmt.Sprintf("%d", portNumber)
|
||||
|
||||
str := `
|
||||
[XMPP]
|
||||
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]
|
||||
n.VerifyPeer=0
|
||||
FortMatchmakingV2.ContentBeaconFailureCancelsMatchmaking=0
|
||||
|
@ -32,7 +52,36 @@ FortMatchmakingV2.EnableContentBeacon=0
|
|||
|
||||
[/Script/Qos.QosRegionManager]
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user