trivia questions and pokemon data will be stored in redis, instead of per-shard

This commit is contained in:
Master Kwoth 2017-11-06 10:01:38 +01:00
parent 817fd7ce31
commit e79b3db818
13 changed files with 195 additions and 77 deletions

View File

@ -0,0 +1,8 @@
namespace NadekoBot.Core.Common.Pokemon
{
public class PokemonNameId
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace NadekoBot.Modules.Searches.Common namespace NadekoBot.Core.Common.Pokemon
{ {
public class SearchPokemon public class SearchPokemon
{ {
@ -9,6 +9,7 @@ namespace NadekoBot.Modules.Searches.Common
public float M { get; set; } public float M { get; set; }
public float F { get; set; } public float F { get; set; }
} }
public class BaseStatsClass public class BaseStatsClass
{ {
public int HP { get; set; } public int HP { get; set; }
@ -21,6 +22,7 @@ namespace NadekoBot.Modules.Searches.Common
public override string ToString() => $@"**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4} public override string ToString() => $@"**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4}
**SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}"; **SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}";
} }
public int Id { get; set; } public int Id { get; set; }
public string Species { get; set; } public string Species { get; set; }
public string[] Types { get; set; } public string[] Types { get; set; }
@ -32,24 +34,5 @@ namespace NadekoBot.Modules.Searches.Common
public string Color { get; set; } public string Color { get; set; }
public string[] Evos { get; set; } public string[] Evos { get; set; }
public string[] EggGroups { get; set; } public string[] EggGroups { get; set; }
// public override string ToString() => $@"`Name:` {Species}
//`Types:` {string.Join(", ", Types)}
//`Stats:` {BaseStats}
//`Height:` {HeightM,4}m `Weight:` {WeightKg}kg
//`Abilities:` {string.Join(", ", Abilities.Values)}";
}
public class SearchPokemonAbility
{
public string Desc { get; set; }
public string ShortDesc { get; set; }
public string Name { get; set; }
public float Rating { get; set; }
// public override string ToString() => $@"`Name:` : {Name}
//`Rating:` {Rating}
//`Description:` {Desc}";
} }
} }

View File

@ -0,0 +1,10 @@
namespace NadekoBot.Core.Common.Pokemon
{
public class SearchPokemonAbility
{
public string Desc { get; set; }
public string ShortDesc { get; set; }
public string Name { get; set; }
public float Rating { get; set; }
}
}

View File

