Add "Open Folder" functionality to plugins tab, crash-safe plugin updating

This commit is contained in:
John Gross
2017-09-14 21:09:20 -07:00
parent 3fd7b66905
commit a97542e649
6 changed files with 76 additions and 33 deletions

View File

@@ -10,7 +10,7 @@ namespace Torch.API.Plugins
/// <summary>
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
/// </summary>
[Obsolete]
[Obsolete("All plugin meta-information is now defined in the manifest.xml.")]
[AttributeUsage(AttributeTargets.Class)]
public class PluginAttribute : Attribute
{

View File

@@ -12,7 +12,7 @@
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
@@ -27,7 +27,7 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Row="1" Content="Open Folder" Margin="3" DockPanel.Dock="Bottom" IsEnabled="false"/>
<Button Grid.Row="1" Content="Open Folder" Margin="3" DockPanel.Dock="Bottom" Click="OpenFolder_OnClick"/>
</Grid>
<Frame Grid.Column="1" NavigationUIVisibility="Hidden" Content="{Binding SelectedPlugin.Control}"/>
</Grid>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -15,6 +16,8 @@ using System.Windows.Navigation;
using System.Windows.Shapes;
using NLog;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using Torch.Server.ViewModels;
namespace Torch.Server.Views
@@ -24,6 +27,9 @@ namespace Torch.Server.Views
/// </summary>
public partial class PluginsControl : UserControl
{
private ITorchServer _server;
private PluginManager _plugins;
public PluginsControl()
{
InitializeComponent();
@@ -31,8 +37,15 @@ namespace Torch.Server.Views
public void BindServer(ITorchServer server)
{
var pluginManager = new PluginManagerViewModel(server.Plugins);
_server = server;
_plugins = _server.Managers.GetManager<PluginManager>();
var pluginManager = new PluginManagerViewModel(_plugins);
DataContext = pluginManager;
}
private void OpenFolder_OnClick(object sender, RoutedEventArgs e)
{
Process.Start("explorer.exe", _plugins.PluginDir);
}
}
}

View File

@@ -98,10 +98,13 @@ namespace Torch.Managers
var count = 0;
var pluginItems = Directory.EnumerateFiles(PluginDir, "*.zip").Union(Directory.EnumerateDirectories(PluginDir));
Parallel.ForEach(pluginItems, async item =>
{
PluginManifest manifest = null;
try
{
var path = Path.Combine(PluginDir, item);
var isZip = item.EndsWith(".zip", StringComparison.CurrentCultureIgnoreCase);
var manifest = isZip ? GetManifestFromZip(path) : GetManifestFromDirectory(path);
manifest = isZip ? GetManifestFromZip(path) : GetManifestFromDirectory(path);
if (manifest == null)
{
_log.Warn($"Item '{item}' is missing a manifest, skipping update check.");
@@ -118,11 +121,20 @@ namespace Torch.Managers
}
if (latest.Item1 <= currentVersion)
{
_log.Debug($"{manifest.Name} {manifest.Version} is up to date.");
return;
}
_log.Info($"Updating plugin '{manifest.Name}' from {currentVersion} to {latest.Item1}.");
await UpdatePlugin(path, latest.Item2).ConfigureAwait(false);
await UpdatePluginAsync(path, latest.Item2).ConfigureAwait(false);
count++;
}
catch (Exception e)
{
_log.Error($"An error occurred updating the plugin {manifest.Name}.");
_log.Error(e);
}
});
_log.Info($"Updated {count} plugins.");
@@ -155,7 +167,7 @@ namespace Torch.Managers
}
}
private Task UpdatePlugin(string localPath, string downloadUrl)
private Task UpdatePluginAsync(string localPath, string downloadUrl)
{
if (File.Exists(localPath))
File.Delete(localPath);
@@ -238,9 +250,7 @@ namespace Torch.Managers
using (var stream = new StreamReader(entry.Open()))
{
var ser = new XmlSerializer(typeof(PluginManifest));
var manifest = (PluginManifest)ser.Deserialize(stream);
return manifest;
return PluginManifest.Load(stream);
}
}
}
@@ -266,7 +276,7 @@ namespace Torch.Managers
if (pluginType != null)
{
_log.Error($"The plugin '{manifest.Name}' has multiple implementations of {nameof(ITorchPlugin)}.");
_log.Error($"The plugin '{manifest.Name}' has multiple implementations of {nameof(ITorchPlugin)}, not loading.");
return;
}
@@ -276,7 +286,7 @@ namespace Torch.Managers
if (pluginType == null)
{
_log.Error($"The plugin '{manifest.Name}' does not have an implementation of {nameof(ITorchPlugin)}.");
_log.Error($"The plugin '{manifest.Name}' does not have an implementation of {nameof(ITorchPlugin)}, not loading.");
return;
}
@@ -301,6 +311,7 @@ namespace Torch.Managers
_commandManager.RegisterPluginCommands(plugin);
}
/// <inheritdoc cref="IEnumerable.GetEnumerator"/>
public IEnumerator<ITorchPlugin> GetEnumerator()
{
return Plugins.Values.GetEnumerator();

View File

@@ -10,11 +10,30 @@ namespace Torch
{
public class PluginManifest
{
/// <summary>
/// The display name of the plugin.
/// </summary>
public string Name { get; set; }
/// <summary>
/// A unique identifier for the plugin.
/// </summary>
public Guid Guid { get; set; }
/// <summary>
/// A GitHub repository in the format of Author/Repository to retrieve plugin updates.
/// </summary>
public string Repository { get; set; }
/// <summary>
/// The plugin version. This must include a string in the format of #[.#[.#]] for update checking purposes.
/// </summary>
public string Version { get; set; }
public List<Guid> Dependencies { get; } = new List<Guid>();
/// <summary>
/// A list of dependent plugin repositories. This may be updated to include GUIDs in the future.
/// </summary>
public List<string> Dependencies { get; } = new List<string>();
public void Save(string path)
{

View File

@@ -181,7 +181,7 @@
<Compile Include="Managers\MultiplayerManager.cs" />
<Compile Include="Managers\UpdateManager.cs" />
<Compile Include="Persistent.cs" />
<Compile Include="PluginManifest.cs" />
<Compile Include="Plugins\PluginManifest.cs" />
<Compile Include="Utils\Reflection.cs" />
<Compile Include="Managers\ScriptingManager.cs" />
<Compile Include="Utils\TorchAssemblyResolver.cs" />
@@ -198,7 +198,7 @@
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="ViewModels\PlayerViewModel.cs" />
<Compile Include="ViewModels\ViewModel.cs" />
<Compile Include="Managers\PluginManager.cs" />
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="ViewModels\PluginViewModel.cs" />
<Compile Include="Views\CollectionEditor.xaml.cs">
<DependentUpon>CollectionEditor.xaml</DependentUpon>