Zum Hauptinhalt springen

EternalTD-Level & Karten

Ein "Level" in EternalTD ist eine YAML-Konfiguration in plugins/EternalTD/levels/, gepaart mit einem Vorlagen-Weltordner in plugins/EternalTD/worlds/. Wenn ein Spieler beitritt, klont EternalTD die Vorlagenwelt in den Server-Weltcontainer und führt die Sitzung in dieser geklonten Kopie aus.

Felder der Level-Konfiguration

FeldTypStandardHinweise
isEnabledbooltrueDeaktivierte Level werden beim Laden übersprungen
levelNamestringnullAnzeigename, der in Nachrichten, Scoreboards und NPC-Menüs angezeigt wird
levelDescriptionstring list[]Zeilen, die in NPC-Menüs angezeigt werden. Unterstützt die Platzhalter $highscoreWave und $highscorePlayer
worldNamestringnullDer Name des Vorlagenordners unter plugins/EternalTD/worlds/
startLocationstring listnullListe serialisierter Positionen, an denen Mobs spawnen
endLocationstring listnullListe serialisierter Positionen, zu denen die Mobs laufen (die "roten" Felder)
levelLocationsstring listnullJedes begehbare Rasterfeld im Level; im aktuellen Build müssen diese aus dem Paket stammen oder von Hand in die YAML geschrieben werden
wavesConfigFilestringerforderlichDateiname der verknüpften Wellenkonfiguration (waves/<name>.yml)
highscoreWaveint0Beste erreichte Welle in diesem Level
highscorePlayerNamestring"no one"Anzeigename des Spielers, der den Punktestand aufgestellt hat
environmentenumNORMALWeltumgebung, die beim Laden der geklonten Welt verwendet wird

Die durchgängig verwendete Rastergröße beträgt 3 Blöcke pro logischem Feld (Konstante GRID_SIZE im Code).

waveCount existiert nicht mehr. Ältere Level-Dateien, die den Schlüssel noch enthalten, werden schlicht ignoriert — das Plugin liest ihn nie und schreibt ihn nie zurück.

Format der Positions-Strings

Positionen in EternalTD werden als kommagetrennte Strings in folgender Form serialisiert:

worldName,x,y,z,yaw,pitch

Heruntergeladene Pakete liefern diese Werte normalerweise mit. Bei einer eigenen Karte speichern die aktuellen Auswahl- und Register-Befehle levelLocations nicht, du musst die erzeugten Rasterpositionen also von Hand in die Level-YAML schreiben.

Welt-Lebenszyklus

Wenn ein Spieler einem Level beitritt:

  1. EternalTD prüft, dass für diesen Spieler nicht bereits eine Kopie läuft. Ein zweites /etd join, während die erste noch kopiert, wird mit "Your level is already being prepared." abgelehnt.
  2. Es sucht den Vorlagenordner anhand von worldName in plugins/EternalTD/worlds/ und lehnt den Beitritt ab, wenn der Weltordner fehlt, die verknüpfte Wellendatei nicht geladen werden konnte oder der Weltname kein sicherer Ordnername ist.
  3. Es reserviert das nächste freie numerische Suffix (<worldName>_0, <worldName>_1, ...). Die Reservierung ist synchronisiert und merkt sich Namen, die reserviert, aber noch nicht auf der Festplatte sind, sodass zwei gleichzeitig beitretende Spieler nicht auf denselben Instanznamen kollidieren können.
  4. Die Vorlage wird außerhalb des Haupt-Threads in den Server-Weltcontainer kopiert, sodass der Server bei einer großen Karte nicht stockt.
  5. Die geklonte Welt wird über MagmaCores TemporaryWorldManager als temporäre Void-Welt geladen, sodass die Migration unter Paper 26.1+ in Quarantäne gehalten wird und fehlende Chunks als Void zurückkommen statt als frisch generiertes Terrain.
  6. Der Spieler wird in die neue Welt teleportiert; ein interner InstanceProtector wendet die Schutzregeln von EternalTD an.

Anforderungen an Vorlagen

Die Kopie wird validiert, bevor irgendetwas geschrieben wird, und eine Vorlage wird abgelehnt, wenn:

  • sie kein Verzeichnis ist oder ein symbolischer Link ist
  • sie kein level.dat in ihrem Wurzelverzeichnis hat
  • sie irgendwo in ihrem Inneren einen symbolischen Link enthält
  • sie eine Datei namens .eternaltd-instance enthält (dieser Name ist reserviert, siehe unten)

Eine fehlgeschlagene Kopie wird zurückgerollt und dem Spieler wird mitgeteilt, dass das Level nicht gestartet wurde.

Besitzmarkierungen von Instanzen

Jeder Klon erhält in seinem Wurzelverzeichnis eine Datei .eternaltd-instance, die den Vorlagennamen und den Instanznamen festhält. EternalTD löscht nur einen Weltordner, dessen Markierung zu der Instanz passt, die es zu löschen glaubt. Das ist die Schutzvorrichtung, die verhindert, dass eine veraltete Namenskollision, ein von Hand erstellter Ordner oder eine unpassende Sitzung eine Welt entfernt, die EternalTD nicht erstellt hat — fehlt die Markierung oder passt sie nicht, bleibt der Ordner erhalten und es wird stattdessen eine Warnung protokolliert.

