StyleCop: everything else

This commit is contained in:
Raymond Lynch 2021-05-30 07:10:00 -04:00
parent f64c9b8321
commit 595fd3f1e4
134 changed files with 16346 additions and 6202 deletions

View file

@ -1,16 +1,21 @@
using System.Collections.Generic;
using Dalamud.Game.Network.Structures;
namespace Dalamud.Game.Network {
internal class MarketBoardItemRequest {
namespace Dalamud.Game.Network
{
internal class MarketBoardItemRequest
{
public uint CatalogId { get; set; }
public byte AmountToArrive { get; set; }
public List<MarketBoardCurrentOfferings.MarketBoardItemListing> Listings { get; set; }
public List<MarketBoardHistory.MarketBoardHistoryListing> History { get; set; }
public int ListingsRequestId { get; set; } = -1;
public bool IsDone => Listings.Count == AmountToArrive && History.Count != 0;
public bool IsDone => this.Listings.Count == this.AmountToArrive && this.History.Count != 0;
}
}

View file

@ -1,8 +1,22 @@
using Dalamud.Game.Network.Structures;
namespace Dalamud.Game.Network.MarketBoardUploaders {
internal interface IMarketBoardUploader {
void Upload(MarketBoardItemRequest itemRequest);
namespace Dalamud.Game.Network.MarketBoardUploaders
{
/// <summary>
/// An interface binding for the Universalis uploader.
/// </summary>
internal interface IMarketBoardUploader
{
/// <summary>
/// Upload data about an item.
/// </summary>
/// <param name="item">The item request data being uploaded.</param>
void Upload(MarketBoardItemRequest item);
/// <summary>
/// Upload tax rate data.
/// </summary>
/// <param name="taxRates">The tax rate data being uploaded.</param>
void UploadTax(MarketTaxRates taxRates);
}
}

View file

@ -1,28 +1,57 @@
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis {
internal class UniversalisHistoryEntry {
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisHistoryEntry
{
/// <summary>
/// Gets or sets a value indicating whether the item is HQ or not.
/// </summary>
[JsonProperty("hq")]
public bool Hq { get; set; }
/// <summary>
/// Gets or sets the item price per unit.
/// </summary>
[JsonProperty("pricePerUnit")]
public uint PricePerUnit { get; set; }
/// <summary>
/// Gets or sets the quantity of items available.
/// </summary>
[JsonProperty("quantity")]
public uint Quantity { get; set; }
/// <summary>
/// Gets or sets the name of the buyer.
/// </summary>
[JsonProperty("buyerName")]
public string BuyerName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this item was on a mannequin.
/// </summary>
[JsonProperty("onMannequin")]
public bool OnMannequin { get; set; }
/// <summary>
/// Gets or sets the seller ID.
/// </summary>
[JsonProperty("sellerID")]
public string SellerId { get; set; }
/// <summary>
/// Gets or sets the buyer ID.
/// </summary>
[JsonProperty("buyerID")]
public string BuyerId { get; set; }
/// <summary>
/// Gets or sets the timestamp of the transaction.
/// </summary>
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
}

View file

@ -1,17 +1,35 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis {
internal class UniversalisHistoryUploadRequest {
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisHistoryUploadRequest
{
/// <summary>
/// Gets or sets the world ID.
/// </summary>
[JsonProperty("worldID")]
public uint WorldId { get; set; }
/// <summary>
/// Gets or sets the item ID.
/// </summary>
[JsonProperty("itemID")]
public uint ItemId { get; set; }
/// <summary>
/// Gets or sets the list of available entries.
/// </summary>
[JsonProperty("entries")]
public List<UniversalisHistoryEntry> Entries { get; set; }
/// <summary>
/// Gets or sets the uploader ID.
/// </summary>
[JsonProperty("uploaderID")]
public string UploaderId { get; set; }
}

View file

@ -1,47 +1,95 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis {
internal class UniversalisItemListingsEntry {
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisItemListingsEntry
{
/// <summary>
/// Gets or sets the listing ID.
/// </summary>
[JsonProperty("listingID")]
public string ListingId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the item is HQ.
/// </summary>
[JsonProperty("hq")]
public bool Hq { get; set; }
/// <summary>
/// Gets or sets the item price per unit.
/// </summary>
[JsonProperty("pricePerUnit")]
public uint PricePerUnit { get; set; }
/// <summary>
/// Gets or sets the item quantity.
/// </summary>
[JsonProperty("quantity")]
public uint Quantity { get; set; }
/// <summary>
/// Gets or sets the name of the retainer selling the item.
/// </summary>
[JsonProperty("retainerName")]
public string RetainerName { get; set; }
/// <summary>
/// Gets or sets the ID of the retainer selling the item.
/// </summary>
[JsonProperty("retainerID")]
public string RetainerId { get; set; }
/// <summary>
/// Gets or sets the name of the user who created the entry.
/// </summary>
[JsonProperty("creatorName")]
public string CreatorName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the item is on a mannequin.
/// </summary>
[JsonProperty("onMannequin")]
public bool OnMannequin { get; set; }
/// <summary>
/// Gets or sets the seller ID.
/// </summary>
[JsonProperty("sellerID")]
public string SellerId { get; set; }
/// <summary>
/// Gets or sets the ID of the user who created the entry.
/// </summary>
[JsonProperty("creatorID")]
public string CreatorId { get; set; }
/// <summary>
/// Gets or sets the ID of the dye on the item.
/// </summary>
[JsonProperty("stainID")]
public int StainId { get; set; }
/// <summary>
/// Gets or sets the city where the selling retainer resides.
/// </summary>
[JsonProperty("retainerCity")]
public int RetainerCity { get; set; }
/// <summary>
/// Gets or sets the last time the entry was reviewed.
/// </summary>
[JsonProperty("lastReviewTime")]
public long LastReviewTime { get; set; }
/// <summary>
/// Gets or sets the materia attached to the item.
/// </summary>
[JsonProperty("materia")]
public List<UniversalisItemMateria> Materia { get; set; }
}

View file

@ -1,17 +1,35 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis {
internal class UniversalisItemListingsUploadRequest {
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisItemListingsUploadRequest
{
/// <summary>
/// Gets or sets the world ID.
/// </summary>
[JsonProperty("worldID")]
public uint WorldId { get; set; }
/// <summary>
/// Gets or sets the item ID.
/// </summary>
[JsonProperty("itemID")]
public uint ItemId { get; set; }
/// <summary>
/// Gets or sets the list of available items.
/// </summary>
[JsonProperty("listings")]
public List<UniversalisItemListingsEntry> Listings { get; set; }
/// <summary>
/// Gets or sets the uploader ID.
/// </summary>
[JsonProperty("uploaderID")]
public string UploaderId { get; set; }
}

View file

@ -1,10 +1,21 @@
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis {
internal class UniversalisItemMateria {
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisItemMateria
{
/// <summary>
/// Gets or sets the item slot ID.
/// </summary>
[JsonProperty("slotID")]
public int SlotId { get; set; }
/// <summary>
/// Gets or sets the materia ID.
/// </summary>
[JsonProperty("materiaID")]
public int MateriaId { get; set; }
}

View file

@ -1,117 +1,140 @@
using System;
using System.Collections.Generic;
using System.Net;
using Dalamud.Game.Network.MarketBoardUploaders;
using Dalamud.Game.Network.MarketBoardUploaders.Universalis;
using Dalamud.Game.Network.Structures;
using Newtonsoft.Json;
using Serilog;
namespace Dalamud.Game.Network.Universalis.MarketBoardUploaders {
internal class UniversalisMarketBoardUploader : IMarketBoardUploader {
namespace Dalamud.Game.Network.Universalis.MarketBoardUploaders
{
/// <summary>
/// This class represents an uploader for contributing data to Universalis.
/// </summary>
internal class UniversalisMarketBoardUploader : IMarketBoardUploader
{
private const string ApiBase = "https://universalis.app";
//private const string ApiBase = "https://127.0.0.1:443";
// private const string ApiBase = "https://127.0.0.1:443";
private const string ApiKey = "GGD6RdSfGyRiHM5WDnAo0Nj9Nv7aC5NDhMj3BebT";
private readonly Dalamud dalamud;
public UniversalisMarketBoardUploader(Dalamud dalamud) {
/// <summary>
/// Initializes a new instance of the <see cref="UniversalisMarketBoardUploader"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
public UniversalisMarketBoardUploader(Dalamud dalamud)
{
this.dalamud = dalamud;
}
public void Upload(MarketBoardItemRequest request) {
using (var client = new WebClient()) {
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
/// <inheritdoc/>
public void Upload(MarketBoardItemRequest request)
{
using var client = new WebClient();
Log.Verbose("Starting Universalis upload.");
var uploader = this.dalamud.ClientState.LocalContentId;
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var listingsRequestObject = new UniversalisItemListingsUploadRequest();
listingsRequestObject.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
listingsRequestObject.UploaderId = uploader.ToString();
listingsRequestObject.ItemId = request.CatalogId;
Log.Verbose("Starting Universalis upload.");
var uploader = this.dalamud.ClientState.LocalContentId;
listingsRequestObject.Listings = new List<UniversalisItemListingsEntry>();
foreach (var marketBoardItemListing in request.Listings) {
var universalisListing = new UniversalisItemListingsEntry {
Hq = marketBoardItemListing.IsHq,
SellerId = marketBoardItemListing.RetainerOwnerId.ToString(),
RetainerName = marketBoardItemListing.RetainerName,
RetainerId = marketBoardItemListing.RetainerId.ToString(),
CreatorId = marketBoardItemListing.ArtisanId.ToString(),
CreatorName = marketBoardItemListing.PlayerName,
OnMannequin = marketBoardItemListing.OnMannequin,
LastReviewTime = ((DateTimeOffset) marketBoardItemListing.LastReviewTime).ToUnixTimeSeconds(),
PricePerUnit = marketBoardItemListing.PricePerUnit,
Quantity = marketBoardItemListing.ItemQuantity,
RetainerCity = marketBoardItemListing.RetainerCityId
};
var listingsRequestObject = new UniversalisItemListingsUploadRequest();
listingsRequestObject.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
listingsRequestObject.UploaderId = uploader.ToString();
listingsRequestObject.ItemId = request.CatalogId;
universalisListing.Materia = new List<UniversalisItemMateria>();
foreach (var itemMateria in marketBoardItemListing.Materia)
universalisListing.Materia.Add(new UniversalisItemMateria {
MateriaId = itemMateria.MateriaId,
SlotId = itemMateria.Index
});
listingsRequestObject.Listings.Add(universalisListing);
}
var upload = JsonConvert.SerializeObject(listingsRequestObject);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", upload);
Log.Verbose(upload);
var historyRequestObject = new UniversalisHistoryUploadRequest();
historyRequestObject.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
historyRequestObject.UploaderId = uploader.ToString();
historyRequestObject.ItemId = request.CatalogId;
historyRequestObject.Entries = new List<UniversalisHistoryEntry>();
foreach (var marketBoardHistoryListing in request.History)
historyRequestObject.Entries.Add(new UniversalisHistoryEntry {
BuyerName = marketBoardHistoryListing.BuyerName,
Hq = marketBoardHistoryListing.IsHq,
OnMannequin = marketBoardHistoryListing.OnMannequin,
PricePerUnit = marketBoardHistoryListing.SalePrice,
Quantity = marketBoardHistoryListing.Quantity,
Timestamp = ((DateTimeOffset) marketBoardHistoryListing.PurchaseTime).ToUnixTimeSeconds()
});
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var historyUpload = JsonConvert.SerializeObject(historyRequestObject);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
Log.Verbose(historyUpload);
Log.Verbose("Universalis data upload for item#{0} completed.", request.CatalogId);
}
}
public void UploadTax(MarketTaxRates taxRates) {
using (var client = new WebClient())
listingsRequestObject.Listings = new List<UniversalisItemListingsEntry>();
foreach (var marketBoardItemListing in request.Listings)
{
var taxRatesRequest = new UniversalisTaxUploadRequest();
taxRatesRequest.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
taxRatesRequest.UploaderId = this.dalamud.ClientState.LocalContentId.ToString();
taxRatesRequest.TaxData = new UniversalisTaxData {
LimsaLominsa = taxRates.LimsaLominsaTax,
Gridania = taxRates.GridaniaTax,
Uldah = taxRates.UldahTax,
Ishgard = taxRates.IshgardTax,
Kugane = taxRates.KuganeTax,
Crystarium = taxRates.CrystariumTax
var universalisListing = new UniversalisItemListingsEntry
{
Hq = marketBoardItemListing.IsHq,
SellerId = marketBoardItemListing.RetainerOwnerId.ToString(),
RetainerName = marketBoardItemListing.RetainerName,
RetainerId = marketBoardItemListing.RetainerId.ToString(),
CreatorId = marketBoardItemListing.ArtisanId.ToString(),
CreatorName = marketBoardItemListing.PlayerName,
OnMannequin = marketBoardItemListing.OnMannequin,
LastReviewTime = ((DateTimeOffset)marketBoardItemListing.LastReviewTime).ToUnixTimeSeconds(),
PricePerUnit = marketBoardItemListing.PricePerUnit,
Quantity = marketBoardItemListing.ItemQuantity,
RetainerCity = marketBoardItemListing.RetainerCityId,
};
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
universalisListing.Materia = new List<UniversalisItemMateria>();
foreach (var itemMateria in marketBoardItemListing.Materia)
{
universalisListing.Materia.Add(new UniversalisItemMateria
{
MateriaId = itemMateria.MateriaId,
SlotId = itemMateria.Index,
});
}
var historyUpload = JsonConvert.SerializeObject(taxRatesRequest);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
Log.Verbose(historyUpload);
Log.Verbose("Universalis tax upload completed.");
listingsRequestObject.Listings.Add(universalisListing);
}
var upload = JsonConvert.SerializeObject(listingsRequestObject);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", upload);
Log.Verbose(upload);
var historyRequestObject = new UniversalisHistoryUploadRequest();
historyRequestObject.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
historyRequestObject.UploaderId = uploader.ToString();
historyRequestObject.ItemId = request.CatalogId;
historyRequestObject.Entries = new List<UniversalisHistoryEntry>();
foreach (var marketBoardHistoryListing in request.History)
{
historyRequestObject.Entries.Add(new UniversalisHistoryEntry
{
BuyerName = marketBoardHistoryListing.BuyerName,
Hq = marketBoardHistoryListing.IsHq,
OnMannequin = marketBoardHistoryListing.OnMannequin,
PricePerUnit = marketBoardHistoryListing.SalePrice,
Quantity = marketBoardHistoryListing.Quantity,
Timestamp = ((DateTimeOffset)marketBoardHistoryListing.PurchaseTime).ToUnixTimeSeconds(),
});
}
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var historyUpload = JsonConvert.SerializeObject(historyRequestObject);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
Log.Verbose(historyUpload);
Log.Verbose("Universalis data upload for item#{0} completed.", request.CatalogId);
}
/// <inheritdoc/>
public void UploadTax(MarketTaxRates taxRates)
{
using var client = new WebClient();
var taxRatesRequest = new UniversalisTaxUploadRequest();
taxRatesRequest.WorldId = this.dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
taxRatesRequest.UploaderId = this.dalamud.ClientState.LocalContentId.ToString();
taxRatesRequest.TaxData = new UniversalisTaxData
{
LimsaLominsa = taxRates.LimsaLominsaTax,
Gridania = taxRates.GridaniaTax,
Uldah = taxRates.UldahTax,
Ishgard = taxRates.IshgardTax,
Kugane = taxRates.KuganeTax,
Crystarium = taxRates.CrystariumTax,
};
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var historyUpload = JsonConvert.SerializeObject(taxRatesRequest);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
Log.Verbose(historyUpload);
Log.Verbose("Universalis tax upload completed.");
}
}
}

View file

@ -0,0 +1,46 @@
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisTaxData
{
/// <summary>
/// Gets or sets Limsa Lominsa's current tax rate.
/// </summary>
[JsonProperty("limsaLominsa")]
public uint LimsaLominsa { get; set; }
/// <summary>
/// Gets or sets Gridania's current tax rate.
/// </summary>
[JsonProperty("gridania")]
public uint Gridania { get; set; }
/// <summary>
/// Gets or sets Ul'dah's current tax rate.
/// </summary>
[JsonProperty("uldah")]
public uint Uldah { get; set; }
/// <summary>
/// Gets or sets Ishgard's current tax rate.
/// </summary>
[JsonProperty("ishgard")]
public uint Ishgard { get; set; }
/// <summary>
/// Gets or sets Kugane's current tax rate.
/// </summary>
[JsonProperty("kugane")]
public uint Kugane { get; set; }
/// <summary>
/// Gets or sets The Crystarium's current tax rate.
/// </summary>
[JsonProperty("crystarium")]
public uint Crystarium { get; set; }
}
}

View file

@ -1,41 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
class UniversalisTaxUploadRequest
/// <summary>
/// A Universalis API structure.
/// </summary>
internal class UniversalisTaxUploadRequest
{
/// <summary>
/// Gets or sets the uploader's ID.
/// </summary>
[JsonProperty("uploaderID")]
public string UploaderId { get; set; }
/// <summary>
/// Gets or sets the world to retrieve data from.
/// </summary>
[JsonProperty("worldID")]
public uint WorldId { get; set; }
/// <summary>
/// Gets or sets tax data for each city's market.
/// </summary>
[JsonProperty("marketTaxRates")]
public UniversalisTaxData TaxData { get; set; }
}
class UniversalisTaxData {
[JsonProperty("limsaLominsa")]
public uint LimsaLominsa { get; set; }
[JsonProperty("gridania")]
public uint Gridania { get; set; }
[JsonProperty("uldah")]
public uint Uldah { get; set; }
[JsonProperty("ishgard")]
public uint Ishgard { get; set; }
[JsonProperty("kugane")]
public uint Kugane { get; set; }
[JsonProperty("crystarium")]
public uint Crystarium { get; set; }
}
}

View file

@ -4,47 +4,58 @@ using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Dalamud.Game.Internal.Network;
using Dalamud.Game.Network.MarketBoardUploaders;
using Dalamud.Game.Network.Structures;
using Dalamud.Game.Network.Universalis.MarketBoardUploaders;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json.Linq;
using Serilog;
namespace Dalamud.Game.Network {
public class NetworkHandlers {
namespace Dalamud.Game.Network
{
/// <summary>
/// This class handles network notifications and uploading Marketboard data.
/// </summary>
public class NetworkHandlers
{
private readonly Dalamud dalamud;
private readonly List<MarketBoardItemRequest> marketBoardRequests = new List<MarketBoardItemRequest>();
private readonly List<MarketBoardItemRequest> marketBoardRequests = new();
private readonly bool optOutMbUploads;
private readonly IMarketBoardUploader uploader;
/// <summary>
/// Event which gets fired when a duty is ready.
/// Initializes a new instance of the <see cref="NetworkHandlers"/> class.
/// </summary>
public event EventHandler<ContentFinderCondition> CfPop;
public NetworkHandlers(Dalamud dalamud, bool optOutMbUploads) {
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="optOutMbUploads">Whether the client should opt out of marketboard uploads.</param>
public NetworkHandlers(Dalamud dalamud, bool optOutMbUploads)
{
this.dalamud = dalamud;
this.optOutMbUploads = optOutMbUploads;
this.uploader = new UniversalisMarketBoardUploader(dalamud);
dalamud.Framework.Network.OnNetworkMessage += OnNetworkMessage;
dalamud.Framework.Network.OnNetworkMessage += this.OnNetworkMessage;
}
private void OnNetworkMessage(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction) {
/// <summary>
/// Event which gets fired when a duty is ready.
/// </summary>
public event EventHandler<ContentFinderCondition> CfPop;
private void OnNetworkMessage(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
{
if (direction != NetworkMessageDirection.ZoneDown)
return;
if (!this.dalamud.Data.IsDataReady)
return;
if (opCode == this.dalamud.Data.ServerOpCodes["CfNotifyPop"]) {
if (opCode == this.dalamud.Data.ServerOpCodes["CfNotifyPop"])
{
var data = new byte[64];
Marshal.Copy(dataPtr, data, 0, 64);
@ -63,98 +74,114 @@ namespace Dalamud.Game.Network {
}
var cfcName = contentFinderCondition.Name.ToString();
if (string.IsNullOrEmpty(contentFinderCondition.Name)) {
if (string.IsNullOrEmpty(contentFinderCondition.Name))
{
cfcName = "Duty Roulette";
contentFinderCondition.Image = 112324;
}
if (this.dalamud.Configuration.DutyFinderTaskbarFlash && !NativeFunctions.ApplicationIsActivated()) {
var flashInfo = new NativeFunctions.FLASHWINFO
if (this.dalamud.Configuration.DutyFinderTaskbarFlash && !NativeFunctions.ApplicationIsActivated())
{
var flashInfo = new NativeFunctions.FlashWindowInfo
{
cbSize = (uint)Marshal.SizeOf<NativeFunctions.FLASHWINFO>(),
cbSize = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
uCount = uint.MaxValue,
dwTimeout = 0,
dwFlags = NativeFunctions.FlashWindow.FLASHW_ALL |
NativeFunctions.FlashWindow.FLASHW_TIMERNOFG,
hwnd = Process.GetCurrentProcess().MainWindowHandle
dwFlags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
hwnd = Process.GetCurrentProcess().MainWindowHandle,
};
NativeFunctions.FlashWindowEx(ref flashInfo);
}
Task.Run(() => {
if(this.dalamud.Configuration.DutyFinderChatMessage)
Task.Run(() =>
{
if (this.dalamud.Configuration.DutyFinderChatMessage)
this.dalamud.Framework.Gui.Chat.Print("Duty pop: " + cfcName);
CfPop?.Invoke(this, contentFinderCondition);
this.CfPop?.Invoke(this, contentFinderCondition);
});
return;
}
if (!this.optOutMbUploads) {
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardItemRequestStart"]) {
var catalogId = (uint) Marshal.ReadInt32(dataPtr);
if (!this.optOutMbUploads)
{
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardItemRequestStart"])
{
var catalogId = (uint)Marshal.ReadInt32(dataPtr);
var amount = Marshal.ReadByte(dataPtr + 0xB);
this.marketBoardRequests.Add(new MarketBoardItemRequest {
this.marketBoardRequests.Add(new MarketBoardItemRequest
{
CatalogId = catalogId,
AmountToArrive = amount,
Listings = new List<MarketBoardCurrentOfferings.MarketBoardItemListing>(),
History = new List<MarketBoardHistory.MarketBoardHistoryListing>()
History = new List<MarketBoardHistory.MarketBoardHistoryListing>(),
});
Log.Verbose($"NEW MB REQUEST START: item#{catalogId} amount#{amount}");
return;
}
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardOfferings"]) {
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardOfferings"])
{
var listing = MarketBoardCurrentOfferings.Read(dataPtr);
var request =
this.marketBoardRequests.LastOrDefault(
r => r.CatalogId == listing.ItemListings[0].CatalogId && !r.IsDone);
var request = this.marketBoardRequests.LastOrDefault(r => r.CatalogId == listing.ItemListings[0].CatalogId && !r.IsDone);
if (request == null) {
Log.Error(
$"Market Board data arrived without a corresponding request: item#{listing.ItemListings[0].CatalogId}");
if (request == null)
{
Log.Error($"Market Board data arrived without a corresponding request: item#{listing.ItemListings[0].CatalogId}");
return;
}
if (request.Listings.Count + listing.ItemListings.Count > request.AmountToArrive) {
Log.Error(
$"Too many Market Board listings received for request: {request.Listings.Count + listing.ItemListings.Count} > {request.AmountToArrive} item#{listing.ItemListings[0].CatalogId}");
if (request.Listings.Count + listing.ItemListings.Count > request.AmountToArrive)
{
Log.Error($"Too many Market Board listings received for request: {request.Listings.Count + listing.ItemListings.Count} > {request.AmountToArrive} item#{listing.ItemListings[0].CatalogId}");
return;
}
if (request.ListingsRequestId != -1 && request.ListingsRequestId != listing.RequestId) {
Log.Error(
$"Non-matching RequestIds for Market Board data request: {request.ListingsRequestId}, {listing.RequestId}");
if (request.ListingsRequestId != -1 && request.ListingsRequestId != listing.RequestId)
{
Log.Error($"Non-matching RequestIds for Market Board data request: {request.ListingsRequestId}, {listing.RequestId}");
return;
}
if (request.ListingsRequestId == -1 && request.Listings.Count > 0) {
Log.Error(
$"Market Board data request sequence break: {request.ListingsRequestId}, {request.Listings.Count}");
if (request.ListingsRequestId == -1 && request.Listings.Count > 0)
{
Log.Error($"Market Board data request sequence break: {request.ListingsRequestId}, {request.Listings.Count}");
return;
}
if (request.ListingsRequestId == -1) {
if (request.ListingsRequestId == -1)
{
request.ListingsRequestId = listing.RequestId;
Log.Verbose($"First Market Board packet in sequence: {listing.RequestId}");
}
request.Listings.AddRange(listing.ItemListings);
Log.Verbose("Added {0} ItemListings to request#{1}, now {2}/{3}, item#{4}",
listing.ItemListings.Count, request.ListingsRequestId, request.Listings.Count,
request.AmountToArrive, request.CatalogId);
Log.Verbose(
"Added {0} ItemListings to request#{1}, now {2}/{3}, item#{4}",
listing.ItemListings.Count,
request.ListingsRequestId,
request.Listings.Count,
request.AmountToArrive,
request.CatalogId);
if (request.IsDone) {
Log.Verbose("Market Board request finished, starting upload: request#{0} item#{1} amount#{2}",
request.ListingsRequestId, request.CatalogId, request.AmountToArrive);
try {
if (request.IsDone)
{
Log.Verbose(
"Market Board request finished, starting upload: request#{0} item#{1} amount#{2}",
request.ListingsRequestId,
request.CatalogId,
request.AmountToArrive);
try
{
Task.Run(() => this.uploader.Upload(request));
} catch (Exception ex) {
}
catch (Exception ex)
{
Log.Error(ex, "Market Board data upload failed.");
}
}
@ -162,18 +189,21 @@ namespace Dalamud.Game.Network {
return;
}
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardHistory"]) {
if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardHistory"])
{
var listing = MarketBoardHistory.Read(dataPtr);
var request = this.marketBoardRequests.LastOrDefault(r => r.CatalogId == listing.CatalogId);
if (request == null) {
if (request == null)
{
Log.Error(
$"Market Board data arrived without a corresponding request: item#{listing.CatalogId}");
return;
}
if (request.ListingsRequestId != -1) {
if (request.ListingsRequestId != -1)
{
Log.Error(
$"Market Board data history sequence break: {request.ListingsRequestId}, {request.Listings.Count}");
return;
@ -183,7 +213,8 @@ namespace Dalamud.Game.Network {
Log.Verbose("Added history for item#{0}", listing.CatalogId);
if (request.AmountToArrive == 0) {
if (request.AmountToArrive == 0)
{
Log.Verbose("Request had 0 amount, uploading now");
try
@ -197,17 +228,25 @@ namespace Dalamud.Game.Network {
}
}
if (opCode == this.dalamud.Data.ServerOpCodes["MarketTaxRates"]) {
var category = (uint) Marshal.ReadInt32(dataPtr);
if (opCode == this.dalamud.Data.ServerOpCodes["MarketTaxRates"])
{
var category = (uint)Marshal.ReadInt32(dataPtr);
// Result dialog packet does not contain market tax rates
if (category != 720905) {
if (category != 720905)
{
return;
}
var taxes = MarketTaxRates.Read(dataPtr);
Log.Verbose("MarketTaxRates: limsa#{0} grid#{1} uldah#{2} ish#{3} kugane#{4} cr#{5}",
taxes.LimsaLominsaTax, taxes.GridaniaTax, taxes.UldahTax, taxes.IshgardTax, taxes.KuganeTax, taxes.CrystariumTax);
Log.Verbose(
"MarketTaxRates: limsa#{0} grid#{1} uldah#{2} ish#{3} kugane#{4} cr#{5}",
taxes.LimsaLominsaTax,
taxes.GridaniaTax,
taxes.UldahTax,
taxes.IshgardTax,
taxes.KuganeTax,
taxes.CrystariumTax);
try
{
Task.Run(() => this.uploader.UploadTax(taxes));

View file

@ -17,69 +17,66 @@ namespace Dalamud.Game.Network.Structures
{
var output = new MarketBoardCurrentOfferings();
using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream);
output.ItemListings = new List<MarketBoardItemListing>();
for (var i = 0; i < 10; i++)
{
using (var reader = new BinaryReader(stream))
var listingEntry = new MarketBoardItemListing();
listingEntry.ListingId = reader.ReadUInt64();
listingEntry.RetainerId = reader.ReadUInt64();
listingEntry.RetainerOwnerId = reader.ReadUInt64();
listingEntry.ArtisanId = reader.ReadUInt64();
listingEntry.PricePerUnit = reader.ReadUInt32();
listingEntry.TotalTax = reader.ReadUInt32();
listingEntry.ItemQuantity = reader.ReadUInt32();
listingEntry.CatalogId = reader.ReadUInt32();
listingEntry.LastReviewTime = DateTimeOffset.UtcNow.AddSeconds(-reader.ReadUInt16()).DateTime;
reader.ReadUInt16(); // container
reader.ReadUInt32(); // slot
reader.ReadUInt16(); // durability
reader.ReadUInt16(); // spiritbond
listingEntry.Materia = new List<MarketBoardItemListing.ItemMateria>();
for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++)
{
output.ItemListings = new List<MarketBoardItemListing>();
var materiaVal = reader.ReadUInt16();
for (var i = 0; i < 10; i++)
{
var listingEntry = new MarketBoardItemListing();
var materiaEntry = new MarketBoardItemListing.ItemMateria();
materiaEntry.MateriaId = (materiaVal & 0xFF0) >> 4;
materiaEntry.Index = materiaVal & 0xF;
listingEntry.ListingId = reader.ReadUInt64();
listingEntry.RetainerId = reader.ReadUInt64();
listingEntry.RetainerOwnerId = reader.ReadUInt64();
listingEntry.ArtisanId = reader.ReadUInt64();
listingEntry.PricePerUnit = reader.ReadUInt32();
listingEntry.TotalTax = reader.ReadUInt32();
listingEntry.ItemQuantity = reader.ReadUInt32();
listingEntry.CatalogId = reader.ReadUInt32();
listingEntry.LastReviewTime = DateTimeOffset.UtcNow.AddSeconds(-reader.ReadUInt16()).DateTime;
reader.ReadUInt16(); // container
reader.ReadUInt32(); // slot
reader.ReadUInt16(); // durability
reader.ReadUInt16(); // spiritbond
listingEntry.Materia = new List<MarketBoardItemListing.ItemMateria>();
for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++)
{
var materiaVal = reader.ReadUInt16();
var materiaEntry = new MarketBoardItemListing.ItemMateria();
materiaEntry.MateriaId = (materiaVal & 0xFF0) >> 4;
materiaEntry.Index = materiaVal & 0xF;
if (materiaEntry.MateriaId != 0)
listingEntry.Materia.Add(materiaEntry);
}
reader.ReadUInt16();
reader.ReadUInt32();
listingEntry.RetainerName = Encoding.UTF8.GetString(reader.ReadBytes(32)).TrimEnd('\u0000');
listingEntry.PlayerName = Encoding.UTF8.GetString(reader.ReadBytes(32)).TrimEnd('\u0000');
listingEntry.IsHq = reader.ReadBoolean();
listingEntry.MateriaCount = reader.ReadByte();
listingEntry.OnMannequin = reader.ReadBoolean();
listingEntry.RetainerCityId = reader.ReadByte();
listingEntry.StainId = reader.ReadUInt16();
reader.ReadUInt16();
reader.ReadUInt32();
if (listingEntry.CatalogId != 0)
output.ItemListings.Add(listingEntry);
}
output.ListingIndexEnd = reader.ReadByte();
output.ListingIndexStart = reader.ReadByte();
output.RequestId = reader.ReadUInt16();
if (materiaEntry.MateriaId != 0)
listingEntry.Materia.Add(materiaEntry);
}
reader.ReadUInt16();
reader.ReadUInt32();
listingEntry.RetainerName = Encoding.UTF8.GetString(reader.ReadBytes(32)).TrimEnd('\u0000');
listingEntry.PlayerName = Encoding.UTF8.GetString(reader.ReadBytes(32)).TrimEnd('\u0000');
listingEntry.IsHq = reader.ReadBoolean();
listingEntry.MateriaCount = reader.ReadByte();
listingEntry.OnMannequin = reader.ReadBoolean();
listingEntry.RetainerCityId = reader.ReadByte();
listingEntry.StainId = reader.ReadUInt16();
reader.ReadUInt16();
reader.ReadUInt32();
if (listingEntry.CatalogId != 0)
output.ItemListings.Add(listingEntry);
}
output.ListingIndexEnd = reader.ReadByte();
output.ListingIndexStart = reader.ReadByte();
output.RequestId = reader.ReadUInt16();
return output;
}

View file

@ -3,47 +3,52 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Dalamud.Game.Network.Structures {
public class MarketBoardHistory {
namespace Dalamud.Game.Network.Structures
{
public class MarketBoardHistory
{
public uint CatalogId;
public uint CatalogId2;
public List<MarketBoardHistoryListing> HistoryListings;
public static unsafe MarketBoardHistory Read(IntPtr dataPtr) {
public static unsafe MarketBoardHistory Read(IntPtr dataPtr)
{
var output = new MarketBoardHistory();
using (var stream = new UnmanagedMemoryStream((byte*) dataPtr.ToPointer(), 1544)) {
using (var reader = new BinaryReader(stream)) {
output.CatalogId = reader.ReadUInt32();
output.CatalogId2 = reader.ReadUInt32();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream);
output.HistoryListings = new List<MarketBoardHistoryListing>();
output.CatalogId = reader.ReadUInt32();
output.CatalogId2 = reader.ReadUInt32();
for (var i = 0; i < 10; i++) {
var listingEntry = new MarketBoardHistoryListing();
output.HistoryListings = new List<MarketBoardHistoryListing>();
listingEntry.SalePrice = reader.ReadUInt32();
listingEntry.PurchaseTime = DateTimeOffset.FromUnixTimeSeconds(reader.ReadUInt32()).UtcDateTime;
listingEntry.Quantity = reader.ReadUInt32();
listingEntry.IsHq = reader.ReadBoolean();
for (var i = 0; i < 10; i++)
{
var listingEntry = new MarketBoardHistoryListing
{
SalePrice = reader.ReadUInt32(),
PurchaseTime = DateTimeOffset.FromUnixTimeSeconds(reader.ReadUInt32()).UtcDateTime,
Quantity = reader.ReadUInt32(),
IsHq = reader.ReadBoolean(),
};
reader.ReadBoolean();
reader.ReadBoolean();
listingEntry.OnMannequin = reader.ReadBoolean();
listingEntry.BuyerName = Encoding.UTF8.GetString(reader.ReadBytes(33)).TrimEnd('\u0000');
listingEntry.CatalogId = reader.ReadUInt32();
listingEntry.OnMannequin = reader.ReadBoolean();
listingEntry.BuyerName = Encoding.UTF8.GetString(reader.ReadBytes(33)).TrimEnd('\u0000');
listingEntry.CatalogId = reader.ReadUInt32();
if (listingEntry.CatalogId != 0)
output.HistoryListings.Add(listingEntry);
}
}
if (listingEntry.CatalogId != 0)
output.HistoryListings.Add(listingEntry);
}
return output;
}
public class MarketBoardHistoryListing {
public class MarketBoardHistoryListing
{
public string BuyerName;
public uint CatalogId;

View file

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
namespace Dalamud.Game.Network.Structures
{
public class MarketTaxRates
{
public uint LimsaLominsaTax;
public uint GridaniaTax;
public uint UldahTax;
public uint IshgardTax;
public uint KuganeTax;
public uint CrystariumTax;
public static unsafe MarketTaxRates Read(IntPtr dataPtr)
{
var output = new MarketTaxRates();
using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
{
using (var reader = new BinaryReader(stream))
{
stream.Position += 8;
output.LimsaLominsaTax = reader.ReadUInt32();
output.GridaniaTax = reader.ReadUInt32();
output.UldahTax = reader.ReadUInt32();
output.IshgardTax = reader.ReadUInt32();
output.KuganeTax = reader.ReadUInt32();
output.CrystariumTax = reader.ReadUInt32();
}
}
return output;
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.IO;
namespace Dalamud.Game.Network.Structures
{
public class MarketTaxRates
{
public uint LimsaLominsaTax;
public uint GridaniaTax;
public uint UldahTax;
public uint IshgardTax;
public uint KuganeTax;
public uint CrystariumTax;
public static unsafe MarketTaxRates Read(IntPtr dataPtr)
{
var output = new MarketTaxRates();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream);
stream.Position += 8;
output.LimsaLominsaTax = reader.ReadUInt32();
output.GridaniaTax = reader.ReadUInt32();
output.UldahTax = reader.ReadUInt32();
output.IshgardTax = reader.ReadUInt32();
output.KuganeTax = reader.ReadUInt32();
output.CrystariumTax = reader.ReadUInt32();
return output;
}
}
}

View file

@ -1,28 +1,38 @@
using Dalamud.Hooking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Dalamud.Hooking;
namespace Dalamud.Game
{
/// <summary>
/// This class enables TCP optimizations in the game socket for better performance.
/// </summary>
internal sealed class WinSockHandlers : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate IntPtr SocketDelegate(int af, int type, int protocol);
private Hook<SocketDelegate> ws2SocketHook;
[DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi)]
private static extern int setsockopt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
public WinSockHandlers() {
this.ws2SocketHook = Hook<SocketDelegate>.FromSymbol("ws2_32.dll", "socket", new SocketDelegate(OnSocket));
/// <summary>
/// Initializes a new instance of the <see cref="WinSockHandlers"/> class.
/// </summary>
public WinSockHandlers()
{
this.ws2SocketHook = Hook<SocketDelegate>.FromSymbol("ws2_32.dll", "socket", new SocketDelegate(this.OnSocket));
this.ws2SocketHook.Enable();
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate IntPtr SocketDelegate(int af, int type, int protocol);
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
{
this.ws2SocketHook.Dispose();
}
private IntPtr OnSocket(int af, int type, int protocol)
{
var socket = this.ws2SocketHook.Original(af, type, protocol);
@ -30,26 +40,22 @@ namespace Dalamud.Game
// IPPROTO_TCP
if (type == 1)
{
// INVALID_SOCKET
// INVALID_SOCKET
if (socket != new IntPtr(-1))
{
// In case you're not aware of it: (albeit you should)
// https://linux.die.net/man/7/tcp
// https://assets.extrahop.com/whitepapers/TCP-Optimization-Guide-by-ExtraHop.pdf
var value = new IntPtr(1);
setsockopt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
// Enable tcp_quickack option. This option is undocumented in MSDN but it is supported in Windows 7 and onwards.
value = new IntPtr(1);
setsockopt(socket, SocketOptionLevel.Tcp, (SocketOptionName)12, ref value, 4);
NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.AddMembership, ref value, 4);
}
}
return socket;
}
public void Dispose() {
ws2SocketHook.Dispose();
}
}
}