DatabaseService and other fixes
This commit is contained in:
@@ -63,4 +63,22 @@ public class Locations
|
||||
/** Holds a mapping of the linkages between locations on the UI */
|
||||
[JsonPropertyName("base")]
|
||||
public LocationsBase? Base { get; set; }
|
||||
|
||||
public Eft.Common.Location? this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Eft.Common.Location?) GetType()
|
||||
.GetProperties()
|
||||
.First(p => p.Name.ToLower() == key.ToLower()).GetGetMethod()?
|
||||
.Invoke(this, null) ?? null;
|
||||
}
|
||||
set
|
||||
{
|
||||
GetType()
|
||||
.GetProperties()
|
||||
.First(p => p.Name.ToLower() == key.ToLower()).GetSetMethod()?
|
||||
.Invoke(this, [value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class Templates
|
||||
|
||||
/** Flea prices of items - gathered from online flea market dump */
|
||||
[JsonPropertyName("prices")]
|
||||
public Dictionary<string, decimal> Prices { get; set; }
|
||||
public Dictionary<string, double> Prices { get; set; }
|
||||
|
||||
/** Default equipment loadouts that show on main inventory screen */
|
||||
[JsonPropertyName("defaultEquipmentPresets")]
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
using System.Diagnostics;
|
||||
using Core.Annotations;
|
||||
using Core.Models.Eft.Common;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Spt.Bots;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Models.Spt.Server;
|
||||
using Core.Models.Spt.Templates;
|
||||
using Core.Servers;
|
||||
using Core.Utils;
|
||||
using Hideout = Core.Models.Spt.Hideout.Hideout;
|
||||
using ILogger = Core.Models.Utils.ILogger;
|
||||
using Locations = Core.Models.Spt.Server.Locations;
|
||||
|
||||
namespace Core.Services;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class DatabaseService
|
||||
{
|
||||
protected LocationConfig locationConfig;
|
||||
protected bool isDataValid;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly DatabaseServer _databaseServer;
|
||||
private readonly LocalisationService _localisationService;
|
||||
private readonly HashUtil _hashUtil;
|
||||
|
||||
public DatabaseService(
|
||||
ILogger logger,
|
||||
DatabaseServer databaseServer,
|
||||
LocalisationService localisationService,
|
||||
HashUtil hashUtil
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_databaseServer = databaseServer;
|
||||
_localisationService = localisationService;
|
||||
_hashUtil = hashUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/
|
||||
*/
|
||||
public DatabaseTables GetTables()
|
||||
{
|
||||
return _databaseServer.GetTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/bots/
|
||||
*/
|
||||
public Bots GetBots()
|
||||
{
|
||||
if (_databaseServer.GetTables().bots == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/bots"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().bots!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/globals.json
|
||||
*/
|
||||
public Globals GetGlobals()
|
||||
{
|
||||
if (_databaseServer.GetTables().globals == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/globals.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().globals!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/hideout/
|
||||
*/
|
||||
public Hideout GetHideout()
|
||||
{
|
||||
if (_databaseServer.GetTables().hideout == null)
|
||||
{
|
||||
throw new Exception(
|
||||
_localisationService.GetText("database-data_at_path_missing", "assets/database/hideout"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().hideout!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/locales/
|
||||
*/
|
||||
public LocaleBase GetLocales()
|
||||
{
|
||||
if (_databaseServer.GetTables().locales == null)
|
||||
{
|
||||
throw new Exception(
|
||||
_localisationService.GetText("database-data_at_path_missing", "assets/database/locales"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().locales!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/locations
|
||||
*/
|
||||
public Locations GetLocations()
|
||||
{
|
||||
if (_databaseServer.GetTables().locations == null)
|
||||
{
|
||||
throw new Exception(
|
||||
_localisationService.GetText("database-data_at_path_missing", "assets/database/locales"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().locations!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific location by its Id
|
||||
* @param locationId Desired location id
|
||||
* @returns assets/database/locations/
|
||||
*/
|
||||
public Location GetLocation(string locationId)
|
||||
{
|
||||
var locations = GetLocations();
|
||||
var desiredLocation = locations[locationId.ToLower()];
|
||||
if (desiredLocation == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-no_location_found_with_id", locationId));
|
||||
}
|
||||
|
||||
return desiredLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/match/
|
||||
*/
|
||||
public Match GetMatch()
|
||||
{
|
||||
if (_databaseServer.GetTables().match == null)
|
||||
{
|
||||
throw new Exception(
|
||||
_localisationService.GetText("database-data_at_path_missing", "assets/database/locales"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().match!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/server.json
|
||||
*/
|
||||
public ServerBase GetServer()
|
||||
{
|
||||
if (_databaseServer.GetTables().server == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/server.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().server!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/settings.json
|
||||
*/
|
||||
public SettingsBase GetSettings()
|
||||
{
|
||||
if (_databaseServer.GetTables().settings == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/settings.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().settings!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/
|
||||
*/
|
||||
public Templates GetTemplates()
|
||||
{
|
||||
if (_databaseServer.GetTables().templates == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/templates"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/achievements.json
|
||||
*/
|
||||
public List<Achievement> GetAchievements()
|
||||
{
|
||||
if (_databaseServer.GetTables().templates?.Achievements == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/templates/achievements.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Achievements!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/customisation.json
|
||||
*/
|
||||
public Dictionary<string, CustomizationItem> GetCustomization()
|
||||
{
|
||||
if (_databaseServer.GetTables().templates?.Customization == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing",
|
||||
"assets/database/templates/customization.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Customization!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/handbook.json
|
||||
*/
|
||||
public HandbookBase GetHandbook()
|
||||
{
|
||||
if (_databaseServer.GetTables().templates?.Handbook == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/templates/handbook.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Handbook!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/items.json
|
||||
*/
|
||||
public Dictionary<string, TemplateItem> GetItems() {
|
||||
if (_databaseServer.GetTables().templates?.Items == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/templates/items.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Items!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/prices.json
|
||||
*/
|
||||
public Dictionary<string, double> GetPrices() {
|
||||
if (_databaseServer.GetTables().templates?.Prices == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/templates/prices.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Prices!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/profiles.json
|
||||
*/
|
||||
public ProfileTemplates GetProfiles()
|
||||
{
|
||||
if (_databaseServer.GetTables().templates?.Profiles == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/templates/profiles.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Profiles!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/templates/quests.json
|
||||
*/
|
||||
public Dictionary<string, Quest> GetQuests() {
|
||||
if (_databaseServer.GetTables().templates?.Quests == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/templates/quests.json"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().templates?.Quests!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/traders/
|
||||
*/
|
||||
public Dictionary<string, Trader> GetTraders() {
|
||||
if (_databaseServer.GetTables().traders == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-data_at_path_missing", "assets/database/traders"));
|
||||
}
|
||||
|
||||
return _databaseServer.GetTables().traders!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific trader by their Id
|
||||
* @param traderId Desired trader id
|
||||
* @returns assets/database/traders/
|
||||
*/
|
||||
public Trader GetTrader(string traderId)
|
||||
{
|
||||
var traders = GetTraders();
|
||||
if (!traders.TryGetValue(traderId, out var desiredTrader))
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("database-no_trader_found_with_id", traderId));
|
||||
}
|
||||
|
||||
return desiredTrader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns assets/database/locationServices/
|
||||
*/
|
||||
public LocationServices GetLocationServices() {
|
||||
if (_databaseServer.GetTables().templates?.LocationServices == null)
|
||||
{
|
||||
throw new Exception(_localisationService.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(_localisationService.GetText("database-invalid_data"));
|
||||
}
|
||||
|
||||
start.Stop();
|
||||
_logger.Debug($"ID validation took: {start.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the given table only contains valid MongoIDs
|
||||
* @param table Table to validate for MongoIDs
|
||||
* @param tableType The type of table, used in output message
|
||||
* @returns True if the table only contains valid data
|
||||
*/
|
||||
private bool ValidateTable<T>(Dictionary<string, T> table, string tableType) {
|
||||
foreach (var keyValuePair in table)
|
||||
{
|
||||
if (!_hashUtil.IsValidMongoId(keyValuePair.Key))
|
||||
{
|
||||
_logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is valid
|
||||
* @returns True if the database contains valid data, false otherwise
|
||||
*/
|
||||
public bool IsDatabaseValid() => isDataValid;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Core.Annotations;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Servers;
|
||||
using Core.Services;
|
||||
using ILogger = Core.Models.Utils.ILogger;
|
||||
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class App
|
||||
{
|
||||
protected Dictionary<string, long> _onUpdateLastRun;
|
||||
protected CoreConfig _coreConfig;
|
||||
|
||||
private ILogger _logger;
|
||||
private TimeUtil _timeUtil;
|
||||
private LocalisationService _localisationService;
|
||||
private ConfigServer _configServer;
|
||||
private EncodingUtil _encodingUtil;
|
||||
private HttpServer _httpServer;
|
||||
private DatabaseService _databaseService;
|
||||
private IEnumerable<OnLoad> _onLoad;
|
||||
private IEnumerable<OnUpdate> _onUpdate;
|
||||
|
||||
public App(
|
||||
ILogger logger,
|
||||
TimeUtil timeUtil,
|
||||
LocalisationService localisationService,
|
||||
ConfigServer configServer,
|
||||
EncodingUtil encodingUtil,
|
||||
HttpServer httpServer,
|
||||
DatabaseService databaseService,
|
||||
IEnumerable<OnLoad> onLoadComponents,
|
||||
IEnumerable<OnUpdate> onUpdateComponents
|
||||
) {
|
||||
_logger = logger;
|
||||
_timeUtil = timeUtil;
|
||||
_localisationService = localisationService;
|
||||
_configServer = configServer;
|
||||
_encodingUtil = encodingUtil;
|
||||
_httpServer = httpServer;
|
||||
_databaseService = databaseService;
|
||||
_onLoad = onLoadComponents;
|
||||
_onUpdate = onUpdateComponents;
|
||||
|
||||
_coreConfig = configServer.GetConfig<CoreConfig>(ConfigTypes.CORE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Text;
|
||||
using Core.Annotations;
|
||||
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class EncodingUtil
|
||||
{
|
||||
public string Encode(string value, EncodeType encode)
|
||||
{
|
||||
return encode switch
|
||||
{
|
||||
EncodeType.BASE64 => Convert.ToBase64String(Encoding.Default.GetBytes(value)),
|
||||
EncodeType.HEX => Convert.ToHexString(Encoding.Default.GetBytes(value)),
|
||||
EncodeType.ASCII => Encoding.ASCII.GetString(Encoding.Default.GetBytes(value)),
|
||||
EncodeType.UTF8 => Encoding.UTF8.GetString(Encoding.Default.GetBytes(value)),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(encode), encode, null)
|
||||
};
|
||||
}
|
||||
|
||||
public string Decode(string value, EncodeType encode)
|
||||
{
|
||||
switch (encode)
|
||||
{
|
||||
case EncodeType.BASE64:
|
||||
return Encoding.UTF8.GetString(Convert.FromBase64String(value));
|
||||
case EncodeType.HEX:
|
||||
return Encoding.UTF8.GetString(Convert.FromHexString(value));
|
||||
case EncodeType.ASCII:
|
||||
return Encoding.ASCII.GetString(Encoding.Default.GetBytes(value));
|
||||
case EncodeType.UTF8:
|
||||
return Encoding.UTF8.GetString(Encoding.Default.GetBytes(value));
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(encode), encode, null);
|
||||
}
|
||||
}
|
||||
|
||||
public string FromBase64(string value)
|
||||
{
|
||||
return Decode(value, EncodeType.BASE64);
|
||||
}
|
||||
|
||||
public string ToBase64(string value)
|
||||
{
|
||||
return Encode(value, EncodeType.BASE64);
|
||||
}
|
||||
|
||||
public string FromHex(string value)
|
||||
{
|
||||
return Decode(value, EncodeType.HEX);
|
||||
}
|
||||
|
||||
public string ToHex(string value)
|
||||
{
|
||||
return Encode(value, EncodeType.HEX);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EncodeType {
|
||||
BASE64,
|
||||
HEX,
|
||||
ASCII,
|
||||
UTF8
|
||||
}
|
||||
+11
-12
@@ -6,13 +6,15 @@ using Core.Annotations;
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public partial class HashUtil
|
||||
public class HashUtil
|
||||
{
|
||||
private readonly Regex MongoIdRegex = new Regex("^[a-fA-F0-9]{24}$");
|
||||
|
||||
/// <summary>
|
||||
/// Create a 24 character id using the sha256 algorithm + current timestamp
|
||||
/// </summary>
|
||||
/// <returns>24 character hash</returns>
|
||||
public static string Generate()
|
||||
public string Generate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -22,22 +24,22 @@ public partial class HashUtil
|
||||
/// </summary>
|
||||
/// <param name="stringToCheck">String to check</param>
|
||||
/// <returns>True when string is a valid mongo id</returns>
|
||||
public static bool IsValidMongoId(string stringToCheck)
|
||||
public bool IsValidMongoId(string stringToCheck)
|
||||
{
|
||||
return MongoIdRegex().IsMatch(stringToCheck);
|
||||
return MongoIdRegex.IsMatch(stringToCheck);
|
||||
}
|
||||
|
||||
public static string GenerateMd5ForData(string data)
|
||||
public string GenerateMd5ForData(string data)
|
||||
{
|
||||
return GenerateHashForData(HashingAlgorithm.MD5, data);
|
||||
}
|
||||
|
||||
public static string GenerateSha1ForData(string data)
|
||||
public string GenerateSha1ForData(string data)
|
||||
{
|
||||
return GenerateHashForData(HashingAlgorithm.SHA1, data);
|
||||
}
|
||||
|
||||
public static string GenerateCrc32ForData(string data)
|
||||
public string GenerateCrc32ForData(string data)
|
||||
{
|
||||
// TODO: Could not find a ms way of doing this.
|
||||
// May need a custom impl to avoid an external lib. - CJ
|
||||
@@ -51,7 +53,7 @@ public partial class HashUtil
|
||||
/// <param name="data">data to be hashed</param>
|
||||
/// <returns>hash value</returns>
|
||||
/// <exception cref="NotImplementedException">thrown if the provided algorithm is not implemented</exception>>
|
||||
public static string GenerateHashForData(HashingAlgorithm algorithm, string data)
|
||||
public string GenerateHashForData(HashingAlgorithm algorithm, string data)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
@@ -71,7 +73,7 @@ public partial class HashUtil
|
||||
/// Generates an account ID for a profile
|
||||
/// </summary>
|
||||
/// <returns>Generated account ID</returns>
|
||||
public static int GenerateAccountId()
|
||||
public int GenerateAccountId()
|
||||
{
|
||||
const int min = 1000000;
|
||||
const int max = 1999999;
|
||||
@@ -80,9 +82,6 @@ public partial class HashUtil
|
||||
|
||||
return random.Next() * (max - min + 1) + min;
|
||||
}
|
||||
|
||||
[GeneratedRegex("^[a-fA-F0-9]{24}$", RegexOptions.IgnoreCase, "en")]
|
||||
private static partial Regex MongoIdRegex();
|
||||
}
|
||||
|
||||
public enum HashingAlgorithm
|
||||
|
||||
Reference in New Issue
Block a user