Merge pull request #680 from daemitus/http

This commit is contained in:
goaaats 2021-11-11 03:04:14 +01:00 committed by GitHub
commit 4cff810d1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 214 additions and 193 deletions

View file

@ -1,3 +1,5 @@
using System.Threading.Tasks;
using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Structures;
namespace Dalamud.Game.Network.Internal.MarketBoardUploaders namespace Dalamud.Game.Network.Internal.MarketBoardUploaders
@ -11,18 +13,21 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders
/// Upload data about an item. /// Upload data about an item.
/// </summary> /// </summary>
/// <param name="item">The item request data being uploaded.</param> /// <param name="item">The item request data being uploaded.</param>
void Upload(MarketBoardItemRequest item); /// <returns>An async task.</returns>
Task Upload(MarketBoardItemRequest item);
/// <summary> /// <summary>
/// Upload tax rate data. /// Upload tax rate data.
/// </summary> /// </summary>
/// <param name="taxRates">The tax rate data being uploaded.</param> /// <param name="taxRates">The tax rate data being uploaded.</param>
void UploadTax(MarketTaxRates taxRates); /// <returns>An async task.</returns>
Task UploadTax(MarketTaxRates taxRates);
/// <summary> /// <summary>
/// Upload information about a purchase this client has made. /// Upload information about a purchase this client has made.
/// </summary> /// </summary>
/// <param name="purchaseHandler">The purchase handler data associated with the sale.</param> /// <param name="purchaseHandler">The purchase handler data associated with the sale.</param>
void UploadPurchase(MarketBoardPurchaseHandler purchaseHandler); /// <returns>An async task.</returns>
Task UploadPurchase(MarketBoardPurchaseHandler purchaseHandler);
} }
} }

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Structures;
@ -9,25 +11,29 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders
/// </summary> /// </summary>
internal class MarketBoardItemRequest internal class MarketBoardItemRequest
{ {
/// <summary> private MarketBoardItemRequest()
/// Gets or sets the catalog ID. {
/// </summary> }
public uint CatalogId { get; set; }
/// <summary> /// <summary>
/// Gets or sets the amount to arrive. /// Gets the catalog ID.
/// </summary> /// </summary>
public byte AmountToArrive { get; set; } public uint CatalogId { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the offered item listings. /// Gets the amount to arrive.
/// </summary> /// </summary>
public List<MarketBoardCurrentOfferings.MarketBoardItemListing> Listings { get; set; } public byte AmountToArrive { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the historical item listings. /// Gets the offered item listings.
/// </summary> /// </summary>
public List<MarketBoardHistory.MarketBoardHistoryListing> History { get; set; } public List<MarketBoardCurrentOfferings.MarketBoardItemListing> Listings { get; } = new();
/// <summary>
/// Gets the historical item listings.
/// </summary>
public List<MarketBoardHistory.MarketBoardHistoryListing> History { get; } = new();
/// <summary> /// <summary>
/// Gets or sets the listing request ID. /// Gets or sets the listing request ID.
@ -38,5 +44,24 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders
/// Gets a value indicating whether the upload is complete. /// Gets a value indicating whether the upload is complete.
/// </summary> /// </summary>
public bool IsDone => this.Listings.Count == this.AmountToArrive && this.History.Count != 0; public bool IsDone => this.Listings.Count == this.AmountToArrive && this.History.Count != 0;
/// <summary>
/// Read a packet off the wire.
/// </summary>
/// <param name="dataPtr">Packet data.</param>
/// <returns>An object representing the data read.</returns>
public static unsafe MarketBoardItemRequest Read(IntPtr dataPtr)
{
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream);
var output = new MarketBoardItemRequest();
output.CatalogId = reader.ReadUInt32();
stream.Position += 0x7;
output.AmountToArrive = reader.ReadByte();
return output;
}
} }
} }

View file