Veraltete Instanzen, die von einem Absturz übrig geblieben sind, werden beim Laden der Level bereinigt, und auch dann nur für Ordner, die eine gültige Markierung tragen und nicht gerade geladen sind.

Validierung beim Sitzungsstart

Eine Sitzung wird nur dann spielbar, wenn alle folgenden Bedingungen erfüllt sind. Jeder Fehlschlag bricht den Lauf ab, stellt den Spieler wieder her (Inventar, Spielmodus, Flug, Scoreboard) und löscht die temporäre Welt:

  • levelLocations, startLocation und endLocation sind alle vorhanden und nicht leer — andernfalls "This level doesn't have a complete play area, start, and end configuration!"
  • der Spielbereich selbst lässt sich in ein gültiges Raster parsen — andernfalls "This level's play area is invalid!"
  • jedes Startfeld hat sowohl einen Land- als auch einen Luftpfad zu irgendeinem Endfeld — andernfalls "This level does not have complete land and air paths from every start to an end!"

Wenn die Sitzung endet:

  • Alle verbleibenden Spieler in der geklonten Welt werden zur Spawn-Position aus config.yml zurückteleportiert oder gekickt, wenn kein Spawn konfiguriert ist.
  • Die Schutzregeln der Welt werden entfernt und alle verbleibenden Türme abgebaut (ein Turm, der beim Entfernen einen Fehler wirft, wird protokolliert und übersprungen, statt den Rest hängen zu lassen).
  • Die über dem Spielbereich gezogenen Chunk-Tickets werden freigegeben (siehe Pfadvalidierung).
  • Die geklonte Welt wird entladen und von der Festplatte gelöscht (TemporaryWorldManager.permanentlyDeleteWorld), sowohl im Legacy- als auch im migrierten Paper-26.1+-Layout.
  • Das Beenden ist idempotent — ein zweites end() (zum Beispiel /etd quit unmittelbar gefolgt von /etd hub) ist ein No-Op.

Instanz-Schutzregeln

Während ein Level aktiv ist, gelten für die geklonte Welt diese Regeln:

  • Explosionen deaktiviert
  • Flüssigkeitsfluss deaktiviert
  • Elytra deaktiviert
  • Umschalten des Flugmodus verhindert
  • Friendly Fire verhindert
  • Vanilla-Mob-Spawning verhindert

Workflow zur Kartenerstellung

