mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-08 00:25:30 -05:00
Rename gcsp to gpsp, master to qr2
This commit is contained in:
193
gcsp/main.go
193
gcsp/main.go
@@ -1,193 +0,0 @@
|
||||
package gcsp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
userId int64
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
// Start SQL
|
||||
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
|
||||
dbConf, err := pgxpool.ParseConfig(dbString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := config.Address + ":29901"
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice("GCSP", "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)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
moduleName := "GCSP"
|
||||
knownProfileId := uint32(0)
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Notice(moduleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
|
||||
if err != nil {
|
||||
logging.Notice(moduleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
logging.Notice(moduleName, "Connection established from", conn.RemoteAddr().String())
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
buffer := make([]byte, 1024)
|
||||
_, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Client closed connection, terminate.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(string(buffer))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
logging.Notice(moduleName, "Command:", aurora.Yellow(command.Command).String())
|
||||
switch command.Command {
|
||||
case "ka":
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
break
|
||||
|
||||
case "otherslist":
|
||||
strProfileId, ok := command.OtherValues["profileid"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing profileid in otherslist")
|
||||
return
|
||||
}
|
||||
|
||||
profileId, err := strconv.ParseUint(strProfileId, 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if knownProfileId == 0 {
|
||||
knownProfileId = uint32(profileId)
|
||||
moduleName = "GCSP:" + strconv.FormatUint(profileId, 10)
|
||||
moduleName += "/" + common.CalcFriendCodeString(uint32(profileId), "RMCJ")
|
||||
} else if uint32(profileId) != knownProfileId {
|
||||
logging.Notice(moduleName, "WARN: Mismatched profile ID in otherslist:", aurora.Cyan(strProfileId).String())
|
||||
}
|
||||
|
||||
conn.Write([]byte(handleOthersList(moduleName, uint32(profileId), command)))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleOthersList(moduleName string, profileId uint32, command common.GameSpyCommand) string {
|
||||
empty := `\otherslist\\final\`
|
||||
|
||||
_, ok := command.OtherValues["sesskey"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing sesskey in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
numopids, ok := command.OtherValues["numopids"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing numopids in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
opids, ok := command.OtherValues["opids"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing opids in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
_, ok = command.OtherValues["gamename"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing gamename in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
numOpidsValue, err := strconv.Atoi(numopids)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
opidsSplit := strings.Split(opids, "|")
|
||||
if len(opidsSplit) != numOpidsValue {
|
||||
logging.Notice(moduleName, "Mismatch opids length with numopids:", aurora.Cyan(len(opidsSplit)).String(), "!=", aurora.Cyan(numOpidsValue).String())
|
||||
return empty
|
||||
}
|
||||
|
||||
payload := `\otherslist\`
|
||||
for _, strOtherId := range opidsSplit {
|
||||
otherId, err := strconv.ParseUint(strOtherId, 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: Perhaps this could be condensed into one database query
|
||||
// Also TODO: Check if the players are actually friends
|
||||
user, ok := database.GetProfile(pool, ctx, uint32(otherId))
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Other ID doesn't exist:", aurora.Cyan(strOtherId).String())
|
||||
// If the profile doesn't exist then skip adding it
|
||||
continue
|
||||
}
|
||||
|
||||
payload += `\o\` + strconv.FormatUint(uint64(user.ProfileId), 10)
|
||||
payload += `\uniquenick\` + user.UniqueNick
|
||||
}
|
||||
|
||||
payload += `\oldone\\final\`
|
||||
return payload
|
||||
}
|
||||
162
gpsp/main.go
162
gpsp/main.go
@@ -1,18 +1,45 @@
|
||||
package gpsp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
userId int64
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
address := config.Address + ":27900"
|
||||
// Start SQL
|
||||
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
|
||||
dbConf, err := pgxpool.ParseConfig(dbString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := config.Address + ":29901"
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -36,16 +63,131 @@ func StartServer() {
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
// Make a buffer to hold incoming data.
|
||||
buf := make([]byte, 1024)
|
||||
// Read the incoming connection into the buffer.
|
||||
reqLen, err := conn.Read(buf)
|
||||
defer conn.Close()
|
||||
|
||||
moduleName := "GPSP"
|
||||
knownProfileId := uint32(0)
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Notice(moduleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
|
||||
if err != nil {
|
||||
logging.Notice(moduleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
logging.Notice(moduleName, "Connection established from", conn.RemoteAddr().String())
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
buffer := make([]byte, 1024)
|
||||
_, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Client closed connection, terminate.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(string(buffer))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
logging.Notice(moduleName, "Command:", aurora.Yellow(command.Command).String())
|
||||
switch command.Command {
|
||||
case "ka":
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
break
|
||||
|
||||
case "otherslist":
|
||||
strProfileId, ok := command.OtherValues["profileid"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing profileid in otherslist")
|
||||
return
|
||||
}
|
||||
|
||||
profileId, err := strconv.ParseUint(strProfileId, 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if knownProfileId == 0 {
|
||||
knownProfileId = uint32(profileId)
|
||||
moduleName = "GPSP:" + strconv.FormatUint(profileId, 10)
|
||||
moduleName += "/" + common.CalcFriendCodeString(uint32(profileId), "RMCJ")
|
||||
} else if uint32(profileId) != knownProfileId {
|
||||
logging.Notice(moduleName, "WARN: Mismatched profile ID in otherslist:", aurora.Cyan(strProfileId).String())
|
||||
}
|
||||
|
||||
conn.Write([]byte(handleOthersList(moduleName, uint32(profileId), command)))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleOthersList(moduleName string, profileId uint32, command common.GameSpyCommand) string {
|
||||
empty := `\otherslist\\final\`
|
||||
|
||||
_, ok := command.OtherValues["sesskey"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing sesskey in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
numopids, ok := command.OtherValues["numopids"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing numopids in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
opids, ok := command.OtherValues["opids"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing opids in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
_, ok = command.OtherValues["gamename"]
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Missing gamename in otherslist")
|
||||
return empty
|
||||
}
|
||||
|
||||
numOpidsValue, err := strconv.Atoi(numopids)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Send a response back to person contacting us.
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
// Close the connection when you're done with it.
|
||||
conn.Close()
|
||||
opidsSplit := strings.Split(opids, "|")
|
||||
if len(opidsSplit) != numOpidsValue {
|
||||
logging.Notice(moduleName, "Mismatch opids length with numopids:", aurora.Cyan(len(opidsSplit)).String(), "!=", aurora.Cyan(numOpidsValue).String())
|
||||
return empty
|
||||
}
|
||||
|
||||
payload := `\otherslist\`
|
||||
for _, strOtherId := range opidsSplit {
|
||||
otherId, err := strconv.ParseUint(strOtherId, 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: Perhaps this could be condensed into one database query
|
||||
// Also TODO: Check if the players are actually friends
|
||||
user, ok := database.GetProfile(pool, ctx, uint32(otherId))
|
||||
if !ok {
|
||||
logging.Notice(moduleName, "Other ID doesn't exist:", aurora.Cyan(strOtherId).String())
|
||||
// If the profile doesn't exist then skip adding it
|
||||
continue
|
||||
}
|
||||
|
||||
payload += `\o\` + strconv.FormatUint(uint64(user.ProfileId), 10)
|
||||
payload += `\uniquenick\` + user.UniqueNick
|
||||
}
|
||||
|
||||
payload += `\oldone\\final\`
|
||||
return payload
|
||||
}
|
||||
|
||||
6
main.go
6
main.go
@@ -2,16 +2,16 @@ package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"wwfc/gcsp"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/master"
|
||||
"wwfc/gpsp"
|
||||
"wwfc/matchmaking"
|
||||
"wwfc/nas"
|
||||
"wwfc/qr2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wg := &sync.WaitGroup{}
|
||||
actions := []func(){nas.StartServer, gpcm.StartServer, master.StartServer, gcsp.StartServer, matchmaking.StartServer}
|
||||
actions := []func(){nas.StartServer, gpcm.StartServer, qr2.StartServer, gpsp.StartServer, matchmaking.StartServer}
|
||||
wg.Add(5)
|
||||
for _, action := range actions {
|
||||
go func(ac func()) {
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
ModuleName = "MATCHMAKING"
|
||||
ModuleName = "SB"
|
||||
|
||||
// Requests sent from the client
|
||||
ServerListRequest = 0x00
|
||||
@@ -66,7 +66,7 @@ func StartServer() {
|
||||
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice("MATCHMAKING", "Listening on", address)
|
||||
logging.Notice(ModuleName, "Listening on", address)
|
||||
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
@@ -105,10 +105,11 @@ func handleRequest(conn net.Conn) {
|
||||
bufferSize -= int(packetSize)
|
||||
packetSize = 0
|
||||
|
||||
// Packets tend to be sent in fragments, so this loop makes sure the packets has been fully received before continuing
|
||||
for {
|
||||
if bufferSize > 2 {
|
||||
packetSize = binary.BigEndian.Uint16(buffer[:2])
|
||||
if packetSize < 3 {
|
||||
if packetSize < 3 || packetSize >= 1024 {
|
||||
logging.Notice(ModuleName, "Invalid packet size - terminating")
|
||||
return
|
||||
}
|
||||
@@ -133,9 +134,6 @@ func handleRequest(conn net.Conn) {
|
||||
bufferSize += readSize
|
||||
}
|
||||
|
||||
logging.Notice(ModuleName, "packet size:", aurora.Cyan(packetSize).String())
|
||||
logging.Notice(ModuleName, "buffer size:", aurora.Cyan(bufferSize).String())
|
||||
|
||||
switch buffer[2] {
|
||||
case ServerListRequest:
|
||||
logging.Notice(ModuleName, "Command:", aurora.Yellow("SERVER_LIST_REQUEST").String())
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
"wwfc/master"
|
||||
"wwfc/qr2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,7 +40,7 @@ const (
|
||||
|
||||
func FindServers(gueryGame string, filter string) ([]map[string]string, error) {
|
||||
// TODO: Handle gueryGame, filter
|
||||
return master.GetSessionServers(), nil
|
||||
return qr2.GetSessionServers(), nil
|
||||
}
|
||||
|
||||
func popString(buffer []byte, index int) (string, int) {
|
||||
@@ -249,5 +249,5 @@ func handleSendMessageRequest(conn net.Conn, buffer []byte) {
|
||||
// TODO: Perform basic packet verification
|
||||
// TODO SECURITY: Check if the selected IP is actually online, or at least make sure it's not a local IP
|
||||
|
||||
master.SendClientMessage(destIP, buffer[9:])
|
||||
qr2.SendClientMessage(destIP, buffer[9:])
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package master
|
||||
package qr2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package master
|
||||
package qr2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func heartbeat(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||
sessionId := binary.BigEndian.Uint32(buffer[1:5])
|
||||
moduleName := "MASTER:" + strconv.FormatInt(int64(sessionId), 10)
|
||||
moduleName := "QR2:" + strconv.FormatInt(int64(sessionId), 10)
|
||||
|
||||
logging.Notice(moduleName, "Received heartbeat from", aurora.Cyan(addr).String())
|
||||
values := strings.Split(string(buffer[5:]), "\u0000")
|
||||
@@ -1,4 +1,4 @@
|
||||
package master
|
||||
package qr2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -46,7 +46,7 @@ func StartServer() {
|
||||
|
||||
// Close the listener when the application closes.
|
||||
defer conn.Close()
|
||||
logging.Notice("MASTER", "Listening on", address)
|
||||
logging.Notice("QR2", "Listening on", address)
|
||||
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
@@ -61,13 +61,13 @@ func StartServer() {
|
||||
|
||||
func handleConnection(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||
if buffer[0] == AvailableRequest {
|
||||
logging.Notice("MASTER", "Command:", aurora.Yellow("AVAILABLE").String())
|
||||
logging.Notice("QR2", "Command:", aurora.Yellow("AVAILABLE").String())
|
||||
conn.WriteTo(createResponseHeader(AvailableRequest, 0), addr)
|
||||
return
|
||||
}
|
||||
|
||||
sessionId := binary.BigEndian.Uint32(buffer[1:5])
|
||||
moduleName := "MASTER:" + strconv.FormatInt(int64(sessionId), 10)
|
||||
moduleName := "QR2:" + strconv.FormatInt(int64(sessionId), 10)
|
||||
|
||||
switch buffer[0] {
|
||||
case QueryRequest:
|
||||
@@ -1,4 +1,4 @@
|
||||
package master
|
||||
package qr2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -38,7 +38,7 @@ func setSessionData(sessionId uint32, payload map[string]string) Session {
|
||||
|
||||
session, exists := sessions[sessionId]
|
||||
if !exists {
|
||||
logging.Notice("MASTER", "Creating session", aurora.Cyan(sessionId).String())
|
||||
logging.Notice("QR2", "Creating session", aurora.Cyan(sessionId).String())
|
||||
data := Session{
|
||||
SessionID: sessionId,
|
||||
Challenge: "",
|
||||
@@ -123,12 +123,12 @@ func SendClientMessage(destIP string, message []byte) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logging.Notice("MASTER", "Sending message...")
|
||||
logging.Notice("QR2", "Sending message...")
|
||||
masterConn.WriteTo(payload, destIPAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
logging.Notice("MASTER", "Could not find destination server")
|
||||
logging.Notice("QR2", "Could not find destination server")
|
||||
}
|
||||
Reference in New Issue
Block a user