客户端rsa,冗余清理,bug修复,安卓build问题
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 构建前防线:若随包发布的玩家进度数据(Player_SO / AllyHero_SO / SongData)
|
||||
/// 仍带有非零测试值,直接中断构建。
|
||||
///
|
||||
/// 目的:杜绝"忘记跑一键初始化 → 测试存档随安装包发布 → 玩家首启被灌入 999988
|
||||
/// 金币等测试数据"的事故(这是历史上多次出现的存档污染根因)。
|
||||
///
|
||||
/// 只做只读校验,不改动任何资产;发现问题时抛出 BuildFailedException 并给出
|
||||
/// 明确指引(跑 Tools/Packaging/一键初始化打包设置)。
|
||||
/// </summary>
|
||||
public sealed class PackagingBuildGuard : IPreprocessBuildWithReport
|
||||
{
|
||||
public int callbackOrder => 0;
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
var problems = new List<string>();
|
||||
|
||||
CheckPlayers(problems);
|
||||
CheckHeroes(problems);
|
||||
CheckSongs(problems);
|
||||
|
||||
if (problems.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("检测到随包发布的玩家进度数据仍带测试值,已中断构建。");
|
||||
sb.AppendLine("请先执行菜单:Tools/Packaging/一键初始化打包设置(恢复默认),再重新打包。");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("问题明细:");
|
||||
int shown = 0;
|
||||
foreach (var p in problems)
|
||||
{
|
||||
sb.AppendLine(" - " + p);
|
||||
if (++shown >= 40)
|
||||
{
|
||||
sb.AppendLine(" - ...(其余 " + (problems.Count - shown) + " 项省略)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogError("[PackagingBuildGuard]\n" + sb);
|
||||
throw new BuildFailedException(sb.ToString());
|
||||
}
|
||||
|
||||
private static void CheckPlayers(List<string> problems)
|
||||
{
|
||||
string[] intFields =
|
||||
{
|
||||
"player_coins", "player_material", "player_currentEXP", "player_currentLevel",
|
||||
"commonExpBottle78001", "mediumExpBottle78002", "superiorExpBottle78003",
|
||||
"supremeExpBottle78004", "extraordinaryExpBottle78005", "celestialExpBottle78006",
|
||||
"dushMaterial78021", "dushMaterial78022", "dushMaterial78023", "dushMaterial78024",
|
||||
};
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:Player_SO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<Player_SO>(path);
|
||||
if (so == null) continue;
|
||||
|
||||
var s = new SerializedObject(so);
|
||||
foreach (var f in intFields)
|
||||
{
|
||||
var prop = s.FindProperty(f);
|
||||
if (prop != null && prop.intValue != 0)
|
||||
{
|
||||
problems.Add(path + " :: " + f + " = " + prop.intValue);
|
||||
}
|
||||
}
|
||||
var rank = s.FindProperty("uRankingScore");
|
||||
if (rank != null && Mathf.Abs(rank.floatValue) > 0.0001f)
|
||||
{
|
||||
problems.Add(path + " :: uRankingScore = " + rank.floatValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckHeroes(List<string> problems)
|
||||
{
|
||||
string[] intFields =
|
||||
{
|
||||
"ally_currentEXP", "ally_growthUnlockedTierIndex",
|
||||
"ally_battleDeployCount", "ally_finishCount", "ally_mvpCount",
|
||||
};
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:AllyHero_SO"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<AllyHero_SO>(path);
|
||||
if (so == null) continue;
|
||||
|
||||
var s = new SerializedObject(so);
|
||||
foreach (var f in intFields)
|
||||
{
|
||||
var prop = s.FindProperty(f);
|
||||
if (prop != null && prop.intValue != 0)
|
||||
{
|
||||
problems.Add(path + " :: " + f + " = " + prop.intValue);
|
||||
}
|
||||
}
|
||||
var join = s.FindProperty("ally_joinDateUtcTicks");
|
||||
if (join != null && join.longValue != 0L)
|
||||
{
|
||||
problems.Add(path + " :: ally_joinDateUtcTicks = " + join.longValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckSongs(List<string> problems)
|
||||
{
|
||||
string[] recordFields =
|
||||
{
|
||||
"lastScoreForThisDifficulty", "chartPersonalRecordForThisDifficulty",
|
||||
"idolPersonalRecordForThisDifficulty", "totalPersonalRecordForThisDifficulty",
|
||||
};
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:SongData"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<SongData>(path);
|
||||
if (so == null) continue;
|
||||
|
||||
var s = new SerializedObject(so);
|
||||
var enter = s.FindProperty("game_enterTimes");
|
||||
if (enter != null && enter.intValue != 0)
|
||||
{
|
||||
problems.Add(path + " :: game_enterTimes = " + enter.intValue);
|
||||
}
|
||||
|
||||
var chartFiles = s.FindProperty("chartFiles");
|
||||
if (chartFiles != null && chartFiles.isArray)
|
||||
{
|
||||
for (int i = 0; i < chartFiles.arraySize; i++)
|
||||
{
|
||||
var entry = chartFiles.GetArrayElementAtIndex(i);
|
||||
foreach (var f in recordFields)
|
||||
{
|
||||
var prop = entry.FindPropertyRelative(f);
|
||||
if (prop != null && prop.intValue != 0)
|
||||
{
|
||||
problems.Add(path + " :: chartFiles[" + i + "]." + f + " = " + prop.intValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 821a652caceb13346a933a59d5de5f90
|
||||
@@ -35,6 +35,7 @@ public static class PackagingResetTool
|
||||
"将执行以下重置(仅玩家进度,不改配置/解锁/技能配装):\n\n" +
|
||||
"1. 玩家:金币、材料、经验、等级清零\n" +
|
||||
"2. 英雄:经验、成长阶位、自动突破、等级锁、出战/完成/MVP 统计、入队日期清零\n" +
|
||||
"2.5 歌曲:游玩次数、累计时长、各难度成绩记录清零(歌曲解锁/谱面配置不动)\n" +
|
||||
"3. 装备:删除 " + GeneratedEquipmentFolder + " 下所有生成装备,清除英雄已装备引用\n" +
|
||||
"4. 商店:所有物品 purchasedCount、user_has_read 清零(限购配置 itemPurchaseQuota 不动)\n" +
|
||||
"5. 清理本机测试存档(.cache_bridge 货币/成长/装备/商店存档,不含拥有权账本)与相关 PlayerPrefs\n\n" +
|
||||
@@ -56,6 +57,7 @@ public static class PackagingResetTool
|
||||
|
||||
errors += ResetPlayers(report);
|
||||
errors += ResetHeroes(report);
|
||||
errors += ResetSongs(report);
|
||||
errors += DeleteGeneratedEquipmentAssets(report);
|
||||
errors += ResetStoreItems(report);
|
||||
}
|
||||
@@ -160,6 +162,68 @@ public static class PackagingResetTool
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ResetSongs(StringBuilder report)
|
||||
{
|
||||
int count = 0;
|
||||
int charts = 0;
|
||||
foreach (var guid in AssetDatabase.FindAssets("t:SongData"))
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<SongData>(path);
|
||||
if (so == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(so);
|
||||
// 顶层游玩进度(不动 songID / isUnlocked / bpm / 谱面配置等)。
|
||||
SetStringIfPresent(serialized, "usernameID", string.Empty);
|
||||
SetIntIfPresent(serialized, "game_enterTimes", 0);
|
||||
SetFloatIfPresent(serialized, "time_totalPlayingTime", 0f);
|
||||
|
||||
// 逐难度成绩记录(chartFiles 数组内),只清成绩,保留难度/敌人/谱面配置。
|
||||
var chartFiles = serialized.FindProperty("chartFiles");
|
||||
if (chartFiles != null && chartFiles.isArray)
|
||||
{
|
||||
for (int i = 0; i < chartFiles.arraySize; i++)
|
||||
{
|
||||
var entry = chartFiles.GetArrayElementAtIndex(i);
|
||||
SetChildInt(entry, "lastScoreForThisDifficulty", 0);
|
||||
SetChildInt(entry, "chartPersonalRecordForThisDifficulty", 0);
|
||||
SetChildInt(entry, "idolPersonalRecordForThisDifficulty", 0);
|
||||
SetChildInt(entry, "totalPersonalRecordForThisDifficulty", 0);
|
||||
SetChildFloat(entry, "levelProgressForThisDifficulty", 0f);
|
||||
charts++;
|
||||
}
|
||||
}
|
||||
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(so);
|
||||
count++;
|
||||
}
|
||||
|
||||
report.AppendLine("歌曲资产已重置:" + count + " 个 SongData(游玩次数/时长/" + charts + " 条难度成绩记录)。");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void SetChildInt(SerializedProperty parent, string field, int value)
|
||||
{
|
||||
var prop = parent.FindPropertyRelative(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.intValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetChildFloat(SerializedProperty parent, string field, float value)
|
||||
{
|
||||
var prop = parent.FindPropertyRelative(field);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.floatValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static int DeleteGeneratedEquipmentAssets(StringBuilder report)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(GeneratedEquipmentFolder))
|
||||
|
||||
Reference in New Issue
Block a user