diff --git a/CloudflareSolverRe.Captcha.dll b/CloudflareSolverRe.Captcha.dll
new file mode 100644
index 0000000..ae5bd46
Binary files /dev/null and b/CloudflareSolverRe.Captcha.dll differ
diff --git a/CloudflareSolverRe.dll b/CloudflareSolverRe.dll
new file mode 100644
index 0000000..ee41a91
Binary files /dev/null and b/CloudflareSolverRe.dll differ
diff --git a/Extreme.Net.dll b/Extreme.Net.dll
index 872c305..0b1c5d0 100644
Binary files a/Extreme.Net.dll and b/Extreme.Net.dll differ
diff --git a/OpenBullet.sln b/OpenBullet.sln
index 7faa0cf..d518d6d 100644
--- a/OpenBullet.sln
+++ b/OpenBullet.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2036
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBullet", "OpenBullet\OpenBullet.csproj", "{99E40E7F-00A4-4FB1-9441-B05A56C47C08}"
EndProject
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuriLib", "RuriLib\RuriLib.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBulletCLI", "OpenBulletCLI\OpenBulletCLI.csproj", "{0B6D8B01-861E-4CAF-B1C9-6670884381DB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginFramework", "PluginFramework\PluginFramework.csproj", "{E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
{0B6D8B01-861E-4CAF-B1C9-6670884381DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B6D8B01-861E-4CAF-B1C9-6670884381DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B6D8B01-861E-4CAF-B1C9-6670884381DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/OpenBullet/App.config b/OpenBullet/App.config
index b203939..a24c5e9 100644
--- a/OpenBullet/App.config
+++ b/OpenBullet/App.config
@@ -12,7 +12,7 @@
-
+
diff --git a/OpenBullet/Extensions.cs b/OpenBullet/Extensions.cs
index aaccf01..775b362 100644
--- a/OpenBullet/Extensions.cs
+++ b/OpenBullet/Extensions.cs
@@ -8,10 +8,25 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
+using PluginFramework;
+using RuriLib;
using RuriLib.Models;
namespace OpenBullet
{
+ public static class BlocksExtensions
+ {
+ public static IEnumerable OnlyPlugins(this IEnumerable blocks)
+ {
+ return blocks.Where(b => b.IsPlugin());
+ }
+
+ public static bool IsPlugin(this BlockBase block)
+ {
+ return block.GetType().GetInterface(nameof(IBlockPlugin)) == typeof(IBlockPlugin);
+ }
+ }
+
public static class EnumerableExtensions
{
public static void SaveToFile(this IEnumerable items, string fileName, Func mapping)
diff --git a/OpenBullet/LSDoc.xml b/OpenBullet/LSDoc.xml
index 149ebd2..b469c2b 100644
--- a/OpenBullet/LSDoc.xml
+++ b/OpenBullet/LSDoc.xml
@@ -171,39 +171,39 @@ ELEMENT LOCATOR "STRING" [INDEX]
Allowed Actions:
## Spawn a div with a given id to a (x,y) absolute position, so you can use it as element hook for mouse movement
-SPAWN "ID" X Y
+SPAWN "ID" "X" "Y"
CLICK [ELEMENT]
CLICKANDHOLD [ELEMENT]
RIGHTCLICK [ELEMENT]
DOUBLECLICK [ELEMENT]
DRAGANDDROP ELEMENT -> ELEMENT
-DRAGANDDROPWITHOFFSET X Y ELEMENT
+DRAGANDDROPWITHOFFSET "X" "Y" ELEMENT
KEYDOWN "KEY" [ELEMENT]
KEYUP "KEY" [ELEMENT]
-MOVEBY X Y
+MOVEBY "X" "Y"
MOVETO ELEMENT
RELEASE [ELEMENT]
SENDKEYS "KEYS" [ELEMENT]
## Draws random points in a square starting from the top left corner and with the given width and height
DRAWPOINTS MAXW MAXH AMOUNT
-DRAWLINE (X1 Y2 -> X2 Y2 / ELEMENT -> ELEMENT) : AMOUNT
-DRAWLINEHUMAN (X1 Y1 -> X2 Y2 / ELEMENT -> ELEMENT) : AMOUNT [GRAVITY WIND]
+DRAWLINE ("X1" "Y1" -> "X2" "Y2" / ELEMENT -> ELEMENT) : AMOUNT
+DRAWLINEHUMAN ("X1" "Y1" -> "X2" "Y2" / ELEMENT -> ELEMENT) : AMOUNT [GRAVITY WIND]
Examples:
## Moving to an element and clicking 100 pixels right of it
MOUSEACTION
MOVETO ELEMENT CLASS "testclass" 1
- MOVEBY 100 0
+ MOVEBY "100" "0"
CLICK
## Moving to a specific set of coordinates on the page
MOUSEACTION
- SPAWN "target" 100 150
+ SPAWN "target" "100" "150"
MOVETO ELEMENT ID "target"
## Moving like a human from point A (0,0) to point B (700,700). The lower the AMOUNT, the faster the line is drawn
MOUSEACTION
- DRAWLINEHUMAN 0 0 -> 700 700 : 150 1 1
+ DRAWLINEHUMAN "0" "0" -> "700" "700" : 150 1 1
## Moving like a human from element with id="one" to element with id="two", 20 points and default gravity/wind
MOUSEACTION
@@ -239,7 +239,7 @@ FUNCTION Translate StopAfterFirstMatch=True
FUNCTION DateToUnixTime "format" "input" -> VAR "DATE"
FUNCTION Replace "what" "with" UseRegex=True "input" -> VAR "REPLACED"
FUNCTION RegexMatch "pattern" "input" -> VAR "MATCHED"
-FUNCTION RandomNum 0 100 -> VAR "RANDOM"
+FUNCTION RandomNum "0" "100" -> VAR "RANDOM"
FUNCTION CountOccurrences "tofind" "input" -> VAR "COUNT"
FUNCTION CharAt "index" "input" -> VAR "CHAR"
FUNCTION Substring "startindex" "length" "input" -> VAR "SUBSTRING"
diff --git a/OpenBullet/MainWindow.xaml b/OpenBullet/MainWindow.xaml
index 6d66cc5..c5aaf99 100644
--- a/OpenBullet/MainWindow.xaml
+++ b/OpenBullet/MainWindow.xaml
@@ -88,6 +88,7 @@
+
diff --git a/OpenBullet/MainWindow.xaml.cs b/OpenBullet/MainWindow.xaml.cs
index 93348bc..02f4e19 100644
--- a/OpenBullet/MainWindow.xaml.cs
+++ b/OpenBullet/MainWindow.xaml.cs
@@ -19,6 +19,11 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
+using OpenBullet.Plugins;
+using System.Collections.Generic;
+using OpenBullet.Views.UserControls;
+using OpenBullet.Views.StackerBlocks;
+using RuriLib.LS;
namespace OpenBullet
{
@@ -38,6 +43,7 @@ namespace OpenBullet
public HitsDB HitsDBPage { get; set; }
public Settings OBSettingsPage { get; set; }
public ToolsSection ToolsPage { get; set; }
+ public PluginsSection PluginsPage { get; set; }
public About AboutPage { get; set; }
public Rectangle Bounds { get; private set; }
@@ -59,8 +65,8 @@ namespace OpenBullet
titleLabel.Content = title;
// Make sure all folders are there or recreate them
- var folders = new string[] { "Captchas", "ChromeExtensions", "Configs", "DB", "Screenshots", "Settings", "Sounds", "Wordlists" };
- foreach (var folder in folders.Select(f => System.IO.Path.Combine(Directory.GetCurrentDirectory(), f)))
+ var folders = new string[] { "Captchas", "ChromeExtensions", "Configs", "DB", "Plugins", "Screenshots", "Settings", "Sounds", "Wordlists" };
+ foreach (var folder in folders.Select(f => Path.Combine(Directory.GetCurrentDirectory(), f)))
{
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
@@ -73,13 +79,15 @@ namespace OpenBullet
}
catch
{
- MessageBox.Show("Could not find / parse the Environment Settings file. Please fix the issue and try again.");
+ OB.Logger.LogError(Components.Main,
+ "Could not find / parse the Environment Settings file. Please fix the issue and try again.", true);
Environment.Exit(0);
}
if (OB.Settings.Environment.WordlistTypes.Count == 0 || OB.Settings.Environment.CustomKeychains.Count == 0)
{
- MessageBox.Show("At least one WordlistType and one CustomKeychain must be defined in the Environment Settings file.");
+ OB.Logger.LogError(Components.Main,
+ "At least one WordlistType and one CustomKeychain must be defined in the Environment Settings file.", true);
Environment.Exit(0);
}
@@ -140,6 +148,46 @@ namespace OpenBullet
Topmost = OB.OBSettings.General.AlwaysOnTop;
+ // Load Plugins
+ var (plugins, blockPlugins) = Loader.LoadPlugins(OB.pluginsFolder);
+ OB.BlockPlugins = blockPlugins.ToList();
+
+ // Set mappings
+ OB.BlockMappings = new List<(Type, Type, System.Windows.Media.Color)>()
+ {
+ ( typeof(BlockBypassCF), typeof(PageBlockBypassCF), Colors.DarkSalmon ),
+ ( typeof(BlockImageCaptcha), typeof(PageBlockCaptcha), Colors.DarkOrange ),
+ ( typeof(BlockFunction), typeof(PageBlockFunction), Colors.YellowGreen ),
+ ( typeof(BlockKeycheck), typeof(PageBlockKeycheck), Colors.DodgerBlue ),
+ ( typeof(BlockLSCode), typeof(PageBlockLSCode), Colors.White ),
+ ( typeof(BlockParse), typeof(PageBlockParse), Colors.Gold ),
+ ( typeof(BlockRecaptcha), typeof(PageBlockRecaptcha), Colors.Turquoise ),
+ ( typeof(BlockRequest), typeof(PageBlockRequest), Colors.LimeGreen ),
+ ( typeof(BlockTCP), typeof(PageBlockTCP), Colors.MediumPurple ),
+ ( typeof(BlockUtility), typeof(PageBlockUtility), Colors.Wheat ),
+ ( typeof(SBlockBrowserAction), typeof(PageSBlockBrowserAction), Colors.Green ),
+ ( typeof(SBlockElementAction), typeof(PageSBlockElementAction), Colors.Firebrick ),
+ ( typeof(SBlockExecuteJS), typeof(PageSBlockExecuteJS), Colors.Indigo ),
+ ( typeof(SBlockNavigate), typeof(PageSBlockNavigate), Colors.RoyalBlue )
+ };
+
+ // Add block plugins to mappings
+ foreach (var plugin in blockPlugins)
+ {
+ try
+ {
+ var color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(plugin.Color);
+ OB.BlockMappings.Add((plugin.GetType(), typeof(BlockPluginPage), color));
+ BlockParser.BlockMappings.Add(plugin.Name, plugin.GetType());
+ OB.Logger.LogInfo(Components.Main, $"Initialized {plugin.Name} block plugin");
+ }
+ catch
+ {
+ OB.Logger.LogError(Components.Main, $"The color {plugin.Color} in block plugin {plugin.Name} is invalid", true);
+ Environment.Exit(0);
+ }
+ }
+
// ViewModels
OB.RunnerManager = new RunnerManagerViewModel();
OB.ProxyManager = new ProxyManagerViewModel();
@@ -164,6 +212,8 @@ namespace OpenBullet
OB.Logger.LogInfo(Components.Main, "Initialized Settings");
ToolsPage = new ToolsSection();
OB.Logger.LogInfo(Components.Main, "Initialized Tools");
+ PluginsPage = new PluginsSection(plugins);
+ OB.Logger.LogInfo(Components.Main, "Initialized Plugins");
AboutPage = new About();
menuOptionRunner_MouseDown(this, null);
@@ -284,6 +334,12 @@ namespace OpenBullet
menuOptionSelected(menuOptionTools);
}
+ private void menuOptionPlugins_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ Main.Content = PluginsPage;
+ menuOptionSelected(menuOptionPlugins);
+ }
+
private void menuOptionSettings_MouseDown(object sender, MouseButtonEventArgs e)
{
Main.Content = OBSettingsPage;
diff --git a/OpenBullet/OB.cs b/OpenBullet/OB.cs
index e1dd783..acee7bb 100644
--- a/OpenBullet/OB.cs
+++ b/OpenBullet/OB.cs
@@ -1,6 +1,11 @@
using OpenBullet.ViewModels;
+using PluginFramework;
+using RuriLib;
using RuriLib.Interfaces;
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Media;
namespace OpenBullet
{
@@ -20,7 +25,14 @@ namespace OpenBullet
};
public static string Version => "1.2.0";
- public static Random Random { get; } = new Random();
+
+ // Block Mappings (including Plugins)
+ public static List<(Type, Type, Color)> BlockMappings = new List<(Type, Type, Color)>();
+
+ // Block Plugins
+ // HACK: Find a better way to do this and a better place to put them
+ public static List BlockPlugins;
+ public static IEnumerable BlockPluginsAsBase => BlockPlugins.Cast();
// Windows
// TODO: Remove these from here, everything should only depend on the ViewModels not on the Views!
@@ -48,5 +60,6 @@ namespace OpenBullet
public static readonly string licenseFile = @"Settings/License.txt";
public static readonly string logFile = @"Log.txt";
public static readonly string configFolder = @"Configs";
+ public static readonly string pluginsFolder = @"Plugins";
}
}
diff --git a/OpenBullet/OBIOManager.cs b/OpenBullet/OBIOManager.cs
index e49b198..45caf7f 100644
--- a/OpenBullet/OBIOManager.cs
+++ b/OpenBullet/OBIOManager.cs
@@ -1,6 +1,10 @@
using Newtonsoft.Json;
using OpenBullet.ViewModels;
+using RuriLib;
+using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
namespace OpenBullet
{
@@ -17,5 +21,16 @@ namespace OpenBullet
{
return JsonConvert.DeserializeObject(File.ReadAllText(settingsFile));
}
+
+ public static void CheckRequiredPlugins(IEnumerable available, Config config)
+ {
+ foreach (var required in config.Settings.RequiredPlugins)
+ {
+ if (!available.Contains(required))
+ {
+ throw new Exception($"This config requires the plugin {required} which is missing from the Plugins folder and hence cannot be opened!");
+ }
+ }
+ }
}
}
diff --git a/OpenBullet/OpenBullet.csproj b/OpenBullet/OpenBullet.csproj
index 54e6ee9..f556201 100644
--- a/OpenBullet/OpenBullet.csproj
+++ b/OpenBullet/OpenBullet.csproj
@@ -71,8 +71,8 @@
False
..\Extreme.Net.dll
-
- ..\packages\AvalonEdit.5.0.4\lib\Net40\ICSharpCode.AvalonEdit.dll
+
+ ..\packages\AvalonEdit.6.0.1\lib\net45\ICSharpCode.AvalonEdit.dll
..\packages\IronPython.2.7.9\lib\net45\IronPython.dll
@@ -102,7 +102,7 @@
..\packages\DynamicLanguageRuntime.1.2.2\lib\net45\Microsoft.Scripting.Metadata.dll
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
+ ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
@@ -121,13 +121,13 @@
..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll
-
- ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
+
+ ..\packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll
True
True
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll
@@ -147,8 +147,8 @@
..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll
-
- ..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll
+
+ ..\packages\System.Text.Encoding.CodePages.4.7.0\lib\net461\System.Text.Encoding.CodePages.dll
@@ -179,20 +179,20 @@
..\packages\Infragistics.Themes.MetroDark.Wpf.1.0.0\lib\net40\WPFToolkit.dll
-
- ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.AvalonDock.dll
+
+ ..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.AvalonDock.dll
-
- ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.AvalonDock.Themes.Aero.dll
+
+ ..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.AvalonDock.Themes.Aero.dll
-
- ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.AvalonDock.Themes.Metro.dll
+
+ ..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.AvalonDock.Themes.Metro.dll
-
- ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.AvalonDock.Themes.VS2010.dll
+
+ ..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.AvalonDock.Themes.VS2010.dll
-
- ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.Toolkit.dll
+
+ ..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.Toolkit.dll
@@ -213,6 +213,10 @@
LogWindow.xaml
+
+
+
+
DialogLSDoc.xaml
@@ -235,6 +239,48 @@
LoadingWindow.xaml
+
+ PluginsSection.xaml
+
+
+ ButtonContainer.xaml
+
+
+ BlockPluginPage.xaml
+
+
+ PluginControl.xaml
+
+
+ UserControlCheckbox.xaml
+
+
+ UserControlContainer.xaml
+
+
+ UserControlDropdown.xaml
+
+
+ UserControlConfig.xaml
+
+
+ UserControlInfoText.xaml
+
+
+ UserControlWordlist.xaml
+
+
+ UserControlFilePicker.xaml
+
+
+ UserControlNumeric.xaml
+
+
+ UserControlText.xaml
+
+
+ UserControlTextMulti.xaml
+
Always
Designer
@@ -381,8 +427,8 @@
True
Resources.resx
-
- Tools.xaml
+
+ ToolsSection.xaml
ListGenerator.xaml
@@ -511,6 +557,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -690,7 +740,7 @@
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
@@ -702,6 +752,58 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
@@ -813,6 +915,10 @@
+
+ {E1DD89D0-EC1D-4849-A2B6-ADB261DFA3CE}
+ PluginFramework
+
{17e95d0b-c1fd-445c-9fdf-244af7299a67}
RuriLib
@@ -852,15 +958,16 @@
Questo progetto fa riferimento a uno o più pacchetti NuGet che non sono presenti in questo computer. Usare lo strumento di ripristino dei pacchetti NuGet per scaricarli. Per altre informazioni, vedere http://go.microsoft.com/fwlink/?LinkID=322105. Il file mancante è {0}.
-
-
-
+
+
+
if not exist "$(TargetDir)bin" mkdir "$(TargetDir)bin"
if $(ConfigurationName) == Release move "$(TargetDir)*.dll" "$(TargetDir)bin\"
if $(ConfigurationName) == Release move "$(TargetDir)*driver.exe" "$(TargetDir)bin\"
+if exist "$(TargetDir)cs-CZ" RD /S /Q "$(TargetDir)cs-CZ"
if exist "$(TargetDir)de" RD /S /Q "$(TargetDir)de"
if exist "$(TargetDir)es" RD /S /Q "$(TargetDir)es"
if exist "$(TargetDir)fr" RD /S /Q "$(TargetDir)fr"
@@ -877,8 +984,8 @@ if not exist "$(TargetDir)Sounds" mkdir "$(TargetDir)Sounds"
copy "$(ProjectDir)Sounds\rifle_hit.wav" "$(TargetDir)Sounds\rifle_hit.wav"
copy "$(ProjectDir)Sounds\rifle_reload.wav" "$(TargetDir)Sounds\rifle_reload.wav"
-
-
-
+
+
+
\ No newline at end of file
diff --git a/OpenBullet/Plugins/Build.cs b/OpenBullet/Plugins/Build.cs
new file mode 100644
index 0000000..d873fba
--- /dev/null
+++ b/OpenBullet/Plugins/Build.cs
@@ -0,0 +1,72 @@
+using OpenBullet.Views.UserControls;
+using PluginFramework;
+using PluginFramework.Attributes;
+using System;
+using System.Reflection;
+
+namespace OpenBullet.Plugins
+{
+ public static class Build
+ {
+ public static UserControlContainer InputField(object plugin, PropertyInfo property)
+ {
+ // Get the first attribute
+ var attribute = property.GetCustomAttribute();
+
+ IControl control;
+ var defaultValue = (dynamic)Convert.ChangeType(property.GetValue(plugin), property.PropertyType);
+
+ switch (attribute)
+ {
+ case InfoText a:
+ control = new UserControlInfoText(defaultValue);
+ break;
+
+ case Text a:
+ control = new UserControlText(defaultValue);
+ break;
+
+ case Numeric a:
+ control = new UserControlNumeric(defaultValue, a.minimum, a.maximum);
+ break;
+
+ case Checkbox a:
+ control = new UserControlCheckbox(defaultValue);
+ break;
+
+ case TextMulti a:
+ control = new UserControlTextMulti(defaultValue);
+ break;
+
+ case FilePicker a:
+ control = new UserControlFilePicker(defaultValue, a.filter);
+ break;
+
+ case Dropdown a:
+ control = new UserControlDropdown(defaultValue, a.options);
+ break;
+
+ case WordlistPicker a:
+ control = new UserControlWordlist();
+ break;
+
+ case ConfigPicker a:
+ control = new UserControlConfig();
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+
+ return new UserControlContainer(property.Name, control, attribute.label, attribute.tooltip);
+ }
+
+ public static ButtonContainer Button(MethodInfo method, PluginControl pluginControl)
+ {
+ // Get the first attribute
+ var attribute = method.GetCustomAttribute