@ -19,6 +19,7 @@ namespace NadekoBot.Modules.Games.Common.Trivia
{ {
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
private readonly Logger _log; private readonly Logger _log;
private readonly IDataCache _cache;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotConfigProvider _bc; private readonly IBotConfigProvider _bc;
@ -43,11 +44,15 @@ namespace NadekoBot.Modules.Games.Common.Trivia
public int WinRequirement { get; } public int WinRequirement { get; }
private readonly TriviaQuestionPool _questionPool;
public TriviaGame(NadekoStrings strings, DiscordSocketClient client, IBotConfigProvider bc, public TriviaGame(NadekoStrings strings, DiscordSocketClient client, IBotConfigProvider bc,
CurrencyService cs, IGuild guild, ITextChannel channel, IDataCache cache, CurrencyService cs, IGuild guild, ITextChannel channel,
bool showHints, int winReq, bool isPokemon) bool showHints, int winReq, bool isPokemon)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_cache = cache;
_questionPool = new TriviaQuestionPool(_cache);
_strings = strings; _strings = strings;
_client = client; _client = client;
_bc = bc; _bc = bc;
@ -74,7 +79,7 @@ namespace NadekoBot.Modules.Games.Common.Trivia
_triviaCancelSource = new CancellationTokenSource(); _triviaCancelSource = new CancellationTokenSource();
// load question // load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(OldQuestions, IsPokemon); CurrentQuestion = _questionPool.GetRandomQuestion(OldQuestions, IsPokemon);
if (string.IsNullOrWhiteSpace(CurrentQuestion?.Answer) || string.IsNullOrWhiteSpace(CurrentQuestion.Question)) if (string.IsNullOrWhiteSpace(CurrentQuestion?.Answer) || string.IsNullOrWhiteSpace(CurrentQuestion.Question))
{ {
await Channel.SendErrorAsync(GetText("trivia_game"), GetText("failed_loading_question")).ConfigureAwait(false); await Channel.SendErrorAsync(GetText("trivia_game"), GetText("failed_loading_question")).ConfigureAwait(false);

View File

@ -1,62 +1,43 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using NadekoBot.Common; using NadekoBot.Common;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Newtonsoft.Json; using NadekoBot.Core.Services;
namespace NadekoBot.Modules.Games.Common.Trivia namespace NadekoBot.Modules.Games.Common.Trivia
{ {
public class TriviaQuestionPool public class TriviaQuestionPool
{ {
public class PokemonNameId private readonly IDataCache _cache;
{
public int Id { get; set; }
public string Name { get; set; }
}
private static TriviaQuestionPool _instance;
public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
private const string questionsFile = "data/trivia_questions.json";
private const string pokemonMapPath = "data/pokemon/name-id_map4.json";
private readonly int maxPokemonId; private readonly int maxPokemonId;
private Random rng { get; } = new NadekoRandom(); private readonly NadekoRandom _rng = new NadekoRandom();
private TriviaQuestion[] pool { get; }
private ImmutableDictionary<int, string> map { get; }
static TriviaQuestionPool() { } private TriviaQuestion[] Pool => _cache.LocalData.TriviaQuestions;
private IReadOnlyDictionary<int, string> Map => _cache.LocalData.PokemonMap;
private TriviaQuestionPool() public TriviaQuestionPool(IDataCache cache)
{ {
pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile)); _cache = cache;
map = JsonConvert.DeserializeObject<PokemonNameId[]>(File.ReadAllText(pokemonMapPath))
.ToDictionary(x => x.Id, x => x.Name)
.ToImmutableDictionary();
maxPokemonId = 721; //xd maxPokemonId = 721; //xd
} }
public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude, bool isPokemon) public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude, bool isPokemon)
{ {
if (pool.Length == 0) if (Pool.Length == 0)
return null; return null;
if (isPokemon) if (isPokemon)
{ {
var num = rng.Next(1, maxPokemonId + 1); var num = _rng.Next(1, maxPokemonId + 1);
return new TriviaQuestion("Who's That Pokémon?", return new TriviaQuestion("Who's That Pokémon?",
map[num].ToTitleCase(), Map[num].ToTitleCase(),
"Pokemon", "Pokemon",
$@"http://nadekobot.me/images/pokemon/shadows/{num}.png", $@"http://nadekobot.me/images/pokemon/shadows/{num}.png",
$@"http://nadekobot.me/images/pokemon/real/{num}.png"); $@"http://nadekobot.me/images/pokemon/real/{num}.png");
} }
TriviaQuestion randomQuestion; TriviaQuestion randomQuestion;
while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ; while (exclude.Contains(randomQuestion = Pool[_rng.Next(0, Pool.Length)])) ;
return randomQuestion; return randomQuestion;
} }

View File

@ -15,12 +15,15 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class TriviaCommands : NadekoSubmodule<GamesService> public class TriviaCommands : NadekoSubmodule<GamesService>
{ {
private readonly IDataCache _cache;
private readonly CurrencyService _cs; private readonly CurrencyService _cs;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotConfigProvider _bc; private readonly IBotConfigProvider _bc;
public TriviaCommands(DiscordSocketClient client, IBotConfigProvider bc, CurrencyService cs) public TriviaCommands(DiscordSocketClient client, IDataCache cache,
IBotConfigProvider bc, CurrencyService cs)
{ {
_cache = cache;
_cs = cs; _cs = cs;
_client = client; _client = client;
_bc = bc; _bc = bc;
@ -45,7 +48,7 @@ namespace NadekoBot.Modules.Games
var showHints = !additionalArgs.Contains("nohint"); var showHints = !additionalArgs.Contains("nohint");
var isPokemon = additionalArgs.Contains("pokemon"); var isPokemon = additionalArgs.Contains("pokemon");
var trivia = new TriviaGame(_strings, _client, _bc, _cs, channel.Guild, channel, showHints, winReq, isPokemon); var trivia = new TriviaGame(_strings, _client, _bc, _cache, _cs, channel.Guild, channel, showHints, winReq, isPokemon);
if (_service.RunningTrivias.TryAdd(channel.Guild.Id, trivia)) if (_service.RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{ {
try try

View File

@ -6,7 +6,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Common.Attributes; using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Searches.Common; using NadekoBot.Core.Common.Pokemon;
using NadekoBot.Core.Services;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
@ -15,8 +16,15 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class PokemonSearchCommands : NadekoSubmodule<SearchesService> public class PokemonSearchCommands : NadekoSubmodule<SearchesService>
{ {
public Dictionary<string, SearchPokemon> Pokemons => _service.Pokemons; private readonly IDataCache _cache;
public Dictionary<string, SearchPokemonAbility> PokemonAbilities => _service.PokemonAbilities;
public IReadOnlyDictionary<string, SearchPokemon> Pokemons => _cache.LocalData.Pokemons;
public IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities => _cache.LocalData.PokemonAbilities;
public PokemonSearchCommands(IDataCache cache)
{
_cache = cache;
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Pokemon([Remainder] string pokemon = null) public async Task Pokemon([Remainder] string pokemon = null)

View File

@ -17,7 +17,6 @@ using System.Net.Http;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using AngleSharp; using AngleSharp;
using System.Threading; using System.Threading;
using NadekoBot.Modules.Searches.Exceptions;
using ImageSharp; using ImageSharp;
using Image = ImageSharp.Image; using Image = ImageSharp.Image;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -41,12 +40,6 @@ namespace NadekoBot.Modules.Searches.Services
public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>(); public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
public ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>(); public ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
public readonly string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
public readonly string PokemonListFile = "data/pokemon/pokemon_list7.json";
public Dictionary<string, SearchPokemon> Pokemons { get; } = new Dictionary<string, SearchPokemon>();
public Dictionary<string, SearchPokemonAbility> PokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>(); public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
public List<MagicItem> MagicItems { get; } = new List<MagicItem>(); public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
@ -113,17 +106,6 @@ namespace NadekoBot.Modules.Searches.Services
return Task.CompletedTask; return Task.CompletedTask;
}; };
//pokemon commands
if (File.Exists(PokemonListFile))
{
Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
}
else
_log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded.");
if (File.Exists(PokemonAbilitiesFile))
PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile));
else
_log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded.");
//joke commands //joke commands
if (File.Exists("data/wowjokes.json")) if (File.Exists("data/wowjokes.json"))

View File

@ -8,6 +8,7 @@ namespace NadekoBot.Core.Services
{ {
ConnectionMultiplexer Redis { get; } ConnectionMultiplexer Redis { get; }
IImageCache LocalImages { get; } IImageCache LocalImages { get; }
ILocalDataCache LocalData { get; }
Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key); Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key);
Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key); Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key);

View File

@ -0,0 +1,14 @@
using NadekoBot.Core.Common.Pokemon;
using NadekoBot.Modules.Games.Common.Trivia;
using System.Collections.Generic;
namespace NadekoBot.Core.Services
{
public interface ILocalDataCache
{
IReadOnlyDictionary<string, SearchPokemon> Pokemons { get; }
IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities { get; }
TriviaQuestion[] TriviaQuestions { get; }
IReadOnlyDictionary<int, string> PokemonMap { get; }
}
}

View File

@ -1,7 +1,6 @@
using NadekoBot.Extensions; using NadekoBot.Extensions;
using StackExchange.Redis; using StackExchange.Redis;
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Core.Services.Impl namespace NadekoBot.Core.Services.Impl
@ -11,6 +10,7 @@ namespace NadekoBot.Core.Services.Impl
public ConnectionMultiplexer Redis { get; } public ConnectionMultiplexer Redis { get; }
public IImageCache LocalImages { get; } public IImageCache LocalImages { get; }
public ILocalDataCache LocalData { get; }
private readonly IDatabase _db; private readonly IDatabase _db;
private readonly string _redisKey; private readonly string _redisKey;
@ -20,6 +20,7 @@ namespace NadekoBot.Core.Services.Impl
Redis = ConnectionMultiplexer.Connect("127.0.0.1"); Redis = ConnectionMultiplexer.Connect("127.0.0.1");
Redis.PreserveAsyncOrder = false; Redis.PreserveAsyncOrder = false;
LocalImages = new RedisImagesCache(Redis, creds); LocalImages = new RedisImagesCache(Redis, creds);
LocalData = new RedisLocalDataCache(Redis, creds);
_db = Redis.GetDatabase(); _db = Redis.GetDatabase();
_redisKey = creds.RedisKey(); _redisKey = creds.RedisKey();
} }

View File

@ -0,0 +1,122 @@
using NadekoBot.Core.Common.Pokemon;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Common.Trivia;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace NadekoBot.Core.Services.Impl
{
public class RedisLocalDataCache : ILocalDataCache
{
private readonly ConnectionMultiplexer _con;
private readonly IBotCredentials _creds;
private readonly Logger _log;
private IDatabase _db => _con.GetDatabase();
private const string pokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
private const string pokemonListFile = "data/pokemon/pokemon_list7.json";
private const string pokemonMapPath = "data/pokemon/name-id_map4.json";
private const string questionsFile = "data/trivia_questions.json";
public IReadOnlyDictionary<string, SearchPokemon> Pokemons
{
get
{
return Get<Dictionary<string, SearchPokemon>>("pokemon_list");
}
private set
{
Set("pokemon_list", value);
}
}
public IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities
{
get
{
return Get<Dictionary<string, SearchPokemonAbility>>("pokemon_abilities");
}
private set
{
Set("pokemon_abilities", value);
}
}
public TriviaQuestion[] TriviaQuestions
{
get
{
return Get<TriviaQuestion[]>("trivia_questions");
}
private set
{
Set("trivia_questions", value);
}
}
public IReadOnlyDictionary<int, string> PokemonMap
{
get
{
return Get<Dictionary<int, string>>("pokemon_map");
}
private set
{
Set("pokemon_map", value);
}
}
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds)
{
_con = con;
_creds = creds;
_log = LogManager.GetCurrentClassLogger();
if (!File.Exists(pokemonListFile))
{
_log.Warn(pokemonListFile + " is missing. Pokemon abilities not loaded.");
}
else
{
Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(pokemonListFile));
}
if (!File.Exists(pokemonAbilitiesFile))
{
_log.Warn(pokemonAbilitiesFile + " is missing. Pokemon abilities not loaded.");
}
else
{
PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(pokemonAbilitiesFile));
}
try
{
TriviaQuestions = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
PokemonMap = JsonConvert.DeserializeObject<PokemonNameId[]>(File.ReadAllText(pokemonMapPath))
.ToDictionary(x => x.Id, x => x.Name);
}
catch (Exception ex)
{
_log.Error(ex);
throw;
}
}
private T Get<T>(string key) where T : class
{
return JsonConvert.DeserializeObject<T>(_db.StringGet($"{_creds.RedisKey()}_localdata_{key}"));
}
private void Set(string key, object obj)
{
_db.StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj));
}
}
}

View File

@ -21,7 +21,7 @@ namespace NadekoBot.Core.Services.Impl
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly DateTime _started; private readonly DateTime _started;
public const string BotVersion = "2.3.5"; public const string BotVersion = "2.3.6";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";