Der aktuelle Ablauf zur Kartenerstellung nutzt die spielinternen Werkzeuge:

  1. Lege einen Vorlagen-Weltordner unter plugins/EternalTD/worlds/<worldName>/ ab.
  2. Erstelle oder lade eine passende Level-YAML in plugins/EternalTD/levels/.
  3. Führe /etd reload aus und tritt der Level-Welt manuell bei (oder öffne sie im Einzelspielermodus zur Einrichtung).
  4. Verwende /etd selectfloor und klicke mit Rechts-/Linksklick zwei Ecken an, um den Spielbereich zu markieren, oder verwende /etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2>, um sie direkt anzugeben.
  5. Führe /etd showselection <level> aus, um zu bestätigen, dass die Auswahl korrekt aussieht.
  6. Führe /etd register <level> aus, um die Auswahl zu löschen. Beachte, dass im aktuellen Build weder register noch showselection den Bodenbereich tatsächlich speichern — der Helfer, der levelLocations speichern würde (LevelsConfigFields#addLevelLocations), ist zwar definiert, wird aber von keinem Befehl aufgerufen. Du musst levelLocations derzeit von Hand in die Level-YAML schreiben, falls es nicht bereits durch ein heruntergeladenes Paket befüllt ist.
  7. Stelle dich auf ein Start-Spawnfeld und führe /etd register <level> start aus. Wiederhole dies für jedes Startfeld (dieser Befehl speichert tatsächlich in startLocation).
  8. Stelle dich auf ein Endfeld und führe /etd register <level> end aus. Wiederhole dies für jedes Endfeld (dieser Befehl speichert tatsächlich in endLocation).
  9. Lade erneut neu und teste das Level, indem du ihm über das NPC-Menü oder /etd join <level> beitrittst.

Die Auswahlbefehle generieren Rasterfelder mithilfe folgender Formel:

size = abs(corner1 - corner2 + 1) / 3

Ein Feld wird übersprungen, wenn sein Bodenblock durchlässig ist oder wenn der Block direkt über dem Boden nicht durchlässig ist. Anders gesagt: Der Boden muss solide und der Raum darüber frei sein, damit sich das Feld als bespielbar registriert — wähle den Boden aus, nicht die Luft darüber.

Pfadvalidierung

EternalTD speichert für jedes Startfeld den günstigsten A*-Pfad zu irgendeinem Endfeld zwischen — einen Landpfad und einen Luftpfad. Bei mehreren Endfeldern vergleicht es die fertigen Pfade nach Kosten und behält den kürzesten, sodass eine Karte mit mehreren Ausgängen die Gegner zum nächstgelegenen erreichbaren leitet statt zum ersten gefundenen.

Der Land-Cache wird beim Sitzungsstart, bei jeder Turmplatzierung und bei jedem Turmverkauf neu aufgebaut. Der Luft-Cache wird nur einmal berechnet, beim ersten Aufbau: Luftpfade ignorieren Türme vollständig, sodass nichts, was ein Spieler baut, sie ungültig machen kann.

Luft-Gegner verwenden den separaten Luftpfad, der Turmblockaden vollständig ignoriert und stattdessen dem Luftoffset folgt (4 Blöcke über dem konfigurierten Pfad).

Beim Laden des Spielbereichs wird außerdem jeder Chunk, der ein Rasterfeld enthält, zwangsgeladen, sodass ein langer Lauf nicht an einem entladenen Chunk hängen bleiben kann. Diese Chunk-Tickets werden am Ende der Sitzung freigegeben, kurz bevor die Instanzwelt gelöscht wird.

Der Cache entscheidet außerdem darüber, ob ein Level überhaupt spielbar ist: Fehlt einem Startfeld entweder sein Landpfad oder sein Luftpfad, wird die Sitzung schon beim Beitritt abgelehnt, statt einen Lauf zu starten, den die Gegner nicht beenden können. Dieselbe Prüfung läuft bei der Turmplatzierung, sodass ein Turm, der ein Startfeld abriegeln würde, abgelehnt und das Gold nicht ausgegeben wird.

Eine Start- oder Endposition, die nicht in der Mitte ihres 3x3-Rasterfelds liegt, wird mit einer Konsolenwarnung verworfen — das ist die übliche Ursache dafür, dass ein Level unvollständige Pfade meldet.

Jedes aufgelöste Start- und Endfeld wird zudem als nicht bebaubar markiert, sodass Spieler einen Spawn oder Ausgang nicht durch Bauen abriegeln können. Diese Felder erscheinen unter der Platzierungsmarkierung hellblau.

NPCs und Level-Menüs

NPC-Konfigurationen in plugins/EternalTD/npcs/ verknüpfen Dorfbewohner-NPCs mit einem oder mehreren Levels. Ein Rechtsklick auf den NPC öffnet ein Inventar mit 9 Slots, in dem jedes Level als grün gefärbte Glasscheibe mit dem Level-Namen und der Beschreibung aufgeführt ist.

FeldTypStandardHinweise
isEnabledbooltrueDeaktivierte NPCs werden übersprungen
levelIDsstring listerforderlichDateinamen der Levels, die dieser NPC anbietet
locationstringnullSpawn-Position im Standardformat worldName,x,y,z,yaw,pitch
namestring"Default Name"NPC-Anzeigename
difficultystring"Difficulty: Not Set"Schwierigkeitslabel, das über dem NPC angezeigt wird
disguisestringnullLibsDisguises-Deskriptor (z. B. custom:etd_tutorial_npc)
customDisguiseDatastringnullZusätzliche LibsDisguises-Befehlsdaten — meist der lange Spieler-Skin-String

Der Dorfbewohner wird unverwundbar, mit deaktivierter KI, persistent und mit dem namensraumbasierten NPC-Schlüssel von EternalTD getaggt gespawnt. Wenn LibsDisguises installiert ist und sowohl disguise als auch customDisguiseData gesetzt sind, wird der Dorfbewohner beim Spawnen verkleidet.

Ein schwebender Rüstungsständer mit dem difficulty-Label wird 2,3 Blöcke über dem NPC gespawnt.

Spawn-Verhalten

DefaultConfig steuert, wie Spieler in der Hub-Welt verwaltet werden:

  • setupDone — Flag, das verfolgt, ob die Ersteinrichtungs-Anleitung abgeschlossen wurde (Standard false).
  • spawnLocations — Standard ist etd_spawn,0,65,0,0,0 und wird immer in die Konfiguration geschrieben. Der Wert wird nur aufgelöst und verwendet, wenn die Welt etd_spawn existiert.
  • manageSpawn — Standard ist true. Wenn aktiviert, werden beitretende Spieler 1 Tick nach dem Login zur Spawn-Position teleportiert.
  • playerGuide — der Text des spielinternen Anleitungsbuchs.
  • nightbreak.autoDownloadPluginUpdates — gemeinsame MagmaCore-Einstellung (Standard false). Wenn aktiviert, werden Plugin- und Inhalts-Updates beim Start automatisch heruntergeladen.

Wenn manageSpawn auf true gesetzt und die Spawn-Welt geladen ist, wird jeder Spieler, der dem Server beitritt, zu spawnLocations teleportiert. Spieler, die bereits verbunden waren, während EternalTD noch initialisierte, werden nach Abschluss der Initialisierung zum selben Spawn geholt, sofern sie sich nicht bereits in der Hub-Welt befinden.