mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Rewrote most of the WoW module
This commit is contained in:
parent
681ce58c1e
commit
53dce2c4a5
@ -404,6 +404,12 @@
|
|||||||
<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\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" />
|
||||||
|
|||||||
@ -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);
|
||||||
@ -207,4 +208,4 @@ namespace Artemis.Managers
|
|||||||
RenderCompleted?.Invoke(this, EventArgs.Empty);
|
RenderCompleted?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
@ -72,4 +71,4 @@ namespace Artemis.Models
|
|||||||
_ctx = null;
|
_ctx = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
Artemis/Artemis/Modules/Games/WoW/Models/WoWAura.cs
Normal file
27
Artemis/Artemis/Modules/Games/WoW/Models/WoWAura.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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 string Caster { get; set; }
|
||||||
|
public int Stacks { get; set; }
|
||||||
|
public DateTime StartTime { set; get; }
|
||||||
|
public DateTime EndTime { get; set; }
|
||||||
|
|
||||||
|
public void ApplyJson(JToken buffJson)
|
||||||
|
{
|
||||||
|
Name = buffJson["name"].Value<string>();
|
||||||
|
Id = buffJson["spellID"].Value<int>();
|
||||||
|
Stacks = buffJson["count"].Value<int>();
|
||||||
|
Caster = buffJson["caster"]?.Value<string>();
|
||||||
|
|
||||||
|
// TODO: Duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
Artemis/Artemis/Modules/Games/WoW/Models/WoWCastBar.cs
Normal file
57
Artemis/Artemis/Modules/Games/WoW/Models/WoWCastBar.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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 void ApplyJson(JToken spellJson)
|
||||||
|
{
|
||||||
|
var castMs = spellJson["endTime"].Value<int>() - spellJson["startTime"].Value<int>();
|
||||||
|
var tickCount = Environment.TickCount;
|
||||||
|
var difference = tickCount - spellJson["startTime"].Value<int>();
|
||||||
|
|
||||||
|
Spell.Name = spellJson["name"].Value<string>();
|
||||||
|
Spell.Id = spellJson["spellID"].Value<int>();
|
||||||
|
StartTime = new DateTime(DateTime.Now.Ticks + difference);
|
||||||
|
EndTime = StartTime.AddMilliseconds(castMs);
|
||||||
|
NonInterruptible = spellJson["notInterruptible"].Value<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProgress()
|
||||||
|
{
|
||||||
|
if (Spell.Name == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var elapsed = DateTime.Now - StartTime;
|
||||||
|
var total = EndTime - StartTime;
|
||||||
|
|
||||||
|
Progress = (float) (elapsed.TotalMilliseconds / total.TotalMilliseconds);
|
||||||
|
if (Progress > 1)
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Spell.Name = null;
|
||||||
|
Spell.Id = 0;
|
||||||
|
StartTime = DateTime.MinValue;
|
||||||
|
EndTime = DateTime.MinValue;
|
||||||
|
NonInterruptible = false;
|
||||||
|
Progress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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["name"].Value<string>();
|
||||||
|
Id = specJson["id"].Value<int>();
|
||||||
|
Role = specJson["role"].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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
118
Artemis/Artemis/Modules/Games/WoW/Models/WoWUnit.cs
Normal file
118
Artemis/Artemis/Modules/Games/WoW/Models/WoWUnit.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
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; private set; }
|
||||||
|
|
||||||
|
public void ApplyJson(JObject json)
|
||||||
|
{
|
||||||
|
if (json["name"] == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Name = json["name"].Value<string>();
|
||||||
|
Level = json["level"].Value<int>();
|
||||||
|
Class = json["class"].Value<string>();
|
||||||
|
Gender = json["gender"].Value<int>() == 3 ? WoWGender.Female : WoWGender.Male;
|
||||||
|
|
||||||
|
if (json["race"] != null)
|
||||||
|
Race = GeneralHelpers.ParseEnum<WoWRace>(json["race"].Value<string>());
|
||||||
|
if (json["specialization"] != null)
|
||||||
|
Specialization.ApplyJson(json["specialization"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyStateJson(JObject json)
|
||||||
|
{
|
||||||
|
Health = json["health"].Value<int>();
|
||||||
|
MaxHealth = json["maxHealth"].Value<int>();
|
||||||
|
PowerType = GeneralHelpers.ParseEnum<WoWPowerType>(json["powerType"].Value<int>().ToString());
|
||||||
|
Power = json["power"].Value<int>();
|
||||||
|
MaxPower = json["maxPower"].Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyAuraJson(JObject json)
|
||||||
|
{
|
||||||
|
Buffs.Clear();
|
||||||
|
if (json["buffs"] != null)
|
||||||
|
{
|
||||||
|
foreach (var auraJson in json["buffs"].Children())
|
||||||
|
{
|
||||||
|
var aura = new WoWAura();
|
||||||
|
aura.ApplyJson(auraJson);
|
||||||
|
Buffs.Add(aura);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debuffs.Clear();
|
||||||
|
if (json["debuffs"] != null)
|
||||||
|
{
|
||||||
|
foreach (var auraJson in json["debuffs"].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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,10 @@
|
|||||||
using System;
|
using Artemis.Modules.Abstract;
|
||||||
using System.Collections.Generic;
|
using Artemis.Modules.Games.WoW.Models;
|
||||||
using System.Diagnostics;
|
using MoonSharp.Interpreter;
|
||||||
using Artemis.Modules.Abstract;
|
|
||||||
using Artemis.Utilities;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace Artemis.Modules.Games.WoW
|
namespace Artemis.Modules.Games.WoW
|
||||||
{
|
{
|
||||||
|
[MoonSharpUserData]
|
||||||
public class WoWDataModel : ModuleDataModel
|
public class WoWDataModel : ModuleDataModel
|
||||||
{
|
{
|
||||||
public WoWDataModel()
|
public WoWDataModel()
|
||||||
@ -22,203 +20,4 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
public string Zone { get; set; }
|
public string Zone { get; set; }
|
||||||
public string SubZone { get; set; }
|
public string SubZone { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WoWUnit
|
|
||||||
{
|
|
||||||
public WoWUnit()
|
|
||||||
{
|
|
||||||
Buffs = new List<WoWAura>();
|
|
||||||
Debuffs = new List<WoWAura>();
|
|
||||||
CastBar = new WoWCastBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 List<WoWAura> Buffs { get; set; }
|
|
||||||
public List<WoWAura> Debuffs { get; set; }
|
|
||||||
public WoWCastBar CastBar { get; set; }
|
|
||||||
|
|
||||||
public void ApplyJson(JObject json)
|
|
||||||
{
|
|
||||||
if (json["name"] == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Name = json["name"].Value<string>();
|
|
||||||
Level = json["level"].Value<int>();
|
|
||||||
Class = json["class"].Value<string>();
|
|
||||||
Gender = json["gender"].Value<int>() == 3 ? WoWGender.Female : WoWGender.Male;
|
|
||||||
|
|
||||||
if (json["race"] != null)
|
|
||||||
Race = GeneralHelpers.ParseEnum<WoWRace>(json["race"].Value<string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyStateJson(JObject json)
|
|
||||||
{
|
|
||||||
Health = json["health"].Value<int>();
|
|
||||||
MaxHealth = json["maxHealth"].Value<int>();
|
|
||||||
PowerType = GeneralHelpers.ParseEnum<WoWPowerType>(json["powerType"].Value<int>().ToString());
|
|
||||||
Power = json["power"].Value<int>();
|
|
||||||
MaxPower = json["maxPower"].Value<int>();
|
|
||||||
|
|
||||||
Buffs.Clear();
|
|
||||||
if (json["buffs"] != null)
|
|
||||||
{
|
|
||||||
foreach (var auraJson in json["buffs"].Children())
|
|
||||||
{
|
|
||||||
var aura = new WoWAura();
|
|
||||||
aura.ApplyJson(auraJson);
|
|
||||||
Buffs.Add(aura);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Debuffs.Clear();
|
|
||||||
if (json["debuffs"] != null)
|
|
||||||
{
|
|
||||||
foreach (var auraJson in json["debuffs"].Children())
|
|
||||||
{
|
|
||||||
var aura = new WoWAura();
|
|
||||||
aura.ApplyJson(auraJson);
|
|
||||||
Debuffs.Add(aura);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WoWAura
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Caster { get; set; }
|
|
||||||
public int Stacks { get; set; }
|
|
||||||
public DateTime StartTime { set; get; }
|
|
||||||
public DateTime EndTime { get; set; }
|
|
||||||
|
|
||||||
public void ApplyJson(JToken buffJson)
|
|
||||||
{
|
|
||||||
Name = buffJson["name"].Value<string>();
|
|
||||||
Id = buffJson["spellID"].Value<int>();
|
|
||||||
Caster = buffJson["caster"].Value<string>();
|
|
||||||
Stacks = buffJson["count"].Value<int>();
|
|
||||||
|
|
||||||
// TODO: Duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WoWCastBar
|
|
||||||
{
|
|
||||||
public void ApplyJson(JToken spellJson)
|
|
||||||
{
|
|
||||||
var castMs = spellJson["endTime"].Value<int>() - spellJson["startTime"].Value<int>();
|
|
||||||
var tickCount = Environment.TickCount;
|
|
||||||
var difference = tickCount - spellJson["startTime"].Value<int>();
|
|
||||||
|
|
||||||
SpellName = spellJson["name"].Value<string>();
|
|
||||||
SpellId = spellJson["spellID"].Value<int>();
|
|
||||||
StartTime = new DateTime(DateTime.Now.Ticks + difference);
|
|
||||||
EndTime = StartTime.AddMilliseconds(castMs);
|
|
||||||
NonInterruptible = spellJson["notInterruptible"].Value<bool>();
|
|
||||||
|
|
||||||
|
|
||||||
// SpellName = spellJson["name"].Value<string>();
|
|
||||||
// SpellId = spellJson["spellID"].Value<int>();
|
|
||||||
// StartTime = DateTime.Now.AddMilliseconds(spellJson["startTime"].Value<long>()/1000.0);
|
|
||||||
// EndTime = StartTime.AddMilliseconds(spellJson["endTime"].Value<long>()/1000.0);
|
|
||||||
// NonInterruptible = spellJson["notInterruptible"].Value<bool>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateProgress()
|
|
||||||
{
|
|
||||||
if (SpellName == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var elapsed = DateTime.Now - StartTime;
|
|
||||||
var total = EndTime - StartTime;
|
|
||||||
Progress = (float) (elapsed.TotalMilliseconds / total.TotalMilliseconds);
|
|
||||||
Debug.WriteLine(Progress);
|
|
||||||
if (Progress > 1)
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
SpellName = null;
|
|
||||||
SpellId = 0;
|
|
||||||
StartTime = DateTime.MinValue;
|
|
||||||
EndTime = DateTime.MinValue;
|
|
||||||
NonInterruptible = false;
|
|
||||||
Progress = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SpellName { get; set; }
|
|
||||||
public int SpellId { get; set; }
|
|
||||||
public DateTime StartTime { set; get; }
|
|
||||||
public DateTime EndTime { get; set; }
|
|
||||||
public bool NonInterruptible { get; set; }
|
|
||||||
public float Progress { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
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 WoWClass
|
|
||||||
{
|
|
||||||
Warrior,
|
|
||||||
Paladin,
|
|
||||||
Hunter,
|
|
||||||
Rogue,
|
|
||||||
Priest,
|
|
||||||
DeathKnight,
|
|
||||||
Shaman,
|
|
||||||
Mage,
|
|
||||||
Warlock,
|
|
||||||
Druid,
|
|
||||||
Monk,
|
|
||||||
DemonHunter
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WoWRace
|
|
||||||
{
|
|
||||||
Human,
|
|
||||||
Orc,
|
|
||||||
Dwarf,
|
|
||||||
NightElf,
|
|
||||||
Undead,
|
|
||||||
Tauren,
|
|
||||||
Gnome,
|
|
||||||
Troll,
|
|
||||||
BloodElf,
|
|
||||||
Draenei,
|
|
||||||
Goblin,
|
|
||||||
Worgen,
|
|
||||||
Pandaren
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WoWGender
|
|
||||||
{
|
|
||||||
Male,
|
|
||||||
Female
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,198 +1,222 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.DAL;
|
using Artemis.DAL;
|
||||||
using Artemis.Managers;
|
using Artemis.Managers;
|
||||||
using Artemis.Modules.Abstract;
|
using Artemis.Modules.Abstract;
|
||||||
using Newtonsoft.Json.Linq;
|
using Artemis.Modules.Games.WoW.Models;
|
||||||
using PcapDotNet.Core;
|
using Newtonsoft.Json.Linq;
|
||||||
using PcapDotNet.Packets;
|
using PcapDotNet.Core;
|
||||||
|
using PcapDotNet.Packets;
|
||||||
namespace Artemis.Modules.Games.WoW
|
|
||||||
{
|
namespace Artemis.Modules.Games.WoW
|
||||||
public class WoWModel : ModuleModel
|
{
|
||||||
{
|
public class WoWModel : ModuleModel
|
||||||
private readonly Regex _rgx;
|
{
|
||||||
private PacketCommunicator _communicator;
|
private readonly Regex _rgx;
|
||||||
|
private PacketCommunicator _communicator;
|
||||||
public WoWModel(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager)
|
|
||||||
{
|
public WoWModel(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager)
|
||||||
Settings = SettingsProvider.Load<WoWSettings>();
|
{
|
||||||
DataModel = new WoWDataModel();
|
Settings = SettingsProvider.Load<WoWSettings>();
|
||||||
ProcessNames.Add("Wow-64");
|
DataModel = new WoWDataModel();
|
||||||
|
ProcessNames.Add("Wow-64");
|
||||||
_rgx = new Regex("(artemis)\\((.*?)\\)", RegexOptions.Compiled);
|
|
||||||
}
|
_rgx = new Regex("(artemis)\\((.*?)\\)", RegexOptions.Compiled);
|
||||||
|
}
|
||||||
public override string Name => "WoW";
|
|
||||||
public override bool IsOverlay => false;
|
public override string Name => "WoW";
|
||||||
public override bool IsBoundToProcess => true;
|
public override bool IsOverlay => false;
|
||||||
|
public override bool IsBoundToProcess => true;
|
||||||
|
|
||||||
public override void Enable()
|
|
||||||
{
|
public override void Enable()
|
||||||
// Start scanning WoW packets
|
{
|
||||||
// Retrieve the device list from the local machine
|
// Start scanning WoW packets
|
||||||
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
|
// Retrieve the device list from the local machine
|
||||||
|
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
|
||||||
if (allDevices.Count == 0)
|
|
||||||
{
|
if (allDevices.Count == 0)
|
||||||
Logger.Warn("No interfaces found! Can't scan WoW packets.");
|
{
|
||||||
return;
|
Logger.Warn("No interfaces found! Can't scan WoW packets.");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
// Take the selected adapter
|
|
||||||
PacketDevice selectedDevice = allDevices.First();
|
// Take the selected adapter
|
||||||
|
PacketDevice selectedDevice = allDevices.First();
|
||||||
// Open the device
|
|
||||||
_communicator = selectedDevice.Open(65536, PacketDeviceOpenAttributes.Promiscuous, 100);
|
// Open the device
|
||||||
Logger.Debug("Listening on " + selectedDevice.Description + " for WoW packets");
|
_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"))
|
// Compile the filter
|
||||||
{
|
using (var filter = _communicator.CreateFilter("tcp"))
|
||||||
// Set the filter
|
{
|
||||||
_communicator.SetFilter(filter);
|
// Set the filter
|
||||||
}
|
_communicator.SetFilter(filter);
|
||||||
|
}
|
||||||
Task.Run(() => ReceivePackets());
|
|
||||||
base.Enable();
|
Task.Run(() => ReceivePackets());
|
||||||
}
|
base.Enable();
|
||||||
|
}
|
||||||
private void ReceivePackets()
|
|
||||||
{
|
private void ReceivePackets()
|
||||||
// start the capture
|
{
|
||||||
try
|
// start the capture
|
||||||
{
|
try
|
||||||
_communicator.ReceivePackets(0, PacketHandler);
|
{
|
||||||
}
|
_communicator.ReceivePackets(0, PacketHandler);
|
||||||
catch (InvalidOperationException)
|
}
|
||||||
{
|
catch (InvalidOperationException)
|
||||||
// ignored, happens on shutdown
|
{
|
||||||
}
|
// ignored, happens on shutdown
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void PacketHandler(Packet packet)
|
|
||||||
{
|
private void PacketHandler(Packet packet)
|
||||||
// Ignore duplicates
|
{
|
||||||
if (packet.Ethernet.IpV4.Udp.SourcePort == 3724)
|
// Ignore duplicates
|
||||||
return;
|
if (packet.Ethernet.IpV4.Udp.SourcePort == 3724)
|
||||||
|
return;
|
||||||
var str = Encoding.Default.GetString(packet.Buffer);
|
|
||||||
if (str.ToLower().Contains("artemis"))
|
var str = Encoding.Default.GetString(packet.Buffer);
|
||||||
{
|
if (str.ToLower().Contains("artemis"))
|
||||||
var match = _rgx.Match(str);
|
{
|
||||||
if (match.Groups.Count != 3)
|
var match = _rgx.Match(str);
|
||||||
return;
|
if (match.Groups.Count != 3)
|
||||||
|
return;
|
||||||
Logger.Debug("[{0}] {1}", packet.Ethernet.IpV4.Udp.SourcePort, match.Groups[2].Value);
|
|
||||||
// Get the command and argument
|
Logger.Trace("[{0}] {1}", packet.Ethernet.IpV4.Udp.SourcePort, match.Groups[2].Value);
|
||||||
var parts = match.Groups[2].Value.Split('|');
|
// Get the command and argument
|
||||||
HandleGameData(parts[0], parts[1]);
|
var parts = match.Groups[2].Value.Split('|');
|
||||||
}
|
HandleGameData(parts[0], parts[1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void HandleGameData(string command, string data)
|
|
||||||
{
|
private void HandleGameData(string command, string data)
|
||||||
JObject json = null;
|
{
|
||||||
if (!data.StartsWith("\"") && !data.EndsWith("\""))
|
JObject json = null;
|
||||||
json = JObject.Parse(data);
|
if (!data.StartsWith("\"") && !data.EndsWith("\""))
|
||||||
|
json = JObject.Parse(data);
|
||||||
lock (DataModel)
|
|
||||||
{
|
lock (DataModel)
|
||||||
var dataModel = (WoWDataModel) DataModel;
|
{
|
||||||
switch (command)
|
var dataModel = (WoWDataModel) DataModel;
|
||||||
{
|
switch (command)
|
||||||
case "player":
|
{
|
||||||
ParsePlayer(json, dataModel);
|
case "player":
|
||||||
break;
|
ParsePlayer(json, dataModel);
|
||||||
case "target":
|
break;
|
||||||
ParseTarget(json, dataModel);
|
case "target":
|
||||||
break;
|
ParseTarget(json, dataModel);
|
||||||
case "playerState":
|
break;
|
||||||
ParsePlayerState(json, dataModel);
|
case "playerState":
|
||||||
break;
|
ParsePlayerState(json, dataModel);
|
||||||
case "targetState":
|
break;
|
||||||
ParseTargetState(json, dataModel);
|
case "targetState":
|
||||||
break;
|
ParseTargetState(json, dataModel);
|
||||||
case "spellCast":
|
break;
|
||||||
ParseSpellCast(json, dataModel);
|
case "auras":
|
||||||
break;
|
ParseAuras(json, dataModel);
|
||||||
case "spellCastFailed":
|
break;
|
||||||
ParseSpellCastFailed(data, dataModel);
|
case "spellCast":
|
||||||
break;
|
ParseSpellCast(json, dataModel);
|
||||||
case "spellCastInterrupted":
|
break;
|
||||||
ParseSpellCastInterrupted(data, dataModel);
|
case "instantSpellCast":
|
||||||
break;
|
ParseInstantSpellCast(json, dataModel);
|
||||||
}
|
break;
|
||||||
}
|
case "spellCastFailed":
|
||||||
}
|
ParseSpellCastFailed(data, dataModel);
|
||||||
|
break;
|
||||||
private void ParsePlayer(JObject json, WoWDataModel dataModel)
|
case "spellCastInterrupted":
|
||||||
{
|
ParseSpellCastInterrupted(data, dataModel);
|
||||||
dataModel.Player.ApplyJson(json);
|
break;
|
||||||
}
|
default:
|
||||||
|
Logger.Warn("The WoW addon sent an unknown command: {0}", command);
|
||||||
private void ParseTarget(JObject json, WoWDataModel dataModel)
|
break;
|
||||||
{
|
}
|
||||||
dataModel.Target.ApplyJson(json);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParsePlayerState(JObject json, WoWDataModel dataModel)
|
private void ParsePlayer(JObject json, WoWDataModel dataModel)
|
||||||
{
|
{
|
||||||
dataModel.Player.ApplyStateJson(json);
|
dataModel.Player.ApplyJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseTargetState(JObject json, WoWDataModel dataModel)
|
private void ParseTarget(JObject json, WoWDataModel dataModel)
|
||||||
{
|
{
|
||||||
dataModel.Target.ApplyStateJson(json);
|
dataModel.Target.ApplyJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseSpellCast(JObject json, WoWDataModel dataModel)
|
private void ParsePlayerState(JObject json, WoWDataModel dataModel)
|
||||||
{
|
{
|
||||||
if (json["unitID"].Value<string>() == "player")
|
dataModel.Player.ApplyStateJson(json);
|
||||||
dataModel.Player.CastBar.ApplyJson(json);
|
}
|
||||||
else if (json["unitID"].Value<string>() == "target")
|
|
||||||
dataModel.Target.CastBar.ApplyJson(json);
|
private void ParseTargetState(JObject json, WoWDataModel dataModel)
|
||||||
}
|
{
|
||||||
|
dataModel.Target.ApplyStateJson(json);
|
||||||
private void ParseInstantSpellCast(JObject json, WoWDataModel dataModel)
|
}
|
||||||
{
|
|
||||||
|
private void ParseAuras(JObject json, WoWDataModel dataModel)
|
||||||
}
|
{
|
||||||
|
dataModel.Player.ApplyAuraJson(json);
|
||||||
private void ParseSpellCastFailed(string data, WoWDataModel dataModel)
|
}
|
||||||
{
|
|
||||||
if (data == "\"player\"")
|
private void ParseSpellCast(JObject json, WoWDataModel dataModel)
|
||||||
dataModel.Player.CastBar.Clear();
|
{
|
||||||
else if (data == "\"target\"")
|
if (json["unitID"].Value<string>() == "player")
|
||||||
dataModel.Target.CastBar.Clear();
|
dataModel.Player.CastBar.ApplyJson(json);
|
||||||
}
|
else if (json["unitID"].Value<string>() == "target")
|
||||||
|
dataModel.Target.CastBar.ApplyJson(json);
|
||||||
private void ParseSpellCastInterrupted(string data, WoWDataModel dataModel)
|
}
|
||||||
{
|
|
||||||
if (data == "\"player\"")
|
private void ParseInstantSpellCast(JObject json, WoWDataModel dataModel)
|
||||||
dataModel.Player.CastBar.Clear();
|
{
|
||||||
else if (data == "\"target\"")
|
var spell = new WoWSpell
|
||||||
dataModel.Target.CastBar.Clear();
|
{
|
||||||
}
|
Name = json["name"].Value<string>(),
|
||||||
|
Id = json["spellID"].Value<int>()
|
||||||
public override void Dispose()
|
};
|
||||||
{
|
|
||||||
_communicator.Break();
|
if (json["unitID"].Value<string>() == "player")
|
||||||
_communicator.Dispose();
|
dataModel.Player.AddInstantCast(spell);
|
||||||
base.Dispose();
|
else if (json["unitID"].Value<string>() == "target")
|
||||||
}
|
dataModel.Target.AddInstantCast(spell);
|
||||||
|
}
|
||||||
public override void Update()
|
|
||||||
{
|
private void ParseSpellCastFailed(string data, WoWDataModel dataModel)
|
||||||
var dataModel = (WoWDataModel)DataModel;
|
{
|
||||||
|
if (data == "\"player\"")
|
||||||
dataModel.Player.CastBar.UpdateProgress();
|
dataModel.Player.CastBar.Clear();
|
||||||
dataModel.Target.CastBar.UpdateProgress();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_communicator.Break();
|
||||||
|
_communicator.Dispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
var dataModel = (WoWDataModel) DataModel;
|
||||||
|
|
||||||
|
dataModel.Player.Update();
|
||||||
|
dataModel.Target.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,4 +5,4 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
public class WoWSettings : ModuleSettings
|
public class WoWSettings : ModuleSettings
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
<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="*" />
|
||||||
|
|||||||
@ -12,4 +12,4 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,4 +14,4 @@ namespace Artemis.Modules.Games.WoW
|
|||||||
|
|
||||||
public override bool UsesProfileEditor => true;
|
public override bool UsesProfileEditor => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -44,6 +44,8 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
var collectionField = _rgx.Match(Field).Groups[1].Value;
|
var collectionField = _rgx.Match(Field).Groups[1].Value;
|
||||||
var collectionInspect = (IEnumerable) GeneralHelpers.GetPropertyValue(subject, collectionField);
|
var collectionInspect = (IEnumerable) GeneralHelpers.GetPropertyValue(subject, collectionField);
|
||||||
var operatorParts = Operator.Split('|');
|
var operatorParts = Operator.Split('|');
|
||||||
|
var field = Field.Split(')').Last().Substring(1);
|
||||||
|
|
||||||
_lastValue = collectionInspect;
|
_lastValue = collectionInspect;
|
||||||
|
|
||||||
if (operatorParts[0] == "any")
|
if (operatorParts[0] == "any")
|
||||||
@ -51,7 +53,6 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
var anyMatch = false;
|
var anyMatch = false;
|
||||||
foreach (var collectionValue in collectionInspect)
|
foreach (var collectionValue in collectionInspect)
|
||||||
{
|
{
|
||||||
var field = Field.Split(')').Last().Substring(1);
|
|
||||||
anyMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
anyMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
if (anyMatch)
|
if (anyMatch)
|
||||||
break;
|
break;
|
||||||
@ -63,7 +64,6 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
var allMatch = true;
|
var allMatch = true;
|
||||||
foreach (var collectionValue in collectionInspect)
|
foreach (var collectionValue in collectionInspect)
|
||||||
{
|
{
|
||||||
var field = Field.Split(')').Last().Substring(1);
|
|
||||||
allMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
allMatch = EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
if (!allMatch)
|
if (!allMatch)
|
||||||
break;
|
break;
|
||||||
@ -75,7 +75,6 @@ namespace Artemis.Profiles.Layers.Models
|
|||||||
var noneMatch = true;
|
var noneMatch = true;
|
||||||
foreach (var collectionValue in collectionInspect)
|
foreach (var collectionValue in collectionInspect)
|
||||||
{
|
{
|
||||||
var field = Field.Split(')').Last().Substring(1);
|
|
||||||
noneMatch = !EvaluateOperator(collectionValue, field, operatorParts[1]);
|
noneMatch = !EvaluateOperator(collectionValue, field, operatorParts[1]);
|
||||||
if (!noneMatch)
|
if (!noneMatch)
|
||||||
break;
|
break;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user