EternalTD Levels & Maps
A "level" in EternalTD is a YAML config in plugins/EternalTD/levels/ paired with a template world folder in plugins/EternalTD/worlds/. When a player joins, EternalTD clones the template world into the server world container and runs the session in that cloned copy.
Level Config Fields
| Field | Type | Default | Notes |
|---|---|---|---|
isEnabled | bool | true | Disabled levels are skipped during load |
levelName | string | null | Display name shown in messages, scoreboards, and NPC menus |
levelDescription | string list | [] | Lines shown in NPC menus. Supports $highscoreWave and $highscorePlayer placeholders |
worldName | string | null | The template folder name under plugins/EternalTD/worlds/ |
startLocation | string list | null | List of serialized locations where mobs spawn |
endLocation | string list | null | List of serialized locations the mobs walk to (the "red" tiles) |
levelLocations | string list | null | Every walkable grid square in the level, generated automatically when you register a floor selection |
wavesConfigFile | string | required | Filename of the linked wave config (waves/<name>.yml) |
waveCount | int | -1 | Cached wave count, currently informational |
highscoreWave | int | 0 | Best wave reached on this level |
highscorePlayerName | string | "no one" | Display name of the player who set the score |
environment | enum | NORMAL | World environment used when the cloned world is loaded |
The grid size used throughout is 3 blocks per logical square (constant GRID_SIZE in code).
Location String Format
Locations in EternalTD are serialized as comma-separated strings of the form:
worldName,x,y,z,yaw,pitch
You normally do not write these by hand — /etd selectfloor plus the register commands compute and save them for you.
World Lifecycle
When a player joins a level:
- EternalTD looks up the template folder by
worldNameinplugins/EternalTD/worlds/. - It picks the next free numeric suffix (
<worldName>_0,<worldName>_1, ...) and writes the clone into the server world container, cleaning up any stale legacy or modern layout copies first. - The cloned world is loaded as a temporary void world through MagmaCore's
TemporaryWorldManager, so Paper 26.1+ migration is quarantined. - The player is teleported into the new world; an internal
InstanceProtectorapplies EternalTD's protection rules.
When the session ends:
- Any remaining players in the cloned world are teleported back to the spawn location from
config.yml, or kicked if no spawn is configured. - The cloned world is unloaded and deleted from disk (
TemporaryWorldManager.permanentlyDeleteWorld).
Instance Protection Rules
While a level is active, the cloned world has these rules applied:
- Explosions disabled
- Liquid flow disabled
- Elytra disabled
- Fly toggling prevented
- Friendly fire prevented
- Vanilla mob spawning prevented
Map Authoring Workflow
The current map authoring flow uses the in-game tooling:
- Place a template world folder under
plugins/EternalTD/worlds/<worldName>/. - Create or download a matching level YAML in
plugins/EternalTD/levels/. - Run
/etd reloadand join the level world manually (or open it in single-player to set up). - Use
/etd selectfloorand right-/left-click two corners to mark the play area, or use/etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2>to supply them directly. - Run
/etd showselection <level>to confirm the selection looks right. - Run
/etd register <level>to clear the selection. Note that in the current build neitherregisternorshowselectionactually persist the floor region — the helper that would savelevelLocations(LevelsConfigFields#addLevelLocations) is defined but never invoked by a command. You currently have to writelevelLocationsinto the level YAML by hand if it isn't already populated by a downloaded package. - Stand on a start spawn tile and run
/etd register <level> start. Repeat for every start tile (this command does persist intostartLocation). - Stand on an end tile and run
/etd register <level> end. Repeat for every end tile (this command does persist intoendLocation). - Reload again and test the level by joining it through the NPC menu or
/etd join <level>.
The selection commands generate grid squares using:
size = abs(corner1 - corner2 + 1) / 3
Squares whose top block is air (or whose floor block is passable) are skipped, so floor blocks must be solid for a square to register as playable.
Path Validation
EternalTD runs an A* pathfinding check whenever a tower is placed. If placing the tower would leave any start tile with no walkable path to any end tile, the placement is rejected and the gold is not spent.
Air enemies use a separate path that ignores towers entirely and instead follows the airborne offset (4 blocks above the configured path).
NPCs and Level Menus
NPC configs in plugins/EternalTD/npcs/ link villager NPCs to one or more levels. Right-clicking the NPC opens a 9-slot inventory listing each level as a green stained-glass pane labelled with the level name and description.
| Field | Type | Default | Notes |
|---|---|---|---|
isEnabled | bool | true | Disabled NPCs are skipped |
levelIDs | string list | required | Filenames of the levels this NPC offers |
location | string | null | Spawn location in the standard worldName,x,y,z,yaw,pitch format |
name | string | "Default Name" | NPC display name |
difficulty | string | "Difficulty: Not Set" | Difficulty label shown above the NPC |
disguise | string | null | LibsDisguises descriptor (e.g. custom:etd_tutorial_npc) |
customDisguiseData | string | null | Extra LibsDisguises command data — usually the long player-skin string |
The villager is spawned invulnerable, AI-disabled, persistent, and tagged with EternalTD's NPC namespaced key. If LibsDisguises is installed and both disguise and customDisguiseData are set, the villager is disguised on spawn.
A floating armor stand with the difficulty label is spawned 2.3 blocks above the NPC.
Spawn Behavior
DefaultConfig controls how players are managed in the hub world:
setupDone— flag tracking whether first-time setup guidance has been completed.spawnLocations— defaults toetd_spawn,0,65,0,0,0. Only used when theetd_spawnworld exists.manageSpawn— defaults totrue. When enabled, joining players are teleported to the spawn location 1 tick after login.playerGuide— the in-game guide book text.
When manageSpawn is true and the spawn world is loaded, every player that joins the server is teleported to spawnLocations.