mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-09 00:55:52 -05:00
Run frontend and backend in a separate process
This will in the future allow the server to be restarted without closing open connections
This commit is contained in:
@@ -409,9 +409,9 @@ func (g *GameSpySession) replyError(err GPError) {
|
||||
if !g.LoginInfoSet {
|
||||
msg := err.GetMessage()
|
||||
// logging.Info(g.ModuleName, "Sending error message:", msg)
|
||||
g.Conn.Write([]byte(msg))
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(msg))
|
||||
if err.Fatal {
|
||||
g.Conn.Close()
|
||||
common.CloseConnection("gpcm", g.ConnIndex)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -423,8 +423,8 @@ func (g *GameSpySession) replyError(err GPError) {
|
||||
|
||||
msg := err.GetMessageTranslate(g.GameName, g.Region, g.Language, g.ConsoleFriendCode, deviceId)
|
||||
// logging.Info(g.ModuleName, "Sending error message:", msg)
|
||||
g.Conn.Write([]byte(msg))
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(msg))
|
||||
if err.Fatal {
|
||||
g.Conn.Close()
|
||||
common.CloseConnection("gpcm", g.ConnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ func sendMessageToSession(msgType string, from uint32, session *GameSpySession,
|
||||
"msg": msg,
|
||||
},
|
||||
})
|
||||
session.Conn.Write([]byte(message))
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(message))
|
||||
}
|
||||
|
||||
func sendMessageToSessionBuffer(msgType string, from uint32, session *GameSpySession, msg string) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package gpcm
|
||||
|
||||
import "wwfc/common"
|
||||
|
||||
func kickPlayer(profileID uint32, reason string) {
|
||||
if session, exists := sessions[profileID]; exists {
|
||||
errorMessage := WWFCMsgKickedGeneric
|
||||
@@ -25,7 +27,7 @@ func kickPlayer(profileID uint32, reason string) {
|
||||
|
||||
case "network_error":
|
||||
// No error message
|
||||
session.Conn.Close()
|
||||
common.CloseConnection("gpcm", session.ConnIndex)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -35,7 +37,7 @@ func kickPlayer(profileID uint32, reason string) {
|
||||
Fatal: true,
|
||||
WWFCMessage: errorMessage,
|
||||
})
|
||||
session.Conn.Close()
|
||||
common.CloseConnection("gpcm", session.ConnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
otherSession, exists := sessions[g.User.ProfileId]
|
||||
if exists {
|
||||
otherSession.replyError(ErrForcedDisconnect)
|
||||
otherSession.Conn.Close()
|
||||
common.CloseConnection("gpcm", otherSession.ConnIndex)
|
||||
|
||||
for i := 0; ; i++ {
|
||||
mutex.Unlock()
|
||||
@@ -302,7 +302,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
g.ModuleName += "/" + common.CalcFriendCodeString(g.User.ProfileId, g.User.GsbrCode[:4])
|
||||
|
||||
// Notify QR2 of the login
|
||||
qr2.Login(g.User.ProfileId, gamecd, ingamesn, cfc, g.User.GsbrCode[:4], g.Conn.RemoteAddr().String(), g.NeedsExploit, g.DeviceAuthenticated, g.User.Restricted, KickPlayer)
|
||||
qr2.Login(g.User.ProfileId, gamecd, ingamesn, cfc, g.User.GsbrCode[:4], g.RemoteAddr, g.NeedsExploit, g.DeviceAuthenticated, g.User.Restricted, KickPlayer)
|
||||
|
||||
replyUserId := g.User.UserId
|
||||
if g.UnitCode == UnitCodeDS {
|
||||
@@ -336,19 +336,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
OtherValues: otherValues,
|
||||
})
|
||||
|
||||
g.Conn.Write([]byte(payload))
|
||||
|
||||
// Now start sending keep alive packets every 5 minutes
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(5 * time.Minute)
|
||||
if !g.LoggedIn {
|
||||
return
|
||||
}
|
||||
|
||||
g.Conn.Write([]byte(`\ka\\final\`))
|
||||
}
|
||||
}()
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(payload))
|
||||
}
|
||||
|
||||
func (g *GameSpySession) exLogin(command common.GameSpyCommand) {
|
||||
@@ -439,7 +427,7 @@ func (g *GameSpySession) verifyExLoginInfo(command common.GameSpyCommand, authTo
|
||||
|
||||
func (g *GameSpySession) performLoginWithDatabase(userId uint64, gsbrCode string, profileId uint32, deviceId uint32) bool {
|
||||
// Get IP address without port
|
||||
ipAddress := g.Conn.RemoteAddr().String()
|
||||
ipAddress := g.RemoteAddr
|
||||
if strings.Contains(ipAddress, ":") {
|
||||
ipAddress = ipAddress[:strings.Index(ipAddress, ":")]
|
||||
}
|
||||
|
||||
181
gpcm/main.go
181
gpcm/main.go
@@ -1,12 +1,8 @@
|
||||
package gpcm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
@@ -18,7 +14,8 @@ import (
|
||||
)
|
||||
|
||||
type GameSpySession struct {
|
||||
Conn net.Conn
|
||||
ConnIndex uint64
|
||||
RemoteAddr string
|
||||
WriteBuffer string
|
||||
User database.User
|
||||
ModuleName string
|
||||
@@ -59,8 +56,9 @@ var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
// I would use a sync.Map instead of the map mutex combo, but this performs better.
|
||||
sessions = map[uint32]*GameSpySession{}
|
||||
mutex = deadlock.Mutex{}
|
||||
sessions = map[uint32]*GameSpySession{}
|
||||
sessionsByConnIndex = map[uint64]*GameSpySession{}
|
||||
mutex = deadlock.Mutex{}
|
||||
|
||||
allowDefaultDolphinKeys bool
|
||||
)
|
||||
@@ -84,58 +82,44 @@ func StartServer() {
|
||||
database.UpdateTables(pool, ctx)
|
||||
|
||||
allowDefaultDolphinKeys = config.AllowDefaultDolphinKeys
|
||||
|
||||
address := *config.GameSpyAddress + ":29900"
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice("GPCM", "Listening on", address)
|
||||
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GameSpySession) closeSession() {
|
||||
if r := recover(); r != nil {
|
||||
logging.Error(g.ModuleName, "Panic:", r)
|
||||
func CloseConnection(index uint64) {
|
||||
mutex.Lock()
|
||||
session := sessionsByConnIndex[index]
|
||||
mutex.Unlock()
|
||||
|
||||
if session == nil {
|
||||
logging.Error("GPCM", "Cannot find session for this connection index:", aurora.Cyan(index))
|
||||
return
|
||||
}
|
||||
|
||||
if g.LoggedIn {
|
||||
qr2.Logout(g.User.ProfileId)
|
||||
if g.QR2IP != 0 {
|
||||
qr2.ProcessGPStatusUpdate(g.User.ProfileId, g.QR2IP, "0")
|
||||
logging.Notice(session.ModuleName, "Connection closed")
|
||||
|
||||
if session.LoggedIn {
|
||||
qr2.Logout(session.User.ProfileId)
|
||||
if session.QR2IP != 0 {
|
||||
qr2.ProcessGPStatusUpdate(session.User.ProfileId, session.QR2IP, "0")
|
||||
}
|
||||
g.sendLogoutStatus()
|
||||
session.sendLogoutStatus()
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
g.Conn.Close()
|
||||
if g.LoggedIn {
|
||||
g.LoggedIn = false
|
||||
delete(sessions, g.User.ProfileId)
|
||||
if session.LoggedIn {
|
||||
session.LoggedIn = false
|
||||
delete(sessions, session.User.ProfileId)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
func NewConnection(index uint64, address string) {
|
||||
session := &GameSpySession{
|
||||
Conn: conn,
|
||||
ConnIndex: index,
|
||||
RemoteAddr: address,
|
||||
User: database.User{},
|
||||
ModuleName: "GPCM:" + conn.RemoteAddr().String(),
|
||||
ModuleName: "GPCM:" + address,
|
||||
LoggedIn: false,
|
||||
Challenge: "",
|
||||
StatusSet: false,
|
||||
@@ -145,16 +129,9 @@ func handleRequest(conn net.Conn) {
|
||||
AuthFriendList: []uint32{},
|
||||
}
|
||||
|
||||
defer session.closeSession()
|
||||
|
||||
// Set session ID and challenge
|
||||
// Set challenge
|
||||
session.Challenge = common.RandomString(10)
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Error(session.ModuleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "lc",
|
||||
CommandValue: "1",
|
||||
@@ -163,66 +140,70 @@ func handleRequest(conn net.Conn) {
|
||||
"id": "1",
|
||||
},
|
||||
})
|
||||
conn.Write([]byte(payload))
|
||||
common.SendPacket("gpcm", index, []byte(payload))
|
||||
|
||||
logging.Notice(session.ModuleName, "Connection established from", conn.RemoteAddr())
|
||||
logging.Notice(session.ModuleName, "Connection established from", address)
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
// TODO: Handle split packets
|
||||
buffer := make([]byte, 1024)
|
||||
n, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Client closed connection, terminate.
|
||||
logging.Info(session.ModuleName, "Client closed connection")
|
||||
return
|
||||
}
|
||||
mutex.Lock()
|
||||
sessionsByConnIndex[index] = session
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
logging.Error(session.ModuleName, "Connection lost")
|
||||
return
|
||||
func HandlePacket(index uint64, data []byte) {
|
||||
mutex.Lock()
|
||||
session := sessionsByConnIndex[index]
|
||||
mutex.Unlock()
|
||||
|
||||
if session == nil {
|
||||
logging.Error("GPCM", "Cannot find session for this connection index:", aurora.Cyan(index))
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logging.Error(session.ModuleName, "Panic:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(string(buffer[:n]))
|
||||
if err != nil {
|
||||
logging.Error(session.ModuleName, "Error parsing message:", err.Error())
|
||||
logging.Error(session.ModuleName, "Raw data:", string(buffer[:n]))
|
||||
session.replyError(ErrParse)
|
||||
return
|
||||
}
|
||||
commands, err := common.ParseGameSpyMessage(string(data))
|
||||
if err != nil {
|
||||
logging.Error(session.ModuleName, "Error parsing message:", err.Error())
|
||||
logging.Error(session.ModuleName, "Raw data:", string(data))
|
||||
session.replyError(ErrParse)
|
||||
return
|
||||
}
|
||||
|
||||
// Commands must be handled in a certain order, not in the order supplied by the client
|
||||
// Commands must be handled in a certain order, not in the order supplied by the client
|
||||
|
||||
commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) {
|
||||
session.Conn.Write([]byte(`\ka\\final\`))
|
||||
})
|
||||
commands = session.handleCommand("login", commands, session.login)
|
||||
commands = session.handleCommand("wwfc_exlogin", commands, session.exLogin)
|
||||
commands = session.ignoreCommand("logout", commands)
|
||||
commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) {
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(`\ka\\final\`))
|
||||
})
|
||||
commands = session.handleCommand("login", commands, session.login)
|
||||
commands = session.handleCommand("wwfc_exlogin", commands, session.exLogin)
|
||||
commands = session.ignoreCommand("logout", commands)
|
||||
|
||||
if len(commands) != 0 && !session.LoggedIn {
|
||||
logging.Error(session.ModuleName, "Attempt to run command before login:", aurora.Cyan(commands[0]))
|
||||
session.replyError(ErrNotLoggedIn)
|
||||
return
|
||||
}
|
||||
if len(commands) != 0 && !session.LoggedIn {
|
||||
logging.Error(session.ModuleName, "Attempt to run command before login:", aurora.Cyan(commands[0]))
|
||||
session.replyError(ErrNotLoggedIn)
|
||||
return
|
||||
}
|
||||
|
||||
commands = session.handleCommand("wwfc_report", commands, session.handleWWFCReport)
|
||||
commands = session.handleCommand("updatepro", commands, session.updateProfile)
|
||||
commands = session.handleCommand("status", commands, session.setStatus)
|
||||
commands = session.handleCommand("addbuddy", commands, session.addFriend)
|
||||
commands = session.handleCommand("delbuddy", commands, session.removeFriend)
|
||||
commands = session.handleCommand("authadd", commands, session.authAddFriend)
|
||||
commands = session.handleCommand("bm", commands, session.bestieMessage)
|
||||
commands = session.handleCommand("getprofile", commands, session.getProfile)
|
||||
commands = session.handleCommand("wwfc_report", commands, session.handleWWFCReport)
|
||||
commands = session.handleCommand("updatepro", commands, session.updateProfile)
|
||||
commands = session.handleCommand("status", commands, session.setStatus)
|
||||
commands = session.handleCommand("addbuddy", commands, session.addFriend)
|
||||
commands = session.handleCommand("delbuddy", commands, session.removeFriend)
|
||||
commands = session.handleCommand("authadd", commands, session.authAddFriend)
|
||||
commands = session.handleCommand("bm", commands, session.bestieMessage)
|
||||
commands = session.handleCommand("getprofile", commands, session.getProfile)
|
||||
|
||||
for _, command := range commands {
|
||||
logging.Error(session.ModuleName, "Unknown command:", aurora.Cyan(command))
|
||||
}
|
||||
for _, command := range commands {
|
||||
logging.Error(session.ModuleName, "Unknown command:", aurora.Cyan(command))
|
||||
}
|
||||
|
||||
if session.WriteBuffer != "" {
|
||||
conn.Write([]byte(session.WriteBuffer))
|
||||
session.WriteBuffer = ""
|
||||
}
|
||||
if session.WriteBuffer != "" {
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(session.WriteBuffer))
|
||||
session.WriteBuffer = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
||||
}
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) != int32(msgMatchData.Reservation.PublicIP) {
|
||||
if common.IPFormatNoPortToInt(g.RemoteAddr) != int32(msgMatchData.Reservation.PublicIP) {
|
||||
logging.Error(g.ModuleName, "RESERVATION: Public IP mismatch")
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
@@ -172,7 +172,7 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
||||
g.QR2IP = uint64(msgMatchData.Reservation.PublicIP) | (uint64(msgMatchData.Reservation.PublicPort) << 32)
|
||||
|
||||
} else if cmd == common.MatchResvOK {
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) != int32(msgMatchData.ResvOK.PublicIP) {
|
||||
if common.IPFormatNoPortToInt(g.RemoteAddr) != int32(msgMatchData.ResvOK.PublicIP) {
|
||||
logging.Error(g.ModuleName, "RESV_OK: Public IP mismatch")
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
@@ -204,7 +204,7 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
||||
return
|
||||
}
|
||||
|
||||
sameAddress := strings.Split(g.Conn.RemoteAddr().String(), ":")[0] == strings.Split(toSession.Conn.RemoteAddr().String(), ":")[0]
|
||||
sameAddress := strings.Split(g.RemoteAddr, ":")[0] == strings.Split(toSession.RemoteAddr, ":")[0]
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
if g.QR2IP == 0 {
|
||||
@@ -360,7 +360,7 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
||||
},
|
||||
})
|
||||
|
||||
toSession.Conn.Write([]byte(message))
|
||||
common.SendPacket("gpcm", toSession.ConnIndex, []byte(message))
|
||||
|
||||
// Append sender's profile ID to dest's RecvStatusFromList
|
||||
toSession.RecvStatusFromList = append(toSession.RecvStatusFromList, g.User.ProfileId)
|
||||
|
||||
Reference in New Issue
Block a user