delete_campaign
Permanently remove a campaign from the DM20 Protocol server. This action deletes all associated data and cannot be reversed.
Instructions
Delete a campaign permanently. This cannot be undone.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Campaign name to delete |
Implementation Reference
- src/dm20_protocol/storage.py:574-631 (handler)The `delete_campaign` method within `DnDStorage` class handles the deletion of campaigns by detecting the storage format (monolithic vs split) and removing the corresponding file or directory, and resetting the active campaign state if necessary.
def delete_campaign(self, name: str) -> str: """Delete a campaign from storage. Supports both monolithic (single JSON file) and split (directory) formats. If the deleted campaign is the currently active one, all state is cleared. Args: name: The name of the campaign to delete Returns: The name of the deleted campaign Raises: FileNotFoundError: If the campaign does not exist """ storage_format = self._detect_campaign_format(name) if storage_format == StorageFormat.NOT_FOUND: raise FileNotFoundError(f"Campaign '{name}' not found") safe_name = "".join(c for c in name if c.isalnum() or c in (' ', '-', '_', "'")).rstrip() if storage_format == StorageFormat.MONOLITHIC: file_path = self._get_campaign_file(name) file_path.unlink() logger.info(f"๐๏ธ Deleted monolithic campaign file: {file_path}") elif storage_format == StorageFormat.SPLIT: dir_path = self.data_dir / "campaigns" / safe_name # Safety check: path must be under campaigns directory campaigns_dir = (self.data_dir / "campaigns").resolve() if not dir_path.resolve().is_relative_to(campaigns_dir): raise ValueError(f"Unsafe path detected: {dir_path}") try: shutil.rmtree(dir_path) except OSError: # Retry: macOS race condition with file watchers / .DS_Store import time time.sleep(0.3) shutil.rmtree(dir_path) logger.info(f"๐๏ธ Deleted split campaign directory: {dir_path}") # If deleting the active campaign, clear all state if self._current_campaign and self._current_campaign.name == name: self._current_campaign = None self._current_format = StorageFormat.NOT_FOUND self._character_id_index.clear() self._player_name_index.clear() self._campaign_hash = "" self._rulebook_manager = None self._rules_version = "2024" self._interaction_mode = "classic" self._library_bindings = None self._discovery_tracker = None if hasattr(self, '_split_backend'): self._split_backend._current_campaign = None logger.info(f"๐งน Cleared active campaign state (was: '{name}')") return name