EternalTD Content Packages
EternalTD tracks official content through a content_packages config set and exposes those packages through /etd setup.
Package Categories
The current code uses three package categories:
FREEPREMIUMRESOURCES
The setup menu currently exposes those as:
Free ContentPremium PacksResources
Verified Premade Packages
These are the premade packages currently defined in the repo:
| Package | Category | Toggleable in /etd setup | Tracked assets |
|---|---|---|---|
EternalTD Hub World | Free | No | Server world-container folder etd_spawn |
EternalTD Tutorial | Free | Yes | tutorial.yml, tutorial_waves.yml, tutorial_npc.yml, etd_tutorial |
Towers of the Grassy Lands | Free | Yes | 5 level files, 1 waves file, 1 NPC file, 5 worlds |
Towers from the Depths | Premium | Yes | 5 level files, 1 waves file, 1 NPC file, 5 worlds |
Towers of the Rising Sun | Premium | Yes | 5 level files, 1 waves file, 1 NPC file, 5 worlds |
EternalTD Custom Models | Resources | No | Marker file only, plus imported FreeMinecraftModels content when present |
Package Lifecycle
The package browser currently distinguishes between:
- not downloaded
- not installed
- installed
- partially installed
- out of date and updatable
- out of date but inaccessible
- needs access
That state is computed from tracked assets on disk, isEnabled flags inside tracked config files, and Nightbreak access/update checks.
Two detection rules matter when a package looks stuck on "partially installed":
- World-container folders are matched at both layouts. Paper 26.1 and newer migrate plugin worlds into
<level-name>/dimensions/minecraft/<world>. EternalTD counts a world-container folder as present if it exists at either the legacy root or the migrated location, and it deliberately checks for the folder, not forlevel.dat— requiringlevel.datwould flip the package to "not installed" during a partial migration and trigger a re-install that Paper then refuses to merge. - The model package has a content fallback. A non-empty
FreeMinecraftModels/models/EternalTD default towersor legacyFreeMinecraftModels/Models/EternalTD default towersfolder underplugins/counts as present. EternalTD then tries to write the marker; a write failure is logged. This folder list is defined in code, not the package YAML.
These are presence and enablement checks, not a playable-content check. A marker can remain after models are removed, and detecting a world folder does not validate its contents. Missing tracked assets take precedence as partially installed; with all assets present, a mix of enabled and disabled tracked configs also gives that state.
Toggleable vs Shared Packages
Toggleable packages work by flipping isEnabled inside tracked config files under:
plugins/EternalTD/levels/
plugins/EternalTD/waves/
plugins/EternalTD/npcs/
The package's own isEnabled flag is saved too, followed by an EternalTD restart. Disabling keeps the files on disk. The installed state is determined by the tracked files, not just the package flag. The toggle reads every tracked file before writing any of them; if one cannot be read, no file is changed. Likewise, if /etd setup cannot read a tracked file while refreshing package states, the menu does not open and you are told to check the console.
Shared packages do not behave like normal toggles:
- The hub world is treated as shared installed content.
- The custom model pack is treated as shared resource content.
For those packages, the setup menu tells you to remove files manually and then run /etd reload.
Bulk Operations
The bulk content operations currently implemented are:
/etd downloadall— downloads the EternalTD plugin update and all available content/etd downloadallcontent— downloads all available content (no plugin update)/etd updatecontent(alias/etd updateallcontent) — downloads only outdated content
The content download/update commands:
- require a linked Nightbreak token
- download into
plugins/EternalTD/imports - select missing or outdated packages for downloads; updates select outdated packages and skip cached access denials
- reload EternalTD automatically after successful downloads
For normal downloads, cached access is advisory: the authenticated download request makes the current access decision. Packages already downloaded and current are skipped, even if their configs are disabled. Use their setup toggle to enable them. /etd forcereinstallcontent <package|all> repairs missing or damaged files even at the current version; all selects detected downloads, while a named Nightbreak slug can target a wholly missing package.
/etd downloadall additionally checks for and downloads a plugin update first; if no token is linked or authentication fails it stops before the content step.
Practical Notes
- Package checks deduplicate by Nightbreak slug before bulk download/update.
- The setup UI requests content and access refreshes on a five-minute cooldown instead of hitting Nightbreak on every open.
- The custom models package writes a marker under
plugins/EternalTD/content_markers/so EternalTD can track that the shared resource package was downloaded. - Tracked level, wave, and NPC files are matched by filename anywhere beneath
levels/,waves/, andnpcs/, so packages that ship their files in a subfolder are still detected. Towers of the Rising Sunpreviously tracked its wave file astowers_of_the_rising_sun.ymlwhile the pack actually shipstowers_of_the_rising_sun_waves.yml, which left it permanently reading as not fully installed. The tracked name is now corrected automatically and written back to the package config on load — no manual fix needed.