refactor: use new Dalamud PF event

This commit is contained in:
Anna 2021-03-31 09:47:17 -04:00
parent a83bb75e21
commit a3ce973dda
7 changed files with 13 additions and 459 deletions

View File

@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Dalamud, Version=5.2.3.3, Culture=neutral, PublicKeyToken=null">
<Reference Include="Dalamud, Version=5.2.3.5, Culture=neutral, PublicKeyToken=null">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
@ -35,7 +35,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="1.2.0"/>
<PackageReference Include="DalamudPackager" Version="1.2.0" />
</ItemGroup>
</Project>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Dalamud.Configuration;
using Dalamud.Game.Internal.Gui.Structs;
namespace BetterPartyFinder {
public class Configuration : IPluginConfiguration {

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Gui.Structs;
namespace BetterPartyFinder {
public class Filter : IDisposable {
@ -9,7 +11,11 @@ namespace BetterPartyFinder {
internal Filter(Plugin plugin) {
this.Plugin = plugin;
this.Plugin.Functions.ReceivePartyFinderListing += this.ReceiveListing;
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.ReceiveListing;
}
public void Dispose() {
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.ReceiveListing;
}
private void ReceiveListing(PartyFinderListing listing, PartyFinderListingEventArgs args) {
@ -142,16 +148,12 @@ namespace BetterPartyFinder {
// filter based on player
if (filter.Players.Count > 0) {
if (filter.Players.Any(info => info.Name == listing.Name && info.World == listing.HomeWorld.Value.RowId)) {
if (filter.Players.Any(info => info.Name == listing.Name.TextValue && info.World == listing.HomeWorld.Value.RowId)) {
return false;
}
}
return true;
}
public void Dispose() {
this.Plugin.Functions.ReceivePartyFinderListing -= this.ReceiveListing;
}
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Dalamud.Hooking;
using Dalamud.Plugin;
namespace BetterPartyFinder {
public class GameFunctions : IDisposable {
@ -14,18 +13,6 @@ namespace BetterPartyFinder {
#endregion
#region PF Listings events
internal delegate void PartyFinderListingEventDelegate(PartyFinderListing listing, PartyFinderListingEventArgs args);
internal event PartyFinderListingEventDelegate? ReceivePartyFinderListing;
private delegate void HandlePfPacketDelegate(IntPtr param1, IntPtr data);
private readonly Hook<HandlePfPacketDelegate> _handlePacketHook;
#endregion
private Plugin Plugin { get; }
private IntPtr PartyFinderAgent { get; set; } = IntPtr.Zero;
@ -33,19 +20,14 @@ namespace BetterPartyFinder {
this.Plugin = plugin;
var requestPfPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 40 0F 10 81 ?? ?? ?? ??");
var listingPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("40 53 41 57 48 83 EC 28 48 8B D9");
this._requestPartyFinderListings = Marshal.GetDelegateForFunctionPointer<RequestPartyFinderListingsDelegate>(requestPfPtr);
this._requestPfListingsHook = new Hook<RequestPartyFinderListingsDelegate>(requestPfPtr, new RequestPartyFinderListingsDelegate(this.OnRequestPartyFinderListings));
this._requestPfListingsHook.Enable();
this._handlePacketHook = new Hook<HandlePfPacketDelegate>(listingPtr, new HandlePfPacketDelegate(this.PacketDetour));
this._handlePacketHook.Enable();
}
public void Dispose() {
this._requestPfListingsHook.Dispose();
this._handlePacketHook.Dispose();
}
private byte OnRequestPartyFinderListings(IntPtr agent, byte categoryIdx) {
@ -69,71 +51,5 @@ namespace BetterPartyFinder {
var categoryIdx = Marshal.ReadByte(this.PartyFinderAgent + categoryOffset);
this._requestPartyFinderListings(this.PartyFinderAgent, categoryIdx);
}
private void PacketDetour(IntPtr param1, IntPtr data) {
if (data == IntPtr.Zero) {
goto Return;
}
try {
this.OnPacket(data);
} catch (Exception ex) {
PluginLog.Error(ex, "Unhandled exception in PF packet detour");
}
Return:
this._handlePacketHook!.Original(param1, data);
}
private void OnPacket(IntPtr data) {
var dataPtr = data + 0x10;
// parse the packet into a struct
var packet = Marshal.PtrToStructure<PfPacket>(dataPtr);
var needToRewrite = false;
for (var i = 0; i < packet.listings.Length; i++) {
if (packet.listings[i].IsNull()) {
continue;
}
// invoke event for each non-null listing
var listing = new PartyFinderListing(packet.listings[i], this.Plugin.Interface.Data);
var args = new PartyFinderListingEventArgs();
this.ReceivePartyFinderListing?.Invoke(listing, args);
if (args.Visible) {
continue;
}
// zero the listing if it shouldn't be visible
packet.listings[i] = new PfListing();
needToRewrite = true;
}
if (!needToRewrite) {
return;
}
// get some memory for writing to
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);
// free memory
pinnedArray.Free();
}
}
internal class PartyFinderListingEventArgs {
public bool Visible { get; set; } = true;
}
}

View File

@ -1,242 +0,0 @@
using Dalamud.Data;
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace BetterPartyFinder {
public class PartyFinderListing {
public uint Id { get; }
public string Name { get; }
public string Description { get; }
public Lazy<World> World { get; }
public Lazy<World> HomeWorld { get; }
public Lazy<World> CurrentWorld { get; }
public Category Category { get; }
public ushort RawDuty { get; }
public Lazy<ContentFinderCondition> Duty { get; }
public DutyType DutyType { get; }
public bool BeginnersWelcome { get; }
public ushort SecondsRemaining { get; }
public ushort MinimumItemLevel { get; }
public byte Parties { get; }
public byte SlotsAvailable { get; }
public IEnumerable<PartyFinderSlot> Slots => this._slots;
private readonly byte _objective;
public ObjectiveFlags Objective => (ObjectiveFlags) this._objective;
private readonly byte _conditions;
public ConditionFlags Conditions => (ConditionFlags) this._conditions;
private readonly byte _dutyFinderSettings;
public DutyFinderSettingsFlags DutyFinderSettings => (DutyFinderSettingsFlags) this._dutyFinderSettings;
private readonly byte _lootRules;
public LootRuleFlags LootRules => (LootRuleFlags) this._lootRules;
private readonly byte _searchArea;
public SearchAreaFlags SearchArea => (SearchAreaFlags) this._searchArea;
private readonly PartyFinderSlot[] _slots;
private readonly byte[] _jobsPresent;
internal IEnumerable<byte> RawJobsPresent => this._jobsPresent;
internal IReadOnlyCollection<Lazy<ClassJob?>> JobsPresent { get; }
public bool this[ObjectiveFlags flag] => this._objective == 0 || (this._objective & (uint) flag) > 0;
public bool this[ConditionFlags flag] => this._conditions == 0 || (this._conditions & (uint) flag) > 0;
public bool this[DutyFinderSettingsFlags flag] => this._dutyFinderSettings == 0 || (this._dutyFinderSettings & (uint) flag) > 0;
public bool this[LootRuleFlags flag] => this._lootRules == 0 || (this._lootRules & (uint) flag) > 0;
public bool this[SearchAreaFlags flag] => this._searchArea == 0 || (this._searchArea & (uint) flag) > 0;
internal PartyFinderListing(PfListing listing, DataManager dataManager) {
this.Id = listing.id;
this.Name = listing.Name();
this.Description = listing.Description();
this.World = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.world));
this.HomeWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.homeWorld));
this.CurrentWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.currentWorld));
this.Category = (Category) listing.category;
this.RawDuty = listing.duty;
this.Duty = new Lazy<ContentFinderCondition>(() => dataManager.GetExcelSheet<ContentFinderCondition>().GetRow(listing.duty));
this.DutyType = (DutyType) listing.dutyType;
this.BeginnersWelcome = listing.beginnersWelcome == 1;
this.SecondsRemaining = listing.secondsRemaining;
this.MinimumItemLevel = listing.minimumItemLevel;
this.Parties = listing.numParties;
this.SlotsAvailable = listing.numSlots;
this._objective = listing.objective;
this._conditions = listing.conditions;
this._dutyFinderSettings = listing.dutyFinderSettings;
this._lootRules = listing.lootRules;
this._searchArea = listing.searchArea;
this._slots = listing.slots.Select(accepting => new PartyFinderSlot(accepting)).ToArray();
this._jobsPresent = listing.jobsPresent;
this.JobsPresent = this._jobsPresent
.Select(id => new Lazy<ClassJob?>(() => id == 0
? null
: dataManager.GetExcelSheet<ClassJob>().GetRow(id)))
.ToArray();
}
}
public class PartyFinderSlot {
private readonly uint _accepting;
private JobFlags[]? _listAccepting;
public IReadOnlyCollection<JobFlags> Accepting {
get {
if (this._listAccepting != null) {
return this._listAccepting;
}
this._listAccepting = Enum.GetValues(typeof(JobFlags))
.Cast<JobFlags>()
.Where(flag => this[flag])
.ToArray();
return this._listAccepting;
}
}
public bool this[JobFlags flag] => (this._accepting & (uint) flag) > 0;
internal PartyFinderSlot(uint accepting) {
this._accepting = accepting;
}
}
[Flags]
public enum SearchAreaFlags : uint {
DataCentre = 1 << 0,
Private = 1 << 1,
AllianceRaid = 1 << 2,
World = 1 << 3,
OnePlayerPerJob = 1 << 5,
}
[Flags]
public enum JobFlags {
Gladiator = 1 << 1,
Pugilist = 1 << 2,
Marauder = 1 << 3,
Lancer = 1 << 4,
Archer = 1 << 5,
Conjurer = 1 << 6,
Thaumaturge = 1 << 7,
Paladin = 1 << 8,
Monk = 1 << 9,
Warrior = 1 << 10,
Dragoon = 1 << 11,
Bard = 1 << 12,
WhiteMage = 1 << 13,
BlackMage = 1 << 14,
Arcanist = 1 << 15,
Summoner = 1 << 16,
Scholar = 1 << 17,
Rogue = 1 << 18,
Ninja = 1 << 19,
Machinist = 1 << 20,
DarkKnight = 1 << 21,
Astrologian = 1 << 22,
Samurai = 1 << 23,
RedMage = 1 << 24,
BlueMage = 1 << 25,
Gunbreaker = 1 << 26,
Dancer = 1 << 27,
}
internal static class JobFlagsExt {
internal static ClassJob? ClassJob(this JobFlags job, DataManager data) {
var jobs = data.GetExcelSheet<ClassJob>();
uint? row = job switch {
JobFlags.Gladiator => 1,
JobFlags.Pugilist => 2,
JobFlags.Marauder => 3,
JobFlags.Lancer => 4,
JobFlags.Archer => 5,
JobFlags.Conjurer => 6,
JobFlags.Thaumaturge => 7,
JobFlags.Paladin => 19,
JobFlags.Monk => 20,
JobFlags.Warrior => 21,
JobFlags.Dragoon => 22,
JobFlags.Bard => 23,
JobFlags.WhiteMage => 24,
JobFlags.BlackMage => 25,
JobFlags.Arcanist => 26,
JobFlags.Summoner => 27,
JobFlags.Scholar => 28,
JobFlags.Rogue => 29,
JobFlags.Ninja => 30,
JobFlags.Machinist => 31,
JobFlags.DarkKnight => 32,
JobFlags.Astrologian => 33,
JobFlags.Samurai => 34,
JobFlags.RedMage => 35,
JobFlags.BlueMage => 36,
JobFlags.Gunbreaker => 37,
JobFlags.Dancer => 38,
_ => null,
};
return row == null ? null : jobs.GetRow((uint) row);
}
}
[Flags]
public enum ObjectiveFlags : uint {
None = 0,
DutyCompletion = 1,
Practice = 2,
Loot = 4,
}
[Flags]
public enum ConditionFlags : uint {
None = 1,
DutyComplete = 2,
DutyIncomplete = 4,
}
[Flags]
public enum DutyFinderSettingsFlags : uint {
None = 0,
UndersizedParty = 1 << 0,
MinimumItemLevel = 1 << 1,
SilenceEcho = 1 << 2,
}
[Flags]
public enum LootRuleFlags : uint {
None = 0,
GreedOnly = 1,
Lootmaster = 2,
}
public enum Category {
Duty = 0,
QuestBattles = 1 << 0,
Fates = 1 << 1,
TreasureHunt = 1 << 2,
TheHunt = 1 << 3,
GatheringForays = 1 << 4,
DeepDungeons = 1 << 5,
AdventuringForays = 1 << 6,
}
public enum DutyType {
Other = 0,
Roulette = 1 << 0,
Normal = 1 << 1,
}
}

View File

@ -1,125 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace BetterPartyFinder {
public static class PacketInfo {
public static readonly int PacketSize = Marshal.SizeOf<PfPacket>();
}
[StructLayout(LayoutKind.Sequential)]
public readonly struct PfPacket {
private readonly int unk0;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] padding1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public readonly PfListing[] listings;
}
[StructLayout(LayoutKind.Sequential)]
public readonly struct PfListing {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header1;
internal readonly uint id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header2;
private readonly uint unknownInt1;
private readonly ushort unknownShort1;
private readonly ushort unknownShort2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
private readonly byte[] header3;
internal readonly byte category;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header4;
internal readonly ushort duty;
internal readonly byte dutyType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
private readonly byte[] header5;
internal readonly ushort world;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] header6;
internal readonly byte objective;
internal readonly byte beginnersWelcome;
internal readonly byte conditions;
internal readonly byte dutyFinderSettings;
internal readonly byte lootRules;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header7; // all zero in every pf I've examined
private readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
internal readonly ushort secondsRemaining;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
private readonly byte[] header8; // 00 00 01 00 00 00 in every pf I've examined
internal readonly ushort minimumItemLevel;
internal readonly ushort homeWorld;
internal readonly ushort currentWorld;
private readonly byte header9;
internal readonly byte numSlots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header10;
internal readonly byte searchArea;
private readonly byte header11;
internal readonly byte numParties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header12; // 00 00 00 always. maybe numParties is a u32?
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly uint[] slots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly byte[] jobsPresent;
// Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
private readonly byte[] name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
private readonly byte[] description;
// 128 (0x80) before name and desc
// 160 (0xA0) with name (32 bytes/0x20)
// 352 (0x160) with both (192 bytes/0xC0)
private static string HandleString(IEnumerable<byte> bytes) {
var nonNull = bytes.TakeWhile(b => b != 0).ToArray();
return Encoding.UTF8.GetString(nonNull);
}
internal string Name() {
return HandleString(this.name);
}
internal string Description() {
return HandleString(this.description);
}
internal bool IsNull() {
// a valid party finder must have at least one slot set
return this.slots.All(slot => slot == 0);
}
}
}

View File

@ -3,9 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Data;
using Dalamud.Game.Internal.Gui.Structs;
using Dalamud.Interface;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Addon = Lumina.Excel.GeneratedSheets.Addon;
using GameAddon = Dalamud.Game.Internal.Gui.Addon.Addon;
namespace BetterPartyFinder {