Skip to content

Commit 78807e1

Browse files
committed
Extra catch for really bad case cache failures.
1 parent a297d64 commit 78807e1

File tree

1 file changed

+102
-10
lines changed

1 file changed

+102
-10
lines changed

xivModdingFramework/Cache/XivCache.cs

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using xivModdingFramework.Items.Interfaces;
1515
using xivModdingFramework.Mods;
1616
using xivModdingFramework.Resources;
17+
using xivModdingFramework.SqPack.FileTypes;
1718

1819
namespace xivModdingFramework.Cache
1920
{
@@ -257,7 +258,13 @@ public static void RebuildCache()
257258

258259
} catch (Exception Ex)
259260
{
260-
SetMetaValue("needs_rebuild", "1");
261+
try
262+
{
263+
SetMetaValue("needs_rebuild", "1");
264+
}
265+
catch {
266+
// No-op. We're pretty fucked at this point.
267+
}
261268
_REBUILDING = false;
262269
throw;
263270
}
@@ -1153,6 +1160,42 @@ public static async Task<List<string>> GetChildFiles(string internalFilePath, bo
11531160
return list;
11541161
}
11551162

1163+
/// <summary>
1164+
/// Determines if a file is orphaned or not in the file system.
1165+
/// An Orphaned file is a custom file which no longer has any files in the filesystem actively referencing it.
1166+
/// These files may be safely deleted IFF all mods are currently active in ModList.
1167+
/// ( Determining if any deactivated mods use the file is much more annoying )
1168+
/// </summary>
1169+
/// <param name="internalFilePath"></param>
1170+
/// <returns></returns>
1171+
public static async Task<bool> IsOrphanedFile(string internalFilePath)
1172+
{
1173+
var index = new Index(GameInfo.GameDirectory);
1174+
// So to be orphaned, a file has to pass(fail) a few criteria.
1175+
1176+
// 1. It cannot be a default FFXIV file. Default files are never treated as orphans for safety.
1177+
var def = await index.IsDefaultFilePath(internalFilePath);
1178+
if(def) return false;
1179+
1180+
// 2. It has to have updated cache data.
1181+
await GetParentFiles(internalFilePath);
1182+
1183+
// 3. That cache data must specifically have exactly one parent listed, and that parent must be NULL.
1184+
var wc = new WhereClause() { Column = "child", Comparer = WhereClause.ComparisonType.Equal, Value = internalFilePath };
1185+
var list = await BuildListFromTable("dependencies_parents", wc, async (reader) =>
1186+
{
1187+
return reader.GetString("parent");
1188+
});
1189+
1190+
if(list.Count == 1 && list[0] == null)
1191+
{
1192+
return true;
1193+
}
1194+
return false;
1195+
1196+
1197+
}
1198+
11561199
/// <summary>
11571200
/// Retreives the parent files in the dependency graph for this file.
11581201
/// </summary>
@@ -1179,16 +1222,9 @@ public static async Task<List<string>> GetParentFiles(string internalFilePath, b
11791222

11801223
if (list.Count == 0)
11811224
{
1182-
// Need to pull the raw data to verify a 0 count entry.
1225+
// 0 Length list means there was no cached data.
11831226
list = await XivDependencyGraph.GetParentFiles(internalFilePath);
11841227
await UpdateParentFiles(internalFilePath, list);
1185-
1186-
// So if we updated our own parents, we have to update our children's parents too.
1187-
// Because we may be a previously orphaned node, that now is re-attached to the main tree.
1188-
var children = await GetChildFiles(internalFilePath);
1189-
1190-
// In short, any parent calculation is always cascading downwards.
1191-
QueueParentFilesUpdate(children);
11921228
}
11931229
return list;
11941230
}
@@ -1538,18 +1574,45 @@ public static async Task<List<string>> UpdateChildFiles(string internalFilePath,
15381574
children = await XivDependencyGraph.GetChildFiles(internalFilePath);
15391575
}
15401576

1577+
var oldCacheChildren = new List<string>();
1578+
15411579
using (var db = new SQLiteConnection(CacheConnectionString))
15421580
{
15431581
db.Open();
15441582
using (var transaction = db.BeginTransaction())
15451583
{
1584+
// Clear out our old children.
15461585
var query = "delete from dependencies_children where parent = $parent";
15471586
using (var cmd = new SQLiteCommand(query, db))
15481587
{
15491588
cmd.Parameters.AddWithValue("parent", internalFilePath);
15501589
cmd.ExecuteScalar();
15511590
}
15521591

1592+
// Find all the files that currently point to us as a parent in the cache.
1593+
query = "select child from dependencies_parents where parent = $parent";
1594+
using (var cmd = new SQLiteCommand(query, db))
1595+
{
1596+
cmd.Parameters.AddWithValue("parent", internalFilePath);
1597+
using (var reader = new CacheReader(cmd.ExecuteReader()))
1598+
{
1599+
while (reader.NextRow())
1600+
{
1601+
oldCacheChildren.Add(reader.GetString("child"));
1602+
}
1603+
}
1604+
}
1605+
1606+
// And purge all of those cached file's parents (we'll queue them up for recalculation after the transaction is done)
1607+
// Could let the queue-up do the purging for us here, but then there's potential that some other thread
1608+
// might come in and pluck up bad data between our transaction and queuing call.
1609+
query = "delete from dependencies_parents where child in (select child as c_file from dependencies_parents where parent = $parent)";
1610+
using (var cmd = new SQLiteCommand(query, db))
1611+
{
1612+
cmd.Parameters.AddWithValue("parent", internalFilePath);
1613+
cmd.ExecuteScalar();
1614+
}
1615+
15531616
if (children == null || children.Count == 0)
15541617
{
15551618
// Null indicator says we updated the data, but there were no parents.
@@ -1578,6 +1641,19 @@ public static async Task<List<string>> UpdateChildFiles(string internalFilePath,
15781641
}
15791642
}
15801643

1644+
var toUpdate = new HashSet<string>();
1645+
foreach (var file in oldCacheChildren)
1646+
{
1647+
toUpdate.Add(file);
1648+
}
1649+
foreach(var file in children)
1650+
{
1651+
toUpdate.Add(file);
1652+
}
1653+
1654+
// Queue everything up that we affected.
1655+
QueueParentFilesUpdate(toUpdate.ToList());
1656+
15811657
return children;
15821658
}
15831659

@@ -1806,12 +1882,28 @@ private static void ProcessDependencyQueue(object sender, DoWorkEventArgs e)
18061882
/// <returns></returns>
18071883
internal static async Task<List<string>> GetCacheParents(string internalFilePath)
18081884
{
1885+
18091886
var wc = new WhereClause() { Column = "child", Comparer = WhereClause.ComparisonType.Equal, Value = internalFilePath };
1810-
return await BuildListFromTable("dependencies_children", wc, async (reader) =>
1887+
List<string> parents = await BuildListFromTable("dependencies_children", wc, async (reader) =>
18111888
{
18121889
return reader.GetString("parent");
18131890
});
18141891

1892+
// It's possible this file is a DX9 => 11 conversion texture, in which case we also have to look for references to our
1893+
// DX9 version.
1894+
var dx11Name = internalFilePath.Replace("--", "");
1895+
if (dx11Name != internalFilePath)
1896+
{
1897+
wc = new WhereClause() { Column = "child", Comparer = WhereClause.ComparisonType.Equal, Value = dx11Name };
1898+
List<string> dxParents = await BuildListFromTable("dependencies_children", wc, async (reader) =>
1899+
{
1900+
return reader.GetString("parent");
1901+
});
1902+
1903+
parents.AddRange(dxParents);
1904+
};
1905+
return parents;
1906+
18151907
}
18161908

18171909
/// <summary>

0 commit comments

Comments
 (0)