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
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
isEnabled | bool | true | Deaktivierte Level werden beim Laden übersprungen |
levelName | string | null | Anzeigename, der in Nachrichten, Scoreboards und NPC-Menüs angezeigt wird |
levelDescription | string list | [] | Zeilen, die in NPC-Menüs angezeigt werden. Unterstützt die Platzhalter $highscoreWave und $highscorePlayer |
worldName | string | null | Der Name des Vorlagenordners unter plugins/EternalTD/worlds/ |
startLocation | string list | null | Liste serialisierter Positionen, an denen Mobs spawnen |
endLocation | string list | null | Liste serialisierter Positionen, zu denen die Mobs laufen (die "roten" Felder) |
levelLocations | string list | null | Jedes begehbare Rasterfeld im Level. Pakete liefern es mit; bei einer eigenen Karte schreibt /etd register <level> es aus einer Bodenauswahl, oder du schreibst es von Hand |
wavesConfigFile | string | erforderlich | Dateiname der verknüpften Wellenkonfiguration (waves/<name>.yml) |
highscoreWave | int | 0 | Beste erreichte Welle in diesem Level |
highscorePlayerName | string | "No one" | Anzeigename des Spielers, der den Punktestand aufgestellt hat |
environment | enum | NORMAL | Weltumgebung, 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 wird nicht verwendet. Der Schlüssel bestimmt in alten Dateien nicht die Wellenanzahl; EternalTD verwendet die verknüpfte Wellenkonfiguration.
Format der Positions-Strings
Positionen in EternalTD werden als kommagetrennte Strings in folgender Form serialisiert:
worldName,x,y,z,pitch,yaw
Auch vierteilige Angaben worldName,x,y,z werden mit Pitch und Yaw null akzeptiert, ebenso fünfteilige Angaben x,y,z,pitch,yaw ohne Welt. Für Raster-, Start- und Endeinträge einer Sitzung ersetzt EternalTD den angegebenen Weltnamen durch die Instanzwelt. Bei Hub- und NPC-Positionen muss hingegen die benannte Welt geladen sein.
Heruntergeladene Pakete liefern diese Werte normalerweise mit. Bei eigenen Karten enthält levelLocations eine ganzzahlige Koordinate des mittleren Bodenblocks pro spielbarem 3×3-Feld, mit drei Blöcken Abstand. Beim Laden werden X und Z für den Mittelpunkt in der Welt um 0.5 erhöht. /etd register <level> schreibt diese Liste aus einer Bodenauswahl (siehe Ablauf zur Kartenerstellung); du kannst sie auch von Hand schreiben.
Welt-Lebenszyklus
Wenn ein Spieler einem Level beitritt:
- EternalTD sucht
worldNameunter den direkten Unterordnern vonplugins/EternalTD/worlds/. Namen mit einem beliebigen.werden ausgeschlossen. Fehlende Vorlagen, nicht verfügbare Wellenkonfigurationen und unsichere Namen werden abgelehnt. Ebenso abgelehnt werden Level, derenlevelLocations,startLocationoderendLocationfehlt, leer ist oder einen nicht parsebaren Eintrag enthält: "This level has missing or invalid floor, entrance, or exit coordinates. Nothing was started." - Eine zweite Weltvorbereitungsanfrage wird abgelehnt, solange die erste Anfrage dieses Spielers noch läuft. Abgelehnt werden auch Spieler, die bereits in einer Partie sind, auch in der Partie eines anderen Nightbreak-Plugins ("You are already in a match!"). Die laufende Anfrage reserviert den Spieler im gemeinsamen Match-System, sodass die Partien anderer Plugins ihn ablehnen, bis sie abgeschlossen ist. Ein Verbindungsabbruch oder 10 Minuten ohne bereite Welt brechen sie ab und löschen die unvollständige Kopie.
- 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. - Die Vorlage wird außerhalb des Haupt-Threads in den Weltcontainer kopiert. Weltladen und Sitzungsinitialisierung laufen weiterhin im Haupt-Thread.
- Die geklonte Welt wird über MagmaCores
TemporaryWorldManagerals 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. - EternalTD prüft das Raster (siehe Validierung beim Sitzungsstart) und
InstanceProtectorwendet die Schutzregeln von EternalTD an. Danach lässt das Match-System den Spieler zu: Es speichert seinen Wiederherstellungsdatensatz, teleportiert ihn im nächsten Tick zum Spawnpunkt der neuen Welt und verteilt die Partie-Items. Verweigert ein anderes Plugin diesen Teleport, verlässt der Spieler die Partie und der Lauf endet. Warte vor dem Start auf "Created your world! We're ready to go.". Die frühere Meldung "Joined level!" allein bestätigt keine erfolgreiche Sitzung.
Anforderungen an Vorlagen
EternalTD prüft das Stammverzeichnis vor dem Erstellen eines Klons und jede enthaltene Datei sowie jedes Unterverzeichnis während des Kopierens. Ist ein enthaltener Eintrag ungültig, wird der unvollständige Klon entfernt. Eine Vorlage wird abgelehnt, wenn:
- sie kein Verzeichnis ist oder ein symbolischer Link ist
- sie kein
level.datin ihrem Wurzelverzeichnis hat - sie irgendwo in ihrem Inneren einen symbolischen Link enthält
- sie eine Datei namens
.eternaltd-instanceenthält (dieser Name ist reserviert, siehe unten)
Eine fehlgeschlagene Kopie löst einen Rollback und eine Fehlermeldung aus. Prüfe die Konsole, falls auch die Bereinigung scheitert; die Meldung belegt nicht, dass jede Datei entfernt wurde.
Besitzmarkierungen von Instanzen
Jeder Klon erhält .eternaltd-instance mit Vorlagen- und Instanznamen. EternalTD prüft diese Markierung, bevor es eine veraltete Instanz löscht und bevor es eine kopierte Welt löscht, die die Validierung nicht bestanden hat. Eine unvollständige Kopie wird entfernt, sobald das Kopieren fehlschlägt, und die Welt einer erstellten Sitzung löscht das Match-System beim Ende der Sitzung (siehe unten).
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 spielbar, wenn alle folgenden Bedingungen erfüllt sind:
levelLocations,startLocationundendLocationsind 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!"
Diese Prüfungen laufen, nachdem die kopierte Welt geladen ist und bevor der Spieler zugelassen wird. Ein Fehler lässt den Zustand des Spielers daher unberührt. EternalTD löscht dann die temporäre Welt und meldet "Failed to start the level. Your player and the temporary world were rolled back." Scheitert die Zulassung selbst, nachdem der Wiederherstellungsdatensatz gespeichert wurde, wird der Datensatz sofort wiederhergestellt.
Wenn die Sitzung endet:
- Alle verbleibenden Türme werden abgebaut (ein Turm, der beim Entfernen einen Fehler wirft, wird protokolliert und übersprungen, statt den Rest hängen zu lassen).
- Der Wiederherstellungsdatensatz des Spielers wird wiederhergestellt, und er wird an die Stelle zurückgebracht, an der er sich direkt vor dem Betreten des Levels befand, oder zum Spawn der Hauptwelt, wenn jene Welt nicht mehr geladen ist.
- Die Schutzregeln der Welt werden entfernt. Alle anderen, die sich noch in der geklonten Welt befinden, etwa Teammitglieder mit
eternaltd.match.bypass, werden zum Hub-Spawn ausconfig.ymlgeschickt, oder zum Spawn der Hauptwelt, wenn kein Hub konfiguriert ist. - Die über dem Spielbereich gezogenen Chunk-Tickets werden freigegeben (siehe Pfadvalidierung).
- Die Bereinigung fordert Entladen und Löschen in beiden Layouts an. Wird das Entladen verweigert, werden keine Dateien gelöscht. Bei aktiviertem Plugin erfolgt das Löschen asynchron 5 Sekunden nach dem Entladen. Eine vor der Bereinigung erneut geladene Welt wird ebenfalls übersprungen; unvollständiges Löschen wird protokolliert.
- Das Beenden ist idempotent — ein zweites
end()(zum Beispiel/etd quitunmittelbar 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
Das Match-System bewacht die Welt zusätzlich: Der Spieler im Lauf kann sich weder hinein, hinaus noch innerhalb der Welt teleportieren, außer durch EternalTDs eigene Bewegungen, und andere Spieler ohne eternaltd.match.bypass können sich nicht hineinteleportieren. Siehe Partieregeln.
Ablauf zur Kartenerstellung
Verwende die Vorlage als bearbeitbare Quelle und schreibe das Raster in die Level-YAML:
- Bearbeite die Karte in einer separaten Welt oder im Einzelspielermodus, speichere sie und lege die Vorlage unter
plugins/EternalTD/worlds/<worldName>/ab. Änderungen an einer Spielinstanz aktualisieren diese Vorlage nicht. - Erstelle ihre Level-YAML, verknüpfe eine geladene Wellenkonfiguration und fülle
levelLocations,startLocationundendLocation. Verwende für das Raster mittlere Bodenblöcke und für Start/Ende Positionen, die zu diesen Feldern aufgelöst werden. - Für die Spielwerkzeuge in deiner Bearbeitungswelt: Lade die Konfiguration mit
/etd reload, dann starte/etd selectfloor. Linksklick setzt Ecke 1, Rechtsklick setzt Ecke 2. Beide Ecken müssen in derselben Welt und auf derselben Bodenhöhe liegen; die X/Z-Längen einschließlich beider Enden müssen durch 3 teilbar sein. - Alternativ setzt
/etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2>Ecken in der aktuellen Welt und zieht von jedem angegebenen Y eins ab. Gib daher Boden-Y plus eins an. /etd showselection <level>zeigt über jedem Bodenfeld, das die Auswahl registrieren würde, einenEND_ROD-Partikel an, der nur für dich sichtbar ist. Es wird nichts gespeichert.- Lass die Auswahl für
/etd register <level> startoderendaktiv. Diese Befehle hängen die ganzzahligen Blockkoordinaten des Spielers an und speichern sofort, ohne Y zu verringern oder die Rasterzugehörigkeit zu prüfen. Initialisiere beide Listen vorher. Du kannst sie auch direkt in der YAML bearbeiten. - Führe nach diesen Registrierungen
/etd register <level>aus. Der Befehl schreibtlevelLocationsaus der Auswahl, ersetzt dabei die bestehende Liste, speichert die Level-Datei und leert die Auswahl. Das Abschalten des Auswahlmodus verwirft sie ebenfalls. - Speichere die bearbeitete Vorlage, lade neu und tritt über das NPC-Menü oder
/etd join <level>bei. Warte auf die endgültige Bereitschaftsmeldung und prüfe die Karte im Spiel.
Die Auswahlprüfung berechnet die Abmessungen so und schneidet den Nachkommateil ab:
size = (abs(corner1 - corner2) + 1) / 3
Die Reihenfolge der Ecken spielt keine Rolle. Geprüft werden mittlere Blöcke ab der niedrigeren X/Z-Ecke plus eins, dann alle drei Blöcke, auf der Y-Höhe von Ecke 1 in der aktuellen Welt des Spielers. Ein Feld entfällt, wenn sein mittlerer Bodenblock durchlässig oder der Block darüber undurchlässig ist. Diese Prüfung kontrolliert nicht jeden Block des 3×3-Feldes.
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.
Start- und Endpositionen werden über X/Z einem Rasterfeld zugeordnet. Ein exakter Mittelpunkt wird nicht verlangt und Y nicht verglichen. Einträge ohne passendes Feld werden mit einer Warnung übersprungen. Die Warnung nennt einen falschen Mittelpunkt als mögliche Ursache; prüfe auch, ob das gewünschte Feld in levelLocations enthalten ist.
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 mit Levels. Rechtsklick öffnet ein Menü mit 9 Slots und Einträgen in 0, 2, 4, 6 und 8. Nur die ersten fünf konfigurierten Level-IDs werden berücksichtigt; fehlende IDs hinterlassen Lücken, spätere IDs werden mit Warnung übersprungen. Ein NPC ohne geladenes referenziertes Level wird ganz übersprungen. Linksklick auf eine grüne Glasscheibe fordert das zugehörige Level an.
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
isEnabled | bool | true | Deaktivierte NPCs werden übersprungen |
levelIDs | string list | erforderlich | Dateinamen der Levels, die dieser NPC anbietet |
location | string | null | Spawn-Position im Standardformat worldName,x,y,z,pitch,yaw |
name | string | null bei eigener Konfiguration | NPC-Anzeigename; vorgefertigte Definitionen können einen liefern |
difficulty | string | null bei eigener Konfiguration | Beschriftung über dem NPC; ausdrücklich festlegen |
disguise | string | null | LibsDisguises-Deskriptor (z. B. custom:etd_tutorial_npc) |
customDisguiseData | string | null | Zusätzliche LibsDisguises-Befehlsdaten — meist der lange Spieler-Skin-String |
Das Spawnen des NPCs wird vier Sekunden nach dem Erstellen seiner Konfiguration eingeplant und benötigt die geladene Welt seiner Position. Der Dorfbewohner ist unverwundbar, ohne KI, persistent und mit EternalTDs NPC-Schlüssel markiert. Bei aktiviertem LibsDisguises und beiden gesetzten Verkleidungsfeldern versucht EternalTD die Verkleidung; ein ungültiger Deskriptor kann einen gewöhnlichen Dorfbewohner zurücklassen.
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 (Standardfalse).spawnLocations— Standard istetd_spawn,0,65,0,0,0und wird immer in die Konfiguration geschrieben. Der Wert wird nur aufgelöst und verwendet, wenn die Weltetd_spawnexistiert.manageSpawn— Standardtrue. Bei aktivierter Einstellung und geladenem Ziel fordert EternalTD 1 Tick nach dem Login einen Spawn-Teleport an.playerGuide— der Text des spielinternen Anleitungsbuchs.nightbreak.autoDownloadPluginUpdates— gemeinsame MagmaCore-Einstellung (Standardfalse). Wenn aktiviert, werden Plugin- und Inhalts-Updates beim Start automatisch heruntergeladen.nightbreak.applyPluginUpdatesWithoutRestart(gemeinsame MagmaCore-Einstellung, Standardnever; außerdemwhen-emptyoderimmediately) steuert, wann ein heruntergeladenes Plugin-Update ohne Neustart angewendet wird. Siehe Einrichtung.
Nach der Initialisierung fordert EternalTD diesen Teleport auch für bereits verbundene Spieler an, außer sie befinden sich schon in der Zielwelt. Auch dafür sind manageSpawn und ein geladenes Ziel erforderlich. Diese Login- und Nachholpfade prüfen das Teleportergebnis nicht; ein anderes Plugin kann die Ankunft verhindern.