1299 lines
58 KiB
C#
1299 lines
58 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description: OSS 编辑器(文件下载 · 数据统计 · 上传测试)
|
||
* @Date: 2025年04月22日 星期二 15:04:14
|
||
* @Modify: 2026年04月17日 合并 OssStatisticEditorWindow
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Text;
|
||
using Logic.CrashSight;
|
||
using MemoryPack;
|
||
using RuntimeData;
|
||
using Steamworks;
|
||
using TH1_Logic.Collect;
|
||
using TH1_Logic.Core;
|
||
using TH1_Logic.Net;
|
||
using TH1_Logic.Oss;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
|
||
namespace Logic.Editor
|
||
{
|
||
// ──────────────────────────────────────
|
||
// 统计用数据类
|
||
// ──────────────────────────────────────
|
||
|
||
public enum OssStatisticType { Unit, Tech, Empire }
|
||
|
||
public class UnitStatisticLimit
|
||
{
|
||
public bool LimitUnitType;
|
||
public UnitType UnitType;
|
||
public bool LimitGiantType;
|
||
public GiantType GiantType;
|
||
public bool LimitLevel;
|
||
public int Level;
|
||
public bool LimitCarryUnitType;
|
||
public UnitType CarryUnitType;
|
||
public bool LimitCarryGiantType;
|
||
public GiantType CarryGiantType;
|
||
public bool LimitCarryLevel;
|
||
public int CarryLevel;
|
||
}
|
||
|
||
public class UnitStatisticResult
|
||
{
|
||
public float MatchCount;
|
||
public float TurnCount;
|
||
public float AppearCount;
|
||
public float EarliestAppearTurn;
|
||
public float DeathCount;
|
||
public float KillCount;
|
||
public float TotalDamageDealt;
|
||
public float TotalDamageTaken;
|
||
public float AverageDamageDealt;
|
||
public float AverageDamageTaken;
|
||
public float TransformFromCount;
|
||
public float TransformToCount;
|
||
|
||
public void Clear()
|
||
{
|
||
MatchCount = TurnCount = AppearCount = EarliestAppearTurn = 0;
|
||
DeathCount = KillCount = 0;
|
||
TotalDamageDealt = TotalDamageTaken = AverageDamageDealt = AverageDamageTaken = 0;
|
||
TransformFromCount = TransformToCount = 0;
|
||
}
|
||
}
|
||
|
||
public class TechStatisticLimit
|
||
{
|
||
public bool LimitEmpire;
|
||
public Empire Empire;
|
||
public bool LimitTechType;
|
||
public TechType TechType;
|
||
}
|
||
|
||
public class TechStatisticResult
|
||
{
|
||
public float LearnMatchCount;
|
||
public float LearnCount;
|
||
public float LearnTurn;
|
||
|
||
public void Clear() { LearnMatchCount = LearnCount = LearnTurn = 0; }
|
||
}
|
||
|
||
public class EmpireStatisticLimit
|
||
{
|
||
public bool LimitEmpire;
|
||
public Empire Empire;
|
||
}
|
||
|
||
public class EmpireStatisticResult
|
||
{
|
||
public int MatchCount;
|
||
public int TurnCount;
|
||
public float CoinPerTurn;
|
||
public float TechPointPerTurn;
|
||
public float CityCount;
|
||
public float MaxCityLevel;
|
||
public float CityLevel;
|
||
public float TechCount;
|
||
|
||
public void Clear()
|
||
{
|
||
MatchCount = TurnCount = 0;
|
||
CoinPerTurn = TechPointPerTurn = CityCount = MaxCityLevel = CityLevel = TechCount = 0;
|
||
}
|
||
}
|
||
|
||
|
||
// ──────────────────────────────────────
|
||
// 编辑器窗口
|
||
// ──────────────────────────────────────
|
||
|
||
public class OssEditorWindow : EditorWindow
|
||
{
|
||
// ── 通用 ──
|
||
private Vector2 _barPosition;
|
||
private GUIStyle _redBoxStyle;
|
||
private GUIStyle _whiteBoxStyle;
|
||
private GUIStyle _sectionHeaderStyle;
|
||
private int _selectedTab;
|
||
private static readonly string[] TabLabels = { "📥 文件下载", "📊 数据统计", "🔧 上传测试" };
|
||
|
||
// ── 上传测试 ──
|
||
private StsTokenService _stsService;
|
||
private OssUploadService _uploadService;
|
||
private StsCredentials _cachedCredentials;
|
||
private StsCredentials _cachedCollectCredentials;
|
||
private const string FunctionUrl = "https://get-sts-token-qltjykaafr.cn-shanghai.fcapp.run";
|
||
private bool _mapTestsFoldout;
|
||
private bool _collectTestsFoldout;
|
||
|
||
// ── 文件下载 ──
|
||
private const string PrefKeyAccessKeyId = "OssDownload_AccessKeyId";
|
||
private const string PrefKeyAccessKeySecret = "OssDownload_AccessKeySecret";
|
||
private const string PrefKeyEndpoint = "OssDownload_Endpoint";
|
||
private const string PrefKeyBucket = "OssDownload_Bucket";
|
||
private string _accessKeyId = "";
|
||
private string _accessKeySecret = "";
|
||
private string _ossEndpoint = "oss-cn-shanghai.aliyuncs.com";
|
||
private string _ossBucket = "th1-oss";
|
||
private string _downloadStatus = "";
|
||
private bool _isDownloading;
|
||
|
||
// ── 数据统计 ──
|
||
private string _statisticPath = "";
|
||
private bool _statisticPathInitialized;
|
||
private OssStatisticType _statisticType;
|
||
private UnitStatisticLimit _unitLimit;
|
||
private UnitStatisticResult _unitResult;
|
||
private TechStatisticLimit _techLimit;
|
||
private TechStatisticResult _techResult;
|
||
private EmpireStatisticLimit _empireLimit;
|
||
private EmpireStatisticResult _empireResult;
|
||
private int _totalFileCount;
|
||
private int _validFileCount;
|
||
private string[] _versionFolders = Array.Empty<string>();
|
||
private bool[] _versionSelected = Array.Empty<bool>();
|
||
private bool _versionFoldout = true;
|
||
|
||
/// <summary>Collect 数据的本地存储路径: Tools/OSS/Data</summary>
|
||
public static string DefaultDataPath =>
|
||
Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/OSS/Data"));
|
||
|
||
|
||
[MenuItem("Tools/Oss 编辑器")]
|
||
private static void ShowWindow()
|
||
{
|
||
var window = GetWindow<OssEditorWindow>();
|
||
window.titleContent = new GUIContent("OSS 编辑器");
|
||
window.minSize = new Vector2(480, 360);
|
||
window.Show();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
_stsService = new StsTokenService(FunctionUrl);
|
||
_uploadService = new OssUploadService();
|
||
_accessKeyId = EditorPrefs.GetString(PrefKeyAccessKeyId, "");
|
||
_accessKeySecret = EditorPrefs.GetString(PrefKeyAccessKeySecret, "");
|
||
_ossEndpoint = EditorPrefs.GetString(PrefKeyEndpoint, "oss-cn-shanghai.aliyuncs.com");
|
||
_ossBucket = EditorPrefs.GetString(PrefKeyBucket, "th1-oss");
|
||
}
|
||
|
||
// ─────────────────────────────────
|
||
// OnGUI
|
||
// ─────────────────────────────────
|
||
|
||
private void OnGUI()
|
||
{
|
||
InitStyles();
|
||
|
||
// 顶部 Tab 栏
|
||
EditorGUILayout.Space(4);
|
||
_selectedTab = GUILayout.Toolbar(_selectedTab, TabLabels, GUILayout.Height(28));
|
||
EditorGUILayout.Space(6);
|
||
|
||
_barPosition = EditorGUILayout.BeginScrollView(_barPosition);
|
||
|
||
switch (_selectedTab)
|
||
{
|
||
case 0: DrawDownloadTab(); break;
|
||
case 1: DrawStatisticTab(); break;
|
||
case 2: DrawUploadTestTab(); break;
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
private void InitStyles()
|
||
{
|
||
if (_redBoxStyle == null)
|
||
{
|
||
_redBoxStyle = InspectorUtils.GetHelpBoxStyle();
|
||
InspectorUtils.AddBorder(_redBoxStyle, new Color(0.5f, 0.4f, 0.4f, 0.6f));
|
||
}
|
||
|
||
if (_whiteBoxStyle == null)
|
||
{
|
||
_whiteBoxStyle = InspectorUtils.GetHelpBoxStyle();
|
||
InspectorUtils.AddBorder(_whiteBoxStyle, new Color(1f, 1f, 1f, 0.2f));
|
||
}
|
||
|
||
if (_sectionHeaderStyle == null)
|
||
{
|
||
_sectionHeaderStyle = new GUIStyle(EditorStyles.boldLabel)
|
||
{
|
||
fontSize = 13,
|
||
padding = new RectOffset(2, 0, 4, 4)
|
||
};
|
||
}
|
||
|
||
GUI.skin.button.wordWrap = true;
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// Tab 0 — 文件下载
|
||
// ═══════════════════════════════════
|
||
|
||
private void DrawDownloadTab()
|
||
{
|
||
EditorGUILayout.LabelField("OSS 凭证配置", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
DrawPrefField("AccessKey ID", ref _accessKeyId, PrefKeyAccessKeyId);
|
||
DrawPrefPasswordField("AccessKey Secret", ref _accessKeySecret, PrefKeyAccessKeySecret);
|
||
DrawPrefField("Endpoint", ref _ossEndpoint, PrefKeyEndpoint);
|
||
DrawPrefField("Bucket", ref _ossBucket, PrefKeyBucket);
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(6);
|
||
|
||
EditorGUILayout.LabelField("下载操作", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
EditorGUILayout.LabelField("下载目录", DefaultDataPath, EditorStyles.miniLabel);
|
||
EditorGUILayout.Space(4);
|
||
|
||
EditorGUI.BeginDisabledGroup(_isDownloading);
|
||
if (GUILayout.Button("更新 Collect 文件", GUILayout.Height(30)))
|
||
DownloadCollectFiles();
|
||
EditorGUI.EndDisabledGroup();
|
||
|
||
if (!string.IsNullOrEmpty(_downloadStatus))
|
||
EditorGUILayout.HelpBox(_downloadStatus, MessageType.Info);
|
||
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// Tab 1 — 数据统计
|
||
// ═══════════════════════════════════
|
||
|
||
private void DrawStatisticTab()
|
||
{
|
||
if (!_statisticPathInitialized)
|
||
{
|
||
_statisticPathInitialized = true;
|
||
if (string.IsNullOrEmpty(_statisticPath))
|
||
_statisticPath = DefaultDataPath;
|
||
RefreshVersionFolders();
|
||
}
|
||
|
||
_unitLimit ??= new UnitStatisticLimit();
|
||
_unitResult ??= new UnitStatisticResult();
|
||
_techLimit ??= new TechStatisticLimit();
|
||
_techResult ??= new TechStatisticResult();
|
||
_empireLimit ??= new EmpireStatisticLimit();
|
||
_empireResult ??= new EmpireStatisticResult();
|
||
|
||
// 分析目录
|
||
EditorGUILayout.LabelField("分析目录", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginHorizontal();
|
||
var newPath = EditorGUILayout.TextField(_statisticPath);
|
||
if (newPath != _statisticPath) { _statisticPath = newPath; RefreshVersionFolders(); }
|
||
if (GUILayout.Button("浏览...", GUILayout.Width(60)))
|
||
{
|
||
var selected = EditorUtility.OpenFolderPanel("选择分析文件夹", "", "");
|
||
if (!string.IsNullOrEmpty(selected)) { _statisticPath = selected; RefreshVersionFolders(); }
|
||
}
|
||
if (GUILayout.Button("刷新", GUILayout.Width(50)))
|
||
RefreshVersionFolders();
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.Space(4);
|
||
|
||
// 版本文件夹选择
|
||
if (_versionFolders.Length > 0)
|
||
{
|
||
int selCount = 0;
|
||
for (int i = 0; i < _versionSelected.Length; i++) if (_versionSelected[i]) selCount++;
|
||
|
||
_versionFoldout = EditorGUILayout.BeginFoldoutHeaderGroup(_versionFoldout,
|
||
$"版本选择 ({selCount}/{_versionFolders.Length})");
|
||
if (_versionFoldout)
|
||
{
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("全选", GUILayout.Width(50)))
|
||
for (int i = 0; i < _versionSelected.Length; i++) _versionSelected[i] = true;
|
||
if (GUILayout.Button("全不选", GUILayout.Width(60)))
|
||
for (int i = 0; i < _versionSelected.Length; i++) _versionSelected[i] = false;
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
for (int i = 0; i < _versionFolders.Length; i++)
|
||
_versionSelected[i] = EditorGUILayout.ToggleLeft(_versionFolders[i], _versionSelected[i]);
|
||
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox("未找到版本文件夹,请确认路径正确或点击刷新", MessageType.Warning);
|
||
}
|
||
|
||
EditorGUILayout.Space(6);
|
||
|
||
// 筛选类型
|
||
EditorGUILayout.BeginHorizontal();
|
||
InspectorUtils.InspectorTextWidthRich("<b>筛选类型:</b>");
|
||
_statisticType = (OssStatisticType)EditorGUILayout.EnumPopup(_statisticType, GUILayout.Width(200));
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.Space(4);
|
||
|
||
switch (_statisticType)
|
||
{
|
||
case OssStatisticType.Unit: DrawUnitStatisticGUI(); break;
|
||
case OssStatisticType.Tech: DrawTechStatisticGUI(); break;
|
||
case OssStatisticType.Empire: DrawEmpireStatisticGUI(); break;
|
||
}
|
||
}
|
||
|
||
// ── Unit 统计 ──
|
||
|
||
private void DrawUnitStatisticGUI()
|
||
{
|
||
EditorGUILayout.LabelField("筛选条件", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
DrawFilterRow("UnitType", ref _unitLimit.LimitUnitType,
|
||
() => _unitLimit.UnitType = (UnitType)EditorGUILayout.EnumPopup(_unitLimit.UnitType, GUILayout.Width(150)));
|
||
DrawFilterRow("GiantType", ref _unitLimit.LimitGiantType,
|
||
() => _unitLimit.GiantType = (GiantType)EditorGUILayout.EnumPopup(_unitLimit.GiantType, GUILayout.Width(150)));
|
||
DrawFilterRow("Level", ref _unitLimit.LimitLevel,
|
||
() => _unitLimit.Level = EditorGUILayout.IntField(_unitLimit.Level, GUILayout.Width(150)));
|
||
DrawFilterRow("CarryUnitType", ref _unitLimit.LimitCarryUnitType,
|
||
() => _unitLimit.CarryUnitType = (UnitType)EditorGUILayout.EnumPopup(_unitLimit.CarryUnitType, GUILayout.Width(150)));
|
||
DrawFilterRow("CarryGiantType", ref _unitLimit.LimitCarryGiantType,
|
||
() => _unitLimit.CarryGiantType = (GiantType)EditorGUILayout.EnumPopup(_unitLimit.CarryGiantType, GUILayout.Width(150)));
|
||
DrawFilterRow("CarryLevel", ref _unitLimit.LimitCarryLevel,
|
||
() => _unitLimit.CarryLevel = EditorGUILayout.IntField(_unitLimit.CarryLevel, GUILayout.Width(150)));
|
||
|
||
EditorGUILayout.Space(4);
|
||
if (GUILayout.Button("计算", GUILayout.Height(26)))
|
||
CalculateUnitStatistic();
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(6);
|
||
|
||
// 结果
|
||
if (_unitResult != null && _unitResult.MatchCount > 0)
|
||
{
|
||
EditorGUILayout.LabelField("统计结果", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_redBoxStyle);
|
||
InspectorUtils.InspectorTextWidthRich($"<b>扫描文件:</b> {_totalFileCount} <b>有效文件:</b> {_validFileCount} <b>有效局数:</b> {_unitResult.MatchCount}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局出战:</b> {_unitResult.AppearCount:F2} <b>最早出战回合:</b> Turn {_unitResult.EarliestAppearTurn:F1}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局击杀:</b> {_unitResult.KillCount:F2} <b>单局死亡:</b> {_unitResult.DeathCount:F2}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局总造伤:</b> {_unitResult.TotalDamageDealt:F1} <b>单局总承伤:</b> {_unitResult.TotalDamageTaken:F1}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单位均造伤:</b> {_unitResult.AverageDamageDealt:F1} <b>单位均承伤:</b> {_unitResult.AverageDamageTaken:F1}");
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
}
|
||
|
||
// ── Tech 统计 ──
|
||
|
||
private void DrawTechStatisticGUI()
|
||
{
|
||
EditorGUILayout.LabelField("筛选条件", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
DrawFilterRow("Empire", ref _techLimit.LimitEmpire, () =>
|
||
{
|
||
_techLimit.Empire.Civ = (CivEnum)EditorGUILayout.EnumPopup(_techLimit.Empire.Civ, GUILayout.Width(150));
|
||
_techLimit.Empire.Force = (ForceEnum)EditorGUILayout.EnumPopup(_techLimit.Empire.Force, GUILayout.Width(150));
|
||
});
|
||
DrawFilterRow("TechType", ref _techLimit.LimitTechType,
|
||
() => _techLimit.TechType = (TechType)EditorGUILayout.EnumPopup(_techLimit.TechType, GUILayout.Width(150)));
|
||
|
||
EditorGUILayout.Space(4);
|
||
if (GUILayout.Button("计算", GUILayout.Height(26)))
|
||
CalculateTechStatistic();
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(6);
|
||
|
||
if (_techResult != null && _techResult.LearnMatchCount > 0)
|
||
{
|
||
EditorGUILayout.LabelField("统计结果", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_redBoxStyle);
|
||
InspectorUtils.InspectorTextWidthRich($"<b>扫描文件:</b> {_totalFileCount} <b>有效文件:</b> {_validFileCount} <b>学习局数:</b> {_techResult.LearnMatchCount}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>平均学习次数(所有局):</b> {_techResult.LearnCount:F2}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>平均首次学习回合(学习局):</b> Turn {_techResult.LearnTurn:F1}");
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
}
|
||
|
||
// ── Empire 统计 ──
|
||
|
||
private void DrawEmpireStatisticGUI()
|
||
{
|
||
EditorGUILayout.LabelField("筛选条件", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
|
||
DrawFilterRow("Empire", ref _empireLimit.LimitEmpire, () =>
|
||
{
|
||
_empireLimit.Empire.Civ = (CivEnum)EditorGUILayout.EnumPopup(_empireLimit.Empire.Civ, GUILayout.Width(150));
|
||
_empireLimit.Empire.Force = (ForceEnum)EditorGUILayout.EnumPopup(_empireLimit.Empire.Force, GUILayout.Width(150));
|
||
});
|
||
|
||
EditorGUILayout.Space(4);
|
||
if (GUILayout.Button("计算", GUILayout.Height(26)))
|
||
CalculateEmpireStatistic();
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(6);
|
||
|
||
if (_empireResult != null && _empireResult.MatchCount > 0)
|
||
{
|
||
EditorGUILayout.LabelField("统计结果", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_redBoxStyle);
|
||
InspectorUtils.InspectorTextWidthRich($"<b>扫描文件:</b> {_totalFileCount} <b>有效文件:</b> {_validFileCount} <b>有效局数:</b> {_empireResult.MatchCount}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局回合金币:</b> {_empireResult.CoinPerTurn:F1} <b>单局回合科技点:</b> {_empireResult.TechPointPerTurn:F1}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局城市数量:</b> {_empireResult.CityCount:F1} <b>最高城市等级:</b> {_empireResult.MaxCityLevel:F1}");
|
||
InspectorUtils.InspectorTextWidthRich($"<b>单局城市等级:</b> {_empireResult.CityLevel:F1} <b>单局科技数量:</b> {_empireResult.TechCount:F1}");
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// Tab 2 — 上传测试
|
||
// ═══════════════════════════════════
|
||
|
||
private void DrawUploadTestTab()
|
||
{
|
||
// Map 测试组
|
||
EditorGUILayout.LabelField("Map 上传测试", _sectionHeaderStyle);
|
||
_mapTestsFoldout = EditorGUILayout.BeginFoldoutHeaderGroup(_mapTestsFoldout, "Map 测试项");
|
||
if (_mapTestsFoldout)
|
||
{
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
DrawTestButton("请求 STS 令牌", TestMapRequestStsToken);
|
||
DrawTestButton("上传文件", TestMapUploadFile);
|
||
DrawTestButton("完整流程 (令牌+上传)", TestMapFullFlow);
|
||
DrawTestButton("上传(错误路径)", TestMapUploadErrorNameFile);
|
||
DrawTestButton("上传(大文件)", TestMapUploadBigFile);
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||
EditorGUILayout.Space(6);
|
||
|
||
// Collect 测试组
|
||
EditorGUILayout.LabelField("Collect 上传测试", _sectionHeaderStyle);
|
||
_collectTestsFoldout = EditorGUILayout.BeginFoldoutHeaderGroup(_collectTestsFoldout, "Collect 测试项");
|
||
if (_collectTestsFoldout)
|
||
{
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
DrawTestButton("请求 STS 令牌", TestCollectRequestStsToken);
|
||
DrawTestButton("上传文件", TestCollectUploadFile);
|
||
DrawTestButton("完整流程 (令牌+上传)", TestCollectFullFlow);
|
||
DrawTestButton("上传(错误路径)", TestCollectUploadErrorNameFile);
|
||
DrawTestButton("上传(大文件)", TestCollectUploadBigFile);
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||
EditorGUILayout.Space(6);
|
||
|
||
// 反序列化测试
|
||
EditorGUILayout.LabelField("存档验证", _sectionHeaderStyle);
|
||
EditorGUILayout.BeginVertical(_whiteBoxStyle);
|
||
DrawTestButton("测试存档反序列化", TestDeserializeArchive);
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
|
||
// ─────────────────────────────────
|
||
// GUI 辅助方法
|
||
// ─────────────────────────────────
|
||
|
||
private static void DrawPrefField(string label, ref string value, string prefKey)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField(label, GUILayout.Width(120));
|
||
var newVal = EditorGUILayout.TextField(value);
|
||
if (newVal != value) { value = newVal; EditorPrefs.SetString(prefKey, newVal); }
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
private static void DrawPrefPasswordField(string label, ref string value, string prefKey)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField(label, GUILayout.Width(120));
|
||
var newVal = EditorGUILayout.PasswordField(value);
|
||
if (newVal != value) { value = newVal; EditorPrefs.SetString(prefKey, newVal); }
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
private static void DrawFilterRow(string label, ref bool enabled, System.Action drawControl)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
InspectorUtils.InspectorTextWidthRich($"<b> 限制 {label} : </b> ");
|
||
enabled = EditorGUILayout.Toggle(enabled, GUILayout.Width(20));
|
||
if (enabled) drawControl();
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
private static void DrawTestButton(string label, System.Action action)
|
||
{
|
||
if (GUILayout.Button(label, GUILayout.Height(24)))
|
||
action();
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// 上传测试逻辑
|
||
// ═══════════════════════════════════
|
||
|
||
public async void TestMapRequestStsToken()
|
||
{
|
||
var id = LobbyManager.Instance.Lobby.GetSelfMemberId();
|
||
Debug.Log("=== 开始测试 STS 令牌请求 ===");
|
||
Debug.Log($"SteamID: {id}");
|
||
|
||
try
|
||
{
|
||
var ticket = new byte[1024];
|
||
var identity = new SteamNetworkingIdentity();
|
||
SteamUser.GetAuthSessionTicket(ticket, 1024, out var ticketSize, ref identity);
|
||
var authTicket = BitConverter.ToString(ticket, 0, (int)ticketSize).Replace("-", "").ToLower();
|
||
_cachedCredentials = await _stsService.RequestStsTokenAsync(id.ToString(), authTicket);
|
||
|
||
Debug.Log("<color=green>STS 令牌获取成功!</color>");
|
||
Debug.Log($"AccessKeyId: {_cachedCredentials.accessKeyId.Substring(0, 10)}...");
|
||
Debug.Log($"Endpoint: {_cachedCredentials.endpoint}");
|
||
Debug.Log($"Bucket: {_cachedCredentials.bucket}");
|
||
Debug.Log($"ObjectKey: {_cachedCredentials.objectKey}");
|
||
Debug.Log($"ExpiresIn: {_cachedCredentials.expiresIn} 秒");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"<color=red>STS 令牌获取失败:</color> {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public async void TestCollectRequestStsToken()
|
||
{
|
||
var id = LobbyManager.Instance.Lobby.GetSelfMemberId();
|
||
Debug.Log("=== 开始测试 Collect STS 令牌请求 ===");
|
||
Debug.Log($"SteamID: {id}");
|
||
|
||
try
|
||
{
|
||
var ticket = new byte[1024];
|
||
var identity = new SteamNetworkingIdentity();
|
||
SteamUser.GetAuthSessionTicket(ticket, 1024, out var ticketSize, ref identity);
|
||
var authTicket = BitConverter.ToString(ticket, 0, (int)ticketSize).Replace("-", "").ToLower();
|
||
_cachedCollectCredentials = await _stsService.RequestStsTokenAsync(id.ToString(), authTicket, "collectdata");
|
||
|
||
Debug.Log("<color=green>Collect STS 令牌获取成功!</color>");
|
||
Debug.Log($"AccessKeyId: {_cachedCollectCredentials.accessKeyId.Substring(0, 10)}...");
|
||
Debug.Log($"Endpoint: {_cachedCollectCredentials.endpoint}");
|
||
Debug.Log($"Bucket: {_cachedCollectCredentials.bucket}");
|
||
Debug.Log($"ObjectKey: {_cachedCollectCredentials.objectKey}");
|
||
Debug.Log($"ExpiresIn: {_cachedCollectCredentials.expiresIn} 秒");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"<color=red>STS 令牌获取失败:</color> {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public async void TestMapUploadFile()
|
||
{
|
||
if (_cachedCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 OSS 上传 ===");
|
||
try
|
||
{
|
||
var testData = Encoding.UTF8.GetBytes("{\"test\":true,\"timestamp\":" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "}");
|
||
Debug.Log($"上传数据大小: {testData.Length} bytes");
|
||
var result = await _uploadService.UploadFileAsync(_cachedCredentials, testData);
|
||
Debug.Log(result
|
||
? $"<color=green>文件上传成功!</color> 路径: {_cachedCredentials.bucket}/{_cachedCredentials.objectKey}"
|
||
: "<color=red>文件上传失败</color>");
|
||
}
|
||
catch (Exception ex) { Debug.LogError($"<color=red>上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestCollectUploadFile()
|
||
{
|
||
if (_cachedCollectCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 Collect 上传 ===");
|
||
try
|
||
{
|
||
var testData = Encoding.UTF8.GetBytes("{\"test\":true,\"timestamp\":" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "}");
|
||
Debug.Log($"上传数据大小: {testData.Length} bytes");
|
||
var result = await _uploadService.UploadFileAsync(_cachedCollectCredentials, testData);
|
||
Debug.Log(result
|
||
? $"<color=green>Collect 文件上传成功!</color> 路径: {_cachedCollectCredentials.bucket}/{_cachedCollectCredentials.objectKey}"
|
||
: "<color=red>Collect 文件上传失败</color>");
|
||
}
|
||
catch (Exception ex) { Debug.LogError($"<color=red>上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestMapUploadErrorNameFile()
|
||
{
|
||
if (_cachedCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 OSS 上传(错误路径) ===");
|
||
try
|
||
{
|
||
var testData = Encoding.UTF8.GetBytes("{\"test\":true,\"timestamp\":" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "}");
|
||
await _uploadService.TestInvalidObjectKeyAsync(_cachedCredentials, testData);
|
||
}
|
||
catch (Exception ex) { Debug.LogError($"<color=red>上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestCollectUploadErrorNameFile()
|
||
{
|
||
if (_cachedCollectCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 Collect 上传(错误路径) ===");
|
||
try
|
||
{
|
||
var testData = Encoding.UTF8.GetBytes("{\"test\":true,\"timestamp\":" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "}");
|
||
await _uploadService.TestInvalidObjectKeyAsync(_cachedCollectCredentials, testData);
|
||
}
|
||
catch (Exception ex) { Debug.LogError($"<color=red>Collect 上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestMapUploadBigFile()
|
||
{
|
||
if (_cachedCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 OSS 上传(大文件) ===");
|
||
try { await _uploadService.TestOversizedFileAsync(_cachedCredentials); }
|
||
catch (Exception ex) { Debug.LogError($"<color=red>上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestCollectUploadBigFile()
|
||
{
|
||
if (_cachedCollectCredentials == null) { Debug.LogError("请先执行 请求STS令牌"); return; }
|
||
Debug.Log("=== 开始测试 Collect 上传(大文件) ===");
|
||
try { await _uploadService.TestOversizedFileAsync(_cachedCollectCredentials); }
|
||
catch (Exception ex) { Debug.LogError($"<color=red>Collect 上传异常:</color> {ex.Message}"); }
|
||
}
|
||
|
||
public async void TestMapFullFlow()
|
||
{
|
||
Debug.Log("=== 开始完整流程测试 ===");
|
||
var manager = new OssManager();
|
||
var testData = Encoding.UTF8.GetBytes("{\"match\":\"test\",\"score\":100}");
|
||
var id = LobbyManager.Instance.Lobby.GetSelfMemberId();
|
||
var result = await manager.UploadGameDataAsync(id.ToString(), testData);
|
||
Debug.Log(result ? "<color=green>完整流程测试成功!</color>" : "<color=red>完整流程测试失败</color>");
|
||
}
|
||
|
||
public async void TestCollectFullFlow()
|
||
{
|
||
Debug.Log("=== 开始 Collect 完整流程测试 ===");
|
||
var manager = new OssManager();
|
||
var id = LobbyManager.Instance.Lobby.GetSelfMemberId();
|
||
var result = await manager.UploadCollectDataAsync(id.ToString(), new CollectData());
|
||
Debug.Log(result ? "<color=green>完整流程测试成功!</color>" : "<color=red>完整流程测试失败</color>");
|
||
}
|
||
|
||
public void TestDeserializeArchive()
|
||
{
|
||
string path = DefaultDataPath;
|
||
if (!Directory.Exists(path)) { Debug.LogError($"路径不存在: {path}"); return; }
|
||
|
||
var datFiles = Directory.GetFiles(path, "*.dat");
|
||
if (datFiles.Length == 0) { Debug.LogWarning($"路径下没有找到 .dat 文件: {path}"); return; }
|
||
|
||
Debug.Log($"=== 开始测试存档反序列化,共找到 {datFiles.Length} 个文件 ===");
|
||
int successCount = 0, failCount = 0;
|
||
|
||
foreach (var filePath in datFiles)
|
||
{
|
||
string fileName = Path.GetFileName(filePath);
|
||
try
|
||
{
|
||
byte[] data = File.ReadAllBytes(filePath);
|
||
var ossData = MemoryPackSerializer.Deserialize<OssData>(data);
|
||
if (ossData != null) { successCount++; Debug.Log($"<color=green>[成功] {fileName}</color>"); }
|
||
else { failCount++; Debug.LogError($"<color=red>[失败] {fileName} 反序列化结果为 null</color>"); }
|
||
}
|
||
catch (Exception ex) { failCount++; Debug.LogError($"<color=red>[异常] {fileName}: {ex.Message}</color>"); }
|
||
}
|
||
Debug.Log($"=== 反序列化完成: 成功 {successCount} / 失败 {failCount} / 共 {datFiles.Length} ===");
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// 文件下载逻辑
|
||
// ═══════════════════════════════════
|
||
|
||
private async void DownloadCollectFiles()
|
||
{
|
||
if (string.IsNullOrEmpty(_accessKeyId) || string.IsNullOrEmpty(_accessKeySecret))
|
||
{
|
||
_downloadStatus = "错误: 请先填写 AccessKey ID 和 AccessKey Secret";
|
||
Debug.LogError(_downloadStatus);
|
||
return;
|
||
}
|
||
|
||
_isDownloading = true;
|
||
_downloadStatus = "正在获取文件列表...";
|
||
Repaint();
|
||
|
||
try
|
||
{
|
||
var service = new OssDownloadService(_accessKeyId, _accessKeySecret, _ossEndpoint, _ossBucket);
|
||
var dataPath = DefaultDataPath;
|
||
if (!Directory.Exists(dataPath)) Directory.CreateDirectory(dataPath);
|
||
|
||
var allKeys = await service.ListObjectsAsync("collect/");
|
||
Debug.Log($"OSS collect/ 目录共发现 {allKeys.Count} 个文件");
|
||
|
||
if (allKeys.Count == 0)
|
||
{
|
||
_downloadStatus = "OSS collect/ 目录下没有文件";
|
||
_isDownloading = false;
|
||
Repaint();
|
||
return;
|
||
}
|
||
|
||
int skipCount = 0, downloadCount = 0, failCount = 0;
|
||
|
||
for (int i = 0; i < allKeys.Count; i++)
|
||
{
|
||
var key = allKeys[i];
|
||
var relativePath = key.Substring("collect/".Length);
|
||
var localPath = Path.Combine(dataPath, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
||
|
||
_downloadStatus = $"处理中 ({i + 1}/{allKeys.Count}): {relativePath}";
|
||
EditorUtility.DisplayProgressBar("下载 Collect 文件", _downloadStatus, (float)i / allKeys.Count);
|
||
|
||
if (File.Exists(localPath)) { skipCount++; continue; }
|
||
|
||
var success = await service.DownloadObjectAsync(key, localPath);
|
||
if (success) downloadCount++; else failCount++;
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
_downloadStatus = $"完成! 下载: {downloadCount}, 跳过: {skipCount}, 失败: {failCount}, 共: {allKeys.Count}";
|
||
Debug.Log($"<color=green>Collect 文件更新完成: 下载 {downloadCount}, 跳过 {skipCount}, 失败 {failCount}</color>");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
EditorUtility.ClearProgressBar();
|
||
_downloadStatus = $"错误: {ex.Message}";
|
||
Debug.LogError($"<color=red>Collect 文件下载失败: {ex.Message}</color>");
|
||
}
|
||
finally
|
||
{
|
||
_isDownloading = false;
|
||
Repaint();
|
||
}
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// 版本文件夹辅助
|
||
// ═══════════════════════════════════
|
||
|
||
private void RefreshVersionFolders()
|
||
{
|
||
if (string.IsNullOrEmpty(_statisticPath) || !Directory.Exists(_statisticPath))
|
||
{
|
||
_versionFolders = Array.Empty<string>();
|
||
_versionSelected = Array.Empty<bool>();
|
||
return;
|
||
}
|
||
|
||
var dirs = Directory.GetDirectories(_statisticPath);
|
||
Array.Sort(dirs);
|
||
var names = new string[dirs.Length];
|
||
for (int i = 0; i < dirs.Length; i++)
|
||
names[i] = Path.GetFileName(dirs[i]);
|
||
|
||
// 保留已有选中状态
|
||
var oldFolders = _versionFolders;
|
||
var oldSelected = _versionSelected;
|
||
_versionFolders = names;
|
||
_versionSelected = new bool[names.Length];
|
||
|
||
for (int i = 0; i < names.Length; i++)
|
||
{
|
||
int oldIdx = Array.IndexOf(oldFolders, names[i]);
|
||
_versionSelected[i] = oldIdx >= 0 ? oldSelected[oldIdx] : true;
|
||
}
|
||
}
|
||
|
||
private string[] GetSelectedDatFiles()
|
||
{
|
||
var files = new List<string>();
|
||
for (int i = 0; i < _versionFolders.Length; i++)
|
||
{
|
||
if (!_versionSelected[i]) continue;
|
||
var versionPath = Path.Combine(_statisticPath, _versionFolders[i]);
|
||
if (Directory.Exists(versionPath))
|
||
files.AddRange(Directory.GetFiles(versionPath, "*.dat", SearchOption.AllDirectories));
|
||
}
|
||
return files.ToArray();
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// 数据统计逻辑
|
||
// ═══════════════════════════════════
|
||
|
||
private void CalculateUnitStatistic()
|
||
{
|
||
_unitResult = new UnitStatisticResult();
|
||
_totalFileCount = 0;
|
||
_validFileCount = 0;
|
||
|
||
if (string.IsNullOrEmpty(_statisticPath) || !Directory.Exists(_statisticPath))
|
||
{
|
||
LogSystem.LogError($"路径不存在: {_statisticPath}");
|
||
return;
|
||
}
|
||
|
||
var files = GetSelectedDatFiles();
|
||
if (files.Length == 0)
|
||
{
|
||
LogSystem.LogError("没有选中任何版本文件夹,或选中的文件夹中没有 .dat 文件");
|
||
return;
|
||
}
|
||
_totalFileCount = files.Length;
|
||
|
||
foreach (var file in files)
|
||
{
|
||
try
|
||
{
|
||
var bytes = File.ReadAllBytes(file);
|
||
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
|
||
if (collectData == null) continue;
|
||
_validFileCount++;
|
||
ProcessCollectData(collectData, _unitResult);
|
||
}
|
||
catch (Exception e) { LogSystem.LogError($"反序列化失败: {file}\n{e.Message}"); }
|
||
}
|
||
|
||
if (_unitResult.MatchCount > 0)
|
||
{
|
||
_unitResult.AppearCount /= _unitResult.MatchCount;
|
||
_unitResult.DeathCount /= _unitResult.MatchCount;
|
||
_unitResult.KillCount /= _unitResult.MatchCount;
|
||
_unitResult.TotalDamageDealt /= _unitResult.MatchCount;
|
||
_unitResult.TotalDamageTaken /= _unitResult.MatchCount;
|
||
_unitResult.AverageDamageDealt /= _unitResult.MatchCount;
|
||
_unitResult.AverageDamageTaken /= _unitResult.MatchCount;
|
||
_unitResult.EarliestAppearTurn /= _unitResult.MatchCount;
|
||
}
|
||
|
||
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
|
||
}
|
||
|
||
private void CalculateTechStatistic()
|
||
{
|
||
_techResult = new TechStatisticResult();
|
||
_totalFileCount = 0;
|
||
_validFileCount = 0;
|
||
|
||
if (string.IsNullOrEmpty(_statisticPath) || !Directory.Exists(_statisticPath))
|
||
{
|
||
LogSystem.LogError($"路径不存在: {_statisticPath}");
|
||
return;
|
||
}
|
||
|
||
var files = GetSelectedDatFiles();
|
||
if (files.Length == 0)
|
||
{
|
||
LogSystem.LogError("没有选中任何版本文件夹,或选中的文件夹中没有 .dat 文件");
|
||
return;
|
||
}
|
||
_totalFileCount = files.Length;
|
||
|
||
foreach (var file in files)
|
||
{
|
||
try
|
||
{
|
||
var bytes = File.ReadAllBytes(file);
|
||
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
|
||
if (collectData == null) continue;
|
||
_validFileCount++;
|
||
ProcessCollectData(collectData, _techResult);
|
||
}
|
||
catch (Exception e) { LogSystem.LogError($"反序列化失败: {file}\n{e.Message}"); }
|
||
}
|
||
|
||
if (_techResult.LearnMatchCount > 0)
|
||
{
|
||
_techResult.LearnCount /= _techResult.LearnMatchCount;
|
||
_techResult.LearnTurn /= _techResult.LearnMatchCount;
|
||
}
|
||
|
||
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
|
||
}
|
||
|
||
private void CalculateEmpireStatistic()
|
||
{
|
||
_empireResult = new EmpireStatisticResult();
|
||
_totalFileCount = 0;
|
||
_validFileCount = 0;
|
||
|
||
if (string.IsNullOrEmpty(_statisticPath) || !Directory.Exists(_statisticPath))
|
||
{
|
||
LogSystem.LogError($"路径不存在: {_statisticPath}");
|
||
return;
|
||
}
|
||
|
||
var files = GetSelectedDatFiles();
|
||
if (files.Length == 0)
|
||
{
|
||
LogSystem.LogError("没有选中任何版本文件夹,或选中的文件夹中没有 .dat 文件");
|
||
return;
|
||
}
|
||
_totalFileCount = files.Length;
|
||
|
||
foreach (var file in files)
|
||
{
|
||
try
|
||
{
|
||
var bytes = File.ReadAllBytes(file);
|
||
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
|
||
if (collectData == null) continue;
|
||
_validFileCount++;
|
||
ProcessCollectData(collectData, _empireResult);
|
||
}
|
||
catch (Exception e) { LogSystem.LogError($"反序列化失败: {file}\n{e.Message}"); }
|
||
}
|
||
|
||
if (_empireResult.MatchCount > 0)
|
||
{
|
||
_empireResult.CoinPerTurn /= _empireResult.MatchCount;
|
||
_empireResult.TechPointPerTurn /= _empireResult.MatchCount;
|
||
_empireResult.CityCount /= _empireResult.MatchCount;
|
||
_empireResult.MaxCityLevel /= _empireResult.MatchCount;
|
||
_empireResult.CityLevel /= _empireResult.MatchCount;
|
||
_empireResult.TechCount /= _empireResult.MatchCount;
|
||
}
|
||
|
||
LogSystem.LogInfo($"统计完成: 共扫描 {_totalFileCount} 个文件,有效 {_validFileCount} 个");
|
||
}
|
||
|
||
|
||
// ─────────────────────────────────
|
||
// 数据处理
|
||
// ─────────────────────────────────
|
||
|
||
private void ProcessCollectData(CollectData collectData, UnitStatisticResult result)
|
||
{
|
||
var turn = -1;
|
||
var appearCount = 0;
|
||
foreach (var addUnit in collectData.AddUnits)
|
||
{
|
||
if (!MatchUnitFullType(addUnit.UnitType)) continue;
|
||
appearCount++;
|
||
if (turn < 0 || addUnit.Turn < turn) turn = (int)addUnit.Turn;
|
||
}
|
||
|
||
if (appearCount == 0) return;
|
||
result.MatchCount++;
|
||
result.EarliestAppearTurn += turn;
|
||
result.AppearCount += appearCount;
|
||
|
||
foreach (var dmg in collectData.Damages)
|
||
{
|
||
if (MatchUnitFullType(dmg.OriginUnitType))
|
||
{
|
||
result.TotalDamageDealt += dmg.OriginDamage;
|
||
result.AverageDamageDealt += dmg.OriginDamage / (float)appearCount;
|
||
if (dmg.IsKill) result.KillCount++;
|
||
}
|
||
if (MatchUnitFullType(dmg.TargetUnitType))
|
||
{
|
||
result.TotalDamageTaken += dmg.TargetDamage;
|
||
result.AverageDamageTaken += dmg.TargetDamage / (float)appearCount;
|
||
if (dmg.IsKill) result.DeathCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ProcessCollectData(CollectData collectData, TechStatisticResult result)
|
||
{
|
||
var learnTurn = -1;
|
||
foreach (var techCollectData in collectData.LearnTechs)
|
||
{
|
||
if (!MatchTech(techCollectData.Empire, techCollectData.TechType)) continue;
|
||
result.LearnCount++;
|
||
if (learnTurn < 0 || learnTurn > techCollectData.Turn) learnTurn = (int)techCollectData.Turn;
|
||
}
|
||
|
||
if (learnTurn > 0)
|
||
{
|
||
result.LearnTurn += learnTurn;
|
||
result.LearnMatchCount++;
|
||
}
|
||
|
||
LogSystem.LogInfo($"result.LearnCount {result.LearnCount} result.LearnMatchCount{result.LearnMatchCount}");
|
||
}
|
||
|
||
private void ProcessCollectData(CollectData collectData, EmpireStatisticResult result)
|
||
{
|
||
var resultCache = new EmpireStatisticResult();
|
||
foreach (var turnStartData in collectData.OnTurnStarts)
|
||
{
|
||
if (!MatchEmpire(turnStartData.Empire)) continue;
|
||
resultCache.CoinPerTurn += turnStartData.PlayerCoinPerTurn;
|
||
resultCache.TechPointPerTurn += turnStartData.PlayerTechPointPerTurn;
|
||
resultCache.CityCount += turnStartData.CityCount;
|
||
resultCache.MaxCityLevel += turnStartData.MaxCityLevel;
|
||
resultCache.CityLevel += turnStartData.AverageCityLevel;
|
||
resultCache.TechCount += turnStartData.TechCount;
|
||
resultCache.TurnCount++;
|
||
}
|
||
|
||
if (resultCache.TurnCount > 0)
|
||
{
|
||
result.MatchCount++;
|
||
result.CoinPerTurn += resultCache.CoinPerTurn / resultCache.TurnCount;
|
||
result.TechPointPerTurn += resultCache.TechPointPerTurn / resultCache.TurnCount;
|
||
result.CityCount += resultCache.CityCount / resultCache.TurnCount;
|
||
result.MaxCityLevel += resultCache.MaxCityLevel / resultCache.TurnCount;
|
||
result.CityLevel += resultCache.CityLevel / resultCache.TurnCount;
|
||
result.TechCount += resultCache.TechCount / resultCache.TurnCount;
|
||
}
|
||
}
|
||
|
||
|
||
// ─────────────────────────────────
|
||
// 匹配方法
|
||
// ─────────────────────────────────
|
||
|
||
private bool MatchUnitFullType(UnitFullType unitFullType)
|
||
{
|
||
if (_unitLimit.LimitUnitType && unitFullType.UnitType != _unitLimit.UnitType) return false;
|
||
if (_unitLimit.LimitGiantType && unitFullType.GiantType != _unitLimit.GiantType) return false;
|
||
if (_unitLimit.LimitLevel && unitFullType.UnitLevel != _unitLimit.Level) return false;
|
||
return true;
|
||
}
|
||
|
||
private bool MatchTech(Empire empire, TechType techType)
|
||
{
|
||
if (_techLimit.LimitEmpire && empire != _techLimit.Empire) return false;
|
||
if (_techLimit.LimitTechType && techType != _techLimit.TechType) return false;
|
||
return true;
|
||
}
|
||
|
||
private bool MatchEmpire(Empire empire)
|
||
{
|
||
if (_empireLimit.LimitEmpire && empire != _empireLimit.Empire) return false;
|
||
return true;
|
||
}
|
||
|
||
|
||
// ═══════════════════════════════════
|
||
// JSON 导出 (供 Dashboard 使用)
|
||
// ═══════════════════════════════════
|
||
|
||
[MenuItem("Tools/OSS 导出 JSON (Dashboard)")]
|
||
private static void ExportCollectDataToJson()
|
||
{
|
||
var dataPath = DefaultDataPath;
|
||
if (!Directory.Exists(dataPath))
|
||
{
|
||
Debug.LogError($"OSS 数据目录不存在: {dataPath}");
|
||
return;
|
||
}
|
||
|
||
var jsonOutDir = Path.Combine(dataPath, "JsonExport");
|
||
if (!Directory.Exists(jsonOutDir))
|
||
Directory.CreateDirectory(jsonOutDir);
|
||
|
||
var allDatFiles = Directory.GetFiles(dataPath, "*.dat", SearchOption.AllDirectories);
|
||
int success = 0, fail = 0, skip = 0;
|
||
|
||
for (int i = 0; i < allDatFiles.Length; i++)
|
||
{
|
||
var datFile = allDatFiles[i];
|
||
// 跳过 JsonExport 目录自身
|
||
if (datFile.Replace('\\', '/').Contains("/JsonExport/")) continue;
|
||
|
||
var relativePath = datFile.Substring(dataPath.Length).TrimStart(Path.DirectorySeparatorChar, '/');
|
||
var jsonRelPath = Path.ChangeExtension(relativePath, ".json");
|
||
var jsonOutPath = Path.Combine(jsonOutDir, jsonRelPath);
|
||
|
||
// 如果 JSON 已存在且比 dat 新,跳过
|
||
if (File.Exists(jsonOutPath) && File.GetLastWriteTimeUtc(jsonOutPath) >= File.GetLastWriteTimeUtc(datFile))
|
||
{
|
||
skip++;
|
||
continue;
|
||
}
|
||
|
||
EditorUtility.DisplayProgressBar("导出 JSON", $"{i + 1}/{allDatFiles.Length}: {relativePath}",
|
||
(float)i / allDatFiles.Length);
|
||
|
||
try
|
||
{
|
||
var bytes = File.ReadAllBytes(datFile);
|
||
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
|
||
if (collectData == null) { fail++; continue; }
|
||
|
||
var json = CollectDataToJson(collectData, relativePath);
|
||
var dir = Path.GetDirectoryName(jsonOutPath);
|
||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||
Directory.CreateDirectory(dir);
|
||
File.WriteAllText(jsonOutPath, json, new UTF8Encoding(false));
|
||
success++;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
fail++;
|
||
Debug.LogWarning($"导出失败: {relativePath} - {e.Message}");
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
Debug.Log($"<color=green>JSON 导出完成: 成功 {success}, 跳过 {skip}, 失败 {fail}</color>");
|
||
}
|
||
|
||
private static string CollectDataToJson(CollectData data, string sourcePath)
|
||
{
|
||
var sb = new StringBuilder(4096);
|
||
sb.Append("{");
|
||
|
||
// 元信息
|
||
sb.Append("\"memberId\":").Append(data.MemberId);
|
||
sb.Append(",\"version\":\"").Append(EscapeJson(data.Version ?? "")).Append("\"");
|
||
sb.Append(",\"source\":\"").Append(EscapeJson(sourcePath)).Append("\"");
|
||
|
||
// Damages
|
||
sb.Append(",\"damages\":[");
|
||
for (int i = 0; i < data.Damages.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var d = data.Damages[i];
|
||
sb.Append("{\"turn\":").Append(d.Turn)
|
||
.Append(",\"isKill\":").Append(d.IsKill ? "true" : "false")
|
||
.Append(",\"originCiv\":").Append((int)d.OriginEmpire.Civ)
|
||
.Append(",\"originForce\":").Append((int)d.OriginEmpire.Force)
|
||
.Append(",\"originUnitType\":").Append((int)d.OriginUnitType.UnitType)
|
||
.Append(",\"originGiantType\":").Append((int)d.OriginUnitType.GiantType)
|
||
.Append(",\"originLevel\":").Append(d.OriginUnitType.UnitLevel)
|
||
.Append(",\"originDamage\":").Append(d.OriginDamage)
|
||
.Append(",\"targetCiv\":").Append((int)d.TargetEmpire.Civ)
|
||
.Append(",\"targetForce\":").Append((int)d.TargetEmpire.Force)
|
||
.Append(",\"targetUnitType\":").Append((int)d.TargetUnitType.UnitType)
|
||
.Append(",\"targetGiantType\":").Append((int)d.TargetUnitType.GiantType)
|
||
.Append(",\"targetLevel\":").Append(d.TargetUnitType.UnitLevel)
|
||
.Append(",\"targetDamage\":").Append(d.TargetDamage)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// AddUnits
|
||
sb.Append(",\"addUnits\":[");
|
||
for (int i = 0; i < data.AddUnits.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var a = data.AddUnits[i];
|
||
sb.Append("{\"turn\":").Append(a.Turn)
|
||
.Append(",\"civ\":").Append((int)a.Empire.Civ)
|
||
.Append(",\"force\":").Append((int)a.Empire.Force)
|
||
.Append(",\"unitType\":").Append((int)a.UnitType.UnitType)
|
||
.Append(",\"giantType\":").Append((int)a.UnitType.GiantType)
|
||
.Append(",\"level\":").Append(a.UnitType.UnitLevel)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// TransformUnits
|
||
sb.Append(",\"transformUnits\":[");
|
||
for (int i = 0; i < data.TransformUnits.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var t = data.TransformUnits[i];
|
||
sb.Append("{\"turn\":").Append(t.Turn)
|
||
.Append(",\"civ\":").Append((int)t.Empire.Civ)
|
||
.Append(",\"force\":").Append((int)t.Empire.Force)
|
||
.Append(",\"originUnitType\":").Append((int)t.OriginUnitType.UnitType)
|
||
.Append(",\"originGiantType\":").Append((int)t.OriginUnitType.GiantType)
|
||
.Append(",\"originLevel\":").Append(t.OriginUnitType.UnitLevel)
|
||
.Append(",\"targetUnitType\":").Append((int)t.TargetUnitType.UnitType)
|
||
.Append(",\"targetGiantType\":").Append((int)t.TargetUnitType.GiantType)
|
||
.Append(",\"targetLevel\":").Append(t.TargetUnitType.UnitLevel)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// LearnTechs
|
||
sb.Append(",\"learnTechs\":[");
|
||
for (int i = 0; i < data.LearnTechs.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var l = data.LearnTechs[i];
|
||
sb.Append("{\"turn\":").Append(l.Turn)
|
||
.Append(",\"civ\":").Append((int)l.Empire.Civ)
|
||
.Append(",\"force\":").Append((int)l.Empire.Force)
|
||
.Append(",\"techType\":").Append((int)l.TechType)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// OnTurnStarts
|
||
sb.Append(",\"onTurnStarts\":[");
|
||
for (int i = 0; i < data.OnTurnStarts.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var o = data.OnTurnStarts[i];
|
||
sb.Append("{\"turn\":").Append(o.Turn)
|
||
.Append(",\"civ\":").Append((int)o.Empire.Civ)
|
||
.Append(",\"force\":").Append((int)o.Empire.Force)
|
||
.Append(",\"coinPerTurn\":").Append(o.PlayerCoinPerTurn)
|
||
.Append(",\"techPointPerTurn\":").Append(o.PlayerTechPointPerTurn)
|
||
.Append(",\"cityCount\":").Append(o.CityCount)
|
||
.Append(",\"maxCityLevel\":").Append(o.MaxCityLevel)
|
||
.Append(",\"avgCityLevel\":").Append(o.AverageCityLevel)
|
||
.Append(",\"techCount\":").Append(o.TechCount)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// MatchGameEnds
|
||
sb.Append(",\"matchGameEnds\":[");
|
||
for (int i = 0; i < data.MatchGameEnds.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var m = data.MatchGameEnds[i];
|
||
sb.Append("{\"turn\":").Append(m.Turn)
|
||
.Append(",\"civ\":").Append((int)m.Empire.Civ)
|
||
.Append(",\"force\":").Append((int)m.Empire.Force)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
// PlayerGameEnds
|
||
sb.Append(",\"playerGameEnds\":[");
|
||
for (int i = 0; i < data.PlayerGameEnds.Count; i++)
|
||
{
|
||
if (i > 0) sb.Append(",");
|
||
var p = data.PlayerGameEnds[i];
|
||
sb.Append("{\"turn\":").Append(p.Turn)
|
||
.Append(",\"selfCiv\":").Append((int)p.SelfEmpire.Civ)
|
||
.Append(",\"selfForce\":").Append((int)p.SelfEmpire.Force)
|
||
.Append(",\"killerCiv\":").Append((int)p.KillerEmpire.Civ)
|
||
.Append(",\"killerForce\":").Append((int)p.KillerEmpire.Force)
|
||
.Append("}");
|
||
}
|
||
sb.Append("]");
|
||
|
||
sb.Append("}");
|
||
return sb.ToString();
|
||
}
|
||
|
||
private static string EscapeJson(string s)
|
||
{
|
||
if (string.IsNullOrEmpty(s)) return "";
|
||
return s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r");
|
||
}
|
||
}
|
||
} |