From b463a640037e90fc60ab1585375f4d6edae23797 Mon Sep 17 00:00:00 2001
From: Up <upcraftedlp@gmail.com>
Date: Tue, 7 May 2024 22:31:55 +0200
Subject: [PATCH] finish migration code

---
 api/savedata/clear.go  | 17 +++------
 api/savedata/common.go |  4 +--
 api/savedata/delete.go | 22 ++----------
 db/db.go               | 81 +++++++++++++++++++++++++++++++++++++++---
 db/savedata.go         | 33 +++++++++--------
 5 files changed, 102 insertions(+), 55 deletions(-)

diff --git a/api/savedata/clear.go b/api/savedata/clear.go
index 1aa988e..054896b 100644
--- a/api/savedata/clear.go
+++ b/api/savedata/clear.go
@@ -18,14 +18,10 @@
 package savedata
 
 import (
-	"encoding/hex"
 	"fmt"
-	"log"
-	"os"
-	"strconv"
-
 	"github.com/pagefaultgames/rogueserver/db"
 	"github.com/pagefaultgames/rogueserver/defs"
+	"log"
 )
 
 type ClearResponse struct {
@@ -66,14 +62,9 @@ func Clear(uuid []byte, slot int, seed string, save defs.SessionSaveData) (Clear
 		}
 	}
 
-	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)
+	err = db.DeleteSessionSaveData(uuid, slot)
+	if err != nil {
+		log.Printf("failed to delete session save data: %s", err)
 	}
 
 	return response, nil
diff --git a/api/savedata/common.go b/api/savedata/common.go
index 7c03c54..72e1292 100644
--- a/api/savedata/common.go
+++ b/api/savedata/common.go
@@ -28,7 +28,7 @@ import (
 	"github.com/pagefaultgames/rogueserver/defs"
 )
 
-func legacyReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
+func LegacyReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
 	var system defs.SystemSaveData
 
 	file, err := os.Open("userdata/" + hex.EncodeToString(uuid) + "/system.pzs")
@@ -53,7 +53,7 @@ func legacyReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
 	return system, nil
 }
 
-func legacyReadSessionSaveData(uuid []byte, slotID int) (defs.SessionSaveData, error) {
+func LegacyReadSessionSaveData(uuid []byte, slotID int) (defs.SessionSaveData, error) {
 	var session defs.SessionSaveData
 
 	fileName := "session"
diff --git a/api/savedata/delete.go b/api/savedata/delete.go
index eaf6a0e..77183d0 100644
--- a/api/savedata/delete.go
+++ b/api/savedata/delete.go
@@ -19,12 +19,9 @@ package savedata
 
 import (
 	"fmt"
-	"log"
-	"os"
-	"strconv"
-
 	"github.com/pagefaultgames/rogueserver/db"
 	"github.com/pagefaultgames/rogueserver/defs"
+	"log"
 )
 
 // /savedata/delete - delete save data
@@ -36,27 +33,14 @@ func Delete(uuid []byte, datatype, slot int) error {
 
 	switch datatype {
 	case 0: // System
-		err := os.Remove(fmt.Sprintf("userdata/%x/system.pzs", uuid))
-		if err != nil && !os.IsNotExist(err) {
-			return fmt.Errorf("failed to delete save file: %s", err)
-		}
+		return db.DeleteSystemSaveData(uuid)
 	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/%x/%s.pzs", uuid, fileName))
-		if err != nil && !os.IsNotExist(err) {
-			return fmt.Errorf("failed to delete save file: %s", err)
-		}
+		return db.DeleteSessionSaveData(uuid, slot)
 	default:
 		return fmt.Errorf("invalid data type")
 	}
-
-	return nil
 }
diff --git a/db/db.go b/db/db.go
index 070a81c..55f074c 100644
--- a/db/db.go
+++ b/db/db.go
@@ -19,9 +19,12 @@ package db
 
 import (
 	"database/sql"
+	"encoding/hex"
 	"fmt"
-
 	_ "github.com/go-sql-driver/mysql"
+	"github.com/pagefaultgames/rogueserver/api/savedata"
+	"log"
+	"os"
 )
 
 var handle *sql.DB
@@ -36,10 +39,80 @@ func Init(username, password, protocol, address, database string) error {
 
 	handle.SetMaxOpenConns(1000)
 
-	handle.Exec("CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data BLOB, timestamp TIMESTAMP)")
-	handle.Exec("CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16) PRIMARY KEY, data BLOB, timestamp TIMESTAMP)")
+	tx, err := handle.Begin()
+	if err != nil {
+		panic(err)
+	}
+	tx.Exec("CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data BLOB, timestamp TIMESTAMP)")
+	tx.Exec("CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16) PRIMARY KEY, slot TINYINT, data BLOB, timestamp TIMESTAMP)")
+	tx.Exec("CREATE CREATE UNIQUE INDEX IF NOT EXISTS sessionSaveDataByIdAndSlot ON sessionSaveData (uuid, slot)")
+	err = tx.Commit()
+	if err != nil {
+		panic(err)
+	}
 
-	//TODO iterate "userdata/accountid" and create rows for each account
+	// TODO temp code
+	entries, err := os.ReadDir("userdata")
+	if err != nil {
+		log.Fatalln(err)
+		return nil
+	}
+
+	for _, entry := range entries {
+		if !entry.IsDir() {
+			continue
+		}
+
+		uuidString := entry.Name()
+		uuid, err := hex.DecodeString(uuidString)
+		if err != nil {
+			log.Printf("failed to decode uuid: %s", err)
+			continue
+		}
+
+		// store new system data
+		systemData, err := savedata.LegacyReadSystemSaveData(uuid)
+		if err != nil {
+			log.Printf("failed to read system save data for %v: %s", uuidString, err)
+			continue
+		}
+
+		err = StoreSystemSaveData(uuid, systemData)
+		if err != nil {
+			log.Fatalf("failed to store system save data for %v: %s\n", uuidString, err)
+			continue
+		}
+
+		// delete old system data
+		err = os.Remove("userdata/" + uuidString + "/system.pzs")
+		if err != nil {
+			log.Fatalf("failed to remove legacy system save data for %v: %s", uuidString, err)
+		}
+
+		for i := 0; i < 5; i++ {
+			sessionData, err := savedata.LegacyReadSessionSaveData(uuid, i)
+			if err != nil {
+				log.Printf("failed to read session save data %v for %v: %s", i, uuidString, err)
+				continue
+			}
+
+			// store new session data
+			err = StoreSessionSaveData(uuid, sessionData, i)
+			if err != nil {
+				log.Fatalf("failed to store session save data for %v: %s\n", uuidString, err)
+			}
+
+			// delete old session data
+			filename := "session"
+			if i != 0 {
+				filename += fmt.Sprintf("%d", i)
+			}
+			err = os.Remove(fmt.Sprintf("userdata/%s/%s.pzs", uuidString, filename))
+			if err != nil {
+				log.Fatalf("failed to remove legacy session save data %v for %v: %s", i, uuidString, err)
+			}
+		}
+	}
 
 	return nil
 }
diff --git a/db/savedata.go b/db/savedata.go
index 13ba6d4..21a1b6f 100644
--- a/db/savedata.go
+++ b/db/savedata.go
@@ -40,21 +40,11 @@ func TryAddDailyRunCompletion(uuid []byte, seed string, mode int) (bool, error)
 	return true, nil
 }
 
-type DbSystemSaveData struct {
-	uuid []byte
-	data []byte
-}
-
-type DbSessionSaveData struct {
-	uuid []byte
-	data []byte
-}
-
 func ReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
-	var data DbSystemSaveData
-	err := handle.QueryRow("SELECT uuid, data FROM systemSaveData WHERE uuid = ?", uuid).Scan(&data)
+	var data []byte
+	err := handle.QueryRow("SELECT data FROM systemSaveData WHERE uuid = ?", uuid).Scan(&data)
 
-	reader := bytes.NewReader(data.data)
+	reader := bytes.NewReader(data)
 	system := defs.SystemSaveData{}
 	err = gob.NewDecoder(reader).Decode(&system)
 	return system, err
@@ -73,12 +63,16 @@ func StoreSystemSaveData(uuid []byte, data defs.SystemSaveData) error {
 	return err
 }
 
+func DeleteSystemSaveData(uuid []byte) error {
+	_, err := handle.Exec("DELETE FROM systemSaveData WHERE uuid = ?", uuid)
+	return err
+}
+
 func ReadSessionSaveData(uuid []byte, slot int) (defs.SessionSaveData, error) {
+	var data []byte
+	err := handle.QueryRow("SELECT data FROM sessionSaveData WHERE uuid = ? AND slot = ?", uuid, slot).Scan(&data)
 
-	var data DbSystemSaveData
-	err := handle.QueryRow("SELECT uuid, data FROM sessionSaveData WHERE uuid = ?", uuid).Scan(&data)
-
-	reader := bytes.NewReader(data.data)
+	reader := bytes.NewReader(data)
 	save := defs.SessionSaveData{}
 	err = gob.NewDecoder(reader).Decode(&save)
 
@@ -97,3 +91,8 @@ func StoreSessionSaveData(uuid []byte, data defs.SessionSaveData, slot int) erro
 
 	return err
 }
+
+func DeleteSessionSaveData(uuid []byte, slot int) error {
+	_, err := handle.Exec("DELETE FROM sessionSaveData WHERE uuid = ? AND slot = ?", uuid, slot)
+	return err
+}