feat: add 3D Charades word lists management and profanity filter toggle

- Implemented Charades word lists management in the admin UI, allowing for unlimited named word lists with difficulty settings.
- Added functionality to bind live card slots to specific word lists, with automatic fallback to built-in lists.
- Introduced a profanity filter toggle in server settings, enabling or disabling the server-side profanity filter.
- Updated database schema to support new features, including additional columns for profanity filter status and charades slot bindings.
- Enhanced documentation to reflect new features and their usage.
This commit is contained in:
Alexa
2026-07-02 00:54:04 +03:00
parent fbfabaf26e
commit e002b88d71
19 changed files with 1516 additions and 22 deletions
+1
View File
@@ -60,6 +60,7 @@ public class DorkNetDbContext(DbContextOptions<DorkNetDbContext> options) : DbCo
public DbSet<GameSessionEntity> GameSessions => Set<GameSessionEntity>();
public DbSet<CommunityBoardEntity> CommunityBoardRows => Set<CommunityBoardEntity>();
public DbSet<LoadingScreenTipEntity> LoadingScreenTips => Set<LoadingScreenTipEntity>();
public DbSet<CharadesWordListEntity> CharadesWordLists => Set<CharadesWordListEntity>();
public DbSet<ServerSettingsEntity> ServerSettings => Set<ServerSettingsEntity>();
public DbSet<RoomKeyEntity> RoomKeys => Set<RoomKeyEntity>();
public DbSet<RoomKeyPurchaseEntity> RoomKeyPurchases => Set<RoomKeyPurchaseEntity>();
@@ -0,0 +1,46 @@
namespace DorkNet.Server.Data.Entities;
/// <summary>
/// One admin-authored 3D Charades word list. The March 2023 client
/// pulls a deck at card-box spawn time from
/// <c>api/activities/charades/v1/words/{source}</c>, where
/// <c>{source}</c> is one of three baked <c>CardBox.cardSource</c>
/// slots — <c>Charades</c>, <c>CharadesAprilFoolsDay</c>, or
/// <c>Icebreakers</c> (verified in the 2023.03.21 il2cpp dump:
/// <c>GEFMIBEPMKJ.EODJJDILECA</c>). The game itself only ever requests
/// those three ids.
///
/// To let admins keep an unlimited library and freely switch which list
/// each slot serves, every list is a row here (name + words), and the
/// live slot→list binding lives in
/// <see cref="ServerSettingsEntity.CharadesSlotBindingsJson"/>. This is
/// the same admin-editable-collection shape as
/// <see cref="LoadingScreenTipEntity"/>: a plain entity CRUD'd from the
/// SPA, created on existing DBs via the idempotent bootstrap (it
/// post-dates the consolidated Initial migration).
/// </summary>
public class CharadesWordListEntity
{
public long Id { get; set; }
/// <summary>Admin-facing display name, e.g. "Default", "Movie night",
/// "April Fools". Not sent to the client.</summary>
public string Name { get; set; } = string.Empty;
/// <summary>Serialized <c>List&lt;CharadesWord&gt;</c> — each entry is
/// <c>{ Text, Difficulty }</c>. <c>Text</c> becomes the wire
/// <c>EN_US</c> field; <c>Difficulty</c> is the client
/// <c>CNMMMNJJDMM</c> enum (0 easy, 1 hard, 10 very hard, 20
/// icebreaker). Stored as JSON so a list can grow to hundreds of
/// cards without a child table.</summary>
public string WordsJson { get; set; } = "[]";
/// <summary>True for the lists seeded on first boot (Default /
/// April Fools / Icebreakers). Purely informational — admins may
/// still rename, edit, or delete them; it only gates the one-time
/// seed so we don't duplicate them every startup.</summary>
public bool IsBuiltIn { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
@@ -64,5 +64,23 @@ public class ServerSettingsEntity
/// migration each time.</summary>
public string DiscoveredGameConfigsJson { get; set; } = string.Empty;
/// <summary>When true, the server-side profanity filter behind
/// <c>api/sanitize/*</c> is bypassed — every string is treated as
/// clean and returned unmodified. Built for private friend servers
/// that don't want room/invention names or chat censored. Off by
/// default (filter active). See <see cref="Services.ProfanityFilter"/>
/// and <c>SanitizeController</c>.</summary>
public bool ProfanityFilterDisabled { get; set; }
/// <summary>Which charades word list (row id in
/// <see cref="CharadesWordListEntity"/>) is live for each of the
/// client's three baked card-source slots. JSON object keyed by the
/// client enum name — <c>{"Charades":id,"CharadesAprilFoolsDay":id,
/// "Icebreakers":id}</c>. Empty/0 or a dangling id falls back to the
/// list the seeder created for that slot. Lets admins keep an
/// unlimited library and switch which list a slot serves without a
/// redeploy.</summary>
public string CharadesSlotBindingsJson { get; set; } = string.Empty;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}