-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
650 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net5.0</TargetFramework> | ||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> | ||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> | ||
<OutputType>WinExe</OutputType> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<None Remove="ModConfig.json" /> | ||
<None Remove="Preview.png" /> | ||
<None Remove="Publish.ps1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Content Include="ModConfig.json"> | ||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||
</Content> | ||
<Content Include="Preview.png"> | ||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Reloaded.Memory" Version="3.0.1" /> | ||
<PackageReference Include="Reloaded.Memory.Sigscan" Version="1.2.1" /> | ||
<PackageReference Include="Reloaded.Mod.Interfaces" Version="1.6.0" /> | ||
<PackageReference Include="Reloaded.SharedLib.Hooks" Version="1.4.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using BattleUIShortcuts.Configuration.Implementation; | ||
using System.ComponentModel; | ||
|
||
namespace BattleUIShortcuts.Configuration | ||
{ | ||
public class Config : Configurable<Config> | ||
{ | ||
/* | ||
User Properties: | ||
- Please put all of your configurable properties here. | ||
- Tip: Consider using the various available attributes https://stackoverflow.com/a/15051390/11106111 | ||
By default, configuration saves as "Config.json" in mod folder. | ||
Need more config files/classes? See Configuration.cs | ||
*/ | ||
|
||
|
||
[DisplayName("String")] | ||
[Description("This is a string.")] | ||
public string String { get; set; } = "Default Name"; | ||
|
||
[DisplayName("Int")] | ||
[Description("This is an int.")] | ||
public int Integer { get; set; } = 42; | ||
|
||
[DisplayName("Bool")] | ||
[Description("This is a bool.")] | ||
public bool Boolean { get; set; } = true; | ||
|
||
[DisplayName("Float")] | ||
[Description("This is a floating point number.")] | ||
public float Float { get; set; } = 6.987654F; | ||
|
||
[DisplayName("Enum")] | ||
[Description("This is an enumerable.")] | ||
public SampleEnum Reloaded { get; set; } = SampleEnum.ILoveIt; | ||
|
||
public enum SampleEnum | ||
{ | ||
NoOpinion, | ||
Sucks, | ||
IsMediocre, | ||
IsOk, | ||
IsCool, | ||
ILoveIt | ||
} | ||
} | ||
} |
145 changes: 145 additions & 0 deletions
145
PC/DSCS/BattleUIShortcuts/Configuration/Implementation/Configurable.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
using Reloaded.Mod.Interfaces; | ||
using System; | ||
using System.ComponentModel; | ||
using System.IO; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace BattleUIShortcuts.Configuration.Implementation | ||
{ | ||
public class Configurable<TParentType> : IUpdatableConfigurable where TParentType : Configurable<TParentType>, new() | ||
{ | ||
// Default Serialization Options | ||
public static JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions() | ||
{ | ||
Converters = { new JsonStringEnumConverter() }, | ||
WriteIndented = true | ||
}; | ||
|
||
/* Events */ | ||
|
||
/// <summary> | ||
/// Automatically executed when the external configuration file is updated. | ||
/// Passes a new instance of the configuration as parameter. | ||
/// Inside your event handler, replace the variable storing the configuration with the new one. | ||
/// </summary> | ||
[Browsable(false)] | ||
public event Action<IUpdatableConfigurable> ConfigurationUpdated; | ||
|
||
/* Class Properties */ | ||
|
||
/// <summary> | ||
/// Full path to the configuration file. | ||
/// </summary> | ||
[JsonIgnore] | ||
[Browsable(false)] | ||
public string FilePath { get; private set; } | ||
|
||
/// <summary> | ||
/// The name of the configuration file. | ||
/// </summary> | ||
[JsonIgnore] | ||
[Browsable(false)] | ||
public string ConfigName { get; private set; } | ||
|
||
/// <summary> | ||
/// Receives events on whenever the file is actively changed or updated. | ||
/// </summary> | ||
[JsonIgnore] | ||
[Browsable(false)] | ||
private FileSystemWatcher ConfigWatcher { get; set; } | ||
|
||
/* Construction */ | ||
public Configurable() { } | ||
|
||
private void Initialize(string filePath, string configName) | ||
{ | ||
// Initializes an instance after construction by e.g. a serializer. | ||
FilePath = filePath; | ||
ConfigName = configName; | ||
|
||
MakeConfigWatcher(); | ||
Save = OnSave; | ||
} | ||
|
||
/* Cleanup */ | ||
public void DisposeEvents() | ||
{ | ||
// Halts the FilesystemWatcher and all events associated with this instance. | ||
ConfigWatcher?.Dispose(); | ||
ConfigurationUpdated = null; | ||
} | ||
|
||
/* Load/Save support. */ | ||
|
||
/// <summary> | ||
/// Saves the configuration to the hard disk. | ||
/// </summary> | ||
[JsonIgnore] | ||
[Browsable(false)] | ||
public Action Save { get; private set; } | ||
|
||
/// <summary> | ||
/// Safety lock for when changed event gets raised twice on file save. | ||
/// </summary> | ||
[Browsable(false)] | ||
private static object _readLock = new object(); | ||
|
||
/// <summary> | ||
/// Loads a specified configuration from the hard disk, or creates a default if it does not exist. | ||
/// </summary> | ||
/// <param name="filePath">The full file path of the config.</param> | ||
/// <param name="configName">The name of the configuration.</param> | ||
public static TParentType FromFile(string filePath, string configName) => ReadFrom(filePath, configName); | ||
|
||
/* Event */ | ||
|
||
/// <summary> | ||
/// Creates a <see cref="FileSystemWatcher"/> that will automatically raise an | ||
/// <see cref="OnConfigurationUpdated"/> event when the config file is changed. | ||
/// </summary> | ||
/// <returns></returns> | ||
private void MakeConfigWatcher() | ||
{ | ||
ConfigWatcher = new FileSystemWatcher(Path.GetDirectoryName(FilePath), Path.GetFileName(FilePath)); | ||
ConfigWatcher.Changed += (sender, e) => OnConfigurationUpdated(); | ||
ConfigWatcher.EnableRaisingEvents = true; | ||
} | ||
|
||
/// <summary> | ||
/// Reloads the configuration from the hard disk and raises the updated event. | ||
/// </summary> | ||
private void OnConfigurationUpdated() | ||
{ | ||
lock (_readLock) | ||
{ | ||
// Load and copy events. | ||
// Note: External program might still be writing to file while this is being executed, so we need to keep retrying. | ||
var newConfig = Utilities.TryGetValue(() => ReadFrom(this.FilePath, this.ConfigName), 250, 2); | ||
newConfig.ConfigurationUpdated = ConfigurationUpdated; | ||
|
||
// Disable events for this instance. | ||
DisposeEvents(); | ||
|
||
// Call subscribers through the new config. | ||
newConfig.ConfigurationUpdated?.Invoke(newConfig); | ||
} | ||
} | ||
|
||
private void OnSave() | ||
{ | ||
var parent = (TParentType)this; | ||
File.WriteAllText(FilePath, JsonSerializer.Serialize(parent, SerializerOptions)); | ||
} | ||
|
||
/* Utility */ | ||
private static TParentType ReadFrom(string filePath, string configName) | ||
{ | ||
var result = File.Exists(filePath) | ||
? JsonSerializer.Deserialize<TParentType>(File.ReadAllBytes(filePath), SerializerOptions) | ||
: new TParentType(); | ||
result.Initialize(filePath, configName); | ||
return result; | ||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
PC/DSCS/BattleUIShortcuts/Configuration/Implementation/Configurator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using Reloaded.Mod.Interfaces; | ||
using System.IO; | ||
|
||
namespace BattleUIShortcuts.Configuration.Implementation | ||
{ | ||
public class Configurator : IConfigurator | ||
{ | ||
/* For latest documentation: | ||
- See the interface! (Go To Definition) or if not available | ||
- Google the Source Code! | ||
*/ | ||
|
||
/// <summary> | ||
/// Full path to the mod folder. | ||
/// </summary> | ||
public string ModFolder { get; private set; } | ||
|
||
/// <summary> | ||
/// Returns a list of configurations. | ||
/// </summary> | ||
public IUpdatableConfigurable[] Configurations => _configurations ?? MakeConfigurations(); | ||
private IUpdatableConfigurable[] _configurations; | ||
|
||
private IUpdatableConfigurable[] MakeConfigurations() | ||
{ | ||
_configurations = new IUpdatableConfigurable[] | ||
{ | ||
// Add more configurations here if needed. | ||
Configurable<Config>.FromFile(Path.Combine(ModFolder, "Config.json"), "Default Config") | ||
}; | ||
|
||
// Add self-updating to configurations. | ||
for (int x = 0; x < Configurations.Length; x++) | ||
{ | ||
var xCopy = x; | ||
Configurations[x].ConfigurationUpdated += configurable => | ||
{ | ||
Configurations[xCopy] = configurable; | ||
}; | ||
} | ||
|
||
return _configurations; | ||
} | ||
|
||
public Configurator() { } | ||
public Configurator(string modDirectory) : this() | ||
{ | ||
ModFolder = modDirectory; | ||
} | ||
|
||
/* Configurator */ | ||
|
||
/// <summary> | ||
/// Gets an individual user configuration. | ||
/// </summary> | ||
public TType GetConfiguration<TType>(int index) => (TType)Configurations[index]; | ||
|
||
/* IConfigurator. */ | ||
|
||
/// <summary> | ||
/// Sets the mod directory for the Configurator. | ||
/// </summary> | ||
public void SetModDirectory(string modDirectory) => ModFolder = modDirectory; | ||
|
||
/// <summary> | ||
/// Returns a list of user configurations. | ||
/// </summary> | ||
public IConfigurable[] GetConfigurations() => Configurations; | ||
|
||
/// <summary> | ||
/// Allows for custom launcher/configurator implementation. | ||
/// If you have your own configuration program/code, run that code here and return true, else return false. | ||
/// </summary> | ||
public bool TryRunCustomConfiguration() => false; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
PC/DSCS/BattleUIShortcuts/Configuration/Implementation/Utilities.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Threading; | ||
|
||
namespace BattleUIShortcuts.Configuration.Implementation | ||
{ | ||
public class Utilities | ||
{ | ||
/// <param name="getValue">Function that retrieves the value.</param> | ||
/// <param name="timeout">The timeout in milliseconds.</param> | ||
/// <param name="sleepTime">Amount of sleep per iteration/attempt.</param> | ||
/// <param name="token">Token that allows for cancellation of the task.</param> | ||
/// <exception cref="Exception">Timeout expired.</exception> | ||
public static T TryGetValue<T>(Func<T> getValue, int timeout, int sleepTime, CancellationToken token = default) | ||
{ | ||
Stopwatch watch = new Stopwatch(); | ||
watch.Start(); | ||
bool valueSet = false; | ||
T value = default; | ||
|
||
while (watch.ElapsedMilliseconds < timeout) | ||
{ | ||
if (token.IsCancellationRequested) | ||
return value; | ||
|
||
try | ||
{ | ||
value = getValue(); | ||
valueSet = true; | ||
break; | ||
} | ||
catch (Exception) { /* Ignored */ } | ||
|
||
Thread.Sleep(sleepTime); | ||
} | ||
|
||
if (valueSet == false) | ||
throw new Exception($"Timeout limit {timeout} exceeded."); | ||
|
||
return value; | ||
} | ||
} | ||
} |
Oops, something went wrong.