第13章 Catalog系统深入

第13章 Catalog系统深入

Catalog文件结构分析

Unity Addressables的Catalog系统是整个资源管理架构的核心组件之一,它负责维护资源地址到实际位置的映射关系,并支持内容更新和版本管理。Catalog文件是JSON格式的结构化数据,包含了资源定位所需的所有信息。

Catalog文件组成结构

Catalog文件主要由以下几个部分组成:

1
2
3
4
5
6
7
8
9
{
"catalogVersion": 3,
"resourceProviderData": [...],
"locationData": [...],
"dependencyData": [...],
"profileVersion": 1,
"profiles": [...],
"settings": {...}
}

让我们深入分析每个部分的详细结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
using System;
using System.Collections.Generic;
using UnityEngine;

public class CatalogStructureAnalyzer : MonoBehaviour
{
[Header("Catalog结构分析配置")]
public bool enableDetailedAnalysis = true;
public bool enableValidation = true;
public bool enablePerformanceProfiling = true;

// Catalog文件的主要组成部分
[System.Serializable]
public class ContentCatalogData
{
public int catalogVersion; // Catalog版本
public List<ResourceProviderData> providerData; // 资源提供者数据
public List<LocationData> locationData; // 位置数据
public List<DependencyData> dependencyData; // 依赖数据
public int profileVersion; // 配置文件版本
public List<ProfileData> profiles; // 配置文件数据
public SettingsData settings; // 设置数据
public string hash; // 内容哈希
public string providerVersion; // 提供者版本
}

[System.Serializable]
public class ResourceProviderData
{
public string providerId; // 提供者ID
public string internalId; // 内部ID
public string resourceType; // 资源类型
public string providerType; // 提供者类型
public List<string> keys; // 关键字列表
}

[System.Serializable]
public class LocationData
{
public string internalId; // 内部ID(Bundle路径或资源路径)
public string provider; // 使用的提供者
public List<string> keys; // 位置的关键字
public string primaryKey; // 主关键字(通常是资源地址)
public object data; // 额外数据
public List<string> dependencies; // 依赖项列表
public long resourceSize; // 资源大小
public string hash; // 资源哈希
}

[System.Serializable]
public class DependencyData
{
public string key; // 依赖键
public List<string> dependencies; // 依赖列表
}

[System.Serializable]
public class ProfileData
{
public string profileName; // 配置文件名
public Dictionary<string, string> variables; // 变量映射
public string id; // 配置文件ID
}

[System.Serializable]
public class SettingsData
{
public string buildPath; // 构建路径
public string loadPath; // 加载路径
public string bundleMode; // Bundle模式
public string compression; // 压缩方式
public bool useCache; // 是否使用缓存
public int cacheSize; // 缓存大小
}

[System.Serializable]
public class CatalogAnalysisResult
{
public string catalogName;
public int totalLocations;
public int totalProviders;
public int totalDependencies;
public int totalProfiles;
public long totalSize;
public float analysisTime;
public Dictionary<string, int> providerTypeCount;
public Dictionary<string, int> resourceTypeCount;
public List<string> validationErrors;
}

void Start()
{
Debug.Log("Catalog结构分析器启动");
AnalyzeCatalogStructure();
}

private void AnalyzeCatalogStructure()
{
Debug.Log("=== Catalog文件结构分析 ===");

var structureAnalysis = new System.Text.StringBuilder();

structureAnalysis.AppendLine("Catalog文件结构组成:");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("1. catalogVersion (整数):");
structureAnalysis.AppendLine(" - Catalog格式版本号");
structureAnalysis.AppendLine(" - 用于向后兼容性检查");
structureAnalysis.AppendLine(" - 当前版本通常为3");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("2. resourceProviderData (数组):");
structureAnalysis.AppendLine(" - 定义可用的资源提供者");
structureAnalysis.AppendLine(" - 包含提供者ID、类型、支持的资源类型");
structureAnalysis.AppendLine(" - 用于资源加载时选择正确的提供者");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("3. locationData (数组):");
structureAnalysis.AppendLine(" - 核心数据:资源位置映射");
structureAnalysis.AppendLine(" - 包含资源地址、实际路径、依赖关系");
structureAnalysis.AppendLine(" - 每个条目对应一个可加载的资源");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("4. dependencyData (数组):");
structureAnalysis.AppendLine(" - 资源依赖关系数据");
structureAnalysis.AppendLine(" - 定义资源加载时需要预先加载的依赖");
structureAnalysis.AppendLine(" - 用于构建依赖图和加载顺序");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("5. profileVersion (整数):");
structureAnalysis.AppendLine(" - 配置文件格式版本");
structureAnalysis.AppendLine(" - 用于管理不同平台的路径配置");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("6. profiles (数组):");
structureAnalysis.AppendLine(" - 平台和环境特定的配置");
structureAnalysis.AppendLine(" - 包含路径变量和构建设置");
structureAnalysis.AppendLine(" - 支持多平台部署");
structureAnalysis.AppendLine();

structureAnalysis.AppendLine("7. settings (对象):");
structureAnalysis.AppendLine(" - 构建和运行时设置");
structureAnalysis.AppendLine(" - 包含路径、压缩、缓存等配置");
structureAnalysis.AppendLine(" - 影响Bundle的构建和加载行为");

Debug.Log(structureAnalysis.ToString());

if (enableDetailedAnalysis)
{
AnalyzeDetailedStructure();
}
}

private void AnalyzeDetailedStructure()
{
Debug.Log("=== 详细结构分析 ===");

var detailedAnalysis = new System.Text.StringBuilder();

detailedAnalysis.AppendLine("LocationData详细结构:");
detailedAnalysis.AppendLine();

detailedAnalysis.AppendLine("internalId:");
detailedAnalysis.AppendLine(" - 对于AssetBundle: Bundle文件的完整路径");
detailedAnalysis.AppendLine(" - 对于直接资源: 资源在Bundle内的内部路径");
detailedAnalysis.AppendLine(" - 用于ResourceManager定位实际资源");
detailedAnalysis.AppendLine();

detailedAnalysis.AppendLine("provider:");
detailedAnalysis.AppendLine(" - 指定用于加载此资源的提供者类型");
detailedAnalysis.AppendLine(" - 如: 'PrefabProvider', 'TextureProvider', 'AssetProvider'");
detailedAnalysis.AppendLine(" - 决定资源的加载和处理方式");
detailedAnalysis.AppendLine();

detailedAnalysis.AppendLine("keys:");
detailedAnalysis.AppendLine(" - 用于查找此位置的多个关键字");
detailedAnalysis.AppendLine(" - 通常包含资源地址和标签");
detailedAnalysis.AppendLine(" - 支持多种方式定位同一资源");
detailedAnalysis.AppendLine();

detailedAnalysis.AppendLine("primaryKey:");
detailedAnalysis.AppendLine(" - 主要的查找关键字");
detailedAnalysis.AppendLine(" - 通常是开发者指定的资源地址");
detailedAnalysis.AppendLine(" - 最常用的定位方式");
detailedAnalysis.AppendLine();

detailedAnalysis.AppendLine("dependencies:");
detailedAnalysis.AppendLine(" - 此资源依赖的其他资源列表");
detailedAnalysis.AppendLine(" - 在加载此资源前必须先加载的资源");
detailedAnalysis.AppendLine(" - 用于构建资源依赖图");

Debug.Log(detailedAnalysis.ToString());
}

/// <summary>
/// 模拟Catalog数据分析
/// </summary>
public CatalogAnalysisResult SimulateCatalogAnalysis(int locationCount = 1000, int providerCount = 10)
{
var startTime = Time.realtimeSinceStartup;

var result = new CatalogAnalysisResult
{
catalogName = "MockCatalog",
totalLocations = locationCount,
totalProviders = providerCount,
totalDependencies = locationCount / 3, // 假设约1/3的资源有依赖
totalProfiles = 5, // 假设有5个配置文件
totalSize = locationCount * 256, // 假设每个位置数据约256字节
providerTypeCount = new Dictionary<string, int>(),
resourceTypeCount = new Dictionary<string, int>(),
validationErrors = new List<string>()
};

// 统计提供者类型
var providerTypes = new[] { "PrefabProvider", "TextureProvider", "AssetProvider", "SceneProvider", "AudioProvider" };
for (int i = 0; i < providerCount; i++)
{
string type = providerTypes[i % providerTypes.Length];
if (!result.providerTypeCount.ContainsKey(type))
{
result.providerTypeCount[type] = 0;
}
result.providerTypeCount[type]++;
}

// 统计资源类型
var resourceTypes = new[] { "GameObject", "Texture2D", "AudioClip", "Mesh", "Material" };
for (int i = 0; i < locationCount; i++)
{
string type = resourceTypes[i % resourceTypes.Length];
if (!result.resourceTypeCount.ContainsKey(type))
{
result.resourceTypeCount[type] = 0;
}
result.resourceTypeCount[type]++;
}

// 模拟验证过程
if (enableValidation)
{
SimulateValidation(result);
}

result.analysisTime = Time.realtimeSinceStartup - startTime;

if (enablePerformanceProfiling)
{
Debug.Log($"Catalog分析完成: {result.totalLocations} 个位置, " +
$"耗时 {result.analysisTime:F3}s, 大小 {FormatBytes(result.totalSize)}");
}

return result;
}

private void SimulateValidation(CatalogAnalysisResult result)
{
// 模拟一些常见的验证检查
if (result.totalLocations == 0)
{
result.validationErrors.Add("Catalog不包含任何资源位置");
}

if (result.totalProviders == 0)
{
result.validationErrors.Add("Catalog不包含任何资源提供者");
}

// 检查是否有重复的主键
// 在实际实现中,这里会检查primaryKey的唯一性
if (result.totalLocations > 10000) // 假设超过10000个位置需要警告
{
result.validationErrors.Add($"Catalog包含大量资源({result.totalLocations}),可能影响性能");
}
}

/// <summary>
/// 获取Catalog优化建议
/// </summary>
public string GetCatalogOptimizationSuggestions(CatalogAnalysisResult result)
{
var suggestions = new System.Text.StringBuilder();
suggestions.AppendLine("=== Catalog优化建议 ===");

suggestions.AppendLine("1. 资源分组优化:");
suggestions.AppendLine(" - 将相关资源分组到同一个Bundle以减少依赖");
suggestions.AppendLine(" - 避免单个Bundle包含过多不相关的资源");
suggestions.AppendLine(" - 使用合适的分组策略(按功能、场景、更新频率)");
suggestions.AppendLine();

suggestions.AppendLine("2. 依赖管理:");
suggestions.AppendLine(" - 减少不必要的依赖关系");
suggestions.AppendLine(" - 将共享资源提取到独立的Bundle");
suggestions.AppendLine(" - 避免循环依赖");
suggestions.AppendLine();

suggestions.AppendLine("3. 路径优化:");
suggestions.AppendLine(" - 使用相对路径而不是绝对路径");
suggestions.AppendLine(" - 避免过长的路径名称");
suggestions.AppendLine(" - 统一路径格式和命名规范");
suggestions.AppendLine();

suggestions.AppendLine("4. 性能考虑:");
if (result.totalLocations > 5000)
{
suggestions.AppendLine(" - Catalog过大,考虑拆分到多个Catalog");
suggestions.AppendLine(" - 使用Content Update功能进行增量更新");
}
else
{
suggestions.AppendLine(" - Catalog大小适中,性能良好");
}

if (result.validationErrors.Count > 0)
{
suggestions.AppendLine();
suggestions.AppendLine("发现的问题:");
foreach (var error in result.validationErrors)
{
suggestions.AppendLine($" • {error}");
}
}

return suggestions.ToString();
}

private string FormatBytes(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
double len = bytes;

while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}

return $"{len:0.##} {sizes[order]}";
}

