You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Add a Flavor attribute that can be used for different types of package or build configuration. (e.g. with embedded data rather than content-on-demand) Remove unused Build struct Modify "Platforms" (could be renamed in future) control to show Platform or Platform + Flavor for discovered items. Modify Platforms view to sync the size of all checkboxes by wrapping each one in a Grid. #rb yuriy.odonnell@epicgames.com #jira FORT-453980 #ushell-cherrypick of 19981339 by Robert.Millar #preflight skip [CL 19981575 by Yuriy ODonnell in ue5-main branch]
291 lines
6.9 KiB
C#
291 lines
6.9 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks.Dataflow;
|
|
using System.Xml.Linq;
|
|
using System.Net;
|
|
using System.Text.Json;
|
|
|
|
namespace UnsyncUI
|
|
{
|
|
public sealed class Config
|
|
{
|
|
// Structure that represents mirror server entry in the list from /api/v1/mirrors endpoint
|
|
class JsonMirrorDesc
|
|
{
|
|
public String name { get; set; }
|
|
public String address { get; set; }
|
|
public int port { get; set; } = 0;
|
|
public String description { get; set; }
|
|
public String parent { get; set; }
|
|
}
|
|
|
|
public sealed class Proxy
|
|
{
|
|
public string Name { get; set; }
|
|
public string Path { get; set; }
|
|
}
|
|
|
|
public struct BuildTemplate
|
|
{
|
|
public string Stream;
|
|
public string CL;
|
|
public string Suffix;
|
|
public string Platform;
|
|
public string Flavor;
|
|
|
|
public bool IsStreamClFound => Stream != null && CL != null;
|
|
}
|
|
|
|
public sealed class Directory
|
|
{
|
|
public Regex Regex { get; }
|
|
public string Stream { get; }
|
|
public string Suffix { get; }
|
|
public string CL { get; }
|
|
public string Platform { get; }
|
|
public string Flavor { get; }
|
|
public List<Directory> SubDirectories { get; }
|
|
|
|
public Directory(XElement node)
|
|
{
|
|
Regex = new Regex($@"^{node.Attribute("regex")?.Value}$", RegexOptions.IgnoreCase);
|
|
Stream = node.Attribute("stream")?.Value;
|
|
Suffix = node.Attribute("suffix")?.Value;
|
|
CL = node.Attribute("cl")?.Value;
|
|
Platform = node.Attribute("platform")?.Value;
|
|
Flavor = node.Attribute("flavor")?.Value;
|
|
SubDirectories = node.Elements("dir").Select(d => new Directory(d)).ToList();
|
|
}
|
|
|
|
public bool Parse(string path, ref BuildTemplate template)
|
|
{
|
|
var match = Regex.Match(Path.GetFileName(path));
|
|
if (!match.Success)
|
|
return false;
|
|
|
|
string RegexReplace(string field) => Regex.Replace(field, @"\$([0-9]+)", m => match.Groups[int.Parse(m.Groups[1].Value)].Value);
|
|
|
|
if (Stream != null)
|
|
{
|
|
template.Stream = RegexReplace(Stream);
|
|
}
|
|
if (CL != null)
|
|
{
|
|
template.CL = RegexReplace(CL);
|
|
}
|
|
if (Suffix != null)
|
|
{
|
|
template.Suffix = RegexReplace(Suffix);
|
|
}
|
|
if (Platform != null)
|
|
{
|
|
template.Platform = RegexReplace(Platform);
|
|
}
|
|
if (Flavor != null)
|
|
{
|
|
template.Flavor = RegexReplace(Flavor);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public sealed class Project
|
|
{
|
|
public string Name { get; set; }
|
|
public string Root { get; set; }
|
|
public string Destination { get; set; }
|
|
public List<string> Exclusions { get; set; }
|
|
public List<Directory> Children { get; set; }
|
|
|
|
private Task EnumerateBuilds(ITargetBlock<BuildModel> pipe, Directory d, string path, BuildTemplate template, CancellationToken cancelToken)
|
|
{
|
|
return Task.Run(async () =>
|
|
{
|
|
if (!d.Parse(path, ref template))
|
|
return;
|
|
|
|
if (template.IsStreamClFound)
|
|
{
|
|
pipe.Post(new BuildModel(path, d, template));
|
|
}
|
|
else
|
|
{
|
|
var jobs = new List<Task>();
|
|
foreach (var childDir in await AsyncIO.EnumerateDirectoriesAsync(path, cancelToken))
|
|
{
|
|
jobs.AddRange(d.SubDirectories.Select(s => EnumerateBuilds(pipe, s, childDir, template, cancelToken)));
|
|
}
|
|
|
|
await Task.WhenAll(jobs);
|
|
}
|
|
});
|
|
}
|
|
|
|
public ISourceBlock<BuildModel> EnumerateBuilds(CancellationToken cancelToken)
|
|
{
|
|
var pipe = new BufferBlock<BuildModel>();
|
|
Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
var allTasks = new List<Task>();
|
|
foreach (var dir in await AsyncIO.EnumerateDirectoriesAsync(Root, cancelToken))
|
|
{
|
|
allTasks.AddRange(Children.Select(c => EnumerateBuilds(pipe, c, dir, default(BuildTemplate), cancelToken)));
|
|
}
|
|
|
|
await Task.WhenAll(allTasks);
|
|
}
|
|
finally
|
|
{
|
|
pipe.Complete();
|
|
}
|
|
});
|
|
|
|
return pipe;
|
|
}
|
|
}
|
|
|
|
public List<Proxy> Proxies { get; set; } = new List<Proxy>();
|
|
public List<Project> Projects { get; set; }
|
|
|
|
public string UnsyncPath { get; set; }
|
|
public string DFS { get; set; }
|
|
|
|
public Config(string filename)
|
|
{
|
|
var rootNode = XDocument.Load(filename).Root;
|
|
|
|
UnsyncPath = rootNode.Attribute("path")?.Value;
|
|
|
|
if (UnsyncPath != null && !File.Exists(UnsyncPath))
|
|
{
|
|
throw new Exception("Unable to find unsync.exe binary specified in config file.");
|
|
}
|
|
|
|
DFS = rootNode.Attribute("dfs")?.Value;
|
|
|
|
Proxies.Add(new Proxy()
|
|
{
|
|
Name = "(none)",
|
|
Path = null
|
|
});
|
|
|
|
List<Proxy> ConfigProxies = new List<Proxy>();
|
|
|
|
ConfigProxies.AddRange(rootNode.Element("proxies").Elements("proxy").Select(p => new Proxy()
|
|
{
|
|
Name = p.Attribute("name")?.Value,
|
|
Path = p.Attribute("path")?.Value
|
|
}));
|
|
|
|
// Auto-discover proxies
|
|
List<Proxy> DiscoveredProxies = DiscoverProxies(ConfigProxies);
|
|
|
|
if (DiscoveredProxies == null)
|
|
{
|
|
Proxies.AddRange(ConfigProxies);
|
|
}
|
|
else
|
|
{
|
|
Proxies.AddRange(DiscoveredProxies);
|
|
}
|
|
|
|
Projects = rootNode.Element("projects").Elements("project").Select(p => new Project()
|
|
{
|
|
Name = p.Attribute("name")?.Value,
|
|
Root = p.Attribute("root")?.Value,
|
|
Destination = p.Attribute("dest")?.Value,
|
|
Children = p.Elements("dir").Select(d => new Directory(d)).ToList(),
|
|
Exclusions = p.Elements("exclude").Select(d => d.Value).ToList()
|
|
}).ToList();
|
|
}
|
|
|
|
private List<Proxy> DiscoverProxies(List<Proxy> SeedServers)
|
|
{
|
|
int DefaultPort = 53841;
|
|
|
|
foreach (Proxy SeedServer in SeedServers)
|
|
{
|
|
if (SeedServer.Path == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
String Url = SeedServer.Path;
|
|
if (!Url.Contains(":"))
|
|
{
|
|
Url += ":" + DefaultPort.ToString();
|
|
}
|
|
|
|
if (!Url.StartsWith("http://"))
|
|
{
|
|
Url = "http://" + Url;
|
|
}
|
|
|
|
Url += "/api/v1/mirrors";
|
|
|
|
var Request = WebRequest.Create(Url);
|
|
Request.Timeout = 2500;
|
|
var Response = (HttpWebResponse)Request.GetResponse();
|
|
if (Response.StatusCode == HttpStatusCode.OK)
|
|
{
|
|
var Reader = new StreamReader(Response.GetResponseStream());
|
|
var Body = Reader.ReadToEnd();
|
|
var ParsedList = JsonSerializer.Deserialize<List<JsonMirrorDesc>>(Body);
|
|
|
|
var ParsedProxies = new List<Proxy>();
|
|
foreach (var ParsedProxy in ParsedList)
|
|
{
|
|
if (ParsedProxy.address == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var ConvertedProxy = new Proxy();
|
|
ConvertedProxy.Path = ParsedProxy.address;
|
|
|
|
if (ParsedProxy.description != null)
|
|
{
|
|
ConvertedProxy.Name = ParsedProxy.description;
|
|
}
|
|
else if (ParsedProxy.name != null)
|
|
{
|
|
ConvertedProxy.Name = ParsedProxy.name;
|
|
}
|
|
|
|
if (ParsedProxy.port != 0)
|
|
{
|
|
ConvertedProxy.Path += ":" + ParsedProxy.port.ToString();
|
|
}
|
|
|
|
ParsedProxies.Add(ConvertedProxy);
|
|
}
|
|
|
|
if (ParsedProxies.Count != 0)
|
|
{
|
|
return ParsedProxies;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|