Merge branch 'develop' of https://github.com/sp-tarkov/server-csharp into develop
This commit is contained in:
@@ -54,15 +54,15 @@ public class GameCallbacks(
|
||||
/// Save profiles on game close
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ValueTask<string> GameLogout(string url, EmptyRequestData _, string sessionID)
|
||||
public async ValueTask<string> GameLogout(string url, EmptyRequestData _, string sessionID)
|
||||
{
|
||||
_saveServer.SaveProfile(sessionID);
|
||||
return new ValueTask<string>(_httpResponseUtil.GetBody(
|
||||
await _saveServer.SaveProfileAsync(sessionID);
|
||||
return _httpResponseUtil.GetBody(
|
||||
new GameLogoutResponseData
|
||||
{
|
||||
Status = "ok"
|
||||
}
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -26,10 +26,10 @@ public class LauncherCallbacks(
|
||||
return new ValueTask<string>(output ?? "FAILED");
|
||||
}
|
||||
|
||||
public ValueTask<string> Register(string url, RegisterData info, string sessionID)
|
||||
public async ValueTask<string> Register(string url, RegisterData info, string sessionID)
|
||||
{
|
||||
var output = _launcherController.Register(info);
|
||||
return new ValueTask<string>(string.IsNullOrEmpty(output) ? "FAILED" : "OK");
|
||||
var output = await _launcherController.Register(info);
|
||||
return string.IsNullOrEmpty(output) ? "FAILED" : "OK";
|
||||
}
|
||||
|
||||
public ValueTask<string> Get(string url, LoginRequestData info, string sessionID)
|
||||
|
||||
@@ -43,26 +43,26 @@ public class LauncherV2Callbacks(
|
||||
));
|
||||
}
|
||||
|
||||
public ValueTask<string> Register(RegisterData info)
|
||||
public async ValueTask<string> Register(RegisterData info)
|
||||
{
|
||||
return new ValueTask<string>(_httpResponseUtil.NoBody(
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2RegisterResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Register(info),
|
||||
Response = await _launcherV2Controller.Register(info),
|
||||
Profiles = _profileController.GetMiniProfiles()
|
||||
}
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
public ValueTask<string> PasswordChange(ChangeRequestData info)
|
||||
public async ValueTask<string> PasswordChange(ChangeRequestData info)
|
||||
{
|
||||
return new ValueTask<string>(_httpResponseUtil.NoBody(
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2PasswordChangeResponse
|
||||
{
|
||||
Response = _launcherV2Controller.PasswordChange(info),
|
||||
Response = await _launcherV2Controller.PasswordChange(info),
|
||||
Profiles = _profileController.GetMiniProfiles()
|
||||
}
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
public ValueTask<string> Remove(LoginRequestData info)
|
||||
|
||||
@@ -31,10 +31,10 @@ public class PrestigeCallbacks(
|
||||
/// <param name="info"></param>
|
||||
/// <param name="sessionID">Session/player id</param>
|
||||
/// <returns></returns>
|
||||
public ValueTask<string> ObtainPrestige(string url, ObtainPrestigeRequestList info, string sessionID)
|
||||
public async ValueTask<string> ObtainPrestige(string url, ObtainPrestigeRequestList info, string sessionID)
|
||||
{
|
||||
_prestigeController.ObtainPrestige(sessionID, info);
|
||||
await _prestigeController.ObtainPrestige(sessionID, info);
|
||||
|
||||
return new ValueTask<string>(_httpResponseUtil.NullResponse());
|
||||
return _httpResponseUtil.NullResponse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@ public class ProfileCallbacks(
|
||||
/// Handle client/game/profile/create
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ValueTask<string> CreateProfile(string url, ProfileCreateRequestData info, string sessionID)
|
||||
public async ValueTask<string> CreateProfile(string url, ProfileCreateRequestData info, string sessionID)
|
||||
{
|
||||
var id = _profileController.CreateProfile(info, sessionID);
|
||||
return new ValueTask<string>(_httpResponse.GetBody(
|
||||
var id = await _profileController.CreateProfile(info, sessionID);
|
||||
return _httpResponse.GetBody(
|
||||
new CreateProfileResponse
|
||||
{
|
||||
UserId = id
|
||||
}
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,19 +20,19 @@ public class SaveCallbacks(
|
||||
public async Task OnLoad()
|
||||
{
|
||||
_backupService.StartBackupSystem();
|
||||
_saveServer.Load();
|
||||
await _saveServer.LoadAsync();
|
||||
}
|
||||
|
||||
public Task<bool> OnUpdate(long secondsSinceLastRun)
|
||||
public async Task<bool> OnUpdate(long secondsSinceLastRun)
|
||||
{
|
||||
if (secondsSinceLastRun < _coreConfig.ProfileSaveIntervalInSeconds)
|
||||
{
|
||||
// Not enough time has passed since last run, exit early
|
||||
return Task.FromResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
_saveServer.Save();
|
||||
await _saveServer.SaveAsync();
|
||||
|
||||
return Task.FromResult(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public class LauncherController(
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
public string Register(RegisterData info)
|
||||
public async Task<string> Register(RegisterData info)
|
||||
{
|
||||
foreach (var kvp in _saveServer.GetProfiles())
|
||||
{
|
||||
@@ -107,14 +107,14 @@ public class LauncherController(
|
||||
}
|
||||
}
|
||||
|
||||
return CreateAccount(info);
|
||||
return await CreateAccount(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
protected string CreateAccount(RegisterData info)
|
||||
protected async Task<string> CreateAccount(RegisterData info)
|
||||
{
|
||||
var profileId = GenerateProfileId();
|
||||
var scavId = GenerateProfileId();
|
||||
@@ -130,8 +130,8 @@ public class LauncherController(
|
||||
};
|
||||
_saveServer.CreateProfile(newProfileDetails);
|
||||
|
||||
_saveServer.LoadProfile(profileId);
|
||||
_saveServer.SaveProfile(profileId);
|
||||
await _saveServer.LoadProfileAsync(profileId);
|
||||
await _saveServer.SaveProfileAsync(profileId);
|
||||
|
||||
return profileId;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class LauncherV2Controller(
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
public bool Register(RegisterData info)
|
||||
public async Task<bool> Register(RegisterData info)
|
||||
{
|
||||
foreach (var session in _saveServer.GetProfiles())
|
||||
{
|
||||
@@ -81,7 +81,7 @@ public class LauncherV2Controller(
|
||||
}
|
||||
}
|
||||
|
||||
CreateAccount(info);
|
||||
await CreateAccount(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class LauncherV2Controller(
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
public bool PasswordChange(ChangeRequestData info)
|
||||
public async Task<bool> PasswordChange(ChangeRequestData info)
|
||||
{
|
||||
var sessionId = GetSessionId(info);
|
||||
|
||||
@@ -105,7 +105,7 @@ public class LauncherV2Controller(
|
||||
}
|
||||
|
||||
_saveServer.GetProfile(sessionId).ProfileInfo!.Password = info.Change;
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
await _saveServer.SaveProfileAsync(sessionId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public class LauncherV2Controller(
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns></returns>
|
||||
protected string CreateAccount(RegisterData info)
|
||||
protected async Task<string> CreateAccount(RegisterData info)
|
||||
{
|
||||
var profileId = GenerateProfileId();
|
||||
var scavId = GenerateProfileId();
|
||||
@@ -179,8 +179,8 @@ public class LauncherV2Controller(
|
||||
|
||||
_saveServer.CreateProfile(newProfileDetails);
|
||||
|
||||
_saveServer.LoadProfile(profileId);
|
||||
_saveServer.SaveProfile(profileId);
|
||||
await _saveServer.LoadProfileAsync(profileId);
|
||||
await _saveServer.SaveProfileAsync(profileId);
|
||||
|
||||
return profileId;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class PrestigeController(
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void ObtainPrestige(
|
||||
public async Task ObtainPrestige(
|
||||
string sessionId,
|
||||
ObtainPrestigeRequestList request)
|
||||
{
|
||||
@@ -75,7 +75,7 @@ public class PrestigeController(
|
||||
profile.SptData.PendingPrestige = pendingPrestige;
|
||||
profile.ProfileInfo.IsWiped = true;
|
||||
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
await _saveServer.SaveProfileAsync(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ public class ProfileController(
|
||||
/// <param name="request">Create profile request</param>
|
||||
/// <param name="sessionId">Player id</param>
|
||||
/// <returns>Player id</returns>
|
||||
public virtual string CreateProfile(ProfileCreateRequestData request, string sessionId)
|
||||
public virtual async ValueTask<string> CreateProfile(ProfileCreateRequestData request, string sessionId)
|
||||
{
|
||||
return _createProfileService.CreateProfile(sessionId, request);
|
||||
return await _createProfileService.CreateProfile(sessionId, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SaveServer(
|
||||
/// <summary>
|
||||
/// Load all profiles in /user/profiles folder into memory (this.profiles)
|
||||
/// </summary>
|
||||
public void Load()
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
// get files to load
|
||||
if (!_fileUtil.DirectoryExists(profileFilepath))
|
||||
@@ -70,7 +70,7 @@ public class SaveServer(
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
foreach (var file in files)
|
||||
{
|
||||
LoadProfile(_fileUtil.StripExtension(file));
|
||||
await LoadProfileAsync(_fileUtil.StripExtension(file));
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
@@ -83,13 +83,13 @@ public class SaveServer(
|
||||
/// <summary>
|
||||
/// Save changes for each profile from memory into user/profiles json
|
||||
/// </summary>
|
||||
public void Save()
|
||||
public async Task SaveAsync()
|
||||
{
|
||||
// Save every profile
|
||||
var totalTime = 0L;
|
||||
foreach (var sessionID in profiles)
|
||||
{
|
||||
totalTime += SaveProfile(sessionID.Key);
|
||||
totalTime += await SaveProfileAsync(sessionID.Key);
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
@@ -196,14 +196,14 @@ public class SaveServer(
|
||||
/// Execute saveLoadRouters callbacks after being loaded into memory.
|
||||
/// </summary>
|
||||
/// <param name="sessionID"> ID of profile to store in memory </param>
|
||||
public void LoadProfile(string sessionID)
|
||||
public async Task LoadProfileAsync(string sessionID)
|
||||
{
|
||||
var filename = $"{sessionID}.json";
|
||||
var filePath = $"{profileFilepath}{filename}";
|
||||
if (_fileUtil.FileExists(filePath))
|
||||
// File found, store in profiles[]
|
||||
{
|
||||
profiles[sessionID] = _jsonUtil.DeserializeFromFile<SptProfile>(filePath);
|
||||
profiles[sessionID] = await _jsonUtil.DeserializeFromFileAsync<SptProfile>(filePath);
|
||||
}
|
||||
|
||||
// Run callbacks
|
||||
@@ -220,7 +220,7 @@ public class SaveServer(
|
||||
/// </summary>
|
||||
/// <param name="sessionID"> Profile id (user/profiles/id.json) </param>
|
||||
/// <returns> Time taken to save the profile in seconds </returns>
|
||||
public long SaveProfile(string sessionID)
|
||||
public async Task<long> SaveProfileAsync(string sessionID)
|
||||
{
|
||||
var filePath = $"{profileFilepath}{sessionID}.json";
|
||||
|
||||
@@ -250,12 +250,12 @@ public class SaveServer(
|
||||
|
||||
var start = Stopwatch.StartNew();
|
||||
var jsonProfile = _jsonUtil.Serialize(profiles[sessionID], !_configServer.GetConfig<CoreConfig>().Features.CompressProfile);
|
||||
var fmd5 = _hashUtil.GenerateMd5ForData(jsonProfile);
|
||||
var fmd5 = await _hashUtil.GenerateHashForDataAsync(HashingAlgorithm.MD5, jsonProfile);
|
||||
if (!saveMd5.TryGetValue(sessionID, out var currentMd5) || currentMd5 != fmd5)
|
||||
{
|
||||
saveMd5[sessionID] = fmd5;
|
||||
// save profile to disk
|
||||
_fileUtil.WriteFile(filePath, jsonProfile);
|
||||
await _fileUtil.WriteFileAsync(filePath, jsonProfile);
|
||||
}
|
||||
|
||||
start.Stop();
|
||||
|
||||
@@ -46,22 +46,22 @@ public class BackupService
|
||||
/// <summary>
|
||||
/// Start the backup interval if enabled in config.
|
||||
/// </summary>
|
||||
public void StartBackupSystem()
|
||||
public async Task StartBackupSystem()
|
||||
{
|
||||
if (!_backupConfig.BackupInterval.Enabled)
|
||||
{
|
||||
// Not backing up at regular intervals, run once and exit
|
||||
Init();
|
||||
await Init();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_backupIntervalTimer = new Timer(
|
||||
_backupIntervalTimer = new Timer(async
|
||||
_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Init();
|
||||
await Init();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ public class BackupService
|
||||
/// This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning
|
||||
/// up old backups if the number exceeds the configured maximum.
|
||||
/// </summary>
|
||||
public void Init()
|
||||
public async Task Init()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
{
|
||||
@@ -129,7 +129,7 @@ public class BackupService
|
||||
}
|
||||
|
||||
// Write a copy of active mods.
|
||||
_fileUtil.WriteFile(Path.Combine(targetDir, "activeMods.json"), _jsonUtil.Serialize(_activeServerMods));
|
||||
await _fileUtil.WriteFileAsync(Path.Combine(targetDir, "activeMods.json"), _jsonUtil.Serialize(_activeServerMods));
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BundleHashCacheService(
|
||||
public bool CalculateAndMatchHash(string bundlePath)
|
||||
{
|
||||
var fileContents = _fileUtil.ReadFile(bundlePath);
|
||||
var generatedHash = _hashUtil.GenerateMd5ForData(fileContents);
|
||||
var generatedHash = _hashUtil.GenerateHashForData(HashingAlgorithm.MD5, fileContents);
|
||||
|
||||
return MatchWithStoredHash(bundlePath, generatedHash);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class BundleHashCacheService(
|
||||
public void CalculateAndStoreHash(string bundlePath)
|
||||
{
|
||||
var fileContents = _fileUtil.ReadFile(bundlePath);
|
||||
var generatedHash = _hashUtil.GenerateMd5ForData(fileContents);
|
||||
var generatedHash = _hashUtil.GenerateHashForData(HashingAlgorithm.MD5, fileContents);
|
||||
|
||||
StoreValue(bundlePath, generatedHash);
|
||||
}
|
||||
|
||||
@@ -42,14 +42,14 @@ public class ModHashCacheService(
|
||||
|
||||
public bool CalculateAndCompareHash(string modName, string modContent)
|
||||
{
|
||||
var generatedHash = _hashUtil.GenerateSha1ForData(modContent);
|
||||
var generatedHash = _hashUtil.GenerateHashForData(HashingAlgorithm.SHA1, modContent);
|
||||
|
||||
return MatchWithStoredHash(modName, generatedHash);
|
||||
}
|
||||
|
||||
public void CalculateAndStoreHash(string modName, string modContent)
|
||||
{
|
||||
var generatedHash = _hashUtil.GenerateSha1ForData(modContent);
|
||||
var generatedHash = _hashUtil.GenerateHashForData(HashingAlgorithm.SHA1, modContent);
|
||||
|
||||
StoreValue(modName, generatedHash);
|
||||
}
|
||||
|
||||
@@ -549,7 +549,7 @@ public class CircleOfCultistService(
|
||||
// Key is sacrificed items separated by commas, a dash, then the rewards separated by commas
|
||||
var key = $"{{{required}-{reward}}}";
|
||||
|
||||
return _hashUtil.GenerateMd5ForData(key);
|
||||
return _hashUtil.GenerateHashForData(HashingAlgorithm.MD5, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -887,7 +887,7 @@ public class CircleOfCultistService(
|
||||
protected string CreateSacrificeCacheKey(IEnumerable<string> requiredItems)
|
||||
{
|
||||
var concat = string.Join(",", requiredItems.OrderBy(item => item));
|
||||
return _hashUtil.GenerateMd5ForData(concat);
|
||||
return _hashUtil.GenerateHashForData(HashingAlgorithm.MD5, concat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,7 +40,7 @@ public class CreateProfileService(
|
||||
MailSendService _mailSendService
|
||||
)
|
||||
{
|
||||
public string CreateProfile(string sessionId, ProfileCreateRequestData request)
|
||||
public async ValueTask<string> CreateProfile(string sessionId, ProfileCreateRequestData request)
|
||||
{
|
||||
var account = _cloner.Clone(_saveServer.GetProfile(sessionId));
|
||||
var profileTemplateClone = _cloner.Clone(_profileHelper.GetProfileTemplateForSide(account.ProfileInfo.Edition, request.Side));
|
||||
@@ -207,12 +207,12 @@ public class CreateProfileService(
|
||||
_saveServer.GetProfile(sessionId).CharacterData.ScavData = _playerScavGenerator.Generate(sessionId);
|
||||
|
||||
// Store minimal profile and reload it
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
_saveServer.LoadProfile(sessionId);
|
||||
await _saveServer.SaveProfileAsync(sessionId);
|
||||
await _saveServer.LoadProfileAsync(sessionId);
|
||||
|
||||
// Completed account creation
|
||||
_saveServer.GetProfile(sessionId).ProfileInfo.IsWiped = false;
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
await _saveServer.SaveProfileAsync(sessionId);
|
||||
|
||||
return pmcData.Id;
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ public class LocationLifecycleService
|
||||
pmcProfile.Info.LastTimePlayedAsSavage = _timeUtil.GetTimeStamp();
|
||||
|
||||
// Force a profile save
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
_saveServer.SaveProfileAsync(sessionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -90,6 +90,31 @@ public class FileUtil()
|
||||
File.WriteAllBytes(filePath, fileContent);
|
||||
}
|
||||
|
||||
public async Task WriteFileAsync(string filePath, string fileContent)
|
||||
{
|
||||
if (!DirectoryExists(Path.GetDirectoryName(filePath)))
|
||||
{
|
||||
CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
}
|
||||
|
||||
if (!FileExists(filePath))
|
||||
{
|
||||
CreateFile(filePath);
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(filePath, fileContent);
|
||||
}
|
||||
|
||||
public async Task WriteFileAsync(string filePath, byte[] fileContent)
|
||||
{
|
||||
if (!FileExists(filePath))
|
||||
{
|
||||
CreateFile(filePath);
|
||||
}
|
||||
|
||||
await File.WriteAllBytesAsync(filePath, fileContent);
|
||||
}
|
||||
|
||||
private void CreateFile(string filePath)
|
||||
{
|
||||
var stream = File.Create(filePath);
|
||||
|
||||
@@ -50,16 +50,6 @@ public partial class HashUtil(RandomUtil _randomUtil)
|
||||
return MongoIdRegex().IsMatch(stringToCheck);
|
||||
}
|
||||
|
||||
public string GenerateMd5ForData(string data)
|
||||
{
|
||||
return GenerateHashForData(HashingAlgorithm.MD5, data);
|
||||
}
|
||||
|
||||
public string GenerateSha1ForData(string data)
|
||||
{
|
||||
return GenerateHashForData(HashingAlgorithm.SHA1, data);
|
||||
}
|
||||
|
||||
public uint GenerateCrc32ForData(string data)
|
||||
{
|
||||
return Crc32.HashToUInt32(new ArraySegment<byte>(Encoding.UTF8.GetBytes(data)));
|
||||
@@ -89,6 +79,36 @@ public partial class HashUtil(RandomUtil _randomUtil)
|
||||
throw new NotImplementedException($"Provided hash algorithm: {algorithm} is not supported.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a hash for the data parameter asynchronously
|
||||
/// </summary>
|
||||
/// <param name="algorithm">algorithm to use to hash</param>
|
||||
/// <param name="data">data to be hashed</param>
|
||||
/// <returns>A task which contains the hash value</returns>
|
||||
/// <exception cref="NotImplementedException">thrown if the provided algorithm is not implemented</exception>
|
||||
/// >
|
||||
public async Task<string> GenerateHashForDataAsync(HashingAlgorithm algorithm, string data)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case HashingAlgorithm.MD5:
|
||||
{
|
||||
await using var ms = new MemoryStream(Encoding.UTF8.GetBytes(data));
|
||||
var md5HashData = await MD5.HashDataAsync(ms);
|
||||
return Convert.ToHexString(md5HashData).Replace("-", string.Empty);
|
||||
}
|
||||
|
||||
case HashingAlgorithm.SHA1:
|
||||
{
|
||||
await using var ms = new MemoryStream(Encoding.UTF8.GetBytes(data));
|
||||
var sha1HashData = await SHA1.HashDataAsync(ms);
|
||||
return Convert.ToHexString(sha1HashData).Replace("-", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Provided hash algorithm: {algorithm} is not supported.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates an account ID for a profile
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user