void OnDestroy()
{
Debug.Log("Catalog结构分析器清理完成");
}
}

Catalog版本管理

Catalog的版本管理是确保资源更新和兼容性的关键机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
using System;
using System.Collections.Generic;
using UnityEngine;

public class CatalogVersionManager : MonoBehaviour
{
[Header("Catalog版本管理配置")]
public int currentCatalogVersion = 3;
public bool enableVersionValidation = true;
public bool enableBackwardCompatibility = true;

[System.Serializable]
public class CatalogVersionInfo
{
public int version;
public string unityVersion;
public DateTime buildTime;
public string buildPlatform;
public string contentHash;
public List<string> supportedVersions; // 支持的旧版本
public bool isDeprecated;
}

[System.Serializable]
public class VersionCompatibilityResult
{
public bool isCompatible;
public string compatibilityLevel; // "Full", "Partial", "None"
public List<string> compatibilityIssues;
public List<string> requiredUpdates;
}

private Dictionary<int, CatalogVersionInfo> versionHistory = new Dictionary<int, CatalogVersionInfo>();

void Start()
{
Debug.Log("Catalog版本管理器启动");
InitializeVersionHistory();
}

private void InitializeVersionHistory()
{
// 初始化版本历史记录
for (int v = 1; v <= currentCatalogVersion; v++)
{
versionHistory[v] = new CatalogVersionInfo
{
version = v,
unityVersion = Application.unityVersion,
buildTime = DateTime.Now.AddHours(-v), // 模拟不同时间构建
buildPlatform = Application.platform.ToString(),
contentHash = GenerateContentHash(v),
supportedVersions = new List<string>(),
isDeprecated = v < currentCatalogVersion - 2 // 旧版本标记为废弃
};

// 设置兼容性支持
if (enableBackwardCompatibility && v >= 2)
{
versionHistory[v].supportedVersions.Add((v - 1).ToString());
}
}

Debug.Log($"初始化了 {versionHistory.Count} 个Catalog版本");
}

private string GenerateContentHash(int version)
{
// 生成模拟的内容哈希
return $"hash_v{version}_{DateTime.Now.Ticks % 1000000}";
}

/// <summary>
/// 验证Catalog版本兼容性
/// </summary>
public VersionCompatibilityResult ValidateVersionCompatibility(int catalogVersion, string unityVersion)
{
var result = new VersionCompatibilityResult
{
isCompatible = false,
compatibilityLevel = "None",
compatibilityIssues = new List<string>(),
requiredUpdates = new List<string>()
};

if (!versionHistory.ContainsKey(catalogVersion))
{
result.compatibilityIssues.Add($"未知的Catalog版本: {catalogVersion}");
return result;
}

var versionInfo = versionHistory[catalogVersion];

// 检查Unity版本兼容性
if (!IsUnityVersionCompatible(unityVersion, versionInfo.unityVersion))
{
result.compatibilityIssues.Add($"Unity版本不兼容: {unityVersion} vs {versionInfo.unityVersion}");
}

// 检查版本差距
int versionGap = currentCatalogVersion - catalogVersion;
if (versionGap == 0)
{
result.isCompatible = true;
result.compatibilityLevel = "Full";
}
else if (versionGap == 1)
{
result.isCompatible = true;
result.compatibilityLevel = "Partial";
result.requiredUpdates.Add("建议更新到最新版本以获得最佳性能");
}
else if (versionGap <= 3 && enableBackwardCompatibility)
{
result.isCompatible = true;
result.compatibilityLevel = "Partial";
result.requiredUpdates.Add($"版本差距较大({versionGap}),建议更新");
result.requiredUpdates.Add("可能需要额外的兼容性处理");
}
else
{
result.compatibilityIssues.Add($"版本差距过大({versionGap}),不建议使用");
}

return result;
}

private bool IsUnityVersionCompatible(string currentVersion, string catalogVersion)
{
// 简化的版本兼容性检查
// 在实际实现中,这会有更复杂的版本比较逻辑

try
{
var currentVer = new Version(currentVersion.Split('f')[0].Split('p')[0]);
var catalogVer = new Version(catalogVersion.Split('f')[0].Split('p')[0]);

// 主版本相同通常兼容
return currentVer.Major == catalogVer.Major;
}
catch
{
// 如果版本解析失败,保守地认为不兼容
return false;
}
}

/// <summary>
/// 获取版本升级路径
/// </summary>
public List<int> GetUpgradePath(int fromVersion, int toVersion)
{
var path = new List<int>();

if (fromVersion >= toVersion)
{
return path; // 无需升级
}

// 简单的线性升级路径
for (int v = fromVersion + 1; v <= toVersion; v++)
{
path.Add(v);
}

return path;
}

/// <summary>
/// 分析版本使用情况
/// </summary>
public string AnalyzeVersionUsage()
{
var analysis = new System.Text.StringBuilder();
analysis.AppendLine("=== Catalog版本使用分析 ===");

analysis.AppendLine($"当前版本: {currentCatalogVersion}");
analysis.AppendLine($"历史版本数: {versionHistory.Count}");
analysis.AppendLine($"启用版本验证: {enableVersionValidation}");
analysis.AppendLine($"启用向后兼容: {enableBackwardCompatibility}");
analysis.AppendLine();

analysis.AppendLine("版本详情:");
foreach (var kvp in versionHistory)
{
var info = kvp.Value;
analysis.AppendLine($" v{kvp.Key}:");
analysis.AppendLine($" Unity版本: {info.unityVersion}");
analysis.AppendLine($" 构建时间: {info.buildTime:yyyy-MM-dd HH:mm:ss}");
analysis.AppendLine($" 平台: {info.buildPlatform}");
analysis.AppendLine($" 内容哈希: {info.contentHash}");
analysis.AppendLine($" 已废弃: {info.isDeprecated}");
analysis.AppendLine($" 支持版本: {(info.supportedVersions.Count > 0 ? string.Join(", ", info.supportedVersions) : "无")}");
analysis.AppendLine();
}

return analysis.ToString();
}

/// <summary>
/// 获取版本管理最佳实践
/// </summary>
public string GetVersionManagementBestPractices()
{
var practices = new System.Text.StringBuilder();
practices.AppendLine("=== Catalog版本管理最佳实践 ===");

practices.AppendLine("1. 版本控制:");
practices.AppendLine(" - 为每次构建分配唯一的版本号");
practices.AppendLine(" - 使用语义化版本控制");
practices.AppendLine(" - 维护版本变更日志");
practices.AppendLine();

practices.AppendLine("2. 兼容性策略:");
practices.AppendLine(" - 明确定义兼容性范围");
practices.AppendLine(" - 提供版本迁移工具");
practices.AppendLine(" - 测试跨版本兼容性");
practices.AppendLine();

practices.AppendLine("3. 更新策略:");
practices.AppendLine(" - 实施渐进式更新");
practices.AppendLine(" - 提供回滚机制");
practices.AppendLine(" - 监控更新成功率");
practices.AppendLine();

practices.AppendLine("4. 性能考虑:");
practices.AppendLine(" - 避免频繁的版本更新");
practices.AppendLine(" - 优化版本验证逻辑");
practices.AppendLine(" - 实施版本缓存策略");

return practices.ToString();
}

void OnDestroy()
{
Debug.Log("Catalog版本管理器清理完成");
}
}