@ -1,11 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis.Types; using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis.Types;
using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Structures;
using Dalamud.Utility;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
@ -29,10 +30,9 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Upload(MarketBoardItemRequest request) public async Task Upload(MarketBoardItemRequest request)
{ {
var clientState = Service<ClientState.ClientState>.Get(); var clientState = Service<ClientState.ClientState>.Get();
using var client = new HttpClient();
Log.Verbose("Starting Universalis upload."); Log.Verbose("Starting Universalis upload.");
var uploader = clientState.LocalContentId; var uploader = clientState.LocalContentId;
@ -80,7 +80,7 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
var listingPath = "/upload"; var listingPath = "/upload";
var listingUpload = JsonConvert.SerializeObject(listingsUploadObject); var listingUpload = JsonConvert.SerializeObject(listingsUploadObject);
Log.Verbose($"{listingPath}: {listingUpload}"); Log.Verbose($"{listingPath}: {listingUpload}");
client.PostAsync($"{ApiBase}{listingPath}/{ApiKey}", new StringContent(listingUpload, Encoding.UTF8, "application/json")).GetAwaiter().GetResult(); await Util.HttpClient.PostAsync($"{ApiBase}{listingPath}/{ApiKey}", new StringContent(listingUpload, Encoding.UTF8, "application/json"));
// ==================================================================================== // ====================================================================================
@ -108,7 +108,7 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
var historyPath = "/upload"; var historyPath = "/upload";
var historyUpload = JsonConvert.SerializeObject(historyUploadObject); var historyUpload = JsonConvert.SerializeObject(historyUploadObject);
Log.Verbose($"{historyPath}: {historyUpload}"); Log.Verbose($"{historyPath}: {historyUpload}");
client.PostAsync($"{ApiBase}{historyPath}/{ApiKey}", new StringContent(historyUpload, Encoding.UTF8, "application/json")).GetAwaiter().GetResult(); await Util.HttpClient.PostAsync($"{ApiBase}{historyPath}/{ApiKey}", new StringContent(historyUpload, Encoding.UTF8, "application/json"));
// ==================================================================================== // ====================================================================================
@ -116,10 +116,9 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
} }
/// <inheritdoc/> /// <inheritdoc/>
public void UploadTax(MarketTaxRates taxRates) public async Task UploadTax(MarketTaxRates taxRates)
{ {
var clientState = Service<ClientState.ClientState>.Get(); var clientState = Service<ClientState.ClientState>.Get();
using var client = new HttpClient();
// ==================================================================================== // ====================================================================================
@ -142,7 +141,7 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
var taxUpload = JsonConvert.SerializeObject(taxUploadObject); var taxUpload = JsonConvert.SerializeObject(taxUploadObject);
Log.Verbose($"{taxPath}: {taxUpload}"); Log.Verbose($"{taxPath}: {taxUpload}");
client.PostAsync($"{ApiBase}{taxPath}/{ApiKey}", new StringContent(taxUpload, Encoding.UTF8, "application/json")).GetAwaiter().GetResult(); await Util.HttpClient.PostAsync($"{ApiBase}{taxPath}/{ApiKey}", new StringContent(taxUpload, Encoding.UTF8, "application/json"));
// ==================================================================================== // ====================================================================================
@ -155,12 +154,9 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
/// to track the available listings, that is done via the listings packet. All this does is remove /// to track the available listings, that is done via the listings packet. All this does is remove
/// a listing, or delete it, when a purchase has been made. /// a listing, or delete it, when a purchase has been made.
/// </remarks> /// </remarks>
public void UploadPurchase(MarketBoardPurchaseHandler purchaseHandler) public async Task UploadPurchase(MarketBoardPurchaseHandler purchaseHandler)
{ {
var clientState = Service<ClientState.ClientState>.Get(); var clientState = Service<ClientState.ClientState>.Get();
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(ApiKey);
var itemId = purchaseHandler.CatalogId; var itemId = purchaseHandler.CatalogId;
var worldId = clientState.LocalPlayer?.CurrentWorld.Id ?? 0; var worldId = clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
@ -180,7 +176,12 @@ namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis
var deleteListing = JsonConvert.SerializeObject(deleteListingObject); var deleteListing = JsonConvert.SerializeObject(deleteListingObject);
Log.Verbose($"{deletePath}: {deleteListing}"); Log.Verbose($"{deletePath}: {deleteListing}");
client.PostAsync($"{ApiBase}{deletePath}", new StringContent(deleteListing, Encoding.UTF8, "application/json")).GetAwaiter().GetResult(); var content = new StringContent(deleteListing, Encoding.UTF8, "application/json");
var message = new HttpRequestMessage(HttpMethod.Post, $"{ApiBase}{deletePath}");
message.Headers.Add("Authorization", ApiKey);
message.Content = content;
await Util.HttpClient.SendAsync(message);
// ==================================================================================== // ====================================================================================

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,6 +12,7 @@ using Dalamud.Game.Gui;
using Dalamud.Game.Network.Internal.MarketBoardUploaders; using Dalamud.Game.Network.Internal.MarketBoardUploaders;
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis; using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Structures;
using Dalamud.Utility;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Serilog; using Serilog;
@ -58,9 +60,10 @@ namespace Dalamud.Game.Network.Internal
{ {
if (!this.optOutMbUploads) if (!this.optOutMbUploads)
{ {
if (opCode == Service<DataManager>.Get().ClientOpCodes["MarketBoardPurchaseHandler"]) if (opCode == dataManager.ClientOpCodes["MarketBoardPurchaseHandler"])
{ {
this.marketBoardPurchaseHandler = MarketBoardPurchaseHandler.Read(dataPtr); this.marketBoardPurchaseHandler = MarketBoardPurchaseHandler.Read(dataPtr);
return;
} }
} }
@ -69,53 +72,7 @@ namespace Dalamud.Game.Network.Internal
if (opCode == dataManager.ServerOpCodes["CfNotifyPop"]) if (opCode == dataManager.ServerOpCodes["CfNotifyPop"])
{ {
var data = new byte[64]; this.HandleCfPop(dataPtr);
Marshal.Copy(dataPtr, data, 0, 64);
var notifyType = data[0];
var contentFinderConditionId = BitConverter.ToUInt16(data, 0x14);
if (notifyType != 3)
return;
var contentFinderCondition = dataManager.GetExcelSheet<ContentFinderCondition>().GetRow(contentFinderConditionId);
if (contentFinderCondition == null)
{
Log.Error("CFC key {0} not in lumina data.", contentFinderConditionId);
return;
}
var cfcName = contentFinderCondition.Name.ToString();
if (string.IsNullOrEmpty(contentFinderCondition.Name))
{
cfcName = "Duty Roulette";
contentFinderCondition.Image = 112324;
}
if (configuration.DutyFinderTaskbarFlash && !NativeFunctions.ApplicationIsActivated())
{
var flashInfo = new NativeFunctions.FlashWindowInfo
{
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
Count = uint.MaxValue,
Timeout = 0,
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
};
NativeFunctions.FlashWindowEx(ref flashInfo);
}
Task.Run(() =>
{
if (configuration.DutyFinderChatMessage)
{
Service<ChatGui>.Get().Print("Duty pop: " + cfcName);
}
this.CfPop?.Invoke(this, contentFinderCondition);
});
return; return;
} }
@ -123,18 +80,10 @@ namespace Dalamud.Game.Network.Internal
{ {
if (opCode == dataManager.ServerOpCodes["MarketBoardItemRequestStart"]) if (opCode == dataManager.ServerOpCodes["MarketBoardItemRequestStart"])
{ {
var catalogId = (uint)Marshal.ReadInt32(dataPtr); var data = MarketBoardItemRequest.Read(dataPtr);
var amount = Marshal.ReadByte(dataPtr + 0xB); this.marketBoardRequests.Add(data);
this.marketBoardRequests.Add(new MarketBoardItemRequest Log.Verbose($"NEW MB REQUEST START: item#{data.CatalogId} amount#{data.AmountToArrive}");
{
CatalogId = catalogId,
AmountToArrive = amount,
Listings = new List<MarketBoardCurrentOfferings.MarketBoardItemListing>(),
History = new List<MarketBoardHistory.MarketBoardHistoryListing>(),
});
Log.Verbose($"NEW MB REQUEST START: item#{catalogId} amount#{amount}");
return; return;
} }
@ -191,14 +140,9 @@ namespace Dalamud.Game.Network.Internal
request.ListingsRequestId, request.ListingsRequestId,
request.CatalogId, request.CatalogId,
request.AmountToArrive); request.AmountToArrive);
try
{ Task.Run(() => this.uploader.Upload(request))
Task.Run(() => this.uploader.Upload(request)); .ContinueWith((task) => Log.Error(task.Exception, "Market Board offerings data upload failed."), TaskContinuationOptions.OnlyOnFaulted);
}
catch (Exception ex)
{
Log.Error(ex, "Market Board data upload failed.");
}
} }
return; return;
@ -230,14 +174,8 @@ namespace Dalamud.Game.Network.Internal
{ {
Log.Verbose("Request had 0 amount, uploading now"); Log.Verbose("Request had 0 amount, uploading now");
try Task.Run(() => this.uploader.Upload(request))
{ .ContinueWith((task) => Log.Error(task.Exception, "Market Board history data upload failed."), TaskContinuationOptions.OnlyOnFaulted);
Task.Run(() => this.uploader.Upload(request));
}
catch (Exception ex)
{
Log.Error(ex, "Market Board data upload failed.");
}
} }
} }
@ -261,14 +199,11 @@ namespace Dalamud.Game.Network.Internal
taxes.IshgardTax, taxes.IshgardTax,
taxes.KuganeTax, taxes.KuganeTax,
taxes.CrystariumTax); taxes.CrystariumTax);
try
{ Task.Run(() => this.uploader.UploadTax(taxes))
Task.Run(() => this.uploader.UploadTax(taxes)); .ContinueWith((task) => Log.Error(task.Exception, "Market Board tax data upload failed."), TaskContinuationOptions.OnlyOnFaulted);
}
catch (Exception ex) return;
{
Log.Error(ex, "Market Board data upload failed.");
}
} }
if (opCode == dataManager.ServerOpCodes["MarketBoardPurchase"]) if (opCode == dataManager.ServerOpCodes["MarketBoardPurchase"])
@ -278,19 +213,81 @@ namespace Dalamud.Game.Network.Internal
var purchase = MarketBoardPurchase.Read(dataPtr); var purchase = MarketBoardPurchase.Read(dataPtr);
var sameQty = purchase.ItemQuantity == this.marketBoardPurchaseHandler.ItemQuantity;
var itemMatch = purchase.CatalogId == this.marketBoardPurchaseHandler.CatalogId;
var itemMatchHq = purchase.CatalogId == this.marketBoardPurchaseHandler.CatalogId + 1_000_000;
// Transaction succeeded // Transaction succeeded
if (purchase.ItemQuantity == this.marketBoardPurchaseHandler.ItemQuantity if (sameQty && (itemMatch || itemMatchHq))
&& (purchase.CatalogId == this.marketBoardPurchaseHandler.CatalogId {
|| purchase.CatalogId == this.marketBoardPurchaseHandler.CatalogId + 1_000_000))
{ // HQ
Log.Verbose($"Bought {purchase.ItemQuantity}x {this.marketBoardPurchaseHandler.CatalogId} for {this.marketBoardPurchaseHandler.PricePerUnit * purchase.ItemQuantity} gils, listing id is {this.marketBoardPurchaseHandler.ListingId}"); Log.Verbose($"Bought {purchase.ItemQuantity}x {this.marketBoardPurchaseHandler.CatalogId} for {this.marketBoardPurchaseHandler.PricePerUnit * purchase.ItemQuantity} gils, listing id is {this.marketBoardPurchaseHandler.ListingId}");
var handler = this.marketBoardPurchaseHandler; // Capture the object so that we don't pass in a null one when the task starts. var handler = this.marketBoardPurchaseHandler; // Capture the object so that we don't pass in a null one when the task starts.
Task.Run(() => this.uploader.UploadPurchase(handler));
Task.Run(() => this.uploader.UploadPurchase(handler))
.ContinueWith((task) => Log.Error(task.Exception, "Market Board purchase data upload failed."), TaskContinuationOptions.OnlyOnFaulted);
} }
this.marketBoardPurchaseHandler = null; this.marketBoardPurchaseHandler = null;
return;
} }
} }
} }
private unsafe void HandleCfPop(IntPtr dataPtr)
{
var dataManager = Service<DataManager>.Get();
var configuration = Service<DalamudConfiguration>.Get();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 64);
using var reader = new BinaryReader(stream);
var notifyType = reader.ReadByte();
stream.Position += 0x13;
var conditionId = reader.ReadUInt16();
if (notifyType != 3)
return;
var cfConditionSheet = dataManager.GetExcelSheet<ContentFinderCondition>()!;
var cfCondition = cfConditionSheet.GetRow(conditionId);
if (cfCondition == null)
{
Log.Error($"CFC key {conditionId} not in Lumina data.");
return;
}
var cfcName = cfCondition.Name.ToString();
if (cfcName.IsNullOrEmpty())
{
cfcName = "Duty Roulette";
cfCondition.Image = 112324;
}
// Flash window
if (configuration.DutyFinderTaskbarFlash && !NativeFunctions.ApplicationIsActivated())
{
var flashInfo = new NativeFunctions.FlashWindowInfo
{
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
Count = uint.MaxValue,
Timeout = 0,
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
};
NativeFunctions.FlashWindowEx(ref flashInfo);
}
Task.Run(() =>
{
if (configuration.DutyFinderChatMessage)
{
Service<ChatGui>.Get().Print($"Duty pop: {cfcName}");
}
this.CfPop?.Invoke(this, cfCondition);
}).ContinueWith((task) => Log.Error(task.Exception, "CfPop.Invoke failed."), TaskContinuationOptions.OnlyOnFaulted);
}
} }
} }

