diff --git a/.gitmodules b/.gitmodules index 0f61397..d21185f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "external/xwt"] path = external/xwt - url = https://github.com/mono/xwt.git + url = https://github.com/ikkentim/xwt.git diff --git a/AppImage/Packager.sh b/AppImage/Packager.sh index 55c208f..9b9a787 100755 --- a/AppImage/Packager.sh +++ b/AppImage/Packager.sh @@ -15,8 +15,8 @@ mkdir -p ./$APP/$APP.AppDir/usr/bin mkdir -p ./$APP/$APP.AppDir/usr/opt #build Nexus Client and copy release into bin -xbuild /p:Configuration=Release ./../src/ParkitectNexus.Client.Linux.Xwt/ParkitectNexus.Client.Linux.Xwt.csproj -cp -R ./../src/ParkitectNexus.Client.Linux.Xwt/bin/Release/* ./$APP/$APP.AppDir/usr/bin +xbuild /p:Configuration=Release ./../src/ParkitectNexus.Client.Linux/ParkitectNexus.Client.Linux.csproj +cp -R ./../bin/Release/* ./$APP/$APP.AppDir/usr/bin # Figure out $VERSION VERSION=$(git describe origin/master --tags $(git rev-list --tags --max-count=0)) diff --git a/AppImage/parkitectnexus b/AppImage/parkitectnexus index 9fbe861..33e6374 100755 --- a/AppImage/parkitectnexus +++ b/AppImage/parkitectnexus @@ -6,7 +6,7 @@ export MONO_CONFIG=././etc/mono/config export MONO_CFG_DIR=././etc ././bin/mozroots --import --sync -././bin/mono "././bin/ParkitectNexus.Client.Linux.Xwt.exe" "$@" +././bin/mono "././bin/ParkitectNexus.Client.Linux.exe" "$@" diff --git a/external/xwt b/external/xwt index 157ffe3..feeb4e2 160000 --- a/external/xwt +++ b/external/xwt @@ -1 +1 @@ -Subproject commit 157ffe378f5a633050d7e09655471bccd90a360c +Subproject commit feeb4e24c7e2a163d0006549496f589b3da88894 diff --git a/images/.directory b/images/.directory deleted file mode 100644 index d466e93..0000000 --- a/images/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -PreviewsShown=true -Timestamp=2016,4,22,20,48,37 -Version=3 diff --git a/images/parkitectnexus_logo/parkitectnexus_logo.png b/images/parkitectnexus_logo/parkitectnexus_logo.png new file mode 100644 index 0000000..d96c561 Binary files /dev/null and b/images/parkitectnexus_logo/parkitectnexus_logo.png differ diff --git a/src/ParkitectNexus.Client.Base/App.cs b/src/ParkitectNexus.Client.Base/App.cs index b93e08e..fdd3d27 100644 --- a/src/ParkitectNexus.Client.Base/App.cs +++ b/src/ParkitectNexus.Client.Base/App.cs @@ -120,7 +120,7 @@ public bool Initialize(ToolkitType type) _migrator.Migrate(); ModLoaderUtil.InstallModLoader(_parkitect, _log); - + _taskManager.Add(); return true; } diff --git a/src/ParkitectNexus.Client.Base/Pages/AssetsPageView.cs b/src/ParkitectNexus.Client.Base/Pages/AssetsPageView.cs index 5a97be6..d4e35df 100644 --- a/src/ParkitectNexus.Client.Base/Pages/AssetsPageView.cs +++ b/src/ParkitectNexus.Client.Base/Pages/AssetsPageView.cs @@ -23,31 +23,43 @@ using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Presenter; using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; using Xwt; using Xwt.Drawing; using Image = System.Drawing.Image; +using System.Linq; namespace ParkitectNexus.Client.Base.Pages { public class AssetsPageView : LoadableDataTileView { + private string[] _requiredAssets; private readonly IParkitect _parkitect; + private readonly IWebsite _website; private readonly ILogger _log; private readonly AssetType _type; - public AssetsPageView(IParkitect parkitect, ILogger log, AssetType type, IPresenter parent, string displayName) + public AssetsPageView(IParkitect parkitect, IWebsite website, ILogger log, AssetType type, IPresenter parent, string displayName) : base(log, displayName) { if (!(parent is MainView)) throw new ArgumentException("parent must be MainView", nameof(parent)); _parkitect = parkitect; + _website = website; _log = log; _type = type; MainView = (MainView) parent; parkitect.Assets.AssetAdded += Assets_AssetAdded; parkitect.Assets.AssetRemoved += Assets_AssetRemoved; + + GetRequiredMods(); + } + + private async void GetRequiredMods() + { + _requiredAssets = await _website.API.GetRequiredModIdentifiers(); } public MainView MainView { get; } @@ -92,7 +104,10 @@ protected virtual void PopulateViewBoxWithImage(VBox vBox, IAsset asset) protected virtual void PopulateViewBoxWithButtons(VBox vBox, IAsset asset) { - var deleteButton = new Button("Delete"); + var canDelete = _requiredAssets == null || !_requiredAssets.Contains(asset.Id); + + var deleteButton = new Button("Delete") { Sensitive = canDelete }; + deleteButton.Clicked += (sender, args) => { if ( diff --git a/src/ParkitectNexus.Client.Base/Pages/BlueprintsPageView.cs b/src/ParkitectNexus.Client.Base/Pages/BlueprintsPageView.cs index ff736a6..8307f22 100644 --- a/src/ParkitectNexus.Client.Base/Pages/BlueprintsPageView.cs +++ b/src/ParkitectNexus.Client.Base/Pages/BlueprintsPageView.cs @@ -15,14 +15,15 @@ using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Presenter; using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; namespace ParkitectNexus.Client.Base.Pages { public class BlueprintsPageView : AssetsPageView { - public BlueprintsPageView(IParkitect parkitect, ILogger log, IPresenter parent) - : base(parkitect, log, AssetType.Blueprint, parent, "Blueprints") + public BlueprintsPageView(IParkitect parkitect, IWebsite website, ILogger log, IPresenter parent) + : base(parkitect, website, log, AssetType.Blueprint, parent, "Blueprints") { } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Client.Base/Pages/ModsPageView.cs b/src/ParkitectNexus.Client.Base/Pages/ModsPageView.cs index be93e72..b770ef3 100644 --- a/src/ParkitectNexus.Client.Base/Pages/ModsPageView.cs +++ b/src/ParkitectNexus.Client.Base/Pages/ModsPageView.cs @@ -12,6 +12,7 @@ // along with this program. If not, see . using System.Diagnostics; +using System.Linq; using ParkitectNexus.Data.Assets; using ParkitectNexus.Data.Assets.Modding; using ParkitectNexus.Data.Game; @@ -26,12 +27,14 @@ namespace ParkitectNexus.Client.Base.Pages { public class ModsPageView : AssetsPageView { + private readonly IParkitect _parkitect; private readonly IQueueableTaskManager _queueableTaskManager; private readonly IWebsite _website; public ModsPageView(IParkitect parkitect, ILogger log, IPresenter parent, IQueueableTaskManager queueableTaskManager, - IWebsite website) : base(parkitect, log, AssetType.Mod, parent, "Mods") + IWebsite website) : base(parkitect, website, log, AssetType.Mod, parent, "Mods") { + _parkitect = parkitect; _queueableTaskManager = queueableTaskManager; _website = website; } @@ -114,4 +117,4 @@ protected override void PopulateViewBoxWithButtons(VBox vBox, IAsset asset) #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Client.Base/Pages/SavegamesPageView.cs b/src/ParkitectNexus.Client.Base/Pages/SavegamesPageView.cs index 7673112..59b4348 100644 --- a/src/ParkitectNexus.Client.Base/Pages/SavegamesPageView.cs +++ b/src/ParkitectNexus.Client.Base/Pages/SavegamesPageView.cs @@ -15,13 +15,14 @@ using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Presenter; using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; namespace ParkitectNexus.Client.Base.Pages { public class SavegamesPageView : AssetsPageView { - public SavegamesPageView(IParkitect parkitect, ILogger log, IPresenter parent) - : base(parkitect, log, AssetType.Savegame, parent, "Savegames") + public SavegamesPageView(IParkitect parkitect, IWebsite website, ILogger log, IPresenter parent) + : base(parkitect, website, log, AssetType.Savegame, parent, "Savegames") { } } diff --git a/src/ParkitectNexus.Client.Base/Pages/TasksPageView.cs b/src/ParkitectNexus.Client.Base/Pages/TasksPageView.cs index 5ce4ee9..200485a 100644 --- a/src/ParkitectNexus.Client.Base/Pages/TasksPageView.cs +++ b/src/ParkitectNexus.Client.Base/Pages/TasksPageView.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; using ParkitectNexus.Client.Base.Components; using ParkitectNexus.Client.Base.Main; using ParkitectNexus.Data; @@ -31,26 +30,28 @@ namespace ParkitectNexus.Client.Base.Pages { public class TasksPageView : ScrollView, IPresenter, IPageView { - private Type[] _tasksOfInterest = - { - typeof(CompileModTask), - typeof(InstallAssetTask) - }; + private readonly MainView _mainView; private readonly Label _nothingLabel; private readonly IQueueableTaskManager _taskManager; private readonly VBox _vBox; private string _displayName = "Tasks"; - private readonly MainView _mainView; + + private readonly Type[] _tasksOfInterest = + { + typeof (CompileModTask), + typeof (InstallAssetTask), + typeof (UpdateModTask), + }; + private List _taskViews = new List(); public TasksPageView(IQueueableTaskManager taskManager, IPresenter parent) { - if (!(parent is MainView)) throw new ArgumentException("parent must be MainView", nameof(parent)); - _mainView = (MainView)parent; + _mainView = (MainView) parent; _taskManager = taskManager; diff --git a/src/ParkitectNexus.Client.Base/Properties/AssemblyInfo.cs b/src/ParkitectNexus.Client.Base/Properties/AssemblyInfo.cs index 219e741..cb9a9a8 100644 --- a/src/ParkitectNexus.Client.Base/Properties/AssemblyInfo.cs +++ b/src/ParkitectNexus.Client.Base/Properties/AssemblyInfo.cs @@ -48,4 +48,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.*")] +[assembly: AssemblyVersion("2.1.*")] diff --git a/src/ParkitectNexus.Client.Darwin/Main.cs b/src/ParkitectNexus.Client.Darwin/Main.cs index 8d89527..1878491 100644 --- a/src/ParkitectNexus.Client.Darwin/Main.cs +++ b/src/ParkitectNexus.Client.Darwin/Main.cs @@ -72,9 +72,10 @@ static void Main(string[] args) if(!app.Initialize(ToolkitType.Cocoa)) return; - TmpFixModLoaderUtil.InstallModLoader(ObjectFactory.GetInstance(), ObjectFactory.GetInstance()); MacEngine.App.OpenUrl += (sender, e) => { + ObjectFactory.GetInstance().WriteLine($"Got url {e.Url}"); + if(e.Url.StartsWith("parkitectnexus://")) e.Url = e.Url; else @@ -91,6 +92,8 @@ static void Main(string[] args) app.HandleUrl(url); }; + TmpFixModLoaderUtil.InstallModLoader(ObjectFactory.GetInstance(), ObjectFactory.GetInstance()); + app.Run(); } } diff --git a/src/ParkitectNexus.Client.Darwin/ParkitectNexus.Client.Darwin.csproj b/src/ParkitectNexus.Client.Darwin/ParkitectNexus.Client.Darwin.csproj index ef8e643..c5f2c6b 100644 --- a/src/ParkitectNexus.Client.Darwin/ParkitectNexus.Client.Darwin.csproj +++ b/src/ParkitectNexus.Client.Darwin/ParkitectNexus.Client.Darwin.csproj @@ -10,7 +10,7 @@ Resources ParkitectNexus Client True - 2.0.0 + 2.1.5980.38291 false diff --git a/src/ParkitectNexus.Client.Darwin/Properties/AssemblyInfo.cs b/src/ParkitectNexus.Client.Darwin/Properties/AssemblyInfo.cs index 280133c..ee219ab 100644 --- a/src/ParkitectNexus.Client.Darwin/Properties/AssemblyInfo.cs +++ b/src/ParkitectNexus.Client.Darwin/Properties/AssemblyInfo.cs @@ -8,12 +8,12 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("ParkitectNexus.Client.Base")] +[assembly: AssemblyTitle("ParkitectNexus Client")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ParkitectNexus.Client.Base")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyProduct("ParkitectNexus Client")] +[assembly: AssemblyCopyright("Copyright © ParkitectNexus 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -38,4 +38,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.5969.32190")] \ No newline at end of file +[assembly: AssemblyVersion("2.1.5980.38291")] \ No newline at end of file diff --git a/src/ParkitectNexus.Client.Linux/ParkitectNexus.Client.Linux.csproj b/src/ParkitectNexus.Client.Linux/ParkitectNexus.Client.Linux.csproj index f073134..683dbf8 100644 --- a/src/ParkitectNexus.Client.Linux/ParkitectNexus.Client.Linux.csproj +++ b/src/ParkitectNexus.Client.Linux/ParkitectNexus.Client.Linux.csproj @@ -78,9 +78,10 @@ - - parkitectnexus_logo.png + + + Always - + \ No newline at end of file diff --git a/src/ParkitectNexus.Client.Linux/Program.cs b/src/ParkitectNexus.Client.Linux/Program.cs index 64f7785..720006c 100644 --- a/src/ParkitectNexus.Client.Linux/Program.cs +++ b/src/ParkitectNexus.Client.Linux/Program.cs @@ -30,16 +30,15 @@ namespace ParkitectNexus.Client.Linux { public class Program { - public static ManualResetEvent allDone = new ManualResetEvent(false); - public static App app; - public static bool closed; + public static ManualResetEvent AllDone = new ManualResetEvent(false); + public static App App; + public static bool Closed; [STAThread] public static void Main(string[] args) { - String socketPath = Path.GetTempPath() + "/parkitect_nexus.socket"; - bool isHost = false; + var socketPath = Path.GetTempPath() + "/parkitect_nexus.socket"; var endPoint = new UnixEndPoint(socketPath); var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); @@ -52,14 +51,14 @@ public static void Main(string[] args) try { socket.Connect(endPoint); - using (NetworkStream sr = new NetworkStream(socket)) + using (var sr = new NetworkStream(socket)) { - using (StreamWriter writer = new StreamWriter(sr)) + using (var writer = new StreamWriter(sr)) { writer.WriteLine(args.Length); - for (int x = 0; x < args.Length; x++) + foreach (var t in args) { - writer.WriteLine(args[x]); + writer.WriteLine(t); } } } @@ -85,8 +84,8 @@ public static void Main(string[] args) // Create the form and run its message loop. If arguments were specified, process them within the // form. var presenterFactory = ObjectFactory.GetInstance(); - app = presenterFactory.InstantiatePresenter(); - if (!app.Initialize(ToolkitType.Gtk)) + App = presenterFactory.InstantiatePresenter(); + if (!App.Initialize(ToolkitType.Gtk)) return; @@ -95,7 +94,7 @@ public static void Main(string[] args) ProcessArgs(args); } - app.Run(); + App.Run(); } catch (Exception e) { @@ -108,25 +107,25 @@ public static void Main(string[] args) log?.WriteLine("Application crashed!", LogLevel.Fatal); log?.WriteException(e); } - closed = true; + Closed = true; socket.Close(); } public static void OnAccept(IAsyncResult ar) { //release the listening thread and process the args - allDone.Set(); - if (!closed) + AllDone.Set(); + if (!Closed) { - Socket listener = (Socket) ar.AsyncState; - Socket handler = listener.EndAccept(ar); - using (NetworkStream stream = new NetworkStream(handler)) + var listener = (Socket) ar.AsyncState; + var handler = listener.EndAccept(ar); + using (var stream = new NetworkStream(handler)) { - using (StreamReader reader = new StreamReader(stream)) + using (var reader = new StreamReader(stream)) { - int numberArguments = int.Parse(reader.ReadLine()); - string[] args = new string[numberArguments]; - for (int x = 0; x < numberArguments; x++) + var numberArguments = int.Parse(reader.ReadLine()); + var args = new string[numberArguments]; + for (var x = 0; x < numberArguments; x++) { args[x] = reader.ReadLine(); } @@ -141,11 +140,11 @@ public static void ProcessArgs(string[] args) { var options = new AppCommandLineOptions(); Parser.Default.ParseArguments(args, options); - for (int x = 0; x < args.Length; x++) + foreach (var t in args) { - if (args[x].Contains("parkitectnexus://")) + if (t.Contains("parkitectnexus://")) { - options.Url = args[x]; + options.Url = t; } } @@ -158,41 +157,34 @@ public static void ProcessArgs(string[] args) { NexusUrl url; if (NexusUrl.TryParse(options.Url, out url)) - app.HandleUrl(url); + App.HandleUrl(url); } } } public static bool CreateSocket(Socket s, UnixEndPoint end) { - try + //listen for a connection and then rebind the accept + var listeningThread = new Thread(delegate() { - //listen for a connection and then rebind the accept - var listeningThread = new Thread(delegate() - { - s.Bind(end); - s.Listen(10); + s.Bind(end); + s.Listen(10); - while (s.IsBound) - { - allDone.Reset(); - if (closed) - break; - //bind accept and listen for arguments - s.BeginAccept(OnAccept, s); - //end the thread when nothing is connected - - allDone.WaitOne(); - } - }); - listeningThread.Start(); + while (s.IsBound) + { + AllDone.Reset(); + if (Closed) + break; + //bind accept and listen for arguments + s.BeginAccept(OnAccept, s); + //end the thread when nothing is connected + + AllDone.WaitOne(); + } + }); + listeningThread.Start(); - return true; - } - catch (SocketException e) - { - throw e; - } + return true; } } } \ No newline at end of file diff --git a/src/ParkitectNexus.Client.Linux/parkitectnexus_logo.png b/src/ParkitectNexus.Client.Linux/parkitectnexus_logo.png new file mode 100644 index 0000000..d96c561 Binary files /dev/null and b/src/ParkitectNexus.Client.Linux/parkitectnexus_logo.png differ diff --git a/src/ParkitectNexus.Client.Win32/ParkitectNexusProtocol.cs b/src/ParkitectNexus.Client.Win32/ParkitectNexusProtocol.cs index f7d73ca..909d44c 100644 --- a/src/ParkitectNexus.Client.Win32/ParkitectNexusProtocol.cs +++ b/src/ParkitectNexus.Client.Win32/ParkitectNexusProtocol.cs @@ -44,4 +44,4 @@ public static void Install(ILogger logger) } } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Client.Win32/Program.cs b/src/ParkitectNexus.Client.Win32/Program.cs index 3fcc767..f264f76 100644 --- a/src/ParkitectNexus.Client.Win32/Program.cs +++ b/src/ParkitectNexus.Client.Win32/Program.cs @@ -12,6 +12,7 @@ // along with this program. If not, see . using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -31,13 +32,16 @@ public class Program [STAThread] public static void Main(string[] args) { - // Create a mutex which is held while the app is open. If the app is started and the mutex can't be awaited, - // assume the app is already running and broadcast a WM_GIVEFICUS message. - bool mutexIsNew; - var mutex = new Mutex(false, "com.ParkitectNexus.Client", out mutexIsNew); - // Look and see if this is the only running instance of the client. - if (mutexIsNew) + var procCount = +#if !DEBUG + Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName) + .Count(p => p.MainModule.FileName == Process.GetCurrentProcess().MainModule.FileName); +#else + Process.GetProcesses().Count(p => p.ProcessName.Contains("ParkitectNexus")); +#endif + + if (procCount == 1) { // No matter if the application crashes, we must release the mutex when the app closes. Wrap the app // logic in a try-finally block. @@ -45,36 +49,36 @@ public static void Main(string[] args) try { #endif - // Initialize the structure map container. - var registry = ObjectFactory.ConfigureStructureMap(); - registry.IncludeRegistry(new PresenterRegistry()); - registry.For().Singleton().Use(); - ObjectFactory.SetUpContainer(registry); - + // Initialize the structure map container. + var registry = ObjectFactory.ConfigureStructureMap(); + registry.IncludeRegistry(new PresenterRegistry()); + registry.For().Singleton().Use(); + ObjectFactory.SetUpContainer(registry); - // Create the form and run its message loop. If arguments were specified, process them within the - // form. - var presenterFactory = ObjectFactory.GetInstance(); - var app = presenterFactory.InstantiatePresenter(); - if (!app.Initialize(ToolkitType.Wpf)) - return; - ParkitectNexusProtocol.Install(ObjectFactory.GetInstance()); + // Create the form and run its message loop. If arguments were specified, process them within the + // form. + var presenterFactory = ObjectFactory.GetInstance(); + var app = presenterFactory.InstantiatePresenter(); + if (!app.Initialize(ToolkitType.Wpf)) + return; - if (args.Any()) - { - var options = new AppCommandLineOptions(); - Parser.Default.ParseArguments(args, options); + ParkitectNexusProtocol.Install(ObjectFactory.GetInstance()); - if (options.Url != null) + if (args.Any()) { - NexusUrl url; - if (NexusUrl.TryParse(options.Url, out url)) - app.HandleUrl(url); + var options = new AppCommandLineOptions(); + Parser.Default.ParseArguments(args, options); + + if (options.Url != null) + { + NexusUrl url; + if (NexusUrl.TryParse(options.Url, out url)) + app.HandleUrl(url); + } } - } - app.Run(); + app.Run(); #if !DEBUG } catch (Exception e) @@ -121,8 +125,6 @@ public static void Main(string[] args) } } while (attempts < 5); // Limit to 5 attempts. } - - GC.KeepAlive(mutex); } } } \ No newline at end of file diff --git a/src/ParkitectNexus.Client.Win32/Properties/AssemblyInfo.cs b/src/ParkitectNexus.Client.Win32/Properties/AssemblyInfo.cs index df7286c..a620434 100644 --- a/src/ParkitectNexus.Client.Win32/Properties/AssemblyInfo.cs +++ b/src/ParkitectNexus.Client.Win32/Properties/AssemblyInfo.cs @@ -48,4 +48,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.*")] +[assembly: AssemblyVersion("2.1.*")] diff --git a/src/ParkitectNexus.Data/Assets/AssetUpdatesManager.cs b/src/ParkitectNexus.Data/Assets/AssetUpdatesManager.cs deleted file mode 100644 index 7952928..0000000 --- a/src/ParkitectNexus.Data/Assets/AssetUpdatesManager.cs +++ /dev/null @@ -1,243 +0,0 @@ -// ParkitectNexusClient -// Copyright (C) 2016 ParkitectNexus, Tim Potze -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using ParkitectNexus.Data.Assets.Modding; -using ParkitectNexus.Data.Caching; -using ParkitectNexus.Data.Game; - -namespace ParkitectNexus.Data.Assets -{ - public class AssetUpdatesManager : IAssetUpdatesManager - { - private readonly ICacheManager _cacheManager; - private readonly TimeSpan _checkInterval = TimeSpan.FromHours(1); - private readonly IParkitect _parkitect; - private readonly IRemoteAssetRepository _remoteAssetRepository; - - private bool _isChecking; - private IDictionary _updatesAvailable = new Dictionary(); - - public AssetUpdatesManager(IRemoteAssetRepository remoteAssetRepository, IParkitect parkitect, - ICacheManager cacheManager) - { - _remoteAssetRepository = remoteAssetRepository; - _parkitect = parkitect; - _cacheManager = cacheManager; - } - - public bool HasChecked { get; private set; } - - public event EventHandler UpdateFound - { - add - { - BaseUpdateFound += value; - - if (HasChecked) - { - foreach (var asset in _updatesAvailable.Keys) - value(this, new AssetEventArgs(asset)); - } - } - remove { BaseUpdateFound -= value; } - } - - public bool ShouldCheckForUpdates() - { - var cache = _cacheManager.GetItem("updates_check"); - - if (cache == null || DateTime.Now - cache.CheckedDate > _checkInterval) - return true; - - if (_isChecking) - return false; - - ReadFromCache(); - - return !HasChecked; - } - - public async Task CheckForUpdates() - { - _isChecking = true; - var count = 0; - try - { - foreach (var asset in _parkitect.Assets[AssetType.Mod].OfType()) - { - if (asset?.Repository == null || asset.Information.IsDevelopment) - continue; - - var latestTag = await _remoteAssetRepository.GetLatestModTag(asset); - - if (latestTag != null && asset.Tag != latestTag) - { - _updatesAvailable.Add(asset, latestTag); - count++; - } - } - } - catch (Exception e) - { - Debug.WriteLine(e); - } - - _cacheManager.SetItem("updates_check", AssetUpdatesCache.FromAssetsList(_updatesAvailable)); - - HasChecked = true; - - return count; - } - - public async Task IsUpdateAvailableOnline(IAsset asset) - { - if (asset == null) throw new ArgumentNullException(nameof(asset)); - - switch (asset.Type) - { - case AssetType.Blueprint: - case AssetType.Savegame: - return false; - case AssetType.Mod: - var modAsset = asset as IModAsset; - var latestTag = await _remoteAssetRepository.GetLatestModTag(modAsset); - return latestTag != null && latestTag != modAsset.Tag; - default: - throw new ArgumentException("invalid asset type", nameof(asset)); - } - } - - public bool IsUpdateAvailableInMemory(IAsset asset) - { - if (asset == null) throw new ArgumentNullException(nameof(asset)); - - ReadFromCache(); - - switch (asset.Type) - { - case AssetType.Blueprint: - case AssetType.Savegame: - return false; - case AssetType.Mod: - return HasChecked && _updatesAvailable.ContainsKey(asset); - - default: - throw new ArgumentException("invalid asset type", nameof(asset)); - } - } - - public async Task GetLatestVersionName(IAsset asset) - { - if (asset == null) throw new ArgumentNullException(nameof(asset)); - - ReadFromCache(); - - switch (asset.Type) - { - case AssetType.Blueprint: - case AssetType.Savegame: - // TODO: Implement - return null; - case AssetType.Mod: - var modAsset = asset as IModAsset; - - if (!HasChecked) - return await _remoteAssetRepository.GetLatestModTag(modAsset); - - string tag; - return _updatesAvailable.TryGetValue(asset, out tag) ? tag : modAsset.Tag; - default: - throw new ArgumentException("invalid asset type", nameof(asset)); - } - } - - private event EventHandler BaseUpdateFound; - - private void ReadFromCache() - { - if (HasChecked) - return; - - var cache = _cacheManager.GetItem("updates_check"); - - if (cache == null) - return; - - _updatesAvailable = cache.ToAssetsList(_parkitect); - HasChecked = true; - } - - protected virtual void OnUpdateFound(AssetEventArgs e) - { - BaseUpdateFound?.Invoke(this, e); - } - - private class AssetUpdatesCache - { - public IList Updates { get; set; } - - public DateTime CheckedDate { get; set; } - - public IDictionary ToAssetsList(IParkitect parkitect) - { - if (Updates == null) - return null; - - var result = new Dictionary(); - foreach (var keyValue in Updates) - { - var asset = parkitect.Assets[keyValue.Type].FirstOrDefault(a => a.Id == keyValue.Id); - - if (asset != null) - result[asset] = keyValue.Tag; - } - - return result; - } - - public static AssetUpdatesCache FromAssetsList(IDictionary updatesAvailable) - { - if (updatesAvailable == null) throw new ArgumentNullException(nameof(updatesAvailable)); - var result = new AssetUpdatesCache - { - CheckedDate = DateTime.Now, - Updates = new List() - }; - - foreach (var keyValue in updatesAvailable) - result.Updates.Add(new AssetCachedUpdateInfo - { - Type = keyValue.Key.Type, - Id = keyValue.Key.Id, - Tag = keyValue.Value - }); - - return result; - } - } - - private class AssetCachedUpdateInfo - { - public AssetType Type { get; set; } - - public string Id { get; set; } - - public string Tag { get; set; } - } - } -} \ No newline at end of file diff --git a/src/ParkitectNexus.Data/Assets/LocalAssetRepository.cs b/src/ParkitectNexus.Data/Assets/LocalAssetRepository.cs index 0f46756..4033f4c 100644 --- a/src/ParkitectNexus.Data/Assets/LocalAssetRepository.cs +++ b/src/ParkitectNexus.Data/Assets/LocalAssetRepository.cs @@ -24,6 +24,7 @@ using ParkitectNexus.Data.Assets.Modding; using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; namespace ParkitectNexus.Data.Assets { @@ -33,11 +34,13 @@ public class LocalAssetRepository : ILocalAssetRepository private readonly IAssetMetadataStorage _assetMetadataStorage; private readonly ILogger _log; private readonly IParkitect _parkitect; + private readonly IWebsite _website; - public LocalAssetRepository(IParkitect parkitect, ILogger log, IAssetMetadataStorage assetMetadataStorage, + public LocalAssetRepository(IParkitect parkitect, IWebsite website, ILogger log, IAssetMetadataStorage assetMetadataStorage, IAssetCachedDataStorage assetCachedDataStorage) { _parkitect = parkitect; + _website = website; _log = log; _assetMetadataStorage = assetMetadataStorage; _assetCachedDataStorage = assetCachedDataStorage; @@ -248,9 +251,9 @@ public async Task StoreAsset(IDownloadedAsset downloadedAsset) // InstalledVersion = downloadedAsset.ApiAsset.UpdatedAt, Tag = downloadedAsset.Info.Tag, Repository = downloadedAsset.Info.Repository - };// TODO: Re-add installed version + }; // TODO: Re-add installed version - var installationPath = Path.Combine(_parkitect.Paths.GetAssetPath(AssetType.Mod), + var installationPath = Path.Combine(_parkitect.Paths.GetAssetPath(AssetType.Mod), downloadedAsset.Info.Repository.Replace('/', '@')); // TODO: Should actually try and look if the mod has been updated since and delete the whole folder. @@ -282,6 +285,9 @@ public async Task StoreAsset(IDownloadedAsset downloadedAsset) var partDir = entry.FullName.Substring(mainFolder.Length); var path = Path.Combine(installationPath, partDir); + if (partDir == "moddata.cache" || partDir == "modinfo.meta") + continue; + if (string.IsNullOrEmpty(entry.Name)) { _log.WriteLine($"Creating directory '{path}'."); @@ -296,6 +302,8 @@ public async Task StoreAsset(IDownloadedAsset downloadedAsset) } } + _log.WriteLine("Register installation to API."); + _website.API.RegisterDownload(downloadedAsset.ApiAsset.Id); _assetMetadataStorage.StoreMetadata(downloadedAsset.ApiAsset.Type, installationPath, meta); var cachedData = await _assetCachedDataStorage.GetData(downloadedAsset.ApiAsset.Type, meta, @@ -308,9 +316,6 @@ public async Task StoreAsset(IDownloadedAsset downloadedAsset) cachedData as AssetWithImageCachedData, modInformation); OnAssetAdded(new AssetEventArgs(createdAsset)); - // Save and compile the mod. - // TODO compile mod. - return createdAsset; } } diff --git a/src/ParkitectNexus.Data/Assets/Modding/ModCompiler.cs b/src/ParkitectNexus.Data/Assets/Modding/ModCompiler.cs index 8a56327..9c88ef2 100644 --- a/src/ParkitectNexus.Data/Assets/Modding/ModCompiler.cs +++ b/src/ParkitectNexus.Data/Assets/Modding/ModCompiler.cs @@ -56,11 +56,12 @@ public async Task Compile(IModAsset mod) { return await Task.Run(() => { - var dependencies = mod.Information.Dependencies?.Select(repository => + var dependencies = mod.Information.Dependencies?.Where(repository => repository != null) + .Select(repository => { var dep = _parkitect.Assets[AssetType.Mod].OfType() - .FirstOrDefault(m => m.Repository == repository); + .FirstOrDefault(m => m.Repository?.ToLower() == repository.ToLower()); if (dep == null) throw new Exception($"Dependency {repository} was not installed."); @@ -292,4 +293,4 @@ private string ResolveAssembly(IModAsset[] dependencies, string assemblyName) //throw new Exception($"Failed to resolve referenced assembly '{assemblyName}'"); } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Assets/RemoteAssetRepository.cs b/src/ParkitectNexus.Data/Assets/RemoteAssetRepository.cs index adaeafd..a342d27 100644 --- a/src/ParkitectNexus.Data/Assets/RemoteAssetRepository.cs +++ b/src/ParkitectNexus.Data/Assets/RemoteAssetRepository.cs @@ -16,10 +16,10 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Octokit; using ParkitectNexus.Data.Assets.Modding; using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; using ParkitectNexus.Data.Web.API; using ParkitectNexus.Data.Web.Client; @@ -30,16 +30,16 @@ namespace ParkitectNexus.Data.Assets /// public class RemoteAssetRepository : IRemoteAssetRepository { - private readonly IGitHubClient _gitHubClient; private readonly ILogger _logger; + private readonly IWebsite _website; private readonly INexusWebClientFactory _webClientFactory; - public RemoteAssetRepository(ILogger logger, - INexusWebClientFactory webClientFactory, IGitHubClient gitHubClient) + public RemoteAssetRepository(ILogger logger, IWebsite website, + INexusWebClientFactory webClientFactory) { _logger = logger; + _website = website; _webClientFactory = webClientFactory; - _gitHubClient = gitHubClient; } /// @@ -53,7 +53,7 @@ public async Task DownloadAsset(ApiAsset asset) { if (asset == null) throw new ArgumentNullException(nameof(asset)); - var downloadInfo = await ResolveDownloadInfo(asset); + var downloadInfo = ResolveDownloadInfo(asset); using (var webClient = _webClientFactory.CreateWebClient(asset.Type != AssetType.Mod)) { @@ -113,11 +113,10 @@ public async Task GetLatestModTag(IModAsset asset) if (asset == null) throw new ArgumentNullException(nameof(asset)); - var repositoryTag = await GetLatestModTag(asset.Repository); - return repositoryTag.Name; + return (await _website.API.GetAsset(asset.Id)).Resource.Data.Version; } - private async Task ResolveDownloadInfo(ApiAsset asset) + private DownloadInfo ResolveDownloadInfo(ApiAsset asset) { if (asset == null) throw new ArgumentNullException(nameof(asset)); @@ -131,30 +130,12 @@ private async Task ResolveDownloadInfo(ApiAsset asset) var repoUrlParts = repoUrl.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var repo = $"{repoUrlParts[repoUrlParts.Length - 2]}/{repoUrlParts[repoUrlParts.Length - 1]}"; - var tag = await GetLatestModTag(repo); - if (tag == null) + if (asset.Resource.Data.ZipBall == null) throw new Exception("mod has not yet been released(tagged)"); - return new DownloadInfo(tag.ZipballUrl, repo, tag.Name); + return new DownloadInfo(asset.Resource.Data.ZipBall, repo, asset.Resource.Data.Version); default: throw new Exception("unsupported mod type"); } } - - private async Task GetLatestModTag(string repository) - { - if (repository == null) throw new ArgumentNullException(nameof(repository)); - var p = repository.Split('/'); - - if (p.Length != 2 || string.IsNullOrWhiteSpace(p[0]) || string.IsNullOrWhiteSpace(p[1])) - throw new ArgumentException(nameof(repository)); - - _logger.WriteLine($"Getting latest mod tag for '{repository}'."); - - var release = (await _gitHubClient.Repository.Release.GetAll(p[0], p[1])).FirstOrDefault(r => !r.Prerelease); - - return release == null - ? null - : (await _gitHubClient.Repository.GetAllTags(p[0], p[1])).FirstOrDefault(t => t.Name == release.TagName); - } } } diff --git a/src/ParkitectNexus.Data/ObjectFactory.cs b/src/ParkitectNexus.Data/ObjectFactory.cs index 8e15144..c8c55ef 100644 --- a/src/ParkitectNexus.Data/ObjectFactory.cs +++ b/src/ParkitectNexus.Data/ObjectFactory.cs @@ -62,7 +62,6 @@ public static Registry ConfigureStructureMap() registry.For().Use(); registry.For().Use(); registry.For().Use(); - registry.For().Singleton().Use(); registry.For().Use(); return registry; @@ -94,4 +93,4 @@ public static void SetUpContainer(Registry registry) Container = new Container(registry); } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/ParkitectNexus.Data.csproj b/src/ParkitectNexus.Data/ParkitectNexus.Data.csproj index 9d2356d..4806ead 100644 --- a/src/ParkitectNexus.Data/ParkitectNexus.Data.csproj +++ b/src/ParkitectNexus.Data/ParkitectNexus.Data.csproj @@ -75,18 +75,6 @@ 4 - - ..\..\packages\ParkitectNexus.AssetMagic.2.0.5969.20896\lib\net45\ParkitectNexus.AssetMagic.dll - True - - - ..\..\packages\structuremap.4.2.0.402\lib\net40\StructureMap.dll - True - - - ..\..\packages\structuremap.4.2.0.402\lib\net40\StructureMap.Net4.dll - True - @@ -101,19 +89,23 @@ ..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll - - ..\..\packages\Octokit.0.19.0\lib\net45\Octokit.dll + + ..\..\packages\ParkitectNexus.AssetMagic.2.0.5969.20896\lib\net45\ParkitectNexus.AssetMagic.dll + + + ..\..\packages\structuremap.4.2.0.402\lib\net40\StructureMap.dll + + + ..\..\packages\structuremap.4.2.0.402\lib\net40\StructureMap.Net4.dll - - @@ -169,8 +161,9 @@ + + - @@ -188,12 +181,14 @@ + + - + diff --git a/src/ParkitectNexus.Data/Properties/AssemblyInfo.cs b/src/ParkitectNexus.Data/Properties/AssemblyInfo.cs index 1c985c3..d67aa1e 100644 --- a/src/ParkitectNexus.Data/Properties/AssemblyInfo.cs +++ b/src/ParkitectNexus.Data/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.*")] +[assembly: AssemblyVersion("2.1.*")] [assembly: NeutralResourcesLanguage("en")] diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/CheckForUpdatesTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/CheckForUpdatesTask.cs index a2590ec..7d329b8 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/CheckForUpdatesTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/CheckForUpdatesTask.cs @@ -11,31 +11,84 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using ParkitectNexus.Data.Assets; +using ParkitectNexus.Data.Assets.Modding; +using ParkitectNexus.Data.Game; +using ParkitectNexus.Data.Utilities; +using ParkitectNexus.Data.Web; +using ParkitectNexus.Data.Web.API; +using ParkitectNexus.Data.Web.Models; namespace ParkitectNexus.Data.Tasks.Prefab { public class CheckForUpdatesTask : QueueableTask { - private readonly IAssetUpdatesManager _assetUpdatesManager; + private readonly IParkitect _parkitect; + private readonly IWebsite _website; + private readonly ILogger _log; + private readonly IQueueableTaskManager _queueableTaskManager; - public CheckForUpdatesTask(IAssetUpdatesManager assetUpdatesManager) : base("Updates check") + public CheckForUpdatesTask(IParkitect parkitect, IWebsite website, ILogger log, IQueueableTaskManager queueableTaskManager) : base("Check for updates") { - _assetUpdatesManager = assetUpdatesManager; + _parkitect = parkitect; + _website = website; + _log = log; + _queueableTaskManager = queueableTaskManager; + StatusDescription = "Check for updates."; } #region Overrides of QueueableTask public override async Task Run(CancellationToken token) { - UpdateStatus("Checking for updates...", 25, TaskStatus.Running); - var count = await _assetUpdatesManager.CheckForUpdates(); + var count = 0; + UpdateStatus("Gathering information...", 0, TaskStatus.Running); + + foreach (var required in await _website.API.GetRequiredModIdentifiers()) + { + if (_parkitect.Assets[AssetType.Mod].All(m => m.Id != required)) + { + // Create install task for the dependency. + var installDependencyTask = ObjectFactory.GetInstance(); + installDependencyTask.Data = new InstallUrlAction(required); + + // Insert the install task in the queueable task manager. + _queueableTaskManager.Add(installDependencyTask); + } + } + + var mods = _parkitect.Assets[AssetType.Mod].OfType().ToArray(); + + for (var index = 0; index < mods.Length; index++) + { + var mod = mods[index]; + if (mod.Information.IsDevelopment || mod.Id == null) + continue; + + UpdateStatus($"{index + 1}/{mods.Length}: Checking mod: {mod.Name}...", (int)(index * (100.0f/mods.Length)), TaskStatus.Running); + + try + { + var info = await _website.API.GetAsset(mod.Id); + if (mod.Tag != info.Resource.Data.Version) + { + _queueableTaskManager.With(mod).Add(); + count++; + } + } + catch (Exception e) + { + _log.WriteLine("Failed to gather mod info: " + e.Message); + } + } UpdateStatus($"Found {count} available updates.", 100, TaskStatus.Finished); } #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/CompileModTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/CompileModTask.cs index ddcede0..fd7c2e5 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/CompileModTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/CompileModTask.cs @@ -31,6 +31,8 @@ public CompileModTask(IModAsset mod, IModCompiler modCompiler, IModLoadOrderBuil _mod = mod; _modCompiler = modCompiler; _modLoadOrderBuilder = modLoadOrderBuilder; + + StatusDescription = $"Compile mod {mod.Name}."; } #region Overrides of QueueableTask @@ -63,4 +65,4 @@ public override async Task Run(CancellationToken token) #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/InstallAssetTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/InstallAssetTask.cs index 6655120..79658a6 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/InstallAssetTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/InstallAssetTask.cs @@ -39,6 +39,8 @@ public InstallAssetTask(IParkitect parkitect, IWebsite website, IRemoteAssetRepo _website = website; _remoteAssetRepository = remoteAssetRepository; _queueableTaskManager = queueableTaskManager; + + StatusDescription = "Install an asset."; } #region Overrides of QueueableTask @@ -60,6 +62,12 @@ public override async Task Run(CancellationToken token) var assetData = await _website.API.GetAsset(data.Id); + if (assetData.Type == AssetType.Mod && assetData.Resource.Data.Version == null) + { + UpdateStatus($"The '{assetData.Name}' mod has not yet been released! Ask the author to release it.", 100, TaskStatus.Canceled); + return; + } + // Download the asset trough the RemoveAssetRepository. UpdateStatus($"Installing {assetData.Type.ToString().ToLower()} '{assetData.Name}'...", 15, TaskStatus.Running); diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/LaunchGameTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/LaunchGameTask.cs index 1629d12..78cae91 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/LaunchGameTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/LaunchGameTask.cs @@ -24,6 +24,8 @@ public class LaunchGameTask : QueueableTask public LaunchGameTask(IParkitect parkitect) : base("Launch parkitect") { _parkitect = parkitect; + + StatusDescription = "Launch the game."; } #region Overrides of QueueableTask @@ -39,4 +41,4 @@ public override Task Run(CancellationToken token) #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/TestWaitQueueableTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/TestWaitQueueableTask.cs index fa46a4a..4f9628f 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/TestWaitQueueableTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/TestWaitQueueableTask.cs @@ -21,6 +21,7 @@ public class TestWaitQueueableTask : QueueableTask { public TestWaitQueueableTask(int number) : base($"Wait task for testing #{number}") { + StatusDescription = "A test which does nothing."; } #region Overrides of QueueableTask @@ -41,4 +42,4 @@ public override async Task Run(CancellationToken token) #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/UpdateModTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/UpdateModTask.cs index e7d93f8..c37b27f 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/UpdateModTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/UpdateModTask.cs @@ -17,25 +17,28 @@ using ParkitectNexus.Data.Assets; using ParkitectNexus.Data.Assets.Modding; using ParkitectNexus.Data.Game; +using ParkitectNexus.Data.Web; +using ParkitectNexus.Data.Web.API; using ParkitectNexus.Data.Web.Models; namespace ParkitectNexus.Data.Tasks.Prefab { public class UpdateModTask : QueueableTask { - private readonly IAssetUpdatesManager _assetUpdatesManager; private readonly IModAsset _mod; private readonly IParkitect _parkitect; private readonly IQueueableTaskManager _queueableTaskManager; + private readonly IWebsite _website; - public UpdateModTask(IModAsset mod, IParkitect parkitect, IAssetUpdatesManager assetUpdatesManager, - IQueueableTaskManager queueableTaskManager) : base($"Update mod {mod?.Name}") + public UpdateModTask(IModAsset mod, IParkitect parkitect, IQueueableTaskManager queueableTaskManager, IWebsite website) : base($"Update mod {mod?.Name}") { if (mod == null) throw new ArgumentNullException(nameof(mod)); _mod = mod; _parkitect = parkitect; - _assetUpdatesManager = assetUpdatesManager; _queueableTaskManager = queueableTaskManager; + _website = website; + + StatusDescription = $"Update mod {mod.Name}"; } #region Overrides of QueueableTask @@ -44,9 +47,8 @@ public override async Task Run(CancellationToken token) { UpdateStatus("Checking latest version...", 25, TaskStatus.Running); - var updateAvailable = await _assetUpdatesManager.IsUpdateAvailableOnline(_mod); - - if (!updateAvailable) + var info = await _website.API.GetAsset(_mod.Id); + if (_mod.Tag == info.Resource.Data.Version) { UpdateStatus("No update available.", 100, TaskStatus.Finished); return; @@ -60,9 +62,9 @@ public override async Task Run(CancellationToken token) task.Data = new InstallUrlAction(_mod.Id); _queueableTaskManager.Add(task); - UpdateStatus("Finished checking for updates.", 100, TaskStatus.Finished); + UpdateStatus("Started installation task.", 100, TaskStatus.Finished); } #endregion } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/Prefab/UrlQueueableTask.cs b/src/ParkitectNexus.Data/Tasks/Prefab/UrlQueueableTask.cs index bffff6b..4524ff5 100644 --- a/src/ParkitectNexus.Data/Tasks/Prefab/UrlQueueableTask.cs +++ b/src/ParkitectNexus.Data/Tasks/Prefab/UrlQueueableTask.cs @@ -23,4 +23,4 @@ protected UrlQueueableTask(string name) : base(name) public IUrlAction Data { get; set; } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Tasks/QueueableTaskManager.cs b/src/ParkitectNexus.Data/Tasks/QueueableTaskManager.cs index 424a581..8572ae3 100644 --- a/src/ParkitectNexus.Data/Tasks/QueueableTaskManager.cs +++ b/src/ParkitectNexus.Data/Tasks/QueueableTaskManager.cs @@ -200,6 +200,7 @@ private async void RunNext() _currentTask.StatusDescription = "Task finished in an unkown error."; } } + } catch (Exception e) { @@ -261,4 +262,4 @@ protected virtual void OnTaskFinished(QueueableTaskEventArgs e) TaskFinished?.Invoke(this, e); } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Updating/UpdateManager.cs b/src/ParkitectNexus.Data/Updating/UpdateManager.cs index 19ae0d8..e8324d7 100644 --- a/src/ParkitectNexus.Data/Updating/UpdateManager.cs +++ b/src/ParkitectNexus.Data/Updating/UpdateManager.cs @@ -72,9 +72,17 @@ public UpdateInfo CheckForUpdates() } catch(Exception e) { - _log.WriteLine(GetUpdateVersionUrl()); - _log.WriteLine("Failed to check for updates"); - _log.WriteException(e); + //TODO: hotfix to avoid the application from crashing + try{ + _log.WriteLine(GetUpdateVersionUrl()); + _log.WriteLine("Failed to check for updates"); + _log.WriteException(e); + } + catch(Exception ex) + { + _log.WriteException(ex); + + } } return null; @@ -150,9 +158,9 @@ protected virtual string GetUpdateVersionUrl() switch (OperatingSystem.Detect()) { case SupportedOperatingSystem.Windows: - return _website.ResolveUrl("update.json"); + return _website.ResolveUrl("update.json", "client"); case SupportedOperatingSystem.MacOSX: - return _website.ResolveUrl("update-osx.json"); + return _website.ResolveUrl("update-osx.json", "client"); case SupportedOperatingSystem.Linux: throw new NotImplementedException(); default: @@ -168,4 +176,4 @@ private static string RandomString(int length) return string.Concat(Enumerable.Range(0, length).Select(n => eligable[random.Next(eligable.Length)])); } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Web/API/ApiAsset.cs b/src/ParkitectNexus.Data/Web/API/ApiAsset.cs index 2814421..7d37bbe 100644 --- a/src/ParkitectNexus.Data/Web/API/ApiAsset.cs +++ b/src/ParkitectNexus.Data/Web/API/ApiAsset.cs @@ -19,9 +19,9 @@ namespace ParkitectNexus.Data.Web.API /// /// Represents an API asset. /// - public class ApiAsset : IApiResource + public class ApiAsset : IApiObject { - #region Implementation of IApiResource + #region Implementation of IApiObject /// /// Gets or sets the identifier of this resource. @@ -55,12 +55,4 @@ public class ApiAsset : IApiResource [JsonIgnore] public AssetType Type => AssetTypeUtil.Parse(TypeString); } - - public class ApiResourceSource - { - [JsonProperty("source")] - public string Source { get; set; } - [JsonProperty("url")] - public string Url { get; set; } - } } diff --git a/src/ParkitectNexus.Data/Web/API/ApiImage.cs b/src/ParkitectNexus.Data/Web/API/ApiImage.cs index c750542..1a2cdee 100644 --- a/src/ParkitectNexus.Data/Web/API/ApiImage.cs +++ b/src/ParkitectNexus.Data/Web/API/ApiImage.cs @@ -13,15 +13,13 @@ using System; using System.Drawing; -using System.IO; using System.Threading.Tasks; -using ParkitectNexus.Data.Game; using ParkitectNexus.Data.Utilities; using ParkitectNexus.Data.Web.Client; namespace ParkitectNexus.Data.Web.API { - public class ApiImage : ApiResourceSource + public class ApiImage : ApiResource { public async Task Get() { @@ -33,8 +31,8 @@ public async Task Get() using (var webclient = ObjectFactory.GetInstance().CreateWebClient()) { using (var stream = await webclient.OpenReadTaskAsync(Url)) - using (var bmp = new Bitmap(stream)) - return bmp.Clone() as Bitmap; + using (var bmp = new Bitmap(stream)) + return bmp.Clone() as Bitmap; } } catch (Exception e) @@ -48,4 +46,4 @@ public async Task Get() } } } -} +} \ No newline at end of file diff --git a/src/ParkitectNexus.Data/Web/API/ApiResource.cs b/src/ParkitectNexus.Data/Web/API/ApiResource.cs new file mode 100644 index 0000000..75454ad --- /dev/null +++ b/src/ParkitectNexus.Data/Web/API/ApiResource.cs @@ -0,0 +1,26 @@ +// ParkitectNexusClient +// Copyright (C) 2016 ParkitectNexus, Tim Potze +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using Newtonsoft.Json; + +namespace ParkitectNexus.Data.Web.API +{ + public class ApiResource + { + [JsonProperty("source")] + public string Source { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + } +} diff --git a/src/ParkitectNexus.Data/Assets/IAssetUpdatesManager.cs b/src/ParkitectNexus.Data/Web/API/ApiResourceSource.cs similarity index 61% rename from src/ParkitectNexus.Data/Assets/IAssetUpdatesManager.cs rename to src/ParkitectNexus.Data/Web/API/ApiResourceSource.cs index 3311561..b08bc3b 100644 --- a/src/ParkitectNexus.Data/Assets/IAssetUpdatesManager.cs +++ b/src/ParkitectNexus.Data/Web/API/ApiResourceSource.cs @@ -12,20 +12,19 @@ // along with this program. If not, see . using System; -using System.Threading.Tasks; +using Newtonsoft.Json; -namespace ParkitectNexus.Data.Assets +namespace ParkitectNexus.Data.Web.API { - public interface IAssetUpdatesManager + public class ApiResourceSource : ApiResource { - bool HasChecked { get; } + [JsonProperty("version")] + public string Version { get; set; } - event EventHandler UpdateFound; +// [JsonProperty("release_date")] +// public DateTime ReleaseDate { get; set; } - Task CheckForUpdates(); - Task IsUpdateAvailableOnline(IAsset asset); - bool IsUpdateAvailableInMemory(IAsset asset); - Task GetLatestVersionName(IAsset asset); - bool ShouldCheckForUpdates(); + [JsonProperty("zipball")] + public string ZipBall { get; set; } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Web/API/ApiUser.cs b/src/ParkitectNexus.Data/Web/API/ApiUser.cs index 04ca668..b554939 100644 --- a/src/ParkitectNexus.Data/Web/API/ApiUser.cs +++ b/src/ParkitectNexus.Data/Web/API/ApiUser.cs @@ -15,9 +15,9 @@ namespace ParkitectNexus.Data.Web.API { - public class ApiUser : IApiResource + public class ApiUser : IApiObject { - #region Implementation of IApiResource + #region Implementation of IApiObject /// /// Gets or sets the identifier of this resource. diff --git a/src/ParkitectNexus.Data/Web/API/IApiResource.cs b/src/ParkitectNexus.Data/Web/API/IApiObject.cs similarity index 84% rename from src/ParkitectNexus.Data/Web/API/IApiResource.cs rename to src/ParkitectNexus.Data/Web/API/IApiObject.cs index 4493a6b..4f1620c 100644 --- a/src/ParkitectNexus.Data/Web/API/IApiResource.cs +++ b/src/ParkitectNexus.Data/Web/API/IApiObject.cs @@ -16,12 +16,12 @@ namespace ParkitectNexus.Data.Web.API { /// - /// Contains the properties of a resource. + /// Contains the properties of an object. /// - public interface IApiResource + public interface IApiObject { /// - /// Gets or sets the identifier of this resource. + /// Gets or sets the identifier of this object. /// string Id { get; set; } } diff --git a/src/ParkitectNexus.Data/Web/API/IParkitectNexusAPI.cs b/src/ParkitectNexus.Data/Web/API/IParkitectNexusAPI.cs index 35ff3c3..455f15b 100644 --- a/src/ParkitectNexus.Data/Web/API/IParkitectNexusAPI.cs +++ b/src/ParkitectNexus.Data/Web/API/IParkitectNexusAPI.cs @@ -40,5 +40,17 @@ public interface IParkitectNexusAPI /// The authentication key. /// The user information. Task GetUserInfo(string authKey); + + /// + /// Gets the identifiers of the required mods. + /// + /// The identifiers of the required mods. + Task GetRequiredModIdentifiers(); + + /// + /// Registers a download for the asset with the specified identifier. + /// + /// The identifier. + void RegisterDownload(string id); } } diff --git a/src/ParkitectNexus.Data/Web/API/ParkitectNexusAPI.cs b/src/ParkitectNexus.Data/Web/API/ParkitectNexusAPI.cs index b5209a4..d46fbc0 100644 --- a/src/ParkitectNexus.Data/Web/API/ParkitectNexusAPI.cs +++ b/src/ParkitectNexus.Data/Web/API/ParkitectNexusAPI.cs @@ -76,6 +76,31 @@ public async Task GetAsset(string id) } } + /// + /// Gets the identifiers of the required mods. + /// + /// The identifiers of the required mods. + public async Task GetRequiredModIdentifiers() + { + using (var webClient = _webClientFactory.CreateWebClient()) + using (var stream = await webClient.OpenReadTaskAsync(_website.ResolveUrl("api/assets/required", "client"))) + using (var streamReader = new StreamReader(stream)) + { + var info = JsonConvert.DeserializeObject>(await streamReader.ReadToEndAsync()); + return info.Data; + } + } + + /// + /// Registers a download for the asset with the specified identifier. + /// + /// The identifier. + public async void RegisterDownload(string id) + { + using (var webClient = _webClientFactory.CreateWebClient()) + await webClient.OpenReadTaskAsync(_website.ResolveUrl("api/downloads/add/asset/" + id, "client")); + } + /// /// Gets the subscriptions of the authenticated user. /// diff --git a/src/ParkitectNexus.Data/Web/WebRegistry.cs b/src/ParkitectNexus.Data/Web/WebRegistry.cs index 9a085e4..0b73e07 100644 --- a/src/ParkitectNexus.Data/Web/WebRegistry.cs +++ b/src/ParkitectNexus.Data/Web/WebRegistry.cs @@ -11,7 +11,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -using Octokit; using ParkitectNexus.Data.Assets; using ParkitectNexus.Data.Web.API; using ParkitectNexus.Data.Web.Client; @@ -29,9 +28,7 @@ public WebRegistry() For().Use(); For().Use(); - For().Use(() => new GitHubClient(new ProductHeaderValue("parkitect-nexus-client"))); - For().Use(); } } -} \ No newline at end of file +} diff --git a/src/ParkitectNexus.Data/Web/Website.cs b/src/ParkitectNexus.Data/Web/Website.cs index 3f2e98d..c0d78f5 100644 --- a/src/ParkitectNexus.Data/Web/Website.cs +++ b/src/ParkitectNexus.Data/Web/Website.cs @@ -12,6 +12,7 @@ // along with this program. If not, see . using System.Diagnostics; +using ParkitectNexus.Data.Utilities; using ParkitectNexus.Data.Web.API; namespace ParkitectNexus.Data.Web @@ -21,8 +22,9 @@ namespace ParkitectNexus.Data.Web /// public class Website : IWebsite { + private readonly ILogger _log; #if DEBUG - private const string WebsiteUrl = "http://{0}staging.parkitectnexus.com/{1}"; + private const string WebsiteUrl = "http://{0}dev.parkitectnexus.com/{1}"; #else private const string WebsiteUrl = "http://{0}parkitectnexus.com/{1}"; #endif @@ -30,8 +32,9 @@ public class Website : IWebsite /// /// Initializes a new instance of the class. /// - public Website() + public Website(ILogger log) { + _log = log; API = ObjectFactory.With(this).GetInstance(); } @@ -70,7 +73,15 @@ public void Launch(string path, string subdomain) /// The URL. public string ResolveUrl(string path, string subdomain) { - return string.Format(WebsiteUrl, string.IsNullOrEmpty(subdomain) ? string.Empty : subdomain + ".", path); +#if DEBUG + if (subdomain == "client") + subdomain = null; +#endif + + var url = string.Format(WebsiteUrl, string.IsNullOrEmpty(subdomain) ? string.Empty : subdomain + ".", path); + + _log.WriteLine($"Resolved URL: {url}"); + return url; } /// diff --git a/src/ParkitectNexus.Data/packages.config b/src/ParkitectNexus.Data/packages.config index 6184f99..f2282d0 100644 --- a/src/ParkitectNexus.Data/packages.config +++ b/src/ParkitectNexus.Data/packages.config @@ -1,7 +1,6 @@  - \ No newline at end of file diff --git a/src/ParkitectNexus.Mod.ModLoader/Properties/AssemblyInfo.cs b/src/ParkitectNexus.Mod.ModLoader/Properties/AssemblyInfo.cs index 42cfaf3..a400486 100644 --- a/src/ParkitectNexus.Mod.ModLoader/Properties/AssemblyInfo.cs +++ b/src/ParkitectNexus.Mod.ModLoader/Properties/AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following +// General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. @@ -27,8 +27,8 @@ [assembly: AssemblyTrademark("ParkitectNexus")] [assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] @@ -40,13 +40,12 @@ // Version information for an assembly consists of the following four values: // // Major Version -// Minor Version +// Minor Version // Build Number // Revision // -// You can specify all the values or you can default the Build and Revision Numbers +// You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.1.0")] -[assembly: AssemblyFileVersion("1.3.1.0")] \ No newline at end of file +[assembly: AssemblyVersion("2.1.*")] diff --git a/src/Setup/Program.cs b/src/Setup/Program.cs index 9213a1b..98577c2 100644 --- a/src/Setup/Program.cs +++ b/src/Setup/Program.cs @@ -47,7 +47,6 @@ private static void Main() new File(AppExecutable), new File(@"CommandLine.dll"), new File(@"Newtonsoft.Json.dll"), - new File(@"Octokit.dll"), new File(@"ParkitectNexus.AssetMagic.dll"), new File(@"ParkitectNexus.Client.Base.dll"), new File(@"ParkitectNexus.Data.dll"), @@ -103,4 +102,4 @@ private static void Main() project.BuildMsi(); } } -} \ No newline at end of file +}