12 KiB
12 KiB
RpgRoller
Fresh full-stack starter scaffold:
RpgRoller/: ASP.NET Core backend + Blazor frontend host (Components+wwwroot)RpgRoller.Tests/: xUnit integration-heavy test projectRpgRoller.sln: solution used by local CI script
Test layout:
RpgRoller.Tests/Api/: API integration tests grouped by feature concernRpgRoller.Tests/Services/: service-level tests grouped by domain concernRpgRoller.Tests/Support/: shared test harnesses/builders/helpers
Code Organization
Backend:
RpgRoller/Program.cs: thin app bootstrap onlyRpgRoller/Hosting/: service registration + startup initializationRpgRoller/Api/: endpoint mapping modules and auth/session filter helpersRpgRoller/Services/: game workflows with explicit method parameters (no API DTO dependencies)RpgRoller/Services/SkillDefinitionValidator.cs,RoleSerializer.cs,RollVisibilityParser.cs, andCustomRollOptionsResolver.cs: extracted pure backend rule helpers used byGameServiceRpgRoller/Services/GameStateStore.cs,GameStateCloneFactory.cs, andGamePersistenceService.cs: extracted runtime-state ownership and SQLite load/save boundaries used byGameServiceRpgRoller/Services/GameAuthService.cs: extracted auth/session workflow ownership whileGameServicestays on the existingIGameServicecontractRpgRoller/Services/GameCampaignService.cs: extracted campaign creation, visibility reads, roster reads, and deletion workflows behind the same facade contractRpgRoller/Services/GameCharacterService.cs: extracted character creation, transfer, activation, deletion, and owner-scoped listing workflows behind the same facade contractRpgRoller/Services/GameSkillService.cs: extracted skill-group CRUD, skill CRUD, sheet shaping, and ruleset-specific validation orchestration behind the same facade contractRpgRoller/Services/GameRollService.cs: extracted roll execution, log shaping, roll detail visibility, and campaign-state snapshot reads behind the same facade contractRpgRoller/Services/GameUserAdministrationService.cs: extracted username reads, admin user listings, role updates, and account deletion workflows behind the same facade contract
Frontend:
RpgRoller/Components/: Blazor root app, routes, layout and page componentsRpgRoller/Components/Pages/Home.razor: minimal gateway page (loading/auth/workspace switch)RpgRoller/Components/Pages/Home.razor.cs: singleHomecode-behind with only gateway/session-view orchestrationRpgRoller/Components/Pages/Workspace.razor: authenticated workspace UI and workspace-specific state/logicRpgRoller/Components/Pages/WorkspaceState.cs,WorkspaceToast.cs,WorkspaceFeedbackService.cs,WorkspaceSessionCoordinator.cs,WorkspaceAdminCoordinator.cs,WorkspaceCampaignCoordinator.cs,WorkspacePlayCoordinator.cs, andWorkspaceLiveStateController.cs: extracted workspace UI state, toast records, feedback/status handling, session/bootstrap orchestration, admin user actions, campaign-management/modal flows, play/log coordination, and live-state reconciliation whileWorkspaceremains the behavior ownerRpgRoller/Components/**/*.razor.cs: component code-behind classes (state, handlers, parameters, injected dependencies);.razorfiles remain markup-focusedRpgRoller/Components/Pages/Home.Models.cs: reusableFormState<TModel>+ page form modelsRpgRoller/Components/Pages/HomeControls/: auth, campaign management, play-screen, and modal controls extracted fromHome.razor- Form ownership model: controls own transient form/error state and execute their concern-specific API mutations directly
- Skill create/edit workflow ownership:
CharacterPanel(characters own skills in UI and behavior) RpgRoller/Components/RpgRollerApiClient.cs: shared browser API client used byHome,Workspace, and leaf controlsRpgRoller/wwwroot/js/rpgroller-api.js: browser-side API + SSE + session storage interop for BlazorRpgRoller/wwwroot/styles.css: responsive UX styling and theme tokens
Backend state persistence:
- EF Core with SQLite (
Microsoft.EntityFrameworkCore.Sqlite) - Development DB:
RpgRoller/App_Data/rpgroller.development.db - Default DB:
RpgRoller/App_Data/rpgroller.db - Database schema is created/upgraded automatically on startup via EF Core migrations (
Database.Migrate) - Runtime state is loaded once at startup into memory and written back to SQLite on successful state changes
Gameplay capabilities now include:
- Instant skill filtering in the character panel (filters live while typing and hides non-matching skills/groups)
- Supported campaign rulesets include D6 System, D&D 5e, and Rolemaster
- Skill groups per character with skill prototypes (create/edit/delete groups, assign/reassign skills, and prefill new skill forms from group defaults)
- Skill and skill-group deletion flows
- GM-driven character owner transfer within campaign management flows
- Character owner selection in edit modal backed by existing-username dropdown data
- Role-aware authorization with admin role support (including admin user/role management)
- Admin workspace tools include direct download of the live SQLite database file
- Campaign deletion by campaign owner or admin (unlinks characters from the campaign and clears campaign log entries)
- User deletion by admin also deletes campaigns owned by that user and unlinks all characters from those deleted campaigns
- Play screen visibility is owner-scoped: only owned characters are listed, and private log entries are visible only to the roller
- Campaign management owner labels use account display names (no GUID fallback rendering)
- Character edit flow supports unlinking from campaigns (owner/GM/admin) and assigning to any existing campaign via expanded campaign options
- Campaign management supports character deletion by character owner or admin
- Shared top header control across all authenticated workspace screens (play, campaign management, admin)
- Admin user management is integrated into workspace screen toggles (
Play,Campaign Management,Admin) - Rolemaster expression validation now accepts generic standard expressions such as
d10,2d10+48,15d10, andd100-15;d100!+85remains the special open-ended percentile form - Rolemaster open-ended percentile skills and skill-group defaults now persist a nullable
FumbleRangefield, while D6 and D&D rows migrate forward unchanged - Rolemaster create/edit forms now keep the expression authoritative, show generic Rolemaster syntax help, and reveal
FumbleRangeonly when the expression is an open-ended percentile roll - Rolemaster roll execution now supports generic standard Rolemaster rolls (
NdS+x, with implicit count1fordS) plus open-ended percentile (d100!+x) with recursive high-end chaining and low-end subtraction based onFumbleRange; low-end trigger rolls are shown for auditability but do not count toward the total - Compact campaign-log summaries stay dense for Rolemaster rolls, while lazy-loaded roll detail includes ordered die metadata for each open-ended follow-up step
- Play screen campaign logs now include a detached bottom custom-roll panel below the scrollable log feed; it records arbitrary expressions against the selected character without creating a skill, and invalid expressions stay inline on the field with tooltip/error styling instead of using error toasts
- Startup migration coverage is validated against a copied temp-file instance of
RpgRoller/App_Data/rpgroller.development.dbbefore feature work is considered complete
Prerequisites
- .NET SDK 10.0+
- PowerShell 7+
- Node.js 22+
- Run
dotnet tool restoreonce to enable the repo-local dotnet commands. - Run
npm cionce to install the repo-local Playwright toolchain. - Run
npm exec playwright install chromiumonce to install the browser used by local smoke tests.
Local Development
- Run the local CI parity script:
pwsh ./scripts/ci-local.ps1 - Start the backend:
dotnet run --project RpgRoller/RpgRoller.csproj - Open
http://localhost:5000(or the port shown in the console).
Playwright helpers:
- Install/update browser dependencies:
npm exec playwright install chromium - Run the checked-in smoke test against an isolated temp SQLite database:
pwsh ./scripts/run-playwright.ps1 - Run the Playwright suite directly when the app is already running:
npm run e2e
VS Code F5 debug profiles are available in .vscode/launch.json:
RpgRoller: ServerRpgRoller: Server + Edge (F5)RpgRoller: Server + Firefox (F5)
To use a custom SQLite database path, set ConnectionStrings__RpgRoller.
To run under a subfolder (for example /rpgroller), set PathBase (for example PathBase=/rpgroller).
For migration authoring, use the local tool command form:
dotnet dotnet-ef migrations add <MigrationName> --project RpgRoller/RpgRoller.csproj --startup-project RpgRoller/RpgRoller.csproj
For SQLite migrations, keep table-rebuild operations isolated from unrelated schema/data changes. If a migration needs both a rebuild-triggering change (for example AlterColumn) and another independent operation, split them into separate migrations so EF Core does not emit non-transactional migration warnings.
Frontend Runtime
- Runtime frontend is Blazor Server with interactive components.
- Browser interop is in
RpgRoller/wwwroot/js/rpgroller-api.js. - Root static assets such as
styles.cssare linked through Blazor's@Assets[...]pipeline so deploys get fingerprinted cache-busting URLs automatically. - Workspace reads are resolved server-side through scoped query services; browser interop remains for browser-only concerns such as session storage, SSE wiring, and DOM helpers.
- Live workspace refreshes now compare separate roster, per-character sheet, and log versions so unrelated state changes do not force a full roster + sheet + log reload.
- Workspace campaign data is loaded in bounded slices: visible campaign summaries, a selected campaign roster, a selected character sheet, and a 25-row incremental log window backed by
/api/campaigns/{campaignId}/log/page. - Campaign log rows now ship compact summary data first, including structured special-event badges for wild dice spikes, natural 1/20 results, and Rolemaster rare/fumble triggers, and lazy-load dice + breakdown detail through
/api/rolls/{rollId}only when a row is expanded. - Newly appended campaign-log rolls auto-expand in the play workspace, with the roll response reused as the initial detail payload for the local roller to avoid an extra detail fetch.
- The campaign log footer supports custom roll submission for the currently selected character; D6 custom rolls use the baseline one-wild-die/fumble behavior, while D&D 5e and Rolemaster rely only on the submitted expression.
- Hot API contracts share a source-generated
System.Text.Jsoncontext, and HTTP JSON responses are gzip-compressed when the client advertises support. - OpenAPI contract source remains at
openapi/RpgRoller.json.
Test and Coverage
- Tests:
dotnet test RpgRoller.Tests/RpgRoller.Tests.csproj --collect:"XPlat Code Coverage" --settings RpgRoller.Tests/coverlet.runsettings - Regression tests enforce payload budgets for the hottest contracts: character sheet reads, initial log page loads, incremental log updates, roll mutation responses, and lazy-loaded Rolemaster roll detail payloads.
- Coverage gate:
pwsh ./scripts/check-coverage.ps1 -MinLineRate 0.90 -MinBranchRate 0.70 - Coverage collector scope:
RpgRoller.Tests/coverlet.runsettingsnow measures the full backend assembly (RpgRoller), not only service namespace files.