Níveis e mapas do EternalTD
Um nível EternalTD combina um ficheiro YAML em plugins/EternalTD/levels/ com uma pasta de mundo de origem em plugins/EternalTD/worlds/. Quando um jogador entra, o EternalTD copia esse mundo para a pasta de mundos do servidor e executa a sessão na cópia.
Campos da configuração do nível
| Campo | Tipo | Predefinição | Observações |
|---|---|---|---|
isEnabled | booleano | true | Níveis desativados são ignorados no carregamento |
levelName | texto | null | Nome apresentado nas mensagens, painéis de pontuação e menus de NPCs |
levelDescription | lista de textos | [] | Linhas dos menus de NPCs. Aceita $highscoreWave e $highscorePlayer |
worldName | texto | null | Nome da pasta de origem em plugins/EternalTD/worlds/ |
startLocation | lista de textos | null | Posições serializadas onde aparecem os mobs |
endLocation | lista de textos | null | Posições serializadas para onde caminham os mobs, as células vermelhas |
levelLocations | lista de textos | null | Todas as células transitáveis do nível. Os pacotes incluem-na; num mapa personalizado, /etd register <level> escreve-a a partir de uma seleção do chão, ou pode escrevê-la manualmente |
wavesConfigFile | texto | obrigatório | Nome do ficheiro de vagas associado, waves/<name>.yml |
highscoreWave | inteiro | 0 | Melhor vaga alcançada no nível |
highscorePlayerName | texto | "No one" | Nome apresentado do detentor do recorde |
environment | enum | NORMAL | Ambiente usado ao carregar o mundo copiado |
Cada célula lógica tem 3 blocos de lado, conforme a constante GRID_SIZE.
waveCount não é usado. A sua presença num ficheiro antigo não define o número de vagas; o EternalTD usa a configuração de vagas associada.
Formato das posições
As posições são serializadas como texto separado por vírgulas:
worldName,x,y,z,pitch,yaw
Também são aceites textos de quatro partes worldName,x,y,z, com pitch e yaw a zero, e textos de cinco partes x,y,z,pitch,yaw, sem mundo. Para a grelha, os inícios e os fins de uma sessão, o EternalTD substitui o mundo indicado pelo da instância. As posições do hub e dos NPCs precisam de ter o respetivo mundo carregado.
Os pacotes transferidos normalmente incluem estes valores. Num mapa personalizado, levelLocations contém uma coordenada inteira do bloco central por célula de chão jogável de 3×3, com três blocos de intervalo. O carregamento soma 0.5 a X e Z para obter o centro no mundo. /etd register <level> escreve esta lista a partir de uma seleção do chão (consulte Procedimento de criação de mapas); também a pode escrever manualmente.
Ciclo de vida do mundo
Quando um jogador entra num nível:
- O EternalTD procura
worldNamenas subpastas diretas deplugins/EternalTD/worlds/. Nomes com qualquer.são excluídos. Mundos de origem em falta, configurações de vagas indisponíveis e nomes inseguros são rejeitados. Também são rejeitados níveis cujolevelLocations,startLocationouendLocationesteja em falta, vazio ou contenha uma entrada que não possa ser interpretada: "This level has missing or invalid floor, entrance, or exit coordinates. Nothing was started." - Rejeita um segundo pedido de preparação enquanto o primeiro pedido do jogador estiver pendente, e rejeita jogadores que já estejam numa partida, incluindo uma partida de outro plugin Nightbreak ("You are already in a match!"). O pedido pendente reserva o jogador no sistema partilhado de partidas, pelo que as partidas de outros plugins o recusam até o pedido terminar. Desligar-se, ou passarem 10 minutos sem um mundo pronto, cancela o pedido e elimina a cópia parcial.
- Reserva o próximo sufixo numérico livre:
<worldName>_0,<worldName>_1, etc. A reserva é sincronizada e inclui nomes ainda não escritos no disco, evitando colisões entre entradas simultâneas. - Copia o mundo fora da thread principal. O carregamento do mundo e a inicialização da sessão continuam na thread principal.
- Carrega a cópia como mundo vazio temporário através de
TemporaryWorldManagerdo MagmaCore. A migração Paper 26.1+ fica em quarentena e chunks ausentes surgem vazios, sem gerar terreno novo. - O EternalTD valida a grelha (consulte Validação do início da sessão) e
InstanceProtectoraplica as proteções do EternalTD. Depois, o sistema de partidas admite o jogador: guarda o seu registo de recuperação, teleporta-o para o ponto de spawn do novo mundo no tick seguinte e entrega os itens da partida. Se outro plugin recusar esse teletransporte, o jogador sai da partida e esta termina. Espere por "Created your world! We're ready to go." antes de começar. A mensagem anterior "Joined level!" não confirma, por si só, uma sessão bem-sucedida.
Requisitos do mundo de origem
A raiz é validada antes da criação da cópia; cada ficheiro e subpasta é verificado durante a cópia. Uma entrada inválida faz eliminar a cópia parcial. O mundo é rejeitado se:
- não for uma pasta ou for uma ligação simbólica
- não contiver
level.datna raiz - contiver uma ligação simbólica em qualquer ponto
- contiver
.eternaltd-instance, nome reservado descrito abaixo
Uma cópia falhada desencadeia o restauro e uma mensagem de erro. Consulte a consola se a própria limpeza falhar; a mensagem não comprova que todos os ficheiros foram removidos.
Marcadores de propriedade das instâncias
Cada cópia recebe .eternaltd-instance com os nomes do mundo de origem e da instância. O EternalTD verifica este marcador antes de eliminar uma instância abandonada e antes de eliminar um mundo copiado que falhou a validação. Uma cópia parcial é removida assim que a cópia falha, e o mundo de uma sessão criada é eliminado pelo sistema de partidas quando a sessão termina (ver abaixo).
Instâncias abandonadas após uma falha são limpas ao carregar os níveis, apenas se tiverem marcadores válidos e não estiverem carregadas.
Validação do início da sessão
Uma sessão só fica jogável se passar todas as verificações seguintes:
levelLocations,startLocationeendLocationdevem existir e não estar vazios; caso contrário, é apresentada a mensagem "This level doesn't have a complete play area, start, and end configuration!"- a área deve ser convertível numa grelha válida; caso contrário, é apresentada a mensagem "This level's play area is invalid!"
- cada célula inicial deve ter um percurso terrestre e um aéreo até alguma célula final; caso contrário, é apresentada a mensagem "This level does not have complete land and air paths from every start to an end!"
Estas verificações ocorrem depois de o mundo copiado carregar e antes de o jogador ser admitido, pelo que uma falha deixa intacto o estado do próprio jogador. O EternalTD elimina então o mundo temporário e apresenta "Failed to start the level. Your player and the temporary world were rolled back." Se a própria admissão falhar depois de o registo de recuperação ter sido guardado, o registo é reposto de imediato.
Quando a sessão termina:
- todas as torres restantes são desmontadas; erros ao remover uma torre são registados e essa torre é ignorada, sem impedir a remoção das restantes
- o registo de recuperação do jogador é reposto e o jogador regressa ao local onde estava imediatamente antes de entrar no nível, ou ao spawn do mundo principal se esse mundo já não estiver carregado
- as proteções do mundo são removidas. Quem ainda estiver no mundo copiado, como membros da equipa com
eternaltd.match.bypass, é enviado para o spawn do hub definido emconfig.yml, ou para o spawn do mundo principal se não houver hub configurado - são libertadas as referências que mantêm os chunks da área carregados; consulte Validação dos percursos
- a limpeza solicita retirar o mundo da memória e eliminá-lo nos dois formatos; se a retirada for recusada, não há eliminação. Com o plugin ativo, a eliminação é assíncrona e ocorre 5 segundos depois de o mundo ser retirado da memória. Um mundo novamente carregado antes da limpeza também é ignorado, e eliminações incompletas são registadas
- o encerramento é idempotente: uma segunda chamada
end(), como/etd quitseguido de/etd hub, não faz nada
Proteções das instâncias
Enquanto o nível está ativo, o mundo copiado bloqueia:
- explosões
- fluxo de líquidos
- élitros
- alterações do voo
- fogo amigo
- aparecimento vanilla de mobs
O sistema de partidas também protege o mundo: o jogador da partida não se pode teletransportar para dentro, para fora ou dentro dele, exceto através dos movimentos do próprio EternalTD, e outros jogadores sem eternaltd.match.bypass não se podem teletransportar para lá. Consulte Regras da partida.
Procedimento de criação de mapas
Use o mundo de origem como original editável e escreva a grelha no YAML do nível:
- Edite o mapa noutro mundo ou em modo de um jogador, guarde-o e coloque o mundo de origem em
plugins/EternalTD/worlds/<worldName>/. Editar uma cópia de partida não atualiza esse original. - Crie o YAML do nível, associe uma configuração de vagas carregada e preencha
levelLocations,startLocationeendLocation. Use blocos centrais do chão para a grelha e posições iniciais/finais que correspondam a essas células. - Para usar os auxiliares no mundo de edição, execute
/etd reloadpara carregar a configuração e depois/etd selectfloor. O clique esquerdo define o canto 1 e o clique direito define o canto 2. Mantenha ambos no mesmo mundo e na mesma altura do chão; os comprimentos X/Z, incluindo as extremidades, devem ser múltiplos de 3. - Em alternativa,
/etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2>define os cantos no mundo atual e subtrai 1 a cada Y indicado. Forneça o Y do chão mais um. /etd showselection <level>mostra uma partículaEND_ROD, visível apenas para si, por cima de cada célula do chão que a seleção registaria. Não guarda nada.- Mantenha a seleção ativa ao executar
/etd register <level> startouend. Estes comandos acrescentam as coordenadas inteiras de bloco do jogador e guardam imediatamente, sem subtrair 1 a Y nem verificar se o ponto pertence à grelha. Inicialize ambas as listas antes de os usar. Também pode editá-las diretamente no YAML. - Depois destes registos, execute
/etd register <level>. O comando escrevelevelLocationsa partir da seleção, substituindo a lista existente, guarda o ficheiro do nível e limpa a seleção. Desativar o modo de seleção também a descarta. - Guarde o mundo de origem editado, recarregue e entre pelo menu do NPC ou por
/etd join <level>. Aguarde a mensagem final de mundo pronto e verifique o mapa em jogo.
O verificador de seleção calcula as dimensões assim, truncando o resultado para inteiro:
size = (abs(corner1 - corner2) + 1) / 3
A ordem dos cantos não importa. São verificados os blocos centrais a partir do canto com menor X/Z mais um, de três em três blocos, à altura Y do canto 1, no mundo atual do jogador. Uma célula é ignorada se o bloco central do chão for atravessável ou o bloco acima não o for. A verificação não examina todos os blocos da célula 3×3.
Validação dos percursos
Para cada início, o EternalTD guarda em cache o percurso A* de menor custo até um fim: um terrestre e outro aéreo. Com várias saídas, compara os percursos concluídos e conserva o mais curto, encaminhando os inimigos para a saída alcançável mais próxima.
A cache terrestre é reconstruída no início da sessão, ao colocar uma torre e ao vender uma torre. A cache aérea só é calculada uma vez, na primeira reconstrução, porque ignora completamente as torres.
Os inimigos aéreos seguem esse percurso separado, sem bloqueios de torres, a 4 blocos acima do caminho configurado.
Carregar a área também mantém carregados todos os chunks que contêm células da grelha, evitando interrupções em partidas longas. Estas referências são libertadas no fim, antes de eliminar o mundo.
A cache determina ainda se o nível é jogável: se faltar um percurso terrestre ou aéreo a qualquer início, a entrada é recusada. A mesma verificação ocorre ao construir, pelo que uma torre que bloqueasse um início é rejeitada sem gastar ouro.
A procura de inícios e fins usa X/Z para resolver uma célula, sem exigir um centro exato nem comparar Y. Uma entrada sem célula correspondente é ignorada com um aviso. O aviso sugere um centro incorreto, mas confirme também se a célula pretendida existe em levelLocations.
Todas as células iniciais e finais válidas são marcadas como não edificáveis, impedindo bloquear diretamente uma entrada ou saída. O indicador de colocação mostra-as a azul-claro.
NPCs e menus de níveis
As configurações em plugins/EternalTD/npcs/ associam aldeões a níveis. O clique direito abre um menu de nove espaços, com entradas em 0, 2, 4, 6 e 8. Só são considerados os primeiros cinco IDs configurados; os ausentes deixam espaços vazios e os posteriores são ignorados com aviso. Um NPC sem qualquer nível referenciado carregado é ignorado por completo. Clique com o botão esquerdo num painel de vidro verde para pedir o nível correspondente.
| Campo | Tipo | Predefinição | Observações |
|---|---|---|---|
isEnabled | booleano | true | NPCs desativados são ignorados |
levelIDs | lista de textos | obrigatório | Nomes dos ficheiros de níveis oferecidos |
location | texto | null | Posição no formato worldName,x,y,z,pitch,yaw |
name | texto | null numa configuração personalizada | Nome apresentado do NPC; as definições pré-fabricadas podem fornecer um |
difficulty | texto | null numa configuração personalizada | Rótulo acima do NPC; defina-o explicitamente |
disguise | texto | null | Descritor LibsDisguises, por exemplo custom:etd_tutorial_npc |
customDisguiseData | texto | null | Dados adicionais do comando LibsDisguises, normalmente o texto longo da skin de jogador |
O aparecimento do NPC é agendado quatro segundos após a criação da sua configuração e exige que o mundo da posição esteja carregado. O aldeão é invulnerável, sem IA, persistente e marcado com a chave de NPC do EternalTD. Com LibsDisguises ativo e ambos os campos de disfarce definidos, o EternalTD tenta aplicar o disfarce; um descritor inválido pode deixar um aldeão normal.
Um suporte de armadura flutuante apresenta difficulty 2.3 blocos acima do NPC.
Comportamento do spawn
DefaultConfig controla os jogadores no hub:
setupDone: indica se a orientação inicial foi concluída; predefiniçãofalse.spawnLocations: predefinido comoetd_spawn,0,65,0,0,0e sempre escrito na configuração. Só é resolvido e usado com o mundoetd_spawnpresente.manageSpawn: predefiniçãotrue. Com a opção ativa e o destino carregado, o EternalTD solicita um teletransporte para o spawn um tick após a entrada.playerGuide: texto do livro de instruções.nightbreak.autoDownloadPluginUpdates: opção partilhada MagmaCore, predefinida comofalse, que ativa a transferência automática de atualizações do plugin e do conteúdo no arranque.nightbreak.applyPluginUpdatesWithoutRestart: opção partilhada MagmaCore, predefinida comonever(também aceitawhen-emptyouimmediately), que controla quando uma atualização do plugin transferida é aplicada sem reiniciar. Consulte Configuração.
Ao terminar a inicialização, o EternalTD também solicita esse teletransporte para jogadores já ligados, exceto os que já estejam no mundo de destino. Este procedimento também exige manageSpawn e um destino carregado. As rotinas de entrada e de fim da inicialização não verificam o resultado do teletransporte, pelo que outro plugin pode impedir a chegada.