ui基本完毕,修了一大把的bug
This commit is contained in:
@@ -0,0 +1,555 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public class PlayerProgressBackupBundle
|
||||
{
|
||||
public int version = 1;
|
||||
public long savedUtcTicks;
|
||||
public PlayerEconomyPayload economy;
|
||||
public int playerExperience;
|
||||
public float bestOverallRks;
|
||||
public PlayerSkillSaveData playerSkill;
|
||||
public AllyHeroDeployLedgerPayload allyHeroDeploy;
|
||||
public ExpBottleLedgerPayload expBottle;
|
||||
public DushMaterialLedgerPayload dushMaterial;
|
||||
public EquipmentConsumableLedgerPayload equipmentConsumable;
|
||||
public StoreOwnershipPayload storeOwnership;
|
||||
}
|
||||
|
||||
public static class PlayerProgressBackupService
|
||||
{
|
||||
private const string BackupFileName = "player_progress.bbackup";
|
||||
private static PlayerProgressBackupBundle s_cachedBundle;
|
||||
private static bool s_cacheLoaded;
|
||||
private static bool s_isWriting;
|
||||
|
||||
public static bool TryRestoreEconomy(out PlayerEconomyPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.economy == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneEconomy(bundle.economy);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveEconomy(PlayerEconomyPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.economy = CloneEconomy(payload));
|
||||
}
|
||||
|
||||
public static bool TryRestorePlayerExperience(out int experience)
|
||||
{
|
||||
experience = 0;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bundle.playerExperience <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
experience = Mathf.Max(0, bundle.playerExperience);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SavePlayerExperience(int experience)
|
||||
{
|
||||
UpdateBundle(bundle => bundle.playerExperience = Mathf.Max(0, experience));
|
||||
}
|
||||
|
||||
public static bool TryRestoreRks(out float rks)
|
||||
{
|
||||
rks = 0f;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.bestOverallRks <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rks = Mathf.Max(0f, bundle.bestOverallRks);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SaveRks(float rks)
|
||||
{
|
||||
UpdateBundle(bundle => bundle.bestOverallRks = Mathf.Max(0f, rks));
|
||||
}
|
||||
|
||||
public static bool TryRestorePlayerSkill(out PlayerSkillSaveData saveData)
|
||||
{
|
||||
saveData = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.playerSkill == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
saveData = ClonePlayerSkill(bundle.playerSkill);
|
||||
return saveData != null;
|
||||
}
|
||||
|
||||
public static void SavePlayerSkill(PlayerSkillSaveData saveData)
|
||||
{
|
||||
if (saveData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.playerSkill = ClonePlayerSkill(saveData));
|
||||
}
|
||||
|
||||
public static bool TryRestoreAllyHeroDeploy(out AllyHeroDeployLedgerPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.allyHeroDeploy == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneAllyHeroDeploy(bundle.allyHeroDeploy);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveAllyHeroDeploy(AllyHeroDeployLedgerPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.allyHeroDeploy = CloneAllyHeroDeploy(payload));
|
||||
}
|
||||
|
||||
public static bool TryRestoreExpBottle(out ExpBottleLedgerPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.expBottle == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneExpBottle(bundle.expBottle);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveExpBottle(ExpBottleLedgerPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.expBottle = CloneExpBottle(payload));
|
||||
}
|
||||
|
||||
public static bool TryRestoreDushMaterial(out DushMaterialLedgerPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.dushMaterial == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneDushMaterial(bundle.dushMaterial);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveDushMaterial(DushMaterialLedgerPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.dushMaterial = CloneDushMaterial(payload));
|
||||
}
|
||||
|
||||
public static bool TryRestoreEquipmentConsumable(out EquipmentConsumableLedgerPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.equipmentConsumable == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneEquipmentConsumable(bundle.equipmentConsumable);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveEquipmentConsumable(EquipmentConsumableLedgerPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.equipmentConsumable = CloneEquipmentConsumable(payload));
|
||||
}
|
||||
|
||||
public static bool TryRestoreStoreOwnership(out StoreOwnershipPayload payload)
|
||||
{
|
||||
payload = null;
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null || bundle.storeOwnership == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload = CloneStoreOwnership(bundle.storeOwnership);
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public static void SaveStoreOwnership(StoreOwnershipPayload payload)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateBundle(bundle => bundle.storeOwnership = CloneStoreOwnership(payload));
|
||||
}
|
||||
|
||||
private static void UpdateBundle(Action<PlayerProgressBackupBundle> mutator)
|
||||
{
|
||||
if (mutator == null || s_isWriting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerProgressBackupBundle bundle;
|
||||
if (!TryLoadBundle(out bundle) || bundle == null)
|
||||
{
|
||||
bundle = CreateDefaultBundle();
|
||||
}
|
||||
|
||||
mutator(bundle);
|
||||
bundle.savedUtcTicks = DateTime.UtcNow.Ticks;
|
||||
SaveBundle(bundle);
|
||||
}
|
||||
|
||||
private static bool TryLoadBundle(out PlayerProgressBackupBundle bundle)
|
||||
{
|
||||
if (s_cacheLoaded)
|
||||
{
|
||||
bundle = s_cachedBundle;
|
||||
return bundle != null;
|
||||
}
|
||||
|
||||
s_cacheLoaded = true;
|
||||
s_cachedBundle = null;
|
||||
|
||||
IReadOnlyList<string> candidates = SaveIdentityUtility.GetPersistentRootVariants();
|
||||
for (int i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
string root = candidates[i];
|
||||
if (string.IsNullOrWhiteSpace(root))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string path = Path.Combine(root, BackupFileName);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(path, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PlayerProgressBackupBundle loaded = JsonUtility.FromJson<PlayerProgressBackupBundle>(json);
|
||||
if (loaded == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
s_cachedBundle = loaded;
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("[PlayerProgressBackup] Failed to read backup '" + path + "': " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
bundle = s_cachedBundle;
|
||||
return bundle != null;
|
||||
}
|
||||
|
||||
private static void SaveBundle(PlayerProgressBackupBundle bundle)
|
||||
{
|
||||
if (bundle == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string root = SaveIdentityUtility.GetCanonicalPersistentRoot();
|
||||
string path = Path.Combine(root, BackupFileName);
|
||||
try
|
||||
{
|
||||
s_isWriting = true;
|
||||
Directory.CreateDirectory(root);
|
||||
string json = JsonUtility.ToJson(bundle, false);
|
||||
File.WriteAllText(path, json, Encoding.UTF8);
|
||||
s_cachedBundle = bundle;
|
||||
s_cacheLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("[PlayerProgressBackup] Failed to save backup '" + path + "': " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
s_isWriting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static PlayerProgressBackupBundle CreateDefaultBundle()
|
||||
{
|
||||
return new PlayerProgressBackupBundle
|
||||
{
|
||||
version = 1,
|
||||
savedUtcTicks = DateTime.UtcNow.Ticks
|
||||
};
|
||||
}
|
||||
|
||||
private static PlayerEconomyPayload CloneEconomy(PlayerEconomyPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PlayerEconomyPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
coins = source.coins,
|
||||
material = source.material
|
||||
};
|
||||
}
|
||||
|
||||
private static PlayerSkillSaveData ClonePlayerSkill(PlayerSkillSaveData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PlayerSkillSaveData
|
||||
{
|
||||
selectedSkillIndex = source.selectedSkillIndex,
|
||||
postMatchRewardCounter = source.postMatchRewardCounter,
|
||||
skillSwitchCooldownRemainingMatches = source.skillSwitchCooldownRemainingMatches
|
||||
};
|
||||
}
|
||||
|
||||
private static AllyHeroDeployLedgerPayload CloneAllyHeroDeploy(AllyHeroDeployLedgerPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clone = new AllyHeroDeployLedgerPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
entries = new List<AllyHeroDeployEntry>()
|
||||
};
|
||||
|
||||
if (source.entries != null)
|
||||
{
|
||||
for (int i = 0; i < source.entries.Count; i++)
|
||||
{
|
||||
AllyHeroDeployEntry entry = source.entries[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
clone.entries.Add(new AllyHeroDeployEntry
|
||||
{
|
||||
heroId = entry.heroId,
|
||||
currentExp = entry.currentExp,
|
||||
unlockedTierIndex = entry.unlockedTierIndex,
|
||||
levelLock = entry.levelLock,
|
||||
autoBreakthroughEnabled = entry.autoBreakthroughEnabled,
|
||||
deployCount = entry.deployCount,
|
||||
finishCount = entry.finishCount,
|
||||
mvpCount = entry.mvpCount,
|
||||
joinDateUtcTicks = entry.joinDateUtcTicks
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static ExpBottleLedgerPayload CloneExpBottle(ExpBottleLedgerPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clone = new ExpBottleLedgerPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
entries = new List<ExpBottleEntry>()
|
||||
};
|
||||
|
||||
if (source.entries != null)
|
||||
{
|
||||
for (int i = 0; i < source.entries.Count; i++)
|
||||
{
|
||||
ExpBottleEntry entry = source.entries[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
clone.entries.Add(new ExpBottleEntry
|
||||
{
|
||||
key = entry.key,
|
||||
count = entry.count
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static DushMaterialLedgerPayload CloneDushMaterial(DushMaterialLedgerPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clone = new DushMaterialLedgerPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
entries = new List<DushMaterialEntry>()
|
||||
};
|
||||
|
||||
if (source.entries != null)
|
||||
{
|
||||
for (int i = 0; i < source.entries.Count; i++)
|
||||
{
|
||||
DushMaterialEntry entry = source.entries[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
clone.entries.Add(new DushMaterialEntry
|
||||
{
|
||||
key = entry.key,
|
||||
count = entry.count
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static EquipmentConsumableLedgerPayload CloneEquipmentConsumable(EquipmentConsumableLedgerPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clone = new EquipmentConsumableLedgerPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
entries = new List<EquipmentConsumableEntry>()
|
||||
};
|
||||
|
||||
if (source.entries != null)
|
||||
{
|
||||
for (int i = 0; i < source.entries.Count; i++)
|
||||
{
|
||||
EquipmentConsumableEntry entry = source.entries[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
clone.entries.Add(new EquipmentConsumableEntry
|
||||
{
|
||||
key = entry.key,
|
||||
count = entry.count
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static StoreOwnershipPayload CloneStoreOwnership(StoreOwnershipPayload source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clone = new StoreOwnershipPayload
|
||||
{
|
||||
version = source.version,
|
||||
lastUpdatedUtcTicks = source.lastUpdatedUtcTicks,
|
||||
entries = new List<StoreOwnershipEntry>()
|
||||
};
|
||||
|
||||
if (source.entries != null)
|
||||
{
|
||||
for (int i = 0; i < source.entries.Count; i++)
|
||||
{
|
||||
StoreOwnershipEntry entry = source.entries[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
clone.entries.Add(new StoreOwnershipEntry
|
||||
{
|
||||
storeItemId = entry.storeItemId,
|
||||
owned = entry.owned,
|
||||
unlockedStorySonIds = entry.unlockedStorySonIds != null
|
||||
? new List<int>(entry.unlockedStorySonIds)
|
||||
: new List<int>()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user