18 KiB
Import & Export Architecture & Plan
This document outlines the modular architecture and step-by-step technical roadmap for handling monster stat block imports, exports, and sharing across multiple formats in the Monster Cards application.
1. Modular Importer Architecture
To keep import/export code clean and maintainable, all format parsers implement a shared interface:
public interface EntityImporter<T> {
/**
* Determines whether the given input string or payload can be handled by this importer.
*/
boolean canImport(@NonNull String input);
/**
* Parses the raw input string into internal domain memory objects (e.g., Monster).
*/
@NonNull
T parse(@NonNull String input) throws Exception;
}
The output of any EntityImporter is an in-memory domain model (such as Monster), which is then passed to MonsterImportFragment for UI preview and Room database persistence.
2. Step-by-Step Implementation Roadmap
┌────────────────----------------────────────────────────┐
│ Step 0: Restrict File Extension Intent Filters │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 1: Refactor Import Code to Shared Interface │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 2: Update Tetra-cube Importer to Newest Format │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 3: Import from D&D Beyond Character URL │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 4: Export to Custom Internal Format (Open5e) │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 5: Import from Custom Internal Format (Open5e) │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 6: Generic Android Share Button Feature │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────----------------────────────────────────┐
│ Step 7: Specific Share Targets (NFC, Bluetooth, URL) │
└────────────────────────────────────────────────────────┘
Step 0: Restrict File Extension Intent Filters
Restrict the Android app so it reacts only to .monster and .monster.txt files instead of all generic .txt files.
AndroidManifest.xml: Addandroid:pathPatternandandroid:pathSuffixconstraints (.monsterand.monster.txt) to the<intent-filter>forMainActivity.MainActivity.java: Implement runtime display name validation queryingOpenableColumns.DISPLAY_NAMEviaContentResolverto filter out non-monster files passed viacontent://URIs.
Step 1: Refactor Import Code into Importer Architecture
Extract the current parsing logic out of MonsterImportHelper.java into a standalone importer class implementing EntityImporter<Monster>.
- New Class:
TetraCubeMonsterImporter implements EntityImporter<Monster> - Responsibilities:
canImport(json): Validates that the JSON string contains Tetra-cube properties (e.g.hitDice,armorName,strPoints).parse(json): Converts the raw JSON payload into an in-memoryMonsterobject.
Step 2: Update Tetra-cube Importer to Newest Format
Update TetraCubeMonsterImporter to support newer fields from the Tetra-cube generator source (js/statblock-script.js):
- New Fields to Parse:
bonusActions: Array of{ name, desc }objects\rightarrowmapped tomonster.actionsor bonus action structure.mythics/isMythic/mythicDescription: Array of mythic action objects and intro text\rightarrowmapped tomonster.legendaryActions.blind: Parse boolean flag to append"(blind beyond this radius)"toblindsight.- Section intro text: Parse
legendariesDescription,lairDescription,regionalDescription.
Step 3: Import from D&D Beyond URL & Web Services
Allow users to import characters, monsters, and future entity types directly from D&D Beyond URLs using a multi-channel ingestion architecture.
-
Primary Target Share Links:
- Site Share Link:
https://www.dndbeyond.com/characters/49074997/s7sLyX(primary link copied when tapping "Share" on D&D Beyond; contains numeric character ID49074997and share tokens7sLyX). - Standard Web Link:
https://www.dndbeyond.com/characters/49074997(HTML page that loads background JavaScript to fetch stats). - URL Extractor Regex:
https?://(?:www\.)?dndbeyond\.com/characters/(\d+)(?:/([a-zA-Z0-9]+))?
- Site Share Link:
-
URL Ingestion Channels:
- Android Share Sheet (
ACTION_SEND):- Register
intent-filterforACTION_SENDwithtext/plaininAndroidManifest.xml. - Tapping "Share" on D&D Beyond passes
https://www.dndbeyond.com/characters/49074997/s7sLyXdirectly into Monster Cards.
- Register
- Android Web Link Chooser (
ACTION_VIEW):- Register
intent-filterforACTION_VIEWinAndroidManifest.xmlwith schemehttps, hostwww.dndbeyond.com, and path prefixes/characters/,/monsters/,/spells/, etc. - Allows Monster Cards to appear in the Android "Open with..." link chooser dialog.
- Register
- In-App Menu & Clipboard Auto-Detection:
- Add an "Import from URL..." menu item in
LibraryFragmentandMonsterImportFragment. - Displays a URL input dialog with automatic clipboard URL detection (
ClipboardManager).
- Add an "Import from URL..." menu item in
- Android Share Sheet (
-
Known Endpoints & Web Asset Structure:
- Main Character Service Endpoint:
https://character-service.dndbeyond.com/character/v5/character/49074997?includeCustomItems=true- Returns JSON payload containing full character statistics, attributes, modifiers, classes, race, inventory, and spells.
- Full reference sample JSON payload extracted to:
docs/dndbeyond-character-sample.json.
- Auxiliary Service Endpoints:
- Vehicles:
https://character-service.dndbeyond.com/character/v5/vehicles?characterId=49074997 - Vehicle Components:
https://character-service.dndbeyond.com/character/v5/vehicle/components?characterId=49074997 - Known Infusions:
https://character-service.dndbeyond.com/character/v5/known-infusions?characterId=49074997 - Infusion Items:
https://character-service.dndbeyond.com/character/v5/infusion/items?characterId=49074997
- Vehicles:
- Image & Avatar Assets:
- Default Builder Avatar:
https://www.dndbeyond.com/Content/Skins/Waterdeep/images/characters/default-avatar-builder.png - Race Portrait Avatar:
portraitAvatarUrl(e.g.,https://www.dndbeyond.com/avatars/2489/881/636680412207671648.jpeg) - Item / Attunement Icons: Returned in
inventory[].definition.avatarUrl(e.g.https://www.dndbeyond.com/avatars/19/144/636382339478303209.jpeg,https://www.dndbeyond.com/avatars/9249/564/637203446409923453.jpeg).
- Default Builder Avatar:
- Analytics / Telemetry:
https://global.ketchcdn.com/web/v2/log...(returns 204 No Content; user consent log endpoint ignored by importer).
- Main Character Service Endpoint:
-
Architecture (
DnDBeyondImporter implements EntityImporter<Monster>):- URL Extractor: Extracts
{characterId}(49074997) from shared URLs (https://www.dndbeyond.com/characters/49074997/s7sLyX), trimming optional share tokens. - HTTP Service Client: Performs an asynchronous HTTP GET request (via OkHttp/Retrofit) to
character-service.dndbeyond.com/character/v5/character/{characterId}?includeCustomItems=true. - Domain Mapper: Maps D&D Beyond JSON fields (stats, modifiers, AC, HP, speed, proficiencies, actions, bonus actions, reactions, spells, traits, avatar URLs) into the internal
Monsterdomain model (sourceUrl = "https://www.dndbeyond.com/characters/49074997/s7sLyX"). - UI Preview: Passes the generated
Monsterobject toMonsterImportFragmentfor review and persistence.
- URL Extractor: Extracts
Step 4: Export to Custom Internal Format (Open5e Specification)
Implement export capability to output monsters and collections using our two-tier Open5e JSON specification (detailed in /Users/tom/Projects/TTRPG/CharacterDataFiles/rulesets/open5e/import-export.md).
- New Class:
Open5eExporter
Open5e Ruleset Architecture & Schema Specification
The Open5e ruleset is part of a modular, versioned ruleset framework located at /Users/tom/Projects/TTRPG/CharacterDataFiles/rulesets/open5e/. In the future, this ruleset will be embedded directly into the application assets and versioned as our canonical model reference.
-
Ruleset & Entity Identifiers:
- Every ruleset defines a short string ID (
ruleset_id: "open5e") and a unique UUID (uuid: "7a35e4d2-f67b-4890-a292-6a7593c72b21"), declared in the ruleset manifestmanifest.json. - Exported entities contain top-level identifiers (
uuid,ruleset_id,entity_type,display_name) wrapped in a universal envelope. - When importing a JSON payload like
examples/character/goblin.json, importers inspectruleset_id == "open5e"to match the registered ruleset parser.
- Every ruleset defines a short string ID (
-
Base Schemas (
/Users/tom/Projects/TTRPG/CharacterDataFiles/schema/*.json):schema/entity.json: The universal envelope schema wrapping all exported entities (uuid,ruleset_id,entity_type,display_name,description,version,tags,template_id,properties).schema/manifest.json: Schema defining ruleset manifest metadata, registered entity types, and sheet templates.schema/entity-definition.json: Meta-schema describing how entity types and property schemas are declared.
-
Character Entity Schema (
entities/character.json):- Defines the
propertiespayload for Player Characters (PCs), NPCs, and Monsters/Creatures. - Core fields in
properties:- Identity & Vitals:
size,type,subtype,alignment,armor_class,armor_description,hit_points(current,max,formula,hit_dice),speed,speed_desc,challenge_rating,cr. - Ability Scores:
abilities(strength,dexterity,constitution,intelligence,wisdom,charisma). - Proficiencies & Senses:
saving_throws,skills,senses,languages. - Actions & Traits:
traits,actions,reactions,legendary_actions,lair_actions,regional_effects, andspellcasting.
- Identity & Vitals:
- Defines the
-
Rendering Templates (
template_id):- The
template_idfield in the universal envelope specifies how the entity should be rendered in external or host applications. - Registered templates in
manifest.json:"stat_block": Monster Manual style stat block (templates/stat_block.html)."character_card": Compact NPC summary card (templates/character_card.html)."character_list_item": Fixed-height quick-reference list item (templates/character_list_item.html)."character_sheet"/"character_sheet_alt": Full 5e interactive character sheets.
- The
Exporter Output Requirements (Open5eExporter)
- Envelope Generation:
- Emits
$schema: "../../../schema/entity.json". - Populates
uuid(random UUID v4),ruleset_id: "open5e",entity_type: "character",display_name: monster.name,template_id: "stat_block".
- Emits
- Payload Mapping:
- Maps
Monsterfields to Open5e character properties inpropertiesobject conforming toentities/character.json.
- Maps
Step 5: Import from Custom Internal Format (Open5e Specification)
Implement import capability for custom Open5e JSON files.
- New Class:
Open5eImporter implements EntityImporter<Monster> - Pipeline:
canImport(json): Validates"ruleset_id": "open5e"and"entity_type": "character".parse(json): Unpackspropertiesinto internalMonsterdomain models.
Step 6: Generic Android Share Button Feature
Implement a generic "Share" feature allowing users to export and share monsters or collections from detail screens.
- UI Action: Add a "Share" item to action menus in
MonsterDetailFragmentandCollectionDetailFragment. - Android Intent: Uses standard Android
ACTION_SENDintent withIntent.EXTRA_STREAMorIntent.EXTRA_TEXTto pass exported files to other apps (e.g. Email, Drive, Messaging, Files).
Step 7: Specific Share Targets & Channels
Extend the generic sharing feature with specialized, direct sharing channels (to be implemented as individual sub-steps):
- Sub-Step 7.1 (NFC Sharing): Share monster data directly between devices via NFC (NDEF records / Android Beam).
- Sub-Step 7.2 (Bluetooth / Wi-Fi Direct): Share monster files directly between nearby Android devices via Bluetooth / Wi-Fi.
- Sub-Step 7.3 (Web URL with Embedded Payload): Generate a shareable Web URL containing compressed/base64-encoded monster JSON data.
3. Implementation Checklist & Status
- Step 0: Restrict app intent filters in
AndroidManifest.xml(.monster&.monster.txt) and add runtime filename validation inMainActivity.java. - Step 1: Refactor import & conversion code into a shared
EntityImporter<T>interface andTetraCubeMonsterImporterclass. - Step 2: Update Tetra-cube importer class to support the newest Tetra-cube format (
bonusActions,mythics,blind, intro descriptions). - Step 3: Import from D&D Beyond URL (
https://www.dndbeyond.com/characters/49074997fetching from character service endpointcharacter/v2/character/49074997). - Step 4: Export to internal format described by Open5e document (
Open5eExporter). - Step 5: Import from internal format described by Open5e document (
Open5eImporter). - Step 6: Generic Android Share button feature (
ACTION_SEND). - Step 7: Specific share targets (7.1 NFC, 7.2 Bluetooth, 7.3 Embedded Web URL).
4. Schema Planning & Text Formatting Notes
4.1 Schema Architecture Considerations
- Unified Action Entity / Type Column:
- Instead of maintaining separate
List<Trait>columns for each category (actions,reactions,legendaryActions,lairActions,regionalActions, etc.), a unifiedmonster_actionstable (or model list) with anaction_typecolumn (ABILITY,ACTION,BONUS_ACTION,REACTION,LEGENDARY_ACTION,MYTHIC_ACTION,LAIR_ACTION,REGIONAL_EFFECT) simplifies Room DB queries and enables a single reusable editor UI component.
- Instead of maintaining separate
- Section Intro & End Note Metadata:
- Store section-level intro text and end notes (e.g.
legendaryActionsDescription,lairActionsDescription,lairActionsEndNote,regionalActionsDescription,regionalActionsEndNote,mythicActionsDescription) as dedicated metadata fields on theMonsterentity rather than as artificial action traits.
- Store section-level intro text and end notes (e.g.
4.2 Markdown & Content Formatting Support
- Supported Markdown Syntax:
- Emphasis: Italics (
_text_or*text*) and Bold (__text__or**text**). - Lists: Bulleted lists (
-,*) and Numbered lists (1.,2.). - External Links: Standard markdown links
[Text](https://...). - Internal Links (Future Deep Linking): Custom URI scheme
[Label](mc://<type>/<id>)(e.g.,[Fireball](mc://spell/fireball)) reserved for in-app navigation.
- Emphasis: Italics (
- Data Model & Import/Export Handling:
- All text fields across domain models, Room persistence, and import/export payloads store raw CommonMark markdown strings.
- Importers preserve raw markdown syntax as exported by source formats (Tetra-cube, Open5e, D&D Beyond).
- UI rendering layers handle conversion from CommonMark to Android Spanned text at display time.