Hash与CRC校验机制

Hash和CRC校验是确保资源完整性和版本一致性的关键机制,在Addressables系统中发挥着重要作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;

public class HashCRCValidationSystem : MonoBehaviour
{
[Header("Hash与CRC校验系统配置")]
public bool enableContentValidation = true;
public bool enableFastValidation = true;
public int validationBatchSize = 10;

[System.Serializable]
public class HashValidationResult
{
public string resourceId;
public string expectedHash;
public string actualHash;
public bool isValid;
public float validationTime;
public string validationMethod;
public string errorMessage;
}

[System.Serializable]
public class CRCValidationResult
{
public string resourceId;
public uint expectedCRC;
public uint actualCRC;
public bool isValid;
public float validationTime;
public string errorMessage;
}

[System.Serializable]
public class ValidationProfile
{
public string validationType; // "Hash", "CRC", "Both"
public float averageTime;
public int totalValidations;
public int successfulValidations;
public int failedValidations;
public DateTime lastRun;
}

private Dictionary<string, ValidationProfile> validationProfiles = new Dictionary<string, ValidationProfile>();
private Dictionary<string, string> resourceHashCache = new Dictionary<string, string>();
private Dictionary<string, uint> resourceCRCCache = new Dictionary<string, uint>();

void Start()
{
Debug.Log("Hash与CRC校验系统启动");
AnalyzeValidationMechanisms();
}

private void AnalyzeValidationMechanisms()
{
Debug.Log("=== Hash与CRC校验机制分析 ===");

var mechanismAnalysis = new System.Text.StringBuilder();

mechanismAnalysis.AppendLine("Hash校验机制:");
mechanismAnalysis.AppendLine();

mechanismAnalysis.AppendLine("1. SHA-256 Hash:");
mechanismAnalysis.AppendLine(" - 高安全性哈希算法");
mechanismAnalysis.AppendLine(" - 256位输出,极低碰撞概率");
mechanismAnalysis.AppendLine(" - 用于内容完整性验证");
mechanismAnalysis.AppendLine(" - 计算相对较慢,但非常安全");
mechanismAnalysis.AppendLine();

mechanismAnalysis.AppendLine("2. MD5 Hash(不推荐):");
mechanismAnalysis.AppendLine(" - 128位输出");
mechanismAnalysis.AppendLine(" - 计算速度快");
mechanismAnalysis.AppendLine(" - 已发现碰撞漏洞,不安全");
mechanismAnalysis.AppendLine(" - 仅用于非安全性场景");
mechanismAnalysis.AppendLine();

mechanismAnalysis.AppendLine("3. Custom Hash:");
mechanismAnalysis.AppendLine(" - Unity自定义哈希算法");
mechanismAnalysis.AppendLine(" - 平衡安全性和性能");
mechanismAnalysis.AppendLine(" - 用于资源标识和版本控制");

mechanismAnalysis.AppendLine();
mechanismAnalysis.AppendLine("CRC校验机制:");
mechanismAnalysis.AppendLine();

mechanismAnalysis.AppendLine("1. CRC-32:");
mechanismAnalysis.AppendLine(" - 32位循环冗余校验");
mechanismAnalysis.AppendLine(" - 计算速度快");
mechanismAnalysis.AppendLine(" - 用于快速完整性检查");
mechanismAnalysis.AppendLine(" - 不提供加密安全性");
mechanismAnalysis.AppendLine();

mechanismAnalysis.AppendLine("2. 应用场景:");
mechanismAnalysis.AppendLine(" - 文件完整性验证");
mechanismAnalysis.AppendLine(" - 数据传输校验");
mechanismAnalysis.AppendLine(" - 快速错误检测");

Debug.Log(mechanismAnalysis.ToString());

// 分析性能特征
AnalyzePerformanceCharacteristics();
}

private void AnalyzePerformanceCharacteristics()
{
Debug.Log("=== 校验机制性能特征分析 ===");

var performanceAnalysis = new System.Text.StringBuilder();

performanceAnalysis.AppendLine("性能对比分析:");
performanceAnalysis.AppendLine();

performanceAnalysis.AppendLine("Hash算法性能特征:");
performanceAnalysis.AppendLine("• SHA-256: 安全性高,速度中等,适合重要资源验证");
performanceAnalysis.AppendLine("• MD5: 速度快,安全性低,适合快速验证");
performanceAnalysis.AppendLine("• 自定义Hash: 平衡性能和安全性");
performanceAnalysis.AppendLine();

performanceAnalysis.AppendLine("CRC算法性能特征:");
performanceAnalysis.AppendLine("• CRC-32: 速度极快,无安全性,适合频繁校验");
performanceAnalysis.AppendLine("• 适合小文件和实时校验场景");
performanceAnalysis.AppendLine();

performanceAnalysis.AppendLine("选择策略:");
performanceAnalysis.AppendLine("• 首次加载: 使用SHA-256确保完整性");
performanceAnalysis.AppendLine("• 运行时校验: 使用CRC-32提高性能");
performanceAnalysis.AppendLine("• 版本验证: 使用自定义Hash平衡需求");

Debug.Log(performanceAnalysis.ToString());
}

/// <summary>
/// 计算数据的SHA-256哈希
/// </summary>
public string ComputeSHA256Hash(byte[] data)
{
if (data == null || data.Length == 0) return string.Empty;

using (var sha256 = SHA256.Create())
{
byte[] hashBytes = sha256.ComputeHash(data);
return Convert.ToHexString(hashBytes).ToLower();
}
}

/// <summary>
/// 计算数据的MD5哈希
/// </summary>
public string ComputeMD5Hash(byte[] data)
{
if (data == null || data.Length == 0) return string.Empty;

using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(data);
return Convert.ToHexString(hashBytes).ToLower();
}
}

/// <summary>
/// 计算数据的CRC-32校验
/// </summary>
public uint ComputeCRC32(byte[] data)
{
if (data == null || data.Length == 0) return 0;

const uint polynomial = 0xEDB88320;
uint[] table = new uint[256];

// 构建CRC表
for (uint i = 0; i < 256; i++)
{
uint temp = i;
for (int j = 0; j < 8; j++)
{
if ((temp & 1) == 1)
temp = (temp >> 1) ^ polynomial;
else
temp >>= 1;
}
table[i] = temp;
}

uint crc = 0xFFFFFFFF;
foreach (byte b in data)
{
byte tableIndex = (byte)(((crc & 0xFF) ^ b) & 0xFF);
crc = (crc >> 8) ^ table[tableIndex];
}

return crc ^ 0xFFFFFFFF;
}

/// <summary>
/// 验证资源的Hash
/// </summary>
public HashValidationResult ValidateResourceHash(string resourceId, byte[] resourceData, string expectedHash)
{
var startTime = Time.realtimeSinceStartup;

var result = new HashValidationResult
{
resourceId = resourceId,
expectedHash = expectedHash,
actualHash = ComputeSHA256Hash(resourceData),
isValid = false,
validationTime = 0,
validationMethod = "SHA-256"
};

result.isValid = string.Equals(result.expectedHash, result.actualHash, StringComparison.OrdinalIgnoreCase);

result.validationTime = Time.realtimeSinceStartup - startTime;

// 更新验证统计
UpdateValidationProfile("Hash", result.isValid, result.validationTime);

if (!result.isValid)
{
result.errorMessage = $"Hash不匹配: 期望{result.expectedHash}, 实际{result.actualHash}";
Debug.LogWarning($"资源Hash验证失败: {resourceId}, {result.errorMessage}");
}

return result;
}

/// <summary>
/// 验证资源的CRC
/// </summary>
public CRCValidationResult ValidateResourceCRC(string resourceId, byte[] resourceData, uint expectedCRC)
{
var startTime = Time.realtimeSinceStartup;

var result = new CRCValidationResult
{
resourceId = resourceId,
expectedCRC = expectedCRC,
actualCRC = ComputeCRC32(resourceData),
isValid = false,
validationTime = 0
};

result.isValid = result.expectedCRC == result.actualCRC;

result.validationTime = Time.realtimeSinceStartup - startTime;

// 更新验证统计
UpdateValidationProfile("CRC", result.isValid, result.validationTime);

if (!result.isValid)
{
result.errorMessage = $"CRC不匹配: 期望{result.expectedCRC}, 实际{result.actualCRC}";
Debug.LogWarning($"资源CRC验证失败: {resourceId}, {result.errorMessage}");
}

return result;
}

private void UpdateValidationProfile(string validationType, bool success, float time)
{
string profileKey = validationType;
if (!validationProfiles.ContainsKey(profileKey))
{
validationProfiles[profileKey] = new ValidationProfile
{
validationType = validationType,
averageTime = 0,
totalValidations = 0,
successfulValidations = 0,
failedValidations = 0,
lastRun = DateTime.Now
};
}

var profile = validationProfiles[profileKey];
profile.totalValidations++;
profile.averageTime = (profile.averageTime * (profile.totalValidations - 1) + time) / profile.totalValidations;
profile.lastRun = DateTime.Now;

if (success)
profile.successfulValidations++;
else
profile.failedValidations++;
}

/// <summary>
/// 批量验证资源
/// </summary>
public List<HashValidationResult> BatchValidateResources(List<(string id, byte[] data, string expectedHash)> resources)
{
var results = new List<HashValidationResult>();

for (int i = 0; i < resources.Count; i += validationBatchSize)
{
int endIndex = Math.Min(i + validationBatchSize, resources.Count);
for (int j = i; j < endIndex; j++)
{
var resource = resources[j];
var result = ValidateResourceHash(resource.id, resource.data, resource.expectedHash);
results.Add(result);

// 为了性能,可以在这里添加yield机制
if (j % 10 == 0) // 每10个资源检查一次
{
yield return null; // 在实际实现中,这可能是协程
}
}
}

return results;
}

/// <summary>
/// 获取验证性能统计
/// </summary>
public string GetValidationPerformanceStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== 校验性能统计 ===");

foreach (var profile in validationProfiles.Values)
{
float successRate = profile.totalValidations > 0 ?
(float)profile.successfulValidations / profile.totalValidations * 100 : 0;

stats.AppendLine($"{profile.validationType}验证:");
stats.AppendLine($" 总计: {profile.totalValidations}");
stats.AppendLine($" 成功: {profile.successfulValidations} ({successRate:F1}%)");
stats.AppendLine($" 失败: {profile.failedValidations}");
stats.AppendLine($" 平均耗时: {profile.averageTime:F4}s");
stats.AppendLine($" 最后运行: {profile.lastRun:HH:mm:ss}");
stats.AppendLine();
}

return stats.ToString();
}

/// <summary>
/// 获取校验最佳实践
/// </summary>
public string GetValidationBestPractices()
{
var practices = new System.Text.StringBuilder();
practices.AppendLine("=== Hash与CRC校验最佳实践 ===");

practices.AppendLine("1. 算法选择:");
practices.AppendLine(" - 首次下载/安装: 使用SHA-256确保完整性");
practices.AppendLine(" - 运行时验证: 使用CRC-32提高性能");
practices.AppendLine(" - 避免使用MD5(安全性问题)");
practices.AppendLine();

practices.AppendLine("2. 性能优化:");
practices.AppendLine(" - 实施批量验证减少开销");
practices.AppendLine(" - 使用缓存避免重复计算");
practices.AppendLine(" - 异步验证避免阻塞主线程");
practices.AppendLine();

practices.AppendLine("3. 错误处理:");
practices.AppendLine(" - 提供清晰的错误信息");
practices.AppendLine(" - 实现自动重试机制");
practices.AppendLine(" - 记录验证失败的统计信息");
practices.AppendLine();

practices.AppendLine("4. 安全考虑:");
practices.AppendLine(" - 保护校验密钥和算法参数");
practices.AppendLine(" - 防止校验绕过攻击");
practices.AppendLine(" - 定期更新校验策略");

return practices.ToString();
}

void OnDestroy()
{
Debug.Log("Hash与CRC校验系统清理完成");
}
}

ContentCatalogData源码解析

ContentCatalogData是Addressables系统中表示Catalog数据的核心类,理解其内部实现有助于深入掌握Catalog系统的工作原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;

