perf(content): remove exception-driven setup probes
This commit is contained in:
parent
f05afc07c1
commit
230a7df454
11 changed files with 598 additions and 47 deletions
101
src/AcDream.App/Composition/PreparedSetupResolver.cs
Normal file
101
src/AcDream.App/Composition/PreparedSetupResolver.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using AcDream.Content;
|
||||
using AcDream.Content.Pak;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
||||
namespace AcDream.App.Composition;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves Setup presentation metadata only when the immutable prepared
|
||||
/// catalog proves that the source DID is a Setup. Ordinary GfxObj IDs never
|
||||
/// reach the DAT deserializer, so absence is ordinary control flow rather
|
||||
/// than an exception.
|
||||
/// </summary>
|
||||
internal sealed class PreparedSetupResolver
|
||||
{
|
||||
private readonly IPreparedAssetSource _preparedAssets;
|
||||
private readonly Func<uint, Setup?> _loadSetup;
|
||||
private readonly Action<string> _diagnostic;
|
||||
private readonly ConcurrentDictionary<uint, byte> _diagnosed = new();
|
||||
|
||||
public PreparedSetupResolver(
|
||||
IPreparedAssetSource preparedAssets,
|
||||
Func<uint, Setup?> loadSetup,
|
||||
Action<string> diagnostic)
|
||||
{
|
||||
_preparedAssets = preparedAssets
|
||||
?? throw new ArgumentNullException(nameof(preparedAssets));
|
||||
_loadSetup = loadSetup
|
||||
?? throw new ArgumentNullException(nameof(loadSetup));
|
||||
_diagnostic = diagnostic
|
||||
?? throw new ArgumentNullException(nameof(diagnostic));
|
||||
}
|
||||
|
||||
public bool TryResolve(
|
||||
uint sourceId,
|
||||
[NotNullWhen(true)] out Setup? setup)
|
||||
{
|
||||
PreparedAssetPresence presence =
|
||||
_preparedAssets.Probe(PakAssetType.SetupMesh, sourceId);
|
||||
if (presence == PreparedAssetPresence.Missing)
|
||||
{
|
||||
setup = null;
|
||||
return false;
|
||||
}
|
||||
if (presence == PreparedAssetPresence.Corrupt)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Prepared Setup entry 0x{sourceId:X8} is corrupt; " +
|
||||
"activation cannot safely infer presentation metadata.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
setup = _loadSetup(sourceId);
|
||||
if (setup is not null)
|
||||
return true;
|
||||
|
||||
DiagnoseOnce(
|
||||
sourceId,
|
||||
$"Prepared Setup entry 0x{sourceId:X8} exists but its " +
|
||||
"matching DAT record could not be loaded; activation skipped.");
|
||||
return false;
|
||||
}
|
||||
catch (InvalidDataException ex)
|
||||
{
|
||||
return DiagnoseCorruptData(sourceId, ex, out setup);
|
||||
}
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
return DiagnoseCorruptData(sourceId, ex, out setup);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException ex)
|
||||
{
|
||||
// DatReaderWriter's verified malformed-record failure:
|
||||
// QualifiedDataId<T>.Unpack / Setup.Unpack can surface an
|
||||
// ArgumentOutOfRangeException for invalid record contents.
|
||||
return DiagnoseCorruptData(sourceId, ex, out setup);
|
||||
}
|
||||
}
|
||||
|
||||
private bool DiagnoseCorruptData(
|
||||
uint sourceId,
|
||||
Exception exception,
|
||||
out Setup? setup)
|
||||
{
|
||||
setup = null;
|
||||
DiagnoseOnce(
|
||||
sourceId,
|
||||
$"Setup DAT record 0x{sourceId:X8} is malformed " +
|
||||
$"({exception.GetType().Name}: {exception.Message}); " +
|
||||
"activation skipped.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DiagnoseOnce(uint sourceId, string message)
|
||||
{
|
||||
if (_diagnosed.TryAdd(sourceId, 0))
|
||||
_diagnostic(message);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue