source code checkin

This commit is contained in:
Brett Hewitson 2021-01-07 16:23:23 +10:00
parent 5f8fb2c825
commit 7e57b72e35
675 changed files with 168433 additions and 0 deletions

View file

@ -0,0 +1,7 @@
using ServerManagerTool.Plugin.Common.Lib;
using System.Collections.Generic;
namespace ServerManagerTool.Plugin.Common.Delegates
{
public delegate IList<Profile> FetchProfilesDelegate();
}

View file

@ -0,0 +1,15 @@
namespace ServerManagerTool.Plugin.Common
{
public enum AlertType
{
Error,
Shutdown,
ShutdownMessage,
ShutdownReason,
Startup,
Backup,
UpdateResults,
ServerStatusChange,
ModUpdateDetected,
}
}

View file

@ -0,0 +1,47 @@
using System;
using System.Linq;
using System.Windows.Markup;
using System.Windows.Media.Imaging;
namespace ServerManagerTool.Plugin.Common
{
/// <summary>
/// Simple extension for icon, to let you choose icon with specific size.
/// Usage sample:
/// Image Stretch="None" Source="{common:Icon /Controls;component/icons/custom.ico, 16}"
/// Or:
/// Image Source="{common:Icon Source={Binding IconResource}, Size=16}"
/// </summary>
public class IconExtension : MarkupExtension
{
private string _path;
public string Path
{
get
{
return _path;
}
set
{
// Have to make full pack URI from short form, so System.Uri recognizes it.
_path = $"pack://application:,,,{value}";
}
}
public int Size { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var decoder = BitmapDecoder.Create(new Uri(Path), BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand);
var result = decoder.Frames.SingleOrDefault(f => f.Width == Size);
if (result == default(BitmapFrame))
{
result = decoder.Frames.OrderBy(f => f.Width).First();
}
return result;
}
}
}

View file

@ -0,0 +1,13 @@
namespace ServerManagerTool.Plugin.Common
{
public interface IAlertPlugin : IPlugin
{
/// <summary>
/// Handles the alert message passed for the profile.
/// </summary>
/// <param name="alertType">The type of alert message.</param>
/// <param name="profileName">The name of the profile the alert message is associated with.</param>
/// <param name="alertMessage">The message of the alert.</param>
void HandleAlert(AlertType alertType, string profileName, string alertMessage);
}
}

View file

@ -0,0 +1,11 @@
namespace ServerManagerTool.Plugin.Common
{
public interface IBeta
{
bool BetaEnabled
{
get;
set;
}
}
}

View file

@ -0,0 +1,56 @@
using System;
using System.Windows;
namespace ServerManagerTool.Plugin.Common
{
public interface IPlugin
{
/// <summary>
/// Gets a values indicating if the plugin can be used
/// </summary>
bool Enabled
{
get;
}
/// <summary>
/// Gets a value indicating the code of the plugin
/// </summary>
string PluginCode
{
get;
}
/// <summary>
/// Gets a value indicating the name of the plugin
/// </summary>
string PluginName
{
get;
}
/// <summary>
/// Gets a value indicating the version of the plugin
/// </summary>
Version PluginVersion
{
get;
}
/// <summary>
/// Gets a value that indicates if the plugin has a configuration form.
/// </summary>
bool HasConfigForm
{
get;
}
/// <summary>
/// Performs any initialization for the plugin.
/// </summary>
void Initialize();
/// <summary>
/// Opens the configuration form.
/// </summary>
/// <param name="owner">The owner window.</param>
void OpenConfigForm(Window owner);
}
}

View file

@ -0,0 +1,8 @@
namespace ServerManagerTool.Plugin.Common.Lib
{
public class Profile
{
public string ProfileName { get; set; }
public string InstallationFolder { get; set; }
}
}

View file

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<SccProjectName>%24/Development/ServerManagers/Main/Plugin.Common</SccProjectName>
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
<SccAuxPath>https://dev.azure.com/bretthewitson</SccAuxPath>
<SccLocalPath>.</SccLocalPath>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>ServerManagerTool.Plugin.Common</RootNamespace>
<AssemblyName>ServerManager.Plugin.Common</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
using System.Security;
namespace ServerManagerTool.Plugin.Common
{
public class PluginException : Exception
{
public PluginException()
: base()
{
}
public PluginException(string message)
: base(message)
{
}
public PluginException(string message, Exception innerException)
: base(message, innerException)
{
}
[SecuritySafeCritical]
protected PluginException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View file

@ -0,0 +1,305 @@
using ServerManagerTool.Plugin.Common.Delegates;
using ServerManagerTool.Plugin.Common.Lib;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
namespace ServerManagerTool.Plugin.Common
{
public sealed class PluginHelper : IDisposable
{
private const string PLUGINFILE_FOLDER = "Plugins";
private const string PLUGINFILE_EXTENSION = "dll";
private static volatile PluginHelper _instance;
private static readonly object _syncLock = new object();
private readonly Object _syncLockProcessAlert = new Object();
private readonly Object _syncLockFetchProfiles = new Object();
private FetchProfilesDelegate _fetchProfilesCallback;
private bool _disposed;
private PluginHelper()
{
BetaEnabled = false;
Plugins = new ObservableCollection<PluginItem>();
}
public static PluginHelper Instance
{
get
{
if (_instance != null)
return _instance;
lock(_syncLock)
{
if (_instance == null)
_instance = new PluginHelper();
}
return _instance;
}
}
public static string PluginFolder
{
get
{
var folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Environment.CurrentDirectory);
return Path.Combine(folder, PLUGINFILE_FOLDER);
}
}
internal bool BetaEnabled
{
get;
set;
}
public ObservableCollection<PluginItem> Plugins
{
get;
private set;
}
internal void AddPlugin(string folder, string pluginFile)
{
if (!CheckPluginFile(pluginFile))
throw new PluginException("The selected file does not contain server manager plugins or is for a previous version of server manager.");
var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER);
if (!Directory.Exists(pluginFolder))
Directory.CreateDirectory(pluginFolder);
var newPluginFile = Path.Combine(pluginFolder, $"{Path.GetFileName(pluginFile)}");
if (File.Exists(newPluginFile))
throw new PluginException("A file with the same name already exists, delete the existing file and try again.");
File.Copy(pluginFile, newPluginFile, true);
LoadPlugin(newPluginFile);
}
internal bool CheckPluginFile(string pluginFile)
{
if (string.IsNullOrWhiteSpace(pluginFile))
return false;
if (!File.Exists(pluginFile))
return false;
Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile));
if (assembly == null)
return false;
Type[] types;
try
{
types = assembly.GetTypes();
}
catch
{
return false;
}
if (types.Length == 0)
return false;
// check if the file contains a plugin
foreach (Type type in types)
{
if (type.GetInterface(typeof(IPlugin).Name) != null)
return true;
}
return false;
}
internal void DeleteAllPlugins()
{
for (int index = Plugins.Count - 1; index >= 0; index--)
{
var pluginFile = Plugins[index].PluginFile;
Plugins.RemoveAt(index);
if (File.Exists(pluginFile))
File.Delete(pluginFile);
}
}
internal void DeletePlugin(string pluginFile)
{
if (string.IsNullOrWhiteSpace(pluginFile))
return;
for (int index = Plugins.Count - 1; index >= 0; index--)
{
if (Plugins[index].PluginFile.Equals(pluginFile, StringComparison.OrdinalIgnoreCase))
Plugins.RemoveAt(index);
}
if (File.Exists(pluginFile))
File.Delete(pluginFile);
}
public IList<Profile> FetchProfileList()
{
lock (_syncLockFetchProfiles)
{
return _fetchProfilesCallback?.Invoke() ?? new List<Profile>();
}
}
internal void LoadPlugin(string pluginFile)
{
if (string.IsNullOrWhiteSpace(pluginFile))
return;
if (!File.Exists(pluginFile))
return;
Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile));
if (assembly == null)
return;
Type[] types;
try
{
types = assembly.GetTypes();
}
catch
{
return;
}
if (types.Length == 0)
return;
// check if the file contains one or more plugins
foreach (Type type in types)
{
try
{
if (type.GetInterface(typeof(IAlertPlugin).Name) != null)
{
var plugin = assembly.CreateInstance(type.FullName) as IAlertPlugin;
if (plugin != null && plugin.Enabled)
{
if (type.GetInterface(typeof(IBeta).Name) != null)
((IBeta)plugin).BetaEnabled = BetaEnabled;
plugin.Initialize();
Plugins.Add(new PluginItem { Plugin = plugin, PluginFile = pluginFile, PluginType = nameof(IAlertPlugin) });
}
}
else if (type.GetInterface(typeof(IPlugin).Name) != null)
{
var plugin = assembly.CreateInstance(type.FullName) as IPlugin;
if (plugin != null && plugin.Enabled)
{
if (type.GetInterface(typeof(IBeta).Name) != null)
((IBeta)plugin).BetaEnabled = BetaEnabled;
plugin.Initialize();
Plugins.Add(new PluginItem { Plugin = plugin, PluginFile = pluginFile, PluginType = nameof(IPlugin) });
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"ERROR: {nameof(LoadPlugin)} - {type.FullName}\r\n{ex.Message}");
}
}
}
internal void LoadPlugins(string folder, bool ClearExisting)
{
if (ClearExisting)
Plugins.Clear();
var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER);
if (string.IsNullOrWhiteSpace(pluginFolder))
return;
if (!Directory.Exists(pluginFolder))
return;
var pluginFiles = Directory.GetFiles(pluginFolder, $"*.{PLUGINFILE_EXTENSION}");
foreach (var pluginFile in pluginFiles)
{
LoadPlugin(pluginFile);
}
}
internal void OpenConfigForm(string pluginCode, Window owner)
{
if (Plugins == null)
return;
var pluginItem = Plugins.FirstOrDefault(p => p.Plugin.PluginCode.Equals(pluginCode, StringComparison.OrdinalIgnoreCase));
OpenConfigForm(pluginItem.Plugin, owner);
}
internal void OpenConfigForm(IPlugin plugin, Window owner)
{
if (plugin == null || !plugin.Enabled || !plugin.HasConfigForm)
return;
plugin.OpenConfigForm(owner);
}
internal bool ProcessAlert(AlertType alertType, string profileName, string alertMessage)
{
if (Plugins == null || Plugins.Count == 0 || string.IsNullOrWhiteSpace(alertMessage))
return false;
var plugins = Plugins.Where(p => (p.PluginType is nameof(IAlertPlugin)) && (p.Plugin?.Enabled ?? false));
if (plugins.Count() == 0)
return false;
lock (_syncLockProcessAlert)
{
var message = alertMessage.Replace("\\r\\n", "\\n");
message = message.Replace("\\n", "\n");
foreach (var pluginItem in plugins)
{
((IAlertPlugin)pluginItem.Plugin).HandleAlert(alertType, profileName, message.ToString());
}
}
return true;
}
internal void SetFetchProfileCallback(FetchProfilesDelegate callback)
{
_fetchProfilesCallback = callback;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_fetchProfilesCallback = null;
_instance = null;
}
_disposed = true;
}
}
}

View file

@ -0,0 +1,27 @@
namespace ServerManagerTool.Plugin.Common
{
public sealed class PluginItem
{
internal PluginItem()
{
}
public IPlugin Plugin
{
get;
set;
}
public string PluginFile
{
get;
set;
}
public string PluginType
{
get;
set;
}
}
}

View file

@ -0,0 +1,41 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 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.
[assembly: AssemblyTitle("ServerManager Common Plugin Library")]
[assembly: AssemblyDescription("The library is used to provide common plugin functionality to the server managers.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Bletch1971")]
[assembly: AssemblyProduct("Server Managers")]
[assembly: AssemblyCopyright("Copyright © 2015-2020")]
[assembly: AssemblyTrademark("")]
[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
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("679fe859-9a82-4ffb-a758-c1e8df915f58")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// 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.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: InternalsVisibleTo("ARK Server Manager")]
[assembly: InternalsVisibleTo("ConanServerManager")]
[assembly: InternalsVisibleTo("ServerManager")]
[assembly: InternalsVisibleTo("ServerManager.Plugin.Common.UnitTests")]
[assembly: InternalsVisibleTo("ServerManager.Plugin.Discord.UnitTests")]

View file

@ -0,0 +1,64 @@
using System.IO;
using System.Runtime.Serialization.Json;
namespace ServerManagerTool.Plugin.Common
{
public static class JsonUtils
{
public static T DeserializeFromFile<T>(string file)
{
if (string.IsNullOrEmpty(file) || !File.Exists(file))
return default(T);
StreamReader streamReader = null;
try
{
streamReader = File.OpenText(file);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(streamReader.BaseStream);
}
catch
{
return default(T);
}
finally
{
if (streamReader != null)
streamReader.Close();
}
}
public static bool SerializeToFile<T>(T value, string file)
{
if (value == null)
return false;
StreamWriter streamWriter = null;
try
{
var folder = Path.GetDirectoryName(file);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
streamWriter = File.CreateText(file);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
serializer.WriteObject(streamWriter.BaseStream, value);
return true;
}
catch
{
return false;
}
finally
{
if (streamWriter != null)
streamWriter.Close();
}
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Windows;
namespace ServerManagerTool.Plugin.Common
{
public static class ResourceUtils
{
public static string GetResourceString(ResourceDictionary resources, string inKey)
{
if (resources == null)
throw new ArgumentNullException(nameof(resources), "parameter cannot be null.");
if (string.IsNullOrWhiteSpace(inKey))
throw new ArgumentNullException(nameof(inKey), "parameter cannot be null.");
if (resources.Contains(inKey) && resources[inKey] is string)
{
var resourceString = resources[inKey].ToString();
resourceString = resourceString.Replace("\\r", "\r");
resourceString = resourceString.Replace("\\n", "\n");
return resourceString;
}
return null;
}
}
}