Try to fix missing generals
This commit is contained in:
parent
1ad0087a03
commit
ceea825fa2
|
@ -8,6 +8,7 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using KamihamaWeb.Interfaces;
|
using KamihamaWeb.Interfaces;
|
||||||
using KamihamaWeb.Models;
|
using KamihamaWeb.Models;
|
||||||
|
using KamihamaWeb.Services;
|
||||||
using Marvin.Cache.Headers;
|
using Marvin.Cache.Headers;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -90,7 +91,7 @@ namespace KamihamaWeb.Controllers
|
||||||
{
|
{
|
||||||
var md5 = qs.Value.Substring(1);
|
var md5 = qs.Value.Substring(1);
|
||||||
|
|
||||||
if (_masterService.EnglishMasterAssets.ContainsKey(url))
|
if (!url.StartsWith("scenario/json/general/") && _masterService.EnglishMasterAssets.ContainsKey(url))
|
||||||
{
|
{
|
||||||
if (_masterService.EnglishMasterAssets[url].Md5 == md5)
|
if (_masterService.EnglishMasterAssets[url].Md5 == md5)
|
||||||
{
|
{
|
||||||
|
@ -120,17 +121,17 @@ namespace KamihamaWeb.Controllers
|
||||||
}
|
}
|
||||||
var asset = await _diskCache.Get(url, md5);
|
var asset = await _diskCache.Get(url, md5);
|
||||||
|
|
||||||
if (asset.Item1 == 404)
|
if (asset.Result == DiskCacheResultType.NotFound)
|
||||||
{
|
{
|
||||||
return new APIResult(404, "asset not found");
|
return new APIResult(404, "asset not found");
|
||||||
}
|
}
|
||||||
else if (asset.Item1 == 500)
|
else if (asset.Result == DiskCacheResultType.Failed)
|
||||||
{
|
{
|
||||||
return new APIResult(503, "internal error fetching asset");
|
return new APIResult(503, "internal error fetching asset");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new FileStreamResult(asset.Item2, "binary/octet-stream");
|
return new FileStreamResult(asset.Data, "binary/octet-stream");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using KamihamaWeb.Services;
|
||||||
|
|
||||||
namespace KamihamaWeb.Interfaces
|
namespace KamihamaWeb.Interfaces
|
||||||
{
|
{
|
||||||
|
@ -11,6 +12,7 @@ namespace KamihamaWeb.Interfaces
|
||||||
|
|
||||||
public interface IDiskCacheSingleton: IDiskCacheService
|
public interface IDiskCacheSingleton: IDiskCacheService
|
||||||
{
|
{
|
||||||
public Task<Tuple<int, Stream>> Get(string cacheItem, string versionMd5);
|
public Task<DiskCacheItem> Get(string cacheItem, string versionMd5, bool forceOrigin = false);
|
||||||
|
public Task<string> Store(string filepath, byte[] storeContents, DiskCacheService.StoreType type);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using KamihamaWeb.Models;
|
||||||
|
|
||||||
|
namespace KamihamaWeb.Interfaces
|
||||||
|
{
|
||||||
|
public interface IMasterListBuilder
|
||||||
|
{
|
||||||
|
Task<Dictionary<string, GamedataAsset>> GenerateEnglishAssetList();
|
||||||
|
|
||||||
|
public GamedataAsset GetFileInformation(string file);
|
||||||
|
|
||||||
|
public Task<GamedataAsset> BuildScenarioGeneralJson(GamedataAsset generalAsset,
|
||||||
|
Dictionary<string, GamedataAsset> englishAssets);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using KamihamaWeb.Models;
|
using KamihamaWeb.Models;
|
||||||
|
using KamihamaWeb.Services;
|
||||||
|
|
||||||
namespace KamihamaWeb.Interfaces
|
namespace KamihamaWeb.Interfaces
|
||||||
{
|
{
|
||||||
|
@ -13,7 +14,7 @@ namespace KamihamaWeb.Interfaces
|
||||||
public interface IRestSharpTransient : IRestSharpClient
|
public interface IRestSharpTransient : IRestSharpClient
|
||||||
{
|
{
|
||||||
Task<T> GetMasterJson<T>(string masterJsonEndpoint);
|
Task<T> GetMasterJson<T>(string masterJsonEndpoint);
|
||||||
Task<Tuple<int, Stream>> FetchAsset(string item);
|
Task<DiskCacheItem> FetchAsset(string item);
|
||||||
Task<string> GetAdditionalJson(string item);
|
Task<string> GetAdditionalJson(string item);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -51,7 +51,8 @@ namespace KamihamaWeb.Models
|
||||||
public enum AssetSourceType
|
public enum AssetSourceType
|
||||||
{
|
{
|
||||||
Local,
|
Local,
|
||||||
Remote
|
Remote,
|
||||||
|
GeneralScript
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace KamihamaWeb.Models
|
||||||
|
{
|
||||||
|
public class ScenarioGeneral
|
||||||
|
{
|
||||||
|
|
||||||
|
[JsonProperty("story")]
|
||||||
|
public Dictionary<string, dynamic> story { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("version")]
|
||||||
|
public int version { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,18 +26,42 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheDirectory = config["MagiRecoServer:CacheDirectory"];
|
CacheDirectory = config["MagiRecoServer:CacheDirectory"];
|
||||||
|
ScenarioCacheDirectory = $"{config["MagiRecoServer:CacheDirectory"]}/scenario/json/general/";
|
||||||
Directory.CreateDirectory(CacheDirectory);
|
Directory.CreateDirectory(CacheDirectory);
|
||||||
|
Directory.CreateDirectory(ScenarioCacheDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Guid { get; }
|
public Guid Guid { get; }
|
||||||
private IRestSharpTransient Rest { get; set; }
|
private IRestSharpTransient Rest { get; set; }
|
||||||
private string CacheDirectory { get; set; } = "";
|
private string CacheDirectory { get; set; } = "";
|
||||||
|
private string ScenarioCacheDirectory { get; set; } = "";
|
||||||
|
|
||||||
public async Task<Tuple<int, Stream>> Get(string cacheItem, string versionMd5)
|
public async Task<DiskCacheItem> Get(string cacheItem, string versionMd5, bool forceOrigin = false)
|
||||||
{
|
{
|
||||||
var filename = CryptUtil.CalculateSha256(cacheItem + "?" + versionMd5);
|
var filename = CryptUtil.CalculateSha256(cacheItem + "?" + versionMd5);
|
||||||
|
|
||||||
var filePath = Path.Combine(CacheDirectory, filename);
|
var filePath = Path.Combine(CacheDirectory, filename);
|
||||||
|
|
||||||
|
if (!forceOrigin && cacheItem.StartsWith("scenario/json/general"))
|
||||||
|
{
|
||||||
|
var generalJson = Path.Combine(CacheDirectory, cacheItem + versionMd5);
|
||||||
|
if (File.Exists(generalJson))
|
||||||
|
{
|
||||||
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Data = File.Open(generalJson, FileMode.Open, FileAccess.Read, FileShare.Read),
|
||||||
|
Result = DiskCacheResultType.Success
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Warning($"Cache item {generalJson} not found!");
|
||||||
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.Failed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
Log.Debug($"Loading {cacheItem} from disk ({filePath}).");
|
Log.Debug($"Loading {cacheItem} from disk ({filePath}).");
|
||||||
|
@ -56,7 +80,10 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new Tuple<int, Stream>(0, stream);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Data = stream
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException) // File in use, wait
|
catch (IOException) // File in use, wait
|
||||||
|
@ -66,14 +93,39 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.Warning($"Max loops exceeded in DiskCacheService.Get() for {cacheItem}.");
|
Log.Warning($"Max loops exceeded in DiskCacheService.Get() for {cacheItem}.");
|
||||||
return new Tuple<int, Stream>(500, null);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.Failed
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Log.Information($"Fetching {cacheItem}.");
|
Log.Information($"Fetching {cacheItem}.");
|
||||||
|
|
||||||
return await FastFetch(cacheItem, filePath, versionMd5);
|
return await FastFetch(cacheItem, filePath, versionMd5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Tuple<int, Stream>> FastFetch(string item, string path, string md5)
|
public async Task<string> Store(string filepath, byte[] storeContents, StoreType type)
|
||||||
|
{
|
||||||
|
string storePath;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case StoreType.ScenarioGeneral:
|
||||||
|
var md5 = CryptUtil.CalculateMd5Bytes(storeContents);
|
||||||
|
storePath = Path.Combine(CacheDirectory, filepath + md5);
|
||||||
|
await File.WriteAllBytesAsync(storePath, storeContents);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid StoreType.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum StoreType
|
||||||
|
{
|
||||||
|
ScenarioGeneral
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<DiskCacheItem> FastFetch(string item, string path, string md5)
|
||||||
{
|
{
|
||||||
var maxLoops = 5;
|
var maxLoops = 5;
|
||||||
var deleteFlag = false;
|
var deleteFlag = false;
|
||||||
|
@ -89,19 +141,23 @@ namespace KamihamaWeb.Services
|
||||||
|
|
||||||
if (file.Length > 0)
|
if (file.Length > 0)
|
||||||
{
|
{
|
||||||
return new Tuple<int, Stream>(0, file);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Data = file,
|
||||||
|
Result = DiskCacheResultType.Success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = await Rest.FetchAsset(item + $"?{md5}");
|
var stream = await Rest.FetchAsset(item + $"?{md5}");
|
||||||
|
|
||||||
if (stream.Item2 == null)
|
if (stream.Data == null)
|
||||||
{
|
{
|
||||||
deleteFlag = true;
|
deleteFlag = true;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
((MemoryStream) stream.Item2).WriteTo(file);
|
((MemoryStream) stream.Data).WriteTo(file);
|
||||||
stream.Item2.Seek(0, SeekOrigin.Begin);
|
stream.Data.Seek(0, SeekOrigin.Begin);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
@ -126,9 +182,25 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
Log.Warning($"Max loops exceeded in DiskCacheService.FastFetch() for {item}.");
|
Log.Warning($"Max loops exceeded in DiskCacheService.FastFetch() for {item}.");
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
return new Tuple<int, Stream>(500, null);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.Failed
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DiskCacheItem
|
||||||
|
{
|
||||||
|
public Stream Data { get; set; } = null;
|
||||||
|
public DiskCacheResultType Result { get; set; } = DiskCacheResultType.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DiskCacheResultType
|
||||||
|
{
|
||||||
|
Success = 0,
|
||||||
|
Failed = 500,
|
||||||
|
NotFound = 404
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,27 +2,41 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using KamihamaWeb.Interfaces;
|
||||||
using KamihamaWeb.Models;
|
using KamihamaWeb.Models;
|
||||||
|
using KamihamaWeb.Util;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using StackExchange.Redis;
|
||||||
|
|
||||||
namespace KamihamaWeb.Util
|
namespace KamihamaWeb.Services
|
||||||
{
|
{
|
||||||
public class MasterListBuilder
|
public class MasterListBuilder: IMasterListBuilder
|
||||||
{
|
{
|
||||||
private Regex multiPartRegex = new Regex(@"\.a[a-z]{2}"); // A bit crude
|
private readonly Regex _multiPartRegex = new Regex(@"\.a[a-z]{2}"); // A bit crude
|
||||||
public MasterListBuilder()
|
|
||||||
|
private IDiskCacheSingleton _disk;
|
||||||
|
private IDatabase _cache;
|
||||||
|
|
||||||
|
public MasterListBuilder(IDiskCacheSingleton disk, IDistributedCache cache)
|
||||||
{
|
{
|
||||||
BasePathLength =
|
BasePathLength =
|
||||||
@"MagiRecoStatic/magica/resource/download/asset/master/resource/".Length;
|
@"MagiRecoStatic/magica/resource/download/asset/master/resource/".Length;
|
||||||
|
|
||||||
|
_cache = ((RedisCache)cache).GetConnection().GetDatabase();
|
||||||
|
_disk = disk;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int BasePathLength { get; }
|
private int BasePathLength { get; }
|
||||||
|
|
||||||
|
private string StaticDirectory { get; set; } = "MagiRecoStatic/";
|
||||||
|
|
||||||
public async Task<Dictionary<string, GamedataAsset>> GenerateEnglishAssetList()
|
public async Task<Dictionary<string, GamedataAsset>> GenerateEnglishAssetList()
|
||||||
{
|
{
|
||||||
if (File.Exists("en_cache.json"))
|
if (File.Exists("en_cache.json"))
|
||||||
|
@ -48,9 +62,9 @@ namespace KamihamaWeb.Util
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (Directory.Exists("MagiRecoStatic/"))
|
if (Directory.Exists(StaticDirectory))
|
||||||
{
|
{
|
||||||
List<string> files = Directory.GetFiles("MagiRecoStatic/magica/resource/download/asset/master",
|
List<string> files = Directory.GetFiles(Path.Combine(StaticDirectory, "magica/resource/download/asset/master"),
|
||||||
"*.*",
|
"*.*",
|
||||||
SearchOption.AllDirectories).ToList();
|
SearchOption.AllDirectories).ToList();
|
||||||
|
|
||||||
|
@ -93,7 +107,7 @@ namespace KamihamaWeb.Util
|
||||||
// Remove multi-part files
|
// Remove multi-part files
|
||||||
foreach (var asset in englishAssets)
|
foreach (var asset in englishAssets)
|
||||||
{
|
{
|
||||||
if (multiPartRegex.IsMatch(asset.Value.Path.Substring(asset.Value.Path.Length - 4)))
|
if (_multiPartRegex.IsMatch(asset.Value.Path.Substring(asset.Value.Path.Length - 4)))
|
||||||
{
|
{
|
||||||
Log.Debug($"Removing duplicate asset {asset.Key}");
|
Log.Debug($"Removing duplicate asset {asset.Key}");
|
||||||
englishAssets.Remove(asset.Value.Path);
|
englishAssets.Remove(asset.Value.Path);
|
||||||
|
@ -101,7 +115,7 @@ namespace KamihamaWeb.Util
|
||||||
else if (asset.Key.StartsWith("image_native/mini/")
|
else if (asset.Key.StartsWith("image_native/mini/")
|
||||||
|| asset.Key.StartsWith("image_native/live2d/")
|
|| asset.Key.StartsWith("image_native/live2d/")
|
||||||
//|| asset.Key.StartsWith("image_native/scene/gacha")
|
//|| asset.Key.StartsWith("image_native/scene/gacha")
|
||||||
|| asset.Key.StartsWith("scenario/json/general/")
|
//|| asset.Key.StartsWith("scenario/json/general/")
|
||||||
|| asset.Key.StartsWith("scenario/json/oneShot/")
|
|| asset.Key.StartsWith("scenario/json/oneShot/")
|
||||||
|| asset.Key.StartsWith("image_native/scene/event/")
|
|| asset.Key.StartsWith("image_native/scene/event/")
|
||||||
|| asset.Key.StartsWith("image_native/scene/emotion/")
|
|| asset.Key.StartsWith("image_native/scene/emotion/")
|
||||||
|
@ -163,5 +177,76 @@ namespace KamihamaWeb.Util
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<GamedataAsset> BuildScenarioGeneralJson(GamedataAsset generalAsset, Dictionary<string, GamedataAsset> englishAssets)
|
||||||
|
{
|
||||||
|
Log.Information($"Building scenario JSON for {generalAsset.Path}.");
|
||||||
|
if (!englishAssets.ContainsKey(generalAsset.Path)) // No english to replace with (yet)
|
||||||
|
{
|
||||||
|
Log.Warning($"No english asset for {generalAsset.Path}! This should be caught earlier!");
|
||||||
|
return generalAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JP asset
|
||||||
|
DiskCacheItem jpAsset = await _disk.Get(generalAsset.Path, generalAsset.Md5, true);
|
||||||
|
|
||||||
|
if (jpAsset.Result != DiskCacheResultType.Success)
|
||||||
|
{
|
||||||
|
Log.Warning("An error has occurred fetching a general scenario asset.");
|
||||||
|
return generalAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge assets
|
||||||
|
var enPath = Path.Combine(StaticDirectory, "magica/resource/download/asset/master/resource",
|
||||||
|
generalAsset.Path);
|
||||||
|
|
||||||
|
if (!File.Exists(enPath))
|
||||||
|
{
|
||||||
|
Log.Warning($"File {enPath} does not exist!");
|
||||||
|
return generalAsset;
|
||||||
|
}
|
||||||
|
var mergedAsset = MergeScenarioGeneral(
|
||||||
|
Encoding.UTF8.GetString(
|
||||||
|
CryptUtil.ReadFully(jpAsset.Data)
|
||||||
|
),
|
||||||
|
await File.ReadAllTextAsync(enPath)
|
||||||
|
);
|
||||||
|
|
||||||
|
var storeFilePath = await _disk.Store(generalAsset.Path, Encoding.UTF8.GetBytes(mergedAsset),
|
||||||
|
DiskCacheService.StoreType.ScenarioGeneral);
|
||||||
|
|
||||||
|
generalAsset.AssetSource = AssetSourceType.GeneralScript;
|
||||||
|
generalAsset.Md5 = CryptUtil.CalculateMd5File(storeFilePath);
|
||||||
|
generalAsset.FileList[0].Size = new FileInfo(storeFilePath).Length;
|
||||||
|
return generalAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MergeScenarioGeneral(string jp, string en)
|
||||||
|
{
|
||||||
|
if (jp == en)
|
||||||
|
{
|
||||||
|
return jp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jp_json = JsonConvert.DeserializeObject<ScenarioGeneral>(jp);
|
||||||
|
var en_json = JsonConvert.DeserializeObject<ScenarioGeneral>(en);
|
||||||
|
|
||||||
|
foreach (var item in jp_json.story)
|
||||||
|
{
|
||||||
|
if (!en_json.story.ContainsKey(item.Key))
|
||||||
|
{
|
||||||
|
Log.Information($"Adding key {item.Key}.");
|
||||||
|
en_json.story[item.Key] = item.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*jp_json.Merge(en_json, new JsonMergeSettings()
|
||||||
|
{
|
||||||
|
MergeArrayHandling = MergeArrayHandling.Union,
|
||||||
|
MergeNullValueHandling = MergeNullValueHandling.Ignore,
|
||||||
|
});*/
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(en_json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,12 +21,14 @@ namespace KamihamaWeb.Services
|
||||||
private IDatabase _cache { get; set; }
|
private IDatabase _cache { get; set; }
|
||||||
private IConfiguration _config { get; set; }
|
private IConfiguration _config { get; set; }
|
||||||
private IRestSharpTransient _rest { get; set; }
|
private IRestSharpTransient _rest { get; set; }
|
||||||
|
private IMasterListBuilder _builder { get; set; }
|
||||||
|
|
||||||
public MasterService(
|
public MasterService(
|
||||||
IDistributedCache cache,
|
IDistributedCache cache,
|
||||||
IConfiguration config,
|
IConfiguration config,
|
||||||
IRestSharpTransient rest
|
IRestSharpTransient rest,
|
||||||
) : this(Guid.NewGuid(), cache, config, rest)
|
IMasterListBuilder builder
|
||||||
|
) : this(Guid.NewGuid(), cache, config, rest, builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +36,15 @@ namespace KamihamaWeb.Services
|
||||||
Guid guid,
|
Guid guid,
|
||||||
IDistributedCache cache,
|
IDistributedCache cache,
|
||||||
IConfiguration config,
|
IConfiguration config,
|
||||||
IRestSharpTransient rest
|
IRestSharpTransient rest,
|
||||||
|
IMasterListBuilder builder
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Guid = guid;
|
Guid = guid;
|
||||||
_config = config;
|
_config = config;
|
||||||
_cache = ((RedisCache) cache).GetConnection().GetDatabase();
|
_cache = ((RedisCache) cache).GetConnection().GetDatabase();
|
||||||
_rest = rest;
|
_rest = rest;
|
||||||
|
_builder = builder;
|
||||||
Task.Run(Initialize);
|
Task.Run(Initialize);
|
||||||
}
|
}
|
||||||
public Guid Guid { get; set; }
|
public Guid Guid { get; set; }
|
||||||
|
@ -79,9 +83,13 @@ namespace KamihamaWeb.Services
|
||||||
|
|
||||||
Log.Information("Configuring master list...");
|
Log.Information("Configuring master list...");
|
||||||
|
|
||||||
|
var postProcessingGeneralScenario = new Dictionary<string, GamedataAsset>();
|
||||||
|
|
||||||
long counterReplace = 0;
|
long counterReplace = 0;
|
||||||
long counterSkip = 0;
|
long counterSkip = 0;
|
||||||
long counterNew = 0;
|
long counterNew = 0;
|
||||||
|
long counterPost = 0;
|
||||||
|
|
||||||
foreach (var assetType in workGamedataAssets)
|
foreach (var assetType in workGamedataAssets)
|
||||||
{
|
{
|
||||||
var readyAssets = new Dictionary<string, GamedataAsset>();
|
var readyAssets = new Dictionary<string, GamedataAsset>();
|
||||||
|
@ -90,7 +98,12 @@ namespace KamihamaWeb.Services
|
||||||
// Replace with english assets as needed
|
// Replace with english assets as needed
|
||||||
if (EnglishMasterAssets.ContainsKey(asset.Path))
|
if (EnglishMasterAssets.ContainsKey(asset.Path))
|
||||||
{
|
{
|
||||||
if (EnglishMasterAssets[asset.Path].Md5 != asset.Md5)
|
if (asset.Path.StartsWith("scenario/json/general/"))
|
||||||
|
{
|
||||||
|
postProcessingGeneralScenario.Add(asset.Path, asset);
|
||||||
|
counterPost++;
|
||||||
|
}
|
||||||
|
else if (EnglishMasterAssets[asset.Path].Md5 != asset.Md5)
|
||||||
{
|
{
|
||||||
//Log.Debug($"Replacing Japanese asset with English asset for {asset.Path}.");
|
//Log.Debug($"Replacing Japanese asset with English asset for {asset.Path}.");
|
||||||
readyAssets.Add(asset.Path, EnglishMasterAssets[asset.Path]);
|
readyAssets.Add(asset.Path, EnglishMasterAssets[asset.Path]);
|
||||||
|
@ -112,7 +125,7 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
GamedataAssets.Add(assetType.Key, readyAssets);
|
GamedataAssets.Add(assetType.Key, readyAssets);
|
||||||
}
|
}
|
||||||
Log.Information($"Finished setting up. {counterReplace} replaced assets, {counterSkip} duplicate assets, {counterNew} new assets.");
|
Log.Information($"Finished setting up. {counterReplace} replaced assets, {counterSkip} duplicate assets, {counterNew} new assets, {counterPost} assets for post processing.");
|
||||||
|
|
||||||
// Add scripts
|
// Add scripts
|
||||||
foreach (var asset in EnglishMasterAssets)
|
foreach (var asset in EnglishMasterAssets)
|
||||||
|
@ -128,6 +141,14 @@ namespace KamihamaWeb.Services
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post processing
|
||||||
|
foreach (var asset in postProcessingGeneralScenario)
|
||||||
|
{
|
||||||
|
var builtJson = await _builder.BuildScenarioGeneralJson(asset.Value, EnglishMasterAssets);
|
||||||
|
|
||||||
|
GamedataAssets["asset_main"].Add(builtJson.Path, builtJson);
|
||||||
|
}
|
||||||
IsReady = true;
|
IsReady = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -155,8 +176,8 @@ namespace KamihamaWeb.Services
|
||||||
await Task.Delay(delay);
|
await Task.Delay(delay);
|
||||||
delay *= 2;
|
delay *= 2;
|
||||||
}
|
}
|
||||||
var builder= new MasterListBuilder();
|
|
||||||
var lists = await builder.GenerateEnglishAssetList();
|
var lists = await _builder.GenerateEnglishAssetList();
|
||||||
|
|
||||||
if (lists != null)
|
if (lists != null)
|
||||||
{
|
{
|
||||||
|
@ -189,11 +210,6 @@ namespace KamihamaWeb.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public async Task<FileStream> ProvideFile(string filePath)
|
|
||||||
//{
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
public async Task<string> ProvideJson(string which)
|
public async Task<string> ProvideJson(string which)
|
||||||
{
|
{
|
||||||
if (which == "asset_config")
|
if (which == "asset_config")
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace KamihamaWeb.Services
|
||||||
return result.Content;
|
return result.Content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<int, Stream>> FetchAsset(string item)
|
public async Task<DiskCacheItem> FetchAsset(string item)
|
||||||
{
|
{
|
||||||
var request = new RestRequest("resource/" + item, Method.GET);
|
var request = new RestRequest("resource/" + item, Method.GET);
|
||||||
|
|
||||||
|
@ -82,17 +82,27 @@ namespace KamihamaWeb.Services
|
||||||
|
|
||||||
if (response.StatusCode == HttpStatusCode.NotFound || response.ContentType == "text/html")
|
if (response.StatusCode == HttpStatusCode.NotFound || response.ContentType == "text/html")
|
||||||
{
|
{
|
||||||
return new Tuple<int, Stream>(404, null);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.NotFound
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = new MemoryStream(response.RawBytes);
|
var stream = new MemoryStream(response.RawBytes);
|
||||||
return new Tuple<int, Stream>(0, stream);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.Success,
|
||||||
|
Data = stream
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
Log.Warning($"Web exception thrown: Status code {ex.Status}, {ex.ToString()}");
|
Log.Warning($"Web exception thrown: Status code {ex.Status}, {ex.ToString()}");
|
||||||
}
|
}
|
||||||
return new Tuple<int, Stream>(500, null);
|
return new DiskCacheItem()
|
||||||
|
{
|
||||||
|
Result = DiskCacheResultType.Failed
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using KamihamaWeb.Interfaces;
|
using KamihamaWeb.Interfaces;
|
||||||
using KamihamaWeb.Services;
|
using KamihamaWeb.Services;
|
||||||
|
using KamihamaWeb.Util;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.HttpsPolicy;
|
using Microsoft.AspNetCore.HttpsPolicy;
|
||||||
|
@ -46,6 +47,7 @@ namespace KamihamaWeb
|
||||||
services.AddTransient<IRestSharpTransient, RestSharpClient>();
|
services.AddTransient<IRestSharpTransient, RestSharpClient>();
|
||||||
services.AddSingleton<IMasterSingleton, MasterService>();
|
services.AddSingleton<IMasterSingleton, MasterService>();
|
||||||
services.AddSingleton<IDiskCacheSingleton, DiskCacheService>();
|
services.AddSingleton<IDiskCacheSingleton, DiskCacheService>();
|
||||||
|
services.AddTransient<IMasterListBuilder, MasterListBuilder>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
|
|
@ -32,5 +32,26 @@ namespace KamihamaWeb.Util
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] ReadFully(Stream input)
|
||||||
|
{
|
||||||
|
using MemoryStream ms = new MemoryStream();
|
||||||
|
input.CopyTo(ms);
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object CalculateMd5Bytes(byte[] storeContents)
|
||||||
|
{
|
||||||
|
using var md5 = MD5.Create();
|
||||||
|
using var stream = new MemoryStream(storeContents);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (byte b in md5.ComputeHash(stream))
|
||||||
|
{
|
||||||
|
sb.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue