zio fork 1.8.2b-zio11: reloading admins removes deleted and demoted admins
CSS's admin loaders only add, and the per-admin cleanup in CreateAdminsJsonFile reset AdminCache once per admin, so it only ever tracked the last one and a reload never took anyone else out of memory. ReloadAdmins now removes every admin it loaded last time and loads the new set in one tick. admins.json is keyed by SteamID instead of name, so admins sharing a name aren't merged, and a failed admins query keeps the loaded admins instead of removing them.
This commit is contained in:
parent
f46ccf18ce
commit
1b97632211
3 changed files with 63 additions and 103 deletions
|
|
@ -22,7 +22,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
||||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||||
public override string ModuleAuthor => "daffyy";
|
public override string ModuleAuthor => "daffyy";
|
||||||
public override string ModuleVersion => "1.8.2b-zio10";
|
public override string ModuleVersion => "1.8.2b-zio11";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -507,24 +507,29 @@ public partial class CS2_SimpleAdmin
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await PermissionManager.CreateGroupsJsonFile();
|
await PermissionManager.CreateGroupsJsonFile();
|
||||||
await PermissionManager.CreateAdminsJsonFile();
|
var admins = await PermissionManager.CreateAdminsJsonFile();
|
||||||
|
if (admins == null) return;
|
||||||
var adminsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/admins.json");
|
|
||||||
var groupsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/groups.json");
|
|
||||||
|
|
||||||
await Server.NextWorldUpdateAsync(() =>
|
await Server.NextWorldUpdateAsync(() =>
|
||||||
{
|
{
|
||||||
AddTimer(1, () =>
|
// zio: CSS's loaders only ever add, so first remove everyone loaded last time (removed or
|
||||||
{
|
// demoted admins kept their old rank until a restart otherwise), then load the new set,
|
||||||
if (!string.IsNullOrEmpty(adminsFile))
|
// all in one tick so nobody is left without their flags in between. Groups can't be
|
||||||
AddTimer(2.0f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json"));
|
// removed from CSS, so flags taken off a group still need a restart.
|
||||||
if (!string.IsNullOrEmpty(groupsFile))
|
foreach (var steamId in PermissionManager.AdminCache.Keys.ToList())
|
||||||
AddTimer(3.0f, () => AdminManager.LoadAdminGroups(ModuleDirectory + "/data/groups.json"));
|
AdminManager.RemovePlayerAdminData(steamId);
|
||||||
if (!string.IsNullOrEmpty(adminsFile))
|
PermissionManager.AdminCache.Clear();
|
||||||
AddTimer(4.0f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json"));
|
|
||||||
|
|
||||||
_logger?.LogInformation("Loaded admins!");
|
foreach (var admin in admins.GroupBy(a => a.SteamId))
|
||||||
});
|
{
|
||||||
|
var ends = admin.Any(a => a.Ends == null) ? null : admin.Max(a => a.Ends);
|
||||||
|
PermissionManager.AdminCache[admin.Key] = (ends, admin.SelectMany(a => a.Flags).Distinct().ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json");
|
||||||
|
AdminManager.LoadAdminGroups(ModuleDirectory + "/data/groups.json");
|
||||||
|
|
||||||
|
_logger?.LogInformation("Loaded {count} admins!", PermissionManager.AdminCache.Count);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all players' flags and associated data asynchronously.
|
/// Retrieves all players' flags and associated data asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A list of tuples containing player SteamID, name, flags, immunity, and expiration time.</returns>
|
/// <returns>A list of tuples containing player SteamID, name, flags, immunity, and expiration time, or null if the database query failed.</returns>
|
||||||
private async Task<List<(ulong, string ,List<string>, int, DateTime?)>> GetAllPlayersFlags()
|
private async Task<List<(ulong, string ,List<string>, int, DateTime?)>?> GetAllPlayersFlags()
|
||||||
{
|
{
|
||||||
if (databaseProvider == null)
|
if (databaseProvider == null)
|
||||||
return new List<(ulong, string, List<string>, int, DateTime?)>();
|
return new List<(ulong, string, List<string>, int, DateTime?)>();
|
||||||
|
|
@ -68,7 +68,8 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message);
|
CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message);
|
||||||
return [];
|
// zio: null rather than empty, so ReloadAdmins keeps the loaded admins instead of removing them all.
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,85 +151,37 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a JSON file containing admins data asynchronously.
|
/// Creates a JSON file containing admins data asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>zio: every admin row loaded, for ReloadAdmins to rebuild AdminCache from, or null if the database query failed (the file is then left alone).</returns>
|
||||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||||
public async Task CreateAdminsJsonFile()
|
public async Task<List<(SteamID SteamId, DateTime? Ends, List<string> Flags)>?> CreateAdminsJsonFile()
|
||||||
{
|
{
|
||||||
List<(ulong identity, string name, List<string> flags, int immunity, DateTime? ends)> allPlayers = await GetAllPlayersFlags();
|
var allPlayers = await GetAllPlayersFlags();
|
||||||
|
if (allPlayers == null) return null;
|
||||||
|
|
||||||
var validPlayers = allPlayers
|
var validPlayers = allPlayers
|
||||||
.Where(player => SteamID.TryParse(player.identity.ToString(), out _))
|
.Select(player => (player, steamId: SteamID.TryParse(player.Item1.ToString(), out var steamId) ? steamId : null))
|
||||||
|
.Where(p => p.steamId != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// zio: keyed by SteamID, not name: CSS ignores the key, and two admins with the same name were
|
||||||
|
// merged into one entry under the first one's SteamID. Upstream also cleared and refilled
|
||||||
|
// AdminCache here once per admin, so it only ever kept the last admin and a reload never
|
||||||
|
// removed anyone else; ReloadAdmins now does that once, from the list returned here.
|
||||||
var jsonData = validPlayers
|
var jsonData = validPlayers
|
||||||
.GroupBy(player => player.name)
|
.GroupBy(p => p.steamId!.SteamId64)
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
group => group.Key,
|
group => group.Key.ToString(),
|
||||||
object (group) =>
|
object (group) =>
|
||||||
{
|
{
|
||||||
var consolidatedData = group.Aggregate(
|
var rows = group.Select(p => p.player).ToList();
|
||||||
new
|
var flags = rows.SelectMany(r => r.Item3).Distinct().ToList();
|
||||||
|
return new
|
||||||
{
|
{
|
||||||
identity = string.Empty,
|
identity = group.Key.ToString(),
|
||||||
immunity = 0,
|
immunity = rows.Max(r => r.Item4),
|
||||||
flags = new List<string>(),
|
flags = flags.Where(flag => flag.StartsWith($"@")).ToList(),
|
||||||
groups = new List<string>()
|
groups = flags.Where(flag => flag.StartsWith($"#")).ToList()
|
||||||
},
|
|
||||||
(acc, player) =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(acc.identity) && !string.IsNullOrEmpty(player.identity.ToString()))
|
|
||||||
{
|
|
||||||
acc = acc with { identity = player.identity.ToString() };
|
|
||||||
}
|
|
||||||
|
|
||||||
acc = acc with { immunity = Math.Max(acc.immunity, player.immunity) };
|
|
||||||
|
|
||||||
acc = acc with
|
|
||||||
{
|
|
||||||
flags = acc.flags.Concat(player.flags.Where(flag => flag.StartsWith($"@"))).Distinct().ToList(),
|
|
||||||
groups = acc.groups.Concat(player.flags.Where(flag => flag.StartsWith($"#"))).Distinct().ToList()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return acc;
|
|
||||||
});
|
|
||||||
|
|
||||||
Server.NextWorldUpdate(() =>
|
|
||||||
{
|
|
||||||
var keysToRemove = new List<SteamID>();
|
|
||||||
|
|
||||||
foreach (var steamId in AdminCache.Keys.ToList())
|
|
||||||
{
|
|
||||||
var data = AdminManager.GetPlayerAdminData(steamId);
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
var flagsArray = AdminCache[steamId].Flags.ToArray();
|
|
||||||
AdminManager.RemovePlayerPermissions(steamId, flagsArray);
|
|
||||||
AdminManager.RemovePlayerFromGroup(steamId, true, flagsArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
keysToRemove.Add(steamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var steamId in keysToRemove)
|
|
||||||
{
|
|
||||||
if (!AdminCache.TryRemove(steamId, out _)) continue;
|
|
||||||
|
|
||||||
var data = AdminManager.GetPlayerAdminData(steamId);
|
|
||||||
if (data == null) continue;
|
|
||||||
if (data.Flags.Count != 0 && data.Groups.Count != 0) continue;
|
|
||||||
|
|
||||||
AdminManager.ClearPlayerPermissions(steamId);
|
|
||||||
AdminManager.RemovePlayerAdminData(steamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var player in group)
|
|
||||||
{
|
|
||||||
if (SteamID.TryParse(player.identity.ToString(), out var steamId) && steamId != null)
|
|
||||||
{
|
|
||||||
AdminCache.TryAdd(steamId, (player.ends, player.flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return consolidatedData;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var options = new JsonSerializerOptions
|
var options = new JsonSerializerOptions
|
||||||
|
|
@ -240,6 +193,8 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
||||||
var json = JsonSerializer.Serialize(jsonData, options);
|
var json = JsonSerializer.Serialize(jsonData, options);
|
||||||
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "admins.json");
|
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "admins.json");
|
||||||
await File.WriteAllTextAsync(filePath, json);
|
await File.WriteAllTextAsync(filePath, json);
|
||||||
|
|
||||||
|
return validPlayers.Select(p => (p.steamId!, p.player.Item5, p.player.Item3)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue