Extract game user admin service
This commit is contained in:
@@ -11,22 +11,14 @@ public sealed class GameService : IGameService
|
||||
public GameService(IDbContextFactory<RpgRollerDbContext> dbContextFactory, IPasswordHasher<UserAccount> passwordHasher, IDiceRoller diceRoller)
|
||||
{
|
||||
m_StateStore = new();
|
||||
m_CampaignsById = m_StateStore.CampaignsById;
|
||||
m_CampaignStateById = m_StateStore.CampaignStateById;
|
||||
m_CharactersById = m_StateStore.CharactersById;
|
||||
m_Gate = m_StateStore.Gate;
|
||||
m_RollLog = m_StateStore.RollLog;
|
||||
m_SessionsByToken = m_StateStore.SessionsByToken;
|
||||
m_SkillGroupsById = m_StateStore.SkillGroupsById;
|
||||
m_SkillsById = m_StateStore.SkillsById;
|
||||
m_UserIdsByUsername = m_StateStore.UserIdsByUsername;
|
||||
m_UsersById = m_StateStore.UsersById;
|
||||
m_PersistenceService = new(dbContextFactory, m_StateStore);
|
||||
m_AuthService = new(m_StateStore, passwordHasher, m_PersistenceService);
|
||||
m_CampaignService = new(m_StateStore, m_PersistenceService);
|
||||
m_CharacterService = new(m_StateStore, m_PersistenceService);
|
||||
m_RollService = new(m_StateStore, m_PersistenceService, diceRoller);
|
||||
m_SkillService = new(m_StateStore, m_PersistenceService);
|
||||
m_UserAdministrationService = new(m_StateStore, m_PersistenceService);
|
||||
LoadStateFromDatabase();
|
||||
}
|
||||
|
||||
@@ -87,109 +79,22 @@ public sealed class GameService : IGameService
|
||||
|
||||
public ServiceResult<IReadOnlyList<string>> GetUsernames(string sessionToken)
|
||||
{
|
||||
lock (m_Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<IReadOnlyList<string>>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
var usernames = m_UsersById.Values.Select(account => account.Username).OrderBy(username => username, StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
return ServiceResult<IReadOnlyList<string>>.Success(usernames);
|
||||
}
|
||||
return m_UserAdministrationService.GetUsernames(sessionToken);
|
||||
}
|
||||
|
||||
public ServiceResult<IReadOnlyList<AdminUserSummary>> GetUsers(string sessionToken)
|
||||
{
|
||||
lock (m_Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
var users = m_UsersById.Values
|
||||
.OrderBy(account => account.Username, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(ToAdminUserSummary)
|
||||
.ToArray();
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Success(users);
|
||||
}
|
||||
return m_UserAdministrationService.GetUsers(sessionToken);
|
||||
}
|
||||
|
||||
public ServiceResult<AdminUserSummary> UpdateUserRoles(string sessionToken, Guid userId, IReadOnlyList<string> roles)
|
||||
{
|
||||
lock (m_Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<AdminUserSummary>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<AdminUserSummary>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
if (!m_UsersById.TryGetValue(userId, out var targetUser))
|
||||
return ServiceResult<AdminUserSummary>.Failure("user_not_found", "User was not found.");
|
||||
|
||||
var normalizedRoles = RoleSerializer.Normalize(roles);
|
||||
if (normalizedRoles.Any(role => !string.Equals(role, UserRoles.Admin, StringComparison.Ordinal)))
|
||||
return ServiceResult<AdminUserSummary>.Failure("invalid_role", "Unsupported role.");
|
||||
|
||||
if (user.Id == targetUser.Id && !normalizedRoles.Contains(UserRoles.Admin, StringComparer.Ordinal))
|
||||
return ServiceResult<AdminUserSummary>.Failure("forbidden", "You cannot remove your own admin role.");
|
||||
|
||||
targetUser.Roles = RoleSerializer.Serialize(normalizedRoles);
|
||||
PersistStateLocked();
|
||||
return ServiceResult<AdminUserSummary>.Success(ToAdminUserSummary(targetUser));
|
||||
}
|
||||
return m_UserAdministrationService.UpdateUserRoles(sessionToken, userId, roles);
|
||||
}
|
||||
|
||||
public ServiceResult<bool> DeleteUser(string sessionToken, Guid userId)
|
||||
{
|
||||
lock (m_Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<bool>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<bool>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
if (user.Id == userId)
|
||||
return ServiceResult<bool>.Failure("forbidden", "You cannot delete your own account.");
|
||||
|
||||
if (!m_UsersById.TryGetValue(userId, out var targetUser))
|
||||
return ServiceResult<bool>.Failure("user_not_found", "User was not found.");
|
||||
|
||||
var gmCampaignIds = m_CampaignsById.Values.Where(campaign => campaign.GmUserId == targetUser.Id).Select(campaign => campaign.Id).ToArray();
|
||||
var gmCampaignIdSet = gmCampaignIds.ToHashSet();
|
||||
var preservedCharacterIds = m_CharactersById.Values
|
||||
.Where(character => character.CampaignId.HasValue && gmCampaignIdSet.Contains(character.CampaignId.Value))
|
||||
.Select(character => character.Id)
|
||||
.ToHashSet();
|
||||
|
||||
foreach (var campaignId in gmCampaignIds)
|
||||
DeleteCampaignLocked(campaignId);
|
||||
|
||||
var ownedCharacterIds = m_CharactersById.Values
|
||||
.Where(character => character.OwnerUserId == targetUser.Id && !preservedCharacterIds.Contains(character.Id))
|
||||
.Select(character => character.Id)
|
||||
.ToArray();
|
||||
foreach (var characterId in ownedCharacterIds)
|
||||
DeleteCharacterLocked(characterId);
|
||||
|
||||
m_RollLog.RemoveAll(entry => entry.RollerUserId == targetUser.Id);
|
||||
|
||||
var staleSessions = m_SessionsByToken.Values.Where(session => session.UserId == targetUser.Id).Select(session => session.Token).ToArray();
|
||||
foreach (var token in staleSessions)
|
||||
m_SessionsByToken.Remove(token);
|
||||
|
||||
m_UsersById.Remove(targetUser.Id);
|
||||
m_UserIdsByUsername.Remove(targetUser.UsernameNormalized);
|
||||
|
||||
PersistStateLocked();
|
||||
return ServiceResult<bool>.Success(true);
|
||||
}
|
||||
return m_UserAdministrationService.DeleteUser(sessionToken, userId);
|
||||
}
|
||||
|
||||
public ServiceResult<CharacterSummary> CreateCharacter(string sessionToken, string name, Guid campaignId)
|
||||
@@ -282,110 +187,12 @@ public sealed class GameService : IGameService
|
||||
return m_RollService.GetCampaignStateSnapshot(sessionToken, campaignId);
|
||||
}
|
||||
|
||||
private static UserSummary ToUserSummary(UserAccount user)
|
||||
{
|
||||
return new(user.Id, user.Username, user.DisplayName, RoleSerializer.Parse(user.Roles));
|
||||
}
|
||||
|
||||
private static AdminUserSummary ToAdminUserSummary(UserAccount user)
|
||||
{
|
||||
return new(user.Id, user.Username, user.DisplayName, RoleSerializer.Parse(user.Roles));
|
||||
}
|
||||
|
||||
private bool TryGetCurrentCampaignIdLocked(UserAccount user, out Guid campaignId)
|
||||
{
|
||||
campaignId = Guid.Empty;
|
||||
if (user.ActiveCharacterId is not Guid activeCharacterId)
|
||||
return false;
|
||||
|
||||
if (!m_CharactersById.TryGetValue(activeCharacterId, out var character))
|
||||
{
|
||||
user.ActiveCharacterId = null;
|
||||
PersistStateLocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!character.CampaignId.HasValue)
|
||||
return false;
|
||||
|
||||
campaignId = character.CampaignId.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryResolveCharacterCampaignLocked(Character character, out Campaign campaign, out ServiceError? error)
|
||||
{
|
||||
campaign = default!;
|
||||
if (!character.CampaignId.HasValue || !m_CampaignsById.TryGetValue(character.CampaignId.Value, out var resolvedCampaign) || resolvedCampaign is null)
|
||||
{
|
||||
error = new ServiceError("character_not_in_campaign", "Character is not linked to a campaign.");
|
||||
return false;
|
||||
}
|
||||
|
||||
campaign = resolvedCampaign;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DeleteCampaignLocked(Guid campaignId)
|
||||
{
|
||||
if (!m_CampaignsById.Remove(campaignId))
|
||||
return;
|
||||
|
||||
var affectedCharacterIds = m_CharactersById.Values.Where(character => character.CampaignId == campaignId).Select(character => character.Id).ToArray();
|
||||
foreach (var characterId in affectedCharacterIds)
|
||||
m_CharactersById[characterId].CampaignId = null;
|
||||
|
||||
m_RollLog.RemoveAll(entry => entry.CampaignId == campaignId);
|
||||
m_CampaignStateById.Remove(campaignId);
|
||||
}
|
||||
|
||||
private void DeleteCharacterLocked(Guid characterId)
|
||||
{
|
||||
if (!m_CharactersById.TryGetValue(characterId, out var character))
|
||||
return;
|
||||
|
||||
var campaignId = character.CampaignId;
|
||||
m_CharactersById.Remove(characterId);
|
||||
|
||||
var skillGroupIds = m_SkillGroupsById.Values.Where(group => group.CharacterId == characterId).Select(group => group.Id).ToHashSet();
|
||||
foreach (var skillGroupId in skillGroupIds)
|
||||
m_SkillGroupsById.Remove(skillGroupId);
|
||||
|
||||
var skillIds = m_SkillsById.Values.Where(skill => skill.CharacterId == characterId).Select(skill => skill.Id).ToHashSet();
|
||||
foreach (var skillId in skillIds)
|
||||
m_SkillsById.Remove(skillId);
|
||||
|
||||
m_RollLog.RemoveAll(entry => entry.CharacterId == characterId || skillIds.Contains(entry.SkillId));
|
||||
|
||||
foreach (var user in m_UsersById.Values.Where(account => account.ActiveCharacterId == characterId))
|
||||
user.ActiveCharacterId = null;
|
||||
|
||||
RemoveCharacterStateLocked(campaignId, characterId);
|
||||
TouchRosterLocked(campaignId);
|
||||
}
|
||||
|
||||
private static bool UserHasRoleLocked(UserAccount user, string role)
|
||||
{
|
||||
return RoleSerializer.HasRole(user.Roles, role);
|
||||
}
|
||||
|
||||
private UserAccount? ResolveUserLocked(string sessionToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionToken))
|
||||
return null;
|
||||
|
||||
if (!m_SessionsByToken.TryGetValue(sessionToken, out var session))
|
||||
return null;
|
||||
|
||||
return m_UsersById.GetValueOrDefault(session.UserId);
|
||||
}
|
||||
|
||||
private GameCampaignStateTracker GetOrCreateCampaignStateLocked(Guid campaignId)
|
||||
{
|
||||
if (!m_CampaignStateById.TryGetValue(campaignId, out var state))
|
||||
if (!m_StateStore.CampaignStateById.TryGetValue(campaignId, out var state))
|
||||
{
|
||||
state = new GameCampaignStateTracker();
|
||||
m_CampaignStateById[campaignId] = state;
|
||||
m_StateStore.CampaignStateById[campaignId] = state;
|
||||
}
|
||||
|
||||
return state;
|
||||
@@ -393,49 +200,21 @@ public sealed class GameService : IGameService
|
||||
|
||||
private void AddCharacterStateLocked(Guid? campaignId, Guid characterId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_CampaignsById.ContainsKey(campaignId.Value))
|
||||
if (!campaignId.HasValue || !m_StateStore.CampaignsById.ContainsKey(campaignId.Value))
|
||||
return;
|
||||
|
||||
var state = GetOrCreateCampaignStateLocked(campaignId.Value);
|
||||
state.CharacterVersions[characterId] = 1;
|
||||
}
|
||||
|
||||
private void RemoveCharacterStateLocked(Guid? campaignId, Guid characterId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_CampaignStateById.TryGetValue(campaignId.Value, out var state))
|
||||
return;
|
||||
|
||||
state.CharacterVersions.Remove(characterId);
|
||||
}
|
||||
|
||||
private void TouchRosterLocked(Guid? campaignId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_CampaignsById.ContainsKey(campaignId.Value))
|
||||
return;
|
||||
|
||||
var state = GetOrCreateCampaignStateLocked(campaignId.Value);
|
||||
state.TotalVersion += 1;
|
||||
state.RosterVersion += 1;
|
||||
}
|
||||
|
||||
private void TouchLogLocked(Guid? campaignId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_CampaignsById.ContainsKey(campaignId.Value))
|
||||
return;
|
||||
|
||||
var state = GetOrCreateCampaignStateLocked(campaignId.Value);
|
||||
state.TotalVersion += 1;
|
||||
state.LogVersion += 1;
|
||||
}
|
||||
|
||||
private void RebuildCampaignStateLocked()
|
||||
{
|
||||
m_CampaignStateById.Clear();
|
||||
m_StateStore.CampaignStateById.Clear();
|
||||
|
||||
foreach (var campaignId in m_CampaignsById.Keys)
|
||||
m_CampaignStateById[campaignId] = new GameCampaignStateTracker();
|
||||
foreach (var campaignId in m_StateStore.CampaignsById.Keys)
|
||||
m_StateStore.CampaignStateById[campaignId] = new GameCampaignStateTracker();
|
||||
|
||||
foreach (var character in m_CharactersById.Values.Where(character => character.CampaignId.HasValue))
|
||||
foreach (var character in m_StateStore.CharactersById.Values.Where(character => character.CampaignId.HasValue))
|
||||
AddCharacterStateLocked(character.CampaignId, character.Id);
|
||||
}
|
||||
|
||||
@@ -446,26 +225,13 @@ public sealed class GameService : IGameService
|
||||
RebuildCampaignStateLocked();
|
||||
}
|
||||
|
||||
private void PersistStateLocked()
|
||||
{
|
||||
m_PersistenceService.PersistStateLocked();
|
||||
}
|
||||
|
||||
private readonly Dictionary<Guid, Campaign> m_CampaignsById;
|
||||
private readonly Dictionary<Guid, GameCampaignStateTracker> m_CampaignStateById;
|
||||
private readonly GameCampaignService m_CampaignService;
|
||||
private readonly GameCharacterService m_CharacterService;
|
||||
private readonly Dictionary<Guid, Character> m_CharactersById;
|
||||
private readonly GameAuthService m_AuthService;
|
||||
private readonly object m_Gate;
|
||||
private readonly GamePersistenceService m_PersistenceService;
|
||||
private readonly List<RollLogEntry> m_RollLog;
|
||||
private readonly GameRollService m_RollService;
|
||||
private readonly Dictionary<string, UserSession> m_SessionsByToken;
|
||||
private readonly GameSkillService m_SkillService;
|
||||
private readonly Dictionary<Guid, SkillGroup> m_SkillGroupsById;
|
||||
private readonly Dictionary<Guid, Skill> m_SkillsById;
|
||||
private readonly GameStateStore m_StateStore;
|
||||
private readonly Dictionary<string, Guid> m_UserIdsByUsername;
|
||||
private readonly Dictionary<Guid, UserAccount> m_UsersById;
|
||||
private readonly GameUserAdministrationService m_UserAdministrationService;
|
||||
}
|
||||
|
||||
231
RpgRoller/Services/GameUserAdministrationService.cs
Normal file
231
RpgRoller/Services/GameUserAdministrationService.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
using RpgRoller.Contracts;
|
||||
using RpgRoller.Domain;
|
||||
|
||||
namespace RpgRoller.Services;
|
||||
|
||||
public sealed class GameUserAdministrationService
|
||||
{
|
||||
public GameUserAdministrationService(GameStateStore stateStore, GamePersistenceService persistenceService)
|
||||
{
|
||||
m_StateStore = stateStore;
|
||||
m_PersistenceService = persistenceService;
|
||||
}
|
||||
|
||||
public ServiceResult<IReadOnlyList<string>> GetUsernames(string sessionToken)
|
||||
{
|
||||
lock (m_StateStore.Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<IReadOnlyList<string>>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
var usernames = m_StateStore.UsersById.Values
|
||||
.Select(account => account.Username)
|
||||
.OrderBy(username => username, StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
return ServiceResult<IReadOnlyList<string>>.Success(usernames);
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceResult<IReadOnlyList<AdminUserSummary>> GetUsers(string sessionToken)
|
||||
{
|
||||
lock (m_StateStore.Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
var users = m_StateStore.UsersById.Values
|
||||
.OrderBy(account => account.Username, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(ToAdminUserSummary)
|
||||
.ToArray();
|
||||
|
||||
return ServiceResult<IReadOnlyList<AdminUserSummary>>.Success(users);
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceResult<AdminUserSummary> UpdateUserRoles(string sessionToken, Guid userId, IReadOnlyList<string> roles)
|
||||
{
|
||||
lock (m_StateStore.Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<AdminUserSummary>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<AdminUserSummary>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
if (!m_StateStore.UsersById.TryGetValue(userId, out var targetUser))
|
||||
return ServiceResult<AdminUserSummary>.Failure("user_not_found", "User was not found.");
|
||||
|
||||
var normalizedRoles = RoleSerializer.Normalize(roles);
|
||||
if (normalizedRoles.Any(role => !string.Equals(role, UserRoles.Admin, StringComparison.Ordinal)))
|
||||
return ServiceResult<AdminUserSummary>.Failure("invalid_role", "Unsupported role.");
|
||||
|
||||
if (user.Id == targetUser.Id && !normalizedRoles.Contains(UserRoles.Admin, StringComparer.Ordinal))
|
||||
return ServiceResult<AdminUserSummary>.Failure("forbidden", "You cannot remove your own admin role.");
|
||||
|
||||
targetUser.Roles = RoleSerializer.Serialize(normalizedRoles);
|
||||
m_PersistenceService.PersistStateLocked();
|
||||
return ServiceResult<AdminUserSummary>.Success(ToAdminUserSummary(targetUser));
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceResult<bool> DeleteUser(string sessionToken, Guid userId)
|
||||
{
|
||||
lock (m_StateStore.Gate)
|
||||
{
|
||||
var user = ResolveUserLocked(sessionToken);
|
||||
if (user is null)
|
||||
return ServiceResult<bool>.Failure("unauthorized", "You must be logged in.");
|
||||
|
||||
if (!UserHasRoleLocked(user, UserRoles.Admin))
|
||||
return ServiceResult<bool>.Failure("forbidden", "Admin role is required.");
|
||||
|
||||
if (user.Id == userId)
|
||||
return ServiceResult<bool>.Failure("forbidden", "You cannot delete your own account.");
|
||||
|
||||
if (!m_StateStore.UsersById.TryGetValue(userId, out var targetUser))
|
||||
return ServiceResult<bool>.Failure("user_not_found", "User was not found.");
|
||||
|
||||
var gmCampaignIds = m_StateStore.CampaignsById.Values
|
||||
.Where(campaign => campaign.GmUserId == targetUser.Id)
|
||||
.Select(campaign => campaign.Id)
|
||||
.ToArray();
|
||||
var gmCampaignIdSet = gmCampaignIds.ToHashSet();
|
||||
var preservedCharacterIds = m_StateStore.CharactersById.Values
|
||||
.Where(character => character.CampaignId.HasValue && gmCampaignIdSet.Contains(character.CampaignId.Value))
|
||||
.Select(character => character.Id)
|
||||
.ToHashSet();
|
||||
|
||||
foreach (var campaignId in gmCampaignIds)
|
||||
DeleteCampaignLocked(campaignId);
|
||||
|
||||
var ownedCharacterIds = m_StateStore.CharactersById.Values
|
||||
.Where(character => character.OwnerUserId == targetUser.Id && !preservedCharacterIds.Contains(character.Id))
|
||||
.Select(character => character.Id)
|
||||
.ToArray();
|
||||
foreach (var characterId in ownedCharacterIds)
|
||||
DeleteCharacterLocked(characterId);
|
||||
|
||||
m_StateStore.RollLog.RemoveAll(entry => entry.RollerUserId == targetUser.Id);
|
||||
|
||||
var staleSessions = m_StateStore.SessionsByToken.Values
|
||||
.Where(session => session.UserId == targetUser.Id)
|
||||
.Select(session => session.Token)
|
||||
.ToArray();
|
||||
foreach (var token in staleSessions)
|
||||
m_StateStore.SessionsByToken.Remove(token);
|
||||
|
||||
m_StateStore.UsersById.Remove(targetUser.Id);
|
||||
m_StateStore.UserIdsByUsername.Remove(targetUser.UsernameNormalized);
|
||||
|
||||
m_PersistenceService.PersistStateLocked();
|
||||
return ServiceResult<bool>.Success(true);
|
||||
}
|
||||
}
|
||||
|
||||
private UserAccount? ResolveUserLocked(string sessionToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionToken))
|
||||
return null;
|
||||
|
||||
if (!m_StateStore.SessionsByToken.TryGetValue(sessionToken, out var session))
|
||||
return null;
|
||||
|
||||
return m_StateStore.UsersById.GetValueOrDefault(session.UserId);
|
||||
}
|
||||
|
||||
private static bool UserHasRoleLocked(UserAccount user, string role)
|
||||
{
|
||||
return RoleSerializer.HasRole(user.Roles, role);
|
||||
}
|
||||
|
||||
private static AdminUserSummary ToAdminUserSummary(UserAccount user)
|
||||
{
|
||||
return new(user.Id, user.Username, user.DisplayName, RoleSerializer.Parse(user.Roles));
|
||||
}
|
||||
|
||||
private void DeleteCampaignLocked(Guid campaignId)
|
||||
{
|
||||
if (!m_StateStore.CampaignsById.Remove(campaignId))
|
||||
return;
|
||||
|
||||
var affectedCharacterIds = m_StateStore.CharactersById.Values
|
||||
.Where(character => character.CampaignId == campaignId)
|
||||
.Select(character => character.Id)
|
||||
.ToArray();
|
||||
foreach (var characterId in affectedCharacterIds)
|
||||
m_StateStore.CharactersById[characterId].CampaignId = null;
|
||||
|
||||
m_StateStore.RollLog.RemoveAll(entry => entry.CampaignId == campaignId);
|
||||
m_StateStore.CampaignStateById.Remove(campaignId);
|
||||
}
|
||||
|
||||
private void DeleteCharacterLocked(Guid characterId)
|
||||
{
|
||||
if (!m_StateStore.CharactersById.TryGetValue(characterId, out var character))
|
||||
return;
|
||||
|
||||
var campaignId = character.CampaignId;
|
||||
m_StateStore.CharactersById.Remove(characterId);
|
||||
|
||||
var skillGroupIds = m_StateStore.SkillGroupsById.Values
|
||||
.Where(group => group.CharacterId == characterId)
|
||||
.Select(group => group.Id)
|
||||
.ToHashSet();
|
||||
foreach (var skillGroupId in skillGroupIds)
|
||||
m_StateStore.SkillGroupsById.Remove(skillGroupId);
|
||||
|
||||
var skillIds = m_StateStore.SkillsById.Values
|
||||
.Where(skill => skill.CharacterId == characterId)
|
||||
.Select(skill => skill.Id)
|
||||
.ToHashSet();
|
||||
foreach (var skillId in skillIds)
|
||||
m_StateStore.SkillsById.Remove(skillId);
|
||||
|
||||
m_StateStore.RollLog.RemoveAll(entry => entry.CharacterId == characterId || skillIds.Contains(entry.SkillId));
|
||||
|
||||
foreach (var account in m_StateStore.UsersById.Values.Where(account => account.ActiveCharacterId == characterId))
|
||||
account.ActiveCharacterId = null;
|
||||
|
||||
RemoveCharacterStateLocked(campaignId, characterId);
|
||||
TouchRosterLocked(campaignId);
|
||||
}
|
||||
|
||||
private void RemoveCharacterStateLocked(Guid? campaignId, Guid characterId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_StateStore.CampaignStateById.TryGetValue(campaignId.Value, out var state))
|
||||
return;
|
||||
|
||||
state.CharacterVersions.Remove(characterId);
|
||||
}
|
||||
|
||||
private void TouchRosterLocked(Guid? campaignId)
|
||||
{
|
||||
if (!campaignId.HasValue || !m_StateStore.CampaignsById.ContainsKey(campaignId.Value))
|
||||
return;
|
||||
|
||||
var state = GetOrCreateCampaignStateLocked(campaignId.Value);
|
||||
state.TotalVersion += 1;
|
||||
state.RosterVersion += 1;
|
||||
}
|
||||
|
||||
private GameCampaignStateTracker GetOrCreateCampaignStateLocked(Guid campaignId)
|
||||
{
|
||||
if (!m_StateStore.CampaignStateById.TryGetValue(campaignId, out var state))
|
||||
{
|
||||
state = new GameCampaignStateTracker();
|
||||
m_StateStore.CampaignStateById[campaignId] = state;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private readonly GamePersistenceService m_PersistenceService;
|
||||
private readonly GameStateStore m_StateStore;
|
||||
}
|
||||
Reference in New Issue
Block a user