From 13bc954dfb584b59170041a5f31a09ab28dcdb6a Mon Sep 17 00:00:00 2001 From: sultanbarys Date: Mon, 15 Jun 2026 22:23:21 +0500 Subject: [PATCH 1/2] Implement UploadCompetitionRankingScore for Splatfest Finally got around to finishing the Splatfest implementation. The method was completely stubbed out so I added the handler to actually save competition ranking scores to the database. Basically what I did: - Created the handler that reads the CompetitionRankingUploadScoreParam from the packet and extracts festival_id, score, team_id, team_score, is_first_upload - Added the competition_ranking_scores table to store the results - Wired it up in the protocol registration Splatfest should now actually work instead of just silently dropping all the score data. Haven't tested it on actual Wii U yet but the server compiles and the handler logic looks solid. --- competition_ranking_scores.sql | 9 +++ globals/upload_competition_ranking_score.go | 61 +++++++++++++++++++ ...register_common_secure_server_protocols.go | 1 + 3 files changed, 71 insertions(+) create mode 100644 competition_ranking_scores.sql create mode 100644 globals/upload_competition_ranking_score.go diff --git a/competition_ranking_scores.sql b/competition_ranking_scores.sql new file mode 100644 index 0000000..083df1d --- /dev/null +++ b/competition_ranking_scores.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS competition_ranking_scores ( + id bigserial PRIMARY KEY, + pid numeric(10) NOT NULL, + festival_id integer NOT NULL, + score integer NOT NULL, + team_id smallint NOT NULL, + team_score integer NOT NULL, + is_first_upload boolean NOT NULL +); diff --git a/globals/upload_competition_ranking_score.go b/globals/upload_competition_ranking_score.go new file mode 100644 index 0000000..5147189 --- /dev/null +++ b/globals/upload_competition_ranking_score.go @@ -0,0 +1,61 @@ +package globals + +import ( + nex "github.com/PretendoNetwork/nex-go/v2" + ranking "github.com/PretendoNetwork/nex-protocols-go/v2/ranking/splatoon" +) + +func UploadCompetitionRankingScore(err error, packet nex.PacketInterface, callID uint32, packetPayload []byte) (*nex.RMCMessage, *nex.Error) { + if err != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error()) + } + + request := packet.RMCMessage() + parameters := request.Parameters + endpoint := packet.Sender().Endpoint() + parametersStream := nex.NewByteStreamIn(parameters, endpoint.LibraryVersions(), endpoint.ByteStreamSettings()) + + // Read CompetitionRankingUploadScoreParam data + festivalID, streamErr := parametersStream.ReadUInt32LE() + if streamErr != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read festival_id") + } + + score, streamErr := parametersStream.ReadUInt32LE() + if streamErr != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read score") + } + + teamID, streamErr := parametersStream.ReadUInt8() + if streamErr != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read team_id") + } + + teamScore, streamErr := parametersStream.ReadUInt32LE() + if streamErr != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read team_score") + } + + isFirstUpload, streamErr := parametersStream.ReadBool() + if streamErr != nil { + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read is_first_upload") + } + + pid := packet.Sender().PID() + + _, dbErr := Postgres.Exec( + "INSERT INTO competition_ranking_scores (pid, festival_id, score, team_id, team_score, is_first_upload) VALUES ($1, $2, $3, $4, $5, $6)", + pid, festivalID, score, teamID, teamScore, isFirstUpload, + ) + if dbErr != nil { + Logger.Error(dbErr.Error()) + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Database error") + } + + rmcResponse := nex.NewRMCSuccess(endpoint, nil) + rmcResponse.ProtocolID = ranking.ProtocolID + rmcResponse.MethodID = ranking.MethodUploadCompetitionRankingScore + rmcResponse.CallID = callID + + return rmcResponse, nil +} diff --git a/nex/register_common_secure_server_protocols.go b/nex/register_common_secure_server_protocols.go index 7e6cb85..1e5c1ba 100644 --- a/nex/register_common_secure_server_protocols.go +++ b/nex/register_common_secure_server_protocols.go @@ -56,6 +56,7 @@ func registerCommonSecureServerProtocols() { commonMatchmakeExtensionProtocol.SetManager(globals.MatchmakingManager) rankingProtocol := ranking.NewProtocol(globals.SecureEndpoint) + rankingProtocol.UploadCompetitionRankingScore = globals.UploadCompetitionRankingScore globals.SecureEndpoint.RegisterServiceProtocol(rankingProtocol) commonranking.NewCommonProtocol(rankingProtocol) } From e4639d4e2cdcb3c6e2f9f5a56123b65b29d5c924 Mon Sep 17 00:00:00 2001 From: sultanbarys Date: Mon, 15 Jun 2026 22:34:20 +0500 Subject: [PATCH 2/2] fix: use bigint for competition ranking fields and include stream error details in upload handler --- competition_ranking_scores.sql | 6 +++--- globals/upload_competition_ranking_score.go | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/competition_ranking_scores.sql b/competition_ranking_scores.sql index 083df1d..9397774 100644 --- a/competition_ranking_scores.sql +++ b/competition_ranking_scores.sql @@ -1,9 +1,9 @@ CREATE TABLE IF NOT EXISTS competition_ranking_scores ( id bigserial PRIMARY KEY, pid numeric(10) NOT NULL, - festival_id integer NOT NULL, - score integer NOT NULL, + festival_id bigint NOT NULL, + score bigint NOT NULL, team_id smallint NOT NULL, - team_score integer NOT NULL, + team_score bigint NOT NULL, is_first_upload boolean NOT NULL ); diff --git a/globals/upload_competition_ranking_score.go b/globals/upload_competition_ranking_score.go index 5147189..2789762 100644 --- a/globals/upload_competition_ranking_score.go +++ b/globals/upload_competition_ranking_score.go @@ -1,6 +1,7 @@ package globals import ( + "fmt" nex "github.com/PretendoNetwork/nex-go/v2" ranking "github.com/PretendoNetwork/nex-protocols-go/v2/ranking/splatoon" ) @@ -18,27 +19,27 @@ func UploadCompetitionRankingScore(err error, packet nex.PacketInterface, callID // Read CompetitionRankingUploadScoreParam data festivalID, streamErr := parametersStream.ReadUInt32LE() if streamErr != nil { - return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read festival_id") + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, fmt.Sprintf("Failed to read festival_id: %v", streamErr)) } score, streamErr := parametersStream.ReadUInt32LE() if streamErr != nil { - return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read score") + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, fmt.Sprintf("Failed to read score: %v", streamErr)) } teamID, streamErr := parametersStream.ReadUInt8() if streamErr != nil { - return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read team_id") + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, fmt.Sprintf("Failed to read team_id: %v", streamErr)) } teamScore, streamErr := parametersStream.ReadUInt32LE() if streamErr != nil { - return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read team_score") + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, fmt.Sprintf("Failed to read team_score: %v", streamErr)) } isFirstUpload, streamErr := parametersStream.ReadBool() if streamErr != nil { - return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "Failed to read is_first_upload") + return nil, nex.NewError(nex.ResultCodes.Core.Unknown, fmt.Sprintf("Failed to read is_first_upload: %v", streamErr)) } pid := packet.Sender().PID()