mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 02:03:32 +00:00
Merge branch 'development'
This commit is contained in:
commit
9437c9386b
@ -10,7 +10,7 @@
|
|||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
|
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
|
||||||
@ -30,7 +30,7 @@
|
|||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
|
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-3.0.2.0" newVersion="3.0.2.0" />
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
|
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
|
||||||
@ -44,6 +44,10 @@
|
|||||||
<assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" />
|
<assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
|
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||||
|
</dependentAssembly>
|
||||||
</assemblyBinding></runtime>
|
</assemblyBinding></runtime>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
@ -45,6 +45,7 @@
|
|||||||
<CreateDesktopShortcut>true</CreateDesktopShortcut>
|
<CreateDesktopShortcut>true</CreateDesktopShortcut>
|
||||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
<HockeyAppResourceId>38ead84566f241de8f334abe173b1038</HockeyAppResourceId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
@ -140,32 +141,23 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Caliburn.Micro, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
<Reference Include="Caliburn.Micro, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Caliburn.Micro.Core.3.0.3\lib\net45\Caliburn.Micro.dll</HintPath>
|
<HintPath>..\packages\Caliburn.Micro.Core.3.1.0\lib\net45\Caliburn.Micro.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Caliburn.Micro.Platform, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
<Reference Include="Caliburn.Micro.Platform, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Caliburn.Micro.3.0.3\lib\net45\Caliburn.Micro.Platform.dll</HintPath>
|
<HintPath>..\packages\Caliburn.Micro.3.1.0\lib\net45\Caliburn.Micro.Platform.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Caliburn.Micro.Platform.Core, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
<Reference Include="Caliburn.Micro.Platform.Core, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Caliburn.Micro.3.0.3\lib\net45\Caliburn.Micro.Platform.Core.dll</HintPath>
|
<HintPath>..\packages\Caliburn.Micro.3.1.0\lib\net45\Caliburn.Micro.Platform.Core.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Corale.Colore, Version=5.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Corale.Colore, Version=5.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Colore.5.1.0\lib\net35\Corale.Colore.dll</HintPath>
|
<HintPath>..\packages\Colore.5.1.0\lib\net35\Corale.Colore.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="CSCore, Version=1.1.6245.30570, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
|
<Reference Include="CSCore, Version=1.2.1.2, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\CSCore.1.2.0\lib\net35-client\CSCore.dll</HintPath>
|
<HintPath>..\packages\CSCore.1.2.1.2\lib\net35-client\CSCore.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="CUE.NET, Version=1.1.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="CUE.NET, Version=1.1.3.1, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\CUE.NET.1.1.3.0\lib\net45\CUE.NET.dll</HintPath>
|
<HintPath>..\packages\CUE.NET.1.1.3.1\lib\net45\CUE.NET.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
|
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll</HintPath>
|
<HintPath>..\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll</HintPath>
|
||||||
@ -179,9 +171,8 @@
|
|||||||
<HintPath>..\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
|
<HintPath>..\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="DynamicExpresso.Core, Version=1.3.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="DynamicExpresso.Core, Version=1.3.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\DynamicExpresso.Core.1.3.3.5\lib\net40\DynamicExpresso.Core.dll</HintPath>
|
<HintPath>..\packages\DynamicExpresso.Core.1.3.4.7\lib\net40\DynamicExpresso.Core.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Gma.System.MouseKeyHook, Version=5.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Gma.System.MouseKeyHook, Version=5.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\MouseKeyHook.5.4.0\lib\net40\Gma.System.MouseKeyHook.dll</HintPath>
|
<HintPath>..\packages\MouseKeyHook.5.4.0\lib\net40\Gma.System.MouseKeyHook.dll</HintPath>
|
||||||
@ -197,7 +188,6 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
|
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
|
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
|
||||||
@ -205,7 +195,9 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="MahApps.Metro, Version=1.4.3.0, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL">
|
<Reference Include="MahApps.Metro, Version=1.4.3.0, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\MahApps.Metro.1.4.3\lib\net45\MahApps.Metro.dll</HintPath>
|
<HintPath>..\packages\MahApps.Metro.1.4.3\lib\net45\MahApps.Metro.dll</HintPath>
|
||||||
<Private>True</Private>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.6.5.0, Culture=neutral, PublicKeyToken=c416bc1b32d97233, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\TaskScheduler.2.6.5\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
|
||||||
@ -227,9 +219,8 @@
|
|||||||
<HintPath>..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
|
<HintPath>..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Ninject, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
|
<Reference Include="Ninject, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
|
<HintPath>..\packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
|
||||||
@ -248,36 +239,42 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.4\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
||||||
<Private>True</Private>
|
</Reference>
|
||||||
|
<Reference Include="PcapDotNet.Base, Version=1.0.4.25027, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Pcap.Net.x64.1.0.4.1\lib\net45\PcapDotNet.Base.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PcapDotNet.Core, Version=1.0.4.25149, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=AMD64">
|
||||||
|
<HintPath>..\packages\Pcap.Net.x64.1.0.4.1\lib\net45\PcapDotNet.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PcapDotNet.Core.Extensions, Version=1.0.4.25151, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=AMD64">
|
||||||
|
<HintPath>..\packages\Pcap.Net.x64.1.0.4.1\lib\net45\PcapDotNet.Core.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PcapDotNet.Packets, Version=1.0.4.25028, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Pcap.Net.x64.1.0.4.1\lib\net45\PcapDotNet.Packets.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Process.NET, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Process.NET, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Process.NET.1.0.8\lib\Process.NET.dll</HintPath>
|
<HintPath>..\packages\Process.NET.1.0.8\lib\Process.NET.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
<Reference Include="SharpDX, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll</HintPath>
|
<HintPath>..\packages\SharpDX.4.0.1\lib\net45\SharpDX.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SharpDX.Direct3D9, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
<Reference Include="SharpDX.Direct3D9, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\SharpDX.Direct3D9.3.1.1\lib\net45\SharpDX.Direct3D9.dll</HintPath>
|
<HintPath>..\packages\SharpDX.Direct3D9.4.0.1\lib\net45\SharpDX.Direct3D9.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
|
<HintPath>..\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SpotifyAPI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="SpotifyAPI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\SpotifyAPI-NET.2.13.1\lib\SpotifyAPI.dll</HintPath>
|
<HintPath>..\packages\SpotifyAPI-NET.2.16.1\lib\SpotifyAPI.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Squirrel, Version=1.4.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Squirrel, Version=1.4.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\Squirrel.dll</HintPath>
|
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\Squirrel.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.ComponentModel.Composition" />
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
@ -291,7 +288,6 @@
|
|||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\MahApps.Metro.1.4.3\lib\net45\System.Windows.Interactivity.dll</HintPath>
|
<HintPath>..\packages\MahApps.Metro.1.4.3\lib\net45\System.Windows.Interactivity.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@ -340,6 +336,7 @@
|
|||||||
<Compile Include="DeviceProviders\Logitech\G810.cs" />
|
<Compile Include="DeviceProviders\Logitech\G810.cs" />
|
||||||
<Compile Include="DeviceProviders\Logitech\LogitechGeneric.cs" />
|
<Compile Include="DeviceProviders\Logitech\LogitechGeneric.cs" />
|
||||||
<Compile Include="DeviceProviders\Logitech\LogitechKeyboard.cs" />
|
<Compile Include="DeviceProviders\Logitech\LogitechKeyboard.cs" />
|
||||||
|
<Compile Include="DeviceProviders\Logitech\Utilities\KeyMapG810.cs" />
|
||||||
<Compile Include="Dialogs\MarkdownDialog.xaml.cs">
|
<Compile Include="Dialogs\MarkdownDialog.xaml.cs">
|
||||||
<DependentUpon>MarkdownDialog.xaml</DependentUpon>
|
<DependentUpon>MarkdownDialog.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -384,6 +381,13 @@
|
|||||||
<DependentUpon>AssettoCorsaView.xaml</DependentUpon>
|
<DependentUpon>AssettoCorsaView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Modules\Games\AssettoCorsa\AssettoCorsaViewModel.cs" />
|
<Compile Include="Modules\Games\AssettoCorsa\AssettoCorsaViewModel.cs" />
|
||||||
|
<Compile Include="Modules\Games\FormulaOne2017\FormulaOne2017Model.cs" />
|
||||||
|
<Compile Include="Modules\Games\FormulaOne2017\FormulaOne2017Settings.cs" />
|
||||||
|
<Compile Include="Modules\Games\FormulaOne2017\FormulaOne2017View.xaml.cs">
|
||||||
|
<DependentUpon>FormulaOne2017View.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Modules\Games\FormulaOne2017\FormulaOne2017ViewModel.cs" />
|
||||||
|
<Compile Include="Modules\Games\FormulaOne2017\FormulaOne2017DataModel.cs" />
|
||||||
<Compile Include="Modules\Games\Terraria\TerrariaDataModel.cs" />
|
<Compile Include="Modules\Games\Terraria\TerrariaDataModel.cs" />
|
||||||
<Compile Include="Modules\Games\Terraria\TerrariaSettings.cs" />
|
<Compile Include="Modules\Games\Terraria\TerrariaSettings.cs" />
|
||||||
<Compile Include="Modules\Games\Terraria\TerrariaModel.cs" />
|
<Compile Include="Modules\Games\Terraria\TerrariaModel.cs" />
|
||||||
@ -391,6 +395,13 @@
|
|||||||
<DependentUpon>TerrariaView.xaml</DependentUpon>
|
<DependentUpon>TerrariaView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Modules\Games\Terraria\TerrariaViewModel.cs" />
|
<Compile Include="Modules\Games\Terraria\TerrariaViewModel.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWAura.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWCastBar.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWEnums.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWSpecialization.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWSpell.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\Models\WoWUnit.cs" />
|
||||||
|
<Compile Include="Modules\Games\WoW\WoWPacketScanner.cs" />
|
||||||
<Compile Include="Modules\General\GeneralProfile\PerformanceInfo.cs" />
|
<Compile Include="Modules\General\GeneralProfile\PerformanceInfo.cs" />
|
||||||
<Compile Include="Modules\Games\EurotruckSimulator2\Data\Ets2TelemetryData.cs" />
|
<Compile Include="Modules\Games\EurotruckSimulator2\Data\Ets2TelemetryData.cs" />
|
||||||
<Compile Include="Modules\Games\EurotruckSimulator2\Data\Ets2TelemetryDataReader.cs" />
|
<Compile Include="Modules\Games\EurotruckSimulator2\Data\Ets2TelemetryDataReader.cs" />
|
||||||
@ -470,15 +481,6 @@
|
|||||||
<DependentUpon>UnrealTournamentView.xaml</DependentUpon>
|
<DependentUpon>UnrealTournamentView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Modules\Games\UnrealTournament\UnrealTournamentViewModel.cs" />
|
<Compile Include="Modules\Games\UnrealTournament\UnrealTournamentViewModel.cs" />
|
||||||
<Compile Include="Modules\Games\WoW\Data\Int128.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWEnums.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWOffsets.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWStructs.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWNameCache.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWObjectManager.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWObject.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWPlayer.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\Data\WoWUnit.cs" />
|
|
||||||
<Compile Include="Modules\Games\WoW\WoWView.xaml.cs">
|
<Compile Include="Modules\Games\WoW\WoWView.xaml.cs">
|
||||||
<DependentUpon>WoWView.xaml</DependentUpon>
|
<DependentUpon>WoWView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -486,7 +488,6 @@
|
|||||||
<Compile Include="Modules\Games\WoW\WoWModel.cs" />
|
<Compile Include="Modules\Games\WoW\WoWModel.cs" />
|
||||||
<Compile Include="Modules\Games\WoW\WoWSettings.cs" />
|
<Compile Include="Modules\Games\WoW\WoWSettings.cs" />
|
||||||
<Compile Include="Modules\Games\WoW\WoWViewModel.cs" />
|
<Compile Include="Modules\Games\WoW\WoWViewModel.cs" />
|
||||||
<Compile Include="Modules\Games\WoW\WoWAddresses.cs" />
|
|
||||||
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileDataModel.cs" />
|
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileDataModel.cs" />
|
||||||
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileModel.cs" />
|
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileModel.cs" />
|
||||||
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileSettings.cs" />
|
<Compile Include="Modules\Overlays\OverlayProfile\OverlayProfileSettings.cs" />
|
||||||
@ -573,6 +574,7 @@
|
|||||||
<Compile Include="Profiles\Lua\Modules\Gui\LuaCheckBox.cs" />
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaCheckBox.cs" />
|
||||||
<Compile Include="Profiles\Lua\Modules\Gui\LuaComboBox.cs" />
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaComboBox.cs" />
|
||||||
<Compile Include="Profiles\Lua\Modules\Gui\LuaLabel.cs" />
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaLabel.cs" />
|
||||||
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaSlider.cs" />
|
||||||
<Compile Include="Profiles\Lua\Modules\Gui\LuaTextBox.cs" />
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaTextBox.cs" />
|
||||||
<Compile Include="Profiles\Lua\Modules\Gui\LuaWindowView.xaml.cs">
|
<Compile Include="Profiles\Lua\Modules\Gui\LuaWindowView.xaml.cs">
|
||||||
<DependentUpon>LuaWindowView.xaml</DependentUpon>
|
<DependentUpon>LuaWindowView.xaml</DependentUpon>
|
||||||
@ -801,6 +803,17 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Artemis.toc" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Core.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceAddon-3.0\AceAddon-3.0.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceComm-3.0\AceComm-3.0.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceComm-3.0\ChatThrottleLib.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceConsole-3.0\AceConsole-3.0.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceEvent-3.0\AceEvent-3.0.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceTimer-3.0\AceTimer-3.0.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\json.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\LibStub\LibStub.lua" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\wow-addon.zip" />
|
||||||
<None Include="NLog.xsd">
|
<None Include="NLog.xsd">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</None>
|
</None>
|
||||||
@ -880,6 +893,10 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Modules\Games\FormulaOne2017\FormulaOne2017View.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
<Page Include="Modules\Games\Terraria\TerrariaView.xaml">
|
<Page Include="Modules\Games\Terraria\TerrariaView.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -1075,6 +1092,13 @@
|
|||||||
<None Include="Modules\Games\EurotruckSimulator2\Resources\Win64\ets2-telemetry-server.dll" />
|
<None Include="Modules\Games\EurotruckSimulator2\Resources\Win64\ets2-telemetry-server.dll" />
|
||||||
<None Include="Resources\audio.png" />
|
<None Include="Resources\audio.png" />
|
||||||
<None Include="Resources\ambilight.png" />
|
<None Include="Resources\ambilight.png" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\embeds.xml" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceAddon-3.0\AceAddon-3.0.xml" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceComm-3.0\AceComm-3.0.xml" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceConsole-3.0\AceConsole-3.0.xml" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceEvent-3.0\AceEvent-3.0.xml" />
|
||||||
|
<None Include="Modules\Games\WoW\Resources\Addon source\Libs\AceTimer-3.0\AceTimer-3.0.xml" />
|
||||||
|
<Resource Include="Resources\Artemis autorun.xml" />
|
||||||
<Content Include="Resources\CounterStrike\csgoGamestateConfiguration.txt" />
|
<Content Include="Resources\CounterStrike\csgoGamestateConfiguration.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -1096,15 +1120,16 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Folder Include="Modules\General\GeneralProfile\Discord\" />
|
||||||
<Folder Include="Resources\Lua\" />
|
<Folder Include="Resources\Lua\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="..\packages\CUE.NET.1.1.3.0\build\net45\CUE.NET.targets" Condition="Exists('..\packages\CUE.NET.1.1.3.0\build\net45\CUE.NET.targets')" />
|
<Import Project="..\packages\CUE.NET.1.1.3.1\build\net45\CUE.NET.targets" Condition="Exists('..\packages\CUE.NET.1.1.3.1\build\net45\CUE.NET.targets')" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('..\packages\CUE.NET.1.1.3.0\build\net45\CUE.NET.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CUE.NET.1.1.3.0\build\net45\CUE.NET.targets'))" />
|
<Error Condition="!Exists('..\packages\CUE.NET.1.1.3.1\build\net45\CUE.NET.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CUE.NET.1.1.3.1\build\net45\CUE.NET.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@ -18,8 +15,6 @@ using Artemis.ViewModels;
|
|||||||
using Caliburn.Micro;
|
using Caliburn.Micro;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using NLog;
|
|
||||||
using LogManager = NLog.LogManager;
|
|
||||||
|
|
||||||
namespace Artemis
|
namespace Artemis
|
||||||
{
|
{
|
||||||
@ -57,10 +52,10 @@ namespace Artemis
|
|||||||
var e = ctx.EventArgs as MouseEventArgs;
|
var e = ctx.EventArgs as MouseEventArgs;
|
||||||
|
|
||||||
// If there is an image control, get the scaled position
|
// If there is an image control, get the scaled position
|
||||||
if ((img != null) && (e != null))
|
if (img != null && e != null)
|
||||||
{
|
{
|
||||||
var position = e.GetPosition(img);
|
var position = e.GetPosition(img);
|
||||||
return (int) (img.Source.Width*(position.X/img.ActualWidth));
|
return (int) (img.Source.Width * (position.X / img.ActualWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is another type of of IInputControl get the non-scaled position - or do some processing to get a scaled position, whatever needs to happen
|
// If there is another type of of IInputControl get the non-scaled position - or do some processing to get a scaled position, whatever needs to happen
|
||||||
@ -77,14 +72,14 @@ namespace Artemis
|
|||||||
var e = ctx.EventArgs as MouseEventArgs;
|
var e = ctx.EventArgs as MouseEventArgs;
|
||||||
|
|
||||||
// If there is an image control, get the scaled position
|
// If there is an image control, get the scaled position
|
||||||
if ((img != null) && (e != null))
|
if (img != null && e != null)
|
||||||
{
|
{
|
||||||
var position = e.GetPosition(img);
|
var position = e.GetPosition(img);
|
||||||
return (int) (img.Source.Width*(position.Y/img.ActualWidth));
|
return (int) (img.Source.Width * (position.Y / img.ActualWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is another type of of IInputControl get the non-scaled position - or do some processing to get a scaled position, whatever needs to happen
|
// If there is another type of of IInputControl get the non-scaled position - or do some processing to get a scaled position, whatever needs to happen
|
||||||
if ((e != null) && (input != null))
|
if (e != null && input != null)
|
||||||
return e.GetPosition(input).Y;
|
return e.GetPosition(input).Y;
|
||||||
|
|
||||||
// Return 0 if no processing could be done
|
// Return 0 if no processing could be done
|
||||||
@ -94,14 +89,6 @@ namespace Artemis
|
|||||||
|
|
||||||
protected override void Configure()
|
protected override void Configure()
|
||||||
{
|
{
|
||||||
// Sleep for a while if ran from autorun to allow full system boot
|
|
||||||
if (Environment.GetCommandLineArgs().Contains("--autorun"))
|
|
||||||
{
|
|
||||||
var logger = LogManager.GetCurrentClassLogger();
|
|
||||||
logger.Info("Artemis was run using the autorun shortcut, sleeping for 15 sec.");
|
|
||||||
Thread.Sleep(15000);
|
|
||||||
}
|
|
||||||
|
|
||||||
_kernel = new StandardKernel(new BaseModules(), new ManagerModules());
|
_kernel = new StandardKernel(new BaseModules(), new ManagerModules());
|
||||||
|
|
||||||
_kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
|
_kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
|
||||||
|
|||||||
@ -102,12 +102,16 @@ namespace Artemis.DAL
|
|||||||
if (string.IsNullOrEmpty(name))
|
if (string.IsNullOrEmpty(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove the old profile
|
// Store the profile path before it is renamed
|
||||||
DeleteProfile(profile);
|
var oldPath = ProfileFolder + $@"\{profile.KeyboardSlug}\{profile.GameName}\{profile.Slug}.json";
|
||||||
|
|
||||||
// Update the profile, creating a new file
|
// Update the profile, creating a new file
|
||||||
profile.Name = name;
|
profile.Name = name;
|
||||||
AddOrUpdate(profile);
|
AddOrUpdate(profile);
|
||||||
|
|
||||||
|
// Remove the file with the old name
|
||||||
|
if (File.Exists(oldPath))
|
||||||
|
File.Delete(oldPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DeleteProfile(ProfileModel prof)
|
public static void DeleteProfile(ProfileModel prof)
|
||||||
@ -163,7 +167,8 @@ namespace Artemis.DAL
|
|||||||
var gifDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\gifs";
|
var gifDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\gifs";
|
||||||
Directory.CreateDirectory(gifDir);
|
Directory.CreateDirectory(gifDir);
|
||||||
var gifPath = gifDir + $"\\{fileName}.gif";
|
var gifPath = gifDir + $"\\{fileName}.gif";
|
||||||
gifFile.Save(gifPath);
|
if (!File.Exists(gifPath))
|
||||||
|
gifFile.Save(gifPath);
|
||||||
|
|
||||||
foreach (var profile in profiles)
|
foreach (var profile in profiles)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,9 +23,16 @@ namespace Artemis.DeviceProviders.Corsair
|
|||||||
|
|
||||||
public override bool TryEnable()
|
public override bool TryEnable()
|
||||||
{
|
{
|
||||||
CanUse = CanInitializeSdk();
|
try
|
||||||
if (CanUse && !CueSDK.IsInitialized)
|
{
|
||||||
CueSDK.Initialize();
|
CanUse = CanInitializeSdk();
|
||||||
|
if (CanUse && !CueSDK.IsInitialized)
|
||||||
|
CueSDK.Initialize();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
CanUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Debug("Attempted to enable Corsair headset. CanUse: {0}", CanUse);
|
Logger.Debug("Attempted to enable Corsair headset. CanUse: {0}", CanUse);
|
||||||
|
|
||||||
|
|||||||
@ -128,6 +128,8 @@ namespace Artemis.DeviceProviders.Corsair
|
|||||||
{
|
{
|
||||||
cueLed = _keyboard.Leds.FirstOrDefault(k => k.Id.ToString() == keyCode.ToString()) ??
|
cueLed = _keyboard.Leds.FirstOrDefault(k => k.Id.ToString() == keyCode.ToString()) ??
|
||||||
_keyboard.Leds.FirstOrDefault(k => k.Id == KeyMap.FormsKeys[keyCode]);
|
_keyboard.Leds.FirstOrDefault(k => k.Id == KeyMap.FormsKeys[keyCode]);
|
||||||
|
|
||||||
|
Logger.Trace("Keycode: {0} resolved to CUE LED: {1}", keyCode, cueLed);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,9 +23,16 @@ namespace Artemis.DeviceProviders.Corsair
|
|||||||
|
|
||||||
public override bool TryEnable()
|
public override bool TryEnable()
|
||||||
{
|
{
|
||||||
CanUse = CanInitializeSdk();
|
try
|
||||||
if (CanUse && !CueSDK.IsInitialized)
|
{
|
||||||
CueSDK.Initialize();
|
CanUse = CanInitializeSdk();
|
||||||
|
if (CanUse && !CueSDK.IsInitialized)
|
||||||
|
CueSDK.Initialize();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
CanUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Debug("Attempted to enable Corsair mice. CanUse: {0}", CanUse);
|
Logger.Debug("Attempted to enable Corsair mice. CanUse: {0}", CanUse);
|
||||||
|
|
||||||
|
|||||||
@ -23,9 +23,16 @@ namespace Artemis.DeviceProviders.Corsair
|
|||||||
|
|
||||||
public override bool TryEnable()
|
public override bool TryEnable()
|
||||||
{
|
{
|
||||||
CanUse = CanInitializeSdk();
|
try
|
||||||
if (CanUse && !CueSDK.IsInitialized)
|
{
|
||||||
CueSDK.Initialize();
|
CanUse = CanInitializeSdk();
|
||||||
|
if (CanUse && !CueSDK.IsInitialized)
|
||||||
|
CueSDK.Initialize();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
CanUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Debug("Attempted to enable Corsair mousemat. CanUse: {0}", CanUse);
|
Logger.Debug("Attempted to enable Corsair mousemat. CanUse: {0}", CanUse);
|
||||||
|
|
||||||
|
|||||||
@ -23,9 +23,9 @@ namespace Artemis.DeviceProviders.Corsair.Utilities
|
|||||||
{Keys.Capital, CorsairLedId.CapsLock},
|
{Keys.Capital, CorsairLedId.CapsLock},
|
||||||
{Keys.Oem1, CorsairLedId.SemicolonAndColon},
|
{Keys.Oem1, CorsairLedId.SemicolonAndColon},
|
||||||
{Keys.Oem7, CorsairLedId.ApostropheAndDoubleQuote},
|
{Keys.Oem7, CorsairLedId.ApostropheAndDoubleQuote},
|
||||||
{Keys.OemBackslash, CorsairLedId.Backslash},
|
{Keys.OemBackslash, CorsairLedId.NonUsBackslash},
|
||||||
{Keys.LShiftKey, CorsairLedId.LeftShift},
|
{Keys.LShiftKey, CorsairLedId.LeftShift},
|
||||||
{Keys.Oem5, CorsairLedId.NonUsBackslash},
|
{Keys.Oem5, CorsairLedId.NonUsTilde},
|
||||||
{Keys.Oemcomma, CorsairLedId.CommaAndLessThan},
|
{Keys.Oemcomma, CorsairLedId.CommaAndLessThan},
|
||||||
{Keys.OemPeriod, CorsairLedId.PeriodAndBiggerThan},
|
{Keys.OemPeriod, CorsairLedId.PeriodAndBiggerThan},
|
||||||
{Keys.OemQuestion, CorsairLedId.SlashAndQuestionMark},
|
{Keys.OemQuestion, CorsairLedId.SlashAndQuestionMark},
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Artemis.DAL;
|
using Artemis.DAL;
|
||||||
@ -20,23 +21,68 @@ namespace Artemis.DeviceProviders.Logitech
|
|||||||
"Please check your cables and updating the Logitech Gaming Software\n" +
|
"Please check your cables and updating the Logitech Gaming Software\n" +
|
||||||
"A minimum version of 8.81.15 is required.\n\n" +
|
"A minimum version of 8.81.15 is required.\n\n" +
|
||||||
"If needed, you can select a different keyboard in Artemis under settings.";
|
"If needed, you can select a different keyboard in Artemis under settings.";
|
||||||
Height = 6;
|
Height = 7;
|
||||||
Width = 21;
|
Width = 21;
|
||||||
PreviewSettings = new PreviewSettings(new Rect(19, 70, 1010, 269), Resources.g810);
|
PreviewSettings = new PreviewSettings(new Rect(19, 36, 1010, 304), Resources.g810);
|
||||||
_generalSettings = SettingsProvider.Load<GeneralSettings>();
|
_generalSettings = SettingsProvider.Load<GeneralSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The G910 also updates the G-logo, G-badge and G-keys
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bitmap"></param>
|
||||||
|
public override void DrawBitmap(Bitmap bitmap)
|
||||||
|
{
|
||||||
|
using (var croppedBitmap = new Bitmap(21 * 4, 6 * 4))
|
||||||
|
{
|
||||||
|
// Deal with non-standard DPI
|
||||||
|
croppedBitmap.SetResolution(96, 96);
|
||||||
|
// Don't forget that the image is upscaled 4 times
|
||||||
|
using (var g = Graphics.FromImage(croppedBitmap))
|
||||||
|
{
|
||||||
|
g.DrawImage(bitmap, new Rectangle(0, 0, 84, 24), new Rectangle(4, 4, 84, 24), GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_PERKEY_RGB);
|
||||||
|
// TODO: Remapping
|
||||||
|
LogitechGSDK.LogiLedSetLightingFromBitmap(OrionUtilities.BitmapToByteArray(bitmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var resized = OrionUtilities.ResizeImage(bitmap, 21, 7))
|
||||||
|
{
|
||||||
|
// Color G-logo, lets also try some other values to see what happens
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_LOGO, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_BADGE, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_1, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_2, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_3, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_4, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_5, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_6, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_7, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_8, 0, 0);
|
||||||
|
SetLogitechColorFromCoordinates(resized, KeyboardNames.G_9, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override KeyMatch? GetKeyPosition(Keys keyCode)
|
public override KeyMatch? GetKeyPosition(Keys keyCode)
|
||||||
{
|
{
|
||||||
|
KeyMatch value;
|
||||||
switch (_generalSettings.Layout)
|
switch (_generalSettings.Layout)
|
||||||
{
|
{
|
||||||
case "Qwerty":
|
case "Qwerty":
|
||||||
return KeyMap.QwertyLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
value = KeyMap.QwertyLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
||||||
|
break;
|
||||||
case "Qwertz":
|
case "Qwertz":
|
||||||
return KeyMap.QwertzLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
value = KeyMap.QwertzLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return KeyMap.AzertyLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
value = KeyMap.AzertyLayout.FirstOrDefault(k => k.KeyCode == keyCode);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the distance by 1 on y for the G810
|
||||||
|
return new KeyMatch(value.KeyCode, value.X, value.Y + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +64,8 @@ namespace Artemis.DeviceProviders.Logitech
|
|||||||
g.DrawImage(bitmap, new Rectangle(0, 0, 84, 24), new Rectangle(4, 4, 84, 24), GraphicsUnit.Pixel);
|
g.DrawImage(bitmap, new Rectangle(0, 0, 84, 24), new Rectangle(4, 4, 84, 24), GraphicsUnit.Pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.DrawBitmap(croppedBitmap);
|
LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_PERKEY_RGB);
|
||||||
|
LogitechGSDK.LogiLedSetLightingFromBitmap(OrionUtilities.BitmapToByteArray(bitmap, G910Keymappings));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var resized = OrionUtilities.ResizeImage(bitmap, 22, 7))
|
using (var resized = OrionUtilities.ResizeImage(bitmap, 22, 7))
|
||||||
@ -88,14 +89,147 @@ namespace Artemis.DeviceProviders.Logitech
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetLogitechColorFromCoordinates(Bitmap bitmap, KeyboardNames key, int x, int y)
|
// These mappings are used by the G910 to fix alignments
|
||||||
|
public static OrionUtilities.KeyMapping[] G910Keymappings =
|
||||||
{
|
{
|
||||||
var color = bitmap.GetPixel(x, y);
|
// First row
|
||||||
var rPer = (int) Math.Round(color.R / 2.55);
|
new OrionUtilities.KeyMapping(0, 0),
|
||||||
var gPer = (int) Math.Round(color.G / 2.55);
|
new OrionUtilities.KeyMapping(1, 1),
|
||||||
var bPer = (int) Math.Round(color.B / 2.55);
|
new OrionUtilities.KeyMapping(2, 1),
|
||||||
|
new OrionUtilities.KeyMapping(3, 2),
|
||||||
|
new OrionUtilities.KeyMapping(4, 3),
|
||||||
|
new OrionUtilities.KeyMapping(5, 4),
|
||||||
|
new OrionUtilities.KeyMapping(6, 5),
|
||||||
|
new OrionUtilities.KeyMapping(7, 6),
|
||||||
|
new OrionUtilities.KeyMapping(8, 7),
|
||||||
|
new OrionUtilities.KeyMapping(9, 8),
|
||||||
|
new OrionUtilities.KeyMapping(10, 9),
|
||||||
|
new OrionUtilities.KeyMapping(11, 9),
|
||||||
|
new OrionUtilities.KeyMapping(12, 10),
|
||||||
|
new OrionUtilities.KeyMapping(13, 11),
|
||||||
|
new OrionUtilities.KeyMapping(13, 12),
|
||||||
|
new OrionUtilities.KeyMapping(14, 13),
|
||||||
|
new OrionUtilities.KeyMapping(15, 14),
|
||||||
|
new OrionUtilities.KeyMapping(16, 15),
|
||||||
|
new OrionUtilities.KeyMapping(17, 16),
|
||||||
|
new OrionUtilities.KeyMapping(18, 17),
|
||||||
|
new OrionUtilities.KeyMapping(19, 18),
|
||||||
|
|
||||||
LogitechGSDK.LogiLedSetLightingForKeyWithKeyName(key, rPer, gPer, bPer);
|
// Second row
|
||||||
}
|
new OrionUtilities.KeyMapping(21, 21),
|
||||||
|
new OrionUtilities.KeyMapping(22, 22),
|
||||||
|
new OrionUtilities.KeyMapping(23, 23),
|
||||||
|
new OrionUtilities.KeyMapping(24, 24),
|
||||||
|
new OrionUtilities.KeyMapping(25, 25),
|
||||||
|
new OrionUtilities.KeyMapping(26, 26),
|
||||||
|
new OrionUtilities.KeyMapping(27, 27),
|
||||||
|
new OrionUtilities.KeyMapping(28, 28),
|
||||||
|
new OrionUtilities.KeyMapping(29, 29),
|
||||||
|
new OrionUtilities.KeyMapping(30, 30),
|
||||||
|
new OrionUtilities.KeyMapping(31, 31),
|
||||||
|
new OrionUtilities.KeyMapping(32, 32),
|
||||||
|
new OrionUtilities.KeyMapping(33, 33),
|
||||||
|
new OrionUtilities.KeyMapping(34, 34),
|
||||||
|
new OrionUtilities.KeyMapping(35, 35),
|
||||||
|
new OrionUtilities.KeyMapping(36, 36),
|
||||||
|
new OrionUtilities.KeyMapping(37, 37),
|
||||||
|
new OrionUtilities.KeyMapping(38, 38),
|
||||||
|
new OrionUtilities.KeyMapping(39, 39),
|
||||||
|
new OrionUtilities.KeyMapping(40, 40),
|
||||||
|
new OrionUtilities.KeyMapping(41, 41),
|
||||||
|
|
||||||
|
// Third row
|
||||||
|
new OrionUtilities.KeyMapping(42, 42),
|
||||||
|
new OrionUtilities.KeyMapping(43, 43),
|
||||||
|
new OrionUtilities.KeyMapping(44, 44),
|
||||||
|
new OrionUtilities.KeyMapping(45, 45),
|
||||||
|
new OrionUtilities.KeyMapping(46, 46),
|
||||||
|
new OrionUtilities.KeyMapping(47, 46),
|
||||||
|
new OrionUtilities.KeyMapping(48, 47),
|
||||||
|
new OrionUtilities.KeyMapping(49, 48),
|
||||||
|
new OrionUtilities.KeyMapping(50, 49),
|
||||||
|
new OrionUtilities.KeyMapping(51, 50),
|
||||||
|
new OrionUtilities.KeyMapping(52, 51),
|
||||||
|
new OrionUtilities.KeyMapping(53, 52),
|
||||||
|
new OrionUtilities.KeyMapping(54, 53),
|
||||||
|
new OrionUtilities.KeyMapping(54, 54),
|
||||||
|
new OrionUtilities.KeyMapping(55, 55),
|
||||||
|
new OrionUtilities.KeyMapping(56, 56),
|
||||||
|
new OrionUtilities.KeyMapping(57, 57),
|
||||||
|
new OrionUtilities.KeyMapping(58, 58),
|
||||||
|
new OrionUtilities.KeyMapping(59, 59),
|
||||||
|
new OrionUtilities.KeyMapping(60, 60),
|
||||||
|
new OrionUtilities.KeyMapping(61, 61),
|
||||||
|
new OrionUtilities.KeyMapping(62, 62),
|
||||||
|
|
||||||
|
// Fourth row
|
||||||
|
new OrionUtilities.KeyMapping(63, 63),
|
||||||
|
new OrionUtilities.KeyMapping(64, 64),
|
||||||
|
new OrionUtilities.KeyMapping(65, 65),
|
||||||
|
new OrionUtilities.KeyMapping(66, 65),
|
||||||
|
new OrionUtilities.KeyMapping(67, 66),
|
||||||
|
new OrionUtilities.KeyMapping(68, 67),
|
||||||
|
new OrionUtilities.KeyMapping(69, 68),
|
||||||
|
new OrionUtilities.KeyMapping(70, 69),
|
||||||
|
new OrionUtilities.KeyMapping(71, 70),
|
||||||
|
new OrionUtilities.KeyMapping(72, 71),
|
||||||
|
new OrionUtilities.KeyMapping(73, 72),
|
||||||
|
new OrionUtilities.KeyMapping(74, 73),
|
||||||
|
new OrionUtilities.KeyMapping(75, 74),
|
||||||
|
new OrionUtilities.KeyMapping(76, 75),
|
||||||
|
new OrionUtilities.KeyMapping(76, 76),
|
||||||
|
new OrionUtilities.KeyMapping(78, 77),
|
||||||
|
new OrionUtilities.KeyMapping(79, 78),
|
||||||
|
new OrionUtilities.KeyMapping(79, 79),
|
||||||
|
new OrionUtilities.KeyMapping(80, 80),
|
||||||
|
new OrionUtilities.KeyMapping(81, 81),
|
||||||
|
new OrionUtilities.KeyMapping(82, 82),
|
||||||
|
|
||||||
|
// Fifth row
|
||||||
|
new OrionUtilities.KeyMapping(84, 84),
|
||||||
|
new OrionUtilities.KeyMapping(85, 85),
|
||||||
|
new OrionUtilities.KeyMapping(86, 86),
|
||||||
|
new OrionUtilities.KeyMapping(87, 87),
|
||||||
|
new OrionUtilities.KeyMapping(88, 88),
|
||||||
|
new OrionUtilities.KeyMapping(89, 89),
|
||||||
|
new OrionUtilities.KeyMapping(90, 90),
|
||||||
|
new OrionUtilities.KeyMapping(91, 91),
|
||||||
|
new OrionUtilities.KeyMapping(92, 92),
|
||||||
|
new OrionUtilities.KeyMapping(93, 93),
|
||||||
|
new OrionUtilities.KeyMapping(94, 94),
|
||||||
|
new OrionUtilities.KeyMapping(95, 95),
|
||||||
|
new OrionUtilities.KeyMapping(96, 96),
|
||||||
|
new OrionUtilities.KeyMapping(97, 97),
|
||||||
|
new OrionUtilities.KeyMapping(98, 98),
|
||||||
|
new OrionUtilities.KeyMapping(99, 99),
|
||||||
|
new OrionUtilities.KeyMapping(100, 100),
|
||||||
|
new OrionUtilities.KeyMapping(101, 101),
|
||||||
|
new OrionUtilities.KeyMapping(102, 102),
|
||||||
|
new OrionUtilities.KeyMapping(103, 103),
|
||||||
|
new OrionUtilities.KeyMapping(104, 104),
|
||||||
|
|
||||||
|
// Sixth row
|
||||||
|
new OrionUtilities.KeyMapping(105, 105),
|
||||||
|
new OrionUtilities.KeyMapping(106, 106),
|
||||||
|
new OrionUtilities.KeyMapping(107, 107),
|
||||||
|
new OrionUtilities.KeyMapping(108, 107),
|
||||||
|
new OrionUtilities.KeyMapping(109, 109),
|
||||||
|
new OrionUtilities.KeyMapping(110, 110),
|
||||||
|
new OrionUtilities.KeyMapping(111, 110),
|
||||||
|
new OrionUtilities.KeyMapping(112, 111),
|
||||||
|
new OrionUtilities.KeyMapping(113, 112),
|
||||||
|
new OrionUtilities.KeyMapping(114, 113),
|
||||||
|
new OrionUtilities.KeyMapping(115, 114),
|
||||||
|
new OrionUtilities.KeyMapping(116, 115),
|
||||||
|
new OrionUtilities.KeyMapping(115, 116), // ALTGR
|
||||||
|
new OrionUtilities.KeyMapping(116, 117),
|
||||||
|
new OrionUtilities.KeyMapping(117, 118),
|
||||||
|
new OrionUtilities.KeyMapping(118, 119),
|
||||||
|
new OrionUtilities.KeyMapping(119, 120),
|
||||||
|
new OrionUtilities.KeyMapping(120, 121),
|
||||||
|
new OrionUtilities.KeyMapping(121, 122),
|
||||||
|
new OrionUtilities.KeyMapping(122, 123),
|
||||||
|
new OrionUtilities.KeyMapping(124, 124)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,55 +1,55 @@
|
|||||||
using System;
|
//using System;
|
||||||
using System.Drawing;
|
//using System.Drawing;
|
||||||
using Artemis.DeviceProviders.Logitech.Utilities;
|
//using Artemis.DeviceProviders.Logitech.Utilities;
|
||||||
using Ninject.Extensions.Logging;
|
//using Ninject.Extensions.Logging;
|
||||||
|
//
|
||||||
namespace Artemis.DeviceProviders.Logitech
|
//namespace Artemis.DeviceProviders.Logitech
|
||||||
{
|
//{
|
||||||
// TODO: Handle shutdown, maybe implement Disable() afterall?
|
// // TODO: Handle shutdown, maybe implement Disable() afterall?
|
||||||
public class LogitechGeneric : DeviceProvider
|
// public class LogitechGeneric : DeviceProvider
|
||||||
{
|
// {
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// A generic Logitech DeviceProvider. Because the Logitech SDK currently doesn't allow specific
|
// /// A generic Logitech DeviceProvider. Because the Logitech SDK currently doesn't allow specific
|
||||||
/// device targeting (only very broad per-key-RGB and full RGB etc..)
|
// /// device targeting (only very broad per-key-RGB and full RGB etc..)
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
public LogitechGeneric(ILogger logger)
|
// public LogitechGeneric(ILogger logger)
|
||||||
{
|
// {
|
||||||
Logger = logger;
|
// Logger = logger;
|
||||||
Type = DeviceType.Generic;
|
// Type = DeviceType.Generic;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public ILogger Logger { get; set; }
|
// public ILogger Logger { get; set; }
|
||||||
|
//
|
||||||
public override void UpdateDevice(Bitmap bitmap)
|
// public override void UpdateDevice(Bitmap bitmap)
|
||||||
{
|
// {
|
||||||
if (!CanUse || bitmap == null)
|
// if (!CanUse || bitmap == null)
|
||||||
return;
|
// return;
|
||||||
|
//
|
||||||
var col = bitmap.GetPixel(bitmap.Width/2, bitmap.Height/2);
|
// var col = bitmap.GetPixel(bitmap.Width/2, bitmap.Height/2);
|
||||||
LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_RGB);
|
// LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_RGB);
|
||||||
LogitechGSDK.LogiLedSetLighting((int) (col.R/2.55), (int) (col.G/2.55), (int) (col.B/2.55));
|
// LogitechGSDK.LogiLedSetLighting((int) (col.R/2.55), (int) (col.G/2.55), (int) (col.B/2.55));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public override bool TryEnable()
|
// public override bool TryEnable()
|
||||||
{
|
// {
|
||||||
var majorNum = 0;
|
// var majorNum = 0;
|
||||||
var minorNum = 0;
|
// var minorNum = 0;
|
||||||
var buildNum = 0;
|
// var buildNum = 0;
|
||||||
|
//
|
||||||
LogitechGSDK.LogiLedInit();
|
// LogitechGSDK.LogiLedInit();
|
||||||
LogitechGSDK.LogiLedGetSdkVersion(ref majorNum, ref minorNum, ref buildNum);
|
// LogitechGSDK.LogiLedGetSdkVersion(ref majorNum, ref minorNum, ref buildNum);
|
||||||
|
//
|
||||||
// Turn it into one long number...
|
// // Turn it into one long number...
|
||||||
var version = int.Parse($"{majorNum}{minorNum}{buildNum}");
|
// var version = int.Parse($"{majorNum}{minorNum}{buildNum}");
|
||||||
CanUse = version >= 88115;
|
// CanUse = version >= 88115;
|
||||||
Logger.Debug("Attempted to enable Logitech generic device. CanUse: {0}", CanUse);
|
// Logger.Debug("Attempted to enable Logitech generic device. CanUse: {0}", CanUse);
|
||||||
|
//
|
||||||
return CanUse;
|
// return CanUse;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public override void Disable()
|
// public override void Disable()
|
||||||
{
|
// {
|
||||||
throw new NotSupportedException("Can only disable a keyboard");
|
// throw new NotSupportedException("Can only disable a keyboard");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Artemis.DeviceProviders.Logitech.Utilities;
|
using Artemis.DeviceProviders.Logitech.Utilities;
|
||||||
using Artemis.Utilities.DataReaders;
|
using Artemis.Utilities.DataReaders;
|
||||||
@ -13,17 +14,6 @@ namespace Artemis.DeviceProviders.Logitech
|
|||||||
// Just to be sure, restore the Logitech DLL registry key
|
// Just to be sure, restore the Logitech DLL registry key
|
||||||
DllManager.RestoreLogitechDll();
|
DllManager.RestoreLogitechDll();
|
||||||
|
|
||||||
// Check to see if VC++ 2012 x64 is installed.
|
|
||||||
if (Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes\Installer\Dependencies\{ca67548a-5ebe-413a-b50c-4b9ceb6d66c6}") == null)
|
|
||||||
{
|
|
||||||
CantEnableText = "Couldn't connect to your Logitech keyboard.\n" +
|
|
||||||
"The Visual C++ 2012 Redistributable v11.0.61030.0 could not be found, which is required.\n" +
|
|
||||||
"Please download it by going to the following URL (link also in wiki):\n\n" +
|
|
||||||
"https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe";
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int majorNum = 0, minorNum = 0, buildNum = 0;
|
int majorNum = 0, minorNum = 0, buildNum = 0;
|
||||||
|
|
||||||
LogitechGSDK.LogiLedInit();
|
LogitechGSDK.LogiLedInit();
|
||||||
@ -58,10 +48,14 @@ namespace Artemis.DeviceProviders.Logitech
|
|||||||
LogitechGSDK.LogiLedShutdown();
|
LogitechGSDK.LogiLedShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawBitmap(Bitmap bitmap)
|
protected void SetLogitechColorFromCoordinates(Bitmap bitmap, KeyboardNames key, int x, int y)
|
||||||
{
|
{
|
||||||
LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_PERKEY_RGB);
|
var color = bitmap.GetPixel(x, y);
|
||||||
LogitechGSDK.LogiLedSetLightingFromBitmap(OrionUtilities.BitmapToByteArray(bitmap));
|
var rPer = (int)Math.Round(color.R / 2.55);
|
||||||
|
var gPer = (int)Math.Round(color.G / 2.55);
|
||||||
|
var bPer = (int)Math.Round(color.B / 2.55);
|
||||||
|
|
||||||
|
LogitechGSDK.LogiLedSetLightingForKeyWithKeyName(key, rPer, gPer, bPer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,7 +132,7 @@ namespace Artemis.DeviceProviders.Logitech.Utilities
|
|||||||
new KeyMatch(Keys.Decimal, 19, 5)
|
new KeyMatch(Keys.Decimal, 19, 5)
|
||||||
};
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Qwertz
|
#region Qwertz
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ namespace Artemis.DeviceProviders.Logitech.Utilities
|
|||||||
new KeyMatch(Keys.Decimal, 19, 5)
|
new KeyMatch(Keys.Decimal, 19, 5)
|
||||||
};
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Azerty
|
#region Azerty
|
||||||
|
|
||||||
|
|||||||
392
Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyMapG810.cs
Normal file
392
Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyMapG810.cs
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace Artemis.DeviceProviders.Logitech.Utilities
|
||||||
|
{
|
||||||
|
public static class KeyMapG810
|
||||||
|
{
|
||||||
|
static KeyMapG810()
|
||||||
|
{
|
||||||
|
// There are several keyboard layouts
|
||||||
|
|
||||||
|
#region Qwerty
|
||||||
|
|
||||||
|
QwertyLayout = new List<KeyMatch>
|
||||||
|
{
|
||||||
|
// Row 1
|
||||||
|
new KeyMatch(Keys.Escape, 0, 0),
|
||||||
|
new KeyMatch(Keys.F1, 2, 0),
|
||||||
|
new KeyMatch(Keys.F2, 3, 0),
|
||||||
|
new KeyMatch(Keys.F3, 4, 0),
|
||||||
|
new KeyMatch(Keys.F4, 5, 0),
|
||||||
|
new KeyMatch(Keys.F5, 6, 0),
|
||||||
|
new KeyMatch(Keys.F6, 7, 0),
|
||||||
|
new KeyMatch(Keys.F7, 8, 0),
|
||||||
|
new KeyMatch(Keys.F8, 9, 0),
|
||||||
|
new KeyMatch(Keys.F9, 11, 0),
|
||||||
|
new KeyMatch(Keys.F10, 12, 0),
|
||||||
|
new KeyMatch(Keys.F11, 13, 0),
|
||||||
|
new KeyMatch(Keys.F12, 14, 0),
|
||||||
|
new KeyMatch(Keys.PrintScreen, 15, 0),
|
||||||
|
new KeyMatch(Keys.Scroll, 16, 0),
|
||||||
|
new KeyMatch(Keys.Pause, 17, 0),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
new KeyMatch(Keys.Oemtilde, 0, 1),
|
||||||
|
new KeyMatch(Keys.D1, 1, 1),
|
||||||
|
new KeyMatch(Keys.D2, 2, 1),
|
||||||
|
new KeyMatch(Keys.D3, 3, 1),
|
||||||
|
new KeyMatch(Keys.D4, 4, 1),
|
||||||
|
new KeyMatch(Keys.D5, 5, 1),
|
||||||
|
new KeyMatch(Keys.D6, 6, 1),
|
||||||
|
new KeyMatch(Keys.D7, 7, 1),
|
||||||
|
new KeyMatch(Keys.D8, 8, 1),
|
||||||
|
new KeyMatch(Keys.D9, 9, 1),
|
||||||
|
new KeyMatch(Keys.D0, 10, 1),
|
||||||
|
new KeyMatch(Keys.OemMinus, 11, 1),
|
||||||
|
new KeyMatch(Keys.Oemplus, 12, 1),
|
||||||
|
new KeyMatch(Keys.Back, 13, 1),
|
||||||
|
new KeyMatch(Keys.Insert, 14, 1),
|
||||||
|
new KeyMatch(Keys.Home, 15, 1),
|
||||||
|
new KeyMatch(Keys.PageUp, 16, 1),
|
||||||
|
new KeyMatch(Keys.NumLock, 17, 1),
|
||||||
|
new KeyMatch(Keys.Divide, 18, 1),
|
||||||
|
new KeyMatch(Keys.Multiply, 19, 1),
|
||||||
|
new KeyMatch(Keys.Subtract, 20, 1),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
new KeyMatch(Keys.Tab, 0, 2),
|
||||||
|
new KeyMatch(Keys.Q, 1, 2),
|
||||||
|
new KeyMatch(Keys.W, 2, 2),
|
||||||
|
new KeyMatch(Keys.E, 3, 2),
|
||||||
|
new KeyMatch(Keys.R, 5, 2),
|
||||||
|
new KeyMatch(Keys.T, 6, 2),
|
||||||
|
new KeyMatch(Keys.Y, 7, 2),
|
||||||
|
new KeyMatch(Keys.U, 8, 2),
|
||||||
|
new KeyMatch(Keys.I, 9, 2),
|
||||||
|
new KeyMatch(Keys.O, 10, 2),
|
||||||
|
new KeyMatch(Keys.P, 11, 2),
|
||||||
|
new KeyMatch(Keys.OemOpenBrackets, 12, 2),
|
||||||
|
new KeyMatch(Keys.Oem6, 13, 2),
|
||||||
|
new KeyMatch(Keys.Delete, 14, 2),
|
||||||
|
new KeyMatch(Keys.End, 15, 2),
|
||||||
|
new KeyMatch(Keys.Next, 16, 2),
|
||||||
|
new KeyMatch(Keys.NumPad7, 17, 2),
|
||||||
|
new KeyMatch(Keys.NumPad8, 18, 2),
|
||||||
|
new KeyMatch(Keys.NumPad9, 19, 2),
|
||||||
|
new KeyMatch(Keys.Add, 20, 2),
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
new KeyMatch(Keys.Capital, 0, 3),
|
||||||
|
new KeyMatch(Keys.A, 1, 3),
|
||||||
|
new KeyMatch(Keys.S, 3, 3),
|
||||||
|
new KeyMatch(Keys.D, 4, 3),
|
||||||
|
new KeyMatch(Keys.F, 5, 3),
|
||||||
|
new KeyMatch(Keys.G, 6, 3),
|
||||||
|
new KeyMatch(Keys.H, 7, 3),
|
||||||
|
new KeyMatch(Keys.J, 8, 3),
|
||||||
|
new KeyMatch(Keys.K, 9, 3),
|
||||||
|
new KeyMatch(Keys.L, 10, 3),
|
||||||
|
new KeyMatch(Keys.Oem1, 11, 3),
|
||||||
|
new KeyMatch(Keys.Oem7, 12, 3),
|
||||||
|
new KeyMatch(Keys.Oem5, 13, 3),
|
||||||
|
new KeyMatch(Keys.Return, 14, 3),
|
||||||
|
new KeyMatch(Keys.NumPad4, 17, 3),
|
||||||
|
new KeyMatch(Keys.NumPad5, 18, 3),
|
||||||
|
new KeyMatch(Keys.NumPad6, 19, 3),
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
new KeyMatch(Keys.LShiftKey, 1, 4),
|
||||||
|
new KeyMatch(Keys.OemBackslash, 2, 4),
|
||||||
|
new KeyMatch(Keys.Z, 2, 4),
|
||||||
|
new KeyMatch(Keys.X, 3, 4),
|
||||||
|
new KeyMatch(Keys.C, 4, 4),
|
||||||
|
new KeyMatch(Keys.V, 5, 4),
|
||||||
|
new KeyMatch(Keys.B, 6, 4),
|
||||||
|
new KeyMatch(Keys.N, 7, 4),
|
||||||
|
new KeyMatch(Keys.M, 8, 4),
|
||||||
|
new KeyMatch(Keys.Oemcomma, 9, 4),
|
||||||
|
new KeyMatch(Keys.OemPeriod, 10, 4),
|
||||||
|
new KeyMatch(Keys.OemQuestion, 11, 4),
|
||||||
|
new KeyMatch(Keys.RShiftKey, 13, 4),
|
||||||
|
new KeyMatch(Keys.Up, 15, 4),
|
||||||
|
new KeyMatch(Keys.NumPad1, 17, 4),
|
||||||
|
new KeyMatch(Keys.NumPad2, 18, 4),
|
||||||
|
new KeyMatch(Keys.NumPad3, 19, 4),
|
||||||
|
// Both returns return "Return" (Yes...)
|
||||||
|
// new OrionKey(System.Windows.Forms.Keys.Return, 20, 4),
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
new KeyMatch(Keys.LControlKey, 0, 5),
|
||||||
|
new KeyMatch(Keys.LWin, 1, 5),
|
||||||
|
new KeyMatch(Keys.LMenu, 3, 5),
|
||||||
|
new KeyMatch(Keys.Space, 6, 5),
|
||||||
|
new KeyMatch(Keys.RMenu, 11, 5),
|
||||||
|
new KeyMatch(Keys.RWin, 12, 5),
|
||||||
|
new KeyMatch(Keys.Apps, 13, 5),
|
||||||
|
new KeyMatch(Keys.RControlKey, 14, 5),
|
||||||
|
new KeyMatch(Keys.Left, 15, 5),
|
||||||
|
new KeyMatch(Keys.Down, 16, 5),
|
||||||
|
new KeyMatch(Keys.Right, 17, 5),
|
||||||
|
new KeyMatch(Keys.NumPad0, 18, 5),
|
||||||
|
new KeyMatch(Keys.Decimal, 19, 5)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Qwertz
|
||||||
|
|
||||||
|
QwertzLayout = new List<KeyMatch>
|
||||||
|
{
|
||||||
|
// Row 1
|
||||||
|
new KeyMatch(Keys.Escape, 0, 0),
|
||||||
|
new KeyMatch(Keys.F1, 2, 0),
|
||||||
|
new KeyMatch(Keys.F2, 3, 0),
|
||||||
|
new KeyMatch(Keys.F3, 4, 0),
|
||||||
|
new KeyMatch(Keys.F4, 5, 0),
|
||||||
|
new KeyMatch(Keys.F5, 6, 0),
|
||||||
|
new KeyMatch(Keys.F6, 7, 0),
|
||||||
|
new KeyMatch(Keys.F7, 8, 0),
|
||||||
|
new KeyMatch(Keys.F8, 9, 0),
|
||||||
|
new KeyMatch(Keys.F9, 11, 0),
|
||||||
|
new KeyMatch(Keys.F10, 12, 0), // returns 'None'
|
||||||
|
new KeyMatch(Keys.F11, 13, 0),
|
||||||
|
new KeyMatch(Keys.F12, 14, 0),
|
||||||
|
new KeyMatch(Keys.PrintScreen, 15, 0),
|
||||||
|
new KeyMatch(Keys.Scroll, 16, 0),
|
||||||
|
new KeyMatch(Keys.Pause, 17, 0),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
new KeyMatch(Keys.Oem5, 0, 1),
|
||||||
|
new KeyMatch(Keys.D1, 1, 1),
|
||||||
|
new KeyMatch(Keys.D2, 2, 1),
|
||||||
|
new KeyMatch(Keys.D3, 3, 1),
|
||||||
|
new KeyMatch(Keys.D4, 4, 1),
|
||||||
|
new KeyMatch(Keys.D5, 5, 1),
|
||||||
|
new KeyMatch(Keys.D6, 6, 1),
|
||||||
|
new KeyMatch(Keys.D7, 7, 1),
|
||||||
|
new KeyMatch(Keys.D8, 8, 1),
|
||||||
|
new KeyMatch(Keys.D9, 9, 1),
|
||||||
|
new KeyMatch(Keys.D0, 10, 1),
|
||||||
|
new KeyMatch(Keys.OemOpenBrackets, 11, 1),
|
||||||
|
new KeyMatch(Keys.Oem6, 12, 1),
|
||||||
|
new KeyMatch(Keys.Back, 13, 1),
|
||||||
|
new KeyMatch(Keys.Insert, 14, 1),
|
||||||
|
new KeyMatch(Keys.Home, 15, 1),
|
||||||
|
new KeyMatch(Keys.PageUp, 16, 1),
|
||||||
|
new KeyMatch(Keys.NumLock, 17, 1),
|
||||||
|
new KeyMatch(Keys.Divide, 18, 1),
|
||||||
|
new KeyMatch(Keys.Multiply, 19, 1),
|
||||||
|
new KeyMatch(Keys.Subtract, 20, 1),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
new KeyMatch(Keys.Tab, 0, 2),
|
||||||
|
new KeyMatch(Keys.Q, 1, 2),
|
||||||
|
new KeyMatch(Keys.W, 2, 2),
|
||||||
|
new KeyMatch(Keys.E, 3, 2),
|
||||||
|
new KeyMatch(Keys.R, 5, 2),
|
||||||
|
new KeyMatch(Keys.T, 6, 2),
|
||||||
|
new KeyMatch(Keys.Z, 7, 2),
|
||||||
|
new KeyMatch(Keys.U, 8, 2),
|
||||||
|
new KeyMatch(Keys.I, 9, 2),
|
||||||
|
new KeyMatch(Keys.O, 10, 2),
|
||||||
|
new KeyMatch(Keys.P, 11, 2),
|
||||||
|
new KeyMatch(Keys.Oem1, 12, 2),
|
||||||
|
new KeyMatch(Keys.Oemplus, 13, 2),
|
||||||
|
new KeyMatch(Keys.Delete, 14, 2),
|
||||||
|
new KeyMatch(Keys.End, 15, 2),
|
||||||
|
new KeyMatch(Keys.Next, 16, 2),
|
||||||
|
new KeyMatch(Keys.NumPad7, 17, 2),
|
||||||
|
new KeyMatch(Keys.NumPad8, 18, 2),
|
||||||
|
new KeyMatch(Keys.NumPad9, 19, 2),
|
||||||
|
new KeyMatch(Keys.Add, 20, 2),
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
new KeyMatch(Keys.Capital, 0, 3),
|
||||||
|
new KeyMatch(Keys.A, 1, 3),
|
||||||
|
new KeyMatch(Keys.S, 3, 3),
|
||||||
|
new KeyMatch(Keys.D, 4, 3),
|
||||||
|
new KeyMatch(Keys.F, 5, 3),
|
||||||
|
new KeyMatch(Keys.G, 6, 3),
|
||||||
|
new KeyMatch(Keys.H, 7, 3),
|
||||||
|
new KeyMatch(Keys.J, 8, 3),
|
||||||
|
new KeyMatch(Keys.K, 9, 3),
|
||||||
|
new KeyMatch(Keys.L, 10, 3),
|
||||||
|
new KeyMatch(Keys.Oemtilde, 11, 3),
|
||||||
|
new KeyMatch(Keys.Oem7, 12, 3),
|
||||||
|
new KeyMatch(Keys.OemQuestion, 13, 3),
|
||||||
|
new KeyMatch(Keys.Return, 14, 3),
|
||||||
|
new KeyMatch(Keys.NumPad4, 17, 3),
|
||||||
|
new KeyMatch(Keys.NumPad5, 18, 3),
|
||||||
|
new KeyMatch(Keys.NumPad6, 19, 3),
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
new KeyMatch(Keys.LShiftKey, 1, 4),
|
||||||
|
new KeyMatch(Keys.OemBackslash, 2, 4),
|
||||||
|
new KeyMatch(Keys.Y, 2, 4),
|
||||||
|
new KeyMatch(Keys.X, 3, 4),
|
||||||
|
new KeyMatch(Keys.C, 4, 4),
|
||||||
|
new KeyMatch(Keys.V, 5, 4),
|
||||||
|
new KeyMatch(Keys.B, 6, 4),
|
||||||
|
new KeyMatch(Keys.N, 7, 4),
|
||||||
|
new KeyMatch(Keys.M, 8, 4),
|
||||||
|
new KeyMatch(Keys.Oemcomma, 9, 4),
|
||||||
|
new KeyMatch(Keys.OemPeriod, 10, 4),
|
||||||
|
new KeyMatch(Keys.OemMinus, 11, 4),
|
||||||
|
new KeyMatch(Keys.RShiftKey, 13, 4),
|
||||||
|
new KeyMatch(Keys.Up, 15, 4),
|
||||||
|
new KeyMatch(Keys.NumPad1, 17, 4),
|
||||||
|
new KeyMatch(Keys.NumPad2, 18, 4),
|
||||||
|
new KeyMatch(Keys.NumPad3, 19, 4),
|
||||||
|
// Both returns return "Return" (Yes...)
|
||||||
|
// new OrionKey(System.Windows.Forms.Keys.Return, 20, 4),
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
new KeyMatch(Keys.LControlKey, 0, 5),
|
||||||
|
new KeyMatch(Keys.LWin, 1, 5),
|
||||||
|
new KeyMatch(Keys.Menu, 3, 5), // returns 'None'
|
||||||
|
new KeyMatch(Keys.Space, 6, 5),
|
||||||
|
new KeyMatch(Keys.RMenu, 11, 5),
|
||||||
|
new KeyMatch(Keys.RWin, 12, 5),
|
||||||
|
new KeyMatch(Keys.Apps, 13, 5),
|
||||||
|
new KeyMatch(Keys.RControlKey, 14, 5),
|
||||||
|
new KeyMatch(Keys.Left, 15, 5),
|
||||||
|
new KeyMatch(Keys.Down, 16, 5),
|
||||||
|
new KeyMatch(Keys.Right, 17, 5),
|
||||||
|
new KeyMatch(Keys.NumPad0, 18, 5),
|
||||||
|
new KeyMatch(Keys.Decimal, 19, 5)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Azerty
|
||||||
|
|
||||||
|
AzertyLayout = new List<KeyMatch>
|
||||||
|
{
|
||||||
|
// Row 1
|
||||||
|
new KeyMatch(Keys.Escape, 0, 0),
|
||||||
|
new KeyMatch(Keys.F1, 2, 0),
|
||||||
|
new KeyMatch(Keys.F2, 3, 0),
|
||||||
|
new KeyMatch(Keys.F3, 4, 0),
|
||||||
|
new KeyMatch(Keys.F4, 5, 0),
|
||||||
|
new KeyMatch(Keys.F5, 6, 0),
|
||||||
|
new KeyMatch(Keys.F6, 7, 0),
|
||||||
|
new KeyMatch(Keys.F7, 8, 0),
|
||||||
|
new KeyMatch(Keys.F8, 9, 0),
|
||||||
|
new KeyMatch(Keys.F9, 11, 0),
|
||||||
|
new KeyMatch(Keys.F10, 12, 0),
|
||||||
|
new KeyMatch(Keys.F11, 13, 0),
|
||||||
|
new KeyMatch(Keys.F12, 14, 0),
|
||||||
|
new KeyMatch(Keys.PrintScreen, 15, 0),
|
||||||
|
new KeyMatch(Keys.Scroll, 16, 0),
|
||||||
|
new KeyMatch(Keys.Pause, 17, 0),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
new KeyMatch(Keys.Oemtilde, 0, 1),
|
||||||
|
new KeyMatch(Keys.D1, 1, 1),
|
||||||
|
new KeyMatch(Keys.D2, 2, 1),
|
||||||
|
new KeyMatch(Keys.D3, 3, 1),
|
||||||
|
new KeyMatch(Keys.D4, 4, 1),
|
||||||
|
new KeyMatch(Keys.D5, 5, 1),
|
||||||
|
new KeyMatch(Keys.D6, 6, 1),
|
||||||
|
new KeyMatch(Keys.D7, 7, 1),
|
||||||
|
new KeyMatch(Keys.D8, 8, 1),
|
||||||
|
new KeyMatch(Keys.D9, 9, 1),
|
||||||
|
new KeyMatch(Keys.D0, 10, 1),
|
||||||
|
new KeyMatch(Keys.OemMinus, 11, 1),
|
||||||
|
new KeyMatch(Keys.Oemplus, 12, 1),
|
||||||
|
new KeyMatch(Keys.Back, 13, 1),
|
||||||
|
new KeyMatch(Keys.Insert, 14, 1),
|
||||||
|
new KeyMatch(Keys.Home, 15, 1),
|
||||||
|
new KeyMatch(Keys.PageUp, 16, 1),
|
||||||
|
new KeyMatch(Keys.NumLock, 17, 1),
|
||||||
|
new KeyMatch(Keys.Divide, 18, 1),
|
||||||
|
new KeyMatch(Keys.Multiply, 19, 1),
|
||||||
|
new KeyMatch(Keys.Subtract, 20, 1),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
new KeyMatch(Keys.Tab, 0, 2),
|
||||||
|
new KeyMatch(Keys.A, 1, 2),
|
||||||
|
new KeyMatch(Keys.Z, 2, 2),
|
||||||
|
new KeyMatch(Keys.E, 3, 2),
|
||||||
|
new KeyMatch(Keys.R, 5, 2),
|
||||||
|
new KeyMatch(Keys.T, 6, 2),
|
||||||
|
new KeyMatch(Keys.Y, 7, 2),
|
||||||
|
new KeyMatch(Keys.U, 8, 2),
|
||||||
|
new KeyMatch(Keys.I, 9, 2),
|
||||||
|
new KeyMatch(Keys.O, 10, 2),
|
||||||
|
new KeyMatch(Keys.P, 11, 2),
|
||||||
|
new KeyMatch(Keys.OemQuotes, 12, 2),
|
||||||
|
new KeyMatch(Keys.Oem6, 13, 2),
|
||||||
|
new KeyMatch(Keys.Delete, 14, 2),
|
||||||
|
new KeyMatch(Keys.End, 15, 2),
|
||||||
|
new KeyMatch(Keys.Next, 16, 2),
|
||||||
|
new KeyMatch(Keys.NumPad7, 17, 2),
|
||||||
|
new KeyMatch(Keys.NumPad8, 18, 2),
|
||||||
|
new KeyMatch(Keys.NumPad9, 19, 2),
|
||||||
|
new KeyMatch(Keys.Add, 20, 2),
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
new KeyMatch(Keys.Capital, 0, 3),
|
||||||
|
new KeyMatch(Keys.Q, 1, 3),
|
||||||
|
new KeyMatch(Keys.S, 3, 3),
|
||||||
|
new KeyMatch(Keys.D, 4, 3),
|
||||||
|
new KeyMatch(Keys.F, 5, 3),
|
||||||
|
new KeyMatch(Keys.G, 6, 3),
|
||||||
|
new KeyMatch(Keys.H, 7, 3),
|
||||||
|
new KeyMatch(Keys.J, 8, 3),
|
||||||
|
new KeyMatch(Keys.K, 9, 3),
|
||||||
|
new KeyMatch(Keys.L, 10, 3),
|
||||||
|
new KeyMatch(Keys.M, 11, 3),
|
||||||
|
new KeyMatch(Keys.Oem7, 12, 3),
|
||||||
|
new KeyMatch(Keys.Oem5, 13, 3),
|
||||||
|
new KeyMatch(Keys.Return, 14, 3),
|
||||||
|
new KeyMatch(Keys.NumPad4, 17, 3),
|
||||||
|
new KeyMatch(Keys.NumPad5, 18, 3),
|
||||||
|
new KeyMatch(Keys.NumPad6, 19, 3),
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
new KeyMatch(Keys.LShiftKey, 1, 4),
|
||||||
|
new KeyMatch(Keys.OemBackslash, 2, 4),
|
||||||
|
new KeyMatch(Keys.W, 2, 4),
|
||||||
|
new KeyMatch(Keys.X, 3, 4),
|
||||||
|
new KeyMatch(Keys.C, 4, 4),
|
||||||
|
new KeyMatch(Keys.V, 5, 4),
|
||||||
|
new KeyMatch(Keys.B, 6, 4),
|
||||||
|
new KeyMatch(Keys.N, 7, 4),
|
||||||
|
new KeyMatch(Keys.OemQuestion, 8, 4),
|
||||||
|
new KeyMatch(Keys.Oemcomma, 9, 4),
|
||||||
|
new KeyMatch(Keys.OemPeriod, 10, 4),
|
||||||
|
new KeyMatch(Keys.OemQuestion, 11, 4),
|
||||||
|
new KeyMatch(Keys.RShiftKey, 13, 4),
|
||||||
|
new KeyMatch(Keys.Up, 15, 4),
|
||||||
|
new KeyMatch(Keys.NumPad1, 17, 4),
|
||||||
|
new KeyMatch(Keys.NumPad2, 18, 4),
|
||||||
|
new KeyMatch(Keys.NumPad3, 19, 4),
|
||||||
|
// Both returns return "Return" (Yes...)
|
||||||
|
// new OrionKey(System.Windows.Forms.Keys.Return, 20, 4),
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
new KeyMatch(Keys.LControlKey, 0, 5),
|
||||||
|
new KeyMatch(Keys.LWin, 1, 5),
|
||||||
|
new KeyMatch(Keys.LMenu, 3, 5),
|
||||||
|
new KeyMatch(Keys.Space, 6, 5),
|
||||||
|
new KeyMatch(Keys.RMenu, 11, 5),
|
||||||
|
new KeyMatch(Keys.RWin, 12, 5),
|
||||||
|
new KeyMatch(Keys.Apps, 13, 5),
|
||||||
|
new KeyMatch(Keys.RControlKey, 14, 5),
|
||||||
|
new KeyMatch(Keys.Left, 15, 5),
|
||||||
|
new KeyMatch(Keys.Down, 16, 5),
|
||||||
|
new KeyMatch(Keys.Right, 17, 5),
|
||||||
|
new KeyMatch(Keys.NumPad0, 18, 5),
|
||||||
|
new KeyMatch(Keys.Decimal, 19, 5)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<KeyMatch> QwertyLayout { get; set; }
|
||||||
|
public static List<KeyMatch> QwertzLayout { get; set; }
|
||||||
|
public static List<KeyMatch> AzertyLayout { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,149 +7,7 @@ namespace Artemis.DeviceProviders.Logitech.Utilities
|
|||||||
{
|
{
|
||||||
public static class OrionUtilities
|
public static class OrionUtilities
|
||||||
{
|
{
|
||||||
public static KeyMapping[] Keymappings =
|
public static byte[] BitmapToByteArray(Bitmap b, KeyMapping[] keymappings = null)
|
||||||
{
|
|
||||||
// First row
|
|
||||||
new KeyMapping(0, 0),
|
|
||||||
new KeyMapping(1, 1),
|
|
||||||
new KeyMapping(2, 1),
|
|
||||||
new KeyMapping(3, 2),
|
|
||||||
new KeyMapping(4, 3),
|
|
||||||
new KeyMapping(5, 4),
|
|
||||||
new KeyMapping(6, 5),
|
|
||||||
new KeyMapping(7, 6),
|
|
||||||
new KeyMapping(8, 7),
|
|
||||||
new KeyMapping(9, 8),
|
|
||||||
new KeyMapping(10, 9),
|
|
||||||
new KeyMapping(11, 9),
|
|
||||||
new KeyMapping(12, 10),
|
|
||||||
new KeyMapping(13, 11),
|
|
||||||
new KeyMapping(13, 12),
|
|
||||||
new KeyMapping(14, 13),
|
|
||||||
new KeyMapping(15, 14),
|
|
||||||
new KeyMapping(16, 15),
|
|
||||||
new KeyMapping(17, 16),
|
|
||||||
new KeyMapping(18, 17),
|
|
||||||
new KeyMapping(19, 18),
|
|
||||||
|
|
||||||
// Second row
|
|
||||||
new KeyMapping(21, 21),
|
|
||||||
new KeyMapping(22, 22),
|
|
||||||
new KeyMapping(23, 23),
|
|
||||||
new KeyMapping(24, 24),
|
|
||||||
new KeyMapping(25, 25),
|
|
||||||
new KeyMapping(26, 26),
|
|
||||||
new KeyMapping(27, 27),
|
|
||||||
new KeyMapping(28, 28),
|
|
||||||
new KeyMapping(29, 29),
|
|
||||||
new KeyMapping(30, 30),
|
|
||||||
new KeyMapping(31, 31),
|
|
||||||
new KeyMapping(32, 32),
|
|
||||||
new KeyMapping(33, 33),
|
|
||||||
new KeyMapping(34, 34),
|
|
||||||
new KeyMapping(35, 35),
|
|
||||||
new KeyMapping(36, 36),
|
|
||||||
new KeyMapping(37, 37),
|
|
||||||
new KeyMapping(38, 38),
|
|
||||||
new KeyMapping(39, 39),
|
|
||||||
new KeyMapping(40, 40),
|
|
||||||
new KeyMapping(41, 41),
|
|
||||||
|
|
||||||
// Third row
|
|
||||||
new KeyMapping(42, 42),
|
|
||||||
new KeyMapping(43, 43),
|
|
||||||
new KeyMapping(44, 44),
|
|
||||||
new KeyMapping(45, 45),
|
|
||||||
new KeyMapping(46, 46),
|
|
||||||
new KeyMapping(47, 46),
|
|
||||||
new KeyMapping(48, 47),
|
|
||||||
new KeyMapping(49, 48),
|
|
||||||
new KeyMapping(50, 49),
|
|
||||||
new KeyMapping(51, 50),
|
|
||||||
new KeyMapping(52, 51),
|
|
||||||
new KeyMapping(53, 52),
|
|
||||||
new KeyMapping(54, 53),
|
|
||||||
new KeyMapping(54, 54),
|
|
||||||
new KeyMapping(55, 55),
|
|
||||||
new KeyMapping(56, 56),
|
|
||||||
new KeyMapping(57, 57),
|
|
||||||
new KeyMapping(58, 58),
|
|
||||||
new KeyMapping(59, 59),
|
|
||||||
new KeyMapping(60, 60),
|
|
||||||
new KeyMapping(61, 61),
|
|
||||||
new KeyMapping(62, 62),
|
|
||||||
|
|
||||||
// Fourth row
|
|
||||||
new KeyMapping(63, 63),
|
|
||||||
new KeyMapping(64, 64),
|
|
||||||
new KeyMapping(65, 65),
|
|
||||||
new KeyMapping(66, 65),
|
|
||||||
new KeyMapping(67, 66),
|
|
||||||
new KeyMapping(68, 67),
|
|
||||||
new KeyMapping(69, 68),
|
|
||||||
new KeyMapping(70, 69),
|
|
||||||
new KeyMapping(71, 70),
|
|
||||||
new KeyMapping(72, 71),
|
|
||||||
new KeyMapping(73, 72),
|
|
||||||
new KeyMapping(74, 73),
|
|
||||||
new KeyMapping(75, 74),
|
|
||||||
new KeyMapping(76, 75),
|
|
||||||
new KeyMapping(76, 76),
|
|
||||||
new KeyMapping(78, 77),
|
|
||||||
new KeyMapping(79, 78),
|
|
||||||
new KeyMapping(79, 79),
|
|
||||||
new KeyMapping(80, 80),
|
|
||||||
new KeyMapping(81, 81),
|
|
||||||
new KeyMapping(82, 82),
|
|
||||||
|
|
||||||
// Fifth row
|
|
||||||
new KeyMapping(84, 84),
|
|
||||||
new KeyMapping(85, 85),
|
|
||||||
new KeyMapping(86, 86),
|
|
||||||
new KeyMapping(87, 87),
|
|
||||||
new KeyMapping(88, 88),
|
|
||||||
new KeyMapping(89, 89),
|
|
||||||
new KeyMapping(90, 90),
|
|
||||||
new KeyMapping(91, 91),
|
|
||||||
new KeyMapping(92, 92),
|
|
||||||
new KeyMapping(93, 93),
|
|
||||||
new KeyMapping(94, 94),
|
|
||||||
new KeyMapping(95, 95),
|
|
||||||
new KeyMapping(96, 96),
|
|
||||||
new KeyMapping(97, 97),
|
|
||||||
new KeyMapping(98, 98),
|
|
||||||
new KeyMapping(99, 99),
|
|
||||||
new KeyMapping(100, 100),
|
|
||||||
new KeyMapping(101, 101),
|
|
||||||
new KeyMapping(102, 102),
|
|
||||||
new KeyMapping(103, 103),
|
|
||||||
new KeyMapping(104, 104),
|
|
||||||
|
|
||||||
// Sixth row
|
|
||||||
new KeyMapping(105, 105),
|
|
||||||
new KeyMapping(106, 106),
|
|
||||||
new KeyMapping(107, 107),
|
|
||||||
new KeyMapping(108, 107),
|
|
||||||
new KeyMapping(109, 109),
|
|
||||||
new KeyMapping(110, 110),
|
|
||||||
new KeyMapping(111, 110),
|
|
||||||
new KeyMapping(112, 111),
|
|
||||||
new KeyMapping(113, 112),
|
|
||||||
new KeyMapping(114, 113),
|
|
||||||
new KeyMapping(115, 114),
|
|
||||||
new KeyMapping(116, 115),
|
|
||||||
new KeyMapping(115, 116), // ALTGR
|
|
||||||
new KeyMapping(116, 117),
|
|
||||||
new KeyMapping(117, 118),
|
|
||||||
new KeyMapping(118, 119),
|
|
||||||
new KeyMapping(119, 120),
|
|
||||||
new KeyMapping(120, 121),
|
|
||||||
new KeyMapping(121, 122),
|
|
||||||
new KeyMapping(122, 123),
|
|
||||||
new KeyMapping(124, 124)
|
|
||||||
};
|
|
||||||
|
|
||||||
public static byte[] BitmapToByteArray(Bitmap b, bool remap = true)
|
|
||||||
{
|
{
|
||||||
if (b.Width > 21 || b.Height > 6)
|
if (b.Width > 21 || b.Height > 6)
|
||||||
b = ResizeImage(b, 21, 6);
|
b = ResizeImage(b, 21, 6);
|
||||||
@ -158,23 +16,23 @@ namespace Artemis.DeviceProviders.Logitech.Utilities
|
|||||||
var bitmapData = b.LockBits(rect, ImageLockMode.ReadWrite, b.PixelFormat);
|
var bitmapData = b.LockBits(rect, ImageLockMode.ReadWrite, b.PixelFormat);
|
||||||
|
|
||||||
var depth = Image.GetPixelFormatSize(b.PixelFormat);
|
var depth = Image.GetPixelFormatSize(b.PixelFormat);
|
||||||
var step = depth/8;
|
var step = depth / 8;
|
||||||
var pixels = new byte[21*6*step];
|
var pixels = new byte[21 * 6 * step];
|
||||||
var iptr = bitmapData.Scan0;
|
var iptr = bitmapData.Scan0;
|
||||||
|
|
||||||
// Copy data from pointer to array
|
// Copy data from pointer to array
|
||||||
Marshal.Copy(iptr, pixels, 0, pixels.Length);
|
Marshal.Copy(iptr, pixels, 0, pixels.Length);
|
||||||
|
|
||||||
if (!remap)
|
if (keymappings == null)
|
||||||
return pixels;
|
return pixels;
|
||||||
|
|
||||||
var remapped = new byte[pixels.Length];
|
var remapped = new byte[pixels.Length];
|
||||||
|
|
||||||
// Every key is 4 bytes
|
// Every key is 4 bytes
|
||||||
for (var i = 0; i <= pixels.Length/4; i++)
|
for (var i = 0; i <= pixels.Length / 4; i++)
|
||||||
{
|
{
|
||||||
var firstSByte = Keymappings[i].Source*4;
|
var firstSByte = keymappings[i].Source * 4;
|
||||||
var firstTByte = Keymappings[i].Target*4;
|
var firstTByte = keymappings[i].Target * 4;
|
||||||
|
|
||||||
for (var j = 0; j < 4; j++)
|
for (var j = 0; j < 4; j++)
|
||||||
remapped[firstTByte + j] = pixels[firstSByte + j];
|
remapped[firstTByte + j] = pixels[firstSByte + j];
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
using Artemis.Managers;
|
using Artemis.Managers;
|
||||||
using Artemis.Models;
|
using Artemis.Models;
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
|
using Artemis.Modules.Games.WoW;
|
||||||
using Artemis.Profiles.Layers.Abstract;
|
using Artemis.Profiles.Layers.Abstract;
|
||||||
using Artemis.Profiles.Layers.Interfaces;
|
using Artemis.Profiles.Layers.Interfaces;
|
||||||
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
|
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
|
||||||
@ -65,7 +66,7 @@ namespace Artemis.InjectionModules
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Effects
|
#region Modules
|
||||||
|
|
||||||
Kernel.Bind(x =>
|
Kernel.Bind(x =>
|
||||||
x.FromThisAssembly()
|
x.FromThisAssembly()
|
||||||
@ -81,6 +82,7 @@ namespace Artemis.InjectionModules
|
|||||||
.BindAllBaseClasses()
|
.BindAllBaseClasses()
|
||||||
.Configure(b => b.InSingletonScope())
|
.Configure(b => b.InSingletonScope())
|
||||||
);
|
);
|
||||||
|
Bind<WowPacketScanner>().ToSelf();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -16,9 +16,12 @@ namespace Artemis.Managers
|
|||||||
{
|
{
|
||||||
private readonly DebugViewModel _debugViewModel;
|
private readonly DebugViewModel _debugViewModel;
|
||||||
private readonly DeviceManager _deviceManager;
|
private readonly DeviceManager _deviceManager;
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
//private readonly Timer _loopTimer;
|
//private readonly Timer _loopTimer;
|
||||||
private readonly Task _loopTask;
|
private readonly Task _loopTask;
|
||||||
|
|
||||||
private readonly ModuleManager _moduleManager;
|
private readonly ModuleManager _moduleManager;
|
||||||
|
|
||||||
public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
|
public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
|
||||||
@ -163,9 +166,7 @@ namespace Artemis.Managers
|
|||||||
var keyboardOnly = !mice.Any() && !headsets.Any() && !generics.Any() && !mousemats.Any();
|
var keyboardOnly = !mice.Any() && !headsets.Any() && !generics.Any() && !mousemats.Any();
|
||||||
|
|
||||||
// Setup the frame for this tick
|
// Setup the frame for this tick
|
||||||
using (
|
using (var frame = new FrameModel(_deviceManager.ActiveKeyboard, mice.Any(), headsets.Any(), generics.Any(), mousemats.Any()))
|
||||||
var frame = new FrameModel(_deviceManager.ActiveKeyboard, mice.Any(), headsets.Any(), generics.Any(),
|
|
||||||
mousemats.Any()))
|
|
||||||
{
|
{
|
||||||
if (renderModule.IsInitialized)
|
if (renderModule.IsInitialized)
|
||||||
renderModule.Render(frame, keyboardOnly);
|
renderModule.Render(frame, keyboardOnly);
|
||||||
|
|||||||
@ -7,7 +7,6 @@ using Artemis.Profiles;
|
|||||||
using Artemis.Profiles.Lua;
|
using Artemis.Profiles.Lua;
|
||||||
using Artemis.Profiles.Lua.Modules;
|
using Artemis.Profiles.Lua.Modules;
|
||||||
using Artemis.Profiles.Lua.Modules.Gui;
|
using Artemis.Profiles.Lua.Modules.Gui;
|
||||||
using Castle.Core.Internal;
|
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Extensions.Logging;
|
using Ninject.Extensions.Logging;
|
||||||
@ -64,7 +63,7 @@ namespace Artemis.Managers
|
|||||||
LuaScript.Globals[luaModule.ModuleName] = luaModule;
|
LuaScript.Globals[luaModule.ModuleName] = luaModule;
|
||||||
|
|
||||||
// If there is no LUA script, don't bother executing the string
|
// If there is no LUA script, don't bother executing the string
|
||||||
if (ProfileModel.LuaScript.IsNullOrEmpty())
|
if (string.IsNullOrEmpty(ProfileModel.LuaScript))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@ -24,10 +24,7 @@ namespace Artemis.Managers
|
|||||||
|
|
||||||
Modules = new List<ModuleModel>(moduleModels.Where(m => !m.IsOverlay && !m.IsBoundToProcess));
|
Modules = new List<ModuleModel>(moduleModels.Where(m => !m.IsOverlay && !m.IsBoundToProcess));
|
||||||
OverlayModules = new List<ModuleModel>(moduleModels.Where(m => m.IsOverlay));
|
OverlayModules = new List<ModuleModel>(moduleModels.Where(m => m.IsOverlay));
|
||||||
// Exclude WoW if needed
|
ProcessModules = new List<ModuleModel>(moduleModels.Where(m => m.IsBoundToProcess));
|
||||||
ProcessModules = _generalSettings.GamestatePort == 62575
|
|
||||||
? new List<ModuleModel>(moduleModels.Where(m => m.IsBoundToProcess))
|
|
||||||
: new List<ModuleModel>(moduleModels.Where(m => m.IsBoundToProcess && m.Name != "WoW"));
|
|
||||||
|
|
||||||
_logger.Info("Intialized ModuleManager");
|
_logger.Info("Intialized ModuleManager");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,8 +54,7 @@ namespace Artemis.Models
|
|||||||
using (var g = Graphics.FromImage(bitmap))
|
using (var g = Graphics.FromImage(bitmap))
|
||||||
{
|
{
|
||||||
g.Clear(Color.Black);
|
g.Clear(Color.Black);
|
||||||
g.DrawImage(frame, new Rectangle(0, 0, bitmap.Width, bitmap.Height), RelativeRectangle,
|
g.DrawImage(frame, new Rectangle(0, 0, bitmap.Width, bitmap.Height), RelativeRectangle, GraphicsUnit.Pixel);
|
||||||
GraphicsUnit.Pixel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
|
|||||||
@ -141,15 +141,22 @@ namespace Artemis.Models
|
|||||||
|
|
||||||
public async Task RenameProfile(ProfileModel profileModel)
|
public async Task RenameProfile(ProfileModel profileModel)
|
||||||
{
|
{
|
||||||
|
// Store the old name
|
||||||
|
var oldName = profileModel.Name;
|
||||||
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
|
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
|
||||||
// User cancelled
|
// User cancelled
|
||||||
if (name == null)
|
if (name == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// MakeProfileUnique does a check but also modifies the profile, set the old name back
|
||||||
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
|
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
|
||||||
|
var newName = profileModel.Name;
|
||||||
|
profileModel.Name = oldName;
|
||||||
|
|
||||||
if (!doRename)
|
if (!doRename)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProfileProvider.RenameProfile(profileModel, profileModel.Name);
|
ProfileProvider.RenameProfile(profileModel, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileModel> DuplicateProfile(ProfileModel selectedProfile)
|
public async Task<ProfileModel> DuplicateProfile(ProfileModel selectedProfile)
|
||||||
@ -170,21 +177,13 @@ namespace Artemis.Models
|
|||||||
return newProfile;
|
return newProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DeleteProfile(ProfileModel selectedProfile, ModuleModel moduleModel)
|
public async Task<bool> ConfirmDeleteProfile(ProfileModel selectedProfile, ModuleModel moduleModel)
|
||||||
{
|
{
|
||||||
var confirm = await _dialogService.ShowQuestionMessageBox("Delete profile",
|
var confirm = await _dialogService.ShowQuestionMessageBox("Delete profile",
|
||||||
$"Are you sure you want to delete the profile named: {selectedProfile.Name}?\n\n" +
|
$"Are you sure you want to delete the profile named: {selectedProfile.Name}?\n\n" +
|
||||||
"This cannot be undone.");
|
"This cannot be undone.");
|
||||||
if (!confirm.Value)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var defaultProfile = ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, moduleModel, "Default");
|
return confirm.Value;
|
||||||
var deleteProfile = selectedProfile;
|
|
||||||
|
|
||||||
moduleModel.ChangeProfile(defaultProfile);
|
|
||||||
ProfileProvider.DeleteProfile(deleteProfile);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileModel> ImportProfile(ModuleModel moduleModel)
|
public async Task<ProfileModel> ImportProfile(ModuleModel moduleModel)
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows;
|
|
||||||
using Artemis.DAL;
|
using Artemis.DAL;
|
||||||
using Artemis.Events;
|
using Artemis.Events;
|
||||||
using Artemis.Managers;
|
using Artemis.Managers;
|
||||||
using Artemis.Models;
|
using Artemis.Models;
|
||||||
using Artemis.Profiles;
|
using Artemis.Profiles;
|
||||||
using Artemis.Profiles.Layers.Interfaces;
|
|
||||||
using Artemis.Profiles.Layers.Models;
|
using Artemis.Profiles.Layers.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
@ -96,10 +94,12 @@ namespace Artemis.Modules.Abstract
|
|||||||
|
|
||||||
public void ChangeProfile(ProfileModel profileModel)
|
public void ChangeProfile(ProfileModel profileModel)
|
||||||
{
|
{
|
||||||
if (!IsInitialized || Equals(profileModel, ProfileModel))
|
if (!IsInitialized || Equals(ProfileModel, profileModel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ProfileModel?.Deactivate(_luaManager);
|
||||||
ProfileModel = profileModel;
|
ProfileModel = profileModel;
|
||||||
|
|
||||||
if (!IsOverlay)
|
if (!IsOverlay)
|
||||||
ProfileModel?.Activate(_luaManager);
|
ProfileModel?.Activate(_luaManager);
|
||||||
if (ProfileModel != null)
|
if (ProfileModel != null)
|
||||||
@ -131,7 +131,7 @@ namespace Artemis.Modules.Abstract
|
|||||||
ChangeToLastProfile();
|
ChangeToLastProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeToLastProfile()
|
public void ChangeToLastProfile()
|
||||||
{
|
{
|
||||||
var profileName = !string.IsNullOrEmpty(Settings?.LastProfile) ? Settings.LastProfile : "Default";
|
var profileName = !string.IsNullOrEmpty(Settings?.LastProfile) ? Settings.LastProfile : "Default";
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,8 @@ namespace Artemis.Modules.Games.CounterStrike
|
|||||||
public class Round
|
public class Round
|
||||||
{
|
{
|
||||||
public string phase { get; set; }
|
public string phase { get; set; }
|
||||||
|
public string bomb { get; set; }
|
||||||
|
public string win_team { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[MoonSharpUserData]
|
[MoonSharpUserData]
|
||||||
|
|||||||
@ -0,0 +1,309 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Artemis.Modules.Abstract;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.FormulaOne2017
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class FormulaOne2017DataModel : ModuleDataModel
|
||||||
|
{
|
||||||
|
public FormulaOne2017DataModel()
|
||||||
|
{
|
||||||
|
Car = new Car();
|
||||||
|
Session = new Session();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Car Car { get; set; }
|
||||||
|
public Session Session { get; set; }
|
||||||
|
|
||||||
|
#region Native structs
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct UdpPacketData
|
||||||
|
{
|
||||||
|
public float m_time;
|
||||||
|
public float m_lapTime;
|
||||||
|
public float m_lapDistance;
|
||||||
|
public float m_totalDistance;
|
||||||
|
public float m_x; // World space position
|
||||||
|
public float m_y; // World space position
|
||||||
|
public float m_z; // World space position
|
||||||
|
public float m_speed; // Speed of car in MPH
|
||||||
|
public float m_xv; // Velocity in world space
|
||||||
|
public float m_yv; // Velocity in world space
|
||||||
|
public float m_zv; // Velocity in world space
|
||||||
|
public float m_xr; // World space right direction
|
||||||
|
public float m_yr; // World space right direction
|
||||||
|
public float m_zr; // World space right direction
|
||||||
|
public float m_xd; // World space forward direction
|
||||||
|
public float m_yd; // World space forward direction
|
||||||
|
public float m_zd; // World space forward direction
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_susp_pos; // Note: All wheel arrays have the order:
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_susp_vel; // RL, RR, FL, FR
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_wheel_speed;
|
||||||
|
public float m_throttle;
|
||||||
|
public float m_steer;
|
||||||
|
public float m_brake;
|
||||||
|
public float m_clutch;
|
||||||
|
public float m_gear;
|
||||||
|
public float m_gforce_lat;
|
||||||
|
public float m_gforce_lon;
|
||||||
|
public float m_lap;
|
||||||
|
public float m_engineRate;
|
||||||
|
public float m_sli_pro_native_support; // SLI Pro support
|
||||||
|
public float m_car_position; // car race position
|
||||||
|
public float m_kers_level; // kers energy left
|
||||||
|
public float m_kers_max_level; // kers maximum energy
|
||||||
|
public float m_drs; // 0 = off, 1 = on
|
||||||
|
public float m_traction_control; // 0 (off) - 2 (high)
|
||||||
|
public float m_anti_lock_brakes; // 0 (off) - 1 (on)
|
||||||
|
public float m_fuel_in_tank; // current fuel mass
|
||||||
|
public float m_fuel_capacity; // fuel capacity
|
||||||
|
public float m_in_pits; // 0 = none, 1 = pitting, 2 = in pit area
|
||||||
|
public float m_sector; // 0 = sector1, 1 = sector2, 2 = sector3
|
||||||
|
public float m_sector1_time; // time of sector1 (or 0)
|
||||||
|
public float m_sector2_time; // time of sector2 (or 0)
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_brakes_temp; // brakes temperature (centigrade)
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_tyres_pressure; // tyres pressure PSI
|
||||||
|
public float m_team_info; // team ID
|
||||||
|
public float m_total_laps; // total number of laps in this race
|
||||||
|
public float m_track_size; // track size meters
|
||||||
|
public float m_last_lap_time; // last lap time
|
||||||
|
public float m_max_rpm; // cars max RPM, at which point the rev limiter will kick in
|
||||||
|
public float m_idle_rpm; // cars idle RPM
|
||||||
|
public float m_max_gears; // maximum number of gears
|
||||||
|
public float m_sessionType; // 0 = unknown, 1 = practice, 2 = qualifying, 3 = race
|
||||||
|
public float m_drsAllowed; // 0 = not allowed, 1 = allowed, -1 = invalid / unknown
|
||||||
|
public float m_track_number; // -1 for unknown, 0-21 for tracks
|
||||||
|
public float m_vehicleFIAFlags; // -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow, 4 = red
|
||||||
|
public float m_era; // era, 2017 (modern) or 1980 (classic)
|
||||||
|
public float m_engine_temperature; // engine temperature (centigrade)
|
||||||
|
public float m_gforce_vert; // vertical g-force component
|
||||||
|
public float m_ang_vel_x; // angular velocity x-component
|
||||||
|
public float m_ang_vel_y; // angular velocity y-component
|
||||||
|
public float m_ang_vel_z; // angular velocity z-component
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public byte[] m_tyres_temperature; // tyres temperature (centigrade)
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public byte[] m_tyres_wear; // tyre wear percentage
|
||||||
|
public byte m_tyre_compound; // compound of tyre – 0 = ultra soft, 1 = super soft, 2 = soft, 3 = medium, 4 = hard, 5 = inter, 6 = wet
|
||||||
|
public byte m_front_brake_bias; // front brake bias (percentage)
|
||||||
|
public byte m_fuel_mix; // fuel mix - 0 = lean, 1 = standard, 2 = rich, 3 = max
|
||||||
|
public byte m_currentLapInvalid; // current lap invalid - 0 = valid, 1 = invalid
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public byte[] m_tyres_damage; // tyre damage (percentage)
|
||||||
|
public byte m_front_left_wing_damage; // front left wing damage (percentage)
|
||||||
|
public byte m_front_right_wing_damage; // front right wing damage (percentage)
|
||||||
|
public byte m_rear_wing_damage; // rear wing damage (percentage)
|
||||||
|
public byte m_engine_damage; // engine damage (percentage)
|
||||||
|
public byte m_gear_box_damage; // gear box damage (percentage)
|
||||||
|
public byte m_exhaust_damage; // exhaust damage (percentage)
|
||||||
|
public byte m_pit_limiter_status; // pit limiter status – 0 = off, 1 = on
|
||||||
|
public byte m_pit_speed_limit; // pit speed limit in mph
|
||||||
|
public float m_session_time_left; // NEW: time left in session in seconds
|
||||||
|
public byte m_rev_lights_percent; // NEW: rev lights indicator (percentage)
|
||||||
|
public byte m_is_spectating; // NEW: whether the player is spectating
|
||||||
|
public byte m_spectator_car_index; // NEW: index of the car being spectated
|
||||||
|
|
||||||
|
// Car data
|
||||||
|
public byte m_num_cars; // number of cars in data
|
||||||
|
public byte m_player_car_index; // index of player's car in the array
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
|
||||||
|
public CarUDPData[] m_car_data; // data for all cars on track
|
||||||
|
|
||||||
|
public float m_yaw; // NEW (v1.8)
|
||||||
|
public float m_pitch; // NEW (v1.8)
|
||||||
|
public float m_roll; // NEW (v1.8)
|
||||||
|
public float m_x_local_velocity; // NEW (v1.8) Velocity in local space
|
||||||
|
public float m_y_local_velocity; // NEW (v1.8) Velocity in local space
|
||||||
|
public float m_z_local_velocity; // NEW (v1.8) Velocity in local space
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public float[] m_susp_acceleration; // NEW (v1.8) RL, RR, FL, FR
|
||||||
|
public float m_ang_acc_x; // NEW (v1.8) angular acceleration x-component
|
||||||
|
public float m_ang_acc_y; // NEW (v1.8) angular acceleration y-component
|
||||||
|
public float m_ang_acc_z; // NEW (v1.8) angular acceleration z-component
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CarUDPData
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||||
|
public float[] m_worldPosition; // world co-ordinates of vehicle
|
||||||
|
public float m_lastLapTime;
|
||||||
|
public float m_currentLapTime;
|
||||||
|
public float m_bestLapTime;
|
||||||
|
public float m_sector1Time;
|
||||||
|
public float m_sector2Time;
|
||||||
|
public float m_lapDistance;
|
||||||
|
public byte m_driverId;
|
||||||
|
public byte m_teamId;
|
||||||
|
public byte m_carPosition; // UPDATED: track positions of vehicle
|
||||||
|
public byte m_currentLapNum;
|
||||||
|
public byte m_tyreCompound; // compound of tyre – 0 = ultra soft, 1 = super soft, 2 = soft, 3 = medium, 4 = hard, 5 = inter, 6 = wet
|
||||||
|
public byte m_inPits; // 0 = none, 1 = pitting, 2 = in pit area
|
||||||
|
public byte m_sector; // 0 = sector1, 1 = sector2, 2 = sector3
|
||||||
|
public byte m_currentLapInvalid; // current lap invalid - 0 = valid, 1 = invalid
|
||||||
|
public byte m_penalties; // NEW: accumulated time penalties in seconds to be added
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class Car
|
||||||
|
{
|
||||||
|
public Car()
|
||||||
|
{
|
||||||
|
Overview = new CarOverview();
|
||||||
|
Details = new CarDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CarOverview Overview { get; set; }
|
||||||
|
public CarDetails Details { get; set; }
|
||||||
|
|
||||||
|
public double SpeedKph { get; set; }
|
||||||
|
public double SpeedMph { get; set; }
|
||||||
|
|
||||||
|
public float Steering { get; set; }
|
||||||
|
public float Throttle { get; set; }
|
||||||
|
public float Brake { get; set; }
|
||||||
|
public float Clutch { get; set; }
|
||||||
|
|
||||||
|
public bool Drs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class CarDetails
|
||||||
|
{
|
||||||
|
public float Rpm { get; set; }
|
||||||
|
public float MaxRpm { get; set; }
|
||||||
|
public float IdleRpm { get; set; }
|
||||||
|
public int RevLightsPercent { get; set; }
|
||||||
|
|
||||||
|
public int Gear { get; set; }
|
||||||
|
public int MaxGear { get; set; }
|
||||||
|
|
||||||
|
public float Kers { get; set; }
|
||||||
|
public float MaxKers { get; set; }
|
||||||
|
|
||||||
|
public float Fuel { get; set; }
|
||||||
|
public float MaxFuel { get; set; }
|
||||||
|
|
||||||
|
public float LateralG { get; set; }
|
||||||
|
public float LongitudinalG { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class CarOverview
|
||||||
|
{
|
||||||
|
public F1Team Team { get; set; }
|
||||||
|
public AssistLevel TractionControl { get; set; }
|
||||||
|
public bool AntiLockBrakes { get; set; }
|
||||||
|
|
||||||
|
public static AssistLevel FloatToAssistLevel(float input)
|
||||||
|
{
|
||||||
|
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||||
|
if (input == 0)
|
||||||
|
return AssistLevel.Off;
|
||||||
|
if (input == 0.5)
|
||||||
|
return AssistLevel.Medium;
|
||||||
|
if (input == 1)
|
||||||
|
return AssistLevel.High;
|
||||||
|
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||||
|
|
||||||
|
return AssistLevel.Off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum F1Team
|
||||||
|
{
|
||||||
|
RedBull = 0,
|
||||||
|
Ferrari = 1,
|
||||||
|
McLaren = 2,
|
||||||
|
Renault = 3,
|
||||||
|
Mercedes = 4,
|
||||||
|
Sauber = 5,
|
||||||
|
ForceIndia = 6,
|
||||||
|
Williams = 7,
|
||||||
|
TorroRosso = 8,
|
||||||
|
Haas = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AssistLevel
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
Medium,
|
||||||
|
High
|
||||||
|
}
|
||||||
|
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class Session
|
||||||
|
{
|
||||||
|
public F1Track Track { get; set; }
|
||||||
|
public SessionType SessionType { get; set; }
|
||||||
|
public bool DrsEnabled { get; set; }
|
||||||
|
public SessionFlag Flags { get; set; }
|
||||||
|
|
||||||
|
public float TotalSeconds { get; set; }
|
||||||
|
public float LapSeconds { get; set; }
|
||||||
|
|
||||||
|
public float TrackLength { get; set; }
|
||||||
|
public float TotalDistance { get; set; }
|
||||||
|
public float LapDistance { get; set; }
|
||||||
|
|
||||||
|
public int LapNumber { get; set; }
|
||||||
|
public int TotalLaps { get; set; }
|
||||||
|
public int Position { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum F1Track
|
||||||
|
{
|
||||||
|
Australia = 0,
|
||||||
|
China = 2,
|
||||||
|
Bahrain = 3,
|
||||||
|
BahrainShort = 21,
|
||||||
|
Russia = 18,
|
||||||
|
Spain = 4,
|
||||||
|
Monaco = 5,
|
||||||
|
Canada = 6,
|
||||||
|
Azerbaijan = 20,
|
||||||
|
Austria = 17,
|
||||||
|
Britain = 7,
|
||||||
|
BritainShort = 22,
|
||||||
|
Hungary = 9,
|
||||||
|
Belgium = 10,
|
||||||
|
Italy = 11,
|
||||||
|
Singapore = 12,
|
||||||
|
Malaysia = 1,
|
||||||
|
Japan = 13,
|
||||||
|
JapanShort = 24,
|
||||||
|
USA = 15,
|
||||||
|
USAShort = 23,
|
||||||
|
Mexico = 19,
|
||||||
|
Brazil = 16,
|
||||||
|
AbuDhabi = 14
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SessionFlag
|
||||||
|
{
|
||||||
|
Unknown = -1,
|
||||||
|
None = 0,
|
||||||
|
Green = 1,
|
||||||
|
Blue = 2,
|
||||||
|
Yellow = 3,
|
||||||
|
Red = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SessionType
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Practise,
|
||||||
|
Qualifying,
|
||||||
|
Race
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.DAL;
|
||||||
|
using Artemis.Managers;
|
||||||
|
using Artemis.Modules.Abstract;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.FormulaOne2017
|
||||||
|
{
|
||||||
|
public class FormulaOne2017Model : ModuleModel
|
||||||
|
{
|
||||||
|
private bool _mustListen;
|
||||||
|
private UdpClient _udpClient;
|
||||||
|
private DateTime _lastUpdate;
|
||||||
|
private int _revAtZeroFrames;
|
||||||
|
|
||||||
|
public FormulaOne2017Model(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager)
|
||||||
|
{
|
||||||
|
Settings = SettingsProvider.Load<FormulaOne2017Settings>();
|
||||||
|
DataModel = new FormulaOne2017DataModel();
|
||||||
|
ProcessNames.Add("F1_2017");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "FormulaOne2017";
|
||||||
|
public override bool IsOverlay => false;
|
||||||
|
public override bool IsBoundToProcess => true;
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
// If we're not receiving updates, assume the game is paused/in the main menu
|
||||||
|
if (DateTime.Now - _lastUpdate > TimeSpan.FromSeconds(1))
|
||||||
|
((FormulaOne2017DataModel) DataModel).Session.SessionType = SessionType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
_mustListen = true;
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
_udpClient = new UdpClient(20777);
|
||||||
|
while (_mustListen)
|
||||||
|
{
|
||||||
|
//IPEndPoint object will allow us to read datagrams sent from any source.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var receivedResults = await _udpClient.ReceiveAsync();
|
||||||
|
HandleGameData(receivedResults);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// ignored, happens when shutting the module down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
base.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleGameData(UdpReceiveResult receivedResults)
|
||||||
|
{
|
||||||
|
_lastUpdate = DateTime.Now;
|
||||||
|
var dataModel = (FormulaOne2017DataModel) DataModel;
|
||||||
|
var pinnedPacket = GCHandle.Alloc(receivedResults.Buffer, GCHandleType.Pinned);
|
||||||
|
var msg = (FormulaOne2017DataModel.UdpPacketData) Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(FormulaOne2017DataModel.UdpPacketData));
|
||||||
|
pinnedPacket.Free();
|
||||||
|
|
||||||
|
dataModel.Car.SpeedKph = msg.m_speed * 1.609344;
|
||||||
|
dataModel.Car.SpeedMph = msg.m_speed;
|
||||||
|
|
||||||
|
dataModel.Car.Steering = msg.m_steer;
|
||||||
|
dataModel.Car.Throttle = msg.m_throttle;
|
||||||
|
dataModel.Car.Brake = msg.m_brake;
|
||||||
|
dataModel.Car.Clutch = msg.m_clutch;
|
||||||
|
|
||||||
|
dataModel.Car.Drs = msg.m_drs > 0;
|
||||||
|
|
||||||
|
dataModel.Car.Overview.Team = (F1Team) msg.m_team_info;
|
||||||
|
dataModel.Car.Overview.TractionControl = CarOverview.FloatToAssistLevel(msg.m_traction_control);
|
||||||
|
dataModel.Car.Overview.AntiLockBrakes = msg.m_anti_lock_brakes > 0;
|
||||||
|
|
||||||
|
dataModel.Car.Details.Rpm = msg.m_engineRate;
|
||||||
|
dataModel.Car.Details.MaxRpm = msg.m_max_rpm;
|
||||||
|
dataModel.Car.Details.IdleRpm = msg.m_idle_rpm;
|
||||||
|
|
||||||
|
// The one the game provides is all over the place at max rev causing blinking etc
|
||||||
|
// easily fixed by simply ignoring rapid changes from 100 to 0
|
||||||
|
if (msg.m_rev_lights_percent == 0)
|
||||||
|
_revAtZeroFrames++;
|
||||||
|
else
|
||||||
|
_revAtZeroFrames = 0;
|
||||||
|
if (_revAtZeroFrames > 2 || msg.m_rev_lights_percent != 0 || dataModel.Car.Details.RevLightsPercent != 100)
|
||||||
|
dataModel.Car.Details.RevLightsPercent = msg.m_rev_lights_percent;
|
||||||
|
|
||||||
|
dataModel.Car.Details.Gear = (int) msg.m_gear;
|
||||||
|
dataModel.Car.Details.MaxGear = (int) msg.m_max_gears;
|
||||||
|
|
||||||
|
dataModel.Car.Details.Kers = msg.m_kers_level;
|
||||||
|
dataModel.Car.Details.MaxKers = msg.m_kers_max_level;
|
||||||
|
|
||||||
|
dataModel.Car.Details.Fuel = msg.m_fuel_in_tank;
|
||||||
|
dataModel.Car.Details.MaxFuel = msg.m_fuel_capacity;
|
||||||
|
|
||||||
|
dataModel.Car.Details.LateralG = msg.m_gforce_lat;
|
||||||
|
dataModel.Car.Details.LongitudinalG = msg.m_gforce_lon;
|
||||||
|
|
||||||
|
dataModel.Session.SessionType = (SessionType) msg.m_sessionType;
|
||||||
|
// It's unknown in time trial but lets overwrite that to race
|
||||||
|
if (dataModel.Session.SessionType == SessionType.Unknown && dataModel.Car.SpeedMph > 0)
|
||||||
|
dataModel.Session.SessionType = SessionType.Race;
|
||||||
|
|
||||||
|
dataModel.Session.DrsEnabled = msg.m_drsAllowed > 0;
|
||||||
|
dataModel.Session.Flags = (SessionFlag) msg.m_vehicleFIAFlags;
|
||||||
|
|
||||||
|
dataModel.Session.TotalSeconds = msg.m_time;
|
||||||
|
dataModel.Session.LapSeconds = msg.m_lapTime;
|
||||||
|
|
||||||
|
dataModel.Session.Track = (F1Track) msg.m_track_number;
|
||||||
|
dataModel.Session.TrackLength = msg.m_track_size;
|
||||||
|
dataModel.Session.TotalDistance = msg.m_totalDistance;
|
||||||
|
dataModel.Session.LapDistance = msg.m_lapDistance;
|
||||||
|
|
||||||
|
dataModel.Session.LapNumber = (int) msg.m_lap;
|
||||||
|
dataModel.Session.TotalLaps = (int) msg.m_total_laps;
|
||||||
|
dataModel.Session.Position = (int) msg.m_car_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_mustListen = false;
|
||||||
|
_udpClient.Dispose();
|
||||||
|
_udpClient = null;
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
using Artemis.Modules.Abstract;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.FormulaOne2017
|
||||||
|
{
|
||||||
|
public class FormulaOne2017Settings : ModuleSettings
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
<UserControl x:Class="Artemis.Modules.Games.FormulaOne2017.FormulaOne2017View"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:cal="http://www.caliburnproject.org"
|
||||||
|
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="559.725" d:DesignWidth="882.696">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="30" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" FontSize="20" HorizontalAlignment="Left">
|
||||||
|
<Label.Content>
|
||||||
|
<AccessText TextWrapping="Wrap" Text="There is currently no default profile available for F1 2017." />
|
||||||
|
</Label.Content>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<!-- Sub header -->
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" TextWrapping="Wrap" HorizontalAlignment="Left" FontFamily="Segoe UI Semibold" TextAlignment="Justify" Margin="5,0,0,10">
|
||||||
|
The F1 2017 module requires UDP Telemetry to be enabled in the ingame settings menu.
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<!-- Enable -->
|
||||||
|
<StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||||
|
<Label Content="Enable module" HorizontalAlignment="Right" Margin="0,0,0,3" />
|
||||||
|
<controls:ToggleSwitchButton IsChecked="{Binding Path=IsModuleEnabled, Mode=OneWay}" cal:Message.Attach="[Event Click] = [Action ToggleModule]"
|
||||||
|
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" ToolTip="Note: You can't enable an module when Artemis is disabled" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Profile editor -->
|
||||||
|
<ContentControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="ProfileEditor" />
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Grid.Column="0" Grid.Row="3" Orientation="Horizontal" VerticalAlignment="Bottom">
|
||||||
|
<Button x:Name="ResetSettings" Content="Reset effect" VerticalAlignment="Top" Width="100" Style="{DynamicResource SquareButtonStyle}" />
|
||||||
|
<Button x:Name="SaveSettings" Content="Save changes" VerticalAlignment="Top" Width="100" Margin="10,0,0,0" Style="{DynamicResource SquareButtonStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.FormulaOne2017
|
||||||
|
{
|
||||||
|
public partial class FormulaOne2017View : UserControl
|
||||||
|
{
|
||||||
|
public FormulaOne2017View()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Artemis.Managers;
|
||||||
|
using Artemis.Modules.Abstract;
|
||||||
|
using Ninject;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.FormulaOne2017
|
||||||
|
{
|
||||||
|
public sealed class FormulaOne2017ViewModel : ModuleViewModel
|
||||||
|
{
|
||||||
|
public FormulaOne2017ViewModel(MainManager mainManager, [Named(nameof(FormulaOne2017Model))] ModuleModel moduleModel,
|
||||||
|
IKernel kernel) : base(mainManager, moduleModel, kernel)
|
||||||
|
{
|
||||||
|
DisplayName = "F1 2017";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool UsesProfileEditor => true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,6 +48,8 @@ namespace Artemis.Modules.Games.Overwatch
|
|||||||
Symmetra,
|
Symmetra,
|
||||||
Zenyatta,
|
Zenyatta,
|
||||||
Ana,
|
Ana,
|
||||||
Sombra
|
Sombra,
|
||||||
|
Orisa,
|
||||||
|
Doomfist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +76,9 @@ namespace Artemis.Modules.Games.Overwatch
|
|||||||
new CharacterColor {Character = OverwatchCharacter.Symmetra, Color = Color.FromRgb(46, 116, 148)},
|
new CharacterColor {Character = OverwatchCharacter.Symmetra, Color = Color.FromRgb(46, 116, 148)},
|
||||||
new CharacterColor {Character = OverwatchCharacter.Zenyatta, Color = Color.FromRgb(248, 218, 26)},
|
new CharacterColor {Character = OverwatchCharacter.Zenyatta, Color = Color.FromRgb(248, 218, 26)},
|
||||||
new CharacterColor {Character = OverwatchCharacter.Ana, Color = Color.FromRgb(16, 36, 87)},
|
new CharacterColor {Character = OverwatchCharacter.Ana, Color = Color.FromRgb(16, 36, 87)},
|
||||||
new CharacterColor {Character = OverwatchCharacter.Sombra, Color = Color.FromRgb(20, 5, 101)}
|
new CharacterColor {Character = OverwatchCharacter.Sombra, Color = Color.FromRgb(20, 5, 101)},
|
||||||
|
new CharacterColor {Character = OverwatchCharacter.Orisa, Color = Color.FromRgb(1,40,0)},
|
||||||
|
new CharacterColor {Character = OverwatchCharacter.Doomfist, Color = Color.FromRgb(33, 3, 1)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
<!-- Game directory -->
|
<!-- Game directory -->
|
||||||
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,1,0">
|
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,1,0">
|
||||||
<Label FontSize="20" HorizontalAlignment="Left" Content="Overwatch Directory" />
|
<Label FontSize="20" HorizontalAlignment="Left" Content="Overwatch directory" />
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBox x:Name="GameDirectory" Height="23" TextWrapping="Wrap" Margin="0,0,30,0" Text="{Binding Path=Settings.GameDirectory, Mode=TwoWay}" cal:Message.Attach="[Event LostFocus] = [Action PlaceDll]" />
|
<TextBox x:Name="GameDirectory" Height="23" TextWrapping="Wrap" Margin="0,0,30,0" Text="{Binding Path=Settings.GameDirectory, Mode=TwoWay}" cal:Message.Attach="[Event LostFocus] = [Action PlaceDll]" />
|
||||||
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" HorizontalAlignment="Right" Width="25" Style="{DynamicResource SquareButtonStyle}" Height="26" />
|
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" HorizontalAlignment="Right" Width="25" Style="{DynamicResource SquareButtonStyle}" Height="26" />
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace Artemis.Modules.Games.RocketLeague
|
|||||||
}
|
}
|
||||||
|
|
||||||
Updater.GetPointers();
|
Updater.GetPointers();
|
||||||
var version = SettingsProvider.Load<OffsetSettings>().RocketLeague.GameVersion;
|
var version = SettingsProvider.Load<OffsetSettings>().RocketLeague?.GameVersion;
|
||||||
VersionText = $"Requires patch {version}. When a new patch is released Artemis downloads new pointers for the latest version (unless disabled in settings).";
|
VersionText = $"Requires patch {version}. When a new patch is released Artemis downloads new pointers for the latest version (unless disabled in settings).";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -47,7 +47,7 @@ namespace Artemis.Modules.Games.UnrealTournament
|
|||||||
var gameSettings = (UnrealTournamentSettings) Settings;
|
var gameSettings = (UnrealTournamentSettings) Settings;
|
||||||
// If already propertly set up, don't do anything
|
// If already propertly set up, don't do anything
|
||||||
if (gameSettings.GameDirectory != null &&
|
if (gameSettings.GameDirectory != null &&
|
||||||
File.Exists(gameSettings.GameDirectory + "UE4-Win64-Shipping.exe"))
|
File.Exists(gameSettings.GameDirectory + @"\Engine\Binaries\Win64\UE4-Win64-Shipping.exe"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Attempt to read the file
|
// Attempt to read the file
|
||||||
@ -65,7 +65,7 @@ namespace Artemis.Modules.Games.UnrealTournament
|
|||||||
// Use backslash in path for consistency
|
// Use backslash in path for consistency
|
||||||
utDir = utDir.Replace('/', '\\');
|
utDir = utDir.Replace('/', '\\');
|
||||||
|
|
||||||
if (!File.Exists(utDir + @"\UE4-Win64-Shipping.exe"))
|
if (!File.Exists(utDir + @"\Engine\Binaries\Win64\UE4-Win64-Shipping.exe"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gameSettings.GameDirectory = utDir;
|
gameSettings.GameDirectory = utDir;
|
||||||
@ -78,7 +78,7 @@ namespace Artemis.Modules.Games.UnrealTournament
|
|||||||
var gameSettings = (UnrealTournamentSettings) Settings;
|
var gameSettings = (UnrealTournamentSettings) Settings;
|
||||||
var path = gameSettings.GameDirectory;
|
var path = gameSettings.GameDirectory;
|
||||||
|
|
||||||
if (!File.Exists(path + @"\UE4-Win64-Shipping.exe"))
|
if (!File.Exists(path + @"\Engine\Binaries\Win64\UE4-Win64-Shipping.exe"))
|
||||||
{
|
{
|
||||||
_dialogService.ShowErrorMessageBox("Please select a valid Unreal Tournament directory\n\n" +
|
_dialogService.ShowErrorMessageBox("Please select a valid Unreal Tournament directory\n\n" +
|
||||||
@"By default Unreal Tournament is in C:\Program Files\Epic Games\UnrealTournament");
|
@"By default Unreal Tournament is in C:\Program Files\Epic Games\UnrealTournament");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,157 +0,0 @@
|
|||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public static class WoWEnums
|
|
||||||
{
|
|
||||||
public enum GuidType : byte
|
|
||||||
{
|
|
||||||
Null = 0,
|
|
||||||
Uniq = 1,
|
|
||||||
Player = 2,
|
|
||||||
Item = 3,
|
|
||||||
StaticDoor = 4,
|
|
||||||
Transport = 5,
|
|
||||||
Conversation = 6,
|
|
||||||
Creature = 7,
|
|
||||||
Vehicle = 8,
|
|
||||||
Pet = 9,
|
|
||||||
GameObject = 10,
|
|
||||||
DynamicObject = 11,
|
|
||||||
AreaTrigger = 12,
|
|
||||||
Corpse = 13,
|
|
||||||
LootObject = 14,
|
|
||||||
SceneObject = 15,
|
|
||||||
Scenario = 16,
|
|
||||||
AiGroup = 17,
|
|
||||||
DynamicDoor = 18,
|
|
||||||
ClientActor = 19,
|
|
||||||
Vignette = 20,
|
|
||||||
CallForHelp = 21,
|
|
||||||
AiResource = 22,
|
|
||||||
AiLock = 23,
|
|
||||||
AiLockTicket = 24,
|
|
||||||
ChatChannel = 25,
|
|
||||||
Party = 26,
|
|
||||||
Guild = 27,
|
|
||||||
WowAccount = 28,
|
|
||||||
BNetAccount = 29,
|
|
||||||
GmTask = 30,
|
|
||||||
MobileSession = 31,
|
|
||||||
RaidGroup = 32,
|
|
||||||
Spell = 33,
|
|
||||||
Mail = 34,
|
|
||||||
WebObj = 35,
|
|
||||||
LfgObject = 36,
|
|
||||||
LfgList = 37,
|
|
||||||
UserRouter = 38,
|
|
||||||
PvpQueueGroup = 39,
|
|
||||||
UserClient = 40,
|
|
||||||
PetBattle = 41,
|
|
||||||
UniqueUserClient = 42,
|
|
||||||
BattlePet = 43
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ObjectType
|
|
||||||
{
|
|
||||||
Object = 0,
|
|
||||||
Item = 1,
|
|
||||||
Container = 2,
|
|
||||||
Unit = 3,
|
|
||||||
Player = 4,
|
|
||||||
GameObject = 5,
|
|
||||||
DynamicObject = 6,
|
|
||||||
Corpse = 7,
|
|
||||||
AreaTrigger = 8,
|
|
||||||
SceneObject = 9,
|
|
||||||
Conversation = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PowerType
|
|
||||||
{
|
|
||||||
Mana = 0,
|
|
||||||
Rage = 1,
|
|
||||||
Focus = 2,
|
|
||||||
Energy = 3,
|
|
||||||
Happiness = 4,
|
|
||||||
RunicPower = 5,
|
|
||||||
Runes = 6,
|
|
||||||
Health = 7,
|
|
||||||
Maelstrom = 11,
|
|
||||||
Insanity = 13,
|
|
||||||
Fury = 17,
|
|
||||||
Pain = 18,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Reaction
|
|
||||||
{
|
|
||||||
Hostile = 1,
|
|
||||||
Neutral = 3,
|
|
||||||
Friendly = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ShapeshiftForm
|
|
||||||
{
|
|
||||||
Normal = 0,
|
|
||||||
Cat = 1,
|
|
||||||
TreeOfLife = 2,
|
|
||||||
Travel = 3,
|
|
||||||
Aqua = 4,
|
|
||||||
Bear = 5,
|
|
||||||
Ambient = 6,
|
|
||||||
Ghoul = 7,
|
|
||||||
DireBear = 8,
|
|
||||||
CreatureBear = 14,
|
|
||||||
CreatureCat = 15,
|
|
||||||
GhostWolf = 16,
|
|
||||||
BattleStance = 17,
|
|
||||||
DefensiveStance = 18,
|
|
||||||
BerserkerStance = 19,
|
|
||||||
EpicFlightForm = 27,
|
|
||||||
Shadow = 28,
|
|
||||||
Stealth = 30,
|
|
||||||
Moonkin = 31,
|
|
||||||
SpiritOfRedemption = 32
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WoWClass
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Warrior = 1,
|
|
||||||
Paladin = 2,
|
|
||||||
Hunter = 3,
|
|
||||||
Rogue = 4,
|
|
||||||
Priest = 5,
|
|
||||||
DeathKnight = 6,
|
|
||||||
Shaman = 7,
|
|
||||||
Mage = 8,
|
|
||||||
Warlock = 9,
|
|
||||||
Druid = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WoWRace
|
|
||||||
{
|
|
||||||
Human = 1,
|
|
||||||
Orc = 2,
|
|
||||||
Dwarf = 3,
|
|
||||||
NightElf = 4,
|
|
||||||
Undead = 5,
|
|
||||||
Tauren = 6,
|
|
||||||
Gnome = 7,
|
|
||||||
Troll = 8,
|
|
||||||
Goblin = 9,
|
|
||||||
BloodElf = 10,
|
|
||||||
Draenei = 11,
|
|
||||||
FelOrc = 12,
|
|
||||||
Naga = 13,
|
|
||||||
Broken = 14,
|
|
||||||
Skeleton = 15,
|
|
||||||
Worgen = 22
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WoWType
|
|
||||||
{
|
|
||||||
Player,
|
|
||||||
Npc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using Process.NET;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public class WoWNameCache
|
|
||||||
{
|
|
||||||
public WoWNameCache(ProcessSharp process, IntPtr baseAddress)
|
|
||||||
{
|
|
||||||
Process = process;
|
|
||||||
CurrentCacheAddress = process.Native.MainModule.BaseAddress + baseAddress.ToInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProcessSharp Process { get; set; }
|
|
||||||
|
|
||||||
public IntPtr CurrentCacheAddress { get; set; }
|
|
||||||
|
|
||||||
public WoWDetails GetNameByGuid(Guid searchGuid)
|
|
||||||
{
|
|
||||||
var current = Process.Memory.Read<IntPtr>(CurrentCacheAddress);
|
|
||||||
var index = 0;
|
|
||||||
while (current != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
var guid = Process.Memory.Read<Guid>(current + 0x20);
|
|
||||||
if (guid.Equals(searchGuid))
|
|
||||||
{
|
|
||||||
var pRace = Process.Memory.Read<int>(current + 0x88);
|
|
||||||
var pClass = Process.Memory.Read<int>(current + 0x90);
|
|
||||||
var pName = Process.Memory.Read(current + 0x31, Encoding.ASCII, 48);
|
|
||||||
|
|
||||||
var name = new WoWDetails(guid, pRace, pClass, WoWEnums.WoWType.Player, pName);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 20000)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
index++;
|
|
||||||
current = Process.Memory.Read<IntPtr>(current);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WoWDetails
|
|
||||||
{
|
|
||||||
public WoWDetails(Guid guid, int race, int @class, WoWEnums.WoWType type, string name)
|
|
||||||
{
|
|
||||||
Guid = guid;
|
|
||||||
Race = (WoWEnums.WoWRace) race;
|
|
||||||
Class = (WoWEnums.WoWClass) @class;
|
|
||||||
Type = type;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid Guid { get; set; }
|
|
||||||
public WoWEnums.WoWRace Race { get; set; }
|
|
||||||
public WoWEnums.WoWClass Class { get; set; }
|
|
||||||
public WoWEnums.WoWType Type { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Process.NET;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public class WoWObject
|
|
||||||
{
|
|
||||||
private readonly bool _readPointer;
|
|
||||||
|
|
||||||
public WoWObject(IProcess process, IntPtr baseAddress, bool readPointer = false)
|
|
||||||
{
|
|
||||||
Process = process;
|
|
||||||
BaseAddress = baseAddress;
|
|
||||||
_readPointer = readPointer;
|
|
||||||
|
|
||||||
Guid = ReadField<Guid>(0x00);
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public IntPtr BaseAddress { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public IProcess Process { get; set; }
|
|
||||||
|
|
||||||
public Guid Guid { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public WoWStructs.ObjectData Data { get; set; }
|
|
||||||
|
|
||||||
public T ReadField<T>(int offset)
|
|
||||||
{
|
|
||||||
var address = GetAddress();
|
|
||||||
if (address == IntPtr.Zero)
|
|
||||||
return default(T);
|
|
||||||
|
|
||||||
var ptr = Process.Memory.Read<IntPtr>(address + 0x10);
|
|
||||||
return Process.Memory.Read<T>(ptr + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T ReadField<T>(Enum offset)
|
|
||||||
{
|
|
||||||
var address = GetAddress();
|
|
||||||
if (address == IntPtr.Zero)
|
|
||||||
return default(T);
|
|
||||||
|
|
||||||
var ptr = Process.Memory.Read<IntPtr>(address + 0x10);
|
|
||||||
return Process.Memory.Read<T>(ptr + Convert.ToInt32(offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntPtr GetAddress()
|
|
||||||
{
|
|
||||||
return _readPointer
|
|
||||||
? Process.Memory.Read<IntPtr>(Process.Native.MainModule.BaseAddress + BaseAddress.ToInt32())
|
|
||||||
: BaseAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WoWDetails GetNpcDetails()
|
|
||||||
{
|
|
||||||
var address = GetAddress();
|
|
||||||
if (address == IntPtr.Zero)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var npcCachePtr = Process.Memory.Read<IntPtr>(address + 0x1760);
|
|
||||||
if (npcCachePtr == IntPtr.Zero)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var npcName = Process.Memory.Read(Process.Memory.Read<IntPtr>(npcCachePtr + 0x00A0), Encoding.ASCII, 48);
|
|
||||||
return new WoWDetails(Guid, 0, 0, WoWEnums.WoWType.Npc, npcName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Process.NET;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public class WoWObjectManager
|
|
||||||
{
|
|
||||||
public WoWObjectManager(IProcess process, IntPtr baseAddress)
|
|
||||||
{
|
|
||||||
Process = process;
|
|
||||||
CurrentManagerAddress = process.Native.MainModule.BaseAddress + baseAddress.ToInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IProcess Process { get; set; }
|
|
||||||
|
|
||||||
public IntPtr CurrentManagerAddress { get; set; }
|
|
||||||
|
|
||||||
public Dictionary<WoWStructs.Guid, WoWObject> WoWObjects { get; set; }
|
|
||||||
|
|
||||||
public IntPtr GetFirstObject()
|
|
||||||
{
|
|
||||||
var mgr = GetCurrentManager();
|
|
||||||
return mgr.VisibleObjects.m_fulllist.baseClass.m_terminator.m_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WoWStructs.CurrentManager GetCurrentManager()
|
|
||||||
{
|
|
||||||
return Process.Memory.Read<WoWStructs.CurrentManager>(Process.Memory.Read<IntPtr>(CurrentManagerAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr GetNextObjectFromCurrent(IntPtr current)
|
|
||||||
{
|
|
||||||
var mgr = GetCurrentManager();
|
|
||||||
|
|
||||||
return Process.Memory.Read<IntPtr>(
|
|
||||||
current + mgr.VisibleObjects.m_fulllist.baseClass.m_linkoffset + IntPtr.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
WoWObjects.Clear();
|
|
||||||
var wowObjects = EnumVisibleObjects();
|
|
||||||
foreach (var wowObject in wowObjects)
|
|
||||||
WoWObjects[wowObject.Data.Guid] = wowObject;
|
|
||||||
|
|
||||||
OnObjectsUpdated(WoWObjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<Dictionary<WoWStructs.Guid, WoWObject>> ObjectsUpdated;
|
|
||||||
|
|
||||||
// Loop through the games object list.
|
|
||||||
public IEnumerable<WoWObject> EnumVisibleObjects()
|
|
||||||
{
|
|
||||||
var first = GetFirstObject();
|
|
||||||
var typeOffset = Marshal.OffsetOf(typeof(WoWStructs.ObjectData), "ObjectType").ToInt32();
|
|
||||||
|
|
||||||
while (((first.ToInt64() & 1) == 0) && (first != IntPtr.Zero))
|
|
||||||
{
|
|
||||||
var type = (WoWEnums.ObjectType) Process.Memory.Read<int>(first + typeOffset);
|
|
||||||
|
|
||||||
// Fix below with other object types as added.
|
|
||||||
// ReSharper disable once SwitchStatementMissingSomeCases
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case WoWEnums.ObjectType.Object:
|
|
||||||
yield return new WoWObject(Process, first);
|
|
||||||
break;
|
|
||||||
case WoWEnums.ObjectType.Container:
|
|
||||||
break;
|
|
||||||
case WoWEnums.ObjectType.Unit:
|
|
||||||
yield return new WoWUnit(Process, first);
|
|
||||||
break;
|
|
||||||
case WoWEnums.ObjectType.Player:
|
|
||||||
yield return new WoWPlayer(Process, first, new IntPtr(0x179A6E0));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
yield return new WoWObject(Process, first);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
first = GetNextObjectFromCurrent(first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnObjectsUpdated(Dictionary<WoWStructs.Guid, WoWObject> e)
|
|
||||||
{
|
|
||||||
ObjectsUpdated?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,368 +0,0 @@
|
|||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
internal static class WoWOffsets
|
|
||||||
{
|
|
||||||
internal enum AreaTriggerData
|
|
||||||
{
|
|
||||||
OverrideScaleCurve = 0x30, // Size: 0x7, Flags: 0x201
|
|
||||||
ExtraScaleCurve = 0x4C, // Size: 0x7, Flags: 0x201
|
|
||||||
Caster = 0x68, // Size: 0x4, Flags: 0x1
|
|
||||||
Duration = 0x78, // Size: 0x1, Flags: 0x1
|
|
||||||
TimeToTarget = 0x7C, // Size: 0x1, Flags: 0x201
|
|
||||||
TimeToTargetScale = 0x80, // Size: 0x1, Flags: 0x201
|
|
||||||
TimeToTargetExtraScale = 0x84, // Size: 0x1, Flags: 0x201
|
|
||||||
SpellId = 0x88, // Size: 0x1, Flags: 0x1
|
|
||||||
SpellVisualId = 0x8C, // Size: 0x1, Flags: 0x80
|
|
||||||
BoundsRadius2D = 0x90, // Size: 0x1, Flags: 0x280
|
|
||||||
DecalPropertiesId = 0x94 // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum ContainerData
|
|
||||||
{
|
|
||||||
Slots = 0x150, // Size: 0x90, Flags: 0x1
|
|
||||||
NumSlots = 0x390 // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum ConversationData
|
|
||||||
{
|
|
||||||
LastLineDuration = 0x30 // Size: 0x1, Flags: 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum CorpseData
|
|
||||||
{
|
|
||||||
Owner = 0x30, // Size: 0x4, Flags: 0x1
|
|
||||||
PartyGuid = 0x40, // Size: 0x4, Flags: 0x1
|
|
||||||
DisplayId = 0x50, // Size: 0x1, Flags: 0x1
|
|
||||||
Items = 0x54, // Size: 0x13, Flags: 0x1
|
|
||||||
SkinId = 0xA0, // Size: 0x1, Flags: 0x1
|
|
||||||
FacialHairStyleId = 0xA4, // Size: 0x1, Flags: 0x1
|
|
||||||
Flags = 0xA8, // Size: 0x1, Flags: 0x1
|
|
||||||
DynamicFlags = 0xAC, // Size: 0x1, Flags: 0x80
|
|
||||||
FactionTemplate = 0xB0, // Size: 0x1, Flags: 0x1
|
|
||||||
CustomDisplayOption = 0xB4 // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum DynamicObjectData
|
|
||||||
{
|
|
||||||
Caster = 0x30, // Size: 0x4, Flags: 0x1
|
|
||||||
TypeAndVisualId = 0x40, // Size: 0x1, Flags: 0x80
|
|
||||||
SpellId = 0x44, // Size: 0x1, Flags: 0x1
|
|
||||||
Radius = 0x48, // Size: 0x1, Flags: 0x1
|
|
||||||
CastTime = 0x4C // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum GameObjectData
|
|
||||||
{
|
|
||||||
CreatedBy = 0x30, // Size: 0x4, Flags: 0x1
|
|
||||||
DisplayId = 0x40, // Size: 0x1, Flags: 0x280
|
|
||||||
Flags = 0x44, // Size: 0x1, Flags: 0x201
|
|
||||||
ParentRotation = 0x48, // Size: 0x4, Flags: 0x1
|
|
||||||
FactionTemplate = 0x58, // Size: 0x1, Flags: 0x1
|
|
||||||
Level = 0x5C, // Size: 0x1, Flags: 0x1
|
|
||||||
PercentHealth = 0x60, // Size: 0x1, Flags: 0x201
|
|
||||||
SpellVisualId = 0x64, // Size: 0x1, Flags: 0x281
|
|
||||||
StateSpellVisualId = 0x68, // Size: 0x1, Flags: 0x280
|
|
||||||
StateAnimId = 0x6C, // Size: 0x1, Flags: 0x280
|
|
||||||
StateAnimKitId = 0x70, // Size: 0x1, Flags: 0x280
|
|
||||||
StateWorldEffectId = 0x74 // Size: 0x4, Flags: 0x280
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum ItemData
|
|
||||||
{
|
|
||||||
Owner = 0x30, // Size: 0x4, Flags: 0x1
|
|
||||||
ContainedIn = 0x40, // Size: 0x4, Flags: 0x1
|
|
||||||
Creator = 0x50, // Size: 0x4, Flags: 0x1
|
|
||||||
GiftCreator = 0x60, // Size: 0x4, Flags: 0x1
|
|
||||||
StackCount = 0x70, // Size: 0x1, Flags: 0x4
|
|
||||||
Expiration = 0x74, // Size: 0x1, Flags: 0x4
|
|
||||||
SpellCharges = 0x78, // Size: 0x5, Flags: 0x4
|
|
||||||
DynamicFlags = 0x8C, // Size: 0x1, Flags: 0x1
|
|
||||||
Enchantment = 0x90, // Size: 0x27, Flags: 0x1
|
|
||||||
PropertySeed = 0x12C, // Size: 0x1, Flags: 0x1
|
|
||||||
RandomPropertiesId = 0x130, // Size: 0x1, Flags: 0x1
|
|
||||||
Durability = 0x134, // Size: 0x1, Flags: 0x4
|
|
||||||
MaxDurability = 0x138, // Size: 0x1, Flags: 0x4
|
|
||||||
CreatePlayedTime = 0x13C, // Size: 0x1, Flags: 0x1
|
|
||||||
ModifiersMask = 0x140, // Size: 0x1, Flags: 0x4
|
|
||||||
Context = 0x144, // Size: 0x1, Flags: 0x1
|
|
||||||
ArtifactXp = 0x148, // Size: 0x1, Flags: 0x4
|
|
||||||
ItemAppearanceModId = 0x14C // Size: 0x1, Flags: 0x4
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum KeyBinding
|
|
||||||
{
|
|
||||||
NumKeyBindings = 0x1700030, // -0x17C0
|
|
||||||
First = 0xC8,
|
|
||||||
Next = 0xB8,
|
|
||||||
Key = 0x30,
|
|
||||||
Command = 0x58
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum ObjectData
|
|
||||||
{
|
|
||||||
Guid = 0x0, // Size: 0x4, Flags: 0x1
|
|
||||||
Data = 0x10, // Size: 0x4, Flags: 0x1
|
|
||||||
Type = 0x20, // Size: 0x1, Flags: 0x1
|
|
||||||
EntryId = 0x24, // Size: 0x1, Flags: 0x80
|
|
||||||
DynamicFlags = 0x28, // Size: 0x1, Flags: 0x280
|
|
||||||
Scale = 0x2C // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum PlayerData
|
|
||||||
{
|
|
||||||
DuelArbiter = 0x360, // Size: 0x4, Flags: 0x1
|
|
||||||
WowAccount = 0x370, // Size: 0x4, Flags: 0x1
|
|
||||||
LootTargetGuid = 0x380, // Size: 0x4, Flags: 0x1
|
|
||||||
PlayerFlags = 0x390, // Size: 0x1, Flags: 0x1
|
|
||||||
PlayerFlagsEx = 0x394, // Size: 0x1, Flags: 0x1
|
|
||||||
GuildRankId = 0x398, // Size: 0x1, Flags: 0x1
|
|
||||||
GuildDeleteDate = 0x39C, // Size: 0x1, Flags: 0x1
|
|
||||||
GuildLevel = 0x3A0, // Size: 0x1, Flags: 0x1
|
|
||||||
HairColorId = 0x3A4, // Size: 0x1, Flags: 0x1
|
|
||||||
CustomDisplayOption = 0x3A8, // Size: 0x1, Flags: 0x1
|
|
||||||
Inebriation = 0x3AC, // Size: 0x1, Flags: 0x1
|
|
||||||
ArenaFaction = 0x3B0, // Size: 0x1, Flags: 0x1
|
|
||||||
DuelTeam = 0x3B4, // Size: 0x1, Flags: 0x1
|
|
||||||
GuildTimeStamp = 0x3B8, // Size: 0x1, Flags: 0x1
|
|
||||||
QuestLog = 0x3BC, // Size: 0x320, Flags: 0x20
|
|
||||||
VisibleItems = 0x103C, // Size: 0x26, Flags: 0x1
|
|
||||||
PlayerTitle = 0x10D4, // Size: 0x1, Flags: 0x1
|
|
||||||
FakeInebriation = 0x10D8, // Size: 0x1, Flags: 0x1
|
|
||||||
VirtualPlayerRealm = 0x10DC, // Size: 0x1, Flags: 0x1
|
|
||||||
CurrentSpecId = 0x10E0, // Size: 0x1, Flags: 0x1
|
|
||||||
TaxiMountAnimKitId = 0x10E4, // Size: 0x1, Flags: 0x1
|
|
||||||
AvgItemLevel = 0x10E8, // Size: 0x4, Flags: 0x1
|
|
||||||
CurrentBattlePetBreedQuality = 0x10F8, // Size: 0x1, Flags: 0x1
|
|
||||||
Prestige = 0x10FC, // Size: 0x1, Flags: 0x1
|
|
||||||
HonorLevel = 0x1100, // Size: 0x1, Flags: 0x1
|
|
||||||
InvSlots = 0x1104, // Size: 0x2EC, Flags: 0x2
|
|
||||||
FarsightObject = 0x1CB4, // Size: 0x4, Flags: 0x2
|
|
||||||
SummonedBattlePetGuid = 0x1CC4, // Size: 0x4, Flags: 0x2
|
|
||||||
KnownTitles = 0x1CD4, // Size: 0xC, Flags: 0x2
|
|
||||||
Coinage = 0x1D04, // Size: 0x2, Flags: 0x2
|
|
||||||
Xp = 0x1D0C, // Size: 0x1, Flags: 0x2
|
|
||||||
NextLevelXp = 0x1D10, // Size: 0x1, Flags: 0x2
|
|
||||||
Skill = 0x1D14, // Size: 0x1C0, Flags: 0x2
|
|
||||||
CharacterPoints = 0x2414, // Size: 0x1, Flags: 0x2
|
|
||||||
MaxTalentTiers = 0x2418, // Size: 0x1, Flags: 0x2
|
|
||||||
TrackCreatureMask = 0x241C, // Size: 0x1, Flags: 0x2
|
|
||||||
TrackResourceMask = 0x2420, // Size: 0x1, Flags: 0x2
|
|
||||||
MainhandExpertise = 0x2424, // Size: 0x1, Flags: 0x2
|
|
||||||
OffhandExpertise = 0x2428, // Size: 0x1, Flags: 0x2
|
|
||||||
RangedExpertise = 0x242C, // Size: 0x1, Flags: 0x2
|
|
||||||
CombatRatingExpertise = 0x2430, // Size: 0x1, Flags: 0x2
|
|
||||||
BlockPercentage = 0x2434, // Size: 0x1, Flags: 0x2
|
|
||||||
DodgePercentage = 0x2438, // Size: 0x1, Flags: 0x2
|
|
||||||
ParryPercentage = 0x243C, // Size: 0x1, Flags: 0x2
|
|
||||||
CritPercentage = 0x2440, // Size: 0x1, Flags: 0x2
|
|
||||||
RangedCritPercentage = 0x2444, // Size: 0x1, Flags: 0x2
|
|
||||||
OffhandCritPercentage = 0x2448, // Size: 0x1, Flags: 0x2
|
|
||||||
SpellCritPercentage = 0x244C, // Size: 0x1, Flags: 0x2
|
|
||||||
ShieldBlock = 0x2450, // Size: 0x1, Flags: 0x2
|
|
||||||
ShieldBlockCritPercentage = 0x2454, // Size: 0x1, Flags: 0x2
|
|
||||||
Mastery = 0x2458, // Size: 0x1, Flags: 0x2
|
|
||||||
Speed = 0x245C, // Size: 0x1, Flags: 0x2
|
|
||||||
Lifesteal = 0x2460, // Size: 0x1, Flags: 0x2
|
|
||||||
Avoidance = 0x2464, // Size: 0x1, Flags: 0x2
|
|
||||||
Sturdiness = 0x2468, // Size: 0x1, Flags: 0x2
|
|
||||||
Versatility = 0x246C, // Size: 0x1, Flags: 0x2
|
|
||||||
VersatilityBonus = 0x2470, // Size: 0x1, Flags: 0x2
|
|
||||||
PvpPowerDamage = 0x2474, // Size: 0x1, Flags: 0x2
|
|
||||||
PvpPowerHealing = 0x2478, // Size: 0x1, Flags: 0x2
|
|
||||||
ExploredZones = 0x247C, // Size: 0x100, Flags: 0x2
|
|
||||||
RestInfo = 0x287C, // Size: 0x4, Flags: 0x2
|
|
||||||
ModDamageDonePos = 0x288C, // Size: 0x7, Flags: 0x2
|
|
||||||
ModDamageDoneNeg = 0x28A8, // Size: 0x7, Flags: 0x2
|
|
||||||
ModDamageDonePercent = 0x28C4, // Size: 0x7, Flags: 0x2
|
|
||||||
ModHealingDonePos = 0x28E0, // Size: 0x1, Flags: 0x2
|
|
||||||
ModHealingPercent = 0x28E4, // Size: 0x1, Flags: 0x2
|
|
||||||
ModHealingDonePercent = 0x28E8, // Size: 0x1, Flags: 0x2
|
|
||||||
ModPeriodicHealingDonePercent = 0x28EC, // Size: 0x1, Flags: 0x2
|
|
||||||
WeaponDmgMultipliers = 0x28F0, // Size: 0x3, Flags: 0x2
|
|
||||||
WeaponAtkSpeedMultipliers = 0x28FC, // Size: 0x3, Flags: 0x2
|
|
||||||
ModSpellPowerPercent = 0x2908, // Size: 0x1, Flags: 0x2
|
|
||||||
ModResiliencePercent = 0x290C, // Size: 0x1, Flags: 0x2
|
|
||||||
OverrideSpellPowerByApPercent = 0x2910, // Size: 0x1, Flags: 0x2
|
|
||||||
OverrideApBySpellPowerPercent = 0x2914, // Size: 0x1, Flags: 0x2
|
|
||||||
ModTargetResistance = 0x2918, // Size: 0x1, Flags: 0x2
|
|
||||||
ModTargetPhysicalResistance = 0x291C, // Size: 0x1, Flags: 0x2
|
|
||||||
LocalFlags = 0x2920, // Size: 0x1, Flags: 0x2
|
|
||||||
NumRespecs = 0x2924, // Size: 0x1, Flags: 0x2
|
|
||||||
SelfResSpell = 0x2928, // Size: 0x1, Flags: 0x2
|
|
||||||
PvpMedals = 0x292C, // Size: 0x1, Flags: 0x2
|
|
||||||
BuybackPrice = 0x2930, // Size: 0xC, Flags: 0x2
|
|
||||||
BuybackTimestamp = 0x2960, // Size: 0xC, Flags: 0x2
|
|
||||||
YesterdayHonorableKills = 0x2990, // Size: 0x1, Flags: 0x2
|
|
||||||
LifetimeHonorableKills = 0x2994, // Size: 0x1, Flags: 0x2
|
|
||||||
WatchedFactionIndex = 0x2998, // Size: 0x1, Flags: 0x2
|
|
||||||
CombatRatings = 0x299C, // Size: 0x20, Flags: 0x2
|
|
||||||
PvpInfo = 0x2A1C, // Size: 0x24, Flags: 0x2
|
|
||||||
MaxLevel = 0x2AAC, // Size: 0x1, Flags: 0x2
|
|
||||||
ScalingPlayerLevelDelta = 0x2AB0, // Size: 0x1, Flags: 0x2
|
|
||||||
MaxCreatureScalingLevel = 0x2AB4, // Size: 0x1, Flags: 0x2
|
|
||||||
NoReagentCostMask = 0x2AB8, // Size: 0x4, Flags: 0x2
|
|
||||||
PetSpellPower = 0x2AC8, // Size: 0x1, Flags: 0x2
|
|
||||||
Researching = 0x2ACC, // Size: 0xA, Flags: 0x2
|
|
||||||
ProfessionSkillLine = 0x2AF4, // Size: 0x2, Flags: 0x2
|
|
||||||
UiHitModifier = 0x2AFC, // Size: 0x1, Flags: 0x2
|
|
||||||
UiSpellHitModifier = 0x2B00, // Size: 0x1, Flags: 0x2
|
|
||||||
HomeRealmTimeOffset = 0x2B04, // Size: 0x1, Flags: 0x2
|
|
||||||
ModPetHaste = 0x2B08, // Size: 0x1, Flags: 0x2
|
|
||||||
OverrideSpellsId = 0x2B0C, // Size: 0x1, Flags: 0x402
|
|
||||||
LfgBonusFactionId = 0x2B10, // Size: 0x1, Flags: 0x2
|
|
||||||
LootSpecId = 0x2B14, // Size: 0x1, Flags: 0x2
|
|
||||||
OverrideZonePvpType = 0x2B18, // Size: 0x1, Flags: 0x402
|
|
||||||
BagSlotFlags = 0x2B1C, // Size: 0x4, Flags: 0x2
|
|
||||||
BankBagSlotFlags = 0x2B2C, // Size: 0x7, Flags: 0x2
|
|
||||||
InsertItemsLeftToRight = 0x2B48, // Size: 0x1, Flags: 0x2
|
|
||||||
QuestCompleted = 0x2B4C, // Size: 0x36B, Flags: 0x2
|
|
||||||
Honor = 0x38F8, // Size: 0x1, Flags: 0x2
|
|
||||||
HonorNextLevel = 0x38FC // Size: 0x1, Flags: 0x2
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum SceneObjectData
|
|
||||||
{
|
|
||||||
ScriptPackageId = 0x30, // Size: 0x1, Flags: 0x1
|
|
||||||
RndSeedVal = 0x34, // Size: 0x1, Flags: 0x1
|
|
||||||
CreatedBy = 0x38, // Size: 0x4, Flags: 0x1
|
|
||||||
SceneType = 0x48 // Size: 0x1, Flags: 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum Unit
|
|
||||||
{
|
|
||||||
CurrentCastId = 0x1B98,
|
|
||||||
CurrentChanneledId = 0x1BB8,
|
|
||||||
AuraTable = 0x1D10,
|
|
||||||
AuraCount = 0x2390,
|
|
||||||
AuraSize = 0x68,
|
|
||||||
ClientRace = 0x2670,
|
|
||||||
DisplayData = 0x1718
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Invalid possibly!
|
|
||||||
internal enum UnitAuras : uint
|
|
||||||
{
|
|
||||||
AuraCount1 = 0x2390,
|
|
||||||
AuraCount2 = 0x1D10,
|
|
||||||
AuraTable1 = 0x1D14,
|
|
||||||
AuraTable2 = 0x1D18,
|
|
||||||
AuraSize = 0x68,
|
|
||||||
|
|
||||||
OwnerGuid = 0x40,
|
|
||||||
AuraSpellId = 0x50,
|
|
||||||
//AuraFlags = 0x54, //Not exactly sure here.
|
|
||||||
//AuraLevel = 0x58, //Not exactly sure here.
|
|
||||||
AuraStack = 0x59,
|
|
||||||
TimeLeft = 0x60,
|
|
||||||
//In case I need it:
|
|
||||||
DruidEclipse = 0x2694
|
|
||||||
}
|
|
||||||
|
|
||||||
// Below is all of the World of Warcraft in-game object field offsets.
|
|
||||||
// Commenting is not used on purpose and enums below should remain internal.
|
|
||||||
internal enum UnitData
|
|
||||||
{
|
|
||||||
Charm = 0x30, // Size: 0x4, Flags: 0x1
|
|
||||||
Summon = 0x40, // Size: 0x4, Flags: 0x1
|
|
||||||
Critter = 0x50, // Size: 0x4, Flags: 0x2
|
|
||||||
CharmedBy = 0x60, // Size: 0x4, Flags: 0x1
|
|
||||||
SummonedBy = 0x70, // Size: 0x4, Flags: 0x1
|
|
||||||
CreatedBy = 0x80, // Size: 0x4, Flags: 0x1
|
|
||||||
DemonCreator = 0x90, // Size: 0x4, Flags: 0x1
|
|
||||||
Target = 0xA0, // Size: 0x4, Flags: 0x1
|
|
||||||
BattlePetCompanionGuid = 0xB0, // Size: 0x4, Flags: 0x1
|
|
||||||
BattlePetDbid = 0xC0, // Size: 0x2, Flags: 0x1
|
|
||||||
ChannelObject = 0xC8, // Size: 0x4, Flags: 0x201
|
|
||||||
ChannelSpell = 0xD8, // Size: 0x1, Flags: 0x201
|
|
||||||
ChannelSpellXSpellVisual = 0xDC, // Size: 0x1, Flags: 0x201
|
|
||||||
SummonedByHomeRealm = 0xE0, // Size: 0x1, Flags: 0x1
|
|
||||||
Sex = 0xE4, // Size: 0x1, Flags: 0x1
|
|
||||||
DisplayPower = 0xE8, // Size: 0x1, Flags: 0x1
|
|
||||||
OverrideDisplayPowerId = 0xEC, // Size: 0x1, Flags: 0x1
|
|
||||||
Health = 0xF0, // Size: 0x2, Flags: 0x1
|
|
||||||
Power = 0xF8, // Size: 0x6, Flags: 0x401
|
|
||||||
TertiaryPower = 0xFC,
|
|
||||||
SecondaryPower = 0x100,
|
|
||||||
MaxHealth = 0x110, // Size: 0x2, Flags: 0x1
|
|
||||||
MaxPower = 0x118, // Size: 0x6, Flags: 0x1
|
|
||||||
PowerRegenFlatModifier = 0x130, // Size: 0x6, Flags: 0x46
|
|
||||||
PowerRegenInterruptedFlatModifier = 0x148, // Size: 0x6, Flags: 0x46
|
|
||||||
Level = 0x160, // Size: 0x1, Flags: 0x1
|
|
||||||
EffectiveLevel = 0x164, // Size: 0x1, Flags: 0x1
|
|
||||||
ScalingLevelMin = 0x168, // Size: 0x1, Flags: 0x1
|
|
||||||
ScalingLevelMax = 0x16C, // Size: 0x1, Flags: 0x1
|
|
||||||
ScalingLevelDelta = 0x170, // Size: 0x1, Flags: 0x1
|
|
||||||
FactionTemplate = 0x174, // Size: 0x1, Flags: 0x1
|
|
||||||
VirtualItems = 0x178, // Size: 0x6, Flags: 0x1
|
|
||||||
Flags = 0x190, // Size: 0x1, Flags: 0x201
|
|
||||||
Flags2 = 0x194, // Size: 0x1, Flags: 0x201
|
|
||||||
Flags3 = 0x198, // Size: 0x1, Flags: 0x201
|
|
||||||
AuraState = 0x19C, // Size: 0x1, Flags: 0x1
|
|
||||||
AttackRoundBaseTime = 0x1A0, // Size: 0x2, Flags: 0x1
|
|
||||||
RangedAttackRoundBaseTime = 0x1A8, // Size: 0x1, Flags: 0x2
|
|
||||||
BoundingRadius = 0x1AC, // Size: 0x1, Flags: 0x1
|
|
||||||
CombatReach = 0x1B0, // Size: 0x1, Flags: 0x1
|
|
||||||
DisplayId = 0x1B4, // Size: 0x1, Flags: 0x280
|
|
||||||
NativeDisplayId = 0x1B8, // Size: 0x1, Flags: 0x201
|
|
||||||
MountDisplayId = 0x1BC, // Size: 0x1, Flags: 0x201
|
|
||||||
MinDamage = 0x1C0, // Size: 0x1, Flags: 0x16
|
|
||||||
MaxDamage = 0x1C4, // Size: 0x1, Flags: 0x16
|
|
||||||
MinOffHandDamage = 0x1C8, // Size: 0x1, Flags: 0x16
|
|
||||||
MaxOffHandDamage = 0x1CC, // Size: 0x1, Flags: 0x16
|
|
||||||
AnimTier = 0x1D0, // Size: 0x1, Flags: 0x1
|
|
||||||
PetNumber = 0x1D4, // Size: 0x1, Flags: 0x1
|
|
||||||
PetNameTimestamp = 0x1D8, // Size: 0x1, Flags: 0x1
|
|
||||||
PetExperience = 0x1DC, // Size: 0x1, Flags: 0x4
|
|
||||||
PetNextLevelExperience = 0x1E0, // Size: 0x1, Flags: 0x4
|
|
||||||
ModCastingSpeed = 0x1E4, // Size: 0x1, Flags: 0x1
|
|
||||||
ModSpellHaste = 0x1E8, // Size: 0x1, Flags: 0x1
|
|
||||||
ModHaste = 0x1EC, // Size: 0x1, Flags: 0x1
|
|
||||||
ModRangedHaste = 0x1F0, // Size: 0x1, Flags: 0x1
|
|
||||||
ModHasteRegen = 0x1F4, // Size: 0x1, Flags: 0x1
|
|
||||||
ModTimeRate = 0x1F8, // Size: 0x1, Flags: 0x1
|
|
||||||
CreatedBySpell = 0x1FC, // Size: 0x1, Flags: 0x1
|
|
||||||
NpcFlags = 0x200, // Size: 0x2, Flags: 0x81
|
|
||||||
EmoteState = 0x208, // Size: 0x1, Flags: 0x1
|
|
||||||
Stats = 0x20C, // Size: 0x4, Flags: 0x6
|
|
||||||
StatPosBuff = 0x21C, // Size: 0x4, Flags: 0x6
|
|
||||||
StatNegBuff = 0x22C, // Size: 0x4, Flags: 0x6
|
|
||||||
Resistances = 0x23C, // Size: 0x7, Flags: 0x16
|
|
||||||
ResistanceBuffModsPositive = 0x258, // Size: 0x7, Flags: 0x6
|
|
||||||
ResistanceBuffModsNegative = 0x274, // Size: 0x7, Flags: 0x6
|
|
||||||
ModBonusArmor = 0x290, // Size: 0x1, Flags: 0x6
|
|
||||||
BaseMana = 0x294, // Size: 0x1, Flags: 0x1
|
|
||||||
BaseHealth = 0x298, // Size: 0x1, Flags: 0x6
|
|
||||||
ShapeshiftForm = 0x29C, // Size: 0x1, Flags: 0x1
|
|
||||||
AttackPower = 0x2A0, // Size: 0x1, Flags: 0x6
|
|
||||||
AttackPowerModPos = 0x2A4, // Size: 0x1, Flags: 0x6
|
|
||||||
AttackPowerModNeg = 0x2A8, // Size: 0x1, Flags: 0x6
|
|
||||||
AttackPowerMultiplier = 0x2AC, // Size: 0x1, Flags: 0x6
|
|
||||||
RangedAttackPower = 0x2B0, // Size: 0x1, Flags: 0x6
|
|
||||||
RangedAttackPowerModPos = 0x2B4, // Size: 0x1, Flags: 0x6
|
|
||||||
RangedAttackPowerModNeg = 0x2B8, // Size: 0x1, Flags: 0x6
|
|
||||||
RangedAttackPowerMultiplier = 0x2BC, // Size: 0x1, Flags: 0x6
|
|
||||||
SetAttackSpeedAura = 0x2C0, // Size: 0x1, Flags: 0x6
|
|
||||||
MinRangedDamage = 0x2C4, // Size: 0x1, Flags: 0x6
|
|
||||||
MaxRangedDamage = 0x2C8, // Size: 0x1, Flags: 0x6
|
|
||||||
PowerCostModifier = 0x2CC, // Size: 0x7, Flags: 0x6
|
|
||||||
PowerCostMultiplier = 0x2E8, // Size: 0x7, Flags: 0x6
|
|
||||||
MaxHealthModifier = 0x304, // Size: 0x1, Flags: 0x6
|
|
||||||
HoverHeight = 0x308, // Size: 0x1, Flags: 0x1
|
|
||||||
MinItemLevelCutoff = 0x30C, // Size: 0x1, Flags: 0x1
|
|
||||||
MinItemLevel = 0x310, // Size: 0x1, Flags: 0x1
|
|
||||||
MaxItemLevel = 0x314, // Size: 0x1, Flags: 0x1
|
|
||||||
WildBattlePetLevel = 0x318, // Size: 0x1, Flags: 0x1
|
|
||||||
BattlePetCompanionNameTimestamp = 0x31C, // Size: 0x1, Flags: 0x1
|
|
||||||
InteractSpellId = 0x320, // Size: 0x1, Flags: 0x1
|
|
||||||
StateSpellVisualId = 0x324, // Size: 0x1, Flags: 0x280
|
|
||||||
StateAnimId = 0x328, // Size: 0x1, Flags: 0x280
|
|
||||||
StateAnimKitId = 0x32C, // Size: 0x1, Flags: 0x280
|
|
||||||
StateWorldEffectId = 0x330, // Size: 0x4, Flags: 0x280
|
|
||||||
ScaleDuration = 0x340, // Size: 0x1, Flags: 0x1
|
|
||||||
LooksLikeMountId = 0x344, // Size: 0x1, Flags: 0x1
|
|
||||||
LooksLikeCreatureId = 0x348, // Size: 0x1, Flags: 0x1
|
|
||||||
LookAtControllerId = 0x34C, // Size: 0x1, Flags: 0x1
|
|
||||||
LookAtControllerTarget = 0x350 // Size: 0x4, Flags: 0x1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Process.NET;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public class WoWPlayer : WoWUnit
|
|
||||||
{
|
|
||||||
private readonly IntPtr _targetIntPtr;
|
|
||||||
|
|
||||||
public WoWPlayer(IProcess process, IntPtr baseAddress, IntPtr targetIntPtr, bool readPointer = false)
|
|
||||||
: base(process, baseAddress, readPointer)
|
|
||||||
{
|
|
||||||
_targetIntPtr = targetIntPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WoWObject GetTarget(WoWObjectManager manager)
|
|
||||||
{
|
|
||||||
var targetGuid = Process.Memory.Read<Guid>(Process.Native.MainModule.BaseAddress + _targetIntPtr.ToInt32());
|
|
||||||
return manager.EnumVisibleObjects().FirstOrDefault(o => o.Guid == targetGuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Artemis.Modules.Games.WoW.Data.WowSharp.Client.Patchables;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public static class WoWStructs
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
public struct ObjectData
|
|
||||||
{
|
|
||||||
// x32 : x64
|
|
||||||
[FieldOffset(0)] private readonly IntPtr vtable; // 0x00 0x00
|
|
||||||
[FieldOffset(10)] public IntPtr Descriptors; // 0x04 0x10
|
|
||||||
[FieldOffset(18)] private readonly IntPtr unk1; // 0x08 0x18
|
|
||||||
[FieldOffset(20)] public int ObjectType; // 0x0C 0x20
|
|
||||||
[FieldOffset(24)] private readonly IntPtr unk3; // 0x10 0x24
|
|
||||||
[FieldOffset(28)] private readonly IntPtr unk4; // 0x14 0x28
|
|
||||||
[FieldOffset(30)] private readonly IntPtr unk5; // 0x18 0x30
|
|
||||||
[FieldOffset(38)] private readonly IntPtr unk6; // 0x1C 0x38
|
|
||||||
[FieldOffset(40)] private readonly IntPtr unk7; // 0x20 0x40
|
|
||||||
[FieldOffset(48)] private readonly IntPtr unk8; // 0x24 0x48
|
|
||||||
[FieldOffset(50)] public Guid Guid; // 0x28 0x50
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Guid
|
|
||||||
{
|
|
||||||
private readonly Int128 _mGuid;
|
|
||||||
|
|
||||||
public static readonly Guid Zero = new Guid(0);
|
|
||||||
|
|
||||||
public Guid(Int128 guid)
|
|
||||||
{
|
|
||||||
_mGuid = guid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
// ReSharper disable once SwitchStatementMissingSomeCases
|
|
||||||
switch (Type)
|
|
||||||
{
|
|
||||||
case WoWEnums.GuidType.Creature:
|
|
||||||
case WoWEnums.GuidType.Vehicle:
|
|
||||||
case WoWEnums.GuidType.Pet:
|
|
||||||
case WoWEnums.GuidType.GameObject:
|
|
||||||
case WoWEnums.GuidType.AreaTrigger:
|
|
||||||
case WoWEnums.GuidType.DynamicObject:
|
|
||||||
case WoWEnums.GuidType.Corpse:
|
|
||||||
case WoWEnums.GuidType.LootObject:
|
|
||||||
case WoWEnums.GuidType.SceneObject:
|
|
||||||
case WoWEnums.GuidType.Scenario:
|
|
||||||
case WoWEnums.GuidType.AiGroup:
|
|
||||||
case WoWEnums.GuidType.DynamicDoor:
|
|
||||||
case WoWEnums.GuidType.Vignette:
|
|
||||||
case WoWEnums.GuidType.Conversation:
|
|
||||||
case WoWEnums.GuidType.CallForHelp:
|
|
||||||
case WoWEnums.GuidType.AiResource:
|
|
||||||
case WoWEnums.GuidType.AiLock:
|
|
||||||
case WoWEnums.GuidType.AiLockTicket:
|
|
||||||
return $"{Type}-{SubType}-{RealmId}-{MapId}-{ServerId}-{Id}-{CreationBits:X10}";
|
|
||||||
case WoWEnums.GuidType.Player:
|
|
||||||
return $"{Type}-{RealmId}-{(ulong) (_mGuid >> 64):X8}";
|
|
||||||
case WoWEnums.GuidType.Item:
|
|
||||||
return $"{Type}-{RealmId}-{(uint) ((_mGuid >> 18) & 0xFFFFFF)}-{(ulong) (_mGuid >> 64):X10}";
|
|
||||||
//case GuidType.ClientActor:
|
|
||||||
// return String.Format("{0}-{1}-{2}", Type, RealmId, CreationBits);
|
|
||||||
//case GuidType.Transport:
|
|
||||||
//case GuidType.StaticDoor:
|
|
||||||
// return String.Format("{0}-{1}-{2}", Type, RealmId, CreationBits);
|
|
||||||
default:
|
|
||||||
return $"{Type}-{_mGuid:X32}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is Guid)
|
|
||||||
return _mGuid == ((Guid) obj)._mGuid;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return _mGuid.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WoWEnums.GuidType Type => (WoWEnums.GuidType) (byte) ((_mGuid >> 58) & 0x3F);
|
|
||||||
|
|
||||||
public byte SubType => (byte) ((_mGuid >> 120) & 0x3F);
|
|
||||||
|
|
||||||
public ushort RealmId => (ushort) ((_mGuid >> 42) & 0x1FFF);
|
|
||||||
|
|
||||||
public ushort ServerId => (ushort) ((_mGuid >> 104) & 0x1FFF);
|
|
||||||
|
|
||||||
public ushort MapId => (ushort) ((_mGuid >> 29) & 0x1FFF);
|
|
||||||
|
|
||||||
// Creature, Pet, Vehicle
|
|
||||||
public uint Id => (uint) ((_mGuid >> 6) & 0x7FFFFF);
|
|
||||||
|
|
||||||
public ulong CreationBits => (ulong) ((_mGuid >> 64) & 0xFFFFFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Manager
|
|
||||||
|
|
||||||
// Region is here due to the large amount of structs-
|
|
||||||
// the CurremtManager struct depends on.
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct CurrentManager
|
|
||||||
{
|
|
||||||
public TsHashTable VisibleObjects; // m_objects
|
|
||||||
public TsHashTable LazyCleanupObjects; // m_lazyCleanupObjects
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)]
|
|
||||||
// m_lazyCleanupFifo, m_freeObjects, m_visibleObjects, m_reenabledObjects, whateverObjects...
|
|
||||||
public TsExplicitList[] Links; // Links[13] has all objects stored in VisibleObjects it seems
|
|
||||||
|
|
||||||
#if !X64
|
|
||||||
public int Unknown1; // wtf is that and why x86 only?
|
|
||||||
#endif
|
|
||||||
public Int128 ActivePlayer;
|
|
||||||
public int MapId;
|
|
||||||
public IntPtr ClientConnection;
|
|
||||||
public IntPtr MovementGlobals;
|
|
||||||
public int Unk1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct Ts
|
|
||||||
{
|
|
||||||
public IntPtr vtable;
|
|
||||||
public uint m_alloc;
|
|
||||||
public uint m_count;
|
|
||||||
public IntPtr m_data; //TSExplicitList* m_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsExplicitList
|
|
||||||
{
|
|
||||||
public TsList baseClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsFixedArray
|
|
||||||
{
|
|
||||||
public Ts baseClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsGrowableArray
|
|
||||||
{
|
|
||||||
public TsFixedArray baseclass;
|
|
||||||
public uint m_chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsHashTable
|
|
||||||
{
|
|
||||||
public IntPtr vtable;
|
|
||||||
public TsExplicitList m_fulllist;
|
|
||||||
public int m_fullnessIndicator;
|
|
||||||
public TsGrowableArray m_slotlistarray;
|
|
||||||
public int m_slotmask;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsLink
|
|
||||||
{
|
|
||||||
public IntPtr m_prevlink; //TSLink *m_prevlink
|
|
||||||
public IntPtr m_next; // C_OBJECTHASH *m_next
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct TsList
|
|
||||||
{
|
|
||||||
public int m_linkoffset;
|
|
||||||
public TsLink m_terminator;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Process.NET;
|
|
||||||
using static Artemis.Modules.Games.WoW.Data.WoWEnums;
|
|
||||||
using static Artemis.Modules.Games.WoW.Data.WoWOffsets;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW.Data
|
|
||||||
{
|
|
||||||
public class WoWUnit : WoWObject
|
|
||||||
{
|
|
||||||
public WoWUnit(IProcess process, IntPtr baseAddress, bool readPointer = false)
|
|
||||||
: base(process, baseAddress, readPointer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Health => ReadField<int>(UnitData.Health);
|
|
||||||
public int MaxHealth => ReadField<int>(UnitData.MaxHealth);
|
|
||||||
public int Power => ReadField<int>(UnitData.Power);
|
|
||||||
public int SecondaryPower => ReadField<int>(UnitData.SecondaryPower);
|
|
||||||
public int TertiaryPower => ReadField<int>(UnitData.TertiaryPower);
|
|
||||||
public int MaxPower => ReadField<int>(UnitData.MaxPower);
|
|
||||||
public PowerType PowerType => (PowerType) ReadField<int>(UnitData.DisplayPower);
|
|
||||||
public int Level => ReadField<int>(UnitData.Level);
|
|
||||||
|
|
||||||
public WoWDetails Details { get; set; }
|
|
||||||
|
|
||||||
public void UpdateDetails(WoWNameCache nameCache)
|
|
||||||
{
|
|
||||||
Details = GetNpcDetails() ?? nameCache.GetNameByGuid(Guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
Artemis/Artemis/Modules/Games/WoW/Models/WoWAura.cs
Normal file
45
Artemis/Artemis/Modules/Games/WoW/Models/WoWAura.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class WoWAura
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int Stacks { get; set; }
|
||||||
|
public DateTime StartTime { set; get; }
|
||||||
|
public DateTime EndTime { get; set; }
|
||||||
|
public float Progress { get; set; }
|
||||||
|
|
||||||
|
public void ApplyJson(JToken buffJson)
|
||||||
|
{
|
||||||
|
Name = buffJson["n"].Value<string>();
|
||||||
|
Id = buffJson["id"].Value<int>();
|
||||||
|
if (buffJson["c"] != null)
|
||||||
|
Stacks = buffJson["c"].Value<int>();
|
||||||
|
|
||||||
|
if (buffJson["e"] != null)
|
||||||
|
{
|
||||||
|
var expires = buffJson["e"].Value<int>();
|
||||||
|
var tickCount = Environment.TickCount;
|
||||||
|
var timeLeft = expires - tickCount;
|
||||||
|
EndTime = DateTime.Now.AddMilliseconds(timeLeft);
|
||||||
|
}
|
||||||
|
if (buffJson["d"] != null)
|
||||||
|
StartTime = EndTime.AddSeconds(buffJson["d"].Value<int>() * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProgress()
|
||||||
|
{
|
||||||
|
var elapsed = DateTime.Now - StartTime;
|
||||||
|
var total = EndTime - StartTime;
|
||||||
|
|
||||||
|
Progress = (float) (elapsed.TotalMilliseconds / total.TotalMilliseconds);
|
||||||
|
if (Progress > 1)
|
||||||
|
Progress = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Artemis/Artemis/Modules/Games/WoW/Models/WoWCastBar.cs
Normal file
64
Artemis/Artemis/Modules/Games/WoW/Models/WoWCastBar.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class WoWCastBar
|
||||||
|
{
|
||||||
|
public WoWCastBar()
|
||||||
|
{
|
||||||
|
Spell = new WoWSpell();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WoWSpell Spell { get; set; }
|
||||||
|
public DateTime StartTime { set; get; }
|
||||||
|
public DateTime EndTime { get; set; }
|
||||||
|
public bool NonInterruptible { get; set; }
|
||||||
|
public float Progress { get; set; }
|
||||||
|
public bool IsChannel { get; set; }
|
||||||
|
|
||||||
|
public void ApplyJson(JToken spellJson, bool isChannel)
|
||||||
|
{
|
||||||
|
var castMs = spellJson["e"].Value<int>() - spellJson["s"].Value<int>();
|
||||||
|
var tickCount = Environment.TickCount;
|
||||||
|
var difference = tickCount - spellJson["s"].Value<int>();
|
||||||
|
|
||||||
|
Spell.Name = spellJson["n"].Value<string>();
|
||||||
|
Spell.Id = spellJson["sid"].Value<int>();
|
||||||
|
StartTime = new DateTime(DateTime.Now.Ticks + difference);
|
||||||
|
EndTime = StartTime.AddMilliseconds(castMs);
|
||||||
|
NonInterruptible = spellJson["ni"].Value<bool>();
|
||||||
|
IsChannel = isChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProgress()
|
||||||
|
{
|
||||||
|
if (Spell.Name == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var elapsed = DateTime.Now - StartTime;
|
||||||
|
var total = EndTime - StartTime;
|
||||||
|
|
||||||
|
if (IsChannel)
|
||||||
|
Progress = 1 - (float) (elapsed.TotalMilliseconds / total.TotalMilliseconds);
|
||||||
|
else
|
||||||
|
Progress = (float) (elapsed.TotalMilliseconds / total.TotalMilliseconds);
|
||||||
|
|
||||||
|
if (Progress > 1 || Progress < 0)
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Spell.Name = null;
|
||||||
|
Spell.Id = 0;
|
||||||
|
StartTime = DateTime.MinValue;
|
||||||
|
EndTime = DateTime.MinValue;
|
||||||
|
NonInterruptible = false;
|
||||||
|
Progress = 0;
|
||||||
|
IsChannel = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Artemis/Artemis/Modules/Games/WoW/Models/WoWEnums.cs
Normal file
44
Artemis/Artemis/Modules/Games/WoW/Models/WoWEnums.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
public enum WoWRace
|
||||||
|
{
|
||||||
|
Human,
|
||||||
|
Orc,
|
||||||
|
Dwarf,
|
||||||
|
NightElf,
|
||||||
|
Undead,
|
||||||
|
Tauren,
|
||||||
|
Gnome,
|
||||||
|
Troll,
|
||||||
|
BloodElf,
|
||||||
|
Draenei,
|
||||||
|
Goblin,
|
||||||
|
Worgen,
|
||||||
|
Pandaren
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WoWPowerType
|
||||||
|
{
|
||||||
|
Mana = 0,
|
||||||
|
Rage = 1,
|
||||||
|
Focus = 2,
|
||||||
|
Energy = 3,
|
||||||
|
ComboPoints = 4,
|
||||||
|
Runes = 5,
|
||||||
|
RunicPower = 6,
|
||||||
|
SoulShards = 7,
|
||||||
|
LunarPower = 8,
|
||||||
|
HolyPower = 9,
|
||||||
|
AlternatePower = 10,
|
||||||
|
Maelstrom = 11,
|
||||||
|
Chi = 12,
|
||||||
|
Insanity = 13,
|
||||||
|
ArcaneCharges = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WoWGender
|
||||||
|
{
|
||||||
|
Male,
|
||||||
|
Female
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class WoWSpecialization
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Role { get; set; }
|
||||||
|
|
||||||
|
public void ApplyJson(JToken specJson)
|
||||||
|
{
|
||||||
|
Name = specJson["n"].Value<string>();
|
||||||
|
Id = specJson["id"].Value<int>();
|
||||||
|
Role = specJson["r"].Value<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Artemis/Artemis/Modules/Games/WoW/Models/WoWSpell.cs
Normal file
11
Artemis/Artemis/Modules/Games/WoW/Models/WoWSpell.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using MoonSharp.Interpreter;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class WoWSpell
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
134
Artemis/Artemis/Modules/Games/WoW/Models/WoWUnit.cs
Normal file
134
Artemis/Artemis/Modules/Games/WoW/Models/WoWUnit.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Utilities;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW.Models
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class WoWUnit
|
||||||
|
{
|
||||||
|
private readonly List<WoWSpell> _currentFrameCasts = new List<WoWSpell>();
|
||||||
|
|
||||||
|
public WoWUnit()
|
||||||
|
{
|
||||||
|
CastBar = new WoWCastBar();
|
||||||
|
Specialization = new WoWSpecialization();
|
||||||
|
|
||||||
|
Buffs = new List<WoWAura>();
|
||||||
|
Debuffs = new List<WoWAura>();
|
||||||
|
RecentIntantCasts = new List<WoWSpell>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Level { get; set; }
|
||||||
|
public int Health { get; set; }
|
||||||
|
public int MaxHealth { get; set; }
|
||||||
|
public int Power { get; set; }
|
||||||
|
public int MaxPower { get; set; }
|
||||||
|
public WoWPowerType PowerType { get; set; }
|
||||||
|
public string Class { get; set; }
|
||||||
|
public WoWRace Race { get; set; }
|
||||||
|
public WoWGender Gender { get; set; }
|
||||||
|
|
||||||
|
public WoWCastBar CastBar { get; set; }
|
||||||
|
public WoWSpecialization Specialization { get; }
|
||||||
|
|
||||||
|
public List<WoWAura> Buffs { get; }
|
||||||
|
public List<WoWAura> Debuffs { get; }
|
||||||
|
public List<WoWSpell> RecentIntantCasts { get; }
|
||||||
|
|
||||||
|
public void ApplyJson(JToken json)
|
||||||
|
{
|
||||||
|
if (json["n"] == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Name = json["n"].Value<string>();
|
||||||
|
Level = json["l"].Value<int>();
|
||||||
|
Class = json["c"].Value<string>();
|
||||||
|
Gender = json["g"].Value<int>() == 3 ? WoWGender.Female : WoWGender.Male;
|
||||||
|
|
||||||
|
if (json["r"] != null)
|
||||||
|
Race = GeneralHelpers.ParseEnum<WoWRace>(json["r"].Value<string>());
|
||||||
|
if (json["s"] != null)
|
||||||
|
Specialization.ApplyJson(json["s"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyStateJson(JToken json)
|
||||||
|
{
|
||||||
|
Health = json["h"].Value<int>();
|
||||||
|
MaxHealth = json["mh"].Value<int>();
|
||||||
|
PowerType = GeneralHelpers.ParseEnum<WoWPowerType>(json["t"].Value<int>().ToString());
|
||||||
|
Power = json["p"].Value<int>();
|
||||||
|
MaxPower = json["mp"].Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyAuraJson(JToken json, bool buffs)
|
||||||
|
{
|
||||||
|
if (buffs)
|
||||||
|
{
|
||||||
|
lock (Buffs)
|
||||||
|
{
|
||||||
|
Buffs.Clear();
|
||||||
|
foreach (var auraJson in json.Children())
|
||||||
|
{
|
||||||
|
var aura = new WoWAura();
|
||||||
|
aura.ApplyJson(auraJson);
|
||||||
|
Buffs.Add(aura);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (Debuffs)
|
||||||
|
{
|
||||||
|
Debuffs.Clear();
|
||||||
|
foreach (var auraJson in json.Children())
|
||||||
|
{
|
||||||
|
var aura = new WoWAura();
|
||||||
|
aura.ApplyJson(auraJson);
|
||||||
|
Debuffs.Add(aura);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstantCast(WoWSpell spell)
|
||||||
|
{
|
||||||
|
lock (_currentFrameCasts)
|
||||||
|
{
|
||||||
|
_currentFrameCasts.Add(spell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearInstantCasts()
|
||||||
|
{
|
||||||
|
lock (_currentFrameCasts)
|
||||||
|
{
|
||||||
|
// Remove all casts that weren't cast in the after the last frame
|
||||||
|
RecentIntantCasts.Clear();
|
||||||
|
RecentIntantCasts.AddRange(_currentFrameCasts);
|
||||||
|
|
||||||
|
// Clear the that were after the last frame so that they are removed next frame when this method is called again
|
||||||
|
_currentFrameCasts.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
CastBar.UpdateProgress();
|
||||||
|
ClearInstantCasts();
|
||||||
|
|
||||||
|
lock (Buffs)
|
||||||
|
{
|
||||||
|
foreach (var buff in Buffs)
|
||||||
|
buff.UpdateProgress();
|
||||||
|
}
|
||||||
|
lock (Debuffs)
|
||||||
|
{
|
||||||
|
foreach (var debuff in Debuffs)
|
||||||
|
debuff.UpdateProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
## Interface: 70300
|
||||||
|
## Title: Artemis
|
||||||
|
## Notes: Transmits ingame data to Artemis
|
||||||
|
## Author: SpoinkyNL
|
||||||
|
## Version: 1.0.0
|
||||||
|
|
||||||
|
embeds.xml
|
||||||
|
|
||||||
|
Core.lua
|
||||||
@ -0,0 +1,398 @@
|
|||||||
|
Artemis = LibStub("AceAddon-3.0"):NewAddon("Artemis", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0")
|
||||||
|
json = LibStub("json")
|
||||||
|
|
||||||
|
-- Hook onto logout because it seems PLAYER_LOGOUT fires on reload as well
|
||||||
|
local _Logout = Logout
|
||||||
|
|
||||||
|
local debugging = false
|
||||||
|
local lastLine = {}
|
||||||
|
local channeling = {}
|
||||||
|
local unitUpdates = {}
|
||||||
|
local lastTransmitMessage
|
||||||
|
local lastTransmitTime
|
||||||
|
local lastBuffs = "";
|
||||||
|
local lastDebuffs = "";
|
||||||
|
local prefixCounts = {}
|
||||||
|
|
||||||
|
channeling["player"] = false
|
||||||
|
channeling["target"] = false
|
||||||
|
|
||||||
|
function Artemis:OnEnable()
|
||||||
|
-- Register all the various events that Artemis will want to know about
|
||||||
|
Artemis:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||||
|
Artemis:RegisterEvent("PLAYER_LEVEL_UP")
|
||||||
|
Artemis:RegisterEvent("PLAYER_FLAGS_CHANGED")
|
||||||
|
Artemis:RegisterEvent("ACHIEVEMENT_EARNED")
|
||||||
|
Artemis:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
|
||||||
|
Artemis:RegisterEvent("UNIT_TARGET")
|
||||||
|
Artemis:RegisterEvent("UNIT_HEALTH")
|
||||||
|
Artemis:RegisterEvent("UNIT_POWER")
|
||||||
|
Artemis:RegisterEvent("UNIT_AURA")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_START")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_FAILED")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_DELAYED")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
|
||||||
|
Artemis:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
|
||||||
|
Artemis:RegisterEvent("ZONE_CHANGED")
|
||||||
|
Artemis:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||||||
|
|
||||||
|
-- Register the chat command /artemis <something>
|
||||||
|
Artemis:RegisterChatCommand("artemis", "HandleChatCommand")
|
||||||
|
|
||||||
|
-- Create a loop that'll periodically send character data in case of an Artemis restart/late start
|
||||||
|
Artemis:ScheduleRepeatingTimer("PeriodicUpdate", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:HandleChatCommand(input)
|
||||||
|
if input == "debug" then
|
||||||
|
debugging = not (debugging)
|
||||||
|
if debugging then
|
||||||
|
Artemis:Print("Debugging enabled.")
|
||||||
|
else
|
||||||
|
Artemis:Print("Debugging disabled.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if input == "rc" then
|
||||||
|
prefixCounts = {}
|
||||||
|
Artemis:Print("Reset the send counters.")
|
||||||
|
end
|
||||||
|
if input == nill or input == "" or input == "help" then
|
||||||
|
Artemis:Print("Available chat commands:")
|
||||||
|
Artemis:Printf("|cffb7b7b7/artemis debug|r: Toggle debugging")
|
||||||
|
Artemis:Printf("|cffb7b7b7/artemis rc|r: Reset the debug counters")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:Transmit(prefix, data, prio)
|
||||||
|
local msg = "artemis(".. prefix .. "|" .. json.encode(data) ..")"
|
||||||
|
-- If the message is the same as the previous, make sure it wasn't sent less than 250ms ago
|
||||||
|
if msg == lastTransmitMessage then
|
||||||
|
if not (lastTransmitTime == nil) then
|
||||||
|
local diff = GetTime() - lastTransmitTime;
|
||||||
|
if (diff < 0.25) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
lastTransmitTime = GetTime()
|
||||||
|
|
||||||
|
if debugging == true then
|
||||||
|
if prefixCounts[prefix] == nill then
|
||||||
|
prefixCounts[prefix] = 0
|
||||||
|
end
|
||||||
|
prefixCounts[prefix] = prefixCounts[prefix] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if debugging == true then
|
||||||
|
Artemis:Printf("Transmitting with prefix |cfffdff71" .. prefix .. "|r (" .. prefixCounts[prefix] .. ").")
|
||||||
|
Artemis:Print(msg)
|
||||||
|
end
|
||||||
|
Artemis:SendCommMessage("(artemis)", msg, "WHISPER", UnitName("player"), prio)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:TransmitUnitState(unit, ignoreThrottle)
|
||||||
|
if not ignoreThrottle then
|
||||||
|
if not (unitUpdates[unit] == nil) then
|
||||||
|
local diff = GetTime() - unitUpdates[unit]
|
||||||
|
if (diff < 0.5) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local table = {
|
||||||
|
h = UnitHealth(unit),
|
||||||
|
mh = UnitHealthMax(unit),
|
||||||
|
p = UnitPower(unit),
|
||||||
|
mp = UnitPowerMax(unit),
|
||||||
|
t = UnitPowerType(unit)
|
||||||
|
};
|
||||||
|
|
||||||
|
unitUpdates[unit] = GetTime()
|
||||||
|
Artemis:Transmit(unit .. "State", table)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:GetUnitDetails(unit)
|
||||||
|
return {
|
||||||
|
n = UnitName(unit),
|
||||||
|
c = UnitClass(unit),
|
||||||
|
l = UnitLevel(unit),
|
||||||
|
r = UnitRace(unit),
|
||||||
|
g = UnitSex(unit),
|
||||||
|
f = UnitFactionGroup(unit)
|
||||||
|
};
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:GetPlayerDetails()
|
||||||
|
local details = Artemis:GetUnitDetails("player")
|
||||||
|
local id, name, _, _, role = GetSpecializationInfo(GetSpecialization())
|
||||||
|
|
||||||
|
details.realm = GetRealmName()
|
||||||
|
details.achievementPoints = GetTotalAchievementPoints(false)
|
||||||
|
details.s = {id = id, n = name, r = role}
|
||||||
|
|
||||||
|
return details
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:GetUnitAuras(unit, filter)
|
||||||
|
local auras = {};
|
||||||
|
for index = 1, 40 do
|
||||||
|
local name, _, _, count, _, duration, expires, caster, _, _, spellID = UnitAura(unit, index, filter);
|
||||||
|
if not (name == nil) then
|
||||||
|
local buffTable = {n = name, id = spellID}
|
||||||
|
-- Leave these values out if they are 0 to save some space
|
||||||
|
if count > 0 then
|
||||||
|
buffTable["c"] = count
|
||||||
|
end
|
||||||
|
if duration > 0 then
|
||||||
|
buffTable["d"] = duration
|
||||||
|
end
|
||||||
|
if expires > 0 then
|
||||||
|
buffTable["e"] = expires
|
||||||
|
end
|
||||||
|
table.insert(auras, buffTable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return auras
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:PeriodicUpdate()
|
||||||
|
-- Don't do this in combat, enough data going out at that time already
|
||||||
|
if InCombatLockdown() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Artemis:Transmit("player", Artemis:GetPlayerDetails())
|
||||||
|
Artemis:TransmitUnitState("player", true);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:PLAYER_ENTERING_WORLD(...)
|
||||||
|
Artemis:Transmit("player", Artemis:GetPlayerDetails())
|
||||||
|
Artemis:TransmitUnitState("player", true);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Logout()
|
||||||
|
Artemis:Transmit("gameState", "loggedOut")
|
||||||
|
return _Logout()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:PLAYER_LEVEL_UP(...)
|
||||||
|
Artemis:Transmit("player", Artemis:GetPlayerDetails())
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:PLAYER_FLAGS_CHANGED(...)
|
||||||
|
local _, unitID = ...
|
||||||
|
if unitID == "player" then
|
||||||
|
-- AFK overwrites DND
|
||||||
|
if UnitIsAFK("player") then
|
||||||
|
Artemis:Transmit("gameState", "afk")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if UnitIsDND("player") then
|
||||||
|
Artemis:Transmit("gameState", "dnd")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Artemis:Transmit("gameState", "ingame")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:ACHIEVEMENT_EARNED(...)
|
||||||
|
Artemis:Transmit("player", Artemis:GetPlayerDetails())
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:ACTIVE_TALENT_GROUP_CHANGED(...)
|
||||||
|
Artemis:Transmit("player", Artemis:GetPlayerDetails())
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:UNIT_TARGET(...)
|
||||||
|
local _, source = ...
|
||||||
|
if not (source == "player") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local details = Artemis:GetUnitDetails("target")
|
||||||
|
channeling["target"] = false
|
||||||
|
|
||||||
|
Artemis:Transmit("target", details)
|
||||||
|
Artemis:TransmitUnitState("target", true);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:UNIT_HEALTH(...)
|
||||||
|
local _, source = ...
|
||||||
|
if not (source == "player") and not (source == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Artemis:TransmitUnitState(source, false);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:UNIT_POWER(...)
|
||||||
|
local _, source = ...
|
||||||
|
if not (source == "player") and not (source == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Artemis:TransmitUnitState(source, false);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:UNIT_AURA(...)
|
||||||
|
local _, source = ...
|
||||||
|
if not (source == "player") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local buffs = Artemis:GetUnitAuras(source, "PLAYER|HELPFUL")
|
||||||
|
local debuffs = Artemis:GetUnitAuras(source, "PLAYER|HARMFUL")
|
||||||
|
|
||||||
|
local newBuffs = json.encode(buffs)
|
||||||
|
local newDebuffs = json.encode(debuffs)
|
||||||
|
|
||||||
|
if not (lastBuffs == newBuffs) then
|
||||||
|
Artemis:Transmit("buffs", buffs)
|
||||||
|
end
|
||||||
|
if not (lastDebuffs == newDebuffs) then
|
||||||
|
Artemis:Transmit("debuffs", debuffs)
|
||||||
|
end
|
||||||
|
|
||||||
|
lastBuffs = newBuffs
|
||||||
|
lastDebuffs = newDebuffs
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect non-instant spell casts
|
||||||
|
function Artemis:UNIT_SPELLCAST_START(...)
|
||||||
|
local _, unitID, spell, rank, lineID, spellID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
|
||||||
|
local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
|
||||||
|
lastLine[unitID] = lineID
|
||||||
|
|
||||||
|
Artemis:Transmit("spellCast", table, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect instant spell casts
|
||||||
|
function Artemis:UNIT_SPELLCAST_SUCCEEDED (...)
|
||||||
|
local _, unitID, spell, rank, lineID, spellID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if channeling[unitID] == true then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Many spells are irrelevant system spells, don't transmit these
|
||||||
|
if unitID == "player" and not (IsPlayerSpell(spellID)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(unitID)
|
||||||
|
-- Don't trigger on the success of a non instant cast
|
||||||
|
if not (lastLine[unitID] == nil) and lastLine[unitID] == lineID then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set back the last line to what is currently being cast (Fireblast during Fireball per example)
|
||||||
|
if not (name == nil) then
|
||||||
|
lastLine[unitID] = castID
|
||||||
|
else
|
||||||
|
lastLine[unitID] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local table = {uid = unitID, n = spell, sid = spellID}
|
||||||
|
|
||||||
|
Artemis:Transmit("instantSpellCast", table, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect falure of non instant casts
|
||||||
|
function Artemis:UNIT_SPELLCAST_FAILED (...)
|
||||||
|
local source, unitID, _, _, lineID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lastLine[unitID] = nil
|
||||||
|
|
||||||
|
Artemis:Transmit("spellCastFailed", unitID, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect falure of non instant casts
|
||||||
|
function Artemis:UNIT_SPELLCAST_DELAYED (...)
|
||||||
|
local _, unitID, spell, rank, lineID, spellID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
|
||||||
|
local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
|
||||||
|
|
||||||
|
Artemis:Transmit("spellCast", table, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect cancellation of non instant casts
|
||||||
|
function Artemis:UNIT_SPELLCAST_INTERRUPTED (...)
|
||||||
|
local source, unitID, _, _, lineID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lastLine[unitID] = nil
|
||||||
|
|
||||||
|
Artemis:Transmit("spellCastInterrupted", unitID, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect spell channels
|
||||||
|
function Artemis:UNIT_SPELLCAST_CHANNEL_START(...)
|
||||||
|
local _, unitID, spell, rank, lineID, spellID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
channeling[unitID] = true
|
||||||
|
|
||||||
|
local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
|
||||||
|
local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
|
||||||
|
|
||||||
|
Artemis:Transmit("spellChannel", table, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:UNIT_SPELLCAST_CHANNEL_UPDATE (...)
|
||||||
|
local _, unitID, spell, rank, lineID, spellID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
|
||||||
|
local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
|
||||||
|
|
||||||
|
Artemis:Transmit("spellChannel", table, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect cancellation of channels
|
||||||
|
function Artemis:UNIT_SPELLCAST_CHANNEL_STOP (...)
|
||||||
|
local source, unitID, _, _, lineID = ...
|
||||||
|
if not (unitID == "player") and not (unitID == "target") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
channeling[unitID] = false
|
||||||
|
|
||||||
|
Artemis:Transmit("spellChannelInterrupted", unitID, "ALERT")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Artemis:ZONE_CHANGED_NEW_AREA (...)
|
||||||
|
local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
|
||||||
|
|
||||||
|
Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
|
||||||
|
end
|
||||||
|
function Artemis:ZONE_CHANGED (...)
|
||||||
|
local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
|
||||||
|
|
||||||
|
Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
|
||||||
|
end
|
||||||
@ -0,0 +1,674 @@
|
|||||||
|
--- **AceAddon-3.0** provides a template for creating addon objects.
|
||||||
|
-- It'll provide you with a set of callback functions that allow you to simplify the loading
|
||||||
|
-- process of your addon.\\
|
||||||
|
-- Callbacks provided are:\\
|
||||||
|
-- * **OnInitialize**, which is called directly after the addon is fully loaded.
|
||||||
|
-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
|
||||||
|
-- * **OnDisable**, which is only called when your addon is manually being disabled.
|
||||||
|
-- @usage
|
||||||
|
-- -- A small (but complete) addon, that doesn't do anything,
|
||||||
|
-- -- but shows usage of the callbacks.
|
||||||
|
-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
|
||||||
|
--
|
||||||
|
-- function MyAddon:OnInitialize()
|
||||||
|
-- -- do init tasks here, like loading the Saved Variables,
|
||||||
|
-- -- or setting up slash commands.
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- function MyAddon:OnEnable()
|
||||||
|
-- -- Do more initialization here, that really enables the use of your addon.
|
||||||
|
-- -- Register Events, Hook functions, Create Frames, Get information from
|
||||||
|
-- -- the game that wasn't available in OnInitialize
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- function MyAddon:OnDisable()
|
||||||
|
-- -- Unhook, Unregister Events, Hide frames that you created.
|
||||||
|
-- -- You would probably only use an OnDisable if you want to
|
||||||
|
-- -- build a "standby" mode, or be able to toggle modules on/off.
|
||||||
|
-- end
|
||||||
|
-- @class file
|
||||||
|
-- @name AceAddon-3.0.lua
|
||||||
|
-- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceAddon-3.0", 12
|
||||||
|
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceAddon then return end -- No Upgrade needed.
|
||||||
|
|
||||||
|
AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
|
||||||
|
AceAddon.addons = AceAddon.addons or {} -- addons in general
|
||||||
|
AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
|
||||||
|
AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
|
||||||
|
AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
|
||||||
|
AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
|
||||||
|
local fmt, tostring = string.format, tostring
|
||||||
|
local select, pairs, next, type, unpack = select, pairs, next, type, unpack
|
||||||
|
local loadstring, assert, error = loadstring, assert, error
|
||||||
|
local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
|
||||||
|
|
||||||
|
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||||
|
-- List them here for Mikk's FindGlobals script
|
||||||
|
-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
|
||||||
|
|
||||||
|
--[[
|
||||||
|
xpcall safecall implementation
|
||||||
|
]]
|
||||||
|
local xpcall = xpcall
|
||||||
|
|
||||||
|
local function errorhandler(err)
|
||||||
|
return geterrorhandler()(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CreateDispatcher(argCount)
|
||||||
|
local code = [[
|
||||||
|
local xpcall, eh = ...
|
||||||
|
local method, ARGS
|
||||||
|
local function call() return method(ARGS) end
|
||||||
|
|
||||||
|
local function dispatch(func, ...)
|
||||||
|
method = func
|
||||||
|
if not method then return end
|
||||||
|
ARGS = ...
|
||||||
|
return xpcall(call, eh)
|
||||||
|
end
|
||||||
|
|
||||||
|
return dispatch
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ARGS = {}
|
||||||
|
for i = 1, argCount do ARGS[i] = "arg"..i end
|
||||||
|
code = code:gsub("ARGS", tconcat(ARGS, ", "))
|
||||||
|
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||||
|
local dispatcher = CreateDispatcher(argCount)
|
||||||
|
rawset(self, argCount, dispatcher)
|
||||||
|
return dispatcher
|
||||||
|
end})
|
||||||
|
Dispatchers[0] = function(func)
|
||||||
|
return xpcall(func, errorhandler)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function safecall(func, ...)
|
||||||
|
-- we check to see if the func is passed is actually a function here and don't error when it isn't
|
||||||
|
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
|
||||||
|
-- present execution should continue without hinderance
|
||||||
|
if type(func) == "function" then
|
||||||
|
return Dispatchers[select('#', ...)](func, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- local functions that will be implemented further down
|
||||||
|
local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
|
||||||
|
|
||||||
|
-- used in the addon metatable
|
||||||
|
local function addontostring( self ) return self.name end
|
||||||
|
|
||||||
|
-- Check if the addon is queued for initialization
|
||||||
|
local function queuedForInitialization(addon)
|
||||||
|
for i = 1, #AceAddon.initializequeue do
|
||||||
|
if AceAddon.initializequeue[i] == addon then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new AceAddon-3.0 addon.
|
||||||
|
-- Any libraries you specified will be embeded, and the addon will be scheduled for
|
||||||
|
-- its OnInitialize and OnEnable callbacks.
|
||||||
|
-- The final addon object, with all libraries embeded, will be returned.
|
||||||
|
-- @paramsig [object ,]name[, lib, ...]
|
||||||
|
-- @param object Table to use as a base for the addon (optional)
|
||||||
|
-- @param name Name of the addon object to create
|
||||||
|
-- @param lib List of libraries to embed into the addon
|
||||||
|
-- @usage
|
||||||
|
-- -- Create a simple addon object
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
|
||||||
|
--
|
||||||
|
-- -- Create a Addon object based on the table of a frame
|
||||||
|
-- local MyFrame = CreateFrame("Frame")
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
|
||||||
|
function AceAddon:NewAddon(objectorname, ...)
|
||||||
|
local object,name
|
||||||
|
local i=1
|
||||||
|
if type(objectorname)=="table" then
|
||||||
|
object=objectorname
|
||||||
|
name=...
|
||||||
|
i=2
|
||||||
|
else
|
||||||
|
name=objectorname
|
||||||
|
end
|
||||||
|
if type(name)~="string" then
|
||||||
|
error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
|
||||||
|
end
|
||||||
|
if self.addons[name] then
|
||||||
|
error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
object = object or {}
|
||||||
|
object.name = name
|
||||||
|
|
||||||
|
local addonmeta = {}
|
||||||
|
local oldmeta = getmetatable(object)
|
||||||
|
if oldmeta then
|
||||||
|
for k, v in pairs(oldmeta) do addonmeta[k] = v end
|
||||||
|
end
|
||||||
|
addonmeta.__tostring = addontostring
|
||||||
|
|
||||||
|
setmetatable( object, addonmeta )
|
||||||
|
self.addons[name] = object
|
||||||
|
object.modules = {}
|
||||||
|
object.orderedModules = {}
|
||||||
|
object.defaultModuleLibraries = {}
|
||||||
|
Embed( object ) -- embed NewModule, GetModule methods
|
||||||
|
self:EmbedLibraries(object, select(i,...))
|
||||||
|
|
||||||
|
-- add to queue of addons to be initialized upon ADDON_LOADED
|
||||||
|
tinsert(self.initializequeue, object)
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the addon object by its name from the internal AceAddon registry.
|
||||||
|
-- Throws an error if the addon object cannot be found (except if silent is set).
|
||||||
|
-- @param name unique name of the addon object
|
||||||
|
-- @param silent if true, the addon is optional, silently return nil if its not found
|
||||||
|
-- @usage
|
||||||
|
-- -- Get the Addon
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
function AceAddon:GetAddon(name, silent)
|
||||||
|
if not silent and not self.addons[name] then
|
||||||
|
error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
|
||||||
|
end
|
||||||
|
return self.addons[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - Embed a list of libraries into the specified addon.
|
||||||
|
-- This function will try to embed all of the listed libraries into the addon
|
||||||
|
-- and error if a single one fails.
|
||||||
|
--
|
||||||
|
-- **Note:** This function is for internal use by :NewAddon/:NewModule
|
||||||
|
-- @paramsig addon, [lib, ...]
|
||||||
|
-- @param addon addon object to embed the libs in
|
||||||
|
-- @param lib List of libraries to embed into the addon
|
||||||
|
function AceAddon:EmbedLibraries(addon, ...)
|
||||||
|
for i=1,select("#", ... ) do
|
||||||
|
local libname = select(i, ...)
|
||||||
|
self:EmbedLibrary(addon, libname, false, 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - Embed a library into the addon object.
|
||||||
|
-- This function will check if the specified library is registered with LibStub
|
||||||
|
-- and if it has a :Embed function to call. It'll error if any of those conditions
|
||||||
|
-- fails.
|
||||||
|
--
|
||||||
|
-- **Note:** This function is for internal use by :EmbedLibraries
|
||||||
|
-- @paramsig addon, libname[, silent[, offset]]
|
||||||
|
-- @param addon addon object to embed the library in
|
||||||
|
-- @param libname name of the library to embed
|
||||||
|
-- @param silent marks an embed to fail silently if the library doesn't exist (optional)
|
||||||
|
-- @param offset will push the error messages back to said offset, defaults to 2 (optional)
|
||||||
|
function AceAddon:EmbedLibrary(addon, libname, silent, offset)
|
||||||
|
local lib = LibStub:GetLibrary(libname, true)
|
||||||
|
if not lib and not silent then
|
||||||
|
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
|
||||||
|
elseif lib and type(lib.Embed) == "function" then
|
||||||
|
lib:Embed(addon)
|
||||||
|
tinsert(self.embeds[addon], libname)
|
||||||
|
return true
|
||||||
|
elseif lib then
|
||||||
|
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the specified module from an addon object.
|
||||||
|
-- Throws an error if the addon object cannot be found (except if silent is set)
|
||||||
|
-- @name //addon//:GetModule
|
||||||
|
-- @paramsig name[, silent]
|
||||||
|
-- @param name unique name of the module
|
||||||
|
-- @param silent if true, the module is optional, silently return nil if its not found (optional)
|
||||||
|
-- @usage
|
||||||
|
-- -- Get the Addon
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- -- Get the Module
|
||||||
|
-- MyModule = MyAddon:GetModule("MyModule")
|
||||||
|
function GetModule(self, name, silent)
|
||||||
|
if not self.modules[name] and not silent then
|
||||||
|
error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
|
||||||
|
end
|
||||||
|
return self.modules[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function IsModuleTrue(self) return true end
|
||||||
|
|
||||||
|
--- Create a new module for the addon.
|
||||||
|
-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
|
||||||
|
-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
|
||||||
|
-- an addon object.
|
||||||
|
-- @name //addon//:NewModule
|
||||||
|
-- @paramsig name[, prototype|lib[, lib, ...]]
|
||||||
|
-- @param name unique name of the module
|
||||||
|
-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
|
||||||
|
-- @param lib List of libraries to embed into the addon
|
||||||
|
-- @usage
|
||||||
|
-- -- Create a module with some embeded libraries
|
||||||
|
-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
|
||||||
|
--
|
||||||
|
-- -- Create a module with a prototype
|
||||||
|
-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
|
||||||
|
-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
|
||||||
|
function NewModule(self, name, prototype, ...)
|
||||||
|
if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
|
||||||
|
if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
|
||||||
|
|
||||||
|
if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
|
||||||
|
|
||||||
|
-- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
|
||||||
|
-- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
|
||||||
|
local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
|
||||||
|
|
||||||
|
module.IsModule = IsModuleTrue
|
||||||
|
module:SetEnabledState(self.defaultModuleState)
|
||||||
|
module.moduleName = name
|
||||||
|
|
||||||
|
if type(prototype) == "string" then
|
||||||
|
AceAddon:EmbedLibraries(module, prototype, ...)
|
||||||
|
else
|
||||||
|
AceAddon:EmbedLibraries(module, ...)
|
||||||
|
end
|
||||||
|
AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
|
||||||
|
|
||||||
|
if not prototype or type(prototype) == "string" then
|
||||||
|
prototype = self.defaultModulePrototype or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(prototype) == "table" then
|
||||||
|
local mt = getmetatable(module)
|
||||||
|
mt.__index = prototype
|
||||||
|
setmetatable(module, mt) -- More of a Base class type feel.
|
||||||
|
end
|
||||||
|
|
||||||
|
safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
|
||||||
|
self.modules[name] = module
|
||||||
|
tinsert(self.orderedModules, module)
|
||||||
|
|
||||||
|
return module
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the real name of the addon or module, without any prefix.
|
||||||
|
-- @name //addon//:GetName
|
||||||
|
-- @paramsig
|
||||||
|
-- @usage
|
||||||
|
-- print(MyAddon:GetName())
|
||||||
|
-- -- prints "MyAddon"
|
||||||
|
function GetName(self)
|
||||||
|
return self.moduleName or self.name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Enables the Addon, if possible, return true or false depending on success.
|
||||||
|
-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
|
||||||
|
-- and enabling all modules of the addon (unless explicitly disabled).\\
|
||||||
|
-- :Enable() also sets the internal `enableState` variable to true
|
||||||
|
-- @name //addon//:Enable
|
||||||
|
-- @paramsig
|
||||||
|
-- @usage
|
||||||
|
-- -- Enable MyModule
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyModule = MyAddon:GetModule("MyModule")
|
||||||
|
-- MyModule:Enable()
|
||||||
|
function Enable(self)
|
||||||
|
self:SetEnabledState(true)
|
||||||
|
|
||||||
|
-- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still
|
||||||
|
-- it'll be enabled after the init process
|
||||||
|
if not queuedForInitialization(self) then
|
||||||
|
return AceAddon:EnableAddon(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Disables the Addon, if possible, return true or false depending on success.
|
||||||
|
-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
|
||||||
|
-- and disabling all modules of the addon.\\
|
||||||
|
-- :Disable() also sets the internal `enableState` variable to false
|
||||||
|
-- @name //addon//:Disable
|
||||||
|
-- @paramsig
|
||||||
|
-- @usage
|
||||||
|
-- -- Disable MyAddon
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyAddon:Disable()
|
||||||
|
function Disable(self)
|
||||||
|
self:SetEnabledState(false)
|
||||||
|
return AceAddon:DisableAddon(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Enables the Module, if possible, return true or false depending on success.
|
||||||
|
-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
|
||||||
|
-- @name //addon//:EnableModule
|
||||||
|
-- @paramsig name
|
||||||
|
-- @usage
|
||||||
|
-- -- Enable MyModule using :GetModule
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyModule = MyAddon:GetModule("MyModule")
|
||||||
|
-- MyModule:Enable()
|
||||||
|
--
|
||||||
|
-- -- Enable MyModule using the short-hand
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyAddon:EnableModule("MyModule")
|
||||||
|
function EnableModule(self, name)
|
||||||
|
local module = self:GetModule( name )
|
||||||
|
return module:Enable()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Disables the Module, if possible, return true or false depending on success.
|
||||||
|
-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
|
||||||
|
-- @name //addon//:DisableModule
|
||||||
|
-- @paramsig name
|
||||||
|
-- @usage
|
||||||
|
-- -- Disable MyModule using :GetModule
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyModule = MyAddon:GetModule("MyModule")
|
||||||
|
-- MyModule:Disable()
|
||||||
|
--
|
||||||
|
-- -- Disable MyModule using the short-hand
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
|
||||||
|
-- MyAddon:DisableModule("MyModule")
|
||||||
|
function DisableModule(self, name)
|
||||||
|
local module = self:GetModule( name )
|
||||||
|
return module:Disable()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the default libraries to be mixed into all modules created by this object.
|
||||||
|
-- Note that you can only change the default module libraries before any module is created.
|
||||||
|
-- @name //addon//:SetDefaultModuleLibraries
|
||||||
|
-- @paramsig lib[, lib, ...]
|
||||||
|
-- @param lib List of libraries to embed into the addon
|
||||||
|
-- @usage
|
||||||
|
-- -- Create the addon object
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
|
||||||
|
-- -- Configure default libraries for modules (all modules need AceEvent-3.0)
|
||||||
|
-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
|
||||||
|
-- -- Create a module
|
||||||
|
-- MyModule = MyAddon:NewModule("MyModule")
|
||||||
|
function SetDefaultModuleLibraries(self, ...)
|
||||||
|
if next(self.modules) then
|
||||||
|
error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
|
||||||
|
end
|
||||||
|
self.defaultModuleLibraries = {...}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the default state in which new modules are being created.
|
||||||
|
-- Note that you can only change the default state before any module is created.
|
||||||
|
-- @name //addon//:SetDefaultModuleState
|
||||||
|
-- @paramsig state
|
||||||
|
-- @param state Default state for new modules, true for enabled, false for disabled
|
||||||
|
-- @usage
|
||||||
|
-- -- Create the addon object
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
|
||||||
|
-- -- Set the default state to "disabled"
|
||||||
|
-- MyAddon:SetDefaultModuleState(false)
|
||||||
|
-- -- Create a module and explicilty enable it
|
||||||
|
-- MyModule = MyAddon:NewModule("MyModule")
|
||||||
|
-- MyModule:Enable()
|
||||||
|
function SetDefaultModuleState(self, state)
|
||||||
|
if next(self.modules) then
|
||||||
|
error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
|
||||||
|
end
|
||||||
|
self.defaultModuleState = state
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the default prototype to use for new modules on creation.
|
||||||
|
-- Note that you can only change the default prototype before any module is created.
|
||||||
|
-- @name //addon//:SetDefaultModulePrototype
|
||||||
|
-- @paramsig prototype
|
||||||
|
-- @param prototype Default prototype for the new modules (table)
|
||||||
|
-- @usage
|
||||||
|
-- -- Define a prototype
|
||||||
|
-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
|
||||||
|
-- -- Set the default prototype
|
||||||
|
-- MyAddon:SetDefaultModulePrototype(prototype)
|
||||||
|
-- -- Create a module and explicitly Enable it
|
||||||
|
-- MyModule = MyAddon:NewModule("MyModule")
|
||||||
|
-- MyModule:Enable()
|
||||||
|
-- -- should print "OnEnable called!" now
|
||||||
|
-- @see NewModule
|
||||||
|
function SetDefaultModulePrototype(self, prototype)
|
||||||
|
if next(self.modules) then
|
||||||
|
error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
|
||||||
|
end
|
||||||
|
if type(prototype) ~= "table" then
|
||||||
|
error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
|
||||||
|
end
|
||||||
|
self.defaultModulePrototype = prototype
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the state of an addon or module
|
||||||
|
-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
|
||||||
|
-- @name //addon//:SetEnabledState
|
||||||
|
-- @paramsig state
|
||||||
|
-- @param state the state of an addon or module (enabled=true, disabled=false)
|
||||||
|
function SetEnabledState(self, state)
|
||||||
|
self.enabledState = state
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Return an iterator of all modules associated to the addon.
|
||||||
|
-- @name //addon//:IterateModules
|
||||||
|
-- @paramsig
|
||||||
|
-- @usage
|
||||||
|
-- -- Enable all modules
|
||||||
|
-- for name, module in MyAddon:IterateModules() do
|
||||||
|
-- module:Enable()
|
||||||
|
-- end
|
||||||
|
local function IterateModules(self) return pairs(self.modules) end
|
||||||
|
|
||||||
|
-- Returns an iterator of all embeds in the addon
|
||||||
|
-- @name //addon//:IterateEmbeds
|
||||||
|
-- @paramsig
|
||||||
|
local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
|
||||||
|
|
||||||
|
--- Query the enabledState of an addon.
|
||||||
|
-- @name //addon//:IsEnabled
|
||||||
|
-- @paramsig
|
||||||
|
-- @usage
|
||||||
|
-- if MyAddon:IsEnabled() then
|
||||||
|
-- MyAddon:Disable()
|
||||||
|
-- end
|
||||||
|
local function IsEnabled(self) return self.enabledState end
|
||||||
|
local mixins = {
|
||||||
|
NewModule = NewModule,
|
||||||
|
GetModule = GetModule,
|
||||||
|
Enable = Enable,
|
||||||
|
Disable = Disable,
|
||||||
|
EnableModule = EnableModule,
|
||||||
|
DisableModule = DisableModule,
|
||||||
|
IsEnabled = IsEnabled,
|
||||||
|
SetDefaultModuleLibraries = SetDefaultModuleLibraries,
|
||||||
|
SetDefaultModuleState = SetDefaultModuleState,
|
||||||
|
SetDefaultModulePrototype = SetDefaultModulePrototype,
|
||||||
|
SetEnabledState = SetEnabledState,
|
||||||
|
IterateModules = IterateModules,
|
||||||
|
IterateEmbeds = IterateEmbeds,
|
||||||
|
GetName = GetName,
|
||||||
|
}
|
||||||
|
local function IsModule(self) return false end
|
||||||
|
local pmixins = {
|
||||||
|
defaultModuleState = true,
|
||||||
|
enabledState = true,
|
||||||
|
IsModule = IsModule,
|
||||||
|
}
|
||||||
|
-- Embed( target )
|
||||||
|
-- target (object) - target object to embed aceaddon in
|
||||||
|
--
|
||||||
|
-- this is a local function specifically since it's meant to be only called internally
|
||||||
|
function Embed(target, skipPMixins)
|
||||||
|
for k, v in pairs(mixins) do
|
||||||
|
target[k] = v
|
||||||
|
end
|
||||||
|
if not skipPMixins then
|
||||||
|
for k, v in pairs(pmixins) do
|
||||||
|
target[k] = target[k] or v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- - Initialize the addon after creation.
|
||||||
|
-- This function is only used internally during the ADDON_LOADED event
|
||||||
|
-- It will call the **OnInitialize** function on the addon object (if present),
|
||||||
|
-- and the **OnEmbedInitialize** function on all embeded libraries.
|
||||||
|
--
|
||||||
|
-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
|
||||||
|
-- @param addon addon object to intialize
|
||||||
|
function AceAddon:InitializeAddon(addon)
|
||||||
|
safecall(addon.OnInitialize, addon)
|
||||||
|
|
||||||
|
local embeds = self.embeds[addon]
|
||||||
|
for i = 1, #embeds do
|
||||||
|
local lib = LibStub:GetLibrary(embeds[i], true)
|
||||||
|
if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we don't call InitializeAddon on modules specifically, this is handled
|
||||||
|
-- from the event handler and only done _once_
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - Enable the addon after creation.
|
||||||
|
-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
|
||||||
|
-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
|
||||||
|
-- It will call the **OnEnable** function on the addon object (if present),
|
||||||
|
-- and the **OnEmbedEnable** function on all embeded libraries.\\
|
||||||
|
-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
|
||||||
|
--
|
||||||
|
-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
|
||||||
|
-- Use :Enable on the addon itself instead.
|
||||||
|
-- @param addon addon object to enable
|
||||||
|
function AceAddon:EnableAddon(addon)
|
||||||
|
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
|
||||||
|
if self.statuses[addon.name] or not addon.enabledState then return false end
|
||||||
|
|
||||||
|
-- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable.
|
||||||
|
self.statuses[addon.name] = true
|
||||||
|
|
||||||
|
safecall(addon.OnEnable, addon)
|
||||||
|
|
||||||
|
-- make sure we're still enabled before continueing
|
||||||
|
if self.statuses[addon.name] then
|
||||||
|
local embeds = self.embeds[addon]
|
||||||
|
for i = 1, #embeds do
|
||||||
|
local lib = LibStub:GetLibrary(embeds[i], true)
|
||||||
|
if lib then safecall(lib.OnEmbedEnable, lib, addon) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- enable possible modules.
|
||||||
|
local modules = addon.orderedModules
|
||||||
|
for i = 1, #modules do
|
||||||
|
self:EnableAddon(modules[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self.statuses[addon.name] -- return true if we're disabled
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - Disable the addon
|
||||||
|
-- Note: This function is only used internally.
|
||||||
|
-- It will call the **OnDisable** function on the addon object (if present),
|
||||||
|
-- and the **OnEmbedDisable** function on all embeded libraries.\\
|
||||||
|
-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
|
||||||
|
--
|
||||||
|
-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
|
||||||
|
-- Use :Disable on the addon itself instead.
|
||||||
|
-- @param addon addon object to enable
|
||||||
|
function AceAddon:DisableAddon(addon)
|
||||||
|
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
|
||||||
|
if not self.statuses[addon.name] then return false end
|
||||||
|
|
||||||
|
-- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable.
|
||||||
|
self.statuses[addon.name] = false
|
||||||
|
|
||||||
|
safecall( addon.OnDisable, addon )
|
||||||
|
|
||||||
|
-- make sure we're still disabling...
|
||||||
|
if not self.statuses[addon.name] then
|
||||||
|
local embeds = self.embeds[addon]
|
||||||
|
for i = 1, #embeds do
|
||||||
|
local lib = LibStub:GetLibrary(embeds[i], true)
|
||||||
|
if lib then safecall(lib.OnEmbedDisable, lib, addon) end
|
||||||
|
end
|
||||||
|
-- disable possible modules.
|
||||||
|
local modules = addon.orderedModules
|
||||||
|
for i = 1, #modules do
|
||||||
|
self:DisableAddon(modules[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return not self.statuses[addon.name] -- return true if we're disabled
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get an iterator over all registered addons.
|
||||||
|
-- @usage
|
||||||
|
-- -- Print a list of all installed AceAddon's
|
||||||
|
-- for name, addon in AceAddon:IterateAddons() do
|
||||||
|
-- print("Addon: " .. name)
|
||||||
|
-- end
|
||||||
|
function AceAddon:IterateAddons() return pairs(self.addons) end
|
||||||
|
|
||||||
|
--- Get an iterator over the internal status registry.
|
||||||
|
-- @usage
|
||||||
|
-- -- Print a list of all enabled addons
|
||||||
|
-- for name, status in AceAddon:IterateAddonStatus() do
|
||||||
|
-- if status then
|
||||||
|
-- print("EnabledAddon: " .. name)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
|
||||||
|
|
||||||
|
-- Following Iterators are deprecated, and their addon specific versions should be used
|
||||||
|
-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
|
||||||
|
function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
|
||||||
|
function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
|
||||||
|
|
||||||
|
-- Event Handling
|
||||||
|
local function onEvent(this, event, arg1)
|
||||||
|
-- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
|
||||||
|
if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
|
||||||
|
-- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
|
||||||
|
while(#AceAddon.initializequeue > 0) do
|
||||||
|
local addon = tremove(AceAddon.initializequeue, 1)
|
||||||
|
-- this might be an issue with recursion - TODO: validate
|
||||||
|
if event == "ADDON_LOADED" then addon.baseName = arg1 end
|
||||||
|
AceAddon:InitializeAddon(addon)
|
||||||
|
tinsert(AceAddon.enablequeue, addon)
|
||||||
|
end
|
||||||
|
|
||||||
|
if IsLoggedIn() then
|
||||||
|
while(#AceAddon.enablequeue > 0) do
|
||||||
|
local addon = tremove(AceAddon.enablequeue, 1)
|
||||||
|
AceAddon:EnableAddon(addon)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
AceAddon.frame:RegisterEvent("ADDON_LOADED")
|
||||||
|
AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
|
||||||
|
AceAddon.frame:SetScript("OnEvent", onEvent)
|
||||||
|
|
||||||
|
-- upgrade embeded
|
||||||
|
for name, addon in pairs(AceAddon.addons) do
|
||||||
|
Embed(addon, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 2010-10-27 nevcairiel - add new "orderedModules" table
|
||||||
|
if oldminor and oldminor < 10 then
|
||||||
|
for name, addon in pairs(AceAddon.addons) do
|
||||||
|
addon.orderedModules = {}
|
||||||
|
for module_name, module in pairs(addon.modules) do
|
||||||
|
tinsert(addon.orderedModules, module)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceAddon-3.0.lua" />
|
||||||
|
</Ui>
|
||||||
@ -0,0 +1,301 @@
|
|||||||
|
--- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels.
|
||||||
|
-- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\
|
||||||
|
-- **ChatThrottleLib** is of course being used to avoid being disconnected by the server.
|
||||||
|
--
|
||||||
|
-- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by
|
||||||
|
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||||
|
-- and can be accessed directly, without having to explicitly call AceComm itself.\\
|
||||||
|
-- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you
|
||||||
|
-- make into AceComm.
|
||||||
|
-- @class file
|
||||||
|
-- @name AceComm-3.0
|
||||||
|
-- @release $Id: AceComm-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
|
||||||
|
|
||||||
|
--[[ AceComm-3.0
|
||||||
|
|
||||||
|
TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited.
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
local CallbackHandler = LibStub("CallbackHandler-1.0")
|
||||||
|
local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceComm-3.0", 10
|
||||||
|
local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceComm then return end
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local type, next, pairs, tostring = type, next, pairs, tostring
|
||||||
|
local strsub, strfind = string.sub, string.find
|
||||||
|
local match = string.match
|
||||||
|
local tinsert, tconcat = table.insert, table.concat
|
||||||
|
local error, assert = error, assert
|
||||||
|
|
||||||
|
-- WoW APIs
|
||||||
|
local Ambiguate = Ambiguate
|
||||||
|
|
||||||
|
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||||
|
-- List them here for Mikk's FindGlobals script
|
||||||
|
-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix
|
||||||
|
|
||||||
|
AceComm.embeds = AceComm.embeds or {}
|
||||||
|
|
||||||
|
-- for my sanity and yours, let's give the message type bytes some names
|
||||||
|
local MSG_MULTI_FIRST = "\001"
|
||||||
|
local MSG_MULTI_NEXT = "\002"
|
||||||
|
local MSG_MULTI_LAST = "\003"
|
||||||
|
local MSG_ESCAPE = "\004"
|
||||||
|
|
||||||
|
-- remove old structures (pre WoW 4.0)
|
||||||
|
AceComm.multipart_origprefixes = nil
|
||||||
|
AceComm.multipart_reassemblers = nil
|
||||||
|
|
||||||
|
-- the multipart message spool: indexed by a combination of sender+distribution+
|
||||||
|
AceComm.multipart_spool = AceComm.multipart_spool or {}
|
||||||
|
|
||||||
|
--- Register for Addon Traffic on a specified prefix
|
||||||
|
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
|
||||||
|
-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
|
||||||
|
function AceComm:RegisterComm(prefix, method)
|
||||||
|
if method == nil then
|
||||||
|
method = "OnCommReceived"
|
||||||
|
end
|
||||||
|
|
||||||
|
if #prefix > 16 then -- TODO: 15?
|
||||||
|
error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
|
||||||
|
end
|
||||||
|
RegisterAddonMessagePrefix(prefix)
|
||||||
|
|
||||||
|
return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler
|
||||||
|
end
|
||||||
|
|
||||||
|
local warnedPrefix=false
|
||||||
|
|
||||||
|
--- Send a message over the Addon Channel
|
||||||
|
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
|
||||||
|
-- @param text Data to send, nils (\000) not allowed. Any length.
|
||||||
|
-- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API
|
||||||
|
-- @param target Destination for some distributions; see SendAddonMessage API
|
||||||
|
-- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL".
|
||||||
|
-- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send.
|
||||||
|
-- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified.
|
||||||
|
function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg)
|
||||||
|
prio = prio or "NORMAL" -- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery!
|
||||||
|
if not( type(prefix)=="string" and
|
||||||
|
type(text)=="string" and
|
||||||
|
type(distribution)=="string" and
|
||||||
|
(target==nil or type(target)=="string") and
|
||||||
|
(prio=="BULK" or prio=="NORMAL" or prio=="ALERT")
|
||||||
|
) then
|
||||||
|
error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local textlen = #text
|
||||||
|
local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
|
||||||
|
local queueName = prefix..distribution..(target or "")
|
||||||
|
|
||||||
|
local ctlCallback = nil
|
||||||
|
if callbackFn then
|
||||||
|
ctlCallback = function(sent)
|
||||||
|
return callbackFn(callbackArg, sent, textlen)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local forceMultipart
|
||||||
|
if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
|
||||||
|
-- we need to escape the first character with a \004
|
||||||
|
if textlen+1 > maxtextlen then -- would we go over the size limit?
|
||||||
|
forceMultipart = true -- just make it multipart, no escape problems then
|
||||||
|
else
|
||||||
|
text = "\004" .. text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not forceMultipart and textlen <= maxtextlen then
|
||||||
|
-- fits all in one message
|
||||||
|
CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
|
||||||
|
else
|
||||||
|
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
|
||||||
|
|
||||||
|
-- first part
|
||||||
|
local chunk = strsub(text, 1, maxtextlen)
|
||||||
|
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
|
||||||
|
|
||||||
|
-- continuation
|
||||||
|
local pos = 1+maxtextlen
|
||||||
|
|
||||||
|
while pos+maxtextlen <= textlen do
|
||||||
|
chunk = strsub(text, pos, pos+maxtextlen-1)
|
||||||
|
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
|
||||||
|
pos = pos + maxtextlen
|
||||||
|
end
|
||||||
|
|
||||||
|
-- final part
|
||||||
|
chunk = strsub(text, pos)
|
||||||
|
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- Message receiving
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
do
|
||||||
|
local compost = setmetatable({}, {__mode = "k"})
|
||||||
|
local function new()
|
||||||
|
local t = next(compost)
|
||||||
|
if t then
|
||||||
|
compost[t]=nil
|
||||||
|
for i=#t,3,-1 do -- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten
|
||||||
|
t[i]=nil
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lostdatawarning(prefix,sender,where)
|
||||||
|
DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")")
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender)
|
||||||
|
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
|
||||||
|
local spool = AceComm.multipart_spool
|
||||||
|
|
||||||
|
--[[
|
||||||
|
if spool[key] then
|
||||||
|
lostdatawarning(prefix,sender,"First")
|
||||||
|
-- continue and overwrite
|
||||||
|
end
|
||||||
|
--]]
|
||||||
|
|
||||||
|
spool[key] = message -- plain string for now
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender)
|
||||||
|
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
|
||||||
|
local spool = AceComm.multipart_spool
|
||||||
|
local olddata = spool[key]
|
||||||
|
|
||||||
|
if not olddata then
|
||||||
|
--lostdatawarning(prefix,sender,"Next")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(olddata)~="table" then
|
||||||
|
-- ... but what we have is not a table. So make it one. (Pull a composted one if available)
|
||||||
|
local t = new()
|
||||||
|
t[1] = olddata -- add old data as first string
|
||||||
|
t[2] = message -- and new message as second string
|
||||||
|
spool[key] = t -- and put the table in the spool instead of the old string
|
||||||
|
else
|
||||||
|
tinsert(olddata, message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender)
|
||||||
|
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
|
||||||
|
local spool = AceComm.multipart_spool
|
||||||
|
local olddata = spool[key]
|
||||||
|
|
||||||
|
if not olddata then
|
||||||
|
--lostdatawarning(prefix,sender,"End")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
spool[key] = nil
|
||||||
|
|
||||||
|
if type(olddata) == "table" then
|
||||||
|
-- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat
|
||||||
|
tinsert(olddata, message)
|
||||||
|
AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender)
|
||||||
|
compost[olddata] = true
|
||||||
|
else
|
||||||
|
-- if we've only received a "first", the spooled data will still only be a string
|
||||||
|
AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- Embed CallbackHandler
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
if not AceComm.callbacks then
|
||||||
|
AceComm.callbacks = CallbackHandler:New(AceComm,
|
||||||
|
"_RegisterComm",
|
||||||
|
"UnregisterComm",
|
||||||
|
"UnregisterAllComm")
|
||||||
|
end
|
||||||
|
|
||||||
|
AceComm.callbacks.OnUsed = nil
|
||||||
|
AceComm.callbacks.OnUnused = nil
|
||||||
|
|
||||||
|
local function OnEvent(self, event, prefix, message, distribution, sender)
|
||||||
|
if event == "CHAT_MSG_ADDON" then
|
||||||
|
sender = Ambiguate(sender, "none")
|
||||||
|
local control, rest = match(message, "^([\001-\009])(.*)")
|
||||||
|
if control then
|
||||||
|
if control==MSG_MULTI_FIRST then
|
||||||
|
AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
|
||||||
|
elseif control==MSG_MULTI_NEXT then
|
||||||
|
AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
|
||||||
|
elseif control==MSG_MULTI_LAST then
|
||||||
|
AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
|
||||||
|
elseif control==MSG_ESCAPE then
|
||||||
|
AceComm.callbacks:Fire(prefix, rest, distribution, sender)
|
||||||
|
else
|
||||||
|
-- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
|
||||||
|
AceComm.callbacks:Fire(prefix, message, distribution, sender)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
assert(false, "Received "..tostring(event).." event?!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame")
|
||||||
|
AceComm.frame:SetScript("OnEvent", OnEvent)
|
||||||
|
AceComm.frame:UnregisterAllEvents()
|
||||||
|
AceComm.frame:RegisterEvent("CHAT_MSG_ADDON")
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- Base library stuff
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
local mixins = {
|
||||||
|
"RegisterComm",
|
||||||
|
"UnregisterComm",
|
||||||
|
"UnregisterAllComm",
|
||||||
|
"SendCommMessage",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:..
|
||||||
|
-- @param target target object to embed AceComm-3.0 in
|
||||||
|
function AceComm:Embed(target)
|
||||||
|
for k, v in pairs(mixins) do
|
||||||
|
target[v] = self[v]
|
||||||
|
end
|
||||||
|
self.embeds[target] = true
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceComm:OnEmbedDisable(target)
|
||||||
|
target:UnregisterAllComm()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update embeds
|
||||||
|
for target, v in pairs(AceComm.embeds) do
|
||||||
|
AceComm:Embed(target)
|
||||||
|
end
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="ChatThrottleLib.lua" />
|
||||||
|
<Script file="AceComm-3.0.lua" />
|
||||||
|
</Ui>
|
||||||
@ -0,0 +1,524 @@
|
|||||||
|
--
|
||||||
|
-- ChatThrottleLib by Mikk
|
||||||
|
--
|
||||||
|
-- Manages AddOn chat output to keep player from getting kicked off.
|
||||||
|
--
|
||||||
|
-- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
|
||||||
|
-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
|
||||||
|
--
|
||||||
|
-- Priorities get an equal share of available bandwidth when fully loaded.
|
||||||
|
-- Communication channels are separated on extension+chattype+destination and
|
||||||
|
-- get round-robinned. (Destination only matters for whispers and channels,
|
||||||
|
-- obviously)
|
||||||
|
--
|
||||||
|
-- Will install hooks for SendChatMessage and SendAddonMessage to measure
|
||||||
|
-- bandwidth bypassing the library and use less bandwidth itself.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Fully embeddable library. Just copy this file into your addon directory,
|
||||||
|
-- add it to the .toc, and it's done.
|
||||||
|
--
|
||||||
|
-- Can run as a standalone addon also, but, really, just embed it! :-)
|
||||||
|
--
|
||||||
|
-- LICENSE: ChatThrottleLib is released into the Public Domain
|
||||||
|
--
|
||||||
|
|
||||||
|
local CTL_VERSION = 23
|
||||||
|
|
||||||
|
local _G = _G
|
||||||
|
|
||||||
|
if _G.ChatThrottleLib then
|
||||||
|
if _G.ChatThrottleLib.version >= CTL_VERSION then
|
||||||
|
-- There's already a newer (or same) version loaded. Buh-bye.
|
||||||
|
return
|
||||||
|
elseif not _G.ChatThrottleLib.securelyHooked then
|
||||||
|
print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, <v16) in an addon somewhere. Get the addon updated or copy in a newer ChatThrottleLib.lua (>=v16) in it!")
|
||||||
|
-- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
|
||||||
|
-- ... and if someone has securehooked, they can kiss that goodbye too... >.<
|
||||||
|
_G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
|
||||||
|
if _G.ChatThrottleLib.ORIG_SendAddonMessage then
|
||||||
|
_G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_G.ChatThrottleLib.ORIG_SendChatMessage = nil
|
||||||
|
_G.ChatThrottleLib.ORIG_SendAddonMessage = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _G.ChatThrottleLib then
|
||||||
|
_G.ChatThrottleLib = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
|
||||||
|
local ChatThrottleLib = _G.ChatThrottleLib
|
||||||
|
|
||||||
|
ChatThrottleLib.version = CTL_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------ TWEAKABLES -----------------
|
||||||
|
|
||||||
|
ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
|
||||||
|
ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
|
||||||
|
|
||||||
|
ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
|
||||||
|
|
||||||
|
ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
|
||||||
|
|
||||||
|
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local table_remove = table.remove
|
||||||
|
local tostring = tostring
|
||||||
|
local GetTime = GetTime
|
||||||
|
local math_min = math.min
|
||||||
|
local math_max = math.max
|
||||||
|
local next = next
|
||||||
|
local strlen = string.len
|
||||||
|
local GetFramerate = GetFramerate
|
||||||
|
local strlower = string.lower
|
||||||
|
local unpack,type,pairs,wipe = unpack,type,pairs,wipe
|
||||||
|
local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Double-linked ring implementation
|
||||||
|
|
||||||
|
local Ring = {}
|
||||||
|
local RingMeta = { __index = Ring }
|
||||||
|
|
||||||
|
function Ring:New()
|
||||||
|
local ret = {}
|
||||||
|
setmetatable(ret, RingMeta)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
|
||||||
|
if self.pos then
|
||||||
|
obj.prev = self.pos.prev
|
||||||
|
obj.prev.next = obj
|
||||||
|
obj.next = self.pos
|
||||||
|
obj.next.prev = obj
|
||||||
|
else
|
||||||
|
obj.next = obj
|
||||||
|
obj.prev = obj
|
||||||
|
self.pos = obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ring:Remove(obj)
|
||||||
|
obj.next.prev = obj.prev
|
||||||
|
obj.prev.next = obj.next
|
||||||
|
if self.pos == obj then
|
||||||
|
self.pos = obj.next
|
||||||
|
if self.pos == obj then
|
||||||
|
self.pos = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Recycling bin for pipes
|
||||||
|
-- A pipe is a plain integer-indexed queue of messages
|
||||||
|
-- Pipes normally live in Rings of pipes (3 rings total, one per priority)
|
||||||
|
|
||||||
|
ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
|
||||||
|
local PipeBin = setmetatable({}, {__mode="k"})
|
||||||
|
|
||||||
|
local function DelPipe(pipe)
|
||||||
|
PipeBin[pipe] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function NewPipe()
|
||||||
|
local pipe = next(PipeBin)
|
||||||
|
if pipe then
|
||||||
|
wipe(pipe)
|
||||||
|
PipeBin[pipe] = nil
|
||||||
|
return pipe
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Recycling bin for messages
|
||||||
|
|
||||||
|
ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
|
||||||
|
local MsgBin = setmetatable({}, {__mode="k"})
|
||||||
|
|
||||||
|
local function DelMsg(msg)
|
||||||
|
msg[1] = nil
|
||||||
|
-- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
|
||||||
|
MsgBin[msg] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function NewMsg()
|
||||||
|
local msg = next(MsgBin)
|
||||||
|
if msg then
|
||||||
|
MsgBin[msg] = nil
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- ChatThrottleLib:Init
|
||||||
|
-- Initialize queues, set up frame for OnUpdate, etc
|
||||||
|
|
||||||
|
|
||||||
|
function ChatThrottleLib:Init()
|
||||||
|
|
||||||
|
-- Set up queues
|
||||||
|
if not self.Prio then
|
||||||
|
self.Prio = {}
|
||||||
|
self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
|
||||||
|
self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
|
||||||
|
self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- v4: total send counters per priority
|
||||||
|
for _, Prio in pairs(self.Prio) do
|
||||||
|
Prio.nTotalSent = Prio.nTotalSent or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.avail then
|
||||||
|
self.avail = 0 -- v5
|
||||||
|
end
|
||||||
|
if not self.nTotalSent then
|
||||||
|
self.nTotalSent = 0 -- v5
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Set up a frame to get OnUpdate events
|
||||||
|
if not self.Frame then
|
||||||
|
self.Frame = CreateFrame("Frame")
|
||||||
|
self.Frame:Hide()
|
||||||
|
end
|
||||||
|
self.Frame:SetScript("OnUpdate", self.OnUpdate)
|
||||||
|
self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
|
||||||
|
self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||||
|
self.OnUpdateDelay = 0
|
||||||
|
self.LastAvailUpdate = GetTime()
|
||||||
|
self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
|
||||||
|
|
||||||
|
-- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
|
||||||
|
if not self.securelyHooked then
|
||||||
|
-- Use secure hooks as of v16. Old regular hook support yanked out in v21.
|
||||||
|
self.securelyHooked = true
|
||||||
|
--SendChatMessage
|
||||||
|
hooksecurefunc("SendChatMessage", function(...)
|
||||||
|
return ChatThrottleLib.Hook_SendChatMessage(...)
|
||||||
|
end)
|
||||||
|
--SendAddonMessage
|
||||||
|
hooksecurefunc("SendAddonMessage", function(...)
|
||||||
|
return ChatThrottleLib.Hook_SendAddonMessage(...)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
self.nBypass = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
|
||||||
|
|
||||||
|
local bMyTraffic = false
|
||||||
|
|
||||||
|
function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
|
||||||
|
if bMyTraffic then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local self = ChatThrottleLib
|
||||||
|
local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
|
||||||
|
self.avail = self.avail - size
|
||||||
|
self.nBypass = self.nBypass + size -- just a statistic
|
||||||
|
end
|
||||||
|
function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
|
||||||
|
if bMyTraffic then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local self = ChatThrottleLib
|
||||||
|
local size = tostring(text or ""):len() + tostring(prefix or ""):len();
|
||||||
|
size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
|
||||||
|
self.avail = self.avail - size
|
||||||
|
self.nBypass = self.nBypass + size -- just a statistic
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- ChatThrottleLib:UpdateAvail
|
||||||
|
-- Update self.avail with how much bandwidth is currently available
|
||||||
|
|
||||||
|
function ChatThrottleLib:UpdateAvail()
|
||||||
|
local now = GetTime()
|
||||||
|
local MAX_CPS = self.MAX_CPS;
|
||||||
|
local newavail = MAX_CPS * (now - self.LastAvailUpdate)
|
||||||
|
local avail = self.avail
|
||||||
|
|
||||||
|
if now - self.HardThrottlingBeginTime < 5 then
|
||||||
|
-- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
|
||||||
|
avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
|
||||||
|
self.bChoking = true
|
||||||
|
elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
|
||||||
|
avail = math_min(MAX_CPS, avail + newavail*0.5)
|
||||||
|
self.bChoking = true -- just a statistic
|
||||||
|
else
|
||||||
|
avail = math_min(self.BURST, avail + newavail)
|
||||||
|
self.bChoking = false
|
||||||
|
end
|
||||||
|
|
||||||
|
avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
|
||||||
|
|
||||||
|
self.avail = avail
|
||||||
|
self.LastAvailUpdate = now
|
||||||
|
|
||||||
|
return avail
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Despooling logic
|
||||||
|
-- Reminder:
|
||||||
|
-- - We have 3 Priorities, each containing a "Ring" construct ...
|
||||||
|
-- - ... made up of N "Pipe"s (1 for each destination/pipename)
|
||||||
|
-- - and each pipe contains messages
|
||||||
|
|
||||||
|
function ChatThrottleLib:Despool(Prio)
|
||||||
|
local ring = Prio.Ring
|
||||||
|
while ring.pos and Prio.avail > ring.pos[1].nSize do
|
||||||
|
local msg = table_remove(ring.pos, 1)
|
||||||
|
if not ring.pos[1] then -- did we remove last msg in this pipe?
|
||||||
|
local pipe = Prio.Ring.pos
|
||||||
|
Prio.Ring:Remove(pipe)
|
||||||
|
Prio.ByName[pipe.name] = nil
|
||||||
|
DelPipe(pipe)
|
||||||
|
else
|
||||||
|
Prio.Ring.pos = Prio.Ring.pos.next
|
||||||
|
end
|
||||||
|
local didSend=false
|
||||||
|
local lowerDest = strlower(msg[3] or "")
|
||||||
|
if lowerDest == "raid" and not UnitInRaid("player") then
|
||||||
|
-- do nothing
|
||||||
|
elseif lowerDest == "party" and not UnitInParty("player") then
|
||||||
|
-- do nothing
|
||||||
|
else
|
||||||
|
Prio.avail = Prio.avail - msg.nSize
|
||||||
|
bMyTraffic = true
|
||||||
|
msg.f(unpack(msg, 1, msg.n))
|
||||||
|
bMyTraffic = false
|
||||||
|
Prio.nTotalSent = Prio.nTotalSent + msg.nSize
|
||||||
|
DelMsg(msg)
|
||||||
|
didSend = true
|
||||||
|
end
|
||||||
|
-- notify caller of delivery (even if we didn't send it)
|
||||||
|
if msg.callbackFn then
|
||||||
|
msg.callbackFn (msg.callbackArg, didSend)
|
||||||
|
end
|
||||||
|
-- USER CALLBACK MAY ERROR
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ChatThrottleLib.OnEvent(this,event)
|
||||||
|
-- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
|
||||||
|
local self = ChatThrottleLib
|
||||||
|
if event == "PLAYER_ENTERING_WORLD" then
|
||||||
|
self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
|
||||||
|
self.avail = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ChatThrottleLib.OnUpdate(this,delay)
|
||||||
|
local self = ChatThrottleLib
|
||||||
|
|
||||||
|
self.OnUpdateDelay = self.OnUpdateDelay + delay
|
||||||
|
if self.OnUpdateDelay < 0.08 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.OnUpdateDelay = 0
|
||||||
|
|
||||||
|
self:UpdateAvail()
|
||||||
|
|
||||||
|
if self.avail < 0 then
|
||||||
|
return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
|
||||||
|
local n = 0
|
||||||
|
for prioname,Prio in pairs(self.Prio) do
|
||||||
|
if Prio.Ring.pos or Prio.avail < 0 then
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Anything queued still?
|
||||||
|
if n<1 then
|
||||||
|
-- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
|
||||||
|
for prioname, Prio in pairs(self.Prio) do
|
||||||
|
self.avail = self.avail + Prio.avail
|
||||||
|
Prio.avail = 0
|
||||||
|
end
|
||||||
|
self.bQueueing = false
|
||||||
|
self.Frame:Hide()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
|
||||||
|
local avail = self.avail/n
|
||||||
|
self.avail = 0
|
||||||
|
|
||||||
|
for prioname, Prio in pairs(self.Prio) do
|
||||||
|
if Prio.Ring.pos or Prio.avail < 0 then
|
||||||
|
Prio.avail = Prio.avail + avail
|
||||||
|
if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
|
||||||
|
self:Despool(Prio)
|
||||||
|
-- Note: We might not get here if the user-supplied callback function errors out! Take care!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Spooling logic
|
||||||
|
|
||||||
|
function ChatThrottleLib:Enqueue(prioname, pipename, msg)
|
||||||
|
local Prio = self.Prio[prioname]
|
||||||
|
local pipe = Prio.ByName[pipename]
|
||||||
|
if not pipe then
|
||||||
|
self.Frame:Show()
|
||||||
|
pipe = NewPipe()
|
||||||
|
pipe.name = pipename
|
||||||
|
Prio.ByName[pipename] = pipe
|
||||||
|
Prio.Ring:Add(pipe)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipe[#pipe + 1] = msg
|
||||||
|
|
||||||
|
self.bQueueing = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
|
||||||
|
if not self or not prio or not prefix or not text or not self.Prio[prio] then
|
||||||
|
error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
|
||||||
|
end
|
||||||
|
if callbackFn and type(callbackFn)~="function" then
|
||||||
|
error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local nSize = text:len()
|
||||||
|
|
||||||
|
if nSize>255 then
|
||||||
|
error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
nSize = nSize + self.MSG_OVERHEAD
|
||||||
|
|
||||||
|
-- Check if there's room in the global available bandwidth gauge to send directly
|
||||||
|
if not self.bQueueing and nSize < self:UpdateAvail() then
|
||||||
|
self.avail = self.avail - nSize
|
||||||
|
bMyTraffic = true
|
||||||
|
_G.SendChatMessage(text, chattype, language, destination)
|
||||||
|
bMyTraffic = false
|
||||||
|
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
|
||||||
|
if callbackFn then
|
||||||
|
callbackFn (callbackArg, true)
|
||||||
|
end
|
||||||
|
-- USER CALLBACK MAY ERROR
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Message needs to be queued
|
||||||
|
local msg = NewMsg()
|
||||||
|
msg.f = _G.SendChatMessage
|
||||||
|
msg[1] = text
|
||||||
|
msg[2] = chattype or "SAY"
|
||||||
|
msg[3] = language
|
||||||
|
msg[4] = destination
|
||||||
|
msg.n = 4
|
||||||
|
msg.nSize = nSize
|
||||||
|
msg.callbackFn = callbackFn
|
||||||
|
msg.callbackArg = callbackArg
|
||||||
|
|
||||||
|
self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
|
||||||
|
if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
|
||||||
|
error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
|
||||||
|
end
|
||||||
|
if callbackFn and type(callbackFn)~="function" then
|
||||||
|
error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local nSize = text:len();
|
||||||
|
|
||||||
|
if RegisterAddonMessagePrefix then
|
||||||
|
if nSize>255 then
|
||||||
|
error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
nSize = nSize + prefix:len() + 1
|
||||||
|
if nSize>255 then
|
||||||
|
error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nSize = nSize + self.MSG_OVERHEAD;
|
||||||
|
|
||||||
|
-- Check if there's room in the global available bandwidth gauge to send directly
|
||||||
|
if not self.bQueueing and nSize < self:UpdateAvail() then
|
||||||
|
self.avail = self.avail - nSize
|
||||||
|
bMyTraffic = true
|
||||||
|
_G.SendAddonMessage(prefix, text, chattype, target)
|
||||||
|
bMyTraffic = false
|
||||||
|
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
|
||||||
|
if callbackFn then
|
||||||
|
callbackFn (callbackArg, true)
|
||||||
|
end
|
||||||
|
-- USER CALLBACK MAY ERROR
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Message needs to be queued
|
||||||
|
local msg = NewMsg()
|
||||||
|
msg.f = _G.SendAddonMessage
|
||||||
|
msg[1] = prefix
|
||||||
|
msg[2] = text
|
||||||
|
msg[3] = chattype
|
||||||
|
msg[4] = target
|
||||||
|
msg.n = (target~=nil) and 4 or 3;
|
||||||
|
msg.nSize = nSize
|
||||||
|
msg.callbackFn = callbackFn
|
||||||
|
msg.callbackArg = callbackArg
|
||||||
|
|
||||||
|
self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- Get the ball rolling!
|
||||||
|
|
||||||
|
ChatThrottleLib:Init()
|
||||||
|
|
||||||
|
--[[ WoWBench debugging snippet
|
||||||
|
if(WOWB_VER) then
|
||||||
|
local function SayTimer()
|
||||||
|
print("SAY: "..GetTime().." "..arg1)
|
||||||
|
end
|
||||||
|
ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
|
||||||
|
ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,250 @@
|
|||||||
|
--- **AceConsole-3.0** provides registration facilities for slash commands.
|
||||||
|
-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
|
||||||
|
-- to your addons individual needs.
|
||||||
|
--
|
||||||
|
-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
|
||||||
|
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||||
|
-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
|
||||||
|
-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
|
||||||
|
-- make into AceConsole.
|
||||||
|
-- @class file
|
||||||
|
-- @name AceConsole-3.0
|
||||||
|
-- @release $Id: AceConsole-3.0.lua 1143 2016-07-11 08:52:03Z nevcairiel $
|
||||||
|
local MAJOR,MINOR = "AceConsole-3.0", 7
|
||||||
|
|
||||||
|
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceConsole then return end -- No upgrade needed
|
||||||
|
|
||||||
|
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
|
||||||
|
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
|
||||||
|
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local tconcat, tostring, select = table.concat, tostring, select
|
||||||
|
local type, pairs, error = type, pairs, error
|
||||||
|
local format, strfind, strsub = string.format, string.find, string.sub
|
||||||
|
local max = math.max
|
||||||
|
|
||||||
|
-- WoW APIs
|
||||||
|
local _G = _G
|
||||||
|
|
||||||
|
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||||
|
-- List them here for Mikk's FindGlobals script
|
||||||
|
-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
|
||||||
|
|
||||||
|
local tmp={}
|
||||||
|
local function Print(self,frame,...)
|
||||||
|
local n=0
|
||||||
|
if self ~= AceConsole then
|
||||||
|
n=n+1
|
||||||
|
tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
|
||||||
|
end
|
||||||
|
for i=1, select("#", ...) do
|
||||||
|
n=n+1
|
||||||
|
tmp[n] = tostring(select(i, ...))
|
||||||
|
end
|
||||||
|
frame:AddMessage( tconcat(tmp," ",1,n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
|
||||||
|
-- @paramsig [chatframe ,] ...
|
||||||
|
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
|
||||||
|
-- @param ... List of any values to be printed
|
||||||
|
function AceConsole:Print(...)
|
||||||
|
local frame = ...
|
||||||
|
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
|
||||||
|
return Print(self, frame, select(2,...))
|
||||||
|
else
|
||||||
|
return Print(self, DEFAULT_CHAT_FRAME, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
|
||||||
|
-- @paramsig [chatframe ,] "format"[, ...]
|
||||||
|
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
|
||||||
|
-- @param format Format string - same syntax as standard Lua format()
|
||||||
|
-- @param ... Arguments to the format string
|
||||||
|
function AceConsole:Printf(...)
|
||||||
|
local frame = ...
|
||||||
|
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
|
||||||
|
return Print(self, frame, format(select(2,...)))
|
||||||
|
else
|
||||||
|
return Print(self, DEFAULT_CHAT_FRAME, format(...))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Register a simple chat command
|
||||||
|
-- @param command Chat command to be registered WITHOUT leading "/"
|
||||||
|
-- @param func Function to call when the slash command is being used (funcref or methodname)
|
||||||
|
-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
|
||||||
|
function AceConsole:RegisterChatCommand( command, func, persist )
|
||||||
|
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
|
||||||
|
|
||||||
|
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
|
||||||
|
|
||||||
|
local name = "ACECONSOLE_"..command:upper()
|
||||||
|
|
||||||
|
if type( func ) == "string" then
|
||||||
|
SlashCmdList[name] = function(input, editBox)
|
||||||
|
self[func](self, input, editBox)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
SlashCmdList[name] = func
|
||||||
|
end
|
||||||
|
_G["SLASH_"..name.."1"] = "/"..command:lower()
|
||||||
|
AceConsole.commands[command] = name
|
||||||
|
-- non-persisting commands are registered for enabling disabling
|
||||||
|
if not persist then
|
||||||
|
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
|
||||||
|
AceConsole.weakcommands[self][command] = func
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unregister a chatcommand
|
||||||
|
-- @param command Chat command to be unregistered WITHOUT leading "/"
|
||||||
|
function AceConsole:UnregisterChatCommand( command )
|
||||||
|
local name = AceConsole.commands[command]
|
||||||
|
if name then
|
||||||
|
SlashCmdList[name] = nil
|
||||||
|
_G["SLASH_" .. name .. "1"] = nil
|
||||||
|
hash_SlashCmdList["/" .. command:upper()] = nil
|
||||||
|
AceConsole.commands[command] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get an iterator over all Chat Commands registered with AceConsole
|
||||||
|
-- @return Iterator (pairs) over all commands
|
||||||
|
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
|
||||||
|
|
||||||
|
|
||||||
|
local function nils(n, ...)
|
||||||
|
if n>1 then
|
||||||
|
return nil, nils(n-1, ...)
|
||||||
|
elseif n==1 then
|
||||||
|
return nil, ...
|
||||||
|
else
|
||||||
|
return ...
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Retreive one or more space-separated arguments from a string.
|
||||||
|
-- Treats quoted strings and itemlinks as non-spaced.
|
||||||
|
-- @param str The raw argument string
|
||||||
|
-- @param numargs How many arguments to get (default 1)
|
||||||
|
-- @param startpos Where in the string to start scanning (default 1)
|
||||||
|
-- @return Returns arg1, arg2, ..., nextposition\\
|
||||||
|
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
|
||||||
|
function AceConsole:GetArgs(str, numargs, startpos)
|
||||||
|
numargs = numargs or 1
|
||||||
|
startpos = max(startpos or 1, 1)
|
||||||
|
|
||||||
|
local pos=startpos
|
||||||
|
|
||||||
|
-- find start of new arg
|
||||||
|
pos = strfind(str, "[^ ]", pos)
|
||||||
|
if not pos then -- whoops, end of string
|
||||||
|
return nils(numargs, 1e9)
|
||||||
|
end
|
||||||
|
|
||||||
|
if numargs<1 then
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- quoted or space separated? find out which pattern to use
|
||||||
|
local delim_or_pipe
|
||||||
|
local ch = strsub(str, pos, pos)
|
||||||
|
if ch=='"' then
|
||||||
|
pos = pos + 1
|
||||||
|
delim_or_pipe='([|"])'
|
||||||
|
elseif ch=="'" then
|
||||||
|
pos = pos + 1
|
||||||
|
delim_or_pipe="([|'])"
|
||||||
|
else
|
||||||
|
delim_or_pipe="([| ])"
|
||||||
|
end
|
||||||
|
|
||||||
|
startpos = pos
|
||||||
|
|
||||||
|
while true do
|
||||||
|
-- find delimiter or hyperlink
|
||||||
|
local ch,_
|
||||||
|
pos,_,ch = strfind(str, delim_or_pipe, pos)
|
||||||
|
|
||||||
|
if not pos then break end
|
||||||
|
|
||||||
|
if ch=="|" then
|
||||||
|
-- some kind of escape
|
||||||
|
|
||||||
|
if strsub(str,pos,pos+1)=="|H" then
|
||||||
|
-- It's a |H....|hhyper link!|h
|
||||||
|
pos=strfind(str, "|h", pos+2) -- first |h
|
||||||
|
if not pos then break end
|
||||||
|
|
||||||
|
pos=strfind(str, "|h", pos+2) -- second |h
|
||||||
|
if not pos then break end
|
||||||
|
elseif strsub(str,pos, pos+1) == "|T" then
|
||||||
|
-- It's a |T....|t texture
|
||||||
|
pos=strfind(str, "|t", pos+2)
|
||||||
|
if not pos then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- found delimiter, done with this arg
|
||||||
|
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
|
||||||
|
return strsub(str, startpos), nils(numargs-1, 1e9)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- embedding and embed handling
|
||||||
|
|
||||||
|
local mixins = {
|
||||||
|
"Print",
|
||||||
|
"Printf",
|
||||||
|
"RegisterChatCommand",
|
||||||
|
"UnregisterChatCommand",
|
||||||
|
"GetArgs",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
|
||||||
|
-- @param target target object to embed AceBucket in
|
||||||
|
function AceConsole:Embed( target )
|
||||||
|
for k, v in pairs( mixins ) do
|
||||||
|
target[v] = self[v]
|
||||||
|
end
|
||||||
|
self.embeds[target] = true
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceConsole:OnEmbedEnable( target )
|
||||||
|
if AceConsole.weakcommands[target] then
|
||||||
|
for command, func in pairs( AceConsole.weakcommands[target] ) do
|
||||||
|
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceConsole:OnEmbedDisable( target )
|
||||||
|
if AceConsole.weakcommands[target] then
|
||||||
|
for command, func in pairs( AceConsole.weakcommands[target] ) do
|
||||||
|
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for addon in pairs(AceConsole.embeds) do
|
||||||
|
AceConsole:Embed(addon)
|
||||||
|
end
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceConsole-3.0.lua" />
|
||||||
|
</Ui>
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
--- AceEvent-3.0 provides event registration and secure dispatching.
|
||||||
|
-- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
|
||||||
|
-- CallbackHandler, and dispatches all game events or addon message to the registrees.
|
||||||
|
--
|
||||||
|
-- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent:Embed(MyAddon) or by
|
||||||
|
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||||
|
-- and can be accessed directly, without having to explicitly call AceEvent itself.\\
|
||||||
|
-- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
|
||||||
|
-- make into AceEvent.
|
||||||
|
-- @class file
|
||||||
|
-- @name AceEvent-3.0
|
||||||
|
-- @release $Id: AceEvent-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
|
||||||
|
local CallbackHandler = LibStub("CallbackHandler-1.0")
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceEvent-3.0", 4
|
||||||
|
local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceEvent then return end
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
|
AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
|
||||||
|
AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
|
||||||
|
|
||||||
|
-- APIs and registry for blizzard events, using CallbackHandler lib
|
||||||
|
if not AceEvent.events then
|
||||||
|
AceEvent.events = CallbackHandler:New(AceEvent,
|
||||||
|
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceEvent.events:OnUsed(target, eventname)
|
||||||
|
AceEvent.frame:RegisterEvent(eventname)
|
||||||
|
end
|
||||||
|
|
||||||
|
function AceEvent.events:OnUnused(target, eventname)
|
||||||
|
AceEvent.frame:UnregisterEvent(eventname)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- APIs and registry for IPC messages, using CallbackHandler lib
|
||||||
|
if not AceEvent.messages then
|
||||||
|
AceEvent.messages = CallbackHandler:New(AceEvent,
|
||||||
|
"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
|
||||||
|
)
|
||||||
|
AceEvent.SendMessage = AceEvent.messages.Fire
|
||||||
|
end
|
||||||
|
|
||||||
|
--- embedding and embed handling
|
||||||
|
local mixins = {
|
||||||
|
"RegisterEvent", "UnregisterEvent",
|
||||||
|
"RegisterMessage", "UnregisterMessage",
|
||||||
|
"SendMessage",
|
||||||
|
"UnregisterAllEvents", "UnregisterAllMessages",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Register for a Blizzard Event.
|
||||||
|
-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
|
||||||
|
-- Any arguments to the event will be passed on after that.
|
||||||
|
-- @name AceEvent:RegisterEvent
|
||||||
|
-- @class function
|
||||||
|
-- @paramsig event[, callback [, arg]]
|
||||||
|
-- @param event The event to register for
|
||||||
|
-- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
|
||||||
|
-- @param arg An optional argument to pass to the callback function
|
||||||
|
|
||||||
|
--- Unregister an event.
|
||||||
|
-- @name AceEvent:UnregisterEvent
|
||||||
|
-- @class function
|
||||||
|
-- @paramsig event
|
||||||
|
-- @param event The event to unregister
|
||||||
|
|
||||||
|
--- Register for a custom AceEvent-internal message.
|
||||||
|
-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
|
||||||
|
-- Any arguments to the event will be passed on after that.
|
||||||
|
-- @name AceEvent:RegisterMessage
|
||||||
|
-- @class function
|
||||||
|
-- @paramsig message[, callback [, arg]]
|
||||||
|
-- @param message The message to register for
|
||||||
|
-- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
|
||||||
|
-- @param arg An optional argument to pass to the callback function
|
||||||
|
|
||||||
|
--- Unregister a message
|
||||||
|
-- @name AceEvent:UnregisterMessage
|
||||||
|
-- @class function
|
||||||
|
-- @paramsig message
|
||||||
|
-- @param message The message to unregister
|
||||||
|
|
||||||
|
--- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
|
||||||
|
-- @name AceEvent:SendMessage
|
||||||
|
-- @class function
|
||||||
|
-- @paramsig message, ...
|
||||||
|
-- @param message The message to send
|
||||||
|
-- @param ... Any arguments to the message
|
||||||
|
|
||||||
|
|
||||||
|
-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
|
||||||
|
-- @param target target object to embed AceEvent in
|
||||||
|
function AceEvent:Embed(target)
|
||||||
|
for k, v in pairs(mixins) do
|
||||||
|
target[v] = self[v]
|
||||||
|
end
|
||||||
|
self.embeds[target] = true
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
-- AceEvent:OnEmbedDisable( target )
|
||||||
|
-- target (object) - target object that is being disabled
|
||||||
|
--
|
||||||
|
-- Unregister all events messages etc when the target disables.
|
||||||
|
-- this method should be called by the target manually or by an addon framework
|
||||||
|
function AceEvent:OnEmbedDisable(target)
|
||||||
|
target:UnregisterAllEvents()
|
||||||
|
target:UnregisterAllMessages()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Script to fire blizzard events into the event listeners
|
||||||
|
local events = AceEvent.events
|
||||||
|
AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
|
||||||
|
events:Fire(event, ...)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- Finally: upgrade our old embeds
|
||||||
|
for target, v in pairs(AceEvent.embeds) do
|
||||||
|
AceEvent:Embed(target)
|
||||||
|
end
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceEvent-3.0.lua" />
|
||||||
|
</Ui>
|
||||||
@ -0,0 +1,276 @@
|
|||||||
|
--- **AceTimer-3.0** provides a central facility for registering timers.
|
||||||
|
-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
|
||||||
|
-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
|
||||||
|
-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
|
||||||
|
-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
|
||||||
|
-- restricts us to.
|
||||||
|
--
|
||||||
|
-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
|
||||||
|
-- need to cancel the timer you just registered.
|
||||||
|
--
|
||||||
|
-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
|
||||||
|
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||||
|
-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
|
||||||
|
-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
|
||||||
|
-- make into AceTimer.
|
||||||
|
-- @class file
|
||||||
|
-- @name AceTimer-3.0
|
||||||
|
-- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
|
||||||
|
local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceTimer then return end -- No upgrade needed
|
||||||
|
AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
|
||||||
|
local activeTimers = AceTimer.activeTimers -- Upvalue our private data
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local type, unpack, next, error, select = type, unpack, next, error, select
|
||||||
|
-- WoW APIs
|
||||||
|
local GetTime, C_TimerAfter = GetTime, C_Timer.After
|
||||||
|
|
||||||
|
local function new(self, loop, func, delay, ...)
|
||||||
|
if delay < 0.01 then
|
||||||
|
delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
|
||||||
|
end
|
||||||
|
|
||||||
|
local timer = {...}
|
||||||
|
timer.object = self
|
||||||
|
timer.func = func
|
||||||
|
timer.looping = loop
|
||||||
|
timer.argsCount = select("#", ...)
|
||||||
|
timer.delay = delay
|
||||||
|
timer.ends = GetTime() + delay
|
||||||
|
|
||||||
|
activeTimers[timer] = timer
|
||||||
|
|
||||||
|
-- Create new timer closure to wrap the "timer" object
|
||||||
|
timer.callback = function()
|
||||||
|
if not timer.cancelled then
|
||||||
|
if type(timer.func) == "string" then
|
||||||
|
-- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
|
||||||
|
-- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
|
||||||
|
timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
|
||||||
|
else
|
||||||
|
timer.func(unpack(timer, 1, timer.argsCount))
|
||||||
|
end
|
||||||
|
|
||||||
|
if timer.looping and not timer.cancelled then
|
||||||
|
-- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
|
||||||
|
-- due to fps differences
|
||||||
|
local time = GetTime()
|
||||||
|
local delay = timer.delay - (time - timer.ends)
|
||||||
|
-- Ensure the delay doesn't go below the threshold
|
||||||
|
if delay < 0.01 then delay = 0.01 end
|
||||||
|
C_TimerAfter(delay, timer.callback)
|
||||||
|
timer.ends = time + delay
|
||||||
|
else
|
||||||
|
activeTimers[timer.handle or timer] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
C_TimerAfter(delay, timer.callback)
|
||||||
|
return timer
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Schedule a new one-shot timer.
|
||||||
|
-- The timer will fire once in `delay` seconds, unless canceled before.
|
||||||
|
-- @param callback Callback function for the timer pulse (funcref or method name).
|
||||||
|
-- @param delay Delay for the timer, in seconds.
|
||||||
|
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
|
||||||
|
-- @usage
|
||||||
|
-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
|
||||||
|
--
|
||||||
|
-- function MyAddOn:OnEnable()
|
||||||
|
-- self:ScheduleTimer("TimerFeedback", 5)
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- function MyAddOn:TimerFeedback()
|
||||||
|
-- print("5 seconds passed")
|
||||||
|
-- end
|
||||||
|
function AceTimer:ScheduleTimer(func, delay, ...)
|
||||||
|
if not func or not delay then
|
||||||
|
error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
|
||||||
|
end
|
||||||
|
if type(func) == "string" then
|
||||||
|
if type(self) ~= "table" then
|
||||||
|
error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
|
||||||
|
elseif not self[func] then
|
||||||
|
error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return new(self, nil, func, delay, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Schedule a repeating timer.
|
||||||
|
-- The timer will fire every `delay` seconds, until canceled.
|
||||||
|
-- @param callback Callback function for the timer pulse (funcref or method name).
|
||||||
|
-- @param delay Delay for the timer, in seconds.
|
||||||
|
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
|
||||||
|
-- @usage
|
||||||
|
-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
|
||||||
|
--
|
||||||
|
-- function MyAddOn:OnEnable()
|
||||||
|
-- self.timerCount = 0
|
||||||
|
-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- function MyAddOn:TimerFeedback()
|
||||||
|
-- self.timerCount = self.timerCount + 1
|
||||||
|
-- print(("%d seconds passed"):format(5 * self.timerCount))
|
||||||
|
-- -- run 30 seconds in total
|
||||||
|
-- if self.timerCount == 6 then
|
||||||
|
-- self:CancelTimer(self.testTimer)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
|
||||||
|
if not func or not delay then
|
||||||
|
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
|
||||||
|
end
|
||||||
|
if type(func) == "string" then
|
||||||
|
if type(self) ~= "table" then
|
||||||
|
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
|
||||||
|
elseif not self[func] then
|
||||||
|
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return new(self, true, func, delay, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
|
||||||
|
-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
|
||||||
|
-- and the timer has not fired yet or was canceled before.
|
||||||
|
-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
|
||||||
|
function AceTimer:CancelTimer(id)
|
||||||
|
local timer = activeTimers[id]
|
||||||
|
|
||||||
|
if not timer then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
timer.cancelled = true
|
||||||
|
activeTimers[id] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Cancels all timers registered to the current addon object ('self')
|
||||||
|
function AceTimer:CancelAllTimers()
|
||||||
|
for k,v in pairs(activeTimers) do
|
||||||
|
if v.object == self then
|
||||||
|
AceTimer.CancelTimer(self, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
|
||||||
|
-- This function will return 0 when the id is invalid.
|
||||||
|
-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
|
||||||
|
-- @return The time left on the timer.
|
||||||
|
function AceTimer:TimeLeft(id)
|
||||||
|
local timer = activeTimers[id]
|
||||||
|
if not timer then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return timer.ends - GetTime()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- Upgrading
|
||||||
|
|
||||||
|
-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
|
||||||
|
if oldminor and oldminor < 10 then
|
||||||
|
-- disable old timer logic
|
||||||
|
AceTimer.frame:SetScript("OnUpdate", nil)
|
||||||
|
AceTimer.frame:SetScript("OnEvent", nil)
|
||||||
|
AceTimer.frame:UnregisterAllEvents()
|
||||||
|
-- convert timers
|
||||||
|
for object,timers in pairs(AceTimer.selfs) do
|
||||||
|
for handle,timer in pairs(timers) do
|
||||||
|
if type(timer) == "table" and timer.callback then
|
||||||
|
local newTimer
|
||||||
|
if timer.delay then
|
||||||
|
newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
|
||||||
|
else
|
||||||
|
newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
|
||||||
|
end
|
||||||
|
-- Use the old handle for old timers
|
||||||
|
activeTimers[newTimer] = nil
|
||||||
|
activeTimers[handle] = newTimer
|
||||||
|
newTimer.handle = handle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
AceTimer.selfs = nil
|
||||||
|
AceTimer.hash = nil
|
||||||
|
AceTimer.debug = nil
|
||||||
|
elseif oldminor and oldminor < 17 then
|
||||||
|
-- Upgrade from old animation based timers to C_Timer.After timers.
|
||||||
|
AceTimer.inactiveTimers = nil
|
||||||
|
AceTimer.frame = nil
|
||||||
|
local oldTimers = AceTimer.activeTimers
|
||||||
|
-- Clear old timer table and update upvalue
|
||||||
|
AceTimer.activeTimers = {}
|
||||||
|
activeTimers = AceTimer.activeTimers
|
||||||
|
for handle, timer in pairs(oldTimers) do
|
||||||
|
local newTimer
|
||||||
|
-- Stop the old timer animation
|
||||||
|
local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
|
||||||
|
timer:GetParent():Stop()
|
||||||
|
if timer.looping then
|
||||||
|
newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
|
||||||
|
else
|
||||||
|
newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
|
||||||
|
end
|
||||||
|
-- Use the old handle for old timers
|
||||||
|
activeTimers[newTimer] = nil
|
||||||
|
activeTimers[handle] = newTimer
|
||||||
|
newTimer.handle = handle
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Migrate transitional handles
|
||||||
|
if oldminor < 13 and AceTimer.hashCompatTable then
|
||||||
|
for handle, id in pairs(AceTimer.hashCompatTable) do
|
||||||
|
local t = activeTimers[id]
|
||||||
|
if t then
|
||||||
|
activeTimers[id] = nil
|
||||||
|
activeTimers[handle] = t
|
||||||
|
t.handle = handle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
AceTimer.hashCompatTable = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- Embed handling
|
||||||
|
|
||||||
|
AceTimer.embeds = AceTimer.embeds or {}
|
||||||
|
|
||||||
|
local mixins = {
|
||||||
|
"ScheduleTimer", "ScheduleRepeatingTimer",
|
||||||
|
"CancelTimer", "CancelAllTimers",
|
||||||
|
"TimeLeft"
|
||||||
|
}
|
||||||
|
|
||||||
|
function AceTimer:Embed(target)
|
||||||
|
AceTimer.embeds[target] = true
|
||||||
|
for _,v in pairs(mixins) do
|
||||||
|
target[v] = AceTimer[v]
|
||||||
|
end
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
-- AceTimer:OnEmbedDisable(target)
|
||||||
|
-- target (object) - target object that AceTimer is embedded in.
|
||||||
|
--
|
||||||
|
-- cancel all timers registered for the object
|
||||||
|
function AceTimer:OnEmbedDisable(target)
|
||||||
|
target:CancelAllTimers()
|
||||||
|
end
|
||||||
|
|
||||||
|
for addon in pairs(AceTimer.embeds) do
|
||||||
|
AceTimer:Embed(addon)
|
||||||
|
end
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceTimer-3.0.lua" />
|
||||||
|
</Ui>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
|
||||||
|
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
||||||
|
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
|
||||||
|
local LibStub = _G[LIBSTUB_MAJOR]
|
||||||
|
|
||||||
|
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
||||||
|
LibStub = LibStub or {libs = {}, minors = {} }
|
||||||
|
_G[LIBSTUB_MAJOR] = LibStub
|
||||||
|
LibStub.minor = LIBSTUB_MINOR
|
||||||
|
|
||||||
|
function LibStub:NewLibrary(major, minor)
|
||||||
|
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
||||||
|
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
|
||||||
|
|
||||||
|
local oldminor = self.minors[major]
|
||||||
|
if oldminor and oldminor >= minor then return nil end
|
||||||
|
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
||||||
|
return self.libs[major], oldminor
|
||||||
|
end
|
||||||
|
|
||||||
|
function LibStub:GetLibrary(major, silent)
|
||||||
|
if not self.libs[major] and not silent then
|
||||||
|
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
|
||||||
|
end
|
||||||
|
return self.libs[major], self.minors[major]
|
||||||
|
end
|
||||||
|
|
||||||
|
function LibStub:IterateLibraries() return pairs(self.libs) end
|
||||||
|
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
||||||
|
end
|
||||||
@ -0,0 +1,382 @@
|
|||||||
|
local json = LibStub:NewLibrary("json", 1)
|
||||||
|
if not json then return end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2015 rxi
|
||||||
|
--
|
||||||
|
-- This library is free software; you can redistribute it and/or modify it
|
||||||
|
-- under the terms of the MIT license. See LICENSE for details.
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\\\",
|
||||||
|
[ "\"" ] = "\\\"",
|
||||||
|
[ "\b" ] = "\\b",
|
||||||
|
[ "\f" ] = "\\f",
|
||||||
|
[ "\n" ] = "\\n",
|
||||||
|
[ "\r" ] = "\\r",
|
||||||
|
[ "\t" ] = "\\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if val[1] ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
local line_count = 1
|
||||||
|
local col_count = 1
|
||||||
|
for i = 1, idx - 1 do
|
||||||
|
col_count = col_count + 1
|
||||||
|
if str:sub(i, i) == "\n" then
|
||||||
|
line_count = line_count + 1
|
||||||
|
col_count = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||||
|
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local has_unicode_escape = false
|
||||||
|
local has_surrogate_escape = false
|
||||||
|
local has_escape = false
|
||||||
|
local last
|
||||||
|
for j = i + 1, #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
end
|
||||||
|
|
||||||
|
if last == 92 then -- "\\" (escape char)
|
||||||
|
if x == 117 then -- "u" (unicode escape sequence)
|
||||||
|
local hex = str:sub(j + 1, j + 5)
|
||||||
|
if not hex:find("%x%x%x%x") then
|
||||||
|
decode_error(str, j, "invalid unicode escape in string")
|
||||||
|
end
|
||||||
|
if hex:find("^[dD][89aAbB]") then
|
||||||
|
has_surrogate_escape = true
|
||||||
|
else
|
||||||
|
has_unicode_escape = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local c = string.char(x)
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
has_escape = true
|
||||||
|
end
|
||||||
|
last = nil
|
||||||
|
|
||||||
|
elseif x == 34 then -- '"' (end of string)
|
||||||
|
local s = str:sub(i + 1, j - 1)
|
||||||
|
if has_surrogate_escape then
|
||||||
|
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_unicode_escape then
|
||||||
|
s = s:gsub("\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_escape then
|
||||||
|
s = s:gsub("\\.", escape_char_map_inv)
|
||||||
|
end
|
||||||
|
return s, j + 1
|
||||||
|
|
||||||
|
else
|
||||||
|
last = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
return ( parse(str, next_char(str, 1, space_chars, true)) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return json
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
|
||||||
|
<Script file="Libs\LibStub\LibStub.lua" />
|
||||||
|
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml" />
|
||||||
|
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml" />
|
||||||
|
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml" />
|
||||||
|
<Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml" />
|
||||||
|
<Include file="Libs\AceComm-3.0\AceComm-3.0.xml" />
|
||||||
|
<Include file="Libs\json.lua" />
|
||||||
|
</Ui>
|
||||||
BIN
Artemis/Artemis/Modules/Games/WoW/Resources/wow-addon.zip
Normal file
BIN
Artemis/Artemis/Modules/Games/WoW/Resources/wow-addon.zip
Normal file
Binary file not shown.
@ -1,256 +0,0 @@
|
|||||||
namespace Artemis.Modules.Games.WoW
|
|
||||||
{
|
|
||||||
public static class WoWAddresses
|
|
||||||
{
|
|
||||||
public enum ActivateSettings
|
|
||||||
{
|
|
||||||
Activate_Offset = 0x34,
|
|
||||||
AutoDismount_Activate_Pointer = 0xe56850,
|
|
||||||
AutoInteract_Activate_Pointer = 0xe56848,
|
|
||||||
AutoLoot_Activate_Pointer = 0xe56868,
|
|
||||||
AutoSelfCast_Activate_Pointer = 0xe56874
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Battleground
|
|
||||||
{
|
|
||||||
MaxBattlegroundId = 0xec3fdc,
|
|
||||||
PvpExitWindow = 0xec4198,
|
|
||||||
StatPvp = 0xc3c03c
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Chat
|
|
||||||
{
|
|
||||||
chatBufferPos = 0xeb1bf0,
|
|
||||||
chatBufferStart = 0xe58190,
|
|
||||||
msgFormatedChat = 0x65,
|
|
||||||
NextMessage = 0x17e8
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ClickToMove
|
|
||||||
{
|
|
||||||
CTM = 0xddf8f0,
|
|
||||||
CTM_PUSH = 0xddf8ac,
|
|
||||||
CTM_X = 0xddf918,
|
|
||||||
CTM_Y = 0xddf91c,
|
|
||||||
CTM_Z = 0xddf920
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CorpsePlayer
|
|
||||||
{
|
|
||||||
X = 0xe57894,
|
|
||||||
Y = 0xe57898,
|
|
||||||
Z = 0xe5789c
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum DBC
|
|
||||||
{
|
|
||||||
FactionTemplate = 0,
|
|
||||||
ItemClass = 0xd173c0,
|
|
||||||
ItemSubClass = 0,
|
|
||||||
Lock = 0,
|
|
||||||
Map = 0xd291a0,
|
|
||||||
QuestPOIPoint = 0xd1e950,
|
|
||||||
ResearchSite = 0xd1d2d0,
|
|
||||||
SpellCategories = 0,
|
|
||||||
Unknown = 0xf35428
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EventsListener
|
|
||||||
{
|
|
||||||
BaseEvents = 0xcb2474,
|
|
||||||
EventOffsetCount = 0x48,
|
|
||||||
EventOffsetName = 0x18,
|
|
||||||
EventsCount = 0xcb2470
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Fishing
|
|
||||||
{
|
|
||||||
BobberHasMoved = 0xf8
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum FunctionWow
|
|
||||||
{
|
|
||||||
CGUnit_C__InitializeTrackingState = 0x30623b,
|
|
||||||
CGUnit_C__Interact = 0x524ff,
|
|
||||||
CGWorldFrame__Intersect = 0x5e46ab,
|
|
||||||
ClntObjMgrGetActivePlayerObj = 0x816d7,
|
|
||||||
FrameScript__GetLocalizedText = 0x300b48,
|
|
||||||
FrameScript_ExecuteBuffer = 0xa6772,
|
|
||||||
IsOutdoors = 0,
|
|
||||||
Spell_C_HandleTerrainClick = 0x2b76ff,
|
|
||||||
strlen = 0x74fcb0,
|
|
||||||
UnitCanAttack = 0,
|
|
||||||
WowClientDB2__GetRowPointer = 0x20c775
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum GameInfo
|
|
||||||
{
|
|
||||||
AreaId = 0xc32c2c,
|
|
||||||
buildWoWVersionString = 0xd002a8,
|
|
||||||
gameState = 0xe56a49,
|
|
||||||
GetTime = 0xcb2150,
|
|
||||||
isLoading = 0xca59b0,
|
|
||||||
LastHardwareAction = 0xd0e090,
|
|
||||||
MapTextureId = 0xc3bd28,
|
|
||||||
SubAreaId = 0xc32c24,
|
|
||||||
subZoneMap = 0xe56a68,
|
|
||||||
TextBoxActivated = 0xbbe9ac,
|
|
||||||
zoneMap = 0xe56a64
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum GameObject
|
|
||||||
{
|
|
||||||
CachedCastBarCaption = 12,
|
|
||||||
CachedData0 = 20,
|
|
||||||
CachedIconName = 8,
|
|
||||||
CachedName = 180,
|
|
||||||
CachedQuestItem1 = 0x9c,
|
|
||||||
CachedSize = 0x98,
|
|
||||||
DBCacheRow = 620,
|
|
||||||
GAMEOBJECT_FIELD_X = 0x138,
|
|
||||||
GAMEOBJECT_FIELD_Y = 0x13c,
|
|
||||||
GAMEOBJECT_FIELD_Z = 320,
|
|
||||||
PackedRotationQuaternion = 0x150,
|
|
||||||
TransformationMatrice = 0x278
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Hooking
|
|
||||||
{
|
|
||||||
DX_DEVICE = 0xcc523c,
|
|
||||||
DX_DEVICE_IDX = 0x2508,
|
|
||||||
ENDSCENE_IDX = 0xa8
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Login
|
|
||||||
{
|
|
||||||
realmName = 0xf35e16
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MovementFlagsOffsets
|
|
||||||
{
|
|
||||||
Offset1 = 0x124,
|
|
||||||
Offset2 = 0x40
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ObjectManager
|
|
||||||
{
|
|
||||||
continentId = 0x108,
|
|
||||||
firstObject = 0xd8,
|
|
||||||
localGuid = 0xf8,
|
|
||||||
nextObject = 0x44,
|
|
||||||
objectGUID = 0x30,
|
|
||||||
objectTYPE = 0x10
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Party
|
|
||||||
{
|
|
||||||
NumOfPlayers = 200,
|
|
||||||
NumOfPlayersSuBGroup = 0xcc,
|
|
||||||
PartyOffset = 0xeb5458,
|
|
||||||
PlayerGuid = 0x10
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PetBattle
|
|
||||||
{
|
|
||||||
IsInBattle = 0xba8a10
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Player
|
|
||||||
{
|
|
||||||
LocalPlayerSpellsOnCooldown = 0xd372b8,
|
|
||||||
petGUID = 0xec7158,
|
|
||||||
playerName = 0xf35e20,
|
|
||||||
RetrieveCorpseWindow = 0xe576f4,
|
|
||||||
RuneStartCooldown = 0xf18aa8,
|
|
||||||
SkillMaxValue = 0x400,
|
|
||||||
SkillValue = 0x200
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PlayerNameStore
|
|
||||||
{
|
|
||||||
PlayerNameNextOffset = 20,
|
|
||||||
PlayerNameStorePtr = 0xd0b4e0,
|
|
||||||
PlayerNameStringOffset = 0x11
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PowerIndex
|
|
||||||
{
|
|
||||||
Multiplicator = 0x10,
|
|
||||||
PowerIndexArrays = 0xddf914
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Quests
|
|
||||||
{
|
|
||||||
QuestGiverStatus = 0xf4
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SpellBook
|
|
||||||
{
|
|
||||||
FirstTalentBookPtr = 0xeb52ec,
|
|
||||||
KnownAllSpells = 0xeb5130,
|
|
||||||
MountBookMountsPtr = 0xeb5194,
|
|
||||||
MountBookNumMounts = 0xeb5190,
|
|
||||||
NextTalentBookPtr = 0xeb52e4,
|
|
||||||
SpellBookNumSpells = 0xeb5134,
|
|
||||||
SpellBookSpellsPtr = 0xeb5138,
|
|
||||||
SpellDBCMaxIndex = 0x30d40,
|
|
||||||
TalentBookOverrideSpellId = 0x1c,
|
|
||||||
TalentBookSpellId = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum UnitBaseGetUnitAura
|
|
||||||
{
|
|
||||||
AuraSize = 0x58,
|
|
||||||
AuraStructCasterLevel = 0x3a,
|
|
||||||
AuraStructCount = 0x39,
|
|
||||||
AuraStructCreatorGuid = 0x20,
|
|
||||||
AuraStructDuration = 60,
|
|
||||||
AuraStructFlag = 0x34,
|
|
||||||
AuraStructMask = 0x35,
|
|
||||||
AuraStructSpellEndTime = 0x40,
|
|
||||||
AuraStructSpellId = 0x30,
|
|
||||||
AuraStructUnk1 = 0x3b,
|
|
||||||
AuraStructUnk2 = 0x44,
|
|
||||||
AuraTable1 = 0x1150,
|
|
||||||
AuraTable2 = 0x580
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum UnitField
|
|
||||||
{
|
|
||||||
CachedIsBoss = 0x60,
|
|
||||||
CachedModelId1 = 0x6c,
|
|
||||||
CachedName = 0x80,
|
|
||||||
CachedQuestItem1 = 60,
|
|
||||||
CachedSubName = 0,
|
|
||||||
CachedTypeFlag = 0x24,
|
|
||||||
CachedUnitClassification = 0x2c,
|
|
||||||
CanInterrupt = 0xfc4,
|
|
||||||
CanInterruptOffset = 0xe02ea0,
|
|
||||||
CanInterruptOffset2 = 0xe02ea4,
|
|
||||||
CanInterruptOffset3 = 0xe02ea8,
|
|
||||||
CastingSpellEndTime = 0x108c,
|
|
||||||
CastingSpellID = 0x1064,
|
|
||||||
CastingSpellStartTime = 0x1088,
|
|
||||||
ChannelSpellEndTime = 0x1098,
|
|
||||||
ChannelSpellID = 0x1090,
|
|
||||||
ChannelSpellStartTime = 0x1094,
|
|
||||||
DBCacheRow = 0xc80,
|
|
||||||
TransportGUID = 0xae8,
|
|
||||||
UNIT_FIELD_R = 0xb08,
|
|
||||||
UNIT_FIELD_X = 0xaf8,
|
|
||||||
UNIT_FIELD_Y = 0xafc,
|
|
||||||
UNIT_FIELD_Z = 0xb00
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VMT
|
|
||||||
{
|
|
||||||
CGUnit_C__GetFacing = 0x35
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ObjectManagerClass
|
|
||||||
{
|
|
||||||
public static uint clientConnection;
|
|
||||||
public static uint sCurMgr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,33 @@
|
|||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Artemis.Modules.Games.WoW.Data;
|
using Artemis.Modules.Games.WoW.Models;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW
|
namespace Artemis.Modules.Games.WoW
|
||||||
{
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
public class WoWDataModel : ModuleDataModel
|
public class WoWDataModel : ModuleDataModel
|
||||||
{
|
{
|
||||||
|
public WoWDataModel()
|
||||||
|
{
|
||||||
|
Player = new WoWUnit();
|
||||||
|
Target = new WoWUnit();
|
||||||
|
}
|
||||||
|
|
||||||
public WoWUnit Player { get; set; }
|
public WoWUnit Player { get; set; }
|
||||||
public WoWUnit Target { get; set; }
|
public WoWUnit Target { get; set; }
|
||||||
|
|
||||||
|
public string Realm { get; set; }
|
||||||
|
public string Zone { get; set; }
|
||||||
|
public string SubZone { get; set; }
|
||||||
|
|
||||||
|
public WoWState State { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WoWState
|
||||||
|
{
|
||||||
|
LoggedOut,
|
||||||
|
Ingame,
|
||||||
|
Afk,
|
||||||
|
Dnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,116 +1,261 @@
|
|||||||
using System;
|
using Artemis.DAL;
|
||||||
using System.Linq;
|
|
||||||
using Artemis.DAL;
|
|
||||||
using Artemis.Managers;
|
using Artemis.Managers;
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Artemis.Modules.Games.WoW.Data;
|
using Artemis.Modules.Games.WoW.Models;
|
||||||
using Artemis.Settings;
|
using Artemis.Properties;
|
||||||
using Artemis.Utilities.Memory;
|
using Artemis.Services;
|
||||||
using Process.NET;
|
using Artemis.Utilities;
|
||||||
using Process.NET.Memory;
|
using Microsoft.Win32;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW
|
namespace Artemis.Modules.Games.WoW
|
||||||
{
|
{
|
||||||
public class WoWModel : ModuleModel
|
public class WoWModel : ModuleModel
|
||||||
{
|
{
|
||||||
private readonly GamePointersCollection _pointer;
|
private readonly MetroDialogService _dialogService;
|
||||||
private ProcessSharp _process;
|
private readonly WowPacketScanner _packetScanner;
|
||||||
|
|
||||||
|
public WoWModel(DeviceManager deviceManager, LuaManager luaManager, WowPacketScanner packetScanner, MetroDialogService dialogService) : base(deviceManager, luaManager)
|
||||||
public WoWModel(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager)
|
|
||||||
{
|
{
|
||||||
Settings = SettingsProvider.Load<WoWSettings>();
|
Settings = SettingsProvider.Load<WoWSettings>();
|
||||||
DataModel = new WoWDataModel();
|
DataModel = new WoWDataModel();
|
||||||
ProcessNames.Add("Wow-64");
|
ProcessNames.Add("Wow-64");
|
||||||
|
|
||||||
// Currently WoW is locked behind a hidden trigger (obviously not that hidden since you're reading this)
|
_packetScanner = packetScanner;
|
||||||
// It is using memory reading and lets first try to contact Blizzard
|
_dialogService = dialogService;
|
||||||
var settings = SettingsProvider.Load<GeneralSettings>();
|
_packetScanner.RaiseDataReceived += (sender, args) => HandleGameData(args.Command, args.Data);
|
||||||
Settings.IsEnabled = settings.GamestatePort == 62575 && Settings.IsEnabled;
|
|
||||||
|
|
||||||
_pointer = SettingsProvider.Load<OffsetSettings>().WorldOfWarcraft;
|
FindWoW();
|
||||||
//_pointer = new GamePointersCollection
|
|
||||||
//{
|
// I simply cannot be sure wether this addon will bring people's accounts in trouble so
|
||||||
// Game = "WorldOfWarcraft",
|
// lets remove it whenever Artemis isn't running the WoW module.
|
||||||
// GameVersion = "7.0.3.22810",
|
// (This also means the addon isnt left behind should the user uninstall Artemis.)
|
||||||
// GameAddresses = new List<GamePointer>
|
RemoveAddon();
|
||||||
// {
|
|
||||||
// new GamePointer
|
|
||||||
// {
|
|
||||||
// Description = "ObjectManager",
|
|
||||||
// BasePointer = new IntPtr(0x1578070)
|
|
||||||
// },
|
|
||||||
// new GamePointer
|
|
||||||
// {
|
|
||||||
// Description = "LocalPlayer",
|
|
||||||
// BasePointer = new IntPtr(0x169DF10)
|
|
||||||
// },
|
|
||||||
// new GamePointer
|
|
||||||
// {
|
|
||||||
// Description = "NameCache",
|
|
||||||
// BasePointer = new IntPtr(0x151DCE8)
|
|
||||||
// },
|
|
||||||
// new GamePointer
|
|
||||||
// {
|
|
||||||
// Description = "TargetGuid",
|
|
||||||
// BasePointer = new IntPtr(0x179C940)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//};
|
|
||||||
//var res = JsonConvert.SerializeObject(_pointer, Formatting.Indented);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Name => "WoW";
|
public override string Name => "WoW";
|
||||||
public override bool IsOverlay => false;
|
public override bool IsOverlay => false;
|
||||||
public override bool IsBoundToProcess => true;
|
public override bool IsBoundToProcess => true;
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
PlaceAddon();
|
||||||
|
_packetScanner.Start();
|
||||||
|
base.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
|
RemoveAddon();
|
||||||
|
_packetScanner.Stop();
|
||||||
base.Dispose();
|
base.Dispose();
|
||||||
|
|
||||||
_process?.Dispose();
|
|
||||||
_process = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
if (_process == null)
|
|
||||||
{
|
|
||||||
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessNames[0]);
|
|
||||||
if (tempProcess == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_process = new ProcessSharp(tempProcess, MemoryType.Remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ProfileModel == null || DataModel == null || _process == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dataModel = (WoWDataModel) DataModel;
|
var dataModel = (WoWDataModel) DataModel;
|
||||||
|
|
||||||
var objectManager = new WoWObjectManager(_process,
|
dataModel.Player.Update();
|
||||||
_pointer.GameAddresses.First(a => a.Description == "ObjectManager").BasePointer);
|
dataModel.Target.Update();
|
||||||
var nameCache = new WoWNameCache(_process,
|
}
|
||||||
_pointer.GameAddresses.First(a => a.Description == "NameCache").BasePointer);
|
|
||||||
var player = new WoWPlayer(_process,
|
|
||||||
_pointer.GameAddresses.First(a => a.Description == "LocalPlayer").BasePointer,
|
|
||||||
_pointer.GameAddresses.First(a => a.Description == "TargetGuid").BasePointer, true);
|
|
||||||
|
|
||||||
dataModel.Player = player;
|
public void FindWoW()
|
||||||
if (dataModel.Player != null && dataModel.Player.Guid != Guid.Empty)
|
{
|
||||||
{
|
var gameSettings = Settings as WoWSettings;
|
||||||
dataModel.Player.UpdateDetails(nameCache);
|
if (gameSettings == null)
|
||||||
var target = player.GetTarget(objectManager);
|
return;
|
||||||
if (target == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dataModel.Target = new WoWUnit(target.Process, target.BaseAddress);
|
// If already propertly set up, don't do anything
|
||||||
dataModel.Target.UpdateDetails(nameCache);
|
if (gameSettings.GameDirectory != null && File.Exists(gameSettings.GameDirectory + @"\Wow.exe"))
|
||||||
}
|
return;
|
||||||
else
|
|
||||||
|
var key = Registry.LocalMachine.OpenSubKey(
|
||||||
|
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\World of Warcraft");
|
||||||
|
var path = key?.GetValue("DisplayIcon")?.ToString();
|
||||||
|
if (string.IsNullOrEmpty(path) || !File.Exists(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
gameSettings.GameDirectory = path.Substring(0, path.Length - 8);
|
||||||
|
gameSettings.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeDirectory(string directory, bool checkExe)
|
||||||
|
{
|
||||||
|
var settings = (WoWSettings) Settings;
|
||||||
|
if (checkExe && !File.Exists(directory + @"\Wow.exe"))
|
||||||
{
|
{
|
||||||
dataModel.Target = null;
|
_dialogService.ShowErrorMessageBox("Please select a valid WoW directory\n\n" +
|
||||||
|
@"By default WoW is in C:\Program Files (x86)\World of Warcraft");
|
||||||
|
|
||||||
|
settings.GameDirectory = string.Empty;
|
||||||
|
settings.Save();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
settings.GameDirectory = directory;
|
||||||
|
settings.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PlaceAddon()
|
||||||
|
{
|
||||||
|
var settings = (WoWSettings) Settings;
|
||||||
|
var path = settings.GameDirectory;
|
||||||
|
|
||||||
|
if (!File.Exists(path + @"\Wow.exe"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Load the ZIP from resources
|
||||||
|
using (var stream = new MemoryStream(Resources.wow_addon))
|
||||||
|
{
|
||||||
|
using (var archive = new ZipArchive(stream))
|
||||||
|
{
|
||||||
|
archive.ExtractToDirectory(settings.GameDirectory + @"\Interface\Addons\Artemis", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAddon()
|
||||||
|
{
|
||||||
|
var settings = (WoWSettings) Settings;
|
||||||
|
if (Directory.Exists(settings.GameDirectory + @"\Interface\Addons\Artemis"))
|
||||||
|
Directory.Delete(settings.GameDirectory + @"\Interface\Addons\Artemis", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleGameData(string command, string data)
|
||||||
|
{
|
||||||
|
JToken json = null;
|
||||||
|
if (!data.StartsWith("\"") && !data.EndsWith("\""))
|
||||||
|
json = JToken.Parse(data);
|
||||||
|
|
||||||
|
lock (DataModel)
|
||||||
|
{
|
||||||
|
var dataModel = (WoWDataModel) DataModel;
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "gameState":
|
||||||
|
ParseGameState(data, dataModel);
|
||||||
|
break;
|
||||||
|
case "player":
|
||||||
|
ParsePlayer(json, dataModel);
|
||||||
|
break;
|
||||||
|
case "target":
|
||||||
|
ParseTarget(json, dataModel);
|
||||||
|
break;
|
||||||
|
case "playerState":
|
||||||
|
ParsePlayerState(json, dataModel);
|
||||||
|
break;
|
||||||
|
case "targetState":
|
||||||
|
ParseTargetState(json, dataModel);
|
||||||
|
break;
|
||||||
|
case "buffs":
|
||||||
|
ParseAuras(json, dataModel, true);
|
||||||
|
break;
|
||||||
|
case "debuffs":
|
||||||
|
ParseAuras(json, dataModel, false);
|
||||||
|
break;
|
||||||
|
case "spellCast":
|
||||||
|
ParseSpellCast(json, dataModel, false);
|
||||||
|
break;
|
||||||
|
case "instantSpellCast":
|
||||||
|
ParseInstantSpellCast(json, dataModel);
|
||||||
|
break;
|
||||||
|
case "spellCastFailed":
|
||||||
|
ParseSpellCastFailed(data, dataModel);
|
||||||
|
break;
|
||||||
|
case "spellCastInterrupted":
|
||||||
|
ParseSpellCastInterrupted(data, dataModel);
|
||||||
|
break;
|
||||||
|
case "spellChannel":
|
||||||
|
ParseSpellCast(json, dataModel, true);
|
||||||
|
break;
|
||||||
|
case "spellChannelInterrupted":
|
||||||
|
ParseSpellCastInterrupted(data, dataModel);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger.Warn("The WoW addon sent an unknown command: {0}", command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseGameState(string data, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
if (data == "\"ingame\"")
|
||||||
|
dataModel.State = WoWState.Ingame;
|
||||||
|
else if (data == "\"afk\"")
|
||||||
|
dataModel.State = WoWState.Afk;
|
||||||
|
else if (data == "\"dnd\"")
|
||||||
|
dataModel.State = WoWState.Dnd;
|
||||||
|
else if (data == "\"loggedOut\"")
|
||||||
|
dataModel.State = WoWState.LoggedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParsePlayer(JToken json, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
dataModel.Player.ApplyJson(json);
|
||||||
|
|
||||||
|
// At this point class/race data is available so no point pretending to be logged out
|
||||||
|
if (dataModel.State == WoWState.LoggedOut)
|
||||||
|
dataModel.State = WoWState.Ingame;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseTarget(JToken json, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
dataModel.Target.ApplyJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParsePlayerState(JToken json, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
dataModel.Player.ApplyStateJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseTargetState(JToken json, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
dataModel.Target.ApplyStateJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseAuras(JToken json, WoWDataModel dataModel, bool buffs)
|
||||||
|
{
|
||||||
|
dataModel.Player.ApplyAuraJson(json, buffs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseSpellCast(JToken json, WoWDataModel dataModel, bool isChannel)
|
||||||
|
{
|
||||||
|
if (json["uid"].Value<string>() == "player")
|
||||||
|
dataModel.Player.CastBar.ApplyJson(json, isChannel);
|
||||||
|
else if (json["uid"].Value<string>() == "target")
|
||||||
|
dataModel.Target.CastBar.ApplyJson(json, isChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseInstantSpellCast(JToken json, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
var spell = new WoWSpell
|
||||||
|
{
|
||||||
|
Name = json["n"].Value<string>(),
|
||||||
|
Id = json["sid"].Value<int>()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (json["uid"].Value<string>() == "player")
|
||||||
|
dataModel.Player.AddInstantCast(spell);
|
||||||
|
else if (json["uid"].Value<string>() == "target")
|
||||||
|
dataModel.Target.AddInstantCast(spell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseSpellCastFailed(string data, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
if (data == "\"player\"")
|
||||||
|
dataModel.Player.CastBar.Clear();
|
||||||
|
else if (data == "\"target\"")
|
||||||
|
dataModel.Target.CastBar.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseSpellCastInterrupted(string data, WoWDataModel dataModel)
|
||||||
|
{
|
||||||
|
if (data == "\"player\"")
|
||||||
|
dataModel.Player.CastBar.Clear();
|
||||||
|
else if (data == "\"target\"")
|
||||||
|
dataModel.Target.CastBar.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
127
Artemis/Artemis/Modules/Games/WoW/WoWPacketScanner.cs
Normal file
127
Artemis/Artemis/Modules/Games/WoW/WoWPacketScanner.cs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ninject.Extensions.Logging;
|
||||||
|
using PcapDotNet.Core;
|
||||||
|
using PcapDotNet.Packets;
|
||||||
|
|
||||||
|
namespace Artemis.Modules.Games.WoW
|
||||||
|
{
|
||||||
|
public class WowPacketScanner
|
||||||
|
{
|
||||||
|
private const string MsgStart = "\u0001";
|
||||||
|
private const string MsgNext = "\u0002";
|
||||||
|
private const string MsgLast = "\u0003";
|
||||||
|
private PacketCommunicator _communicator;
|
||||||
|
private string _dataParts;
|
||||||
|
|
||||||
|
public WowPacketScanner(ILogger logger)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; }
|
||||||
|
|
||||||
|
public event EventHandler<WowDataReceivedEventArgs> RaiseDataReceived;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
// Start scanning WoW packets
|
||||||
|
// Retrieve the device list from the local machine
|
||||||
|
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
|
||||||
|
|
||||||
|
if (allDevices.Count == 0)
|
||||||
|
{
|
||||||
|
Logger.Warn("No interfaces found! Can't scan WoW packets.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the selected adapter
|
||||||
|
PacketDevice selectedDevice = allDevices.First();
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
_communicator = selectedDevice.Open(65536, PacketDeviceOpenAttributes.Promiscuous, 40);
|
||||||
|
Logger.Debug("Listening on " + selectedDevice.Description + " for WoW packets");
|
||||||
|
|
||||||
|
// Compile the filter
|
||||||
|
using (var filter = _communicator.CreateFilter("tcp"))
|
||||||
|
{
|
||||||
|
// Set the filter
|
||||||
|
_communicator.SetFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(() => ReceivePackets());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_communicator?.Break();
|
||||||
|
_communicator?.Dispose();
|
||||||
|
_communicator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReceivePackets()
|
||||||
|
{
|
||||||
|
// start the capture
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_communicator.ReceivePackets(0, PacketHandler);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// ignored, happens on shutdown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback function invoked by Pcap.Net for every incoming packet
|
||||||
|
private void PacketHandler(Packet packet)
|
||||||
|
{
|
||||||
|
var str = Encoding.Default.GetString(packet.Buffer);
|
||||||
|
if (!str.ToLower().Contains("artemis"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Split the string at the prefix
|
||||||
|
var parts = str.Split(new[] {"(artemis)"}, StringSplitOptions.None);
|
||||||
|
if (parts.Length < 2)
|
||||||
|
return;
|
||||||
|
var msg = parts[1];
|
||||||
|
// Start escape char
|
||||||
|
if (msg.StartsWith(MsgStart))
|
||||||
|
_dataParts = msg.Substring(1);
|
||||||
|
else if (msg.StartsWith(MsgNext))
|
||||||
|
_dataParts = _dataParts + msg.Substring(1);
|
||||||
|
else if (msg.StartsWith(MsgLast))
|
||||||
|
{
|
||||||
|
_dataParts = _dataParts + msg.Substring(1);
|
||||||
|
var dataParts = _dataParts.Split('|');
|
||||||
|
// Data is wrapped in artemis(), take this off
|
||||||
|
OnRaiseDataReceived(dataParts[0].Substring(8), dataParts[1].Substring(0, dataParts[1].Length - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dataParts = msg.Split('|');
|
||||||
|
// Data is wrapped in artemis(), take this off
|
||||||
|
OnRaiseDataReceived(dataParts[0].Substring(8), dataParts[1].Substring(0, dataParts[1].Length - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRaiseDataReceived(string command, string data)
|
||||||
|
{
|
||||||
|
RaiseDataReceived?.Invoke(this, new WowDataReceivedEventArgs(command, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WowDataReceivedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public WowDataReceivedEventArgs(string command, string data)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Command { get; }
|
||||||
|
public string Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,5 +4,6 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
{
|
{
|
||||||
public class WoWSettings : ModuleSettings
|
public class WoWSettings : ModuleSettings
|
||||||
{
|
{
|
||||||
|
public string GameDirectory { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,19 +1,13 @@
|
|||||||
<UserControl x:Class="Artemis.Modules.Games.WoW.WoWView"
|
<UserControl x:Class="Artemis.Modules.Games.WoW.WoWView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:cal="http://www.caliburnproject.org" xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls" mc:Ignorable="d" d:DesignHeight="476.986" d:DesignWidth="538.772">
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
|
||||||
xmlns:cal="http://www.caliburnproject.org"
|
|
||||||
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="476.986" d:DesignWidth="538.772">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@ -23,7 +17,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" FontSize="20" HorizontalAlignment="Left">
|
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" FontSize="20" HorizontalAlignment="Left">
|
||||||
<Label.Content>
|
<Label.Content>
|
||||||
<AccessText TextWrapping="Wrap" Text="Fight the Legion in style with reactive lighting." />
|
<AccessText TextWrapping="Wrap" Text="There is currently no default profile available for World of Warcraft." />
|
||||||
</Label.Content>
|
</Label.Content>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
@ -39,11 +33,20 @@
|
|||||||
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" ToolTip="Note: You can't enable an module when Artemis is disabled" />
|
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" ToolTip="Note: You can't enable an module when Artemis is disabled" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Game directory -->
|
||||||
|
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,1,0">
|
||||||
|
<Label FontSize="20" HorizontalAlignment="Left" Content="World of Warcraft directory" />
|
||||||
|
<Grid>
|
||||||
|
<TextBox x:Name="GameDirectory" Height="23" TextWrapping="Wrap" Margin="0,0,30,0" Text="{Binding Path=Settings.GameDirectory, Mode=TwoWay}" cal:Message.Attach="[Event LostFocus] = [Action PlaceAddon]" />
|
||||||
|
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" HorizontalAlignment="Right" Width="25" Style="{DynamicResource SquareButtonStyle}" Height="26" />
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Profile editor -->
|
<!-- Profile editor -->
|
||||||
<ContentControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="ProfileEditor" />
|
<ContentControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" x:Name="ProfileEditor" />
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<StackPanel Grid.Column="0" Grid.Row="3" Orientation="Horizontal" VerticalAlignment="Bottom">
|
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Bottom">
|
||||||
<Button x:Name="ResetSettings" Content="Reset effect" VerticalAlignment="Top" Width="100" Style="{DynamicResource SquareButtonStyle}" />
|
<Button x:Name="ResetSettings" Content="Reset effect" VerticalAlignment="Top" Width="100" Style="{DynamicResource SquareButtonStyle}" />
|
||||||
<Button x:Name="SaveSettings" Content="Save changes" VerticalAlignment="Top" Width="100" Margin="10,0,0,0" Style="{DynamicResource SquareButtonStyle}" />
|
<Button x:Name="SaveSettings" Content="Save changes" VerticalAlignment="Top" Width="100" Margin="10,0,0,0" Style="{DynamicResource SquareButtonStyle}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Managers;
|
using System.Windows.Forms;
|
||||||
|
using Artemis.Managers;
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
|
||||||
@ -13,5 +14,23 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override bool UsesProfileEditor => true;
|
public override bool UsesProfileEditor => true;
|
||||||
|
|
||||||
|
public void PlaceAddon()
|
||||||
|
{
|
||||||
|
((WoWModel) ModuleModel).PlaceAddon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BrowseDirectory()
|
||||||
|
{
|
||||||
|
var dialog = new FolderBrowserDialog {SelectedPath = ((WoWSettings) Settings).GameDirectory};
|
||||||
|
var result = dialog.ShowDialog();
|
||||||
|
if (result != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
((WoWSettings) Settings).GameDirectory = dialog.SelectedPath;
|
||||||
|
((WoWModel) ModuleModel).PlaceAddon();
|
||||||
|
Settings.Save();
|
||||||
|
NotifyOfPropertyChange(() => Settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,6 +12,7 @@ using Artemis.Managers;
|
|||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Artemis.Utilities.ActiveWindowDetection;
|
using Artemis.Utilities.ActiveWindowDetection;
|
||||||
|
using Betwixt;
|
||||||
using CSCore.CoreAudioAPI;
|
using CSCore.CoreAudioAPI;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SpotifyAPI.Local;
|
using SpotifyAPI.Local;
|
||||||
|
|||||||
@ -164,6 +164,7 @@
|
|||||||
<xs:element name="whenEqual" type="whenEqual" />
|
<xs:element name="whenEqual" type="whenEqual" />
|
||||||
<xs:element name="whenNotContains" type="whenNotContains" />
|
<xs:element name="whenNotContains" type="whenNotContains" />
|
||||||
<xs:element name="whenNotEqual" type="whenNotEqual" />
|
<xs:element name="whenNotEqual" type="whenNotEqual" />
|
||||||
|
<xs:element name="whenRepeated" type="whenRepeated" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:simpleType name="NLogLevel">
|
<xs:simpleType name="NLogLevel">
|
||||||
@ -326,6 +327,7 @@
|
|||||||
<xs:extension base="WrapperTargetBase">
|
<xs:extension base="WrapperTargetBase">
|
||||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="asyncFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
|
<xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
|
||||||
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
@ -334,6 +336,11 @@
|
|||||||
<xs:documentation>Name of the target.</xs:documentation>
|
<xs:documentation>Name of the target.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="asyncFlush" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Delay the flush until the LogEvent has been confirmed as written</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="condition" type="Condition">
|
<xs:attribute name="condition" type="Condition">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Condition expression. Log events who meet this condition will cause a flush on the wrapped target.</xs:documentation>
|
<xs:documentation>Condition expression. Log events who meet this condition will cause a flush on the wrapped target.</xs:documentation>
|
||||||
@ -403,13 +410,14 @@
|
|||||||
<xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
|
<xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
|
||||||
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
<xs:attribute name="name" type="xs:string">
|
<xs:attribute name="name" type="xs:string">
|
||||||
@ -477,9 +485,9 @@
|
|||||||
<xs:documentation>Maximum queue size.</xs:documentation>
|
<xs:documentation>Maximum queue size.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeMdc" type="xs:boolean">
|
<xs:attribute name="includeNdc" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
<xs:documentation>Indicates whether to include stack contents.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeSourceInfo" type="xs:boolean">
|
<xs:attribute name="includeSourceInfo" type="xs:boolean">
|
||||||
@ -492,9 +500,9 @@
|
|||||||
<xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
|
<xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeNdc" type="xs:boolean">
|
<xs:attribute name="includeMdc" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether to include stack contents.</xs:documentation>
|
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeCallSite" type="xs:boolean">
|
<xs:attribute name="includeCallSite" type="xs:boolean">
|
||||||
@ -512,6 +520,11 @@
|
|||||||
<xs:documentation>NDC item separator.</xs:documentation>
|
<xs:documentation>NDC item separator.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdlc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="optimizeBufferReuse" type="xs:boolean">
|
<xs:attribute name="optimizeBufferReuse" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
|
<xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
|
||||||
@ -1001,6 +1014,7 @@
|
|||||||
<xs:element name="source" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="source" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.EventLogTargetOverflowAction" />
|
<xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.EventLogTargetOverflowAction" />
|
||||||
<xs:element name="entryType" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="entryType" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
|
<xs:element name="maxKilobytes" minOccurs="0" maxOccurs="1" type="xs:long" />
|
||||||
<xs:element name="maxMessageLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="maxMessageLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
@ -1049,6 +1063,11 @@
|
|||||||
<xs:documentation>Optional entrytype. When not set, or when not convertable to then determined by </xs:documentation>
|
<xs:documentation>Optional entrytype. When not set, or when not convertable to then determined by </xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="maxKilobytes" type="xs:long">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Maximum Event log size in kilobytes. If null, the value won't be set. Default is 512 Kilobytes as specified by Eventlog API</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="maxMessageLength" type="xs:integer">
|
<xs:attribute name="maxMessageLength" type="xs:integer">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Message length limit to write to the Event Log.</xs:documentation>
|
<xs:documentation>Message length limit to write to the Event Log.</xs:documentation>
|
||||||
@ -1117,18 +1136,19 @@
|
|||||||
<xs:element name="discardAll" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="discardAll" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="fileNameKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
|
<xs:element name="fileNameKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
|
||||||
<xs:element name="forceMutexConcurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="forceMutexConcurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="maxLogFilenames" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="writeFooterOnArchivingOnly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="writeFooterOnArchivingOnly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="fileName" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="fileName" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="archiveDateFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="archiveDateFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="archiveOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="archiveOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="createDirs" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="createDirs" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="enableFileDelete" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
|
||||||
<xs:element name="fileAttributes" minOccurs="0" maxOccurs="1" type="NLog.Targets.Win32FileAttributes" />
|
<xs:element name="fileAttributes" minOccurs="0" maxOccurs="1" type="NLog.Targets.Win32FileAttributes" />
|
||||||
<xs:element name="deleteOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="deleteOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="replaceFileContentsOnEachWrite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="replaceFileContentsOnEachWrite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="enableFileDelete" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="concurrentWriteAttempts" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="concurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="concurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="keepFileOpen" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="keepFileOpen" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="maxLogFilenames" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
|
||||||
<xs:element name="networkWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="networkWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="openFileCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="openFileCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="openFileCacheTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="openFileCacheTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
@ -1136,7 +1156,6 @@
|
|||||||
<xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="concurrentWriteAttemptDelay" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="concurrentWriteAttemptDelay" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="concurrentWriteAttempts" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
<xs:attribute name="name" type="xs:string">
|
<xs:attribute name="name" type="xs:string">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
@ -1228,6 +1247,11 @@
|
|||||||
<xs:documentation>Value indicationg whether file creation calls should be synchronized by a system global mutex.</xs:documentation>
|
<xs:documentation>Value indicationg whether file creation calls should be synchronized by a system global mutex.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="maxLogFilenames" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Maximum number of log filenames that should be stored as existing.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="writeFooterOnArchivingOnly" type="xs:boolean">
|
<xs:attribute name="writeFooterOnArchivingOnly" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether the footer should be written only when the file is archived.</xs:documentation>
|
<xs:documentation>Indicates whether the footer should be written only when the file is archived.</xs:documentation>
|
||||||
@ -1253,11 +1277,6 @@
|
|||||||
<xs:documentation>Indicates whether to create directories if they do not exist.</xs:documentation>
|
<xs:documentation>Indicates whether to create directories if they do not exist.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="enableFileDelete" type="xs:boolean">
|
|
||||||
<xs:annotation>
|
|
||||||
<xs:documentation>Indicates whether to enable log file(s) to be deleted.</xs:documentation>
|
|
||||||
</xs:annotation>
|
|
||||||
</xs:attribute>
|
|
||||||
<xs:attribute name="fileAttributes" type="NLog.Targets.Win32FileAttributes">
|
<xs:attribute name="fileAttributes" type="NLog.Targets.Win32FileAttributes">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>File attributes (Windows only).</xs:documentation>
|
<xs:documentation>File attributes (Windows only).</xs:documentation>
|
||||||
@ -1273,6 +1292,16 @@
|
|||||||
<xs:documentation>Indicates whether to replace file contents on each write instead of appending log message at the end.</xs:documentation>
|
<xs:documentation>Indicates whether to replace file contents on each write instead of appending log message at the end.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="enableFileDelete" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to enable log file(s) to be deleted.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="concurrentWriteAttempts" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Number of times the write is appended on the file before NLog discards the log message.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="concurrentWrites" type="xs:boolean">
|
<xs:attribute name="concurrentWrites" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on the same host.</xs:documentation>
|
<xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on the same host.</xs:documentation>
|
||||||
@ -1283,11 +1312,6 @@
|
|||||||
<xs:documentation>Indicates whether to keep log file open instead of opening and closing it on each logging event.</xs:documentation>
|
<xs:documentation>Indicates whether to keep log file open instead of opening and closing it on each logging event.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="maxLogFilenames" type="xs:integer">
|
|
||||||
<xs:annotation>
|
|
||||||
<xs:documentation>Maximum number of log filenames that should be stored as existing.</xs:documentation>
|
|
||||||
</xs:annotation>
|
|
||||||
</xs:attribute>
|
|
||||||
<xs:attribute name="networkWrites" type="xs:boolean">
|
<xs:attribute name="networkWrites" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on different network hosts.</xs:documentation>
|
<xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on different network hosts.</xs:documentation>
|
||||||
@ -1323,11 +1347,6 @@
|
|||||||
<xs:documentation>Delay in milliseconds to wait before attempting to write to the file again.</xs:documentation>
|
<xs:documentation>Delay in milliseconds to wait before attempting to write to the file again.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="concurrentWriteAttempts" type="xs:integer">
|
|
||||||
<xs:annotation>
|
|
||||||
<xs:documentation>Number of times the write is appended on the file before NLog discards the log message.</xs:documentation>
|
|
||||||
</xs:annotation>
|
|
||||||
</xs:attribute>
|
|
||||||
</xs:extension>
|
</xs:extension>
|
||||||
</xs:complexContent>
|
</xs:complexContent>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
@ -1587,6 +1606,7 @@
|
|||||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="parameterType" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="type" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="type" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
<xs:attribute name="layout" type="SimpleLayoutAttribute">
|
<xs:attribute name="layout" type="SimpleLayoutAttribute">
|
||||||
@ -1599,11 +1619,16 @@
|
|||||||
<xs:documentation>Name of the parameter.</xs:documentation>
|
<xs:documentation>Name of the parameter.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="type" type="xs:string">
|
<xs:attribute name="parameterType" type="xs:string">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Type of the parameter.</xs:documentation>
|
<xs:documentation>Type of the parameter.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="type" type="xs:string">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Type of the parameter. Obsolete alias for </xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:complexType name="Mail">
|
<xs:complexType name="Mail">
|
||||||
<xs:complexContent>
|
<xs:complexContent>
|
||||||
@ -2020,13 +2045,14 @@
|
|||||||
<xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
<xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
<xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
|
<xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
|
||||||
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
<xs:attribute name="name" type="xs:string">
|
<xs:attribute name="name" type="xs:string">
|
||||||
@ -2094,9 +2120,9 @@
|
|||||||
<xs:documentation>Maximum queue size.</xs:documentation>
|
<xs:documentation>Maximum queue size.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeMdc" type="xs:boolean">
|
<xs:attribute name="includeNdc" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
<xs:documentation>Indicates whether to include stack contents.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeSourceInfo" type="xs:boolean">
|
<xs:attribute name="includeSourceInfo" type="xs:boolean">
|
||||||
@ -2109,9 +2135,9 @@
|
|||||||
<xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
|
<xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeNdc" type="xs:boolean">
|
<xs:attribute name="includeMdc" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Indicates whether to include stack contents.</xs:documentation>
|
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="includeCallSite" type="xs:boolean">
|
<xs:attribute name="includeCallSite" type="xs:boolean">
|
||||||
@ -2129,6 +2155,11 @@
|
|||||||
<xs:documentation>NDC item separator.</xs:documentation>
|
<xs:documentation>NDC item separator.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdlc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include dictionary contents.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="optimizeBufferReuse" type="xs:boolean">
|
<xs:attribute name="optimizeBufferReuse" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
|
<xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
|
||||||
@ -2487,8 +2518,10 @@
|
|||||||
<xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="escapeDataNLogLegacy" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="escapeDataNLogLegacy" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="escapeDataRfc3986" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="escapeDataRfc3986" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="header" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
|
||||||
<xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="namespace" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="namespace" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="preAuthenticate" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="protocol" minOccurs="0" maxOccurs="1" type="NLog.Targets.WebServiceProtocol" />
|
<xs:element name="protocol" minOccurs="0" maxOccurs="1" type="NLog.Targets.WebServiceProtocol" />
|
||||||
<xs:element name="url" minOccurs="0" maxOccurs="1" type="xs:anyURI" />
|
<xs:element name="url" minOccurs="0" maxOccurs="1" type="xs:anyURI" />
|
||||||
<xs:element name="xmlRoot" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="xmlRoot" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
@ -2534,6 +2567,11 @@
|
|||||||
<xs:documentation>Web service namespace. Only used with Soap.</xs:documentation>
|
<xs:documentation>Web service namespace. Only used with Soap.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="preAuthenticate" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters)</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="protocol" type="NLog.Targets.WebServiceProtocol">
|
<xs:attribute name="protocol" type="NLog.Targets.WebServiceProtocol">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Protocol to be used when calling web service.</xs:documentation>
|
<xs:documentation>Protocol to be used when calling web service.</xs:documentation>
|
||||||
@ -2677,8 +2715,10 @@
|
|||||||
<xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.JsonAttribute" />
|
<xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.JsonAttribute" />
|
||||||
<xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
<xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="renderEmptyObject" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="renderEmptyObject" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="suppressSpaces" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="suppressSpaces" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
<xs:attribute name="excludeProperties" type="xs:string">
|
<xs:attribute name="excludeProperties" type="xs:string">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
@ -2690,6 +2730,11 @@
|
|||||||
<xs:documentation>Option to include all properties from the log events</xs:documentation>
|
<xs:documentation>Option to include all properties from the log events</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include contents of the dictionary.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="renderEmptyObject" type="xs:boolean">
|
<xs:attribute name="renderEmptyObject" type="xs:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Option to render the empty object value {}</xs:documentation>
|
<xs:documentation>Option to render the empty object value {}</xs:documentation>
|
||||||
@ -2700,12 +2745,18 @@
|
|||||||
<xs:documentation>Option to suppress the extra spaces in the output json</xs:documentation>
|
<xs:documentation>Option to suppress the extra spaces in the output json</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdlc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include contents of the dictionary.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:extension>
|
</xs:extension>
|
||||||
</xs:complexContent>
|
</xs:complexContent>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:complexType name="NLog.Layouts.JsonAttribute">
|
<xs:complexType name="NLog.Layouts.JsonAttribute">
|
||||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
<xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="escapeUnicode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
<xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
|
<xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
<xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
@ -2714,6 +2765,11 @@
|
|||||||
<xs:documentation>Determines wether or not this attribute will be Json encoded.</xs:documentation>
|
<xs:documentation>Determines wether or not this attribute will be Json encoded.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="escapeUnicode" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to escape non-ascii characters</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="layout" type="SimpleLayoutAttribute">
|
<xs:attribute name="layout" type="SimpleLayoutAttribute">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Layout that will be rendered as the attribute's value.</xs:documentation>
|
<xs:documentation>Layout that will be rendered as the attribute's value.</xs:documentation>
|
||||||
@ -2754,7 +2810,26 @@
|
|||||||
<xs:complexType name="Log4JXmlEventLayout">
|
<xs:complexType name="Log4JXmlEventLayout">
|
||||||
<xs:complexContent>
|
<xs:complexContent>
|
||||||
<xs:extension base="Layout">
|
<xs:extension base="Layout">
|
||||||
<xs:choice minOccurs="0" maxOccurs="unbounded" />
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
</xs:choice>
|
||||||
|
<xs:attribute name="includeAllProperties" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Option to include all properties from the log events</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include contents of the dictionary.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="includeMdlc" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Indicates whether to include contents of the dictionary.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:extension>
|
</xs:extension>
|
||||||
</xs:complexContent>
|
</xs:complexContent>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
@ -2929,6 +3004,74 @@
|
|||||||
</xs:extension>
|
</xs:extension>
|
||||||
</xs:complexContent>
|
</xs:complexContent>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
<xs:complexType name="whenRepeated">
|
||||||
|
<xs:complexContent>
|
||||||
|
<xs:extension base="Filter">
|
||||||
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
|
||||||
|
<xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
|
||||||
|
<xs:element name="defaultFilterCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
|
<xs:element name="filterCountMessageAppendFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="filterCountPropertyName" minOccurs="0" maxOccurs="1" type="xs:string" />
|
||||||
|
<xs:element name="maxFilterCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
|
<xs:element name="maxLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
|
<xs:element name="optimizeBufferDefaultLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
|
<xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
|
||||||
|
<xs:element name="timeoutSeconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
|
||||||
|
</xs:choice>
|
||||||
|
<xs:attribute name="action" type="FilterResult">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Action to be taken when filter matches.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="layout" type="SimpleLayoutAttribute">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Layout to be used to filter log messages.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="defaultFilterCacheSize" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Default number of unique filter values to expect, will automatically increase if needed</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="filterCountMessageAppendFormat" type="xs:string">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Append FilterCount to the when an event is no longer filtered</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="filterCountPropertyName" type="xs:string">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Insert FilterCount value into when an event is no longer filtered</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="maxFilterCacheSize" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Max number of unique filter values to expect simultaneously</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="maxLength" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Max length of filter values, will truncate if above limit</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="optimizeBufferDefaultLength" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Default buffer size for the internal buffers</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="optimizeBufferReuse" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Reuse internal buffers, and doesn't have to constantly allocate new buffers</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="timeoutSeconds" type="xs:integer">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>How long before a filter expires, and logging is accepted again</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
</xs:extension>
|
||||||
|
</xs:complexContent>
|
||||||
|
</xs:complexType>
|
||||||
<xs:complexType name="AccurateLocal">
|
<xs:complexType name="AccurateLocal">
|
||||||
<xs:complexContent>
|
<xs:complexContent>
|
||||||
<xs:extension base="TimeSource">
|
<xs:extension base="TimeSource">
|
||||||
|
|||||||
@ -12,6 +12,8 @@ namespace Artemis.Profiles.Layers.Conditions
|
|||||||
lock (layerModel.Properties.Conditions)
|
lock (layerModel.Properties.Conditions)
|
||||||
{
|
{
|
||||||
var checkConditions = layerModel.Properties.Conditions.Where(c => c.Field != null).ToList();
|
var checkConditions = layerModel.Properties.Conditions.Where(c => c.Field != null).ToList();
|
||||||
|
if (!checkConditions.Any())
|
||||||
|
return true;
|
||||||
switch (layerModel.Properties.ConditionType)
|
switch (layerModel.Properties.ConditionType)
|
||||||
{
|
{
|
||||||
case ConditionType.AnyMet:
|
case ConditionType.AnyMet:
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Windows.Documents;
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using DynamicExpresso;
|
using DynamicExpresso;
|
||||||
@ -11,10 +16,12 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
{
|
{
|
||||||
private readonly Interpreter _interpreter;
|
private readonly Interpreter _interpreter;
|
||||||
private object _lastValue;
|
private object _lastValue;
|
||||||
|
private Regex _rgx;
|
||||||
|
|
||||||
public LayerConditionModel()
|
public LayerConditionModel()
|
||||||
{
|
{
|
||||||
_interpreter = new Interpreter();
|
_interpreter = new Interpreter();
|
||||||
|
_rgx = new Regex("\\((.*?)\\)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Field { get; set; }
|
public string Field { get; set; }
|
||||||
@ -30,6 +37,52 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Type))
|
if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// If the path points to a collection, look inside this collection
|
||||||
|
if (Field.Contains("("))
|
||||||
|
{
|
||||||
|
// Find the collection in the field path
|
||||||
|
var collectionField = _rgx.Match(Field).Groups[1].Value;
|
||||||
|
var collectionInspect = (IEnumerable) GeneralHelpers.GetPropertyValue(subject, collectionField);
|
||||||
|
var operatorParts = Operator.Split('|');
|
||||||
|
var field = Field.Split(')').Last().Substring(1);
|
||||||
|
|
||||||
|
_lastValue = collectionInspect;
|
||||||
|
|
||||||
|
if (operatorParts[0] == "any")
|
||||||
|
{
|
||||||
|
var anyMatch = false;
|
||||||
|
foreach (var collectionValue in collectionInspect)
|
||||||
|
{
|
||||||
|
anyMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
|
if (anyMatch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return anyMatch;
|
||||||
|
}
|
||||||
|
if (operatorParts[0] == "all")
|
||||||
|
{
|
||||||
|
var allMatch = true;
|
||||||
|
foreach (var collectionValue in collectionInspect)
|
||||||
|
{
|
||||||
|
allMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
|
if (!allMatch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return allMatch;
|
||||||
|
}
|
||||||
|
if (operatorParts[0] == "none")
|
||||||
|
{
|
||||||
|
var noneMatch = true;
|
||||||
|
foreach (var collectionValue in collectionInspect)
|
||||||
|
{
|
||||||
|
noneMatch = !EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
|
if (!noneMatch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return noneMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var inspect = GeneralHelpers.GetPropertyValue(subject, Field);
|
var inspect = GeneralHelpers.GetPropertyValue(subject, Field);
|
||||||
if (inspect == null)
|
if (inspect == null)
|
||||||
{
|
{
|
||||||
@ -41,7 +94,7 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
if (Operator == "changed" || Operator == "decreased" || Operator == "increased")
|
if (Operator == "changed" || Operator == "decreased" || Operator == "increased")
|
||||||
returnValue = EvaluateEventOperator(subject, inspect);
|
returnValue = EvaluateEventOperator(subject, inspect);
|
||||||
else
|
else
|
||||||
returnValue = EvaluateOperator(subject);
|
returnValue = EvaluateOperator(subject, Field);
|
||||||
|
|
||||||
_lastValue = inspect;
|
_lastValue = inspect;
|
||||||
return returnValue;
|
return returnValue;
|
||||||
@ -68,8 +121,7 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
changeOperator = ">";
|
changeOperator = ">";
|
||||||
|
|
||||||
// Evaluate the result and store it
|
// Evaluate the result and store it
|
||||||
var returnValue = _interpreter.Eval<bool>($"subject.{Field} {changeOperator} value",
|
var returnValue = _interpreter.Eval<bool>($"subject.{Field} {changeOperator} value", new Parameter("subject", subject.GetType(), subject), rightParam);
|
||||||
new Parameter("subject", subject.GetType(), subject), rightParam);
|
|
||||||
|
|
||||||
// Set the last value to the new value
|
// Set the last value to the new value
|
||||||
_lastValue = inspect;
|
_lastValue = inspect;
|
||||||
@ -77,25 +129,26 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool EvaluateOperator(ModuleDataModel subject)
|
private bool EvaluateOperator(object subject, string field, string operatorOverwrite = null)
|
||||||
{
|
{
|
||||||
// Since _lastValue won't be used, rely on Value to not be null
|
// Since _lastValue won't be used, rely on Value to not be null
|
||||||
if (string.IsNullOrEmpty(Value))
|
if (string.IsNullOrEmpty(Value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Put the subject in a list, allowing Dynamic Linq to be used.
|
|
||||||
if (Type == "String")
|
if (Type == "String")
|
||||||
{
|
{
|
||||||
return _interpreter.Eval<bool>($"subject.{Field}.ToLower(){Operator}(value)",
|
var stringExpressionText = operatorOverwrite == null
|
||||||
new Parameter("subject", subject.GetType(), subject),
|
? $"subject.{field}.ToLower(){Operator}(value)"
|
||||||
new Parameter("value", Value.ToLower()));
|
: $"subject.{field}.ToLower(){operatorOverwrite}(value)";
|
||||||
|
|
||||||
|
return _interpreter.Eval<bool>(stringExpressionText, new Parameter("subject", subject.GetType(), subject), new Parameter("value", Value.ToLower()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Parameter rightParam = null;
|
Parameter rightParam = null;
|
||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
case "Enum":
|
case "Enum":
|
||||||
var enumType = GeneralHelpers.GetPropertyValue(subject, Field).GetType();
|
var enumType = GeneralHelpers.GetPropertyValue(subject, field).GetType();
|
||||||
rightParam = new Parameter("value", Enum.Parse(enumType, Value));
|
rightParam = new Parameter("value", Enum.Parse(enumType, Value));
|
||||||
break;
|
break;
|
||||||
case "Boolean":
|
case "Boolean":
|
||||||
@ -111,8 +164,11 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _interpreter.Eval<bool>($"subject.{Field} {Operator} value",
|
var expressionText = operatorOverwrite == null
|
||||||
new Parameter("subject", subject.GetType(), subject), rightParam);
|
? $"subject.{field} {Operator} value"
|
||||||
|
: $"subject.{field} {operatorOverwrite} value";
|
||||||
|
|
||||||
|
return _interpreter.Eval<bool>(expressionText, new Parameter("subject", subject.GetType(), subject), rightParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,6 +11,7 @@ using Artemis.Profiles.Layers.Interfaces;
|
|||||||
using Artemis.Profiles.Layers.Types.Keyboard;
|
using Artemis.Profiles.Layers.Types.Keyboard;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Artemis.Utilities.ParentChild;
|
using Artemis.Utilities.ParentChild;
|
||||||
|
using Betwixt;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Profiles.Layers.Models
|
namespace Artemis.Profiles.Layers.Models
|
||||||
@ -26,8 +27,14 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
var model = Properties as KeyboardPropertiesModel;
|
var model = Properties as KeyboardPropertiesModel;
|
||||||
if (model != null)
|
if (model != null)
|
||||||
GifImage = new GifImage(model.GifFile, Properties.AnimationSpeed);
|
GifImage = new GifImage(model.GifFile, Properties.AnimationSpeed);
|
||||||
|
|
||||||
|
LayerConditionsMet += OnLayerConditionsMet;
|
||||||
|
LayerConditionsUnmet += OnLayerConditionsUnmet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event EventHandler<EventArgs> LayerConditionsMet;
|
||||||
|
public event EventHandler<EventArgs> LayerConditionsUnmet;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public ImageSource LayerImage => LayerType.DrawThumbnail(this);
|
public ImageSource LayerImage => LayerType.DrawThumbnail(this);
|
||||||
|
|
||||||
@ -43,7 +50,18 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
public bool AreConditionsMet(ModuleDataModel dataModel)
|
public bool AreConditionsMet(ModuleDataModel dataModel)
|
||||||
{
|
{
|
||||||
// Conditions are not even checked if the layer isn't enabled
|
// Conditions are not even checked if the layer isn't enabled
|
||||||
return Enabled && LayerCondition.ConditionsMet(this, dataModel);
|
if (!Enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FadeTweener?.Update(40);
|
||||||
|
var conditionsMet = LayerCondition.ConditionsMet(this, dataModel);
|
||||||
|
if (conditionsMet && !_conditionsMetLastFrame)
|
||||||
|
OnLayerConditionsMet();
|
||||||
|
if (!conditionsMet && _conditionsMetLastFrame)
|
||||||
|
OnLayerConditionsUnmet();
|
||||||
|
|
||||||
|
_conditionsMetLastFrame = conditionsMet;
|
||||||
|
return FadeTweener != null && FadeTweener.Running || conditionsMet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -96,7 +114,37 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
if (Brush == null || !preview && !RenderAllowed)
|
if (Brush == null || !preview && !RenderAllowed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ApplyHierarchyOpacity(c);
|
||||||
LayerType.Draw(this, c);
|
LayerType.Draw(this, c);
|
||||||
|
PopHierarchyOpacity(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyHierarchyOpacity(DrawingContext c)
|
||||||
|
{
|
||||||
|
if (FadeTweener != null && FadeTweener.Running)
|
||||||
|
c.PushOpacity(FadeTweener.Value);
|
||||||
|
|
||||||
|
var current = this;
|
||||||
|
while (current.Parent != null)
|
||||||
|
{
|
||||||
|
current = current.Parent;
|
||||||
|
if (current.FadeTweener != null && current.FadeTweener.Running)
|
||||||
|
c.PushOpacity(current.FadeTweener.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopHierarchyOpacity(DrawingContext c)
|
||||||
|
{
|
||||||
|
if (FadeTweener != null && FadeTweener.Running)
|
||||||
|
c.Pop();
|
||||||
|
|
||||||
|
var current = this;
|
||||||
|
while (current.Parent != null)
|
||||||
|
{
|
||||||
|
current = current.Parent;
|
||||||
|
if (current.FadeTweener != null && current.FadeTweener.Running)
|
||||||
|
c.Pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -303,6 +351,29 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
keybindModel.Unregister();
|
keybindModel.Unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnLayerConditionsMet(object sender, EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (FadeInTime <= 0)
|
||||||
|
return;
|
||||||
|
if (FadeTweener != null && FadeTweener.Running)
|
||||||
|
FadeTweener = new Tweener<float>(FadeTweener.Value, 1, FadeInTime, Ease.Quint.Out, TweenModel.LerpFuncFloat);
|
||||||
|
else
|
||||||
|
FadeTweener = new Tweener<float>(0, 1, FadeInTime, Ease.Quint.Out, TweenModel.LerpFuncFloat);
|
||||||
|
FadeTweener.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLayerConditionsUnmet(object sender, EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (FadeOutTime <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (FadeTweener != null && FadeTweener.Running)
|
||||||
|
FadeTweener = new Tweener<float>(FadeTweener.Value, 0, FadeOutTime, Ease.Quint.In, TweenModel.LerpFuncFloat);
|
||||||
|
else
|
||||||
|
FadeTweener = new Tweener<float>(1, 0, FadeOutTime, Ease.Quint.In, TweenModel.LerpFuncFloat);
|
||||||
|
FadeTweener.Start();
|
||||||
|
}
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
#region Layer type properties
|
#region Layer type properties
|
||||||
@ -321,6 +392,8 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
public bool RenderAllowed { get; set; }
|
public bool RenderAllowed { get; set; }
|
||||||
public bool Expanded { get; set; }
|
public bool Expanded { get; set; }
|
||||||
public bool IsEvent { get; set; }
|
public bool IsEvent { get; set; }
|
||||||
|
public double FadeInTime { get; set; }
|
||||||
|
public double FadeOutTime { get; set; }
|
||||||
public LayerPropertiesModel Properties { get; set; }
|
public LayerPropertiesModel Properties { get; set; }
|
||||||
public EventPropertiesModel EventProperties { get; set; }
|
public EventPropertiesModel EventProperties { get; set; }
|
||||||
|
|
||||||
@ -341,6 +414,10 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
#region Render properties
|
#region Render properties
|
||||||
|
|
||||||
[JsonIgnore] private Brush _brush;
|
[JsonIgnore] private Brush _brush;
|
||||||
|
[JsonIgnore] private bool _conditionsMetLastFrame;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Tweener<float> FadeTweener { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double X { get; set; }
|
public double X { get; set; }
|
||||||
@ -416,5 +493,15 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
{
|
{
|
||||||
return $"{nameof(Name)}: {Name}, {nameof(Order)}: {Order}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Width)}: {Width}, {nameof(Height)}: {Height}";
|
return $"{nameof(Name)}: {Name}, {nameof(Order)}: {Order}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Width)}: {Width}, {nameof(Height)}: {Height}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnLayerConditionsMet()
|
||||||
|
{
|
||||||
|
LayerConditionsMet?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnLayerConditionsUnmet()
|
||||||
|
{
|
||||||
|
LayerConditionsUnmet?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,6 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
public double Width { get; set; }
|
public double Width { get; set; }
|
||||||
public double Height { get; set; }
|
public double Height { get; set; }
|
||||||
public bool Contain { get; set; }
|
public bool Contain { get; set; }
|
||||||
public double Opacity { get; set; }
|
|
||||||
public double AnimationSpeed { get; set; }
|
public double AnimationSpeed { get; set; }
|
||||||
public double OpacityEaseTime { get; set; }
|
public double OpacityEaseTime { get; set; }
|
||||||
public double HeightEaseTime { get; set; }
|
public double HeightEaseTime { get; set; }
|
||||||
@ -55,6 +54,10 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
public List<LayerKeybindModel> LayerKeybindModels { get; set; } = new List<LayerKeybindModel>();
|
public List<LayerKeybindModel> LayerKeybindModels { get; set; } = new List<LayerKeybindModel>();
|
||||||
public List<DynamicPropertiesModel> DynamicProperties { get; set; } = new List<DynamicPropertiesModel>();
|
public List<DynamicPropertiesModel> DynamicProperties { get; set; } = new List<DynamicPropertiesModel>();
|
||||||
|
|
||||||
|
// Opacity isn't saved since it's only accesable by LUA
|
||||||
|
[JsonIgnore]
|
||||||
|
public double Opacity { get; set; } = 1;
|
||||||
|
|
||||||
[JsonConverter(typeof(BrushJsonConverter))]
|
[JsonConverter(typeof(BrushJsonConverter))]
|
||||||
public Brush Brush
|
public Brush Brush
|
||||||
{
|
{
|
||||||
|
|||||||
@ -136,7 +136,7 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float LerpFuncFloat(float start, float end, float percent)
|
public static float LerpFuncFloat(float start, float end, float percent)
|
||||||
{
|
{
|
||||||
return start + (end - start) * percent;
|
return start + (end - start) * percent;
|
||||||
}
|
}
|
||||||
|
|||||||
57
Artemis/Artemis/Profiles/Lua/Modules/Gui/LuaSlider.cs
Normal file
57
Artemis/Artemis/Profiles/Lua/Modules/Gui/LuaSlider.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using Artemis.Managers;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using MoonSharp.Interpreter.Interop;
|
||||||
|
|
||||||
|
namespace Artemis.Profiles.Lua.Modules.Gui
|
||||||
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
|
public class LuaSlider
|
||||||
|
{
|
||||||
|
private readonly LuaManager _luaManager;
|
||||||
|
|
||||||
|
public LuaSlider(LuaManager luaManager, int interval, double intialValue, double minimum, double maximum, double x, double y, double? width, double? height)
|
||||||
|
{
|
||||||
|
_luaManager = luaManager;
|
||||||
|
|
||||||
|
Slider = new Slider { Value = intialValue, TickFrequency = interval, Minimum = minimum, Maximum = maximum, TickPlacement = TickPlacement.BottomRight, IsSnapToTickEnabled = true};
|
||||||
|
|
||||||
|
if (width != null)
|
||||||
|
Slider.Width = width.Value;
|
||||||
|
if (height != null)
|
||||||
|
Slider.Height = height.Value;
|
||||||
|
|
||||||
|
Slider.ValueChanged += SliderOnValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MoonSharpVisible(false)]
|
||||||
|
public Slider Slider { get; }
|
||||||
|
|
||||||
|
public double Value
|
||||||
|
{
|
||||||
|
get => Slider.Dispatcher.Invoke(() => (double) Slider.Value);
|
||||||
|
set => Slider.Dispatcher.Invoke(() => Slider.Value = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Interval
|
||||||
|
{
|
||||||
|
get => Slider.Dispatcher.Invoke(() => (int) Slider.Interval);
|
||||||
|
set => Slider.Dispatcher.Invoke(() => Slider.Interval = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SliderOnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
|
||||||
|
{
|
||||||
|
_luaManager.EventsModule.LuaInvoke(_luaManager.ProfileModel, () => OnValueChanged(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<EventArgs> ValueChanged;
|
||||||
|
|
||||||
|
protected virtual void OnValueChanged(LuaSlider slider)
|
||||||
|
{
|
||||||
|
ValueChanged?.Invoke(slider, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -83,6 +83,18 @@ namespace Artemis.Profiles.Lua.Modules.Gui
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LuaSlider CreateSlider(int interval, double initialValue, double minimum, double maximum, double x, double y, double? width = 200.0, double? height = 20.0)
|
||||||
|
{
|
||||||
|
LuaSlider element = null;
|
||||||
|
Execute.OnUIThread(() =>
|
||||||
|
{
|
||||||
|
element = new LuaSlider(_luaManager, interval, initialValue, minimum, maximum, x, y, width, height);
|
||||||
|
AddControl(element.Slider, x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
private void AddControl(UIElement uiElement, double x, double y)
|
private void AddControl(UIElement uiElement, double x, double y)
|
||||||
{
|
{
|
||||||
Canvas.Children.Add(uiElement);
|
Canvas.Children.Add(uiElement);
|
||||||
|
|||||||
@ -53,7 +53,7 @@ using System.Windows;
|
|||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.10.0.0")]
|
[assembly: AssemblyVersion("1.11.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.10.0.0")]
|
[assembly: AssemblyFileVersion("1.11.0.0")]
|
||||||
|
[assembly: AssemblyMetadata("SquirrelAwareVersion", "1")]
|
||||||
[assembly: InternalsVisibleTo("Artemis.Explorables")]
|
[assembly: InternalsVisibleTo("Artemis.Explorables")]
|
||||||
|
|
||||||
|
|||||||
36
Artemis/Artemis/Properties/Resources.Designer.cs
generated
36
Artemis/Artemis/Properties/Resources.Designer.cs
generated
@ -19,7 +19,7 @@ namespace Artemis.Properties {
|
|||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources {
|
internal class Resources {
|
||||||
@ -70,6 +70,29 @@ namespace Artemis.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-16"?>
|
||||||
|
///<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||||
|
/// <RegistrationInfo>
|
||||||
|
/// <Date>2017-11-12T17:45:45.4993602</Date>
|
||||||
|
/// <Author>SpoinkyNL</Author>
|
||||||
|
/// <Description>Task to run Artemis on PC startup without showing a UAC dialog</Description>
|
||||||
|
/// <URI>\Artemis autorun</URI>
|
||||||
|
/// </RegistrationInfo>
|
||||||
|
/// <Triggers>
|
||||||
|
/// <LogonTrigger>
|
||||||
|
/// <Enabled>true</Enabled>
|
||||||
|
/// <Delay>PT30S</Delay>
|
||||||
|
/// </LogonTrigger>
|
||||||
|
/// </Triggers>
|
||||||
|
/// <Set [rest of string was truncated]";.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Artemis_autorun {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Artemis_autorun", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -105,6 +128,7 @@ namespace Artemis.Properties {
|
|||||||
///{
|
///{
|
||||||
/// "uri" "http://localhost:{{port}}/csgo_game_event"
|
/// "uri" "http://localhost:{{port}}/csgo_game_event"
|
||||||
/// "timeout" "0.1"
|
/// "timeout" "0.1"
|
||||||
|
/// "heartbeat" "0.1"
|
||||||
/// "data"
|
/// "data"
|
||||||
/// {
|
/// {
|
||||||
/// "provider" "1"
|
/// "provider" "1"
|
||||||
@ -437,5 +461,15 @@ namespace Artemis.Properties {
|
|||||||
return ((byte[])(obj));
|
return ((byte[])(obj));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] wow_addon {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("wow_addon", resourceCulture);
|
||||||
|
return ((byte[])(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -223,4 +223,10 @@
|
|||||||
<data name="k95_platinum" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="k95_platinum" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\Keyboards\k95-platinum.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\Keyboards\k95-platinum.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="wow_addon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Modules\Games\WoW\Resources\wow-addon.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
<data name="Artemis_autorun" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Artemis autorun.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
BIN
Artemis/Artemis/Resources/Artemis autorun.xml
Normal file
BIN
Artemis/Artemis/Resources/Artemis autorun.xml
Normal file
Binary file not shown.
@ -15,39 +15,39 @@
|
|||||||
|
|
||||||
-- This event is raised after every profile update, before drawing.
|
-- This event is raised after every profile update, before drawing.
|
||||||
function updateHandler(profile, eventArgs)
|
function updateHandler(profile, eventArgs)
|
||||||
-- Don't do anything when previewing (this means the editor is open)
|
-- Don't do anything when previewing (this means the editor is open)
|
||||||
if eventArgs.Preview then
|
if eventArgs.Preview then
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- In this example we only want to update once per frame when the keyboard is
|
|
||||||
-- updated. If you don't do this the updateHandler will trigger on every
|
|
||||||
-- device's update.
|
|
||||||
if eventArgs.DeviceType != "keyboard" then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Custom update code here
|
-- In this example we only want to update once per frame when the keyboard is
|
||||||
|
-- updated. If you don't do this the updateHandler will trigger on every
|
||||||
|
-- device's update.
|
||||||
|
if not (eventArgs.DeviceType == "keyboard") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Custom update code here
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This event is raised after every profile draw, after updating.
|
-- This event is raised after every profile draw, after updating.
|
||||||
function drawHandler(profile, eventArgs)
|
function drawHandler(profile, eventArgs)
|
||||||
-- Don't do anything when previewing (this means the editor is open)
|
-- Don't do anything when previewing (this means the editor is open)
|
||||||
if eventArgs.Preview then
|
if eventArgs.Preview then
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- In this example we only want to draw to the keyboard. Each device has it's
|
|
||||||
-- own drawing event
|
|
||||||
if eventArgs.DeviceType != "keyboard" then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Custom draw code here
|
-- In this example we only want to draw to the keyboard. Each device has it's
|
||||||
|
-- own drawing event
|
||||||
|
if not (eventArgs.DeviceType == "keyboard") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Custom draw code here
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Register the default events, you can rename/remove these if you so desire.
|
-- Register the default events, you can rename/remove these if you so desire.
|
||||||
-- These events are raised every 40 ms (25 times a second).
|
-- These events are raised every 40 ms (25 times a second).
|
||||||
Events.DeviceUpdating.add(updateHandler);
|
Events.DeviceUpdating.add(updateHandler)
|
||||||
Events.DeviceDrawing.add(drawHandler);
|
Events.DeviceDrawing.add(drawHandler)
|
||||||
@ -4,10 +4,12 @@ using System.IO;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Artemis.DAL;
|
using Artemis.DAL;
|
||||||
using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing;
|
using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing;
|
||||||
|
using Artemis.Properties;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Artemis.Utilities.ActiveWindowDetection;
|
using Artemis.Utilities.ActiveWindowDetection;
|
||||||
using Caliburn.Micro;
|
using Caliburn.Micro;
|
||||||
using MahApps.Metro;
|
using MahApps.Metro;
|
||||||
|
using Microsoft.Win32.TaskScheduler;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
@ -111,10 +113,8 @@ namespace Artemis.Settings
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Autorun)
|
// Clean up the shortcut used by the old method
|
||||||
mgr.CreateShortcutsForExecutable("Artemis.exe", ShortcutLocation.Startup, false, "--autorun");
|
mgr.RemoveShortcutsForExecutable("Artemis.exe", ShortcutLocation.Startup);
|
||||||
else
|
|
||||||
mgr.RemoveShortcutsForExecutable("Artemis.exe", ShortcutLocation.Startup);
|
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
@ -125,6 +125,25 @@ namespace Artemis.Settings
|
|||||||
// Ignored, only happens when running from VS
|
// Ignored, only happens when running from VS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using (var ts = new TaskService())
|
||||||
|
{
|
||||||
|
var existing = ts.FindTask("Artemis autorun");
|
||||||
|
if (Autorun)
|
||||||
|
{
|
||||||
|
// Overwrite any existing tasks in case the installation folder changed
|
||||||
|
var path = Path.GetTempFileName();
|
||||||
|
var xml = Resources.Artemis_autorun.Replace("{{executablePath}}", mgr.RootAppDirectory + "\\Update.exe");
|
||||||
|
|
||||||
|
File.WriteAllText(path, xml);
|
||||||
|
ts.RootFolder.ImportTask(null, path);
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
else if (existing != null)
|
||||||
|
{
|
||||||
|
// Remove the task if it is present
|
||||||
|
ts.RootFolder.DeleteTask("Artemis autorun");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using static System.String;
|
using static System.String;
|
||||||
@ -42,10 +41,12 @@ namespace Artemis.Utilities
|
|||||||
return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", ""));
|
return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PropertyCollection> GenerateTypeMap(object o) => GenerateTypeMap(o.GetType().GetProperties());
|
public static List<PropertyCollection> GenerateTypeMap(object o)
|
||||||
|
{
|
||||||
|
return GenerateTypeMap(o.GetType().GetProperties());
|
||||||
|
}
|
||||||
|
|
||||||
private static List<PropertyCollection> GenerateTypeMap(IEnumerable<PropertyInfo> getProperties,
|
private static List<PropertyCollection> GenerateTypeMap(IEnumerable<PropertyInfo> getProperties, string path = "", bool inList = false)
|
||||||
string path = "")
|
|
||||||
{
|
{
|
||||||
var list = new List<PropertyCollection>();
|
var list = new List<PropertyCollection>();
|
||||||
foreach (var propInfo in getProperties)
|
foreach (var propInfo in getProperties)
|
||||||
@ -62,13 +63,28 @@ namespace Artemis.Utilities
|
|||||||
if (propInfo.PropertyType.BaseType?.Name == "Enum")
|
if (propInfo.PropertyType.BaseType?.Name == "Enum")
|
||||||
friendlyName = "(Choice)";
|
friendlyName = "(Choice)";
|
||||||
|
|
||||||
var parent = new PropertyCollection
|
// At this point the loop is in the item type contained in the list
|
||||||
|
PropertyCollection parent;
|
||||||
|
if (path.Contains("Item") && inList)
|
||||||
{
|
{
|
||||||
Type = propInfo.PropertyType.Name,
|
parent = new PropertyCollection
|
||||||
DisplayType = friendlyName,
|
{
|
||||||
Display = $"{path.Replace(".", " → ")}{propInfo.Name}",
|
Type = propInfo.PropertyType.Name,
|
||||||
Path = $"{path}{propInfo.Name}"
|
DisplayType = friendlyName,
|
||||||
};
|
Display = $"{path.Replace("Item.", "").Replace(".", " → ")}{propInfo.Name}",
|
||||||
|
Path = $"{path.Replace("Item.", "")}{propInfo.Name}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent = new PropertyCollection
|
||||||
|
{
|
||||||
|
Type = propInfo.PropertyType.Name,
|
||||||
|
DisplayType = friendlyName,
|
||||||
|
Display = $"{path.Replace(".", " → ")}{propInfo.Name}",
|
||||||
|
Path = $"{path}{propInfo.Name}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (propInfo.PropertyType.BaseType?.Name == "Enum")
|
if (propInfo.PropertyType.BaseType?.Name == "Enum")
|
||||||
{
|
{
|
||||||
@ -83,7 +99,16 @@ namespace Artemis.Utilities
|
|||||||
if (propInfo.PropertyType.Name != "String" &&
|
if (propInfo.PropertyType.Name != "String" &&
|
||||||
propInfo.PropertyType.Name != "DateTime" &&
|
propInfo.PropertyType.Name != "DateTime" &&
|
||||||
propInfo.CustomAttributes.All(a => a.AttributeType != typeof(JsonIgnoreAttribute)))
|
propInfo.CustomAttributes.All(a => a.AttributeType != typeof(JsonIgnoreAttribute)))
|
||||||
list.AddRange(GenerateTypeMap(propInfo.PropertyType.GetProperties(), path + $"{propInfo.Name}."));
|
{
|
||||||
|
var newPath = $"{path}{propInfo.Name}.";
|
||||||
|
var toInList = propInfo.PropertyType.Name == "List`1";
|
||||||
|
if (toInList)
|
||||||
|
{
|
||||||
|
inList = true;
|
||||||
|
newPath = $"({path}{propInfo.Name}).";
|
||||||
|
}
|
||||||
|
list.AddRange(GenerateTypeMap(propInfo.PropertyType.GetProperties(), newPath, inList));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -114,6 +139,21 @@ namespace Artemis.Utilities
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ExecuteSta(Action action)
|
||||||
|
{
|
||||||
|
var thread = new Thread(action.Invoke);
|
||||||
|
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
|
||||||
|
thread.Start();
|
||||||
|
thread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T ParseEnum<T>(string value, bool ignoreCase = true, bool stripWhitespaces = true)
|
||||||
|
{
|
||||||
|
if (stripWhitespaces)
|
||||||
|
value = value.Replace(" ", "");
|
||||||
|
return (T) Enum.Parse(typeof(T), value, true);
|
||||||
|
}
|
||||||
|
|
||||||
public struct PropertyCollection
|
public struct PropertyCollection
|
||||||
{
|
{
|
||||||
public string Display { get; set; }
|
public string Display { get; set; }
|
||||||
@ -128,13 +168,5 @@ namespace Artemis.Utilities
|
|||||||
public List<PropertyCollection> Children { get; set; }
|
public List<PropertyCollection> Children { get; set; }
|
||||||
public string DisplayType { get; set; }
|
public string DisplayType { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteSta(Action action)
|
|
||||||
{
|
|
||||||
var thread = new Thread(action.Invoke);
|
|
||||||
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
|
|
||||||
thread.Start();
|
|
||||||
thread.Join();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.DAL;
|
|
||||||
using Artemis.Managers;
|
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Artemis.Settings;
|
|
||||||
using Artemis.ViewModels.Abstract;
|
using Artemis.ViewModels.Abstract;
|
||||||
|
|
||||||
namespace Artemis.ViewModels
|
namespace Artemis.ViewModels
|
||||||
@ -16,18 +13,7 @@ namespace Artemis.ViewModels
|
|||||||
{
|
{
|
||||||
DisplayName = "Games";
|
DisplayName = "Games";
|
||||||
|
|
||||||
// Currently WoW is locked behind a hidden trigger (obviously not that hidden since you're reading this)
|
_vms = moduleViewModels.Where(m => m.ModuleModel.IsBoundToProcess).OrderBy(g => g.DisplayName).ToList();
|
||||||
// It is using memory reading and lets first try to contact Blizzard
|
|
||||||
if (SettingsProvider.Load<GeneralSettings>().GamestatePort == 62575)
|
|
||||||
{
|
|
||||||
_vms = moduleViewModels.Where(m => m.ModuleModel.IsBoundToProcess)
|
|
||||||
.OrderBy(g => g.DisplayName).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_vms = moduleViewModels.Where(m => m.ModuleModel.IsBoundToProcess && m.DisplayName != "WoW")
|
|
||||||
.OrderBy(g => g.DisplayName).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnActivate()
|
protected override void OnActivate()
|
||||||
|
|||||||
@ -26,7 +26,6 @@ using Artemis.Styles.DropTargetAdorners;
|
|||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Artemis.Utilities.ActiveWindowDetection;
|
using Artemis.Utilities.ActiveWindowDetection;
|
||||||
using Caliburn.Micro;
|
using Caliburn.Micro;
|
||||||
using Castle.Components.DictionaryAdapter;
|
|
||||||
using GongSolutions.Wpf.DragDrop;
|
using GongSolutions.Wpf.DragDrop;
|
||||||
using MahApps.Metro;
|
using MahApps.Metro;
|
||||||
using MahApps.Metro.Controls;
|
using MahApps.Metro.Controls;
|
||||||
@ -406,8 +405,6 @@ namespace Artemis.ViewModels
|
|||||||
ProfileNames.Clear();
|
ProfileNames.Clear();
|
||||||
if (_moduleModel != null && _deviceManager.ActiveKeyboard != null)
|
if (_moduleModel != null && _deviceManager.ActiveKeyboard != null)
|
||||||
ProfileNames.AddRange(ProfileProvider.GetProfileNames(_deviceManager.ActiveKeyboard, _moduleModel));
|
ProfileNames.AddRange(ProfileProvider.GetProfileNames(_deviceManager.ActiveKeyboard, _moduleModel));
|
||||||
|
|
||||||
NotifyOfPropertyChange(() => SelectedProfile);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,7 +441,7 @@ namespace Artemis.ViewModels
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
_moduleModel.ChangeProfile(profile);
|
SelectedProfileName = profile.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void RenameProfile()
|
public async void RenameProfile()
|
||||||
@ -456,7 +453,8 @@ namespace Artemis.ViewModels
|
|||||||
await ProfileEditorModel.RenameProfile(SelectedProfile);
|
await ProfileEditorModel.RenameProfile(SelectedProfile);
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
_moduleModel.ChangeProfile(renameProfile);
|
SelectedProfileName = "Default";
|
||||||
|
SelectedProfileName = renameProfile.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void DuplicateProfile()
|
public async void DuplicateProfile()
|
||||||
@ -469,7 +467,7 @@ namespace Artemis.ViewModels
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
_moduleModel.ChangeProfile(newProfle);
|
SelectedProfileName = newProfle.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void DeleteProfile()
|
public async void DeleteProfile()
|
||||||
@ -477,12 +475,17 @@ namespace Artemis.ViewModels
|
|||||||
if (SelectedProfile == null)
|
if (SelectedProfile == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var confirmed = await ProfileEditorModel.DeleteProfile(SelectedProfile, _moduleModel);
|
var confirmed = await ProfileEditorModel.ConfirmDeleteProfile(SelectedProfile, _moduleModel);
|
||||||
if (!confirmed)
|
if (!confirmed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var deleteProfile = SelectedProfile;
|
||||||
|
|
||||||
|
_moduleModel.ChangeProfile(null);
|
||||||
|
ProfileProvider.DeleteProfile(deleteProfile);
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
ProfileEditorModel.ChangeProfileByName(_moduleModel, null);
|
SelectedProfileName = "Default";
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ImportProfile()
|
public async void ImportProfile()
|
||||||
@ -499,7 +502,7 @@ namespace Artemis.ViewModels
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
_moduleModel.ChangeProfile(importProfile);
|
SelectedProfileName = importProfile.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExportProfile()
|
public void ExportProfile()
|
||||||
@ -633,7 +636,7 @@ namespace Artemis.ViewModels
|
|||||||
return SelectedProfile.GetRenderLayers(null, false, true);
|
return SelectedProfile.GetRenderLayers(null, false, true);
|
||||||
|
|
||||||
if (SelectedLayer == null || !SelectedLayer.Enabled)
|
if (SelectedLayer == null || !SelectedLayer.Enabled)
|
||||||
return new EditableList<LayerModel>();
|
return new List<LayerModel>();
|
||||||
|
|
||||||
if (SelectedLayer.LayerType is FolderType)
|
if (SelectedLayer.LayerType is FolderType)
|
||||||
drawLayers = SelectedLayer.GetRenderLayers(null, false, true);
|
drawLayers = SelectedLayer.GetRenderLayers(null, false, true);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Profiles.Layers.Models;
|
using Artemis.Profiles.Layers.Models;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
@ -41,6 +42,13 @@ namespace Artemis.ViewModels.Profiles
|
|||||||
new NamedOperator("Ends with", ".EndsWith")
|
new NamedOperator("Ends with", ".EndsWith")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly NamedOperator[] _listOperatorsPrefixes =
|
||||||
|
{
|
||||||
|
new NamedOperator("Any", "any"),
|
||||||
|
new NamedOperator("All", "all"),
|
||||||
|
new NamedOperator("None", "none")
|
||||||
|
};
|
||||||
|
|
||||||
private HotKey _hotKey;
|
private HotKey _hotKey;
|
||||||
private bool _keybindIsVisible;
|
private bool _keybindIsVisible;
|
||||||
private GeneralHelpers.PropertyCollection _selectedDataModelProp;
|
private GeneralHelpers.PropertyCollection _selectedDataModelProp;
|
||||||
@ -205,7 +213,6 @@ namespace Artemis.ViewModels.Profiles
|
|||||||
{
|
{
|
||||||
Operators.Clear();
|
Operators.Clear();
|
||||||
DropdownValues.Clear();
|
DropdownValues.Clear();
|
||||||
|
|
||||||
switch (SelectedDataModelProp.Type)
|
switch (SelectedDataModelProp.Type)
|
||||||
{
|
{
|
||||||
case "Int32":
|
case "Int32":
|
||||||
@ -228,7 +235,16 @@ namespace Artemis.ViewModels.Profiles
|
|||||||
UserValueIsVisible = true;
|
UserValueIsVisible = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// If the selected value is in a list, prefix all choices with list choices
|
||||||
|
if (SelectedDataModelProp.Path != null && SelectedDataModelProp.Path.Contains("("))
|
||||||
|
{
|
||||||
|
var listOperators = new List<NamedOperator>();
|
||||||
|
foreach (var o in Operators)
|
||||||
|
listOperators.AddRange(_listOperatorsPrefixes.Select(p => new NamedOperator(p.Display + " " + o.Display.ToLower(), p.Value + "|" + o.Value)));
|
||||||
|
|
||||||
|
Operators.Clear();
|
||||||
|
Operators.AddRange(listOperators);
|
||||||
|
}
|
||||||
// Add Changed operator is the type is event
|
// Add Changed operator is the type is event
|
||||||
if (_editorViewModel.ProposedLayer.IsEvent)
|
if (_editorViewModel.ProposedLayer.IsEvent)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using System.Linq;
|
|||||||
using Artemis.Profiles.Layers.Models;
|
using Artemis.Profiles.Layers.Models;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Caliburn.Micro;
|
using Caliburn.Micro;
|
||||||
using Castle.Core.Internal;
|
|
||||||
|
|
||||||
namespace Artemis.ViewModels.Profiles
|
namespace Artemis.ViewModels.Profiles
|
||||||
{
|
{
|
||||||
@ -219,7 +218,7 @@ namespace Artemis.ViewModels.Profiles
|
|||||||
.DynamicProperties
|
.DynamicProperties
|
||||||
.Where(p => p.LayerProperty != _property).ToList();
|
.Where(p => p.LayerProperty != _property).ToList();
|
||||||
|
|
||||||
if (!Proposed.GameProperty.IsNullOrEmpty())
|
if (!string.IsNullOrEmpty(Proposed.GameProperty))
|
||||||
proposedProperties.DynamicProperties.Add(Proposed);
|
proposedProperties.DynamicProperties.Add(Proposed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Layer name -->
|
<!-- Layer name -->
|
||||||
@ -66,6 +67,14 @@
|
|||||||
<Label Grid.Row="0" Grid.Column="4" Content="Is event: " VerticalAlignment="Center" />
|
<Label Grid.Row="0" Grid.Column="4" Content="Is event: " VerticalAlignment="Center" />
|
||||||
<controls:ToggleSwitchButton Grid.Row="0" Grid.Column="5" VerticalAlignment="Center" IsChecked="{Binding Path=ProposedLayer.IsEvent, Mode=TwoWay}" cal:Message.Attach="[Event Click] = [Action ToggleIsEvent]"
|
<controls:ToggleSwitchButton Grid.Row="0" Grid.Column="5" VerticalAlignment="Center" IsChecked="{Binding Path=ProposedLayer.IsEvent, Mode=TwoWay}" cal:Message.Attach="[Event Click] = [Action ToggleIsEvent]"
|
||||||
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" HorizontalAlignment="Left" Margin="10" />
|
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" HorizontalAlignment="Left" Margin="10" />
|
||||||
|
|
||||||
|
<!-- Fade in -->
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Content="Fade in:" VerticalAlignment="Center" />
|
||||||
|
<controls:NumericUpDown Grid.Row="1" Grid.Column="1" Margin="10" Height="22" Value="{Binding Path=ProposedLayer.FadeInTime}" VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<!-- Fade out -->
|
||||||
|
<Label Grid.Row="1" Grid.Column="2" Content="Fade out:" VerticalAlignment="Center" />
|
||||||
|
<controls:NumericUpDown Grid.Row="1" Grid.Column="3" Margin="10" Height="22" Value="{Binding Path=ProposedLayer.FadeOutTime}" VerticalAlignment="Center" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Advanced -->
|
<!-- Advanced -->
|
||||||
|
|||||||
Binary file not shown.
@ -1,14 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Betwixt" version="1.4.1" targetFramework="net461" />
|
<package id="Betwixt" version="1.4.1" targetFramework="net461" />
|
||||||
<package id="Caliburn.Micro" version="3.0.3" targetFramework="net461" />
|
<package id="Caliburn.Micro" version="3.1.0" targetFramework="net461" />
|
||||||
<package id="Caliburn.Micro.Core" version="3.0.3" targetFramework="net461" />
|
<package id="Caliburn.Micro.Core" version="3.1.0" targetFramework="net461" />
|
||||||
<package id="Castle.Core" version="4.0.0" targetFramework="net461" />
|
|
||||||
<package id="Colore" version="5.1.0" targetFramework="net461" />
|
<package id="Colore" version="5.1.0" targetFramework="net461" />
|
||||||
<package id="CSCore" version="1.2.0" targetFramework="net461" />
|
<package id="CSCore" version="1.2.1.2" targetFramework="net461" />
|
||||||
<package id="CUE.NET" version="1.1.3.0" targetFramework="net461" />
|
<package id="CUE.NET" version="1.1.3.1" targetFramework="net461" />
|
||||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net461" />
|
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net461" />
|
||||||
<package id="DynamicExpresso.Core" version="1.3.3.5" targetFramework="net461" />
|
<package id="DynamicExpresso.Core" version="1.3.4.7" targetFramework="net461" />
|
||||||
<package id="gong-wpf-dragdrop" version="0.1.4.3" targetFramework="net461" />
|
<package id="gong-wpf-dragdrop" version="0.1.4.3" targetFramework="net461" />
|
||||||
<package id="Hardcodet.NotifyIcon.Wpf" version="1.0.8" targetFramework="net452" />
|
<package id="Hardcodet.NotifyIcon.Wpf" version="1.0.8" targetFramework="net452" />
|
||||||
<package id="log4net" version="2.0.8" targetFramework="net461" />
|
<package id="log4net" version="2.0.8" targetFramework="net461" />
|
||||||
@ -17,18 +16,20 @@
|
|||||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net461" />
|
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net461" />
|
||||||
<package id="MoonSharp" version="2.0.0.0" targetFramework="net461" />
|
<package id="MoonSharp" version="2.0.0.0" targetFramework="net461" />
|
||||||
<package id="MouseKeyHook" version="5.4.0" targetFramework="net461" />
|
<package id="MouseKeyHook" version="5.4.0" targetFramework="net461" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
|
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||||
<package id="Ninject" version="3.2.2.0" targetFramework="net452" />
|
<package id="Ninject" version="3.2.2.0" targetFramework="net452" />
|
||||||
<package id="Ninject.Extensions.Conventions" version="3.2.0.0" targetFramework="net461" />
|
<package id="Ninject.Extensions.Conventions" version="3.2.0.0" targetFramework="net461" />
|
||||||
<package id="Ninject.Extensions.Logging" version="3.2.3.0" targetFramework="net452" />
|
<package id="Ninject.Extensions.Logging" version="3.2.3.0" targetFramework="net452" />
|
||||||
<package id="Ninject.Extensions.Logging.nlog4" version="3.2.3.0" targetFramework="net452" />
|
<package id="Ninject.Extensions.Logging.nlog4" version="3.2.3.0" targetFramework="net452" />
|
||||||
<package id="NLog" version="4.4.4" targetFramework="net461" />
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
<package id="NLog.Schema" version="4.4.4" targetFramework="net461" />
|
<package id="NLog.Schema" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="Pcap.Net.x64" version="1.0.4.1" targetFramework="net461" />
|
||||||
<package id="Process.NET" version="1.0.8" targetFramework="net461" />
|
<package id="Process.NET" version="1.0.8" targetFramework="net461" />
|
||||||
<package id="SharpDX" version="3.1.1" targetFramework="net461" />
|
<package id="SharpDX" version="4.0.1" targetFramework="net461" />
|
||||||
<package id="SharpDX.Direct3D9" version="3.1.1" targetFramework="net461" />
|
<package id="SharpDX.Direct3D9" version="4.0.1" targetFramework="net461" />
|
||||||
<package id="Splat" version="2.0.0" targetFramework="net461" />
|
<package id="Splat" version="2.0.0" targetFramework="net461" />
|
||||||
<package id="SpotifyAPI-NET" version="2.13.1" targetFramework="net461" />
|
<package id="SpotifyAPI-NET" version="2.16.1" targetFramework="net461" />
|
||||||
<package id="squirrel.windows" version="1.4.4" targetFramework="net461" />
|
<package id="squirrel.windows" version="1.4.4" targetFramework="net461" />
|
||||||
|
<package id="TaskScheduler" version="2.6.5" targetFramework="net461" />
|
||||||
<package id="WpfExceptionViewer" version="1.0.0.0" targetFramework="net452" />
|
<package id="WpfExceptionViewer" version="1.0.0.0" targetFramework="net452" />
|
||||||
</packages>
|
</packages>
|
||||||
@ -49,9 +49,8 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
|
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
||||||
Loading…
x
Reference in New Issue
Block a user