using System.Globalization;
using System.Text.Json;
namespace XFEEDToolBox.Core.Galaxy;
public sealed record GalaxyMissionItem(ulong MissionId,IReadOnlyList<long> SystemAddresses,long? SelectedSystemAddress)
{
public int MarkerType {get;init;}
}
public sealed record GalaxyMissionsSnapshot(bool Available,IReadOnlyList<GalaxyMissionItem> Missions)
{
public bool Complete=>false;
}
public sealed record GalaxyMissionMapTarget(ulong MissionId,long? SystemAddress,int MarkerType,string Label);
public sealed partial class NativeGalaxyAccountCatalog
{
public async Task<GalaxyMissionsSnapshot> ReadMissionsAsync(CancellationToken token=default)
{
var page=await pipe.CallAsync(new{op="galaxy.missions",limit=16},token);
if(!page.GetProperty("available").GetBoolean())return new(false,[]);
var generation=page.GetProperty("generation").GetUInt64();var total=page.GetProperty("total").GetInt32();var entity=page.GetProperty("entityId").GetString();
if(generation==0||total is <0 or >4096||string.IsNullOrEmpty(entity))throw new InvalidDataException("原游戏任务快照标识无效。");
static long Address(JsonElement value)
{
if(!long.TryParse(value.GetString(),NumberStyles.None,CultureInfo.InvariantCulture,out var address)||address<=0||address>=1L<<55)
throw new InvalidDataException("原游戏任务星系地址无效。");return address;
}
var missions=new List<GalaxyMissionItem>();var offset=0;
while(true)
{
token.ThrowIfCancellationRequested();
if(!page.GetProperty("available").GetBoolean()||page.GetProperty("generation").GetUInt64()!=generation||page.GetProperty("entityId").GetString()!=entity||page.GetProperty("total").GetInt32()!=total)
throw new InvalidDataException("原游戏任务快照已变化,请重试。");
var rows=page.GetProperty("items");if(rows.ValueKind!=JsonValueKind.Array||rows.GetArrayLength()>16||offset+rows.GetArrayLength()>total)throw new InvalidDataException("原游戏任务分页无效。");
foreach(var row in rows.EnumerateArray())
{
// Mission IDs have their own full u64 namespace. The original
// callback compares all bits; no address/sentinel rule applies.
if(!ulong.TryParse(row.GetProperty("missionId").GetString(),NumberStyles.None,CultureInfo.InvariantCulture,out var id))throw new InvalidDataException("原游戏任务 ID 无效。");
var type=row.GetProperty("markerType").GetInt32();var addresses=row.GetProperty("systemAddresses");
if(type is not(4 or 45)||addresses.ValueKind!=JsonValueKind.Array||addresses.GetArrayLength()>4096)throw new InvalidDataException("原游戏任务目标集合无效。");
var systems=addresses.EnumerateArray().Select(Address).ToArray();
var selectedValue=row.GetProperty("selectedSystemAddress");long? selected=selectedValue.ValueKind==JsonValueKind.Null?null:Address(selectedValue);
if(selected!=(systems.Length==0?(long?)null:systems[^1]))throw new InvalidDataException("原游戏任务所选地址不是完整原目标集合的最后项。");
missions.Add(new(id,systems,selected){MarkerType=type});
}
offset+=rows.GetArrayLength();if(offset==total)break;if(rows.GetArrayLength()==0)throw new InvalidDataException("原游戏任务分页未继续。");
page=await pipe.CallAsync(new{op="galaxy.missions",generation,offset,limit=16},token);
}
return new(true,missions);
}
}
using System.Globalization;
using System.Text.Json;
namespace XFEEDToolBox.Core.Galaxy;
public sealed record GalaxyMissionItem(ulong MissionId,IReadOnlyList<long> SystemAddresses,long? SelectedSystemAddress)
{
public int MarkerType {get;init;}
}
public sealed record GalaxyMissionsSnapshot(bool Available,IReadOnlyList<GalaxyMissionItem> Missions)
{
public bool Complete=>false;
}
public sealed record GalaxyMissionMapTarget(ulong MissionId,long? SystemAddress,int MarkerType,string Label);
public sealed partial class NativeGalaxyAccountCatalog
{
public async Task<GalaxyMissionsSnapshot> ReadMissionsAsync(CancellationToken token=default)
{
var page=await pipe.CallAsync(new{op="galaxy.missions",limit=16},token);
if(!page.GetProperty("available").GetBoolean())return new(false,[]);
var generation=page.GetProperty("generation").GetUInt64();var total=page.GetProperty("total").GetInt32();var entity=page.GetProperty("entityId").GetString();
if(generation==0||total is <0 or >4096||string.IsNullOrEmpty(entity))throw new InvalidDataException("原游戏任务快照标识无效。");
static long Address(JsonElement value)
{
if(!long.TryParse(value.GetString(),NumberStyles.None,CultureInfo.InvariantCulture,out var address)||address<=0||address>=1L<<55)
throw new InvalidDataException("原游戏任务星系地址无效。");return address;
}
var missions=new List<GalaxyMissionItem>();var offset=0;
while(true)
{
token.ThrowIfCancellationRequested();
if(!page.GetProperty("available").GetBoolean()||page.GetProperty("generation").GetUInt64()!=generation||page.GetProperty("entityId").GetString()!=entity||page.GetProperty("total").GetInt32()!=total)
throw new InvalidDataException("原游戏任务快照已变化,请重试。");
var rows=page.GetProperty("items");if(rows.ValueKind!=JsonValueKind.Array||rows.GetArrayLength()>16||offset+rows.GetArrayLength()>total)throw new InvalidDataException("原游戏任务分页无效。");
foreach(var row in rows.EnumerateArray())
{
// Mission IDs have their own full u64 namespace. The original
// callback compares all bits; no address/sentinel rule applies.
if(!ulong.TryParse(row.GetProperty("missionId").GetString(),NumberStyles.None,CultureInfo.InvariantCulture,out var id))throw new InvalidDataException("原游戏任务 ID 无效。");
var type=row.GetProperty("markerType").GetInt32();var addresses=row.GetProperty("systemAddresses");
if(type is not(4 or 45)||addresses.ValueKind!=JsonValueKind.Array||addresses.GetArrayLength()>4096)throw new InvalidDataException("原游戏任务目标集合无效。");
var systems=addresses.EnumerateArray().Select(Address).ToArray();
var selectedValue=row.GetProperty("selectedSystemAddress");long? selected=selectedValue.ValueKind==JsonValueKind.Null?null:Address(selectedValue);
if(selected!=(systems.Length==0?(long?)null:systems[^1]))throw new InvalidDataException("原游戏任务所选地址不是完整原目标集合的最后项。");
missions.Add(new(id,systems,selected){MarkerType=type});
}
offset+=rows.GetArrayLength();if(offset==total)break;if(rows.GetArrayLength()==0)throw new InvalidDataException("原游戏任务分页未继续。");
page=await pipe.CallAsync(new{op="galaxy.missions",generation,offset,limit=16},token);
}
return new(true,missions);
}
}