public class ContentCatalogDataSourceCode : MonoBehaviour
{
[Header("ContentCatalogData源码解析配置")]
public bool enableDeepAnalysis = true;
public bool enableSerializationAnalysis = true;

// 模拟Unity的ContentCatalogData结构
[System.Serializable]
public class MockContentCatalogData
{
[SerializeField] private List<MockResourceLocation> m_LocationIdMap = new List<MockResourceLocation>();
[SerializeField] private Dictionary<string, int> m_Locations = new Dictionary<string, int>();
[SerializeField] private List<MockProviderInfo> m_ProviderInfo = new List<MockProviderInfo>();
[SerializeField] private string m_Hash;
[SerializeField] private string m_UtilityInfo;
[SerializeField] private List<string> m_Keys = new List<string>();

public string Hash => m_Hash;
public string UtilityInfo => m_UtilityInfo;

[System.Serializable]
public class MockResourceLocation : IResourceLocation
{
[SerializeField] private string m_InternalId;
[SerializeField] private string m_ProviderId;
[SerializeField] private IList<IResourceLocation> m_Dependencies;
[SerializeField] private object m_Data;
[SerializeField] private string m_PrimaryKey;

public string InternalId => m_InternalId;
public string Provider => m_ProviderId;
public IList<IResourceLocation> Dependencies => m_Dependencies;
public object Data => m_Data;
public string PrimaryKey => m_PrimaryKey;

public MockResourceLocation(string primaryKey, string internalId, string providerId)
{
m_PrimaryKey = primaryKey;
m_InternalId = internalId;
m_ProviderId = providerId;
m_Dependencies = new List<IResourceLocation>();
}

public bool HasKey(string key)
{
return m_PrimaryKey == key;
}

public bool Matches(string key)
{
return m_PrimaryKey == key;
}

public T GetData<T>()
{
return (T)m_Data;
}
}

[System.Serializable]
public class MockProviderInfo
{
public string providerId;
public string resourceType;
public string providerType;
}

public MockContentCatalogData()
{
m_Hash = System.Guid.NewGuid().ToString();
m_UtilityInfo = "Mock Utility Info";
}

/// <summary>
/// 添加资源位置
/// </summary>
public void AddLocation(string locationName, string internalId, string providerId)
{
var location = new MockResourceLocation(locationName, internalId, providerId);
m_LocationIdMap.Add(location);
m_Locations[locationName] = m_LocationIdMap.Count - 1;
m_Keys.Add(locationName);
}

/// <summary>
/// 获取资源位置
/// </summary>
public bool Locate(string key, Type resourceType, out IList<IResourceLocation> locations)
{
locations = new List<IResourceLocation>();

if (m_Locations.ContainsKey(key))
{
int index = m_Locations[key];
if (index >= 0 && index < m_LocationIdMap.Count)
{
locations.Add(m_LocationIdMap[index]);
return true;
}
}

return false;
}

/// <summary>
/// 获取所有位置键
/// </summary>
public string[] GetAllKeys()
{
return m_Keys.ToArray();
}

/// <summary>
/// 检查是否存在指定键
/// </summary>
public bool Contains(string key)
{
return m_Locations.ContainsKey(key);
}

/// <summary>
/// 添加依赖关系
/// </summary>
public void AddDependency(string key, string dependencyKey)
{
if (m_Locations.ContainsKey(key))
{
int index = m_Locations[key];
if (index >= 0 && index < m_LocationIdMap.Count)
{
// 在实际实现中,这里会添加依赖关系
// 简化实现,仅记录依赖
}
}
}

/// <summary>
/// 序列化为JSON
/// </summary>
public string SerializeToJson()
{
var catalogData = new
{
catalogVersion = 3,
resourceProviderData = m_ProviderInfo,
locationData = m_LocationIdMap,
keys = m_Keys,
hash = m_Hash,
utilityInfo = m_UtilityInfo
};

return JsonUtility.ToJson(catalogData, true);
}

/// <summary>
/// 从JSON反序列化
/// </summary>
public static MockContentCatalogData DeserializeFromJson(string json)
{
// 在实际实现中,这里会有完整的反序列化逻辑
// 简化实现,返回新的实例
return new MockContentCatalogData();
}
}

[System.Serializable]
public class CatalogDataAnalysisResult
{
public string catalogName;
public int locationCount;
public int providerCount;
public int keyCount;
public string hash;
public float serializationTime;
public float deserializationTime;
public long memoryUsage;
public List<string> providerTypes;
public Dictionary<string, int> locationTypeDistribution;
}

void Start()
{
Debug.Log("ContentCatalogData源码解析器启动");
AnalyzeSourceImplementation();
}

private void AnalyzeSourceImplementation()
{
Debug.Log("=== ContentCatalogData源码实现分析 ===");

var sourceAnalysis = new System.Text.StringBuilder();

sourceAnalysis.AppendLine("ContentCatalogData核心数据结构:");
sourceAnalysis.AppendLine();

sourceAnalysis.AppendLine("1. m_LocationIdMap (List<IResourceLocation>):");
sourceAnalysis.AppendLine(" - 存储所有资源位置对象");
sourceAnalysis.AppendLine(" - 使用索引访问,提供O(1)访问时间");
sourceAnalysis.AppendLine(" - 包含资源的完整定位信息");
sourceAnalysis.AppendLine();

sourceAnalysis.AppendLine("2. m_Locations (Dictionary<string, int>):");
sourceAnalysis.AppendLine(" - 键到索引的映射");
sourceAnalysis.AppendLine(" - 提供O(1)的键查找性能");
sourceAnalysis.AppendLine(" - 支持多种键类型(地址、标签等)");
sourceAnalysis.AppendLine();

sourceAnalysis.AppendLine("3. m_ProviderInfo (List<ProviderInfo>):");
sourceAnalysis.AppendLine(" - 资源提供者信息");
sourceAnalysis.AppendLine(" - 定义资源类型和处理方式");
sourceAnalysis.AppendLine(" - 支持多种资源类型的处理");
sourceAnalysis.AppendLine();

sourceAnalysis.AppendLine("4. m_Hash (string):");
sourceAnalysis.AppendLine(" - 内容哈希值");
sourceAnalysis.AppendLine(" - 用于验证内容完整性");
sourceAnalysis.AppendLine(" - 支持内容更新检测");
sourceAnalysis.AppendLine();

sourceAnalysis.AppendLine("5. m_Keys (List<string>):");
sourceAnalysis.AppendLine(" - 所有键的列表");
sourceAnalysis.AppendLine(" - 支持枚举所有可用资源");
sourceAnalysis.AppendLine(" - 便于调试和分析");

Debug.Log(sourceAnalysis.ToString());

// 分析关键方法实现
AnalyzeKeyMethods();
}

private void AnalyzeKeyMethods()
{
Debug.Log("=== 关键方法实现分析 ===");

var methodAnalysis = new System.Text.StringBuilder();

methodAnalysis.AppendLine("关键方法分析:");
methodAnalysis.AppendLine();

methodAnalysis.AppendLine("1. Locate方法:");
methodAnalysis.AppendLine(" - 输入:键、资源类型");
methodAnalysis.AppendLine(" - 输出:资源位置列表");
methodAnalysis.AppendLine(" - 实现:通过字典查找获取索引,再通过列表获取位置");
methodAnalysis.AppendLine(" - 时间复杂度:O(1)");
methodAnalysis.AppendLine();

methodAnalysis.AppendLine("2. AddLocation方法:");
methodAnalysis.AppendLine(" - 将新位置添加到列表");
methodAnalysis.AppendLine(" - 在字典中建立键到索引的映射");
methodAnalysis.AppendLine(" - 维护键列表");
methodAnalysis.AppendLine(" - 时间复杂度:O(1)");
methodAnalysis.AppendLine();

methodAnalysis.AppendLine("3. Serialization:");
methodAnalysis.AppendLine(" - 使用Unity的序列化系统");
methodAnalysis.AppendLine(" - 支持JSON和二进制格式");
methodAnalysis.AppendLine(" - 包含版本和校验信息");
methodAnalysis.AppendLine();

methodAnalysis.AppendLine("4. Content Validation:");
methodAnalysis.AppendLine(" - 使用哈希值验证内容完整性");
methodAnalysis.AppendLine(" - 支持远程内容更新检测");
methodAnalysis.AppendLine(" - 防止内容被篡改");

Debug.Log(methodAnalysis.ToString());
}

/// <summary>
/// 模拟Catalog数据操作
/// </summary>
public CatalogDataAnalysisResult SimulateCatalogOperations(int locationCount = 1000)
{
var startTime = Time.realtimeSinceStartup;

var catalog = new MockContentCatalogData();
var providerTypes = new List<string> { "PrefabProvider", "TextureProvider", "AssetProvider", "SceneProvider" };
var locationTypeDistribution = new Dictionary<string, int>();

// 添加位置数据
for (int i = 0; i < locationCount; i++)
{
string key = $"Resource_{i:D4}";
string internalId = $"Assets/Resources/{key}.asset";
string providerId = providerTypes[i % providerTypes.Count];

catalog.AddLocation(key, internalId, providerId);

// 统计位置类型分布
if (!locationTypeDistribution.ContainsKey(providerId))
{
locationTypeDistribution[providerId] = 0;
}
locationTypeDistribution[providerId]++;
}

// 添加一些依赖关系
for (int i = 0; i < locationCount / 10; i++)
{
string key = $"Resource_{i:D4}";
string dependencyKey = $"Resource_{(i + 100) % locationCount:D4}";
catalog.AddDependency(key, dependencyKey);
}

float serializationTime = Time.realtimeSinceStartup - startTime;

// 模拟反序列化时间
float deserializationTime = serializationTime * 0.8f; // 反序列化通常稍快

var result = new CatalogDataAnalysisResult
{
catalogName = "MockCatalog",
locationCount = locationCount,
providerCount = providerTypes.Count,
keyCount = catalog.GetAllKeys().Length,
hash = catalog.Hash,
serializationTime = serializationTime,
deserializationTime = deserializationTime,
memoryUsage = locationCount * 256, // 估算内存使用
providerTypes = providerTypes,
locationTypeDistribution = locationTypeDistribution
};

if (enableDeepAnalysis)
{
Debug.Log($"Catalog操作模拟完成: {locationCount} 个位置, " +
$"序列化耗时 {serializationTime:F3}s, " +
$"内存使用 {FormatBytes(result.memoryUsage)}");
}

return result;
}

/// <summary>
/// 分析序列化性能
/// </summary>
public string AnalyzeSerializationPerformance(CatalogDataAnalysisResult result)
{
var analysis = new System.Text.StringBuilder();
analysis.AppendLine("=== 序列化性能分析 ===");

analysis.AppendLine($"Catalog大小: {result.locationCount} 个位置");
analysis.AppendLine($"序列化时间: {result.serializationTime:F3}s");
analysis.AppendLine($"反序列化时间: {result.deserializationTime:F3}s");
analysis.AppendLine($"内存使用: {FormatBytes(result.memoryUsage)}");
analysis.AppendLine($"平均每位置序列化时间: {(result.serializationTime / result.locationCount * 1000):F3}ms");
analysis.AppendLine();

analysis.AppendLine("位置类型分布:");
foreach (var kvp in result.locationTypeDistribution)
{
float percentage = (float)kvp.Value / result.locationCount * 100;
analysis.AppendLine($" {kvp.Key}: {kvp.Value} 个 ({percentage:F1}%)");
}
analysis.AppendLine();

analysis.AppendLine("性能建议:");
if (result.serializationTime > 1.0f)
{
analysis.AppendLine(" • 序列化时间过长,考虑分批处理");
analysis.AppendLine(" • 实施异步序列化");
analysis.AppendLine(" • 优化Catalog大小");
}
else
{
analysis.AppendLine(" • 序列化性能良好");
}

if (result.memoryUsage > 50 * 1024 * 1024) // 50MB
{
analysis.AppendLine(" • 内存使用较大,考虑Catalog分片");
analysis.AppendLine(" • 实施按需加载策略");
}

return analysis.ToString();
}

/// <summary>
/// 获取源码实现最佳实践
/// </summary>
public string GetSourceImplementationBestPractices()
{
var practices = new System.Text.StringBuilder();
practices.AppendLine("=== ContentCatalogData源码实现最佳实践 ===");

practices.AppendLine("1. 数据结构选择:");
practices.AppendLine(" - 使用Dictionary<string, int>实现快速键查找");
practices.AppendLine(" - 使用List<T>存储实际数据以获得连续内存布局");
practices.AppendLine(" - 平衡内存使用和访问性能");
practices.AppendLine();

practices.AppendLine("2. 序列化优化:");
practices.AppendLine(" - 实现高效的序列化/反序列化");
practices.AppendLine(" - 使用适当的序列化格式(JSON、二进制)");
practices.AppendLine(" - 包含版本和校验信息");
practices.AppendLine();

practices.AppendLine("3. 内存管理:");
practices.AppendLine(" - 避免频繁的内存分配");
practices.AppendLine(" - 实现对象池以重用资源");
practices.AppendLine(" - 监控内存使用情况");
practices.AppendLine();

practices.AppendLine("4. 线程安全:");
practices.AppendLine(" - 在多线程环境中保护数据访问");
practices.AppendLine(" - 使用适当的同步机制");
practices.AppendLine(" - 避免死锁和竞态条件");
practices.AppendLine();

practices.AppendLine("5. 扩展性:");
practices.AppendLine(" - 设计可扩展的数据结构");
practices.AppendLine(" - 支持插件化功能");
practices.AppendLine(" - 保持向后兼容性");

return practices.ToString();
}

private string FormatBytes(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
double len = bytes;

while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}

return $"{len:0.##} {sizes[order]}";
}

void OnDestroy()
{
Debug.Log("ContentCatalogData源码解析器清理完成");
}
}

自定义Catalog Provider

Unity Addressables系统允许开发者创建自定义的Catalog Provider来满足特定需求,如从数据库加载Catalog、动态生成Catalog或从自定义源加载Catalog。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.AddressableAssets;

public class CustomCatalogProvider : MonoBehaviour
{
[Header("自定义Catalog Provider配置")]
public bool enableCustomProvider = true;
public string customCatalogSource = "Database";
public float cacheTimeout = 300f; // 5分钟

// 自定义Catalog Provider接口
public interface ICustomCatalogProvider
{
Task<List<IResourceLocation>> LoadCatalogAsync(string catalogId);
Task<bool> UpdateCatalogAsync(string catalogId);
bool IsValidCatalog(string catalogId);
}

// 自定义Catalog Provider基类
public abstract class BaseCustomCatalogProvider : ICustomCatalogProvider
{
public string ProviderId { get; protected set; }
public float CacheTimeout { get; set; } = 300f;

protected Dictionary<string, CachedCatalogData> catalogCache = new Dictionary<string, CachedCatalogData>();

[System.Serializable]
public class CachedCatalogData
{
public List<IResourceLocation> locations;
public DateTime cacheTime;
public string catalogId;
public string sourceHash;
}

public async Task<List<IResourceLocation>> LoadCatalogAsync(string catalogId)
{
// 检查缓存
if (catalogCache.ContainsKey(catalogId))
{
var cached = catalogCache[catalogId];
if ((DateTime.Now - cached.cacheTime).TotalSeconds < CacheTimeout)
{
Debug.Log($"从缓存加载Catalog: {catalogId}");
return cached.locations;
}
else
{
// 缓存过期,移除
catalogCache.Remove(catalogId);
}
}

// 从源加载
var locations = await LoadCatalogFromSourceAsync(catalogId);

// 缓存结果
catalogCache[catalogId] = new CachedCatalogData
{
locations = locations,
cacheTime = DateTime.Now,
catalogId = catalogId,
sourceHash = GenerateHash(locations)
};

return locations;
}

public async Task<bool> UpdateCatalogAsync(string catalogId)
{
if (!catalogCache.ContainsKey(catalogId))
{
return false;
}

var currentHash = catalogCache[catalogId].sourceHash;
var newLocations = await LoadCatalogFromSourceAsync(catalogId);
var newHash = GenerateHash(newLocations);

if (currentHash != newHash)
{
catalogCache[catalogId] = new CachedCatalogData
{
locations = newLocations,
cacheTime = DateTime.Now,
catalogId = catalogId,
sourceHash = newHash
};

Debug.Log($"Catalog已更新: {catalogId}");
return true;
}

return false;
}

public bool IsValidCatalog(string catalogId)
{
return !string.IsNullOrEmpty(catalogId) && catalogId.Length > 0;
}

protected abstract Task<List<IResourceLocation>> LoadCatalogFromSourceAsync(string catalogId);

protected virtual string GenerateHash(List<IResourceLocation> locations)
{
// 简单的哈希生成
int hash = 0;
foreach (var location in locations)
{
hash ^= location.PrimaryKey.GetHashCode();
}
return hash.ToString();
}
}

// 数据库Catalog Provider示例
public class DatabaseCatalogProvider : BaseCustomCatalogProvider
{
public DatabaseCatalogProvider()
{
ProviderId = "DatabaseCatalogProvider";
}

protected override async Task<List<IResourceLocation>> LoadCatalogFromSourceAsync(string catalogId)
{
// 模拟从数据库加载Catalog数据
await Task.Delay(100); // 模拟网络延迟

var locations = new List<IResourceLocation>();

// 模拟数据库查询结果
for (int i = 0; i < 10; i++)
{
var location = new MockResourceLocation($"DB_Resource_{catalogId}_{i}",
$"Assets/Database/{catalogId}/Resource_{i}.asset",
"AssetDatabaseProvider");
locations.Add(location);
}

Debug.Log($"从数据库加载Catalog: {catalogId}, 资源数: {locations.Count}");
return locations;
}
}

// Web API Catalog Provider示例
public class WebApiCatalogProvider : BaseCustomCatalogProvider
{
public WebApiCatalogProvider()
{
ProviderId = "WebApiCatalogProvider";
}

protected override async Task<List<IResourceLocation>> LoadCatalogFromSourceAsync(string catalogId)
{
// 模拟从Web API加载Catalog数据
await Task.Delay(200); // 模拟网络请求

var locations = new List<IResourceLocation>();

// 模拟API响应
for (int i = 0; i < 15; i++)
{
var location = new MockResourceLocation($"API_Resource_{catalogId}_{i}",
$"http://api.example.com/resources/{catalogId}/Resource_{i}.asset",
"WebAssetProvider");
locations.Add(location);
}

Debug.Log($"从Web API加载Catalog: {catalogId}, 资源数: {locations.Count}");
return locations;
}
}

// 动态生成Catalog Provider示例
public class DynamicCatalogProvider : BaseCustomCatalogProvider
{
public DynamicCatalogProvider()
{
ProviderId = "DynamicCatalogProvider";
}

protected override async Task<List<IResourceLocation>> LoadCatalogFromSourceAsync(string catalogId)
{
// 模拟动态生成Catalog数据
await Task.Delay(50); // 模拟处理时间

var locations = new List<IResourceLocation>();

// 根据catalogId动态生成资源位置
var resourceNames = catalogId.Split('_');
foreach (var name in resourceNames)
{
if (!string.IsNullOrEmpty(name))
{
var location = new MockResourceLocation($"Dynamic_{name}",
$"Assets/Dynamic/{name}.asset",
"DynamicAssetProvider");
locations.Add(location);
}
}

Debug.Log($"动态生成Catalog: {catalogId}, 资源数: {locations.Count}");
return locations;
}
}

[System.Serializable]
public class MockResourceLocation : IResourceLocation
{
public string InternalId { get; set; }
public string Provider { get; set; }
public IList<IResourceLocation> Dependencies { get; set; }
public object Data { get; set; }
public string PrimaryKey { get; set; }

public MockResourceLocation(string primaryKey, string internalId, string provider)
{
PrimaryKey = primaryKey;
InternalId = internalId;
Provider = provider;
Dependencies = new List<IResourceLocation>();
}

public bool HasKey(string key)
{
return PrimaryKey == key;
}

public bool Matches(string key)
{
return PrimaryKey == key;
}

public T GetData<T>()
{
return (T)Data;
}
}

[System.Serializable]
public class CustomProviderAnalysisResult
{
public string providerType;
public float avgLoadTime;
public int totalLoads;
public int cacheHits;
public int cacheMisses;
public float cacheHitRate;
public List<string> errorLogs;
}

private Dictionary<string, CustomProviderAnalysisResult> providerAnalysis = new Dictionary<string, CustomProviderAnalysisResult>();
private List<ICustomCatalogProvider> activeProviders = new List<ICustomCatalogProvider>();

void Start()
{
Debug.Log("自定义Catalog Provider系统启动");
InitializeCustomProviders();
}

private void InitializeCustomProviders()
{
if (!enableCustomProvider) return;

// 根据配置创建相应的Provider
switch (customCatalogSource.ToLower())
{
case "database":
activeProviders.Add(new DatabaseCatalogProvider { CacheTimeout = cacheTimeout });
break;
case "webapi":
activeProviders.Add(new WebApiCatalogProvider { CacheTimeout = cacheTimeout });
break;
case "dynamic":
activeProviders.Add(new DynamicCatalogProvider { CacheTimeout = cacheTimeout });
break;
default:
activeProviders.Add(new DatabaseCatalogProvider { CacheTimeout = cacheTimeout });
break;
}

Debug.Log($"初始化了 {activeProviders.Count} 个自定义Catalog Provider");
}

/// <summary>
/// 使用自定义Provider加载Catalog
/// </summary>
public async Task<List<IResourceLocation>> LoadCatalogWithCustomProvider(string catalogId, string providerType = null)
{
if (activeProviders.Count == 0)
{
Debug.LogError("没有可用的自定义Catalog Provider");
return new List<IResourceLocation>();
}

ICustomCatalogProvider provider = null;

if (!string.IsNullOrEmpty(providerType))
{
// 根据类型选择Provider
foreach (var p in activeProviders)
{
if (p is BaseCustomCatalogProvider baseProvider &&
baseProvider.ProviderId.Contains(providerType))
{
provider = p;
break;
}
}
}
else
{
// 使用第一个Provider
provider = activeProviders[0];
}

if (provider == null)
{
Debug.LogError($"找不到类型为 {providerType} 的Provider");
return new List<IResourceLocation>();
}

try
{
var startTime = Time.realtimeSinceStartup;
var locations = await provider.LoadCatalogAsync(catalogId);
var loadTime = Time.realtimeSinceStartup - startTime;

// 记录分析数据
RecordProviderAnalysis(provider, loadTime, true);

Debug.Log($"使用自定义Provider加载Catalog成功: {catalogId}, 耗时 {loadTime:F3}s, 资源数: {locations.Count}");
return locations;
}
catch (Exception ex)
{
Debug.LogError($"加载Catalog失败: {ex.Message}");

// 记录错误分析
RecordProviderAnalysis(provider, 0, false, ex.Message);

return new List<IResourceLocation>();
}
}

private void RecordProviderAnalysis(ICustomCatalogProvider provider, float loadTime, bool success, string error = null)
{
string providerId = "Unknown";
if (provider is BaseCustomCatalogProvider baseProvider)
{
providerId = baseProvider.ProviderId;
}

if (!providerAnalysis.ContainsKey(providerId))
{
providerAnalysis[providerId] = new CustomProviderAnalysisResult
{
providerType = providerId,
avgLoadTime = 0,
totalLoads = 0,
cacheHits = 0,
cacheMisses = 0,
errorLogs = new List<string>()
};
}

var analysis = providerAnalysis[providerId];
analysis.totalLoads++;

// 更新平均加载时间
analysis.avgLoadTime = (analysis.avgLoadTime * (analysis.totalLoads - 1) + loadTime) / analysis.totalLoads;

// 这里简化缓存统计,实际实现中需要更精确的缓存命中判断
if (loadTime < 0.01f) // 假设小于10ms为缓存命中
{
analysis.cacheHits++;
}
else
{
analysis.cacheMisses++;
}

analysis.cacheHitRate = analysis.totalLoads > 0 ?
(float)analysis.cacheHits / analysis.totalLoads : 0;

if (!success && !string.IsNullOrEmpty(error))
{
analysis.errorLogs.Add($"{DateTime.Now}: {error}");
}
}

/// <summary>
/// 更新Catalog
/// </summary>
public async Task<bool> UpdateCatalog(string catalogId, string providerType = null)
{
if (activeProviders.Count == 0) return false;

ICustomCatalogProvider provider = null;

if (!string.IsNullOrEmpty(providerType))
{
foreach (var p in activeProviders)
{
if (p is BaseCustomCatalogProvider baseProvider &&
baseProvider.ProviderId.Contains(providerType))
{
provider = p;
break;
}
}
}
else
{
provider = activeProviders[0];
}

if (provider != null)
{
return await provider.UpdateCatalogAsync(catalogId);
}

return false;
}

/// <summary>
/// 获取Provider分析报告
/// </summary>
public string GetProviderAnalysisReport()
{
var report = new System.Text.StringBuilder();
report.AppendLine("=== 自定义Catalog Provider分析报告 ===");

foreach (var kvp in providerAnalysis)
{
var analysis = kvp.Value;
report.AppendLine($"{analysis.providerType}:");
report.AppendLine($" 总加载次数: {analysis.totalLoads}");
report.AppendLine($" 平均加载时间: {analysis.avgLoadTime:F3}s");
report.AppendLine($" 缓存命中: {analysis.cacheHits}");
report.AppendLine($" 缓存未命中: {analysis.cacheMisses}");
report.AppendLine($" 缓存命中率: {analysis.cacheHitRate * 100:F1}%");

if (analysis.errorLogs.Count > 0)
{
report.AppendLine(" 错误日志:");
foreach (var error in analysis.errorLogs)
{
report.AppendLine($" {error}");
}
}
report.AppendLine();
}

return report.ToString();
}

/// <summary>
/// 获取自定义Provider最佳实践
/// </summary>
public string GetCustomProviderBestPractices()
{
var practices = new System.Text.StringBuilder();
practices.AppendLine("=== 自定义Catalog Provider最佳实践 ===");

practices.AppendLine("1. 异步操作:");
practices.AppendLine(" - 所有网络或I/O操作必须异步执行");
practices.AppendLine(" - 避免阻塞主线程");
practices.AppendLine(" - 实现适当的超时机制");
practices.AppendLine();

practices.AppendLine("2. 缓存策略:");
practices.AppendLine(" - 实现智能缓存减少重复请求");
practices.AppendLine(" - 设置合理的缓存过期时间");
practices.AppendLine(" - 监控缓存命中率");
practices.AppendLine();

practices.AppendLine("3. 错误处理:");
practices.AppendLine(" - 实现重试机制");
practices.AppendLine(" - 提供降级方案");
practices.AppendLine(" - 记录详细的错误日志");
practices.AppendLine();

practices.AppendLine("4. 性能优化:");
practices.AppendLine(" - 批量加载减少网络请求");
practices.AppendLine(" - 实现连接池管理");
practices.AppendLine(" - 监控和优化加载时间");
practices.AppendLine();

practices.AppendLine("5. 安全考虑:");
practices.AppendLine(" - 验证数据完整性");
practices.AppendLine(" - 实现适当的认证机制");
practices.AppendLine(" - 防止恶意数据注入");

return practices.ToString();
}

void OnDestroy()
{
Debug.Log("自定义Catalog Provider系统清理完成");
}
}

多Catalog管理

在复杂的项目中,通常需要管理多个Catalog来支持不同的功能需求,如内容更新、平台特定资源、模块化加载等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.ResourceManagement.ResourceLocations;

public class MultiCatalogManager : MonoBehaviour
{
[Header("多Catalog管理配置")]
public bool enableCatalogGrouping = true;
public bool enableCatalogCaching = true;
public float catalogCacheTimeout = 600f; // 10分钟
public int maxConcurrentCatalogLoads = 3;

[System.Serializable]
public class CatalogGroup
{
public string groupName;
public List<string> catalogIds;
public CatalogGroupType groupType;
public bool isLoaded;
public DateTime lastLoadTime;
public float loadPriority;
}

public enum CatalogGroupType
{
Core, // 核心资源
LevelSpecific, // 关卡特定资源
Downloadable, // 可下载内容
PlatformSpecific, // 平台特定资源
Module // 模块资源
}

[System.Serializable]
public class CatalogInfo
{
public string catalogId;
public CatalogGroupType catalogType;
public string sourcePath;
public DateTime lastUpdateTime;
public string contentHash;
public long fileSize;
public bool isLoaded;
public bool isValid;
public List<IResourceLocation> locations;
public DateTime cacheTime;
public float loadTime;
public string loadError;
}

[System.Serializable]
public class MultiCatalogAnalysisResult
{
public int totalCatalogs;
public int loadedCatalogs;
public int cachedCatalogs;
public float totalLoadTime;
public float avgLoadTime;
public Dictionary<CatalogGroupType, int> typeDistribution;
public List<string> loadErrors;
public float cacheHitRate;
}

private Dictionary<string, CatalogInfo> catalogRegistry = new Dictionary<string, CatalogInfo>();
private Dictionary<string, CatalogGroup> catalogGroups = new Dictionary<string, CatalogGroup>();
private List<string> activeCatalogs = new List<string>();
private Queue<string> loadQueue = new Queue<string>();
private HashSet<string> loadingCatalogs = new HashSet<string>();

void Start()
{
Debug.Log("多Catalog管理系统启动");
InitializeCatalogManagement();
}

private void InitializeCatalogManagement()
{
Debug.Log("=== 多Catalog管理系统初始化 ===");

// 创建默认的Catalog组
CreateDefaultCatalogGroups();

Debug.Log($"初始化完成,创建了 {catalogGroups.Count} 个Catalog组");
}

private void CreateDefaultCatalogGroups()
{
// 核心资源组
catalogGroups["Core"] = new CatalogGroup
{
groupName = "Core",
catalogIds = new List<string> { "CoreAssets", "UIAssets", "AudioAssets" },
groupType = CatalogGroupType.Core,
loadPriority = 1.0f
};

// 关卡特定资源组
catalogGroups["LevelSpecific"] = new CatalogGroup
{
groupName = "LevelSpecific",
catalogIds = new List<string> { "Level1Assets", "Level2Assets", "Level3Assets" },
groupType = CatalogGroupType.LevelSpecific,
loadPriority = 2.0f
};

// 可下载内容组
catalogGroups["Downloadable"] = new CatalogGroup
{
groupName = "Downloadable",
catalogIds = new List<string> { "DLC1", "DLC2", "SeasonPass" },
groupType = CatalogGroupType.Downloadable,
loadPriority = 3.0f
};

// 平台特定资源组
catalogGroups["PlatformSpecific"] = new CatalogGroup
{
groupName = "PlatformSpecific",
catalogIds = new List<string> { "MobileAssets", "ConsoleAssets", "PCAssets" },
groupType = CatalogGroupType.PlatformSpecific,
loadPriority = 4.0f
};
}

/// <summary>
/// 注册Catalog
/// </summary>
public void RegisterCatalog(string catalogId, string sourcePath, CatalogGroupType catalogType)
{
if (catalogRegistry.ContainsKey(catalogId))
{
Debug.LogWarning($"Catalog已存在: {catalogId}");
return;
}

var catalogInfo = new CatalogInfo
{
catalogId = catalogId,
sourcePath = sourcePath,
catalogType = catalogType,
lastUpdateTime = DateTime.Now,
contentHash = GenerateContentHash(catalogId),
fileSize = 1024 * 1024, // 模拟1MB
isLoaded = false,
isValid = true,
cacheTime = DateTime.MinValue
};

catalogRegistry[catalogId] = catalogInfo;

Debug.Log($"注册Catalog: {catalogId}, 类型: {catalogType}, 路径: {sourcePath}");
}

private string GenerateContentHash(string catalogId)
{
// 生成模拟的内容哈希
return $"hash_{catalogId}_{DateTime.Now.Ticks % 1000000}";
}

/// <summary>
/// 加载Catalog
/// </summary>
public async Task<bool> LoadCatalog(string catalogId)
{
if (!catalogRegistry.ContainsKey(catalogId))
{
Debug.LogError($"Catalog不存在: {catalogId}");
return false;
}

var catalogInfo = catalogRegistry[catalogId];

// 检查是否已在加载中
if (loadingCatalogs.Contains(catalogId))
{
Debug.Log($"Catalog已在加载中: {catalogId}");
return false;
}

// 检查缓存
if (enableCatalogCaching &&
catalogInfo.isLoaded &&
(DateTime.Now - catalogInfo.cacheTime).TotalSeconds < catalogCacheTimeout)
{
Debug.Log($"从缓存加载Catalog: {catalogId}");
return true;
}

// 检查并发限制
if (loadingCatalogs.Count >= maxConcurrentCatalogLoads)
{
// 添加到加载队列
if (!loadQueue.Contains(catalogId))
{
loadQueue.Enqueue(catalogId);
}
Debug.Log($"Catalog加入加载队列: {catalogId}, 队列长度: {loadQueue.Count}");
return false;
}

loadingCatalogs.Add(catalogId);

try
{
var startTime = Time.realtimeSinceStartup;

// 模拟加载过程
await SimulateCatalogLoad(catalogId);

var loadTime = Time.realtimeSinceStartup - startTime;

// 更新Catalog信息
catalogInfo.isLoaded = true;
catalogInfo.cacheTime = DateTime.Now;
catalogInfo.loadTime = loadTime;
catalogInfo.locations = GenerateMockLocations(catalogId);

activeCatalogs.Add(catalogId);

Debug.Log($"Catalog加载成功: {catalogId}, 耗时 {loadTime:F3}s");

// 检查并处理队列中的下一个Catalog
ProcessLoadQueue();

return true;
}
catch (Exception ex)
{
catalogInfo.loadError = ex.Message;
Debug.LogError($"Catalog加载失败: {catalogId}, 错误: {ex.Message}");
return false;
}
finally
{
loadingCatalogs.Remove(catalogId);
}
}

private async Task SimulateCatalogLoad(string catalogId)
{
// 模拟不同类型的Catalog加载时间
int loadTimeMs = catalogRegistry[catalogId].catalogType switch
{
CatalogGroupType.Core => 200,
CatalogGroupType.LevelSpecific => 500,
CatalogGroupType.Downloadable => 800,
CatalogGroupType.PlatformSpecific => 300,
CatalogGroupType.Module => 400,
_ => 300
};

await Task.Delay(loadTimeMs);
}

private List<IResourceLocation> GenerateMockLocations(string catalogId)
{
var locations = new List<IResourceLocation>();

// 根据Catalog类型生成不同数量的虚拟位置
int locationCount = catalogRegistry[catalogId].catalogType switch
{
CatalogGroupType.Core => 50,
CatalogGroupType.LevelSpecific => 100,
CatalogGroupType.Downloadable => 200,
CatalogGroupType.PlatformSpecific => 75,
CatalogGroupType.Module => 30,
_ => 50
};

for (int i = 0; i < locationCount; i++)
{
var location = new MockResourceLocation(
$"{catalogId}_Resource_{i:D4}",
$"Assets/{catalogId}/Resource_{i}.asset",
"AssetProvider"
);
locations.Add(location);
}

return locations;
}

private void ProcessLoadQueue()
{
while (loadQueue.Count > 0 && loadingCatalogs.Count < maxConcurrentCatalogLoads)
{
string catalogId = loadQueue.Dequeue();
if (!loadingCatalogs.Contains(catalogId) && !catalogRegistry[catalogId].isLoaded)
{
// 在实际实现中,这里会启动异步加载任务
// 为简化,这里只记录
Debug.Log($"从队列处理Catalog加载: {catalogId}");
}
}
}

/// <summary>
/// 加载Catalog组
/// </summary>
public async Task<bool> LoadCatalogGroup(string groupName)
{
if (!catalogGroups.ContainsKey(groupName))
{
Debug.LogError($"Catalog组不存在: {groupName}");
return false;
}

var group = catalogGroups[groupName];
bool allLoaded = true;

// 按优先级排序加载
var catalogsToLoad = new List<(string id, float priority)>();
foreach (var catalogId in group.catalogIds)
{
if (catalogRegistry.ContainsKey(catalogId))
{
float priority = catalogRegistry[catalogId].catalogType switch
{
CatalogGroupType.Core => 1.0f,
CatalogGroupType.LevelSpecific => 2.0f,
CatalogGroupType.Downloadable => 3.0f,
CatalogGroupType.PlatformSpecific => 4.0f,
CatalogGroupType.Module => 5.0f,
_ => 2.0f
};
catalogsToLoad.Add((catalogId, priority));
}
}

// 按优先级排序
catalogsToLoad.Sort((a, b) => a.priority.CompareTo(b.priority));

// 并行加载(受并发限制)
var loadTasks = new List<Task<bool>>();
foreach (var (catalogId, priority) in catalogsToLoad)
{
var task = LoadCatalog(catalogId);
loadTasks.Add(task);

if (loadTasks.Count >= maxConcurrentCatalogLoads)
{
var results = await Task.WhenAll(loadTasks);
foreach (var result in results)
{
if (!result) allLoaded = false;
}
loadTasks.Clear();
}
}

// 处理剩余任务
if (loadTasks.Count > 0)
{
var results = await Task.WhenAll(loadTasks);
foreach (var result in results)
{
if (!result) allLoaded = false;
}
}

group.isLoaded = allLoaded;
group.lastLoadTime = DateTime.Now;

Debug.Log($"Catalog组加载完成: {groupName}, 成功: {allLoaded}");
return allLoaded;
}

/// <summary>
/// 分析多Catalog管理性能
/// </summary>
public MultiCatalogAnalysisResult AnalyzeMultiCatalogPerformance()
{
var result = new MultiCatalogAnalysisResult
{
totalCatalogs = catalogRegistry.Count,
loadedCatalogs = 0,
cachedCatalogs = 0,
totalLoadTime = 0,
typeDistribution = new Dictionary<CatalogGroupType, int>(),
loadErrors = new List<string>()
};

foreach (var catalogInfo in catalogRegistry.Values)
{
if (catalogInfo.isLoaded)
{
result.loadedCatalogs++;
result.totalLoadTime += catalogInfo.loadTime;

// 检查是否为缓存
if ((DateTime.Now - catalogInfo.cacheTime).TotalSeconds < catalogCacheTimeout)
{
result.cachedCatalogs++;
}
}

if (!string.IsNullOrEmpty(catalogInfo.loadError))
{
result.loadErrors.Add($"{catalogInfo.catalogId}: {catalogInfo.loadError}");
}

// 统计类型分布
if (!result.typeDistribution.ContainsKey(catalogInfo.catalogType))
{
result.typeDistribution[catalogInfo.catalogType] = 0;
}
result.typeDistribution[catalogInfo.catalogType]++;
}

result.avgLoadTime = result.loadedCatalogs > 0 ?
result.totalLoadTime / result.loadedCatalogs : 0;
result.cacheHitRate = result.totalCatalogs > 0 ?
(float)result.cachedCatalogs / result.totalCatalogs : 0;

return result;
}

/// <summary>
/// 获取多Catalog管理报告
/// </summary>
public string GetMultiCatalogReport()
{
var analysis = AnalyzeMultiCatalogPerformance();
var report = new System.Text.StringBuilder();

report.AppendLine("=== 多Catalog管理报告 ===");
report.AppendLine($"总Catalog数: {analysis.totalCatalogs}");
report.AppendLine($"已加载Catalog数: {analysis.loadedCatalogs}");
report.AppendLine($"缓存Catalog数: {analysis.cachedCatalogs}");
report.AppendLine($"缓存命中率: {analysis.cacheHitRate * 100:F1}%");
report.AppendLine($"总加载时间: {analysis.totalLoadTime:F3}s");
report.AppendLine($"平均加载时间: {analysis.avgLoadTime:F3}s");
report.AppendLine();

report.AppendLine("按类型分布:");
foreach (var kvp in analysis.typeDistribution)
{
report.AppendLine($" {kvp.Key}: {kvp.Value} 个");
}
report.AppendLine();

report.AppendLine("活跃Catalog组:");
foreach (var group in catalogGroups.Values)
{
report.AppendLine($" {group.groupName}: {(group.isLoaded ? "已加载" : "未加载")}, " +
$"优先级 {group.loadPriority}, " +
$"Catalog数 {group.catalogIds.Count}");
}
report.AppendLine();

if (analysis.loadErrors.Count > 0)
{
report.AppendLine("加载错误:");
foreach (var error in analysis.loadErrors)
report.AppendLine($" {error}");
}

return report.ToString();
}

/// <summary>
/// 获取多Catalog管理最佳实践
/// </summary>
public string GetMultiCatalogBestPractices()
{
var practices = new System.Text.StringBuilder();
practices.AppendLine("=== 多Catalog管理最佳实践 ===");

practices.AppendLine("1. 分组策略:");
practices.AppendLine(" - 按功能、关卡、模块分组");
practices.AppendLine(" - 区分核心资源和可选资源");
practices.AppendLine(" - 考虑加载优先级");
practices.AppendLine();

practices.AppendLine("2. 加载管理:");
practices.AppendLine(" - 实现并发加载限制");
practices.AppendLine(" - 使用加载队列管理");
practices.AppendLine(" - 支持按需加载");
practices.AppendLine();

practices.AppendLine("3. 缓存策略:");
practices.AppendLine(" - 设置合理的缓存过期时间");
practices.AppendLine(" - 监控缓存使用情况");
practices.AppendLine(" - 实现缓存清理机制");
practices.AppendLine();

practices.AppendLine("4. 错误处理:");
practices.AppendLine(" - 实现重试机制");
practices.AppendLine(" - 提供降级方案");
practices.AppendLine(" - 记录详细的错误信息");
practices.AppendLine();

practices.AppendLine("5. 性能监控:");
practices.AppendLine(" - 监控加载时间");
practices.AppendLine(" - 跟踪缓存命中率");
practices.AppendLine(" - 分析内存使用情况");

return practices.ToString();
}

[System.Serializable]
public class MockResourceLocation : IResourceLocation
{
public string InternalId { get; set; }
public string Provider { get; set; }
public IList<IResourceLocation> Dependencies { get; set; }
public object Data { get; set; }
public string PrimaryKey { get; set; }

public MockResourceLocation(string primaryKey, string internalId, string provider)
{
PrimaryKey = primaryKey;
InternalId = internalId;
Provider = provider;
Dependencies = new List<IResourceLocation>();
}

public bool HasKey(string key)
{
return PrimaryKey == key;
}

public bool Matches(string key)
{
return PrimaryKey == key;
}

public T GetData<T>()
{
return (T)Data;
}
}

void OnDestroy()
{
Debug.Log("多Catalog管理系统清理完成");
}
}

通过本章的深入学习,您已经全面理解了Unity Addressables的Catalog系统,包括Catalog文件结构、Hash与CRC校验机制、ContentCatalogData源码实现、自定义Catalog Provider开发以及多Catalog管理策略。这些知识将帮助您构建高效、可靠的资源管理系统。