diff --git a/EventMapHpViewer/EventMapHpViewer.csproj b/EventMapHpViewer/EventMapHpViewer.csproj index 12d4c35..a8bffc9 100644 --- a/EventMapHpViewer/EventMapHpViewer.csproj +++ b/EventMapHpViewer/EventMapHpViewer.csproj @@ -34,6 +34,7 @@ prompt 4 false + latest diff --git a/EventMapHpViewer/Models/MapData.cs b/EventMapHpViewer/Models/MapData.cs index 8b5f6e2..9af3cfa 100644 --- a/EventMapHpViewer/Models/MapData.cs +++ b/EventMapHpViewer/Models/MapData.cs @@ -13,6 +13,7 @@ namespace EventMapHpViewer.Models { public class MapData { + private readonly RemoteSettingsClient client = new RemoteSettingsClient(); public int Id { get; set; } public int IsCleared { get; set; } public int IsExBoss { get; set; } @@ -76,31 +77,30 @@ public async Task GetRemainingCount() if (MapHpSettings.UseLocalBossSettings) { var settings = BossSettingsWrapper.FromSettings.List - .Where(x => x.Id == this.Id) + .Where(x => x.MapId == this.Id) .Where(x => x.Rank == (int)this.Eventmap.SelectedRank) - .Where(x => x.GaugeNum == (this.Eventmap.GaugeNum ?? 1)) + .Where(x => x.GaugeNum == this.Eventmap.GaugeNum) .ToArray(); if (settings.Any()) return this.CalculateRemainingCount(settings); } else { - var client = new RemoteSettingsClient(); var remoteBossData = await client.GetSettings( RemoteSettingsClient.BuildBossSettingsUrl( MapHpSettings.RemoteBossSettingsUrl, this.Id, (int)this.Eventmap.SelectedRank, - this.Eventmap.GaugeNum ?? 1)); + this.Eventmap.GaugeNum ?? 1)); // GaugeNum がない場合どうしよう?とりあえず1 client.CloseConnection(); if (remoteBossData == null) return null; - if (!remoteBossData.Any(x => x.isLast)) + if (!remoteBossData.Any(x => x.last)) return null; - if(!remoteBossData.Any(x => !x.isLast)) + if(!remoteBossData.Any(x => !x.last)) return null; return this.CalculateRemainingCount(BossSettingsWrapper.Parse(remoteBossData)); //イベント海域(リモートデータ) diff --git a/EventMapHpViewer/Models/Raw/map_exboss.cs b/EventMapHpViewer/Models/Raw/map_exboss.cs index bf30f2a..51cdf79 100644 --- a/EventMapHpViewer/Models/Raw/map_exboss.cs +++ b/EventMapHpViewer/Models/Raw/map_exboss.cs @@ -8,23 +8,10 @@ namespace EventMapHpViewer.Models.Raw { class map_exboss { - public bool isLast { get; set; } - public Ship ship { get; set; } - - public class Ship - { - //public string shipName { get; set; } - //public int shipId { get; set; } - //public int shipLv { get; set; } - public int maxhp { get; set; } - //public Slot[] slot { get; set; } - //public int[] param { get; set; } - } - - //public class Slot - //{ - // public int slotitemId { get; set; } - // public string slotitemName { get; set; } - //} + public int mapid { get; set; } + public int rank { get; set; } + public int? gauge { get; set; } + public int maxhp { get; set; } + public bool last { get; set; } } } diff --git a/EventMapHpViewer/Models/Settings/AutoCalcTpSettings.cs b/EventMapHpViewer/Models/Settings/AutoCalcTpSettings.cs index f4b4db5..507c4b8 100644 --- a/EventMapHpViewer/Models/Settings/AutoCalcTpSettings.cs +++ b/EventMapHpViewer/Models/Settings/AutoCalcTpSettings.cs @@ -76,9 +76,14 @@ public AutoCalcTpSettings() private AutoCalcTpSettings(string stypeTp, string slotitemTp, string shipTp) { - this.ShipTypeTp = !string.IsNullOrEmpty(stypeTp) ? DynamicJson.Parse(stypeTp) : Default.ShipTypeTp; - this.SlotItemTp = !string.IsNullOrEmpty(slotitemTp) ? DynamicJson.Parse(slotitemTp) : Default.SlotItemTp; - this.ShipTp = !string.IsNullOrEmpty(shipTp) ? DynamicJson.Parse(shipTp) : Default.ShipTp; + try { this.ShipTypeTp = DynamicJson.Parse(stypeTp); } + catch { this.ShipTypeTp = Default.ShipTypeTp; } + + try { this.SlotItemTp = DynamicJson.Parse(slotitemTp); } + catch { this.SlotItemTp = Default.SlotItemTp; } + + try { this.ShipTp = DynamicJson.Parse(shipTp); } + catch { this.ShipTp = Default.ShipTp; } } private AutoCalcTpSettings(IEnumerable stypeTp, IEnumerable slotitemTp, IEnumerable shipTp) diff --git a/EventMapHpViewer/Models/Settings/BossSettingsWrapper.cs b/EventMapHpViewer/Models/Settings/BossSettingsWrapper.cs index 68f4471..1de5394 100644 --- a/EventMapHpViewer/Models/Settings/BossSettingsWrapper.cs +++ b/EventMapHpViewer/Models/Settings/BossSettingsWrapper.cs @@ -27,17 +27,26 @@ private set public BossSettingsWrapper(string json = "") { - this.List = !string.IsNullOrEmpty(json) - ? DynamicJson.Parse(json) - : new ObservableSynchronizedCollection(); + try + { + BossSettingForParse[] parsed = DynamicJson.Parse(json); + this.List = new ObservableSynchronizedCollection(parsed.Select(x => x.ToValue())); + } + catch + { + this.List = new ObservableSynchronizedCollection(); + } } public static IEnumerable Parse(IEnumerable source) { return source.Select(x => new BossSetting { - BossHP = x.ship.maxhp, - IsLast = x.isLast, + MapId = x.mapid, + Rank = x.rank, + GaugeNum = x.gauge, + BossHP = x.maxhp, + IsLast = x.last, }); } @@ -49,13 +58,31 @@ public static BossSettingsWrapper FromSettings public class BossSetting { - public int Id { get; set; } + public int MapId { get; set; } public int Rank { get; set; } - public int GaugeNum { get; set; } + public int? GaugeNum { get; set; } public int BossHP { get; set; } public bool IsLast { get; set; } } + public class BossSettingForParse + { + public int MapId { get; set; } + public int Rank { get; set; } + public string GaugeNum { get; set; } // DynamicJson は int? に Parse できない + public int BossHP { get; set; } + public bool IsLast { get; set; } + public BossSetting ToValue() + => new BossSetting + { + MapId = this.MapId, + Rank = this.Rank, + GaugeNum = int.TryParse(this.GaugeNum, out var num) ? num : (int?)null, + BossHP = this.BossHP, + IsLast = this.IsLast, + }; + } + static class BossSettingsWrapperExtensions { public static void Save(this BossSettingsWrapper settings) diff --git a/EventMapHpViewer/Models/Settings/MapHpSettings.cs b/EventMapHpViewer/Models/Settings/MapHpSettings.cs index 5f5ce53..6e7f6a4 100644 --- a/EventMapHpViewer/Models/Settings/MapHpSettings.cs +++ b/EventMapHpViewer/Models/Settings/MapHpSettings.cs @@ -57,7 +57,7 @@ static class MapHpSettings public static SerializableProperty RemoteBossSettingsUrl { get; } = new SerializableProperty(GetKey(), roamingProvider, - "https://kctadil-admin.azurewebsites.net/admin/maphp/{version}/{id}/{rank}/{gaugeNum}" + "https://kctadilstorage.blob.core.windows.net/viewer/maphp/{mapId}/{rank}/{gaugeNum}.json" ) { AutoSave = true }; #endregion diff --git a/EventMapHpViewer/Models/Settings/RemoteSettingsClient.cs b/EventMapHpViewer/Models/Settings/RemoteSettingsClient.cs index 35cc807..858b0b2 100644 --- a/EventMapHpViewer/Models/Settings/RemoteSettingsClient.cs +++ b/EventMapHpViewer/Models/Settings/RemoteSettingsClient.cs @@ -10,6 +10,7 @@ using System.Net.Http; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -19,14 +20,18 @@ class RemoteSettingsClient { private HttpClient client; - private ConcurrentDictionary lastModified; + private readonly ConcurrentDictionary lastModified; - private ConcurrentDictionary caches; + private readonly ConcurrentDictionary caches; private TimeSpan cacheTtl; private bool updating; + private static readonly object errorObject = new object(); + + public bool IsCacheError { get; set; } = true; + #if DEBUG public RemoteSettingsClient() : this(TimeSpan.FromSeconds(10)) { } #else @@ -62,11 +67,12 @@ public async Task GetSettings(string url) if (DateTimeOffset.Now - lm < this.cacheTtl) { - object value; - if (this.caches.TryGetValue(url, out value) - && value is T) + if (this.caches.TryGetValue(url, out var value)) { - return (T)value; + if (value is T) + return (T)value; + else if (value == errorObject) + return null; } } this.updating = true; @@ -85,6 +91,7 @@ public async Task GetSettings(string url) if (!response.IsSuccessStatusCode) { // 200 じゃなかった + this.CacheError(url, lm); return null; } @@ -97,6 +104,13 @@ public async Task GetSettings(string url) catch (HttpRequestException) { // HTTP リクエストに失敗した + this.CacheError(url, lm); + return null; + } + catch + { + // 不正な JSON 等 + this.CacheError(url, lm); return null; } finally @@ -105,6 +119,14 @@ public async Task GetSettings(string url) } } + private void CacheError(string url, DateTimeOffset lastModified) + { + if (!this.IsCacheError) + return; + this.lastModified.TryUpdate(url, DateTimeOffset.Now, lastModified); + this.caches.AddOrUpdate(url, errorObject, (_, __) => errorObject); + } + public void CloseConnection() { this.client?.Dispose(); @@ -152,7 +174,7 @@ public static string BuildBossSettingsUrl(string url, int id, int rank, int gaug return BuildUrl(url, new Dictionary { { "version", $"{MapHpViewer.version}" }, - { "id", id.ToString() }, + { "mapId", id.ToString() }, { "rank", rank.ToString() }, { "gaugeNum", gaugeNum.ToString() }, }); @@ -164,7 +186,8 @@ public static string BuildUrl(string url, IDictionary placeHolde return url; foreach(var placeHolder in placeHolders) { - url = url.Replace($"{{{placeHolder.Key}}}", placeHolder.Value); + var regex = new Regex($"{{{placeHolder.Key}}}", RegexOptions.IgnoreCase | RegexOptions.Singleline); + url = regex.Replace(url, placeHolder.Value); } return url; } diff --git a/EventMapHpViewer/ViewModels/Settings/BossSettingsViewModel.cs b/EventMapHpViewer/ViewModels/Settings/BossSettingsViewModel.cs index 097bf33..73dbdd3 100644 --- a/EventMapHpViewer/ViewModels/Settings/BossSettingsViewModel.cs +++ b/EventMapHpViewer/ViewModels/Settings/BossSettingsViewModel.cs @@ -44,16 +44,16 @@ public bool UseLocalBossSettings } #endregion - #region Id - private int _Id; - public int Id + #region MapId + private int _MapId; + public int MapId { - get => this._Id; + get => this._MapId; set { - if (value == this._Id) + if (value == this._MapId) return; - this._Id = value; + this._MapId = value; this.RaisePropertyChanged(); } } @@ -100,8 +100,8 @@ public int Rank #endregion #region GaugeNum - private int _GaugeNum; - public int GaugeNum + private string _GaugeNum; + public string GaugeNum { get => this._GaugeNum; set @@ -148,15 +148,15 @@ public bool IsLast public bool IsAddEnabled { get => !this.Settings.List.Any( - x => x.Id == this.Id + x => x.MapId == this.MapId && x.Rank == this.Rank - && x.GaugeNum == this.GaugeNum + && x.GaugeNum.ToString() == this.GaugeNum && x.BossHP == this.BossHP && x.IsLast == this.IsLast ) - && this.Id != default + && this.MapId != default && this.Rank != default - && this.GaugeNum != default + // && this.GaugeNum != default // 空もありとする && this.BossHP != default; } #endregion @@ -178,15 +178,15 @@ public BossSetting SelectedBossSetting this._SelectedBossSetting = value; if(value != null) { - this.Id = value.Id; + this.MapId = value.MapId; this.Rank = value.Rank; - this.GaugeNum = value.GaugeNum; + this.GaugeNum = value.GaugeNum.ToString(); this.BossHP = value.BossHP; this.IsLast = value.IsLast; } else { - this.Id = default; + this.MapId = default; this.Rank = default; this.GaugeNum = default; this.BossHP = default; @@ -234,7 +234,7 @@ public BossSettingsViewModel() this.Settings = BossSettingsWrapper.FromSettings; this.BossSettings = this.Settings.List - .ToSyncedSortedObservableCollection(x => $"{x.Id:D4}{x.Rank:D2}{x.GaugeNum:D2}{(x.IsLast ? 1 : 0)}{x.BossHP:D4}") + .ToSyncedSortedObservableCollection(x => $"{x.MapId:D4}{x.Rank:D2}{x.GaugeNum ?? 0:D2}{(x.IsLast ? 1 : 0)}{x.BossHP:D4}") .ToSyncedSynchronizationContextCollection(SynchronizationContext.Current) .ToSyncedReadOnlyNotifyChangedCollection(); } @@ -243,9 +243,9 @@ public void Add() { var newValue = new BossSetting { - Id = this.Id, + MapId = this.MapId, Rank = this.Rank, - GaugeNum = this.GaugeNum, + GaugeNum = int.TryParse(this.GaugeNum, out var num) ? num : (int?)null, BossHP = this.BossHP, IsLast = this.IsLast }; diff --git a/EventMapHpViewer/ViewModels/ToolViewModel.cs b/EventMapHpViewer/ViewModels/ToolViewModel.cs index f153a67..4e80790 100644 --- a/EventMapHpViewer/ViewModels/ToolViewModel.cs +++ b/EventMapHpViewer/ViewModels/ToolViewModel.cs @@ -58,7 +58,6 @@ public ToolViewModel(MapInfoProxy proxy) public void Initialize() { - Debug.WriteLine("ToolViewModel: Initialize()"); KanColleClient.Current.Homeport.Organization .Subscribe(nameof(Organization.Fleets), this.UpdateFleets, false) .Subscribe(nameof(Organization.Combined), this.UpdateTransportCapacity, false) diff --git a/EventMapHpViewer/Views/Settings/BossSettings.xaml b/EventMapHpViewer/Views/Settings/BossSettings.xaml index 17db2ba..83cac73 100644 --- a/EventMapHpViewer/Views/Settings/BossSettings.xaml +++ b/EventMapHpViewer/Views/Settings/BossSettings.xaml @@ -90,8 +90,7 @@ - - + @@ -130,8 +129,8 @@ - + + Margin="0,4" + Prompt=""> + AllowsEmpty="True"/> @@ -204,7 +206,8 @@ Text="ボスHP: "/> + Margin="0,4" + Prompt="">