Files
bansonic_beta_main/Assets/scripts/_runtimecache/FirstRunFactoryResetService.cs
T
2026-07-16 23:04:59 +08:00

485 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public static class FirstRunFactoryResetService
{
private const string StoreStateLegacyPath = "storeSystem_state.json";
// 明文"已初始化"标记:不依赖 deviceUniqueIdentifier / 加密,仅表示本机曾成功初始化过。
// 用途见 TryPerformFactoryResetFallback:区分"真首启"与"存档存在但暂时读不出"。
private const string InitializedMarkerFileName = ".bansonic_initialized";
private static readonly int[] DefaultTeamHeroIds = { 30201, 30202, 30203, 30204, 30205 };
private static bool executed;
private static string InitializedMarkerPath =>
Path.Combine(Application.persistentDataPath, InitializedMarkerFileName);
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
TryPerformFactoryResetFallback();
}
private static void TryPerformFactoryResetFallback()
{
if (executed)
{
return;
}
executed = true;
if (HasAnyRecoverableLocalState())
{
// 有可恢复存档:补写标记(覆盖历史版本升级上来、尚无标记的老用户),不重置。
EnsureInitializedMarker();
return;
}
// 走到这里说明"当前读不到任何可恢复存档"。必须区分两种情况:
// A) 真正首次运行:应用出厂默认(全 0)并写标记 —— 安全。
// B) 曾经玩过、但存档暂时读不出(典型:deviceUniqueIdentifier 变化致解密/验签失败):
// 此时清零 = 数据丢失灾难。宁可保留现状等待其它恢复路径,也绝不主动抹除。
if (HasInitializedMarker())
{
Debug.LogWarning("[FactoryReset] 检测到已初始化标记,但当前读不到任何存档。" +
"为避免误删既有(暂不可读)玩家数据,跳过出厂重置。");
return;
}
// 物理文件兜底探测(最后一道防线):
// HasAnyRecoverableLocalState() 依赖各 Storage 的解密/结构判断,若因根目录别名未覆盖、
// 或存档结构异常而误判为"无存档",仍可能走到出厂重置。这里直接扫描磁盘上是否存在
// 任何存档物理文件(不解密、只看存在),只要有就绝不重置,宁可等待其它恢复路径。
if (HasAnyPhysicalSaveFileOnDisk())
{
Debug.LogWarning("[FactoryReset] 磁盘上存在存档物理文件但未能读出," +
"为避免误删玩家数据,跳过出厂重置并补写初始化标记。");
EnsureInitializedMarker();
return;
}
Debug.LogWarning("[FactoryReset] 首次运行且无可恢复存档,应用出厂默认设置。");
ApplyFactoryReset();
EnsureInitializedMarker();
}
// 扫描所有可能的存档根目录(含新旧包名/公司名别名),只要磁盘上存在任意一个存档物理文件
// 就返回 true。只判存在、不解密——因此不受 deviceUniqueIdentifier 变化影响。
private static bool HasAnyPhysicalSaveFileOnDisk()
{
try
{
IReadOnlyList<string> roots = SaveIdentityUtility.GetPersistentRootVariants();
for (int i = 0; i < roots.Count; i++)
{
string root = roots[i];
if (string.IsNullOrWhiteSpace(root))
{
continue;
}
// 1) 加密存档目录 .cache_bridge:递归找任意 .dat / .bak
string vaultDir = Path.Combine(root, ".cache_bridge");
if (DirectoryHasFileWithAnyExtension(vaultDir, new[] { ".dat", ".bak" }))
{
return true;
}
// 2) 设备无关明文备份 player_progress.bbackup
if (File.Exists(Path.Combine(root, "player_progress.bbackup")))
{
return true;
}
// 3) 恢复镜像目录 .save_recovery:存在任意文件即视为有存档
string recoveryDir = Path.Combine(root, ".save_recovery");
if (DirectoryHasAnyFile(recoveryDir))
{
return true;
}
}
}
catch (Exception ex)
{
// 探测失败时采取保守策略:报告"存在存档",宁可跳过重置也不误删。
Debug.LogWarning("[FactoryReset] 物理存档探测异常,保守跳过出厂重置: " + ex.Message);
return true;
}
return false;
}
private static bool DirectoryHasFileWithAnyExtension(string directory, string[] extensions)
{
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory))
{
return false;
}
for (int i = 0; i < extensions.Length; i++)
{
string[] matches = Directory.GetFiles(directory, "*" + extensions[i], SearchOption.AllDirectories);
if (matches != null && matches.Length > 0)
{
return true;
}
}
return false;
}
private static bool DirectoryHasAnyFile(string directory)
{
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory))
{
return false;
}
string[] matches = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
return matches != null && matches.Length > 0;
}
private static bool HasInitializedMarker()
{
try
{
return File.Exists(InitializedMarkerPath);
}
catch (Exception ex)
{
Debug.LogWarning("[FactoryReset] 读取初始化标记失败: " + ex.Message);
return false;
}
}
private static void EnsureInitializedMarker()
{
try
{
if (File.Exists(InitializedMarkerPath))
{
return;
}
File.WriteAllText(InitializedMarkerPath, DateTime.UtcNow.ToString("o"));
}
catch (Exception ex)
{
Debug.LogWarning("[FactoryReset] 写入初始化标记失败: " + ex.Message);
}
}
private static bool HasAnyRecoverableLocalState()
{
if (PlayerEconomyStorage.HasAnyRecoverableState())
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("player_experience", "runtime", Path.Combine(Application.persistentDataPath, "player_experience.json")))
{
return true;
}
if (AllyHeroDeployLedgerStorage.HasAnyRecoverableState())
{
return true;
}
if (ExpBottleLedgerStorage.HasAnyRecoverableState())
{
return true;
}
if (DushMaterialLedgerStorage.HasAnyRecoverableState())
{
return true;
}
if (EquipmentConsumableLedgerStorage.HasAnyRecoverableState())
{
return true;
}
if (StoreOwnershipStorage.HasAnyRecoverableState())
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("player_rks", "overall"))
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("player_skills", "runtime"))
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("recent_play_history", "runs_v1"))
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("runtime_equipment", "generated_list"))
{
return true;
}
if (DailyTaskSaveService.HasAnyRecoverableState())
{
return true;
}
if (SecureSaveVault.HasAnyRecoverableState("store_state", "runtime", Path.Combine(Application.persistentDataPath, StoreStateLegacyPath)))
{
return true;
}
if (PlayerProgressBackupService.HasExistingBackupFile())
{
return true;
}
SongData[] songs = RuntimeResourcesCache.LoadAllSongs();
if (songs != null)
{
for (int i = 0; i < songs.Length; i++)
{
SongData song = songs[i];
if (song == null || song.songID <= 0)
{
continue;
}
string legacySongPath = Path.Combine(Application.persistentDataPath, $"SongData_{song.songID}.json");
if (SecureSaveVault.HasAnyRecoverableState("song_runtime", song.songID.ToString(), legacySongPath))
{
return true;
}
}
}
return false;
}
private static void ApplyFactoryReset()
{
ResetPlayerEconomyAndExperience();
ResetHeroGrowthAndLoadout();
ResetPlayerSkillAndRks();
ResetSongProgressAndRecentHistory();
ResetDailyTasks();
ResetStoreState();
ResetGeneratedEquipment();
ResetTeamSelection();
ResetSettingsAndUserPrefs();
ResetPlayerMirrorSo();
ResetDlcOwnershipFlags();
RuntimeResourcesCache.InvalidateAll();
PlayerPrefs.Save();
}
private static void ResetPlayerEconomyAndExperience()
{
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
PlayerEconomyLedger economyLedger = PlayerEconomyLedger.EnsureInstance();
economyLedger.SetCoins(0);
economyLedger.SetMaterial(0);
PlayerExperienceLedger experienceLedger = PlayerExperienceLedger.EnsureInstance();
experienceLedger.SetExperience(0);
if (player != null)
{
player.player_currentEXP = 0;
player.SetCoins(0);
player.SetMaterial(0);
player.SetURankingScore(0f);
player.SetLegacyExpBottleCount("commonExpBottle78001", 0);
player.SetLegacyExpBottleCount("mediumExpBottle78002", 0);
player.SetLegacyExpBottleCount("superiorExpBottle78003", 0);
player.SetLegacyExpBottleCount("supremeExpBottle78004", 0);
player.SetLegacyExpBottleCount("extraordinaryExpBottle78005", 0);
player.SetLegacyExpBottleCount("celestialExpBottle78006", 0);
player.SetLegacyDushMaterialCount("dushMaterial78021", 0);
player.SetLegacyDushMaterialCount("dushMaterial78022", 0);
player.SetLegacyDushMaterialCount("dushMaterial78023", 0);
player.SetLegacyDushMaterialCount("dushMaterial78024", 0);
}
}
private static void ResetHeroGrowthAndLoadout()
{
AllyHeroDeployLedger.EnsureInstance().ResetGrowthProgressOnly();
ExpBottleLedger.EnsureInstance().ResetAllToZero();
DushMaterialLedger.EnsureInstance().ResetAllToZero();
EquipmentConsumableLedger.EnsureInstance().ResetAllToZero();
AllyHero_SO.ClearAllEquippedSkills();
AllyHero_SO[] heroes = RuntimeResourcesCache.LoadAllAllyHeroes();
if (heroes == null)
{
return;
}
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null)
{
continue;
}
hero.ClearEquippedEquipment();
hero.ClearSelectedSkin();
hero.ally_currentEXP = 0;
hero.ally_growthUnlockedTierIndex = 0;
hero.level_lock = false;
hero.ally_autoBreakthroughEnabled = false;
hero.ally_battleDeployCount = 0;
hero.ally_finishCount = 0;
hero.ally_mvpCount = 0;
hero.ally_joinDateUtcTicks = 0L;
}
}
private static void ResetPlayerSkillAndRks()
{
PlayerRksService.ClearPersistentState();
PlayerSkillService.ResetToDefaultLevelOneSkill();
}
private static void ResetSongProgressAndRecentHistory()
{
SongData[] songs = RuntimeResourcesCache.LoadAllSongs();
if (songs != null)
{
for (int i = 0; i < songs.Length; i++)
{
if (songs[i] != null)
{
songs[i].ClearSaveDataAndReload();
}
}
}
RecentPlayHistoryStore.Clear();
}
private static void ResetDailyTasks()
{
DailyTaskService.EnsureInstance().ClearPersistentState();
}
private static void ResetStoreState()
{
SecureSaveVault.Delete("store_state", "runtime", Path.Combine(Application.persistentDataPath, StoreStateLegacyPath));
StoreOwnershipLedger.EnsureInstance().ClearPersistentState();
PlayerProgressBackupService.ClearStoreOwnershipBackup();
storeItemSO[] storeItems = RuntimeResourcesCache.LoadAllStoreItems();
if (storeItems == null)
{
return;
}
for (int i = 0; i < storeItems.Length; i++)
{
storeItemSO item = storeItems[i];
if (item == null)
{
continue;
}
item.purchasedCount = 0;
item.user_has_read = false;
}
}
private static void ResetGeneratedEquipment()
{
equipSmelt.ClearPersistentState();
Bansonic.equipmentGenerator.ClearRuntimeGeneratedPersistence();
PlayerPrefs.DeleteKey("bansonic_equipment_next_id_v1");
}
private static void ResetTeamSelection()
{
for (int i = 0; i < DefaultTeamHeroIds.Length; i++)
{
int slot = i + 1;
PlayerPrefs.SetInt($"selected_heroSlot0{slot}_heroID", DefaultTeamHeroIds[i]);
PlayerPrefs.DeleteKey($"selected_heroSlot0{slot}_exp");
}
PlayerPrefs.SetInt("SelectedMainHeroID", DefaultTeamHeroIds[0]);
}
private static void ResetSettingsAndUserPrefs()
{
string[] keysToDelete =
{
"noteSpeedMultiplier",
"UserGlobalDelaySeconds",
"screenMode",
"resolutionIndex",
"customResW",
"customResH",
"frameRateIndex",
"SuperResolution",
"EnableGlobalMute",
"Volume_Main",
"Volume_NoteHit",
"Volume_MusicInGame",
"Volume_MusicOutGame",
"Volume_UI",
"EnableSyncNotePrefab",
"bansonic_language_code",
"global_chat_last_private_partner_id",
"notebook.selected_category",
"notebook.selected_father_id",
"notebook.selected_son_id",
"bansonic_online_enabled",
"before_everything_agree_ranking",
"before_everything_first_launch_completed",
"before_everything_light_warning_accepted",
"before_everything_user_info_accepted",
"selected_song_last_entered_id",
"song_select_skip_quick_enter_prompt"
};
for (int i = 0; i < keysToDelete.Length; i++)
{
PlayerPrefs.DeleteKey(keysToDelete[i]);
}
string[] keyBindingPrefixes = { "red", "green", "yellow", "purple", "blue" };
for (int i = 0; i < keyBindingPrefixes.Length; i++)
{
PlayerPrefs.DeleteKey($"KeyBinding_{keyBindingPrefixes[i]}");
}
}
private static void ResetPlayerMirrorSo()
{
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
if (player == null)
{
return;
}
player.SetCurrentLevel(0);
player.player_currentEXP = 0;
}
private static void ResetDlcOwnershipFlags()
{
DlcOwnershipService.ClearLocalPersistence();
}
}