using System.Diagnostics;
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Spt.Bots;
using SPTarkov.Server.Core.Models.Spt.Server;
using SPTarkov.Server.Core.Models.Spt.Templates;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using Hideout = SPTarkov.Server.Core.Models.Spt.Hideout.Hideout;
using Locations = SPTarkov.Server.Core.Models.Spt.Server.Locations;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Services;
///
/// Provides access to the servers database, these are in-memory representations of the .JSON files stored inside `Libraries\SPTarkov.Server.Assets\Assets\database`
///
[Injectable(InjectionType.Singleton)]
public class DatabaseService(
ISptLogger logger,
DatabaseServer databaseServer,
ServerLocalisationService serverLocalisationService
)
{
private bool _isDataValid = true;
/// assets/database/
public DatabaseTables GetTables()
{
return databaseServer.GetTables();
}
/// assets/database/bots/
public Bots GetBots()
{
return databaseServer.GetTables().Bots;
}
/// assets/database/globals.json
public Globals GetGlobals()
{
return databaseServer.GetTables().Globals;
}
/// assets/database/hideout/
public Hideout GetHideout()
{
return databaseServer.GetTables().Hideout;
}
/// assets/database/locales/
public LocaleBase GetLocales()
{
return databaseServer.GetTables().Locales;
}
/// assets/database/locations
public Locations GetLocations()
{
return databaseServer.GetTables().Locations;
}
///
/// Get specific location by its ID, automatically ToLowers id
///
/// Desired location ID
/// assets/database/locations/
public Location? GetLocation(string locationId)
{
var desiredLocation = GetLocations().GetByJsonProp(locationId.ToLowerInvariant());
if (desiredLocation == null)
{
logger.Error(serverLocalisationService.GetText("database-no_location_found_with_id", locationId));
return null;
}
return desiredLocation;
}
/// assets/database/match/
public Match GetMatch()
{
return databaseServer.GetTables().Match;
}
/// assets/database/server.json
public ServerBase GetServer()
{
return databaseServer.GetTables().Server;
}
/// assets/database/settings.json
public SettingsBase GetSettings()
{
return databaseServer.GetTables().Settings;
}
/// assets/database/templates/
public Templates GetTemplates()
{
return databaseServer.GetTables().Templates;
}
/// assets/database/templates/achievements.json
public List GetAchievements()
{
return databaseServer.GetTables().Templates.Achievements;
}
/// assets/database/templates/customAchievements.json
public List GetCustomAchievements()
{
return databaseServer.GetTables().Templates.CustomAchievements;
}
/// assets/database/templates/customisation.json
public Dictionary GetCustomization()
{
return databaseServer.GetTables().Templates.Customization;
}
/// assets/database/templates/handbook.json
public HandbookBase GetHandbook()
{
return databaseServer.GetTables().Templates.Handbook;
}
/// assets/database/templates/items.json
public Dictionary GetItems()
{
return databaseServer.GetTables().Templates.Items;
}
/// assets/database/templates/prices.json
public Dictionary GetPrices()
{
return databaseServer.GetTables().Templates.Prices;
}
/// assets/database/templates/profiles.json
public Dictionary GetProfileTemplates()
{
return databaseServer.GetTables().Templates.Profiles;
}
/// assets/database/templates/quests.json
public Dictionary GetQuests()
{
return databaseServer.GetTables().Templates.Quests;
}
/// assets/database/traders/
public Dictionary GetTraders()
{
return databaseServer.GetTables().Traders;
}
///
/// Get specific trader by their ID
///
/// Desired trader ID
/// assets/database/traders/
public Trader? GetTrader(MongoId traderId)
{
if (!databaseServer.GetTables().Traders.TryGetValue(traderId, out var desiredTrader))
{
return null;
}
return desiredTrader;
}
/// assets/database/locationServices/
public LocationServices GetLocationServices()
{
if (databaseServer.GetTables().Templates?.LocationServices == null)
{
throw new Exception(
serverLocalisationService.GetText("database-data_at_path_missing", "assets/database/locationServices.json")
);
}
return databaseServer.GetTables().Templates?.LocationServices!;
}
///
/// Validates that the database doesn't contain invalid ID data
///
public void ValidateDatabase()
{
var start = Stopwatch.StartNew();
_isDataValid =
ValidateTable(GetQuests(), "quest")
&& ValidateTable(GetTraders(), "trader")
&& ValidateTable(GetItems(), "item")
&& ValidateTable(GetCustomization(), "customization");
if (!_isDataValid)
{
logger.Error(serverLocalisationService.GetText("database-invalid_data"));
}
start.Stop();
if (logger.IsLogEnabled(LogLevel.Debug))
{
logger.Debug($"ID validation took: {start.ElapsedMilliseconds}ms");
}
}
///
/// Validate that the given table only contains valid MongoIDs
///
/// Table to validate for MongoIDs
/// The type of table, used in output message
/// True if the table only contains valid data
private bool ValidateTable(Dictionary table, string tableType)
{
foreach (var keyValuePair in table)
{
if (!keyValuePair.Key.IsValidMongoId())
{
logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false;
}
}
return true;
}
private bool ValidateTable(Dictionary table, string tableType)
{
foreach (var keyValuePair in table)
{
if (!keyValuePair.Key.IsValidMongoId())
{
logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false;
}
}
return true;
}
///
/// Check if the database is valid
///
/// True if the database contains valid data, false otherwise
public bool IsDatabaseValid()
{
return _isDataValid;
}
}