mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
Database: Add upload timestamp to MKW ghost data
This commit is contained in:
parent
240fdea556
commit
46f269d470
|
|
@ -26,6 +26,8 @@ var (
|
|||
)
|
||||
|
||||
func GetGameInfoByID(gameId int) *GameInfo {
|
||||
ReadGameList()
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
|
|
@ -37,6 +39,8 @@ func GetGameInfoByID(gameId int) *GameInfo {
|
|||
}
|
||||
|
||||
func GetGameInfoByName(name string) *GameInfo {
|
||||
ReadGameList()
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
|
|
@ -47,6 +51,24 @@ func GetGameInfoByName(name string) *GameInfo {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GetGameID(name string) int {
|
||||
info := GetGameInfoByName(name)
|
||||
if info != nil {
|
||||
return info.GameID
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func GetGameIDOrPanic(name string) int {
|
||||
id := GetGameID(name)
|
||||
if id == -1 {
|
||||
panic("Game not found: " + name)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func ReadGameList() {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ const (
|
|||
"ORDER BY score DESC " +
|
||||
"LIMIT 1"
|
||||
insertGhostFileStatement = "" +
|
||||
"INSERT INTO mario_kart_wii_sake (regionid, courseid, score, pid, playerinfo, ghost) " +
|
||||
"VALUES ($1, $2, $3, $4, $5, $6) " +
|
||||
"INSERT INTO mario_kart_wii_sake (regionid, courseid, score, pid, playerinfo, ghost, upload_time) " +
|
||||
"VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP) " +
|
||||
"ON CONFLICT (courseid, pid) DO UPDATE " +
|
||||
"SET regionid = EXCLUDED.regionid, score = EXCLUDED.score, playerinfo = EXCLUDED.playerinfo, ghost = EXCLUDED.ghost"
|
||||
"SET regionid = EXCLUDED.regionid, score = EXCLUDED.score, playerinfo = EXCLUDED.playerinfo, ghost = EXCLUDED.ghost, upload_time = CURRENT_TIMESTAMP"
|
||||
)
|
||||
|
||||
func GetMarioKartWiiTopTenRankings(pool *pgxpool.Pool, ctx context.Context, regionId common.MarioKartWiiLeaderboardRegionId,
|
||||
|
|
|
|||
|
|
@ -19,22 +19,14 @@ ALTER TABLE ONLY public.users
|
|||
ADD IF NOT EXISTS ban_reason_hidden character varying,
|
||||
ADD IF NOT EXISTS ban_moderator character varying,
|
||||
ADD IF NOT EXISTS ban_tos boolean,
|
||||
ADD IF NOT EXISTS open_host boolean DEFAULT false
|
||||
ADD IF NOT EXISTS open_host boolean DEFAULT false;
|
||||
|
||||
`)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.mario_kart_wii_sake (
|
||||
regionid smallint NOT NULL CHECK (regionid >= 1 AND regionid <= 7),
|
||||
courseid smallint NOT NULL CHECK (courseid >= 0 AND courseid <= 32767),
|
||||
score integer NOT NULL CHECK (score > 0),
|
||||
pid integer NOT NULL CHECK (pid > 0),
|
||||
playerinfo varchar(108) NOT NULL CHECK (LENGTH(playerinfo) = 108),
|
||||
ghost bytea CHECK (ghost IS NULL OR (OCTET_LENGTH(ghost) BETWEEN 148 AND 10240)),
|
||||
pool.Exec(ctx, `
|
||||
|
||||
ALTER TABLE ONLY public.mario_kart_wii_sake
|
||||
ADD IF NOT EXISTS upload_time timestamp without time zone;
|
||||
|
||||
CONSTRAINT one_time_per_course_constraint UNIQUE (courseid, pid)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.mario_kart_wii_sake OWNER TO wiilink;
|
||||
|
||||
`)
|
||||
`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ const (
|
|||
xmlNamespace = "http://gamespy.net/RaceService/"
|
||||
)
|
||||
|
||||
var MarioKartWiiGameID = common.GetGameIDOrPanic("mariokartwii") // 1687
|
||||
|
||||
func handleNintendoRacingServiceRequest(moduleName string, responseWriter http.ResponseWriter, request *http.Request) {
|
||||
soapActionHeader := request.Header.Get("SOAPAction")
|
||||
if soapActionHeader == "" {
|
||||
|
|
@ -102,6 +104,8 @@ func handleNintendoRacingServiceRequest(moduleName string, responseWriter http.R
|
|||
switch soapAction {
|
||||
case "GetTopTenRankings":
|
||||
handleGetTopTenRankingsRequest(moduleName, responseWriter, requestBody)
|
||||
|
||||
// TODO SubmitScores
|
||||
default:
|
||||
logging.Info(moduleName, "Unhandled SOAPAction:", aurora.Cyan(soapAction))
|
||||
}
|
||||
|
|
@ -119,8 +123,8 @@ func handleGetTopTenRankingsRequest(moduleName string, responseWriter http.Respo
|
|||
requestData := requestXML.Body.Data
|
||||
|
||||
gameId := requestData.GameId
|
||||
if gameId != common.GameSpyGameIdMarioKartWii {
|
||||
logging.Error(moduleName, "Wrong GameSpy game id")
|
||||
if gameId != MarioKartWiiGameID {
|
||||
logging.Error(moduleName, "Wrong GameSpy game ID:", aurora.Cyan(gameId))
|
||||
writeErrorResponse(raceServiceResultInvalidParameters, responseWriter)
|
||||
return
|
||||
}
|
||||
|
|
@ -129,19 +133,19 @@ func handleGetTopTenRankingsRequest(moduleName string, responseWriter http.Respo
|
|||
courseId := requestData.CourseId
|
||||
|
||||
if !regionId.IsValid() {
|
||||
logging.Error(moduleName, "Invalid region id")
|
||||
logging.Error(moduleName, "Invalid region ID:", aurora.Cyan(regionId))
|
||||
writeErrorResponse(raceServiceResultInvalidParameters, responseWriter)
|
||||
return
|
||||
}
|
||||
if courseId < common.MarioCircuit || courseId > 32767 {
|
||||
logging.Error(moduleName, "Invalid course id")
|
||||
logging.Error(moduleName, "Invalid course ID:", aurora.Cyan(courseId))
|
||||
writeErrorResponse(raceServiceResultInvalidParameters, responseWriter)
|
||||
return
|
||||
}
|
||||
|
||||
topTenRankings, err := database.GetMarioKartWiiTopTenRankings(pool, ctx, regionId, courseId)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to get the Top 10 rankings")
|
||||
logging.Error(moduleName, "Failed to get the Top 10 rankings:", err)
|
||||
writeErrorResponse(raceServiceResultDatabaseError, responseWriter)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
package common
|
||||
package sake
|
||||
|
||||
const GameSpyMultipartBoundary = "Qr4G823s23d---<<><><<<>--7d118e0536"
|
||||
|
||||
type SakeFileResult int
|
||||
|
||||
const (
|
||||
GameSpyMultipartBoundary = "Qr4G823s23d---<<><><<<>--7d118e0536"
|
||||
GameSpyGameIdMarioKartWii = 1687
|
||||
)
|
||||
|
||||
// https://documentation.help/GameSpy-SDK/SAKEFileResult.html
|
||||
const (
|
||||
SakeFileResultHeader = "Sake-File-Result"
|
||||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
type playerInfo struct {
|
||||
|
|
@ -47,53 +49,53 @@ func handleMarioKartWiiGhostDownloadRequest(moduleName string, responseWriter ht
|
|||
|
||||
regionIdInt, err := strconv.Atoi(regionIdString)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid region id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid region ID:", aurora.Cyan(regionIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
if common.MarioKartWiiLeaderboardRegionId(regionIdInt) != common.Worldwide {
|
||||
logging.Error(moduleName, "Invalid region id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid region ID:", aurora.Cyan(regionIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
courseIdInt, err := strconv.Atoi(courseIdString)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid course id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid course ID:", aurora.Cyan(courseIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
courseId := common.MarioKartWiiCourseId(courseIdInt)
|
||||
if !courseId.IsValid() {
|
||||
logging.Error(moduleName, "Invalid course id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid course ID:", aurora.Cyan(courseIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(pidString)
|
||||
if err != nil || pid <= 0 {
|
||||
logging.Error(moduleName, "Invalid pid")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid profile ID:", aurora.Cyan(pidString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
time, err := strconv.Atoi(timeString)
|
||||
if err != nil || time <= 0 {
|
||||
logging.Error(moduleName, "Invalid time")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid time:", aurora.Cyan(timeString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
ghost, err := database.GetMarioKartWiiGhostFile(pool, ctx, courseId, time, pid)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to get a ghost file from the database")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultServerError))
|
||||
logging.Error(moduleName, "Failed to get a ghost file from the database:", err)
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultServerError))
|
||||
return
|
||||
}
|
||||
|
||||
responseBody := append(downloadedGhostFileHeader(), ghost...)
|
||||
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultSuccess))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultSuccess))
|
||||
responseWriter.Header().Set("Content-Length", strconv.Itoa(len(responseBody)))
|
||||
responseWriter.Write(responseBody)
|
||||
}
|
||||
|
|
@ -117,47 +119,47 @@ func handleMarioKartWiiGhostUploadRequest(moduleName string, responseWriter http
|
|||
|
||||
regionIdInt, err := strconv.Atoi(regionIdString)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid region id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid region ID:", aurora.Cyan(regionIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
regionId := common.MarioKartWiiLeaderboardRegionId(regionIdInt)
|
||||
if !regionId.IsValid() || regionId == common.Worldwide {
|
||||
logging.Error(moduleName, "Invalid region id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid region ID:", aurora.Cyan(regionIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
courseIdInt, err := strconv.Atoi(courseIdString)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid course id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid course ID:", aurora.Cyan(courseIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
courseId := common.MarioKartWiiCourseId(courseIdInt)
|
||||
if courseId < common.MarioCircuit || courseId > 32767 {
|
||||
logging.Error(moduleName, "Invalid course id")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid course ID:", aurora.Cyan(courseIdString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
score, err := strconv.Atoi(scoreString)
|
||||
if err != nil || score <= 0 {
|
||||
logging.Error(moduleName, "Invalid score")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid score:", aurora.Cyan(scoreString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(pidString)
|
||||
if err != nil || pid <= 0 {
|
||||
logging.Error(moduleName, "Invalid pid")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid profile ID:", aurora.Cyan(pidString))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
|
||||
if !isPlayerInfoValid(playerInfo) {
|
||||
logging.Error(moduleName, "Invalid player info")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultMissingParameter))
|
||||
logging.Error(moduleName, "Invalid player info:", aurora.Cyan(playerInfo))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultMissingParameter))
|
||||
return
|
||||
}
|
||||
// Mario Kart Wii expects player information to be in this form
|
||||
|
|
@ -167,7 +169,7 @@ func handleMarioKartWiiGhostUploadRequest(moduleName string, responseWriter http
|
|||
// we need to surround it with double quotation marks.
|
||||
contentType := request.Header.Get("Content-Type")
|
||||
boundary := getMultipartBoundary(contentType)
|
||||
if boundary == common.GameSpyMultipartBoundary {
|
||||
if boundary == GameSpyMultipartBoundary {
|
||||
quotedBoundary := fmt.Sprintf("%q", boundary)
|
||||
contentType := strings.Replace(contentType, boundary, quotedBoundary, 1)
|
||||
request.Header.Set("Content-Type", contentType)
|
||||
|
|
@ -175,36 +177,36 @@ func handleMarioKartWiiGhostUploadRequest(moduleName string, responseWriter http
|
|||
|
||||
err = request.ParseMultipartForm(rkgdFileMaxSize)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to parse the multipart form")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultFileNotFound))
|
||||
logging.Error(moduleName, "Failed to parse the multipart form:", err)
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultFileNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
file, fileHeader, err := request.FormFile(rkgdFileName)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to find the ghost file")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultFileNotFound))
|
||||
logging.Error(moduleName, "Failed to find the ghost file:", err)
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultFileNotFound))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if fileHeader.Size < rkgdFileMinSize || fileHeader.Size > rkgdFileMaxSize {
|
||||
logging.Error(moduleName, "The size of the ghost file is invalid")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultFileTooLarge))
|
||||
logging.Error(moduleName, "The size of the ghost file is invalid:", aurora.Cyan(fileHeader.Size))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultFileTooLarge))
|
||||
return
|
||||
}
|
||||
|
||||
ghostFile := make([]byte, fileHeader.Size)
|
||||
_, err = io.ReadFull(file, ghostFile)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to read contents of the ghost file")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultFileTooLarge))
|
||||
logging.Error(moduleName, "Failed to read contents of the ghost file:", err)
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultFileTooLarge))
|
||||
return
|
||||
}
|
||||
|
||||
if !isRKGDFileValid(ghostFile) {
|
||||
logging.Error(moduleName, "Received an invalid ghost file")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultFileTooLarge))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultFileTooLarge))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -214,12 +216,12 @@ func handleMarioKartWiiGhostUploadRequest(moduleName string, responseWriter http
|
|||
|
||||
err = database.InsertMarioKartWiiGhostFile(pool, ctx, regionId, courseId, score, pid, playerInfo, ghostFile)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to insert the ghost file into the database")
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultServerError))
|
||||
logging.Error(moduleName, "Failed to insert the ghost file into the database:", err)
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultServerError))
|
||||
return
|
||||
}
|
||||
|
||||
responseWriter.Header().Set(common.SakeFileResultHeader, strconv.Itoa(common.SakeFileResultSuccess))
|
||||
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultSuccess))
|
||||
}
|
||||
|
||||
func downloadedGhostFileHeader() []byte {
|
||||
|
|
|
|||
|
|
@ -120,11 +120,11 @@ type StorageSearchForRecordsResponse struct {
|
|||
}
|
||||
|
||||
var fileDownloadHandlers = map[int]func(string, http.ResponseWriter, *http.Request){
|
||||
common.GameSpyGameIdMarioKartWii: handleMarioKartWiiFileDownloadRequest,
|
||||
common.GetGameIDOrPanic("mariokartwii"): handleMarioKartWiiFileDownloadRequest,
|
||||
}
|
||||
|
||||
var fileUploadHandlers = map[int]func(string, http.ResponseWriter, *http.Request){
|
||||
common.GameSpyGameIdMarioKartWii: handleMarioKartWiiFileUploadRequest,
|
||||
common.GetGameIDOrPanic("mariokartwii"): handleMarioKartWiiFileUploadRequest,
|
||||
}
|
||||
|
||||
func handleStorageRequest(moduleName string, w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user