From 9b2b71235c4c56b7bf6eea9326f244e93773584f Mon Sep 17 00:00:00 2001 From: Jos Demmers Date: Sat, 10 Dec 2022 19:08:50 +0100 Subject: [PATCH] Added extended tooltip for raw resources. Improved exception logging. --- .../NwmarketpriceJson.cs | 1 + .../INewWorldDataStore.cs | 1 + NewWorldCompanion.Interfaces/IPriceManager.cs | 2 + .../HttpClientHandler.cs | 2 +- .../NewWorldDataStore.cs | 7 + NewWorldCompanion.Services/OverlayHandler.cs | 48 ++++-- NewWorldCompanion.Services/PriceManager.cs | 147 +++++++++++++++++- NewWorldCompanion/Config/NLog.config | 6 +- .../Tabs/Config/ConfigOverlayViewModel.cs | 1 - NewWorldCompanion/common.props | 4 +- 10 files changed, 198 insertions(+), 21 deletions(-) diff --git a/NewWorldCompanion.Entities/NwmarketpriceJson.cs b/NewWorldCompanion.Entities/NwmarketpriceJson.cs index 2583975..c084587 100644 --- a/NewWorldCompanion.Entities/NwmarketpriceJson.cs +++ b/NewWorldCompanion.Entities/NwmarketpriceJson.cs @@ -14,6 +14,7 @@ namespace NewWorldCompanion.Entities public class NwmarketpriceJson { public string item_name { get; set; } = string.Empty; + public string nwdb_id { get; set; } = string.Empty; public DateTime last_checked { get; set; } = DateTime.MinValue; public double recent_lowest_price { get; set; } = 0.0; public List price_graph_data { get; set; } = new List(); diff --git a/NewWorldCompanion.Interfaces/INewWorldDataStore.cs b/NewWorldCompanion.Interfaces/INewWorldDataStore.cs index 9835203..f29813e 100644 --- a/NewWorldCompanion.Interfaces/INewWorldDataStore.cs +++ b/NewWorldCompanion.Interfaces/INewWorldDataStore.cs @@ -17,5 +17,6 @@ public interface INewWorldDataStore List GetOverlayResources(); string GetItemLocalisation(string itemMasterName); List GetRelatedRecipes(string itemId); + CraftingRecipeJson GetCraftingRecipeDetails(string itemId); } } diff --git a/NewWorldCompanion.Interfaces/IPriceManager.cs b/NewWorldCompanion.Interfaces/IPriceManager.cs index 14d600a..ecbadd3 100644 --- a/NewWorldCompanion.Interfaces/IPriceManager.cs +++ b/NewWorldCompanion.Interfaces/IPriceManager.cs @@ -12,6 +12,8 @@ public interface IPriceManager List Servers { get; } NwmarketpriceJson GetPriceData(string itemName); + double GetCraftingCosts(string itemId); + List GetExtendedPriceData(string itemName); void UpdatePriceData(string itemName); } } diff --git a/NewWorldCompanion.Services/HttpClientHandler.cs b/NewWorldCompanion.Services/HttpClientHandler.cs index bb7b011..bcf3b4c 100644 --- a/NewWorldCompanion.Services/HttpClientHandler.cs +++ b/NewWorldCompanion.Services/HttpClientHandler.cs @@ -68,7 +68,7 @@ public async Task GetRequest(string uri) } catch (Exception ex) { - _logger.LogError(ex, MethodBase.GetCurrentMethod()?.Name); + _logger.LogError(ex, $"GetRequest({uri})"); return string.Empty; } diff --git a/NewWorldCompanion.Services/NewWorldDataStore.cs b/NewWorldCompanion.Services/NewWorldDataStore.cs index 0603d81..4fadd33 100644 --- a/NewWorldCompanion.Services/NewWorldDataStore.cs +++ b/NewWorldCompanion.Services/NewWorldDataStore.cs @@ -416,6 +416,13 @@ public List GetRelatedRecipes(string itemId) recipe.Ingredient7.Equals(itemId))); } + public CraftingRecipeJson GetCraftingRecipeDetails(string itemId) + { + var craftingRecipesJson = new CraftingRecipeJson(); + craftingRecipesJson = _craftingRecipesJson.FirstOrDefault(recipe => recipe.ItemID.ToLower().Equals(itemId), craftingRecipesJson); + return craftingRecipesJson; + } + #endregion } diff --git a/NewWorldCompanion.Services/OverlayHandler.cs b/NewWorldCompanion.Services/OverlayHandler.cs index 524a542..24a6895 100644 --- a/NewWorldCompanion.Services/OverlayHandler.cs +++ b/NewWorldCompanion.Services/OverlayHandler.cs @@ -17,6 +17,7 @@ namespace NewWorldCompanion.Services public class OverlayHandler : IOverlayHandler { private readonly IEventAggregator _eventAggregator; + private readonly ISettingsManager _settingsManager; private readonly ICraftingRecipeManager _craftingRecipeManager; private readonly INewWorldDataStore _newWorldDataStore; private readonly IOcrHandler _ocrHandler; @@ -40,7 +41,7 @@ public class OverlayHandler : IOverlayHandler #region Constructor - public OverlayHandler(IEventAggregator eventAggregator, ICraftingRecipeManager craftingRecipeManager, INewWorldDataStore newWorldDataStore, + public OverlayHandler(IEventAggregator eventAggregator, ISettingsManager settingsManager, ICraftingRecipeManager craftingRecipeManager, INewWorldDataStore newWorldDataStore, IOcrHandler ocrHandler, IPriceManager priceManager, IScreenProcessHandler screenProcessHandler) { // Init IEventAggregator @@ -51,6 +52,7 @@ public OverlayHandler(IEventAggregator eventAggregator, ICraftingRecipeManager c _eventAggregator.GetEvent().Subscribe(HandleRoiImageReadyEvent); // Init services + _settingsManager = settingsManager; _craftingRecipeManager = craftingRecipeManager; _newWorldDataStore = newWorldDataStore; _ocrHandler = ocrHandler; @@ -112,11 +114,6 @@ private void DrawGraphics(object? sender, DrawGraphicsEventArgs e) return; } - _window.X = _overlayX; - _window.Y = _overlayY; - _window.Width = _overlayWidth; - _window.Height = _overlayHeigth; - string itemName = _itemName; string itemId = _newWorldDataStore.GetItemId(itemName); var craftingRecipe = string.IsNullOrWhiteSpace(itemId) @@ -136,10 +133,11 @@ private void DrawGraphics(object? sender, DrawGraphicsEventArgs e) private void DrawGraphicsItem(DrawGraphicsEventArgs e, string itemName) { NwmarketpriceJson nwmarketpriceJson = _priceManager.GetPriceData(itemName); + List extendedNwmarketpriceJson = _priceManager.GetExtendedPriceData(itemName); string infoItemName = itemName; string infoPrice = "Loading..."; - string infoPriceAvg = string.Empty; + string infoPriceAvg = string.Empty; if (!string.IsNullOrWhiteSpace(nwmarketpriceJson.item_name)) { @@ -152,12 +150,35 @@ private void DrawGraphicsItem(DrawGraphicsEventArgs e, string itemName) // Do not show Bind on pickup items. if (!_newWorldDataStore.IsBindOnPickup(itemName)) { + int ExtendedTooltipOffset = _settingsManager.Settings.ExtendedTooltipEnabled ? Math.Min(extendedNwmarketpriceJson.Count, 3) * 20 : 0; + // Add some extra margin + ExtendedTooltipOffset = ExtendedTooltipOffset == 0 ? 0 : ExtendedTooltipOffset + 40; + + _window.X = _overlayX; + _window.Y = _overlayY - ExtendedTooltipOffset; + _window.Width = _overlayWidth; + _window.Height = _overlayHeigth + ExtendedTooltipOffset; + var gfx = e.Graphics; gfx.ClearScene(_brushes["background"]); - gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 20, infoItemName); - gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 40, infoPrice); - gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 60, infoPriceAvg); - gfx.DrawRectangle(_brushes["border"], 0, 0, _overlayWidth, _overlayHeigth, 1); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, ExtendedTooltipOffset+20, infoItemName); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, ExtendedTooltipOffset+40, infoPrice); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, ExtendedTooltipOffset+60, infoPriceAvg); + gfx.DrawRectangle(_brushes["border"], 0, 0, _overlayWidth, _overlayHeigth + ExtendedTooltipOffset, 1); + + // Extended text + if (ExtendedTooltipOffset > 0) + { + for (int i = 0; i < Math.Min(extendedNwmarketpriceJson.Count, 3); i++) + { + var craftingCosts = _priceManager.GetCraftingCosts(extendedNwmarketpriceJson[i].nwdb_id); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, ((i+1)*20), $"{extendedNwmarketpriceJson[i].item_name} " + + $"{extendedNwmarketpriceJson[i].recent_lowest_price.ToString("F2")} (Sell) " + + $"{craftingCosts.ToString("F2")} (Craft) " + + $"{extendedNwmarketpriceJson[i].recent_lowest_price - craftingCosts:F2} (Profit)"); + } + gfx.DrawRectangle(_brushes["border"], 0, 0, _overlayWidth, ExtendedTooltipOffset, 1); + } } else { @@ -183,6 +204,11 @@ private void DrawGraphicsRecipe(DrawGraphicsEventArgs e, CraftingRecipe crafting infoPriceAvg = string.IsNullOrWhiteSpace(infoPriceAvg) ? infoPriceAvg : $"{infoPriceAvg} (15-day avg) ({nwmarketpriceJson.last_checked_string})"; } + _window.X = _overlayX; + _window.Y = _overlayY; + _window.Width = _overlayWidth; + _window.Height = _overlayHeigth; + var gfx = e.Graphics; gfx.ClearScene(_brushes["background"]); gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 20, infoItemName); diff --git a/NewWorldCompanion.Services/PriceManager.cs b/NewWorldCompanion.Services/PriceManager.cs index b8827d7..9f7ca42 100644 --- a/NewWorldCompanion.Services/PriceManager.cs +++ b/NewWorldCompanion.Services/PriceManager.cs @@ -11,6 +11,7 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; namespace NewWorldCompanion.Services @@ -22,6 +23,7 @@ public class PriceManager : IPriceManager private readonly ISettingsManager _settingsManager; private readonly IHttpClientHandler _httpClientHandler; private readonly INewWorldDataStore _newWorldDataStore; + private readonly IRelatedPriceManager _relatedPriceManager; private readonly object priceRequestLock = new object(); @@ -34,7 +36,7 @@ public class PriceManager : IPriceManager #region Constructor - public PriceManager(IEventAggregator eventAggregator, ILogger logger, ISettingsManager settingsManager, IHttpClientHandler httpClientHandler, INewWorldDataStore newWorldDataStore) + public PriceManager(IEventAggregator eventAggregator, ILogger logger, ISettingsManager settingsManager, IHttpClientHandler httpClientHandler, INewWorldDataStore newWorldDataStore, IRelatedPriceManager relatedPriceManager) { // Init IEventAggregator _eventAggregator = eventAggregator; @@ -46,9 +48,10 @@ public PriceManager(IEventAggregator eventAggregator, ILogger logg _settingsManager = settingsManager; _httpClientHandler = httpClientHandler; _newWorldDataStore = newWorldDataStore; + _relatedPriceManager = relatedPriceManager; - // Init servers - UpdateServerList(); + // Init servers + UpdateServerList(); } #endregion @@ -108,8 +111,135 @@ public NwmarketpriceJson GetPriceData(string itemName) return _priceCache.GetValueOrDefault(itemName, nwmarketpriceJson); ; } + public double GetCraftingCosts(string itemId) + { + var recipe = _newWorldDataStore.GetCraftingRecipeDetails(itemId); + double craftingCosts = 0; + + var ingredient = string.Empty; + var nwmarketpriceJson = new NwmarketpriceJson(); + var qty = recipe.Qty1; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient1) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient1.Last()) ? recipe.Ingredient1 : $"{recipe.Ingredient1}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty2; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient2) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient2.Last()) ? recipe.Ingredient2 : $"{recipe.Ingredient2}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty3; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient3) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient3.Last()) ? recipe.Ingredient3 : $"{recipe.Ingredient3}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty4; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient4) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient4.Last()) ? recipe.Ingredient4 : $"{recipe.Ingredient4}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty5; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient5) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient5.Last()) ? recipe.Ingredient5 : $"{recipe.Ingredient5}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty6; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient6) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient6.Last()) ? recipe.Ingredient6 : $"{recipe.Ingredient6}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + qty = recipe.Qty7; + if (!string.IsNullOrWhiteSpace(recipe.Ingredient7) && qty != 0) + { + ingredient = Char.IsDigit(recipe.Ingredient7.Last()) ? recipe.Ingredient7 : $"{recipe.Ingredient7}T1"; + + string ingredientName = _newWorldDataStore.GetItem(ingredient)?.Name ?? string.Empty; + ingredientName = _newWorldDataStore.GetItemLocalisation(ingredientName); + UpdatePriceData(ingredientName); + nwmarketpriceJson = GetPriceData(ingredientName); + craftingCosts = craftingCosts + (nwmarketpriceJson.recent_lowest_price * qty); + } + + return craftingCosts; + } + + public List GetExtendedPriceData(string itemName) + { + var extendedPriceDataList = new List(); + var itemId = _newWorldDataStore.GetItemId(itemName); + var overlayResource = _relatedPriceManager.PersistableOverlayResources.FirstOrDefault(item => item.ItemId.Equals(itemId)); + if (overlayResource != null) + { + foreach (var recipe in overlayResource.PersistableOverlayResourceRecipes) + { + if (!recipe.IsVisible) continue; + + string recipeName = _newWorldDataStore.GetItem(recipe.ItemId)?.Name ?? string.Empty; + recipeName = _newWorldDataStore.GetItemLocalisation(recipeName); + if (string.IsNullOrWhiteSpace(recipeName)) + { + continue; + } + UpdatePriceData(recipeName); + + var nwmarketpriceJson = new NwmarketpriceJson(); + nwmarketpriceJson = _priceCache.GetValueOrDefault(recipeName, nwmarketpriceJson); + extendedPriceDataList.Add(nwmarketpriceJson); + } + } + + return extendedPriceDataList; + } + public void UpdatePriceData(string itemName) { + if (string.IsNullOrWhiteSpace(itemName)) + { + return; + } + if (!_priceCache.ContainsKey(itemName)) { if (!_priceRequestQueue.Contains(itemName)) _priceRequestQueue.Add(itemName); @@ -164,6 +294,8 @@ public void UpdatePriceData(string itemName) last_checked = DateTime.MinValue, }; } + + //Thread.Sleep(100); } catch (Exception ex) { @@ -174,7 +306,14 @@ public void UpdatePriceData(string itemName) // Always remove from queue, even with exceptions. _priceRequestQueue.RemoveAll(item => item.Equals(itemName)); _priceRequestQueueBusy = false; - Task.Delay(100).Wait(); + + + var sw = new Stopwatch(); + sw.Start(); + //Task delay = Task.Delay(100); + //await delay; + Task.Delay(250).Wait(); + Debug.WriteLine($"async: Running for {sw.Elapsed.TotalSeconds} seconds"); if (_priceRequestQueue.Any()) { diff --git a/NewWorldCompanion/Config/NLog.config b/NewWorldCompanion/Config/NLog.config index 329ec01..fbf7762 100644 --- a/NewWorldCompanion/Config/NLog.config +++ b/NewWorldCompanion/Config/NLog.config @@ -6,12 +6,14 @@ - + diff --git a/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs b/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs index 13b6148..0704b16 100644 --- a/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs +++ b/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs @@ -81,7 +81,6 @@ public bool ToggleExtendedTooltip _toggleExtendedTooltip = value; _settingsManager.Settings.ExtendedTooltipEnabled = value; _settingsManager.SaveSettings(); - // TODO: Notify tooltip handler } } diff --git a/NewWorldCompanion/common.props b/NewWorldCompanion/common.props index 1e1a7ea..f38b21d 100644 --- a/NewWorldCompanion/common.props +++ b/NewWorldCompanion/common.props @@ -1,7 +1,7 @@ - 1.0.9.1 - 1.0.9.1 + 1.0.9.2 + 1.0.9.2 Copyright © 2022 net6.0-windows