refactor: fix up some code and prepare for sdk

This commit is contained in:
Anna 2020-12-19 20:19:03 -05:00
parent 2197a61416
commit 0873aadd1b
13 changed files with 254 additions and 431 deletions

View File

@ -1,203 +0,0 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.{cs,json}]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = lf
insert_final_newline = true
[*.cs]
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = true:silent
dotnet_style_qualification_for_field = true:silent
dotnet_style_qualification_for_method = true:silent
dotnet_style_qualification_for_property = true:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Code-block preferences
csharp_prefer_braces = true:error
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

View File

@ -19,7 +19,7 @@ namespace NoSoliciting.Tests.DefinitionsTests {
var allDefs = defs.Chat
.Concat(defs.PartyFinder)
.Concat(defs.Global);
foreach (KeyValuePair<string, Definition> entry in allDefs) {
foreach (var entry in allDefs) {
entry.Value.Initialise(entry.Key);
}
}
@ -37,7 +37,7 @@ namespace NoSoliciting.Tests.DefinitionsTests {
}
public override string ToString() {
string name = channel == ChatType.None ? "PF" : channel.ToString();
var name = channel == ChatType.None ? "PF" : channel.ToString();
return $"[{name}] {this.content}";
}
}
@ -49,7 +49,7 @@ namespace NoSoliciting.Tests.DefinitionsTests {
internal static class DefinitionExt {
internal static void Check(this Definition def, string message, CheckType type) {
TestMessage testMsg = new TestMessage(message);
var testMsg = new TestMessage(message);
def.Check(testMsg, type);
}

View File

@ -6,9 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting", "NoSoliciting\NoSoliciting.csproj", "{E4C12987-9064-4788-8783-BE418B2C0142}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C424003-6609-47D7-8D27-BF309C34E909}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Tests", "NoSoliciting.Tests\NoSoliciting.Tests.csproj", "{1962D91F-543A-4214-88FD-788BB7ACECE3}"
EndProject

View File

@ -36,7 +36,7 @@ namespace NoSoliciting {
Definitions defs = null;
Tuple<Definitions, string> download = await Download().ConfigureAwait(true);
var download = await Download().ConfigureAwait(true);
if (download != null) {
defs = download.Item1;
@ -52,7 +52,7 @@ namespace NoSoliciting {
}
public static Definitions Load(string text) {
IDeserializer de = new DeserializerBuilder()
var de = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new MatcherConverter())
.IgnoreUnmatchedProperties()
@ -74,15 +74,15 @@ namespace NoSoliciting {
throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
}
string pluginFolder = PluginFolder(plugin);
var pluginFolder = PluginFolder(plugin);
string cachedPath = Path.Combine(pluginFolder, "definitions.yaml");
var cachedPath = Path.Combine(pluginFolder, "definitions.yaml");
if (!File.Exists(cachedPath)) {
goto LoadDefaults;
}
string text;
using (StreamReader file = File.OpenText(cachedPath)) {
using (var file = File.OpenText(cachedPath)) {
text = await file.ReadToEndAsync().ConfigureAwait(true);
}
@ -102,8 +102,8 @@ namespace NoSoliciting {
private static async Task<Tuple<Definitions, string>> Download() {
try {
using WebClient client = new WebClient();
string text = await client.DownloadStringTaskAsync(URL).ConfigureAwait(true);
using var client = new WebClient();
var text = await client.DownloadStringTaskAsync(URL).ConfigureAwait(true);
LastError = null;
return Tuple.Create(Load(text), text);
} catch (Exception e) when (e is WebException || e is YamlException) {
@ -115,33 +115,33 @@ namespace NoSoliciting {
}
private static async void UpdateCache(Plugin plugin, string defs) {
string pluginFolder = PluginFolder(plugin);
var pluginFolder = PluginFolder(plugin);
Directory.CreateDirectory(pluginFolder);
string cachePath = Path.Combine(pluginFolder, "definitions.yaml");
var cachePath = Path.Combine(pluginFolder, "definitions.yaml");
byte[] b = Encoding.UTF8.GetBytes(defs);
var b = Encoding.UTF8.GetBytes(defs);
using FileStream file = File.OpenWrite(cachePath);
using var file = File.OpenWrite(cachePath);
await file.WriteAsync(b, 0, b.Length).ConfigureAwait(true);
}
internal void Initialise(Plugin plugin) {
IEnumerable<KeyValuePair<string, Definition>> defs = this.Chat.Select(e => new KeyValuePair<string, Definition>($"chat.{e.Key}", e.Value))
var defs = this.Chat.Select(e => new KeyValuePair<string, Definition>($"chat.{e.Key}", e.Value))
.Concat(this.PartyFinder.Select(e => new KeyValuePair<string, Definition>($"party_finder.{e.Key}", e.Value)));
foreach (KeyValuePair<string, Definition> entry in defs) {
foreach (var entry in defs) {
entry.Value.Initialise(entry.Key);
if (!plugin.Config.FilterStatus.TryGetValue(entry.Key, out _)) {
plugin.Config.FilterStatus[entry.Key] = entry.Value.Default;
}
}
foreach (KeyValuePair<string, Definition> entry in this.Global) {
Definition chat = entry.Value.Clone();
foreach (var entry in this.Global) {
var chat = entry.Value.Clone();
chat.Initialise($"chat.global.{entry.Key}");
this.Chat[$"global.{entry.Key}"] = chat;
Definition pf = entry.Value.Clone();
var pf = entry.Value.Clone();
pf.Initialise($"party_finder.global.{entry.Key}");
this.PartyFinder[$"global.{entry.Key}"] = pf;
@ -184,11 +184,11 @@ namespace NoSoliciting {
return;
}
IEnumerable<Matcher> allMatchers = this.LikelyMatchers
var allMatchers = this.LikelyMatchers
.Concat(this.RequiredMatchers)
.SelectMany(matchers => matchers);
foreach (Matcher matcher in allMatchers) {
foreach (var matcher in allMatchers) {
matcher.MakeIgnoreCase();
}
}
@ -211,15 +211,15 @@ namespace NoSoliciting {
}
// ensure all required matchers match
bool allRequired = this.RequiredMatchers.All(matchers => matchers.Any(matcher => matcher.Matches(text)));
var allRequired = this.RequiredMatchers.All(matchers => matchers.Any(matcher => matcher.Matches(text)));
if (!allRequired) {
return false;
}
// calculate likelihood
int likelihood = 0;
var likelihood = 0;
foreach (List<Matcher> matchers in this.LikelyMatchers) {
foreach (var matchers in this.LikelyMatchers) {
if (matchers.Any(matcher => matcher.Matches(text))) {
likelihood += 1;
}
@ -302,7 +302,7 @@ namespace NoSoliciting {
throw new ArgumentException("matcher was an object but did not specify regex key");
}
Regex regex = new Regex(parser.Consume<Scalar>().Value, RegexOptions.Compiled);
var regex = new Regex(parser.Consume<Scalar>().Value, RegexOptions.Compiled);
matcher = new Matcher(regex);
parser.Consume<MappingEnd>();

View File

@ -21,8 +21,8 @@ namespace NoSoliciting {
public Filter(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
IntPtr listingPtr = this.plugin.Interface.TargetModuleScanner.ScanText("40 53 41 57 48 83 EC 28 48 8B D9");
IntPtr summaryPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B FA 48 8B F1 45 84 C0 74 ?? 0F B7 0A");
var listingPtr = this.plugin.Interface.TargetModuleScanner.ScanText("40 53 41 57 48 83 EC 28 48 8B D9");
var summaryPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B FA 48 8B F1 45 84 C0 74 ?? 0F B7 0A");
if (listingPtr == IntPtr.Zero || summaryPtr == IntPtr.Zero) {
PluginLog.Log("Party Finder filtering disabled because hook could not be created.");
return;
@ -46,30 +46,30 @@ namespace NoSoliciting {
this.clearOnNext = false;
}
IntPtr dataPtr = param_2 + 0x10;
var dataPtr = param_2 + 0x10;
// parse the packet into a struct
PFPacket packet = Marshal.PtrToStructure<PFPacket>(dataPtr);
var packet = Marshal.PtrToStructure<PfPacket>(dataPtr);
for (int i = 0; i < packet.listings.Length; i++) {
PFListing listing = packet.listings[i];
for (var i = 0; i < packet.listings.Length; i++) {
var listing = packet.listings[i];
// only look at listings that aren't null
if (listing.IsNull()) {
continue;
}
string desc = listing.Description();
var desc = listing.Description();
string reason = null;
bool filter = false;
var filter = false;
filter = filter || (this.plugin.Config.FilterHugeItemLevelPFs
&& listing.minimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.plugin.Interface.Data)
&& SetReason(ref reason, "ilvl"));
foreach (Definition def in this.plugin.Definitions.PartyFinder.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out bool enabled)
foreach (var def in this.plugin.Definitions.PartyFinder.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled)
&& enabled
&& def.Matches(XivChatType.None, desc)
&& SetReason(ref reason, def.Id));
@ -93,21 +93,21 @@ namespace NoSoliciting {
}
// replace the listing with an empty one
packet.listings[i] = new PFListing();
packet.listings[i] = new PfListing();
PluginLog.Log($"Filtered PF listing from {listing.Name()} ({reason}): {listing.Description()}");
}
// get some memory for writing to
byte[] newPacket = new byte[PacketInfo.packetSize];
GCHandle pinnedArray = GCHandle.Alloc(newPacket, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
var newPacket = new byte[PacketInfo.PacketSize];
var pinnedArray = GCHandle.Alloc(newPacket, GCHandleType.Pinned);
var pointer = pinnedArray.AddrOfPinnedObject();
// write our struct into the memory (doing this directly crashes the game)
Marshal.StructureToPtr(packet, pointer, false);
// copy our new memory over the game's
Marshal.Copy(newPacket, 0, dataPtr, PacketInfo.packetSize);
Marshal.Copy(newPacket, 0, dataPtr, PacketInfo.PacketSize);
// free memory
pinnedArray.Free();
@ -132,13 +132,13 @@ namespace NoSoliciting {
return;
}
string text = message.TextValue;
var text = message.TextValue;
string reason = null;
bool filter = false;
var filter = false;
foreach (Definition def in this.plugin.Definitions.Chat.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out bool enabled)
foreach (var def in this.plugin.Definitions.Chat.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled)
&& enabled
&& def.Matches(type, text)
&& SetReason(ref reason, def.Id));

View File

@ -8,7 +8,7 @@ using System.Text;
namespace NoSoliciting {
public static class FilterUtil {
private static readonly Dictionary<char, string> replacements = new Dictionary<char, string>() {
private static readonly Dictionary<char, string> Replacements = new Dictionary<char, string>() {
// numerals
['\ue055'] = "1",
['\ue056'] = "2",
@ -57,37 +57,37 @@ namespace NoSoliciting {
// replace ffxiv private use chars
var builder = new StringBuilder(input.Length);
foreach (char c in input) {
foreach (var c in input) {
if (c < LowestReplacement) {
goto AppendNormal;
}
// alphabet
if (c >= 0xe071 && c <= 0xe08a) {
builder.Append((char)(c - 0xe030));
builder.Append((char) (c - 0xe030));
continue;
}
// 0 to 9
if (c >= 0xe060 && c <= 0xe069) {
builder.Append((char)(c - 0xe030));
builder.Append((char) (c - 0xe030));
continue;
}
// 1 to 9
if (c >= 0xe0b1 && c <= 0xe0b9) {
builder.Append((char)(c - 0xe080));
builder.Append((char) (c - 0xe080));
continue;
}
// 1 to 9 again
if (c >= 0xe090 && c <= 0xe098) {
builder.Append((char)(c - 0xe05f));
builder.Append((char) (c - 0xe05f));
continue;
}
// replacements in map
if (replacements.TryGetValue(c, out string rep)) {
if (Replacements.TryGetValue(c, out var rep)) {
builder.Append(rep);
continue;
}
@ -102,9 +102,9 @@ namespace NoSoliciting {
return input.Normalize(NormalizationForm.FormKD);
}
private static int MaxItemLevel { get; set; } = 0;
private static int MaxItemLevel { get; set; }
enum Slot {
private enum Slot {
MainHand,
OffHand,
Head,
@ -120,8 +120,8 @@ namespace NoSoliciting {
RingR,
}
static Slot? SlotFromItem(Item item) {
EquipSlotCategory cat = item.EquipSlotCategory.Value;
private static Slot? SlotFromItem(Item item) {
var cat = item.EquipSlotCategory.Value;
if (cat == null) {
return null;
}
@ -190,34 +190,34 @@ namespace NoSoliciting {
throw new ArgumentNullException(nameof(data), "DataManager cannot be null");
}
Dictionary<Slot, int> ilvls = new Dictionary<Slot, int>();
var ilvls = new Dictionary<Slot, int>();
foreach (Item item in data.GetExcelSheet<Item>()) {
Slot? slot = SlotFromItem(item);
foreach (var item in data.GetExcelSheet<Item>()) {
var slot = SlotFromItem(item);
if (slot == null) {
continue;
}
int itemLevel = 0;
ItemLevel ilvl = item.LevelItem.Value;
var itemLevel = 0;
var ilvl = item.LevelItem.Value;
if (ilvl != null) {
itemLevel = (int)ilvl.RowId;
itemLevel = (int) ilvl.RowId;
}
if (ilvls.TryGetValue((Slot)slot, out int currentMax) && currentMax > itemLevel) {
if (ilvls.TryGetValue((Slot) slot, out var currentMax) && currentMax > itemLevel) {
continue;
}
ilvls[(Slot)slot] = itemLevel;
ilvls[(Slot) slot] = itemLevel;
}
MaxItemLevel = (int)ilvls.Values.Average();
MaxItemLevel = (int) ilvls.Values.Average();
return MaxItemLevel;
}
}
public static class RMTExtensions {
public static class RmtExtensions {
public static bool ContainsIgnoreCase(this string haystack, string needle) {
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle, CompareOptions.IgnoreCase) >= 0;
}

View File

@ -54,7 +54,7 @@ namespace NoSoliciting {
}
public string ToJson() {
JsonMessage msg = new JsonMessage {
var msg = new JsonMessage {
Id = this.Id,
DefinitionsVersion = this.DefinitionsVersion,
Timestamp = this.Timestamp,
@ -156,15 +156,14 @@ namespace NoSoliciting {
}
public static class ChatTypeExt {
//private const ushort THRESHOLD = 127;
private const ushort CLEAR_7 = ~(~0 << 7);
private const ushort Clear7 = ~(~0 << 7);
public static ChatType FromCode(ushort code) {
return (ChatType)(code & CLEAR_7);
return (ChatType)(code & Clear7);
}
public static ChatType FromDalamud(XivChatType type) {
return ChatTypeExt.FromCode((ushort)type);
return FromCode((ushort)type);
}
public static bool IsBattle(this ChatType type) {

View File

@ -0,0 +1,5 @@
namespace NoSoliciting.Ml {
public class Manifest {
public uint Version { get; set; }
}
}

View File

@ -0,0 +1,5 @@
namespace NoSoliciting.Ml {
public class MlFilter {
}
}

View File

@ -81,11 +81,13 @@
<Compile Include="Definitions.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Message.cs" />
<Compile Include="Ml\Manifest.cs" />
<Compile Include="Ml\MlFilter.cs" />
<Compile Include="PartyFinder.cs" />
<Compile Include="PFPacket.cs" />
<Compile Include="PfPacket.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="PluginConfiguration.cs" />
<Compile Include="PluginUI.cs" />
<Compile Include="PluginUi.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Filter.Chat.cs" />
<Compile Include="Filter.cs" />

View File

@ -1,24 +1,25 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace NoSoliciting {
public static class PacketInfo {
public static readonly int packetSize = Marshal.SizeOf<PFPacket>();
public static readonly int PacketSize = Marshal.SizeOf<PfPacket>();
}
[StructLayout(LayoutKind.Sequential)]
public struct PFPacket {
public struct PfPacket {
private readonly int unk0;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] padding1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public PFListing[] listings;
public PfListing[] listings;
}
[StructLayout(LayoutKind.Sequential)]
public struct PFListing {
public struct PfListing {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header1;
@ -39,7 +40,7 @@ namespace NoSoliciting {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header4;
internal readonly ushort duty;
private readonly ushort duty;
internal readonly byte dutyType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
@ -57,9 +58,9 @@ namespace NoSoliciting {
internal readonly byte lootRules;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
internal readonly byte[] header7; // all zero in every pf I've examined
private readonly byte[] header7; // all zero in every pf I've examined
internal readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
private readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
internal readonly ushort secondsRemaining;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
@ -94,8 +95,8 @@ namespace NoSoliciting {
// 160 (0xA0) with name (32 bytes/0x20)
// 352 (0x160) with both (192 bytes/0xC0)
private static string HandleString(byte[] bytes) {
byte[] nonNull = bytes.TakeWhile(b => b != 0).ToArray();
private static string HandleString(IEnumerable<byte> bytes) {
var nonNull = bytes.TakeWhile(b => b != 0).ToArray();
return Encoding.UTF8.GetString(nonNull);
}

View File

@ -10,7 +10,7 @@ namespace NoSoliciting {
public string Name => "NoSoliciting";
private PluginUI ui;
private PluginUi ui;
private Filter filter;
public DalamudPluginInterface Interface { get; private set; }
@ -25,7 +25,7 @@ namespace NoSoliciting {
public void Initialize(DalamudPluginInterface pluginInterface) {
this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null");
this.ui = new PluginUI(this);
this.ui = new PluginUi(this);
this.Config = this.Interface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
this.Config.Initialise(this.Interface);
@ -52,7 +52,7 @@ namespace NoSoliciting {
internal void UpdateDefinitions() {
Task.Run(async () => {
Definitions defs = await Definitions.UpdateAndCache(this).ConfigureAwait(true);
var defs = await Definitions.UpdateAndCache(this).ConfigureAwait(true);
// this shouldn't be possible, but what do I know
if (defs != null) {
defs.Initialise(this);

View File

@ -13,19 +13,27 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NoSoliciting {
public class PluginUI {
private readonly Plugin plugin;
private bool resizeWindow;
private ReportStatus lastReportStatus = ReportStatus.None;
public class PluginUi {
private Plugin Plugin { get; }
private bool _resizeWindow;
private ReportStatus LastReportStatus { get; set; } = ReportStatus.None;
private bool _showSettings;
public bool ShowSettings { get => this._showSettings; set => this._showSettings = value; }
private bool ShowSettings {
get => this._showSettings;
set => this._showSettings = value;
}
private bool _showReporting;
public bool ShowReporting { get => this._showReporting; set => this._showReporting = value; }
public PluginUI(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
private bool ShowReporting {
get => this._showReporting;
set => this._showReporting = value;
}
public PluginUi(Plugin plugin) {
this.Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
}
public void OpenSettings(object sender, EventArgs e) {
@ -42,125 +50,130 @@ namespace NoSoliciting {
}
}
public void DrawSettings() {
if (this.resizeWindow) {
this.resizeWindow = false;
ImGui.SetNextWindowSize(new Vector2(this.plugin.Config.AdvancedMode ? 650 : 0, 0));
private void DrawSettings() {
if (this._resizeWindow) {
this._resizeWindow = false;
ImGui.SetNextWindowSize(new Vector2(this.Plugin.Config.AdvancedMode ? 650 : 0, 0));
} else {
ImGui.SetNextWindowSize(new Vector2(0, 0), ImGuiCond.FirstUseEver);
}
if (ImGui.Begin($"{this.plugin.Name} settings", ref this._showSettings)) {
if (this.plugin.Config.AdvancedMode) {
this.DrawAdvancedSettings();
} else {
this.DrawBasicSettings();
}
ImGui.Separator();
bool advanced = this.plugin.Config.AdvancedMode;
if (ImGui.Checkbox("Advanced mode", ref advanced)) {
this.plugin.Config.AdvancedMode = advanced;
this.plugin.Config.Save();
resizeWindow = true;
}
ImGui.SameLine();
if (ImGui.Button("Show reporting window")) {
this.ShowReporting = true;
}
ImGui.End();
}
}
private void DrawBasicSettings() {
if (this.plugin.Definitions == null) {
if (!ImGui.Begin($"{this.Plugin.Name} settings", ref this._showSettings)) {
return;
}
this.DrawCheckboxes(this.plugin.Definitions.Chat.Values, true, "chat");
if (this.Plugin.Config.AdvancedMode) {
this.DrawAdvancedSettings();
} else {
this.DrawBasicSettings();
}
ImGui.Separator();
this.DrawCheckboxes(this.plugin.Definitions.PartyFinder.Values, true, "Party Finder");
var advanced = this.Plugin.Config.AdvancedMode;
if (ImGui.Checkbox("Advanced mode", ref advanced)) {
this.Plugin.Config.AdvancedMode = advanced;
this.Plugin.Config.Save();
this._resizeWindow = true;
}
bool filterHugeItemLevelPFs = this.plugin.Config.FilterHugeItemLevelPFs;
ImGui.SameLine();
if (ImGui.Button("Show reporting window")) {
this.ShowReporting = true;
}
ImGui.End();
}
private void DrawBasicSettings() {
if (this.Plugin.Definitions == null) {
return;
}
this.DrawCheckboxes(this.Plugin.Definitions.Chat.Values, true, "chat");
ImGui.Separator();
this.DrawCheckboxes(this.Plugin.Definitions.PartyFinder.Values, true, "Party Finder");
var filterHugeItemLevelPFs = this.Plugin.Config.FilterHugeItemLevelPFs;
// ReSharper disable once InvertIf
if (ImGui.Checkbox("Filter PFs with item level above maximum", ref filterHugeItemLevelPFs)) {
this.plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
this.plugin.Config.Save();
this.Plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
this.Plugin.Config.Save();
}
}
private void DrawAdvancedSettings() {
if (ImGui.BeginTabBar("##nosoliciting-tabs")) {
if (this.plugin.Definitions != null) {
if (ImGui.BeginTabItem("Chat")) {
this.DrawCheckboxes(this.plugin.Definitions.Chat.Values, false, "chat");
bool customChat = this.plugin.Config.CustomChatFilter;
if (ImGui.Checkbox("Enable custom chat filters", ref customChat)) {
this.plugin.Config.CustomChatFilter = customChat;
this.plugin.Config.Save();
}
if (this.plugin.Config.CustomChatFilter) {
List<string> substrings = this.plugin.Config.ChatSubstrings;
List<string> regexes = this.plugin.Config.ChatRegexes;
this.DrawCustom("chat", ref substrings, ref regexes);
}
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Party Finder")) {
this.DrawCheckboxes(this.plugin.Definitions.PartyFinder.Values, false, "Party Finder");
bool filterHugeItemLevelPFs = this.plugin.Config.FilterHugeItemLevelPFs;
if (ImGui.Checkbox("Enable built-in maximum item level filter", ref filterHugeItemLevelPFs)) {
this.plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
this.plugin.Config.Save();
}
bool customPF = this.plugin.Config.CustomPFFilter;
if (ImGui.Checkbox("Enable custom Party Finder filters", ref customPF)) {
this.plugin.Config.CustomPFFilter = customPF;
this.plugin.Config.Save();
}
if (this.plugin.Config.CustomPFFilter) {
List<string> substrings = this.plugin.Config.PFSubstrings;
List<string> regexes = this.plugin.Config.PFRegexes;
this.DrawCustom("pf", ref substrings, ref regexes);
}
ImGui.EndTabItem();
}
}
if (ImGui.BeginTabItem("Definitions")) {
if (this.plugin.Definitions != null) {
ImGui.Text($"Version: {this.plugin.Definitions.Version}");
}
if (Definitions.LastUpdate != null) {
ImGui.Text($"Last update: {Definitions.LastUpdate}");
}
string error = Definitions.LastError;
if (error != null) {
ImGui.Text($"Last error: {error}");
}
if (ImGui.Button("Update definitions")) {
this.plugin.UpdateDefinitions();
}
}
ImGui.EndTabBar();
if (!ImGui.BeginTabBar("##nosoliciting-tabs")) {
return;
}
if (this.Plugin.Definitions != null) {
if (ImGui.BeginTabItem("Chat")) {
this.DrawCheckboxes(this.Plugin.Definitions.Chat.Values, false, "chat");
var customChat = this.Plugin.Config.CustomChatFilter;
if (ImGui.Checkbox("Enable custom chat filters", ref customChat)) {
this.Plugin.Config.CustomChatFilter = customChat;
this.Plugin.Config.Save();
}
if (this.Plugin.Config.CustomChatFilter) {
var substrings = this.Plugin.Config.ChatSubstrings;
var regexes = this.Plugin.Config.ChatRegexes;
this.DrawCustom("chat", ref substrings, ref regexes);
}
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Party Finder")) {
this.DrawCheckboxes(this.Plugin.Definitions.PartyFinder.Values, false, "Party Finder");
var filterHugeItemLevelPFs = this.Plugin.Config.FilterHugeItemLevelPFs;
if (ImGui.Checkbox("Enable built-in maximum item level filter", ref filterHugeItemLevelPFs)) {
this.Plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
this.Plugin.Config.Save();
}
var customPf = this.Plugin.Config.CustomPFFilter;
if (ImGui.Checkbox("Enable custom Party Finder filters", ref customPf)) {
this.Plugin.Config.CustomPFFilter = customPf;
this.Plugin.Config.Save();
}
if (this.Plugin.Config.CustomPFFilter) {
var substrings = this.Plugin.Config.PFSubstrings;
var regexes = this.Plugin.Config.PFRegexes;
this.DrawCustom("pf", ref substrings, ref regexes);
}
ImGui.EndTabItem();
}
}
if (ImGui.BeginTabItem("Definitions")) {
if (this.Plugin.Definitions != null) {
ImGui.Text($"Version: {this.Plugin.Definitions.Version}");
}
if (Definitions.LastUpdate != null) {
ImGui.Text($"Last update: {Definitions.LastUpdate}");
}
var error = Definitions.LastError;
if (error != null) {
ImGui.Text($"Last error: {error}");
}
if (ImGui.Button("Update definitions")) {
this.Plugin.UpdateDefinitions();
}
}
ImGui.EndTabBar();
}
private void DrawCustom(string name, ref List<string> substrings, ref List<string> regexes) {
@ -168,8 +181,8 @@ namespace NoSoliciting {
ImGui.Text("Substrings to filter");
if (ImGui.BeginChild($"##{name}-substrings", new Vector2(0, 175))) {
for (int i = 0; i < substrings.Count; i++) {
string input = substrings[i];
for (var i = 0; i < substrings.Count; i++) {
var input = substrings[i];
if (ImGui.InputText($"##{name}-substring-{i}", ref input, 1_000)) {
if (input.Length != 0) {
substrings[i] = input;
@ -199,10 +212,10 @@ namespace NoSoliciting {
ImGui.Text("Regular expressions to filter");
if (ImGui.BeginChild($"##{name}-regexes", new Vector2(0, 175))) {
for (int i = 0; i < regexes.Count; i++) {
string input = regexes[i];
for (var i = 0; i < regexes.Count; i++) {
var input = regexes[i];
if (ImGui.InputText($"##{name}-regex-{i}", ref input, 1_000)) {
bool valid = true;
var valid = true;
try {
_ = new Regex(input);
} catch (ArgumentException) {
@ -235,20 +248,22 @@ namespace NoSoliciting {
ImGui.Columns(1);
// ReSharper disable once InvertIf
if (ImGui.Button($"Save filters##{name}-save")) {
this.plugin.Config.Save();
this.plugin.Config.CompileRegexes();
this.Plugin.Config.Save();
this.Plugin.Config.CompileRegexes();
}
}
private void DrawCheckboxes(IEnumerable<Definition> defs, bool basic, string labelFillIn) {
foreach (Definition def in defs) {
this.plugin.Config.FilterStatus.TryGetValue(def.Id, out bool enabled);
string label = basic ? def.Option.Basic : def.Option.Advanced;
foreach (var def in defs) {
this.Plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled);
var label = basic ? def.Option.Basic : def.Option.Advanced;
label = label.Replace("{}", labelFillIn);
// ReSharper disable once InvertIf
if (ImGui.Checkbox(label, ref enabled)) {
this.plugin.Config.FilterStatus[def.Id] = enabled;
this.plugin.Config.Save();
this.Plugin.Config.FilterStatus[def.Id] = enabled;
this.Plugin.Config.Save();
}
}
}
@ -262,8 +277,8 @@ namespace NoSoliciting {
ImGui.Text("Click on one of the entries below to report it to the developer as miscategorised.");
if (this.lastReportStatus != ReportStatus.None) {
string status = this.lastReportStatus switch {
if (this.LastReportStatus != ReportStatus.None) {
var status = this.LastReportStatus switch {
ReportStatus.Failure => "failed to send",
ReportStatus.Successful => "sent successfully",
ReportStatus.InProgress => "sending",
@ -285,12 +300,12 @@ namespace NoSoliciting {
AddColumn(maxSizes, "Timestamp", "Channel", "Reason", "Sender", "Message");
ImGui.Separator();
foreach (Message message in this.plugin.MessageHistory) {
foreach (var message in this.Plugin.MessageHistory) {
if (message.FilterReason != null) {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
}
string sender = message.Sender.Payloads
var sender = message.Sender.Payloads
.Where(payload => payload.Type == PayloadType.RawText)
.Cast<TextPayload>()
.Select(payload => payload.Text)
@ -307,7 +322,7 @@ namespace NoSoliciting {
this.SetUpReportModal(message);
}
for (int idx = 0; idx < maxSizes.Length; idx++) {
for (var idx = 0; idx < maxSizes.Length; idx++) {
ImGui.SetColumnWidth(idx, maxSizes[idx] + ImGui.GetStyle().ItemSpacing.X * 2);
}
@ -328,12 +343,12 @@ namespace NoSoliciting {
AddColumn(maxSizes, "Timestamp", "Reason", "Host", "Description");
ImGui.Separator();
foreach (Message message in this.plugin.PartyFinderHistory) {
foreach (var message in this.Plugin.PartyFinderHistory) {
if (message.FilterReason != null) {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
}
string sender = message.Sender.Payloads
var sender = message.Sender.Payloads
.Where(payload => payload.Type == PayloadType.RawText)
.Cast<TextPayload>()
.Select(payload => payload.Text)
@ -350,7 +365,7 @@ namespace NoSoliciting {
this.SetUpReportModal(message);
}
for (int idx = 0; idx < maxSizes.Length; idx++) {
for (var idx = 0; idx < maxSizes.Length; idx++) {
ImGui.SetColumnWidth(idx, maxSizes[idx] + ImGui.GetStyle().ItemSpacing.X * 2);
}
@ -397,12 +412,14 @@ namespace NoSoliciting {
Task.Run(async () => {
string resp = null;
try {
using WebClient client = new WebClient();
this.lastReportStatus = ReportStatus.InProgress;
resp = await client.UploadStringTaskAsync(this.plugin.Definitions.ReportUrl, message.ToJson()).ConfigureAwait(true);
} catch (Exception) { }
using var client = new WebClient();
this.LastReportStatus = ReportStatus.InProgress;
resp = await client.UploadStringTaskAsync(this.Plugin.Definitions.ReportUrl, message.ToJson()).ConfigureAwait(true);
} catch (Exception) {
// ignored
}
this.lastReportStatus = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
this.LastReportStatus = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
PluginLog.Log($"Report sent. Response: {resp}");
});
ImGui.CloseCurrentPopup();
@ -427,12 +444,12 @@ namespace NoSoliciting {
InProgress = 2,
}
private static bool AddColumn(float[] maxSizes, params string[] args) {
bool clicked = false;
private static bool AddColumn(IList<float> maxSizes, params string[] args) {
var clicked = false;
for (int i = 0; i < args.Length; i++) {
string arg = args[i];
bool last = i == args.Length - 1;
for (var i = 0; i < args.Length; i++) {
var arg = args[i];
var last = i == args.Length - 1;
if (last) {
ImGui.PushTextWrapPos();