View file

@ -10,17 +10,14 @@ namespace Dalamud.Game.Network.Structures
/// </summary> /// </summary>
public class MarketBoardCurrentOfferings public class MarketBoardCurrentOfferings
{ {
/// <summary> private MarketBoardCurrentOfferings()
/// Initializes a new instance of the <see cref="MarketBoardCurrentOfferings"/> class.
/// </summary>
internal MarketBoardCurrentOfferings()
{ {
} }
/// <summary> /// <summary>
/// Gets the list of individual item listings. /// Gets the list of individual item listings.
/// </summary> /// </summary>
public List<MarketBoardItemListing> ItemListings { get; internal set; } public List<MarketBoardItemListing> ItemListings { get; } = new();
/// <summary> /// <summary>
/// Gets the listing end index. /// Gets the listing end index.
@ -49,8 +46,6 @@ namespace Dalamud.Game.Network.Structures
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544); using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream); using var reader = new BinaryReader(stream);
output.ItemListings = new List<MarketBoardItemListing>();
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
var listingEntry = new MarketBoardItemListing(); var listingEntry = new MarketBoardItemListing();
@ -70,15 +65,14 @@ namespace Dalamud.Game.Network.Structures
reader.ReadUInt16(); // durability reader.ReadUInt16(); // durability
reader.ReadUInt16(); // spiritbond reader.ReadUInt16(); // spiritbond
listingEntry.Materia = new List<MarketBoardItemListing.ItemMateria>();
for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++) for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++)
{ {
var materiaVal = reader.ReadUInt16(); var materiaVal = reader.ReadUInt16();
var materiaEntry = new MarketBoardItemListing.ItemMateria()
var materiaEntry = new MarketBoardItemListing.ItemMateria(); {
materiaEntry.MateriaId = (materiaVal & 0xFF0) >> 4; MateriaId = (materiaVal & 0xFF0) >> 4,
materiaEntry.Index = materiaVal & 0xF; Index = materiaVal & 0xF,
};
if (materiaEntry.MateriaId != 0) if (materiaEntry.MateriaId != 0)
listingEntry.Materia.Add(materiaEntry); listingEntry.Materia.Add(materiaEntry);
@ -154,7 +148,7 @@ namespace Dalamud.Game.Network.Structures
/// <summary> /// <summary>
/// Gets the list of materia attached to this item. /// Gets the list of materia attached to this item.
/// </summary> /// </summary>
public List<ItemMateria> Materia { get; internal set; } public List<ItemMateria> Materia { get; } = new();
/// <summary> /// <summary>
/// Gets the amount of attached materia. /// Gets the amount of attached materia.

View file

@ -20,17 +20,17 @@ namespace Dalamud.Game.Network.Structures
/// <summary> /// <summary>
/// Gets the catalog ID. /// Gets the catalog ID.
/// </summary> /// </summary>
public uint CatalogId { get; internal set; } public uint CatalogId { get; private set; }
/// <summary> /// <summary>
/// Gets the second catalog ID. /// Gets the second catalog ID.
/// </summary> /// </summary>
public uint CatalogId2 { get; internal set; } public uint CatalogId2 { get; private set; }
/// <summary> /// <summary>
/// Gets the list of individual item history listings. /// Gets the list of individual item history listings.
/// </summary> /// </summary>
public List<MarketBoardHistoryListing> HistoryListings { get; internal set; } public List<MarketBoardHistoryListing> HistoryListings { get; } = new();
/// <summary> /// <summary>
/// Read a <see cref="MarketBoardHistory"/> object from memory. /// Read a <see cref="MarketBoardHistory"/> object from memory.
@ -39,17 +39,15 @@ namespace Dalamud.Game.Network.Structures
/// <returns>A new <see cref="MarketBoardHistory"/> object.</returns> /// <returns>A new <see cref="MarketBoardHistory"/> object.</returns>
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 stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream); using var reader = new BinaryReader(stream);
var output = new MarketBoardHistory();
output.CatalogId = reader.ReadUInt32(); output.CatalogId = reader.ReadUInt32();
output.CatalogId2 = reader.ReadUInt32(); output.CatalogId2 = reader.ReadUInt32();
output.HistoryListings = new List<MarketBoardHistoryListing>(); for (var i = 0; i < 20; i++)
for (var i = 0; i < 10; i++)
{ {
var listingEntry = new MarketBoardHistoryListing var listingEntry = new MarketBoardHistoryListing
{ {
@ -63,10 +61,12 @@ namespace Dalamud.Game.Network.Structures
listingEntry.OnMannequin = reader.ReadBoolean(); listingEntry.OnMannequin = reader.ReadBoolean();
listingEntry.BuyerName = Encoding.UTF8.GetString(reader.ReadBytes(33)).TrimEnd('\u0000'); listingEntry.BuyerName = Encoding.UTF8.GetString(reader.ReadBytes(33)).TrimEnd('\u0000');
listingEntry.CatalogId = reader.ReadUInt32(); listingEntry.NextCatalogId = reader.ReadUInt32();
if (listingEntry.CatalogId != 0) output.HistoryListings.Add(listingEntry);
output.HistoryListings.Add(listingEntry);
if (listingEntry.NextCatalogId == 0)
break;
} }
return output; return output;
@ -90,9 +90,9 @@ namespace Dalamud.Game.Network.Structures
public string BuyerName { get; internal set; } public string BuyerName { get; internal set; }
/// <summary> /// <summary>
/// Gets the catalog ID. /// Gets the next entry's catalog ID.
/// </summary> /// </summary>
public uint CatalogId { get; internal set; } public uint NextCatalogId { get; internal set; }
/// <summary> /// <summary>
/// Gets a value indicating whether the item is HQ. /// Gets a value indicating whether the item is HQ.

View file

@ -9,6 +9,10 @@ namespace Dalamud.Game.Network.Structures
/// </summary> /// </summary>
internal class MarketBoardPurchase internal class MarketBoardPurchase
{ {
private MarketBoardPurchase()
{
}
/// <summary> /// <summary>
/// Gets the item ID of the item that was purchased. /// Gets the item ID of the item that was purchased.
/// </summary> /// </summary>
@ -26,10 +30,11 @@ namespace Dalamud.Game.Network.Structures
/// <returns>An object representing the data read.</returns> /// <returns>An object representing the data read.</returns>
public static unsafe MarketBoardPurchase Read(IntPtr dataPtr) public static unsafe MarketBoardPurchase Read(IntPtr dataPtr)
{ {
var output = new MarketBoardPurchase();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544); using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream); using var reader = new BinaryReader(stream);
var output = new MarketBoardPurchase();
output.CatalogId = reader.ReadUInt32(); output.CatalogId = reader.ReadUInt32();
stream.Position += 4; stream.Position += 4;
output.ItemQuantity = reader.ReadUInt32(); output.ItemQuantity = reader.ReadUInt32();

View file

@ -9,6 +9,10 @@ namespace Dalamud.Game.Network.Structures
/// </summary> /// </summary>
internal class MarketBoardPurchaseHandler internal class MarketBoardPurchaseHandler
{ {
private MarketBoardPurchaseHandler()
{
}
/// <summary> /// <summary>
/// Gets the object ID of the retainer associated with the sale. /// Gets the object ID of the retainer associated with the sale.
/// </summary> /// </summary>
@ -41,10 +45,11 @@ namespace Dalamud.Game.Network.Structures
/// <returns>An object representing the data read.</returns> /// <returns>An object representing the data read.</returns>
public static unsafe MarketBoardPurchaseHandler Read(IntPtr dataPtr) public static unsafe MarketBoardPurchaseHandler Read(IntPtr dataPtr)
{ {
var output = new MarketBoardPurchaseHandler();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544); using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream); using var reader = new BinaryReader(stream);
var output = new MarketBoardPurchaseHandler();
output.RetainerId = reader.ReadUInt64(); output.RetainerId = reader.ReadUInt64();
output.ListingId = reader.ReadUInt64(); output.ListingId = reader.ReadUInt64();
output.CatalogId = reader.ReadUInt32(); output.CatalogId = reader.ReadUInt32();

View file

@ -8,42 +8,39 @@ namespace Dalamud.Game.Network.Structures
/// </summary> /// </summary>
public class MarketTaxRates public class MarketTaxRates
{ {
/// <summary> private MarketTaxRates()
/// Initializes a new instance of the <see cref="MarketTaxRates"/> class.
/// </summary>
internal MarketTaxRates()
{ {
} }
/// <summary> /// <summary>
/// Gets the tax rate in Limsa Lominsa. /// Gets the tax rate in Limsa Lominsa.
/// </summary> /// </summary>
public uint LimsaLominsaTax { get; internal set; } public uint LimsaLominsaTax { get; private set; }
/// <summary> /// <summary>
/// Gets the tax rate in Gridania. /// Gets the tax rate in Gridania.
/// </summary> /// </summary>
public uint GridaniaTax { get; internal set; } public uint GridaniaTax { get; private set; }
/// <summary> /// <summary>
/// Gets the tax rate in Ul'dah. /// Gets the tax rate in Ul'dah.
/// </summary> /// </summary>
public uint UldahTax { get; internal set; } public uint UldahTax { get; private set; }
/// <summary> /// <summary>
/// Gets the tax rate in Ishgard. /// Gets the tax rate in Ishgard.
/// </summary> /// </summary>
public uint IshgardTax { get; internal set; } public uint IshgardTax { get; private set; }
/// <summary> /// <summary>
/// Gets the tax rate in Kugane. /// Gets the tax rate in Kugane.
/// </summary> /// </summary>
public uint KuganeTax { get; internal set; } public uint KuganeTax { get; private set; }
/// <summary> /// <summary>
/// Gets the tax rate in the Crystarium. /// Gets the tax rate in the Crystarium.
/// </summary> /// </summary>
public uint CrystariumTax { get; internal set; } public uint CrystariumTax { get; private set; }
/// <summary> /// <summary>
/// Read a <see cref="MarketTaxRates"/> object from memory. /// Read a <see cref="MarketTaxRates"/> object from memory.
@ -52,13 +49,12 @@ namespace Dalamud.Game.Network.Structures
/// <returns>A new <see cref="MarketTaxRates"/> object.</returns> /// <returns>A new <see cref="MarketTaxRates"/> object.</returns>
public static unsafe MarketTaxRates Read(IntPtr dataPtr) public static unsafe MarketTaxRates Read(IntPtr dataPtr)
{ {
var output = new MarketTaxRates();
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544); using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
using var reader = new BinaryReader(stream); using var reader = new BinaryReader(stream);
stream.Position += 8; var output = new MarketTaxRates();
stream.Position += 8;
output.LimsaLominsaTax = reader.ReadUInt32(); output.LimsaLominsaTax = reader.ReadUInt32();
output.GridaniaTax = reader.ReadUInt32(); output.GridaniaTax = reader.ReadUInt32();
output.UldahTax = reader.ReadUInt32(); output.UldahTax = reader.ReadUInt32();

View file

@ -44,8 +44,6 @@ namespace Dalamud.Interface.Internal.Windows
// TODO: Change back to master after release // TODO: Change back to master after release
private const string MainRepoImageUrl = "https://raw.githubusercontent.com/goatcorp/DalamudPlugins/api4/{0}/{1}/images/{2}"; private const string MainRepoImageUrl = "https://raw.githubusercontent.com/goatcorp/DalamudPlugins/api4/{0}/{1}/images/{2}";
private readonly HttpClient httpClient = new();
private BlockingCollection<Func<Task>> downloadQueue = new(); private BlockingCollection<Func<Task>> downloadQueue = new();
private CancellationTokenSource downloadToken = new(); private CancellationTokenSource downloadToken = new();
@ -264,7 +262,7 @@ namespace Dalamud.Interface.Internal.Windows
HttpResponseMessage data; HttpResponseMessage data;
try try
{ {
data = await this.httpClient.GetAsync(url); data = await Util.HttpClient.GetAsync(url);
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
@ -381,7 +379,7 @@ namespace Dalamud.Interface.Internal.Windows
HttpResponseMessage data; HttpResponseMessage data;
try try
{ {
data = await this.httpClient.GetAsync(url); data = await Util.HttpClient.GetAsync(url);
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {

View file

@ -409,8 +409,7 @@ namespace Dalamud.Plugin.Internal
var downloadUrl = useTesting ? repoManifest.DownloadLinkTesting : repoManifest.DownloadLinkInstall; var downloadUrl = useTesting ? repoManifest.DownloadLinkTesting : repoManifest.DownloadLinkInstall;
var version = useTesting ? repoManifest.TestingAssemblyVersion : repoManifest.AssemblyVersion; var version = useTesting ? repoManifest.TestingAssemblyVersion : repoManifest.AssemblyVersion;
using var client = new HttpClient(); var response = await Util.HttpClient.GetAsync(downloadUrl);
var response = await client.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var outputDir = new DirectoryInfo(Path.Combine(this.pluginDirectory.FullName, repoManifest.InternalName, version.ToString())); var outputDir = new DirectoryInfo(Path.Combine(this.pluginDirectory.FullName, repoManifest.InternalName, version.ToString()));

View file

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Dalamud.Logging.Internal; using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Dalamud.Plugin.Internal namespace Dalamud.Plugin.Internal
@ -74,10 +75,9 @@ namespace Dalamud.Plugin.Internal
try try
{ {
Log.Information($"Fetching repo: {this.PluginMasterUrl}"); Log.Information($"Fetching repo: {this.PluginMasterUrl}");
using var client = new HttpClient();
// ?ticks causes a cache invalidation. Get a fresh repo every time. // ?ticks causes a cache invalidation. Get a fresh repo every time.
using var response = await client.GetAsync(this.PluginMasterUrl + "?" + DateTime.Now.Ticks); using var response = await Util.HttpClient.GetAsync(this.PluginMasterUrl + "?" + DateTime.Now.Ticks);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync(); var data = await response.Content.ReadAsStringAsync();

View file

@ -1,4 +1,4 @@
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -28,8 +28,6 @@ namespace Dalamud.Support
if (content.IsNullOrWhitespace()) if (content.IsNullOrWhitespace())
return; return;
using var client = new HttpClient();
var model = new FeedbackModel var model = new FeedbackModel
{ {
Content = content, Content = content,
@ -45,7 +43,7 @@ namespace Dalamud.Support
} }
var postContent = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); var postContent = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
var response = await client.PostAsync(BugBaitUrl, postContent); var response = await Util.HttpClient.PostAsync(BugBaitUrl, postContent);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }

View file

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
@ -23,6 +24,12 @@ namespace Dalamud.Utility
{ {
private static string gitHashInternal; private static string gitHashInternal;
/// <summary>
/// Gets an httpclient for usage.
/// Do NOT await this.
/// </summary>
public static HttpClient HttpClient { get; } = new();
/// <summary> /// <summary>
/// Gets the assembly version of Dalamud. /// Gets the assembly version of Dalamud.
/// </summary> /// </summary>
@ -209,7 +216,7 @@ namespace Dalamud.Utility
} }
/// <summary> /// <summary>
/// Compress a string using GZip. /// Compress a string using GZip.
/// </summary> /// </summary>
/// <param name="str">The input string.</param> /// <param name="str">The input string.</param>
/// <returns>The compressed output bytes.</returns> /// <returns>The compressed output bytes.</returns>
@ -217,35 +224,29 @@ namespace Dalamud.Utility
{ {
var bytes = Encoding.UTF8.GetBytes(str); var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes)) using var msi = new MemoryStream(bytes);
using (var mso = new MemoryStream()) using var mso = new MemoryStream();
{ using var gs = new GZipStream(mso, CompressionMode.Compress);
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
CopyTo(msi, gs);
}
return mso.ToArray(); CopyTo(msi, gs);
}
return mso.ToArray();
} }
/// <summary> /// <summary>
/// Decompress a string using GZip. /// Decompress a string using GZip.
/// </summary> /// </summary>
/// <param name="bytes">The input bytes.</param> /// <param name="bytes">The input bytes.</param>
/// <returns>The compressed output string.</returns> /// <returns>The compressed output string.</returns>
public static string DecompressString(byte[] bytes) public static string DecompressString(byte[] bytes)
{ {
using (var msi = new MemoryStream(bytes)) using var msi = new MemoryStream(bytes);
using (var mso = new MemoryStream()) using var mso = new MemoryStream();
{ using var gs = new GZipStream(msi, CompressionMode.Decompress);
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray()); CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
} }
/// <summary> /// <summary>
@ -262,9 +263,6 @@ namespace Dalamud.Utility
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) dest.Write(bytes, 0, cnt); while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) dest.Write(bytes, 0, cnt);
} }
// TODO: Someone implement GetUTF8String with some IntPtr overloads.
// while(Marshal.ReadByte(0, sz) != 0) { sz++; }
/// <summary> /// <summary>
/// Heuristically determine if Dalamud is running on Linux/WINE. /// Heuristically determine if Dalamud is running on Linux/WINE.
/// </summary> /// </summary>