mirror of
https://github.com/ACEmulator/ACE
synced 2026-08-17 12:26:06 -04:00
* add support for 'crafting chance of success dialog' player option to regular recipes * adding logic for drid * broadcasting icon underlay changes for all recipe mods * update comment * updates * adding server option for 'craft_exact_msg' * . * retail server netsend all property updates in recipe mod, including internal * adding /clearcache recipe * . * using pre-existing method * updating AddSpell * updating AddSpell imbues - already handled in recipe mods * updating logic for sendMsg * further updates * exclude non-armature imbues on trinkets * adding shard fix for existing trinkets with invalid imbues * adding newline * updating AuditItemSpells for new scenario * fixing /ciloot tier * update hotspot cycle variance to use standard formula
893 lines
31 KiB
C#
893 lines
31 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using ACE.Common;
|
|
using ACE.Database.Adapter;
|
|
using ACE.Database.Models.World;
|
|
using ACE.Entity.Enum;
|
|
using ACE.Entity.Enum.Properties;
|
|
|
|
namespace ACE.Database
|
|
{
|
|
public class WorldDatabaseWithEntityCache : WorldDatabase
|
|
{
|
|
// =====================================
|
|
// Weenie
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<uint /* WCID */, ACE.Entity.Models.Weenie> weenieCache = new ConcurrentDictionary<uint /* WCID */, ACE.Entity.Models.Weenie>();
|
|
|
|
private readonly ConcurrentDictionary<string /* Class Name */, uint /* WCID */> weenieClassNameToClassIdCache = new ConcurrentDictionary<string, uint>();
|
|
|
|
/// <summary>
|
|
/// This will populate all sub collections except the following: LandblockInstances, PointsOfInterest<para />
|
|
/// This will also update the weenie cache.
|
|
/// </summary>
|
|
public override Weenie GetWeenie(WorldDbContext context, uint weenieClassId)
|
|
{
|
|
var weenie = base.GetWeenie(context, weenieClassId);
|
|
|
|
// If the weenie doesn't exist in the cache, we'll add it.
|
|
if (weenie != null)
|
|
{
|
|
weenieCache[weenieClassId] = WeenieConverter.ConvertToEntityWeenie(weenie);
|
|
weenieClassNameToClassIdCache[weenie.ClassName.ToLower()] = weenie.ClassId;
|
|
}
|
|
else
|
|
weenieCache[weenieClassId] = null;
|
|
|
|
return weenie;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// This will make sure every weenie in the database has been read and cached.<para />
|
|
/// This function may take 2+ minutes to complete.
|
|
/// </summary>
|
|
public void CacheAllWeeniesInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.Weenie
|
|
.AsNoTracking()
|
|
.ToList();
|
|
|
|
Parallel.ForEach(results, ConfigManager.Config.Server.Threading.DatabaseParallelOptions, result =>
|
|
{
|
|
if (!weenieCache.ContainsKey(result.ClassId))
|
|
GetWeenie(result.ClassId); // This will add the result into the caches
|
|
});
|
|
}
|
|
|
|
PopulateWeenieSpecificCaches();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of weenies currently cached.
|
|
/// </summary>
|
|
public int GetWeenieCacheCount()
|
|
{
|
|
return weenieCache.Count(r => r.Value != null);
|
|
}
|
|
|
|
public void ClearWeenieCache()
|
|
{
|
|
weenieCache.Clear();
|
|
weenieClassNameToClassIdCache.Clear();
|
|
|
|
weenieSpecificCachesPopulated = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Weenies will have all their collections populated except the following: LandblockInstances, PointsOfInterest
|
|
/// </summary>
|
|
public ACE.Entity.Models.Weenie GetCachedWeenie(uint weenieClassId)
|
|
{
|
|
if (weenieCache.TryGetValue(weenieClassId, out var value))
|
|
return value;
|
|
|
|
GetWeenie(weenieClassId); // This will add the result into the caches
|
|
|
|
weenieCache.TryGetValue(weenieClassId, out value);
|
|
|
|
return value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Weenies will have all their collections populated except the following: LandblockInstances, PointsOfInterest
|
|
/// </summary>
|
|
public ACE.Entity.Models.Weenie GetCachedWeenie(string weenieClassName)
|
|
{
|
|
if (weenieClassNameToClassIdCache.TryGetValue(weenieClassName.ToLower(), out var value))
|
|
return GetCachedWeenie(value); // This will add the result into the caches
|
|
|
|
GetWeenie(weenieClassName); // This will add the result into the caches
|
|
|
|
weenieClassNameToClassIdCache.TryGetValue(weenieClassName.ToLower(), out value);
|
|
|
|
return GetCachedWeenie(value); // This will add the result into the caches
|
|
}
|
|
|
|
public bool ClearCachedWeenie(uint weenieClassId)
|
|
{
|
|
return weenieCache.TryRemove(weenieClassId, out _);
|
|
}
|
|
|
|
|
|
|
|
private bool weenieSpecificCachesPopulated;
|
|
|
|
private readonly ConcurrentDictionary<WeenieType, List<ACE.Entity.Models.Weenie>> weenieCacheByType = new ConcurrentDictionary<WeenieType, List<ACE.Entity.Models.Weenie>>();
|
|
|
|
private readonly Dictionary<uint /* Spell ID */, ACE.Entity.Models.Weenie> scrollsBySpellID = new Dictionary<uint, ACE.Entity.Models.Weenie>();
|
|
|
|
private void PopulateWeenieSpecificCaches()
|
|
{
|
|
// populate weenieCacheByType
|
|
foreach (var weenie in weenieCache.Values)
|
|
{
|
|
if (weenie == null)
|
|
continue;
|
|
|
|
if (!weenieCacheByType.TryGetValue(weenie.WeenieType, out var weenies))
|
|
{
|
|
weenies = new List<ACE.Entity.Models.Weenie>();
|
|
weenieCacheByType[weenie.WeenieType] = weenies;
|
|
}
|
|
|
|
if (!weenies.Contains(weenie))
|
|
weenies.Add(weenie);
|
|
}
|
|
|
|
// populate scrollsBySpellID
|
|
foreach (var weenie in weenieCache.Values)
|
|
{
|
|
if (weenie == null)
|
|
continue;
|
|
|
|
if (weenie.WeenieType == WeenieType.Scroll)
|
|
{
|
|
if (weenie.PropertiesDID.TryGetValue(PropertyDataId.Spell, out var value))
|
|
scrollsBySpellID[value] = weenie;
|
|
}
|
|
}
|
|
|
|
weenieSpecificCachesPopulated = true;
|
|
}
|
|
|
|
public List<ACE.Entity.Models.Weenie> GetRandomWeeniesOfType(int weenieTypeId, int count)
|
|
{
|
|
if (!weenieCacheByType.TryGetValue((WeenieType)weenieTypeId, out var weenies))
|
|
{
|
|
if (!weenieSpecificCachesPopulated)
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.Weenie
|
|
.AsNoTracking()
|
|
.Where(r => r.Type == weenieTypeId)
|
|
.ToList();
|
|
|
|
var rand = new Random();
|
|
|
|
weenies = new List<ACE.Entity.Models.Weenie>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var index = rand.Next(0, results.Count - 1);
|
|
|
|
var weenie = GetCachedWeenie(results[index].ClassId);
|
|
|
|
weenies.Add(weenie);
|
|
}
|
|
|
|
return weenies;
|
|
}
|
|
}
|
|
|
|
weenies = new List<ACE.Entity.Models.Weenie>();
|
|
weenieCacheByType[(WeenieType)weenieTypeId] = weenies;
|
|
}
|
|
|
|
if (weenies.Count == 0)
|
|
return new List<ACE.Entity.Models.Weenie>();
|
|
|
|
{
|
|
var rand = new Random();
|
|
|
|
var results = new List<ACE.Entity.Models.Weenie>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var index = rand.Next(0, weenies.Count - 1);
|
|
|
|
var weenie = GetCachedWeenie(weenies[index].WeenieClassId);
|
|
|
|
results.Add(weenie);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
}
|
|
|
|
public ACE.Entity.Models.Weenie GetScrollWeenie(uint spellID)
|
|
{
|
|
if (!scrollsBySpellID.TryGetValue(spellID, out var weenie))
|
|
{
|
|
if (!weenieSpecificCachesPopulated)
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var query = from weenieRecord in context.Weenie
|
|
join did in context.WeeniePropertiesDID on weenieRecord.ClassId equals did.ObjectId
|
|
where weenieRecord.Type == (int)WeenieType.Scroll && did.Type == (ushort)PropertyDataId.Spell && did.Value == spellID
|
|
select weenieRecord;
|
|
|
|
var result = query.FirstOrDefault();
|
|
|
|
weenie = WeenieConverter.ConvertToEntityWeenie(result);
|
|
|
|
scrollsBySpellID[spellID] = weenie;
|
|
}
|
|
}
|
|
}
|
|
|
|
return weenie;
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// CookBook
|
|
// =====================================
|
|
|
|
private readonly Dictionary<uint /* source WCID */, Dictionary<uint /* target WCID */, CookBook>> cookbookCache = new Dictionary<uint, Dictionary<uint, CookBook>>();
|
|
|
|
public override CookBook GetCookbook(WorldDbContext context, uint sourceWeenieClassId, uint targetWeenieClassId)
|
|
{
|
|
var cookbook = base.GetCookbook(context, sourceWeenieClassId, targetWeenieClassId);
|
|
|
|
lock (cookbookCache)
|
|
{
|
|
// We double check before commiting the recipe.
|
|
// We could be in this lock, and queued up behind us is an attempt to add a result for the same source:target pair.
|
|
if (cookbookCache.TryGetValue(sourceWeenieClassId, out var sourceRecipes))
|
|
{
|
|
if (!sourceRecipes.ContainsKey(targetWeenieClassId))
|
|
sourceRecipes.Add(targetWeenieClassId, cookbook);
|
|
}
|
|
else
|
|
cookbookCache.Add(sourceWeenieClassId, new Dictionary<uint, CookBook>() { { targetWeenieClassId, cookbook } });
|
|
}
|
|
|
|
return cookbook;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This can take 1-2 minutes to complete.
|
|
/// </summary>
|
|
public void CacheAllCookbooksInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.CookBook
|
|
.AsNoTracking()
|
|
.ToList();
|
|
|
|
Parallel.ForEach(results, ConfigManager.Config.Server.Threading.DatabaseParallelOptions, result =>
|
|
{
|
|
GetCookbook(result.SourceWCID, result.TargetWCID); // This will add the result into the cache
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of Cookbooks currently cached.
|
|
/// </summary>
|
|
public int GetCookbookCacheCount()
|
|
{
|
|
lock (cookbookCache)
|
|
return cookbookCache.Count(r => r.Value != null);
|
|
}
|
|
|
|
public void ClearCookbookCache()
|
|
{
|
|
lock (cookbookCache)
|
|
cookbookCache.Clear();
|
|
}
|
|
|
|
public CookBook GetCachedCookbook(uint sourceWeenieClassId, uint targetWeenieClassId)
|
|
{
|
|
lock (cookbookCache)
|
|
{
|
|
if (cookbookCache.TryGetValue(sourceWeenieClassId, out var recipesForSource))
|
|
{
|
|
if (recipesForSource.TryGetValue(targetWeenieClassId, out var value))
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return GetCookbook(sourceWeenieClassId, targetWeenieClassId); // This will add the result into the cache
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// Encounter
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<ushort /* Landblock */, List<Encounter>> cachedEncounters = new ConcurrentDictionary<ushort, List<Encounter>>();
|
|
|
|
/// <summary>
|
|
/// Returns the number of Encounters currently cached.
|
|
/// </summary>
|
|
public int GetEncounterCacheCount()
|
|
{
|
|
return cachedEncounters.Count(r => r.Value != null);
|
|
}
|
|
|
|
public List<Encounter> GetCachedEncountersByLandblock(ushort landblock)
|
|
{
|
|
if (cachedEncounters.TryGetValue(landblock, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.Encounter
|
|
.AsNoTracking()
|
|
.Where(r => r.Landblock == landblock)
|
|
.ToList();
|
|
|
|
cachedEncounters.TryAdd(landblock, results);
|
|
return results;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// Event
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<string /* Event Name */, Event> cachedEvents = new ConcurrentDictionary<string, Event>();
|
|
|
|
public override List<Event> GetAllEvents(WorldDbContext context)
|
|
{
|
|
var events = base.GetAllEvents(context);
|
|
|
|
foreach (var result in events)
|
|
cachedEvents[result.Name.ToLower()] = result;
|
|
|
|
return events;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of Events currently cached.
|
|
/// </summary>
|
|
public int GetEventsCacheCount()
|
|
{
|
|
return cachedEvents.Count(r => r.Value != null);
|
|
}
|
|
|
|
public Event GetCachedEvent(string name)
|
|
{
|
|
var nameToLower = name.ToLower();
|
|
|
|
if (cachedEvents.TryGetValue(nameToLower, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var result = context.Event
|
|
.AsNoTracking()
|
|
.FirstOrDefault(r => r.Name.ToLower() == nameToLower);
|
|
|
|
cachedEvents[nameToLower] = result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// HousePortal
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<uint /* House ID */, List<HousePortal>> cachedHousePortals = new ConcurrentDictionary<uint, List<HousePortal>>();
|
|
|
|
/// <summary>
|
|
/// This takes under ? second to complete.
|
|
/// </summary>
|
|
public void CacheAllHousePortals()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.HousePortal
|
|
.AsNoTracking()
|
|
.AsEnumerable()
|
|
.GroupBy(r => r.HouseId);
|
|
|
|
foreach (var result in results)
|
|
cachedHousePortals[result.Key] = result.ToList();
|
|
}
|
|
}
|
|
|
|
public List<HousePortal> GetCachedHousePortals(uint houseId)
|
|
{
|
|
if (cachedHousePortals.TryGetValue(houseId, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.HousePortal
|
|
.AsNoTracking()
|
|
.Where(p => p.HouseId == houseId)
|
|
.ToList();
|
|
|
|
cachedHousePortals[houseId] = results;
|
|
|
|
return results;
|
|
}
|
|
}
|
|
|
|
|
|
private readonly ConcurrentDictionary<uint /* Landblock */, List<HousePortal>> cachedHousePortalsByLandblock = new ConcurrentDictionary<uint, List<HousePortal>>();
|
|
|
|
public List<HousePortal> GetCachedHousePortalsByLandblock(uint landblockId)
|
|
{
|
|
if (cachedHousePortalsByLandblock.TryGetValue(landblockId, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.HousePortal
|
|
.AsNoTracking()
|
|
.Where(p => landblockId == p.ObjCellId >> 16)
|
|
.ToList();
|
|
|
|
cachedHousePortalsByLandblock[landblockId] = results;
|
|
|
|
return results;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// LandblockInstance
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<ushort /* Landblock */, List<LandblockInstance>> cachedLandblockInstances = new ConcurrentDictionary<ushort, List<LandblockInstance>>();
|
|
|
|
/// <summary>
|
|
/// Returns the number of LandblockInstances currently cached.
|
|
/// </summary>
|
|
public int GetLandblockInstancesCacheCount()
|
|
{
|
|
return cachedLandblockInstances.Count(r => r.Value != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the cached landblock instances for all landblocks
|
|
/// </summary>
|
|
public void ClearCachedLandblockInstances()
|
|
{
|
|
cachedLandblockInstances.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the cached landblock instances for a specific landblock
|
|
/// </summary>
|
|
public bool ClearCachedInstancesByLandblock(ushort landblock)
|
|
{
|
|
return cachedLandblockInstances.TryRemove(landblock, out _);
|
|
}
|
|
|
|
public List<LandblockInstance> GetCachedInstancesByLandblock(WorldDbContext context, ushort landblock)
|
|
{
|
|
if (cachedLandblockInstances.TryGetValue(landblock, out var value))
|
|
return value;
|
|
|
|
var results = context.LandblockInstance
|
|
.Include(r => r.LandblockInstanceLink)
|
|
.AsNoTracking()
|
|
.Where(r => r.Landblock == landblock)
|
|
.ToList();
|
|
|
|
cachedLandblockInstances.TryAdd(landblock, results.ToList());
|
|
|
|
return cachedLandblockInstances[landblock];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns statics spawn map and their links for the landblock
|
|
/// </summary>
|
|
public List<LandblockInstance> GetCachedInstancesByLandblock(ushort landblock)
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
return GetCachedInstancesByLandblock(context, landblock);
|
|
}
|
|
|
|
|
|
private readonly ConcurrentDictionary<ushort /* Landblock */, uint /* House GUID */> cachedBasementHouseGuids = new ConcurrentDictionary<ushort, uint>();
|
|
|
|
public uint GetCachedBasementHouseGuid(ushort landblock)
|
|
{
|
|
if (cachedBasementHouseGuids.TryGetValue(landblock, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var result = context.LandblockInstance
|
|
.AsNoTracking()
|
|
.Where(r => r.Landblock == landblock
|
|
&& r.WeenieClassId != 11730 /* Exclude House Portal */
|
|
&& r.WeenieClassId != 278 /* Exclude Door */
|
|
&& r.WeenieClassId != 568 /* Exclude Door (entry) */
|
|
&& !r.IsLinkChild)
|
|
.FirstOrDefault();
|
|
|
|
if (result == null)
|
|
return 0;
|
|
|
|
cachedBasementHouseGuids[landblock] = result.Guid;
|
|
|
|
return result.Guid;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// PointsOfInterest
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<string, PointsOfInterest> cachedPointsOfInterest = new ConcurrentDictionary<string, PointsOfInterest>();
|
|
|
|
/// <summary>
|
|
/// Retrieves all points of interest from the database and adds/updates the points of interest cache entries with every point of interest retrieved.
|
|
/// 57 entries cached in 00:00:00.0057937
|
|
/// </summary>
|
|
public void CacheAllPointsOfInterest()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.PointsOfInterest
|
|
.AsNoTracking();
|
|
|
|
foreach (var result in results)
|
|
cachedPointsOfInterest[result.Name.ToLower()] = result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of PointsOfInterest currently cached.
|
|
/// </summary>
|
|
public int GetPointsOfInterestCacheCount()
|
|
{
|
|
return cachedPointsOfInterest.Count(r => r.Value != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the PointsOfInterest cache.
|
|
/// </summary>
|
|
public ConcurrentDictionary<string, PointsOfInterest> GetPointsOfInterestCache()
|
|
{
|
|
return new ConcurrentDictionary<string, PointsOfInterest>(cachedPointsOfInterest);
|
|
}
|
|
|
|
public PointsOfInterest GetCachedPointOfInterest(string name)
|
|
{
|
|
var nameToLower = name.ToLower();
|
|
|
|
if (cachedPointsOfInterest.TryGetValue(nameToLower, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var result = context.PointsOfInterest
|
|
.AsNoTracking()
|
|
.FirstOrDefault(r => r.Name.ToLower() == nameToLower);
|
|
|
|
cachedPointsOfInterest[nameToLower] = result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// Quest
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<string, Quest> cachedQuest = new ConcurrentDictionary<string, Quest>();
|
|
|
|
public bool ClearCachedQuest(string questName)
|
|
{
|
|
return cachedQuest.TryRemove(questName, out _);
|
|
}
|
|
|
|
public Quest GetCachedQuest(string questName)
|
|
{
|
|
if (cachedQuest.TryGetValue(questName, out var quest))
|
|
return quest;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
quest = context.Quest.FirstOrDefault(q => q.Name.Equals(questName));
|
|
cachedQuest[questName] = quest;
|
|
|
|
return quest;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// Recipe
|
|
// =====================================
|
|
|
|
|
|
// =====================================
|
|
// Spell
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<uint /* Spell ID */, Spell> spellCache = new ConcurrentDictionary<uint, Spell>();
|
|
|
|
/// <summary>
|
|
/// This takes under 1 second to complete.
|
|
/// </summary>
|
|
public void CacheAllSpells()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.Spell
|
|
.AsNoTracking();
|
|
|
|
foreach (var result in results)
|
|
spellCache[result.Id] = result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of Spells currently cached.
|
|
/// </summary>
|
|
public int GetSpellCacheCount()
|
|
{
|
|
return spellCache.Count(r => r.Value != null);
|
|
}
|
|
|
|
public void ClearSpellCache()
|
|
{
|
|
spellCache.Clear();
|
|
}
|
|
|
|
public Spell GetCachedSpell(uint spellId)
|
|
{
|
|
if (spellCache.TryGetValue(spellId, out var spell))
|
|
return spell;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var result = context.Spell
|
|
.AsNoTracking()
|
|
.FirstOrDefault(r => r.Id == spellId);
|
|
|
|
spellCache[spellId] = result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =====================================
|
|
// TreasureDeath
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<uint /* Data ID */, TreasureDeath> cachedDeathTreasure = new ConcurrentDictionary<uint, TreasureDeath>();
|
|
|
|
/// <summary>
|
|
/// This takes under 1 second to complete.
|
|
/// </summary>
|
|
public void CacheAllTreasuresDeath()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureDeath
|
|
.AsNoTracking();
|
|
|
|
foreach (var result in results)
|
|
cachedDeathTreasure[result.TreasureType] = result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of TreasureDeath currently cached.
|
|
/// </summary>
|
|
public int GetDeathTreasureCacheCount()
|
|
{
|
|
return cachedDeathTreasure.Count(r => r.Value != null);
|
|
}
|
|
|
|
public TreasureDeath GetCachedDeathTreasure(uint dataId)
|
|
{
|
|
if (cachedDeathTreasure.TryGetValue(dataId, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var result = context.TreasureDeath
|
|
.AsNoTracking()
|
|
.FirstOrDefault(r => r.TreasureType == dataId);
|
|
|
|
cachedDeathTreasure[dataId] = result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// TreasureMaterial
|
|
// =====================================
|
|
|
|
// The Key is the Material Code (derived from PropertyInt.TsysMaterialData)
|
|
// The Value is a list of all
|
|
private readonly ConcurrentDictionary<int /* Material Code */, List<TreasureMaterialBase>> cachedTreasureMaterialBase = new ConcurrentDictionary<int, List<TreasureMaterialBase>>();
|
|
|
|
public void CacheAllTreasuresMaterialBaseInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureMaterialBase
|
|
.AsNoTracking()
|
|
.AsEnumerable()
|
|
.GroupBy(r => r.MaterialCode);
|
|
|
|
foreach (var result in results)
|
|
cachedTreasureMaterialBase[(int)result.Key] = result.ToList();
|
|
}
|
|
}
|
|
|
|
public List<TreasureMaterialBase> GetCachedTreasureMaterialBase(int materialCode, int tier)
|
|
{
|
|
if (cachedTreasureMaterialBase.Count == 0)
|
|
CacheAllTreasuresMaterialBaseInParallel();
|
|
|
|
if (cachedTreasureMaterialBase.TryGetValue(materialCode, out var value))
|
|
{
|
|
var results = value.Where(r => r.Tier == tier).Where(r => r.Probability > 0).ToList();
|
|
return results;
|
|
}
|
|
|
|
return new List<TreasureMaterialBase>();
|
|
}
|
|
|
|
|
|
private readonly ConcurrentDictionary<int /* Material ID */, List<TreasureMaterialColor>> cachedTreasureMaterialColor = new ConcurrentDictionary<int, List<TreasureMaterialColor>>();
|
|
|
|
public void CacheAllTreasuresMaterialColorInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureMaterialColor
|
|
.AsNoTracking()
|
|
.AsEnumerable()
|
|
.GroupBy(r => r.MaterialId);
|
|
|
|
foreach (var result in results)
|
|
cachedTreasureMaterialColor[(int)result.Key] = result.ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of TreasureMaterialColor currently cached.
|
|
/// </summary>
|
|
public int GetTreasureMaterialColorCacheCount()
|
|
{
|
|
return cachedTreasureMaterialColor.Count(r => r.Value != null);
|
|
}
|
|
|
|
public List<TreasureMaterialColor> GetCachedTreasureMaterialColors(int materialId, int tsysColorCode)
|
|
{
|
|
if (cachedTreasureMaterialColor.Count == 0)
|
|
CacheAllTreasuresMaterialColorInParallel();
|
|
|
|
if (cachedTreasureMaterialColor.TryGetValue(materialId, out var value))
|
|
{
|
|
var results = value.Where(r => r.ColorCode == tsysColorCode).ToList();
|
|
return results;
|
|
}
|
|
|
|
return new List<TreasureMaterialColor>();
|
|
}
|
|
|
|
|
|
// The Key is the Material Group (technically a MaterialId, but more generic...e.g. "Material.Metal", "Material.Cloth", etc.)
|
|
// The Value is a list of all
|
|
private readonly ConcurrentDictionary<int /* Material Group */, List<TreasureMaterialGroups>> cachedTreasureMaterialGroups = new ConcurrentDictionary<int, List<TreasureMaterialGroups>>();
|
|
|
|
public void CacheAllTreasuresMaterialGroupsInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureMaterialGroups
|
|
.AsNoTracking()
|
|
.AsEnumerable()
|
|
.GroupBy(r => r.MaterialGroup);
|
|
|
|
foreach (var result in results)
|
|
cachedTreasureMaterialGroups[(int)result.Key] = result.ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of TreasureMaterialBase currently cached.
|
|
/// </summary>
|
|
public int GetTreasureMaterialGroupCacheCount()
|
|
{
|
|
return cachedTreasureMaterialGroups.Count(r => r.Value != null);
|
|
}
|
|
|
|
public List<TreasureMaterialGroups> GetCachedTreasureMaterialGroup(int materialGroup, int tier)
|
|
{
|
|
if (cachedTreasureMaterialGroups.Count == 0)
|
|
CacheAllTreasuresMaterialGroupsInParallel();
|
|
|
|
if (cachedTreasureMaterialGroups.TryGetValue(materialGroup, out var value))
|
|
{
|
|
var results = value.Where(r => r.Tier == tier).ToList();
|
|
return results;
|
|
}
|
|
|
|
// Something unexpected happened here. Return an empty list!
|
|
return new List<TreasureMaterialGroups>();
|
|
}
|
|
|
|
|
|
// =====================================
|
|
// TreasureWielded
|
|
// =====================================
|
|
|
|
private readonly ConcurrentDictionary<uint /* Data ID */, List<TreasureWielded>> cachedWieldedTreasure = new ConcurrentDictionary<uint, List<TreasureWielded>>();
|
|
|
|
/// <summary>
|
|
/// This takes under 1 second to complete.
|
|
/// </summary>
|
|
public void CacheAllTreasuresWieldedInParallel()
|
|
{
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureWielded
|
|
.AsNoTracking()
|
|
.AsEnumerable()
|
|
.GroupBy(r => r.TreasureType);
|
|
|
|
foreach (var result in results)
|
|
cachedWieldedTreasure[result.Key] = result.ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of TreasureWielded currently cached.
|
|
/// </summary>
|
|
public int GetWieldedTreasureCacheCount()
|
|
{
|
|
return cachedWieldedTreasure.Count(r => r.Value != null);
|
|
}
|
|
|
|
|
|
public List<TreasureWielded> GetCachedWieldedTreasure(uint dataId)
|
|
{
|
|
if (cachedWieldedTreasure.TryGetValue(dataId, out var value))
|
|
return value;
|
|
|
|
using (var context = new WorldDbContext())
|
|
{
|
|
var results = context.TreasureWielded
|
|
.AsNoTracking()
|
|
.Where(r => r.TreasureType == dataId)
|
|
.ToList();
|
|
|
|
cachedWieldedTreasure[dataId] = results;
|
|
return results;
|
|
}
|
|
}
|
|
}
|
|
}
|