Move endpoint categories into their own packages
parent
522ce9f4fa
commit
1f0f38d38e
@ -1,141 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
)
|
||||
|
||||
const (
|
||||
UUIDSize = 16
|
||||
TokenSize = 32
|
||||
)
|
||||
|
||||
var isValidUsername = regexp.MustCompile(`^\w{1,16}$`).MatchString
|
||||
|
||||
type AccountInfoResponse struct {
|
||||
Username string `json:"username"`
|
||||
LastSessionSlot int `json:"lastSessionSlot"`
|
||||
}
|
||||
|
||||
// /account/info - get account info
|
||||
func handleAccountInfo(username string, uuid []byte) (AccountInfoResponse, error) {
|
||||
var latestSave time.Time
|
||||
latestSaveID := -1
|
||||
for id := range sessionSlotCount {
|
||||
fileName := "session"
|
||||
if id != 0 {
|
||||
fileName += strconv.Itoa(id)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(fmt.Sprintf("userdata/%x/%s.pzs", uuid, fileName))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if stat.ModTime().After(latestSave) {
|
||||
latestSave = stat.ModTime()
|
||||
latestSaveID = id
|
||||
}
|
||||
}
|
||||
|
||||
return AccountInfoResponse{Username: username, LastSessionSlot: latestSaveID}, nil
|
||||
}
|
||||
|
||||
type AccountRegisterRequest GenericAuthRequest
|
||||
|
||||
// /account/register - register account
|
||||
func handleAccountRegister(request AccountRegisterRequest) error {
|
||||
if !isValidUsername(request.Username) {
|
||||
return fmt.Errorf("invalid username")
|
||||
}
|
||||
|
||||
if len(request.Password) < 6 {
|
||||
return fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
uuid := make([]byte, UUIDSize)
|
||||
_, err := rand.Read(uuid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate uuid: %s", err)
|
||||
}
|
||||
|
||||
salt := make([]byte, ArgonSaltSize)
|
||||
_, err = rand.Read(salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf(fmt.Sprintf("failed to generate salt: %s", err))
|
||||
}
|
||||
|
||||
err = db.AddAccountRecord(uuid, request.Username, deriveArgon2IDKey([]byte(request.Password), salt), salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add account record: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AccountLoginRequest GenericAuthRequest
|
||||
type AccountLoginResponse GenericAuthResponse
|
||||
|
||||
// /account/login - log into account
|
||||
func handleAccountLogin(request AccountLoginRequest) (AccountLoginResponse, error) {
|
||||
if !isValidUsername(request.Username) {
|
||||
return AccountLoginResponse{}, fmt.Errorf("invalid username")
|
||||
}
|
||||
|
||||
if len(request.Password) < 6 {
|
||||
return AccountLoginResponse{}, fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
key, salt, err := db.FetchAccountKeySaltFromUsername(request.Username)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return AccountLoginResponse{}, fmt.Errorf("account doesn't exist")
|
||||
}
|
||||
|
||||
return AccountLoginResponse{}, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(key, deriveArgon2IDKey([]byte(request.Password), salt)) {
|
||||
return AccountLoginResponse{}, fmt.Errorf("password doesn't match")
|
||||
}
|
||||
|
||||
token := make([]byte, TokenSize)
|
||||
_, err = rand.Read(token)
|
||||
if err != nil {
|
||||
return AccountLoginResponse{}, fmt.Errorf("failed to generate token: %s", err)
|
||||
}
|
||||
|
||||
err = db.AddAccountSession(request.Username, token)
|
||||
if err != nil {
|
||||
return AccountLoginResponse{}, fmt.Errorf("failed to add account session")
|
||||
}
|
||||
|
||||
return AccountLoginResponse{Token: base64.StdEncoding.EncodeToString(token)}, nil
|
||||
}
|
||||
|
||||
// /account/logout - log out of account
|
||||
func handleAccountLogout(token []byte) error {
|
||||
if len(token) != TokenSize {
|
||||
return fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
err := db.RemoveSessionFromToken(token)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to remove account session")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
type GenericAuthRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type GenericAuthResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
const (
|
||||
ArgonTime = 1
|
||||
ArgonMemory = 256 * 1024
|
||||
ArgonThreads = 4
|
||||
ArgonKeySize = 32
|
||||
ArgonSaltSize = 16
|
||||
)
|
||||
|
||||
var isValidUsername = regexp.MustCompile(`^\w{1,16}$`).MatchString
|
||||
|
||||
func deriveArgon2IDKey(password, salt []byte) []byte {
|
||||
return argon2.IDKey(password, salt, ArgonTime, ArgonMemory, ArgonThreads, ArgonKeySize)
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
type InfoResponse struct {
|
||||
Username string `json:"username"`
|
||||
LastSessionSlot int `json:"lastSessionSlot"`
|
||||
}
|
||||
|
||||
// /account/info - get account info
|
||||
func Info(username string, uuid []byte) (InfoResponse, error) {
|
||||
var latestSave time.Time
|
||||
latestSaveID := -1
|
||||
for id := range defs.SessionSlotCount {
|
||||
fileName := "session"
|
||||
if id != 0 {
|
||||
fileName += strconv.Itoa(id)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(fmt.Sprintf("userdata/%x/%s.pzs", uuid, fileName))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if stat.ModTime().After(latestSave) {
|
||||
latestSave = stat.ModTime()
|
||||
latestSaveID = id
|
||||
}
|
||||
}
|
||||
|
||||
return InfoResponse{Username: username, LastSessionSlot: latestSaveID}, nil
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
)
|
||||
|
||||
type LoginRequest GenericAuthRequest
|
||||
type LoginResponse GenericAuthResponse
|
||||
|
||||
// /account/login - log into account
|
||||
func Login(request LoginRequest) (LoginResponse, error) {
|
||||
if !isValidUsername(request.Username) {
|
||||
return LoginResponse{}, fmt.Errorf("invalid username")
|
||||
}
|
||||
|
||||
if len(request.Password) < 6 {
|
||||
return LoginResponse{}, fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
key, salt, err := db.FetchAccountKeySaltFromUsername(request.Username)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return LoginResponse{}, fmt.Errorf("account doesn't exist")
|
||||
}
|
||||
|
||||
return LoginResponse{}, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(key, deriveArgon2IDKey([]byte(request.Password), salt)) {
|
||||
return LoginResponse{}, fmt.Errorf("password doesn't match")
|
||||
}
|
||||
|
||||
token := make([]byte, TokenSize)
|
||||
_, err = rand.Read(token)
|
||||
if err != nil {
|
||||
return LoginResponse{}, fmt.Errorf("failed to generate token: %s", err)
|
||||
}
|
||||
|
||||
err = db.AddAccountSession(request.Username, token)
|
||||
if err != nil {
|
||||
return LoginResponse{}, fmt.Errorf("failed to add account session")
|
||||
}
|
||||
|
||||
return LoginResponse{Token: base64.StdEncoding.EncodeToString(token)}, nil
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
)
|
||||
|
||||
// /account/logout - log out of account
|
||||
func Logout(token []byte) error {
|
||||
if len(token) != TokenSize {
|
||||
return fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
err := db.RemoveSessionFromToken(token)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to remove account session")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
)
|
||||
|
||||
const (
|
||||
UUIDSize = 16
|
||||
TokenSize = 32
|
||||
)
|
||||
|
||||
type RegisterRequest GenericAuthRequest
|
||||
|
||||
// /account/register - register account
|
||||
func Register(request RegisterRequest) error {
|
||||
if !isValidUsername(request.Username) {
|
||||
return fmt.Errorf("invalid username")
|
||||
}
|
||||
|
||||
if len(request.Password) < 6 {
|
||||
return fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
uuid := make([]byte, UUIDSize)
|
||||
_, err := rand.Read(uuid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate uuid: %s", err)
|
||||
}
|
||||
|
||||
salt := make([]byte, ArgonSaltSize)
|
||||
_, err = rand.Read(salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf(fmt.Sprintf("failed to generate salt: %s", err))
|
||||
}
|
||||
|
||||
err = db.AddAccountRecord(uuid, request.Username, deriveArgon2IDKey([]byte(request.Password), salt), salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add account record: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package api
|
||||
|
||||
import "golang.org/x/crypto/argon2"
|
||||
|
||||
const (
|
||||
ArgonTime = 1
|
||||
ArgonMemory = 256 * 1024
|
||||
ArgonThreads = 4
|
||||
ArgonKeySize = 32
|
||||
ArgonSaltSize = 16
|
||||
)
|
||||
|
||||
func deriveArgon2IDKey(password, salt []byte) []byte {
|
||||
return argon2.IDKey(password, salt, ArgonTime, ArgonMemory, ArgonThreads, ArgonKeySize)
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package daily
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
// /daily/rankings - fetch daily rankings
|
||||
func Rankings(uuid []byte, category, page int) ([]defs.DailyRanking, error) {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
rankings, err := db.FetchRankings(category, page)
|
||||
if err != nil {
|
||||
log.Print("failed to retrieve rankings")
|
||||
}
|
||||
|
||||
return rankings, nil
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package daily
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
)
|
||||
|
||||
// /daily/rankingpagecount - fetch daily ranking page count
|
||||
func RankingPageCount(category int) (int, error) {
|
||||
pageCount, err := db.FetchRankingPageCount(category)
|
||||
if err != nil {
|
||||
log.Print("failed to retrieve ranking page count")
|
||||
}
|
||||
|
||||
return pageCount, nil
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
const sessionSlotCount = 3
|
||||
|
||||
// /savedata/get - get save data
|
||||
func handleSavedataGet(uuid []byte, datatype, slot int) (any, error) {
|
||||
switch datatype {
|
||||
case 0: // System
|
||||
system, err := readSystemSaveData(uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compensations, err := db.FetchAndClaimAccountCompensations(uuid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch compensations: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range compensations {
|
||||
typeKey := strconv.Itoa(k)
|
||||
system.VoucherCounts[typeKey] += v
|
||||
}
|
||||
|
||||
return system, nil
|
||||
case 1: // Session
|
||||
if slot < 0 || slot >= sessionSlotCount {
|
||||
return nil, fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
session, err := readSessionSaveData(uuid, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid data type")
|
||||
}
|
||||
}
|
||||
|
||||
// /savedata/update - update save data
|
||||
func handleSavedataUpdate(uuid []byte, slot int, save any) error {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
hexUUID := hex.EncodeToString(uuid)
|
||||
|
||||
switch save := save.(type) {
|
||||
case defs.SystemSaveData: // System
|
||||
if save.TrainerId == 0 && save.SecretId == 0 {
|
||||
return fmt.Errorf("invalid system data")
|
||||
}
|
||||
|
||||
err = db.UpdateAccountStats(uuid, save.GameStats, save.VoucherCounts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update account stats: %s", err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll("userdata/"+hexUUID, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create userdata folder: %s", err)
|
||||
}
|
||||
|
||||
file, err := os.OpenFile("userdata/"+hexUUID+"/system.pzs", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open save file for writing: %s", err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
zstdEncoder, err := zstd.NewWriter(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zstd encoder: %s", err)
|
||||
}
|
||||
|
||||
defer zstdEncoder.Close()
|
||||
|
||||
err = gob.NewEncoder(zstdEncoder).Encode(save)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize save: %s", err)
|
||||
}
|
||||
|
||||
db.DeleteClaimedAccountCompensations(uuid)
|
||||
case defs.SessionSaveData: // Session
|
||||
if slot < 0 || slot >= sessionSlotCount {
|
||||
return fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.MkdirAll("userdata/"+hexUUID, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf(fmt.Sprintf("failed to create userdata folder: %s", err))
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(fmt.Sprintf("userdata/%s/%s.pzs", hexUUID, fileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open save file for writing: %s", err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
zstdEncoder, err := zstd.NewWriter(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zstd encoder: %s", err)
|
||||
}
|
||||
|
||||
defer zstdEncoder.Close()
|
||||
|
||||
err = gob.NewEncoder(zstdEncoder).Encode(save)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize save: %s", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid data type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// /savedata/delete - delete save data
|
||||
func handleSavedataDelete(uuid []byte, datatype, slot int) error {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
hexUUID := hex.EncodeToString(uuid)
|
||||
|
||||
switch datatype {
|
||||
case 0: // System
|
||||
err := os.Remove("userdata/" + hexUUID + "/system.pzs")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
case 1: // Session
|
||||
if slot < 0 || slot >= sessionSlotCount {
|
||||
return fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.Remove(fmt.Sprintf("userdata/%s/%s.pzs", hexUUID, fileName))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid data type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SavedataClearResponse struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// /savedata/clear - mark session save data as cleared and delete
|
||||
func handleSavedataClear(uuid []byte, slot int, save defs.SessionSaveData) (SavedataClearResponse, error) {
|
||||
var response SavedataClearResponse
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
if slot < 0 || slot >= sessionSlotCount {
|
||||
return response, fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
sessionCompleted := validateSessionCompleted(save)
|
||||
|
||||
if save.GameMode == 3 && save.Seed == dailyRunSeed {
|
||||
waveCompleted := save.WaveIndex
|
||||
if !sessionCompleted {
|
||||
waveCompleted--
|
||||
}
|
||||
err = db.AddOrUpdateAccountDailyRun(uuid, save.Score, waveCompleted)
|
||||
if err != nil {
|
||||
log.Printf("failed to add or update daily run record: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if sessionCompleted {
|
||||
response.Success, err = db.TryAddSeedCompletion(uuid, save.Seed, int(save.GameMode))
|
||||
if err != nil {
|
||||
log.Printf("failed to mark seed as completed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.Remove(fmt.Sprintf("userdata/%s/%s.pzs", hex.EncodeToString(uuid), fileName))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return response, fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
type ClearResponse struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// /savedata/clear - mark session save data as cleared and delete
|
||||
func Clear(uuid []byte, slot int, seed string, save defs.SessionSaveData) (ClearResponse, error) {
|
||||
var response ClearResponse
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return response, fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
sessionCompleted := validateSessionCompleted(save)
|
||||
|
||||
if save.GameMode == 3 && save.Seed == seed {
|
||||
waveCompleted := save.WaveIndex
|
||||
if !sessionCompleted {
|
||||
waveCompleted--
|
||||
}
|
||||
err = db.AddOrUpdateAccountDailyRun(uuid, save.Score, waveCompleted)
|
||||
if err != nil {
|
||||
log.Printf("failed to add or update daily run record: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if sessionCompleted {
|
||||
response.Success, err = db.TryAddSeedCompletion(uuid, save.Seed, int(save.GameMode))
|
||||
if err != nil {
|
||||
log.Printf("failed to mark seed as completed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.Remove(fmt.Sprintf("userdata/%s/%s.pzs", hex.EncodeToString(uuid), fileName))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return response, fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package api
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
@ -0,0 +1,48 @@
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
// /savedata/delete - delete save data
|
||||
func Delete(uuid []byte, datatype, slot int) error {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
hexUUID := hex.EncodeToString(uuid)
|
||||
|
||||
switch datatype {
|
||||
case 0: // System
|
||||
err := os.Remove("userdata/" + hexUUID + "/system.pzs")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
case 1: // Session
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.Remove(fmt.Sprintf("userdata/%s/%s.pzs", hexUUID, fileName))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete save file: %s", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid data type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
// /savedata/get - get save data
|
||||
func Get(uuid []byte, datatype, slot int) (any, error) {
|
||||
switch datatype {
|
||||
case 0: // System
|
||||
system, err := readSystemSaveData(uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compensations, err := db.FetchAndClaimAccountCompensations(uuid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch compensations: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range compensations {
|
||||
typeKey := strconv.Itoa(k)
|
||||
system.VoucherCounts[typeKey] += v
|
||||
}
|
||||
|
||||
return system, nil
|
||||
case 1: // Session
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return nil, fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
session, err := readSessionSaveData(uuid, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid data type")
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/pagefaultgames/pokerogue-server/db"
|
||||
"github.com/pagefaultgames/pokerogue-server/defs"
|
||||
)
|
||||
|
||||
// /savedata/update - update save data
|
||||
func Update(uuid []byte, slot int, save any) error {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
if err != nil {
|
||||
log.Print("failed to update account last activity")
|
||||
}
|
||||
|
||||
hexUUID := hex.EncodeToString(uuid)
|
||||
|
||||
switch save := save.(type) {
|
||||
case defs.SystemSaveData: // System
|
||||
if save.TrainerId == 0 && save.SecretId == 0 {
|
||||
return fmt.Errorf("invalid system data")
|
||||
}
|
||||
|
||||
err = db.UpdateAccountStats(uuid, save.GameStats, save.VoucherCounts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update account stats: %s", err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll("userdata/"+hexUUID, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create userdata folder: %s", err)
|
||||
}
|
||||
|
||||
file, err := os.OpenFile("userdata/"+hexUUID+"/system.pzs", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open save file for writing: %s", err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
zstdEncoder, err := zstd.NewWriter(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zstd encoder: %s", err)
|
||||
}
|
||||
|
||||
defer zstdEncoder.Close()
|
||||
|
||||
err = gob.NewEncoder(zstdEncoder).Encode(save)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize save: %s", err)
|
||||
}
|
||||
|
||||
db.DeleteClaimedAccountCompensations(uuid)
|
||||
case defs.SessionSaveData: // Session
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
fileName := "session"
|
||||
if slot != 0 {
|
||||
fileName += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
err = os.MkdirAll("userdata/"+hexUUID, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf(fmt.Sprintf("failed to create userdata folder: %s", err))
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(fmt.Sprintf("userdata/%s/%s.pzs", hexUUID, fileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open save file for writing: %s", err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
zstdEncoder, err := zstd.NewWriter(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zstd encoder: %s", err)
|
||||
}
|
||||
|
||||
defer zstdEncoder.Close()
|
||||
|
||||
err = gob.NewEncoder(zstdEncoder).Encode(save)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize save: %s", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid data type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue