diff --git a/Manifest.cs b/Manifest.cs index 52f435d60213fc606c8b73a9808b13ed7d366a46..a6fcede1d9b0b7631c8c5a38bf12b0314060c251 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -14,16 +14,33 @@ namespace sunrise_launcher public class ManifestMetadata { - [JsonPropertyName("title")] - public string Title { get; set; } [JsonPropertyName("version")] public string Version { get; set; } - [JsonPropertyName("launch_path")] + [JsonPropertyName("launch_options")] + public List<LaunchOption> LaunchOptions { get; set; } + + public bool Verify() + { + if (LaunchOptions.Count == 0) + { + Console.WriteLine("need at least one launch config"); + return false; + } + + return LaunchOptions.All(x => x.Verify()); + } + } + + public class LaunchOption + { + [JsonPropertyName("title")] + public string Title { get; set; } + [JsonPropertyName("path")] public string LaunchPath { get; set; } - [JsonPropertyName("launch_env")] - public string LaunchEnv { get; set; } - [JsonPropertyName("launch_args")] - public string LaunchArgs { get; set; } + [JsonPropertyName("env")] + public string Env { get; set; } + [JsonPropertyName("args")] + public string Args { get; set; } public bool Verify() { diff --git a/ManifestFactory.cs b/ManifestFactory.cs index 31433f7490c7e482b9d140322d99e7e700f3af40..74e9e336636087bcdf3c9e69701ae7622f79d8bd 100644 --- a/ManifestFactory.cs +++ b/ManifestFactory.cs @@ -4,23 +4,26 @@ namespace sunrise_launcher { public interface IManifestFactory { - public IManifest Get(Server server); + public IManifest Get(string manifesturl); } public class ManifestFactory : IManifestFactory { - const string manifiesta_v1 = "manifiesta-v1"; + const string sunrise_api = "sunrise-api"; + const string sunrise_json = "sunrise-json"; const string tequila_xml = "tequila-xml"; - public IManifest Get(Server server) + public IManifest Get(string manifesturl) { - var schema = getSchema(server.ManifestURL); + var schema = getSchema(manifesturl); switch (schema) { - case manifiesta_v1: - return new Manifiesta(server.ManifestURL); + case sunrise_api: + return new SunriseApi(manifesturl); + case sunrise_json: + return new SunriseJson(manifesturl); case tequila_xml: - return new TequilaXML(server.ManifestURL); + return new TequilaXML(manifesturl); } return null; } @@ -29,8 +32,10 @@ namespace sunrise_launcher { if (manifesturl.ToLower().EndsWith(".xml")) return tequila_xml; + else if (manifesturl.ToLower().EndsWith(".json")) + return sunrise_json; else - return manifiesta_v1; + return sunrise_api; } } } diff --git a/Server.cs b/Server.cs index 42c729e70c8245803c2f8f449a7e18805f794bfc..c45baffa482b3a33d792e79c8937087b96930ac8 100644 --- a/Server.cs +++ b/Server.cs @@ -1,15 +1,21 @@ using Qml.Net; +using System.Collections.Generic; using System.Text.Json.Serialization; namespace sunrise_launcher { - public class Server : ManifestMetadata + public class Server { [JsonPropertyName("manifest_url")] [NotifySignal("manifestUrlChanged")] public string ManifestURL { get; set; } [JsonPropertyName("install_path")] public string InstallPath { get; set; } + [JsonPropertyName("metadata")] + public ManifestMetadata Metadata { get; set; } + [JsonPropertyName("launch")] + [NotifySignal("launchChanged")] + public string Launch { get; set; } [JsonIgnore] public State State { get; set; } [JsonIgnore] diff --git a/ServerList.cs b/ServerList.cs index 2bd396884635a82c5118f04105353623410b7be3..52607e611b778e5545f52198669f2b4b73ce3045 100644 --- a/ServerList.cs +++ b/ServerList.cs @@ -68,7 +68,42 @@ namespace sunrise_launcher file.Save(path); } - public async Task AddAsync(string manifesturl, string installpath) + public async Task<ManifestMetadata> FindMetadataAsync(string manifesturl) + { + manifesturl = manifesturl.Trim(); + + try + { + var manifest = manifestFactory.Get(manifesturl); + if (manifest == null) + { + Console.WriteLine("Unknown manifest schema at '{0}'", manifesturl); + return null; + } + + var metadata = await manifest.GetMetadataAsync(); + if (metadata == null) + { + Console.WriteLine("Could not retrieve manifest from '{0}'", manifesturl); + return null; + } + + if (!metadata.Verify()) + { + Console.WriteLine("Manifest metadata failed inspection '{0}'", manifesturl); + return null; + } + + return metadata; + } + catch (Exception ex) + { + Console.WriteLine("Exception in FindMetadataAsync: {0}", ex.Message); + return null; + } + } + + public async Task AddAsync(string manifesturl, string installpath, string launch) { manifesturl = manifesturl.Trim(); if (Servers.Any(x => x.ManifestURL == manifesturl)) @@ -80,11 +115,12 @@ namespace sunrise_launcher var server = new Server(); server.ManifestURL = manifesturl; server.InstallPath = CleanInstallPath(installpath); + server.Launch = launch; - await GetInfoAsync(server); - if (server.State == State.Error) + server.Metadata = await FindMetadataAsync(manifesturl); + if (server.Metadata == null) { - ShowMessage(server.Error); + ShowMessage("Server add failed."); return; } @@ -97,15 +133,16 @@ namespace sunrise_launcher ShowMessage(server.Error); } - public async Task ConfigAsync(string oldmanifesturl, string manifesturl, string installpath) + public async Task ConfigAsync(string oldmanifesturl, string manifesturl, string installpath, string launch) { var server = Get(oldmanifesturl); if (server == null) return; server.ManifestURL = manifesturl.Trim(); server.InstallPath = CleanInstallPath(installpath); + server.Launch = launch; - await GetInfoAsync(server); - this.ActivateSignal("update"); + //await GetInfoAsync(server); + //this.ActivateSignal("update"); await UpdateAsync(server, false); this.ActivateSignal("update"); @@ -161,7 +198,8 @@ namespace sunrise_launcher ShowMessage(server.Error); } - //this is called only when a new server is added + //this is called only when a new server is added or modified + /* private async Task GetInfoAsync(Server server) { if (server.State == State.Updating) @@ -170,7 +208,7 @@ namespace sunrise_launcher try { - var manifest = manifestFactory.Get(server); + var manifest = manifestFactory.Get(server.ManifestURL); if (manifest == null) { Console.WriteLine("Unknown manifest schema at '{0}'", server.ManifestURL); @@ -196,10 +234,16 @@ namespace sunrise_launcher return; } - server.Title = metadata.Title; - server.LaunchPath = metadata.LaunchPath; - server.LaunchEnv = metadata.LaunchEnv; - server.LaunchArgs = metadata.LaunchArgs; + server.Metadata = metadata; + + if (metadata.LaunchOptions.Count == 0) + { + server.LaunchConfig = null; + } + else if (metadata.LaunchOptions.All(x => x.Title != server.LaunchConfig)) + { + server.LaunchConfig = metadata.LaunchOptions[0].Title; + } } catch (Exception ex) { @@ -208,7 +252,7 @@ namespace sunrise_launcher server.Error = "Unknown Exception"; } } - + */ private async Task UpdateAsync(Server server, bool force) { if (server.State == State.Updating) @@ -220,7 +264,7 @@ namespace sunrise_launcher { UpdateProgress(server, "Retrieving Manfiest", 0, 0); - var manifest = manifestFactory.Get(server); + var manifest = manifestFactory.Get(server.ManifestURL); if (manifest == null) { Console.WriteLine("Unknown manifest schema at '{0}'", server.ManifestURL); @@ -246,7 +290,7 @@ namespace sunrise_launcher return; } - if (force || metadata.Version != server.Version) + if (force || metadata.Version != server.Metadata.Version) { await updatefiles(server, manifest, server.InstallPath); } @@ -257,11 +301,13 @@ namespace sunrise_launcher if (server.State == State.Ready) { - server.Title = metadata.Title; - server.Version = metadata.Version; - server.LaunchPath = metadata.LaunchPath; - server.LaunchEnv = metadata.LaunchEnv; - server.LaunchArgs = metadata.LaunchArgs; + server.Metadata = metadata; + } + + //if update occurs which removes the selected launch option, default to first option available + if (metadata.LaunchOptions.All(x => x.Title != server.Launch)) + { + server.Launch = metadata.LaunchOptions[0].Title; } } catch (Exception ex) @@ -436,12 +482,18 @@ namespace sunrise_launcher try { - var fullpath = Path.Combine(server.InstallPath, server.LaunchPath); + var launch = server.Metadata.LaunchOptions.FirstOrDefault(x => x.Title == server.Launch); + if (launch == null) + { + Console.WriteLine("ERROR: launch option not found: {0}", server.Launch); + } + + var fullpath = Path.Combine(server.InstallPath, launch.LaunchPath); var process = new Process(); process.StartInfo.WorkingDirectory = Path.GetDirectoryName(fullpath); process.StartInfo.FileName = fullpath; - process.StartInfo.Arguments = server.LaunchArgs; + process.StartInfo.Arguments = launch.Args; process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized; process.Start(); } diff --git a/Manifiesta.cs b/SunriseApi.cs similarity index 95% rename from Manifiesta.cs rename to SunriseApi.cs index 010258a31c0e8fa4d681fc84bbc0a1b91b5433ee..110ce5ab6603175cb1c4356556e3ca1fa7f645c8 100644 --- a/Manifiesta.cs +++ b/SunriseApi.cs @@ -8,12 +8,12 @@ using System.Threading.Tasks; namespace sunrise_launcher { - public class Manifiesta : IManifest + public class SunriseApi : IManifest { private static HttpClient client = new HttpClient(); private string URL; - public Manifiesta(string url) + public SunriseApi(string url) { URL = url; } diff --git a/SunriseJson.cs b/SunriseJson.cs new file mode 100644 index 0000000000000000000000000000000000000000..2ed5f80709576d87f2d6ecea07fe4abf5fed13dd --- /dev/null +++ b/SunriseJson.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace sunrise_launcher +{ + public class SunriseJson : IManifest + { + private static HttpClient client = new HttpClient(); + private string URL; + private Manifest Manifest; + + public SunriseJson(string url) + { + URL = url; + } + + private async Task FetchManifest() + { + if (Manifest != null) + return; + + try + { + var response = await client.GetAsync(URL); + if (response.IsSuccessStatusCode) + { + using (var reader = await response.Content.ReadAsStreamAsync()) + { + Manifest = await JsonSerializer.DeserializeAsync<Manifest>(reader); + } + } + } + catch (Exception ex) + { + Console.WriteLine("exception while retrieving manifest: {0}", ex.Message); + } + } + + public async Task<ManifestMetadata> GetMetadataAsync() + { + try + { + await FetchManifest(); + return Manifest; + } + catch (Exception ex) + { + Console.WriteLine("exception while retrieving manifest: {0}", ex.Message); + } + return null; + } + + public async Task<IList<ManifestFile>> GetFilesAsync() + { + try + { + await FetchManifest(); + if (Manifest == null) + return null; + + return Manifest.Files; + } + catch (Exception ex) + { + Console.WriteLine("exception while retrieving manifest: {0}", ex.Message); + } + return null; + } + } +} diff --git a/TequilaXML.cs b/TequilaXML.cs index b3ad3750ef597b316763e84b40694b051b04538d..5292b7a160bd199a714676c54ca47a09a8ca4d1b 100644 --- a/TequilaXML.cs +++ b/TequilaXML.cs @@ -54,14 +54,19 @@ namespace sunrise_launcher if (TequilaRoot == null) return null; - if (TequilaRoot.Profiles.Count == 0) - return null; - var metadata = new ManifestMetadata(); metadata.Version = Hash; - metadata.Title = TequilaRoot.Profiles[0].Value; - metadata.LaunchPath = TequilaRoot.Profiles[0].Exec; - metadata.LaunchArgs = TequilaRoot.Profiles[0].Params; + metadata.LaunchOptions = new List<LaunchOption>(); + + foreach (var profile in TequilaRoot.Profiles) + { + var config = new LaunchOption(); + config.Title = profile.Value; + config.LaunchPath = profile.Exec; + config.Args = profile.Params; + metadata.LaunchOptions.Add(config); + } + return metadata; } diff --git a/main.js b/main.js index 6248bd1559a52adc0594257dd56d62b61a96dce8..d528225557fc5b2eab36f042c687595340ae0ead 100644 --- a/main.js +++ b/main.js @@ -109,25 +109,93 @@ function configOpen(manifestURL) { textfield_manifesturl.text = server.manifestURL; textfield_installpath.text = server.installPath; + + populateLaunchOptions(server.metadata); + combobox_launch.currentIndex = combobox_launch.find(server.launch); + dialog_config.manifestURL = manifestURL; - dialog_config.title = server.title; dialog_config.visible = true; } +function populateLaunchOptions(metadata) { + var options = []; + Net.listForEach(metadata.launchOptions, function (option) { + options.push(option.title); + }); + combobox_launch.model = options; +} + +function configUrlChanged() { + combobox_launch.currentIndex = -1; + combobox_launch.model = []; +} + +function findUrl() { + combobox_launch.currentIndex = -1; + combobox_launch.model = []; + + if (textfield_manifesturl.text === "") { + showInfo("Please enter a URL for the manifest.") + return; + } + + var task = serverlist.findMetadataAsync(textfield_manifesturl.text); + Net.await(task, function (metadata) { + if (metadata == null) { + showMessage("Could not retrieve manifest at that location.") + return; + } + populateLaunchOptions(metadata); + combobox_launch.currentIndex = 0; + }); +} + function configNew() { textfield_manifesturl.text = ""; textfield_installpath.text = ""; dialog_config.manifestURL = ""; + combobox_launch.currentIndex = -1; + combobox_launch.model = []; dialog_config.title = "New Server"; dialog_config.visible = true; + } -function configSave() { +function configSave(action) { + if (action.button == StandardButton.Cancel) + return; + + if (textfield_manifesturl.text === "") { + showInfo("Please enter a URL for the manifest.") + action.accepted = false; + return; + } + + //auto "find" and select first launch option + if (combobox_launch.currentIndex == -1) { + var task = serverlist.findMetadataAsync(textfield_manifesturl.text); + Net.await(task, function (metadata) { + if (metadata == null) { + showMessage("Could not retrieve manifest at that location.") + return; + } + populateLaunchOptions(metadata); + combobox_launch.currentIndex = 0; + + if (dialog_config.manifestURL === "") { + serverlist.addAsync(textfield_manifesturl.text, textfield_installpath.text, combobox_launch.currentText); + return; + } + serverlist.configAsync(dialog_config.manifestURL, textfield_manifesturl.text, textfield_installpath.text, combobox_launch.currentText); + }); + return; + } + if (dialog_config.manifestURL === "") { - serverlist.addAsync(textfield_manifesturl.text, textfield_installpath.text); + serverlist.addAsync(textfield_manifesturl.text, textfield_installpath.text, combobox_launch.currentText); return; } - serverlist.configAsync(dialog_config.manifestURL, textfield_manifesturl.text, textfield_installpath.text); + serverlist.configAsync(dialog_config.manifestURL, textfield_manifesturl.text, textfield_installpath.text, combobox_launch.currentText); } function showError(error) { @@ -141,6 +209,11 @@ function showMessage(msg) { dialogError.visible = true; } +function showInfo(msg) { + dialogInfo.text = msg; + dialogInfo.visible = true; +} + function refreshProgress(manifestURL, taskName, taskDone, taskCount) { if (serverlist.selected == null || taskName == "") { paneProgress.visible = false; diff --git a/main.qml b/main.qml index 8a5f0e7651c53e0c990a489f37d71f428d112fd7..af7254ea95921d0e4dc1843dba687e5002ea332a 100644 --- a/main.qml +++ b/main.qml @@ -159,7 +159,7 @@ ApplicationWindow { RadioButton { id: button_server y: 10 - text: modelData.title + text: modelData.launch ButtonGroup.group: buttongroup_servers anchors.verticalCenter: parent.verticalCenter anchors.left: button_remove.right @@ -384,8 +384,10 @@ ApplicationWindow { standardButtons: StandardButton.Save | StandardButton.Cancel modality: Qt.WindowModal property string manifestURL - onAccepted: Main.configSave(); - width: 500 + property bool verified + onActionChosen: Main.configSave(action); + width: 548 + height: 400 GridLayout { @@ -394,7 +396,22 @@ ApplicationWindow { anchors.right: parent.right anchors.rightMargin: 0 - columns: 2 + columns: 3 + + Text { + text: "Install Path" + font.family: fontMont.name + } + + TextField { + placeholderText: "ex: servername or C:/CoH" + font.family: fontMont.name + id: textfield_installpath + Layout.fillWidth: true + selectByMouse: true + } + + Text { text: " " } Text { text: "Manifest URL" @@ -406,19 +423,25 @@ ApplicationWindow { id: textfield_manifesturl Layout.fillWidth: true selectByMouse: true + onTextChanged: Main.configUrlChanged() + } + + Button { + text: "Find" + id: "button_findurl" + onClicked: Main.findUrl() } Text { - text: "Install Path" + text: "Launch" font.family: fontMont.name } - TextField { - placeholderText: "ex: servername or C:/CoH" - font.family: fontMont.name - id: textfield_installpath + ComboBox { + id: combobox_launch Layout.fillWidth: true - selectByMouse: true + font.family: fontMont.name + palette.highlightedText: "black" } } } @@ -441,6 +464,12 @@ ApplicationWindow { informativeText: "See log.txt for details" } + MessageDialog { + id: dialogInfo + modality: Qt.WindowModal + standardButtons: StandardButton.Ok + } + Component.onCompleted: Main.init() onClosing: serverlist.save("./servers.json"); } \ No newline at end of file diff --git a/sunrise-launcher.csproj b/sunrise-launcher.csproj index 87bfcac9ae848daca2fa064c3dbd52c81ed6eeea..a4fbf7b72debb9af393f3688ab9a4069e2bae458 100644 --- a/sunrise-launcher.csproj +++ b/sunrise-launcher.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <OutputType>WinExe</OutputType> + <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.1</TargetFramework> <PublishTrimmed>true</PublishTrimmed> <PublishReadyToRun>true</PublishReadyToRun>