chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
8
Assets/Scripts/SaveSystem/Data.meta
Normal file
8
Assets/Scripts/SaveSystem/Data.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dedb8f49baa023459fcd39e0d014a77
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
52
Assets/Scripts/SaveSystem/Data/GameData.cs
Normal file
52
Assets/Scripts/SaveSystem/Data/GameData.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
[System.Serializable]
|
||||
public class GameData
|
||||
{
|
||||
// Player Stats
|
||||
public int currentLevel;
|
||||
public int currentXP;
|
||||
public int overall_matches;
|
||||
public int overall_wins;
|
||||
public int overall_losses;
|
||||
public int win_streak;
|
||||
public int loss_streak;
|
||||
|
||||
// Rank Data
|
||||
public int rank_game;
|
||||
public int rank_level;
|
||||
public int prevrank_level;
|
||||
public string rank;
|
||||
public string prevRank;
|
||||
|
||||
// Mission Data
|
||||
public List<int> completed_missions;
|
||||
public List<int> completed_missions_toAnimate;
|
||||
public int animation_status;
|
||||
|
||||
// Authentication and Settings
|
||||
public bool isAuthenticated;
|
||||
public GameSettings settings;
|
||||
|
||||
// Constructor with default values
|
||||
public GameData()
|
||||
{
|
||||
currentLevel = 1;
|
||||
currentXP = 100; // Starting XP for new players
|
||||
overall_matches = 0;
|
||||
overall_wins = 0;
|
||||
overall_losses = 0;
|
||||
win_streak = 0;
|
||||
loss_streak = 0;
|
||||
rank_game = 0;
|
||||
rank_level = 1;
|
||||
prevrank_level = 1;
|
||||
rank = "Bronze";
|
||||
prevRank = "Bronze";
|
||||
completed_missions = new List<int>();
|
||||
completed_missions_toAnimate = new List<int>();
|
||||
animation_status = 0;
|
||||
isAuthenticated = false;
|
||||
settings = new GameSettings();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/SaveSystem/Data/GameData.cs.meta
Normal file
2
Assets/Scripts/SaveSystem/Data/GameData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3055bb04776e6a4a835b390bb3416bd
|
||||
8
Assets/Scripts/SaveSystem/Interfaces.meta
Normal file
8
Assets/Scripts/SaveSystem/Interfaces.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47e8022b76108146a58b1ca7dadd1d7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
5
Assets/Scripts/SaveSystem/Interfaces/IDataService.cs
Normal file
5
Assets/Scripts/SaveSystem/Interfaces/IDataService.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
public interface IDataService
|
||||
{
|
||||
bool SaveData<T>(string RelativePath, T Data, bool Encrypted);
|
||||
T LoadData<T>(string RelativePath, bool Encrypted);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee8bf3a8a594b1446a15e6b3876b927c
|
||||
176
Assets/Scripts/SaveSystem/SaveManager.cs
Normal file
176
Assets/Scripts/SaveSystem/SaveManager.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class SaveManager : MonoBehaviour
|
||||
{
|
||||
public static SaveManager Instance { get; private set; }
|
||||
private IDataService dataService;
|
||||
private const string SAVE_FILE = "/gameData.sav";
|
||||
private GameData gameData;
|
||||
private bool isSyncing = false;
|
||||
|
||||
[SerializeField] private float autoSaveInterval = 300f; // 5 minutes
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
// Move to root before DontDestroyOnLoad (required for it to work)
|
||||
transform.SetParent(null);
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
#else
|
||||
DontDestroyOnLoad(gameObject);
|
||||
#endif
|
||||
InitializeSaveSystem();
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private async void InitializeSaveSystem()
|
||||
{
|
||||
dataService = new JsonDataService();
|
||||
// Temporarily rely on local data only while auth is disabled.
|
||||
// await LoadGameDataFromServer();
|
||||
LoadLocalData();
|
||||
StartCoroutine(AutoSaveRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator AutoSaveRoutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(autoSaveInterval);
|
||||
SaveGame();
|
||||
}
|
||||
}
|
||||
|
||||
public async void SaveGame()
|
||||
{
|
||||
if (isSyncing) return;
|
||||
|
||||
isSyncing = true;
|
||||
try
|
||||
{
|
||||
// Save locally first
|
||||
SaveLocalData();
|
||||
// Then try to save to server
|
||||
// await ApiService.SaveUserGameData(gameData);
|
||||
await Task.CompletedTask; // Placeholder await while remote save is disabled.
|
||||
Debug.Log("Game saved locally");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to save locally: {e.Message}");
|
||||
// Debug.LogError($"Failed to save to server: {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isSyncing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadGameDataFromServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
// gameData = await ApiService.FetchUserGameData();
|
||||
// SaveLocalData(); // Cache server data locally
|
||||
await Task.CompletedTask; // Placeholder await while remote load is disabled.
|
||||
LoadLocalData();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
LoadLocalData(); // Fallback to local data
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveLocalData()
|
||||
{
|
||||
dataService.SaveData(SAVE_FILE, gameData, true);
|
||||
}
|
||||
|
||||
private void LoadLocalData()
|
||||
{
|
||||
try
|
||||
{
|
||||
gameData = dataService.LoadData<GameData>(SAVE_FILE, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
gameData = new GameData();
|
||||
SaveLocalData();
|
||||
}
|
||||
}
|
||||
|
||||
public GameData GetGameData()
|
||||
{
|
||||
return gameData;
|
||||
}
|
||||
|
||||
public void AddMatch()
|
||||
{
|
||||
gameData.overall_matches++;
|
||||
SaveGame();
|
||||
}
|
||||
|
||||
public void AddWin()
|
||||
{
|
||||
gameData.overall_wins++;
|
||||
gameData.win_streak++;
|
||||
gameData.loss_streak = 0;
|
||||
SaveGame();
|
||||
}
|
||||
|
||||
public void AddLoss()
|
||||
{
|
||||
gameData.overall_losses++;
|
||||
gameData.loss_streak++;
|
||||
gameData.win_streak = 0;
|
||||
SaveGame();
|
||||
}
|
||||
|
||||
public void AddCompletedMission(int missionID)
|
||||
{
|
||||
if (!gameData.completed_missions.Contains(missionID))
|
||||
{
|
||||
gameData.completed_missions.Add(missionID);
|
||||
SaveGame();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePlayerStats(int xp, int level)
|
||||
{
|
||||
gameData.currentXP = xp;
|
||||
gameData.currentLevel = level;
|
||||
SaveGame();
|
||||
}
|
||||
|
||||
public GameData LoadGame()
|
||||
{
|
||||
if (gameData == null)
|
||||
{
|
||||
LoadLocalData();
|
||||
}
|
||||
return gameData;
|
||||
}
|
||||
|
||||
// Alternative if you need async loading
|
||||
public async Task<GameData> LoadGameAsync()
|
||||
{
|
||||
if (gameData == null)
|
||||
{
|
||||
await LoadGameDataFromServer();
|
||||
}
|
||||
return gameData;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/SaveSystem/SaveManager.cs.meta
Normal file
2
Assets/Scripts/SaveSystem/SaveManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 607488e60497ce64699c50d9e0acc9d5
|
||||
8
Assets/Scripts/SaveSystem/Services.meta
Normal file
8
Assets/Scripts/SaveSystem/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dc7390557cbd874c9884f4fbe267c6e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
92
Assets/Scripts/SaveSystem/Services/ApiService.cs
Normal file
92
Assets/Scripts/SaveSystem/Services/ApiService.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public static class ApiService
|
||||
{
|
||||
private const string BASE_URL = "http://127.0.0.1:8001";
|
||||
|
||||
public static async Task<GameData> FetchUserGameData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = $"{BASE_URL}/api/game/";
|
||||
|
||||
using (UnityWebRequest request = UnityWebRequest.Get(url))
|
||||
{
|
||||
string token = PlayerPrefs.GetString("AuthToken");
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
Debug.LogError("No auth token found");
|
||||
throw new UnauthorizedAccessException("No auth token found");
|
||||
}
|
||||
|
||||
request.SetRequestHeader("Authorization", $"Token {token}");
|
||||
request.SetRequestHeader("Accept", "application/json");
|
||||
|
||||
var operation = request.SendWebRequest();
|
||||
while (!operation.isDone)
|
||||
await Task.Yield();
|
||||
|
||||
Debug.Log($"Response Code: {request.responseCode}");
|
||||
Debug.Log($"Response: {request.downloadHandler.text}");
|
||||
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
string jsonResponse = request.downloadHandler.text;
|
||||
Debug.Log($"Received data: {jsonResponse}");
|
||||
return JsonConvert.DeserializeObject<GameData>(jsonResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"API Error: {request.responseCode} - {request.error}");
|
||||
throw new Exception($"Server error: {request.error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to fetch game data: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SaveUserGameData(GameData gameData)
|
||||
{
|
||||
string token = PlayerPrefs.GetString("AuthToken");
|
||||
string jsonData = JsonConvert.SerializeObject(gameData);
|
||||
|
||||
using (UnityWebRequest request = new UnityWebRequest($"{BASE_URL}/api/game/save", "POST"))
|
||||
{
|
||||
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData);
|
||||
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Authorization", $"Token {token}");
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
var operation = request.SendWebRequest();
|
||||
while (!operation.isDone)
|
||||
await Task.Yield();
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
if (request.responseCode == 401)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Authentication failed");
|
||||
}
|
||||
throw new Exception($"Failed to save: {request.error}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to save game data: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/SaveSystem/Services/ApiService.cs.meta
Normal file
2
Assets/Scripts/SaveSystem/Services/ApiService.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba8e7ec37c2833f45bf7c48206d8e110
|
||||
80
Assets/Scripts/SaveSystem/Services/JsonDataService.cs
Normal file
80
Assets/Scripts/SaveSystem/Services/JsonDataService.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public class JsonDataService : IDataService
|
||||
{
|
||||
public bool SaveData<T>(string relativePath, T data, bool encrypted)
|
||||
{
|
||||
string path = Application.persistentDataPath + relativePath;
|
||||
try
|
||||
{
|
||||
string jsonData = JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
if (encrypted)
|
||||
{
|
||||
jsonData = EncryptDecrypt(jsonData);
|
||||
}
|
||||
using (FileStream stream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(stream))
|
||||
{
|
||||
writer.Write(jsonData);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to save data: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public T LoadData<T>(string relativePath, bool encrypted)
|
||||
{
|
||||
string path = Application.persistentDataPath + relativePath;
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot load file at {path}. File does not exist!");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string jsonData = "";
|
||||
using (FileStream stream = new FileStream(path, FileMode.Open))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
jsonData = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
if (encrypted)
|
||||
{
|
||||
jsonData = EncryptDecrypt(jsonData);
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(jsonData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to load data: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string EncryptDecrypt(string data)
|
||||
{
|
||||
// Simple XOR encryption (replace with more secure method for production)
|
||||
string key = "YOUR_ENCRYPTION_KEY";
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
result.Append((char)(data[i] ^ key[i % key.Length]));
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9317a7208a9f0ac40b2962d943b9bbc9
|
||||
Reference in New Issue
Block a user