forked from aly/qstbak
muffin v7.5.11
This commit is contained in:
parent
addf2d530f
commit
3102923ce2
522 changed files with 41082 additions and 211592 deletions
239
Questionable/Questionable.Data/LgbWorkerSupervisor.cs
Normal file
239
Questionable/Questionable.Data/LgbWorkerSupervisor.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue