mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-08 00:25:30 -05:00
Table bot changes
This commit is contained in:
105
api/pinfo.go
Normal file
105
api/pinfo.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"wwfc/database"
|
||||
)
|
||||
|
||||
type PinfoRequestSpec struct {
|
||||
Secret string `json:"secret"`
|
||||
ProfileID uint32 `json:"pid"`
|
||||
}
|
||||
|
||||
type PinfoResponse struct {
|
||||
User database.User `json:"User"`
|
||||
Success bool `json:"Success"`
|
||||
Error string `json:"Error"`
|
||||
}
|
||||
|
||||
func HandlePinfo(w http.ResponseWriter, r *http.Request) {
|
||||
var response PinfoResponse
|
||||
var statusCode int
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
response, statusCode = handlePinfoImpl(r)
|
||||
} else if r.Method == http.MethodOptions {
|
||||
statusCode = http.StatusNoContent
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
} else {
|
||||
statusCode = http.StatusMethodNotAllowed
|
||||
w.Header().Set("Allow", "POST")
|
||||
response = PinfoResponse{
|
||||
Success: false,
|
||||
Error: "Incorrect request. POST only.",
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
var jsonData []byte
|
||||
if statusCode != http.StatusNoContent {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
jsonData, _ = json.Marshal(response)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(jsonData)))
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(jsonData)
|
||||
}
|
||||
|
||||
func handlePinfoImpl(r *http.Request) (PinfoResponse, int) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return PinfoResponse{
|
||||
Success: false,
|
||||
Error: "Unable to read request body",
|
||||
}, http.StatusBadRequest
|
||||
}
|
||||
|
||||
var req PinfoRequestSpec
|
||||
err = json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
return PinfoResponse{
|
||||
Success: false,
|
||||
Error: err.Error(),
|
||||
}, http.StatusBadRequest
|
||||
}
|
||||
|
||||
if req.ProfileID == 0 {
|
||||
return PinfoResponse{
|
||||
Success: false,
|
||||
Error: "Profile ID missing or 0 in request",
|
||||
}, http.StatusBadRequest
|
||||
}
|
||||
|
||||
realUser, ok := database.GetProfile(pool, ctx, req.ProfileID)
|
||||
if !ok {
|
||||
return PinfoResponse{
|
||||
User: database.User{},
|
||||
Success: false,
|
||||
Error: "Failed to find user in the database",
|
||||
}, http.StatusInternalServerError
|
||||
}
|
||||
|
||||
user := realUser
|
||||
if apiSecret == "" || req.Secret != apiSecret {
|
||||
// Invalid or missing secret: return only the public-safe subset.
|
||||
user = database.User{
|
||||
ProfileId: realUser.ProfileId,
|
||||
Restricted: realUser.Restricted,
|
||||
BanReason: realUser.BanReason,
|
||||
OpenHost: realUser.OpenHost,
|
||||
LastInGameSn: realUser.LastInGameSn,
|
||||
}
|
||||
}
|
||||
|
||||
return PinfoResponse{
|
||||
User: user,
|
||||
Success: true,
|
||||
Error: "",
|
||||
}, http.StatusOK
|
||||
}
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -15,13 +18,14 @@ const (
|
||||
UpdateUserTable = `UPDATE users SET firstname = CASE WHEN $3 THEN $2 ELSE firstname END, lastname = CASE WHEN $5 THEN $4 ELSE lastname END, open_host = CASE WHEN $7 THEN $6 ELSE open_host END WHERE profile_id = $1`
|
||||
UpdateUserProfileID = `UPDATE users SET profile_id = $3 WHERE user_id = $1 AND gsbrcd = $2`
|
||||
UpdateUserNGDeviceID = `UPDATE users SET ng_device_id = $2 WHERE profile_id = $1`
|
||||
GetUser = `SELECT user_id, gsbrcd, email, unique_nick, firstname, lastname, open_host, last_ip_address, last_ingamesn FROM users WHERE profile_id = $1`
|
||||
GetUser = `SELECT user_id, gsbrcd, ng_device_id, email, unique_nick, firstname, lastname, has_ban, ban_reason, open_host, last_ingamesn, last_ip_address, discord_id, ban_moderator, ban_reason_hidden, ban_issued, ban_expires FROM users WHERE profile_id = $1`
|
||||
ClearProfileQuery = `DELETE FROM users WHERE profile_id = $1 RETURNING user_id, gsbrcd, email, unique_nick, firstname, lastname, open_host, last_ip_address, last_ingamesn`
|
||||
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
|
||||
IsProfileIDInUse = `SELECT EXISTS(SELECT 1 FROM users WHERE profile_id = $1)`
|
||||
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
|
||||
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, last_ip_address FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, discord_id, last_ip_address FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
UpdateUserLastIPAddress = `UPDATE users SET last_ip_address = $2, last_ingamesn = $3 WHERE profile_id = $1`
|
||||
UpdateDiscordID = `UPDATE users SET discord_id = $2 WHERE profile_id = $1`
|
||||
UpdateUserBan = `UPDATE users SET has_ban = true, ban_issued = $2, ban_expires = $3, ban_reason = $4, ban_reason_hidden = $5, ban_moderator = $6, ban_tos = $7 WHERE profile_id = $1`
|
||||
DisableUserBan = `UPDATE users SET has_ban = false WHERE profile_id = $1`
|
||||
|
||||
@@ -29,6 +33,15 @@ const (
|
||||
UpdateMKWFriendInfoQuery = `UPDATE users SET mariokartwii_friend_info = $2 WHERE profile_id = $1`
|
||||
)
|
||||
|
||||
type LinkStage byte
|
||||
|
||||
const (
|
||||
LS_NONE LinkStage = iota
|
||||
LS_STARTED
|
||||
LS_FRIENDED
|
||||
LS_FINISHED
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ProfileId uint32
|
||||
UserId uint64
|
||||
@@ -44,6 +57,12 @@ type User struct {
|
||||
OpenHost bool
|
||||
LastInGameSn string
|
||||
LastIPAddress string
|
||||
DiscordID string
|
||||
LinkStage LinkStage
|
||||
BanModerator string
|
||||
BanReasonHidden string
|
||||
BanIssued *time.Time
|
||||
BanExpires *time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -93,6 +112,17 @@ func (user *User) UpdateProfileID(pool *pgxpool.Pool, ctx context.Context, newPr
|
||||
return err
|
||||
}
|
||||
|
||||
func (user *User) UpdateDiscordID(pool *pgxpool.Pool, ctx context.Context, discordID string) error {
|
||||
_, err := pool.Exec(ctx, UpdateDiscordID, user.ProfileId, discordID)
|
||||
if err == nil {
|
||||
user.DiscordID = discordID
|
||||
} else {
|
||||
logging.Error("DB", "Failed to persist DiscordID", aurora.Cyan(discordID), "for profile", aurora.Cyan(user.ProfileId), "error:", aurora.Cyan(err))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUniqueUserID() uint64 {
|
||||
// Not guaranteed unique but doesn't matter in practice if multiple people have the same user ID.
|
||||
return uint64(rand.Int63n(0x80000000000))
|
||||
@@ -128,12 +158,74 @@ func (user *User) UpdateProfile(pool *pgxpool.Pool, ctx context.Context, data ma
|
||||
func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User, bool) {
|
||||
user := User{}
|
||||
row := pool.QueryRow(ctx, GetUser, profileId)
|
||||
err := row.Scan(&user.UserId, &user.GsbrCode, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName, &user.OpenHost, &user.LastIPAddress, &user.LastInGameSn)
|
||||
|
||||
var firstName *string
|
||||
var lastName *string
|
||||
var banReason *string
|
||||
var lastInGameSn *string
|
||||
var lastIPAddress *string
|
||||
var discordID *string
|
||||
var banModerator *string
|
||||
var banReasonHidden *string
|
||||
|
||||
err := row.Scan(
|
||||
&user.UserId,
|
||||
&user.GsbrCode,
|
||||
&user.NgDeviceId,
|
||||
&user.Email,
|
||||
&user.UniqueNick,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&user.Restricted,
|
||||
&banReason,
|
||||
&user.OpenHost,
|
||||
&lastInGameSn,
|
||||
&lastIPAddress,
|
||||
&discordID,
|
||||
&banModerator,
|
||||
&banReasonHidden,
|
||||
&user.BanIssued,
|
||||
&user.BanExpires,
|
||||
)
|
||||
if err != nil {
|
||||
return User{}, false
|
||||
}
|
||||
|
||||
user.ProfileId = profileId
|
||||
|
||||
if firstName != nil {
|
||||
user.FirstName = *firstName
|
||||
}
|
||||
|
||||
if lastName != nil {
|
||||
user.LastName = *lastName
|
||||
}
|
||||
|
||||
if banReason != nil {
|
||||
user.BanReason = *banReason
|
||||
}
|
||||
|
||||
if lastInGameSn != nil {
|
||||
user.LastInGameSn = *lastInGameSn
|
||||
}
|
||||
|
||||
if lastIPAddress != nil {
|
||||
user.LastIPAddress = *lastIPAddress
|
||||
}
|
||||
|
||||
if discordID != nil {
|
||||
user.DiscordID = *discordID
|
||||
user.LinkStage = LS_FINISHED
|
||||
}
|
||||
|
||||
if banModerator != nil {
|
||||
user.BanModerator = *banModerator
|
||||
}
|
||||
|
||||
if banReasonHidden != nil {
|
||||
user.BanReasonHidden = *banReasonHidden
|
||||
}
|
||||
|
||||
return user, true
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for /api/pinfo
|
||||
if r.URL.Path == "/api/pinfo" {
|
||||
api.HandlePinfo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Check for /api/stats
|
||||
if r.URL.Path == "/api/stats" {
|
||||
api.HandleStats(w, r)
|
||||
|
||||
12
qr2/group.go
12
qr2/group.go
@@ -605,12 +605,20 @@ func ProcessMKWRaceResult(profileId uint32, playerPid int, finishTimeMs int, cha
|
||||
|
||||
// Calculate delta using start/finish times
|
||||
var delta int
|
||||
if startTiming, exists := raceStartTimings[profileId]; exists {
|
||||
if finishTiming, exists := raceFinishTimings[profileId]; exists {
|
||||
if startTiming, startExists := raceStartTimings[profileId]; startExists {
|
||||
if finishTiming, finishExists := raceFinishTimings[profileId]; finishExists {
|
||||
clientElapsedTime := finishTiming.ClientTime - startTiming.ClientTime
|
||||
serverElapsedTime := finishTiming.ServerTime - startTiming.ServerTime
|
||||
delta = int(serverElapsedTime - clientElapsedTime)
|
||||
|
||||
logging.Info(moduleName, "Delta calculated:", aurora.Cyan(strconv.Itoa(delta)),
|
||||
"Client elapsed:", aurora.Cyan(strconv.FormatInt(clientElapsedTime, 10)),
|
||||
"Server elapsed:", aurora.Cyan(strconv.FormatInt(serverElapsedTime, 10)))
|
||||
} else {
|
||||
logging.Warn(moduleName, "Missing finish timing data for profile", aurora.Cyan(strconv.FormatUint(uint64(profileId), 10)))
|
||||
}
|
||||
} else {
|
||||
logging.Warn(moduleName, "Missing start timing data for profile", aurora.Cyan(strconv.FormatUint(uint64(profileId), 10)))
|
||||
}
|
||||
|
||||
// Calculate finish position based on current race results
|
||||
|
||||
Reference in New Issue
Block a user