muffin v7.5.11

This commit is contained in:
alydev 2026-08-17 20:29:32 +10:00
parent addf2d530f
commit 3102923ce2
522 changed files with 41082 additions and 211592 deletions

View file

@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.LgbWorker;
namespace Questionable.Data;
internal sealed class LgbWorkerSupervisor
{
private const string WorkerExeName = "Questionable.LgbWorker.exe";
private const string WorkerSubDirectory = "lgb-worker";
private const string ManifestFileName = "lgb-worker-manifest.json";
private const string NpcCacheFileName = "npc-position-cache.bin";
private const string BoundaryCacheFileName = "zone-boundary-cache.bin";
private readonly IDataManager _dataManager;
private readonly ILogger<LgbWorkerSupervisor> _logger;
public LgbWorkerSupervisor(IDataManager dataManager, ILogger<LgbWorkerSupervisor> logger)
{
_dataManager = dataManager;
_logger = logger;
}
public bool RunWorker(string sqpackPath, string gameVersion, string cacheDirectory, string pluginAssemblyDirectory, CancellationToken ct)
{
string text = Path.Combine(pluginAssemblyDirectory, "lgb-worker", "Questionable.LgbWorker.exe");
if (!File.Exists(text))
{
_logger.LogWarning("LGB worker not found at {Path}, falling back to in-process scan", text);
return false;
}
string npcCachePath = Path.Combine(cacheDirectory, "npc-position-cache.bin");
string boundaryCachePath = Path.Combine(cacheDirectory, "zone-boundary-cache.bin");
string text2 = Path.Combine(cacheDirectory, "lgb-worker-manifest.json");
string text3 = $"Questionable.LgbWorker.{Guid.NewGuid():N}";
Directory.CreateDirectory(cacheDirectory);
_logger.LogDebug("Building LGB worker manifest from Excel sheets...");
List<TerritoryEntry> list = BuildTerritoryList();
List<LevelEntry> list2 = BuildLevelEntries();
_logger.LogDebug("Manifest: {TerritoryCount} territories, {LevelCount} level entries", list.Count, list2.Count);
LgbWorkerManifest value = new LgbWorkerManifest
{
SqpackPath = sqpackPath,
GameVersion = gameVersion,
NpcCachePath = npcCachePath,
BoundaryCachePath = boundaryCachePath,
Territories = list,
LevelEntries = list2
};
File.WriteAllText(text2, JsonSerializer.Serialize(value));
using NamedPipeServerStream namedPipeServerStream = new NamedPipeServerStream(text3, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
int processId = Environment.ProcessId;
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = text,
Arguments = $"--pipe {text3} --manifest \"{text2}\" --parent-pid {processId.ToString(CultureInfo.InvariantCulture)}",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true
};
Process process = Process.Start(startInfo);
try
{
if (process == null)
{
_logger.LogError("Failed to start LGB worker process");
return false;
}
_logger.LogInformation("LGB worker started (PID {Pid})", process.Id);
Task.Run(delegate
{
try
{
while (true)
{
string text4 = process.StandardError.ReadLine();
if (text4 == null)
{
break;
}
_logger.LogDebug("[LgbWorker] {Line}", text4);
}
}
catch
{
}
}, ct);
_logger.LogDebug("Waiting for LGB worker to connect...");
try
{
namedPipeServerStream.WaitForConnection();
}
catch (IOException exception)
{
_logger.LogError(exception, "LGB worker pipe connection failed");
process.Kill();
return false;
}
_logger.LogDebug("LGB worker connected, reading messages...");
try
{
while (true)
{
ct.ThrowIfCancellationRequested();
LgbWorkerMessage lgbWorkerMessage = LgbWorkerFraming.Read(namedPipeServerStream);
switch (lgbWorkerMessage.Type)
{
case LgbMessageType.Progress:
if (lgbWorkerMessage.Total > 0 && lgbWorkerMessage.Done % (lgbWorkerMessage.Total / 10 + 1) == 0)
{
_logger.LogDebug("LGB worker progress: {Done}/{Total}", lgbWorkerMessage.Done, lgbWorkerMessage.Total);
}
break;
case LgbMessageType.Final:
if (lgbWorkerMessage.Success)
{
_logger.LogInformation("LGB worker completed in {Duration}ms", lgbWorkerMessage.DurationMs);
process.WaitForExit(5000);
CleanupManifest(text2);
return true;
}
_logger.LogError("LGB worker reported failure: {Error}", lgbWorkerMessage.Error);
process.WaitForExit(5000);
CleanupManifest(text2);
return false;
}
}
}
catch (EndOfStreamException)
{
process.WaitForExit(5000);
_logger.LogError("LGB worker exited without a final message (exit code {Code})", process.ExitCode);
CleanupManifest(text2);
return false;
}
catch (OperationCanceledException)
{
process.Kill();
CleanupManifest(text2);
throw;
}
}
finally
{
if (process != null)
{
((IDisposable)process).Dispose();
}
}
}
private List<TerritoryEntry> BuildTerritoryList()
{
HashSet<uint> hashSet = new HashSet<uint>();
foreach (Aetheryte item in _dataManager.GetExcelSheet<Aetheryte>())
{
hashSet.Add(item.Territory.RowId);
}
List<TerritoryEntry> list = new List<TerritoryEntry>();
List<TerritoryEntry> list2 = new List<TerritoryEntry>();
foreach (TerritoryType item2 in _dataManager.GetExcelSheet<TerritoryType>())
{
if (item2.RowId == 0)
{
continue;
}
string text = item2.Bg.ExtractText();
if (!string.IsNullOrEmpty(text) && text.Contains("/level/", StringComparison.Ordinal))
{
TerritoryEntry territoryEntry = new TerritoryEntry
{
RowId = item2.RowId,
BgPath = text,
IntendedUse = (byte)item2.TerritoryIntendedUse.RowId,
HasAetheryte = hashSet.Contains(item2.RowId)
};
if (territoryEntry.HasAetheryte)
{
list.Add(territoryEntry);
}
else
{
list2.Add(territoryEntry);
}
}
}
list.AddRange(list2);
return list;
}
private List<LevelEntry> BuildLevelEntries()
{
ExcelSheet<ENpcBase> excelSheet = _dataManager.GetExcelSheet<ENpcBase>();
ExcelSheet<EObj> excelSheet2 = _dataManager.GetExcelSheet<EObj>();
List<LevelEntry> list = new List<LevelEntry>();
foreach (Level item in _dataManager.GetExcelSheet<Level>())
{
if (item.RowId != 0 && item.Object.RowId != 0 && item.Territory.IsValid && (excelSheet.HasRow(item.Object.RowId) || excelSheet2.HasRow(item.Object.RowId)))
{
list.Add(new LevelEntry
{
ObjectId = item.Object.RowId,
TerritoryId = (ushort)item.Territory.RowId,
X = item.X,
Y = item.Y,
Z = item.Z
});
}
}
return list;
}
private static void CleanupManifest(string path)
{
try
{
File.Delete(path);
}
catch
{
}
}
}