diff --git a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java index 95369f9d48..1216115fda 100644 --- a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java @@ -244,7 +244,7 @@ public class EntityProcess extends BaseProcessor{ Stype repr = types.first(); String groupType = repr.annotation(Component.class).base() ? baseName(repr) : interfaceName(repr); - String name = group.name().startsWith("g") ? group.name().substring(1) : group.name(); + String name = group.name(); boolean collides = an.collide(); groupDefs.add(new GroupDefinition(name, diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 2e2b9a89ea..538719ebc8 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = \u26a0 Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[accent] {0} mod.enabled = [lightgray]Enabled diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 7d4949db3d..79cce0db4d 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Усталявана] mod.display = [gray]Мадыфікацыя:[orange] {0} mod.enabled = [lightgray]Уключана diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index b37fa61544..156c95e702 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Достъпно обновление: {0} mods.update.all = Обнови всички модове mods.downloading = [accent]Изтегляне на мод: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Инсталиран] mod.display = [gray]Мод:[orange] {0} mod.enabled = [lightgray]Активиран diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 8be2af6d6d..9e90e6918b 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instal·lat] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Activat diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 7d6f23d75d..e181f8965f 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalováno] mod.display = [gray]Modifikace:[][orange] {0}[] mod.enabled = [lightgray]Povoleno[] diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 374d090013..3e58be9036 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Aktiveret diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 7098c2a463..6a00eeff60 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installiert] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Aktiviert diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 38a40261cb..78ff23142b 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalado] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Activado diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 8f041be419..659c765dec 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Enabled diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index b0f433c3ab..5651998d7c 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalatuta] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Gaituta diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 9e819928cb..7d42e559ca 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Asennettu] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Käytössä diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index c8049b2e7c..0c9e725b38 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Nakuha] mod.display = [gray]Pangbago:[orange] {0} mod.enabled = [lightgray]Gumagana diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 957ccf19cf..ea58e198c6 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installé] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Activé diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 49a7248f31..7c91a0d625 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Frissítés érhető el: {0} mods.update.all = Összes mod frissítése mods.downloading = [accent]Mod letöltése: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Telepítve] mod.display = [gray]Mod:[accent] {0} mod.enabled = [lightgray]Aktív diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index c06d3691c9..572517f994 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Pembaruan Tersedia: {0} mods.update.all = Perbarui Semua Mod mods.downloading = [accent]Mengunduh Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Terpasang] mod.display = [gray]Mod:[accent] {0} mod.enabled = [lightgray]Aktif diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 7ac99fd57c..7b5cdaa1e0 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Abilitato diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index a8e744fccb..9b503efdfe 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[インストール済み] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]有効 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 08099bb20d..aedeaf2f1c 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [설치됨] mod.display = [gray]모드:[orange] {0} mod.enabled = [lightgray]활성화됨 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index edbbfa6f94..5e3bc67c75 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Įdiegta] mod.display = [gray]Modifikacijos:[orange] {0} mod.enabled = [lightgray]Įjungta diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 4f4449dca1..a77f67b542 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Ingeschakeld diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 26d3a6342a..19c7fb2516 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Ingeschakeld diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 20355e3364..eeb01d888f 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Zainstalowany] mod.display = [gray]Modyfikacja:[orange] {0} mod.enabled = [lightgray]Włączony diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 166c238d65..62a5075fe8 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalado] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Ativado diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 3c33a942a1..3367166179 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalado] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Ativado diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index c8dab2eef9..720b9fbb64 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Instalat] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Activat diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 48ce487b86..4f2aea1a2b 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Доступно обновление: {0} mods.update.all = Обновить все модификации mods.downloading = [accent]Загрузка модификации: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Установлено] mod.display = [gray]Модификация:[accent] {0} mod.enabled = [lightgray]Включено diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index c869a2b060..3b13cc4811 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [Instalirano] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Omogućeno diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 91a08d2f96..e5ef9c5b30 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Aktiverad diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index e6d40b503a..e71c56067f 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[ติดตั้งแล้ว] mod.display = [gray]ม็อด:[orange] {0} mod.enabled = [lightgray]เปิดใช้งาน diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 0c0c858a4a..042068f3ef 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Enabled diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index c37bc33a9f..09dc7a40f9 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Yüklendi] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Etkin diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 20dec2698d..5e08c2e7a0 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Доступне оновлення: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Установлено] mod.display = [gray]Модифікація:[orange] {0} mod.enabled = [lightgray]Увімкнено diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 89099d2e20..37ef368b68 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Cập Nhật Có Sẵn: {0} mods.update.all = Cập Nhật Tất Cả Bản Mod mods.downloading = [accent]Đang Tải Xuống Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[Đã cài đặt] mod.display = [gray]Mod:[accent] {0} mod.enabled = [lightgray]Đã bật diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 4df01260fc..74725f57a5 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]有可用更新:{0} mods.update.all = 更新所有模组 mods.downloading = [accent]正在下载模组:{0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[已安装] mod.display = [gray]模组:[orange] {0} mod.enabled = [lightgray]已启用 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 55f4c8e453..6581c013ba 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0} mods.update.all = Update All Mods mods.downloading = [accent]Downloading Mod: {0} +mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. +mod.warning.read = I actually read this +mod.warning.accept = I accept the responsibility +mod.warning.deny = Disable my mods mod.installed = [[已安裝] mod.display = [gray]模組:[orange]{0} mod.enabled = [lightgray]已啟用 diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index 7fe87e949b..121f74bc5d 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -175,7 +175,7 @@ public class Control implements ApplicationListener, Loadable{ //autohost for pvp maps Events.on(WorldLoadEvent.class, event -> app.post(() -> { - if(state.rules.pvp && !net.active() && !state.rules.pauseDisabled){ + if(state.rules.pvp && !net.active() && !state.rules.pauseDisabled && !state.isEditor() && !ui.editor.isShown()){ try{ net.host(port); player.admin = true; diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 45790073c8..e86ef6844d 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -461,31 +461,38 @@ public class Logic implements ApplicationListener{ } protected void updateEntities(){ + boolean editor = state.isEditor(); + PerfCounter.entityUpdate.begin(); PerfCounter.entityMisc.begin(); Groups.updatePooling(); Groups.bullet.updatePhysics(); Groups.unit.updatePhysics(); - Groups.all.update(); + Groups.player.update(); + Groups.effect.update(); + if(!editor) Groups.all.update(); PerfCounter.entityMisc.end(); PerfCounter.unitUpdate.begin(); - Groups.unit.update(); + if(editor){ + Groups.unit.update(Unitc::isPlayer); + }else{ + Groups.unit.update(); + } PerfCounter.unitUpdate.end(); PerfCounter.powerUpdate.begin(); - if(!state.isEditor()) Groups.powerGraph.update(); + if(!editor) Groups.powerGraph.update(); PerfCounter.powerUpdate.end(); PerfCounter.buildingUpdate.begin(); - if(!state.isEditor()) Groups.build.update(); + if(!editor) Groups.build.update(); PerfCounter.buildingUpdate.end(); PerfCounter.bulletUpdate.begin(); - Groups.bullet.update(); - - Groups.bullet.collide(); + if(!editor) Groups.bullet.update(); + if(!editor) Groups.bullet.collide(); PerfCounter.bulletUpdate.end(); PerfCounter.entityUpdate.end(); diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index c8c38e501f..9956548d63 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -992,6 +992,7 @@ public class NetServer implements ApplicationListener{ } public boolean isWaitingForPlayers(){ + if(state.is(State.menu)) return false; if(state.rules.pvp && !state.gameOver){ int used = 0; for(TeamData t : state.teams.getActive()){ diff --git a/core/src/mindustry/editor/data/MapAudioView.java b/core/src/mindustry/editor/data/MapAudioView.java index eaee3413e3..533b98e9a3 100644 --- a/core/src/mindustry/editor/data/MapAudioView.java +++ b/core/src/mindustry/editor/data/MapAudioView.java @@ -75,8 +75,6 @@ public class MapAudioView implements AssetView{ m.setPosition(value); } }); - //only ogg can seek - slider.touchable = asset.path.endsWith(".ogg") ? Touchable.enabled : Touchable.disabled; slider.visible(() -> { boolean valid = lastPlaying == m && m.isPlaying(); if(valid){ diff --git a/core/src/mindustry/editor/data/MapImagesView.java b/core/src/mindustry/editor/data/MapImagesView.java index a941c6a647..4104b77b2f 100644 --- a/core/src/mindustry/editor/data/MapImagesView.java +++ b/core/src/mindustry/editor/data/MapImagesView.java @@ -296,6 +296,7 @@ public class MapImagesView implements AssetView{ b.button(Icon.trash, istyle, () -> { ui.showConfirm("@asset.image.delete.confirm", () -> { Core.atlas.getRegionMap().remove(regionPrefix + image.name); + Core.atlas.getDrawables().remove(regionPrefix + image.name); getImages().remove(image); diag.rebuild(); }); diff --git a/core/src/mindustry/entities/EntityGroup.java b/core/src/mindustry/entities/EntityGroup.java index cccfbfdd41..5a9ecf35f6 100644 --- a/core/src/mindustry/entities/EntityGroup.java +++ b/core/src/mindustry/entities/EntityGroup.java @@ -91,6 +91,13 @@ public class EntityGroup implements Iterable{ } } + public void update(Boolf filter){ + for(index = 0; index < array.size; index++){ + var item = array.items[index]; + if(filter.get(item)) array.items[index].update(); + } + } + /** Calls {@link #fixedUpdate(int, int)} with a maximum of 10 updates per frame. */ public void fixedUpdate(int targetFps){ fixedUpdate(targetFps, 10); @@ -345,4 +352,9 @@ public class EntityGroup implements Iterable{ public Iterator iterator(){ return array.iterator(); } + + @Override + public String toString(){ + return array.toString(); + } } diff --git a/core/src/mindustry/entities/Fires.java b/core/src/mindustry/entities/Fires.java index b4c3c3b70b..18a838af5e 100644 --- a/core/src/mindustry/entities/Fires.java +++ b/core/src/mindustry/entities/Fires.java @@ -26,6 +26,7 @@ public class Fires{ fire.set(tile.worldx(), tile.worldy()); fire.add(); set(tile, fire); + Events.fire(Trigger.fireCreate); }else{ fire.lifetime = baseLifetime; fire.time = 0f; diff --git a/core/src/mindustry/entities/GroupDefs.java b/core/src/mindustry/entities/GroupDefs.java index 8ff63511ea..a612659dd3 100644 --- a/core/src/mindustry/entities/GroupDefs.java +++ b/core/src/mindustry/entities/GroupDefs.java @@ -4,16 +4,14 @@ import mindustry.annotations.Annotations.*; import mindustry.gen.*; class GroupDefs{ - @GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class, Bulletc.class}, update = true) G all; + @GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class, Bulletc.class, EffectStatec.class, Playerc.class}, update = true) G all; + @GroupDef(value = {EffectStatec.class}) G effect; @GroupDef(value = Playerc.class, mapping = true) G player; @GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet; @GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit; @GroupDef(value = Buildingc.class, update = true) G build; @GroupDef(value = Syncc.class, mapping = true) G sync; @GroupDef(value = Drawc.class) G draw; - @GroupDef(value = Firec.class) G fire; - @GroupDef(value = Puddlec.class) G puddle; @GroupDef(value = WeatherStatec.class) G weather; - @GroupDef(value = WorldLabelc.class, mapping = true) G label; @GroupDef(value = PowerGraphUpdaterc.class) G powerGraph; } diff --git a/core/src/mindustry/entities/comp/FireComp.java b/core/src/mindustry/entities/comp/FireComp.java index 7fee53d5e4..83658c4848 100644 --- a/core/src/mindustry/entities/comp/FireComp.java +++ b/core/src/mindustry/entities/comp/FireComp.java @@ -38,7 +38,6 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{ @Override public void update(){ - animation += Time.delta / ticksPerFrame; warmup += Time.delta; animation %= frames; diff --git a/core/src/mindustry/entities/comp/PuddleComp.java b/core/src/mindustry/entities/comp/PuddleComp.java index 24de4045a6..aa9a8426ab 100644 --- a/core/src/mindustry/entities/comp/PuddleComp.java +++ b/core/src/mindustry/entities/comp/PuddleComp.java @@ -88,7 +88,6 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc, Syncc{ //force removal without pool free Groups.all.remove(self()); Groups.draw.remove(self()); - Groups.puddle.remove(self()); added = false; return; } diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 2da853508a..a0b96124fc 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -22,6 +22,7 @@ public class EventType{ public enum Trigger{ shock, cannotUpgrade, + fireCreate, openConsole, blastFreeze, impactPower, diff --git a/core/src/mindustry/io/MapIO.java b/core/src/mindustry/io/MapIO.java index 4f085cb6fe..aa1e1aab3e 100644 --- a/core/src/mindustry/io/MapIO.java +++ b/core/src/mindustry/io/MapIO.java @@ -6,6 +6,7 @@ import arc.struct.*; import arc.util.io.*; import mindustry.content.*; import mindustry.core.*; +import mindustry.ctype.*; import mindustry.game.*; import mindustry.maps.*; import mindustry.world.*; @@ -94,9 +95,10 @@ public class MapIO{ CachedTile tile = new CachedTile(){ @Override public void setBlock(Block type){ - super.setBlock(type); - - int c = colorFor(block(), Blocks.air, Blocks.air, team()); + //do not super.setBlock as that affects the current world + this.block = type; + this.build = type.newBuilding().init(this, this.team(), block.update && !state.isEditor(), 0); + int c = colorFor(type, Blocks.air, Blocks.air, team()); if(c != black){ walls.setRaw(x, floors.height - 1 - y, c); floors.set(x, floors.height - 1 - y + 1, shade); @@ -105,7 +107,7 @@ public class MapIO{ }; if(ver.version >= 12) ver.skipChunk(stream); - ver.readRegion("content", stream, counter, ver::readContentHeader); + ver.readRegion("content", stream, counter, MapIO::readPreviewContentHeader); if(ver.version == 11) ver.skipChunk(stream); ver.readRegion("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){ @Override public void resize(int width, int height){} @@ -187,6 +189,27 @@ public class MapIO{ } } + private static void readPreviewContentHeader(DataInput stream) throws IOException{ + //reads content header while refusing to fire patch loaded event + int mapped = stream.readUnsignedByte(); + + MappableContent[][] map = new MappableContent[ContentType.all.length][0]; + + for(int i = 0; i < mapped; i++){ + ContentType type = ContentType.all[stream.readByte()]; + short total = stream.readShort(); + map[type.ordinal()] = new MappableContent[total]; + + for(int j = 0; j < total; j++){ + String name = stream.readUTF(); + //fallback only for blocks + map[type.ordinal()][j] = content.getByName(type, type == ContentType.block ? SaveFileReader.fallback.get(name, name) : name); + } + } + + content.setTemporaryMapper(map); + } + public static Pixmap generatePreview(Tiles tiles){ Pixmap pixmap = new Pixmap(tiles.width, tiles.height); for(int x = 0; x < pixmap.width; x++){ diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 87b09720ee..70c47fb56c 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -124,7 +124,8 @@ public class LExecutor{ public void load(LAssembler builder){ stop = false; nameMap = null; - vars = builder.vars.values().toSeq().retainAll(var -> !var.constant).toArray(LVar.class); + //retain constants that are links, which, by convention, don't start with @ (builtin) or _ (numeric constant) + vars = builder.vars.values().toSeq().retainAll(var -> !var.constant || var.name.charAt(0) != '_' && var.name.charAt(0) != '@').toArray(LVar.class); for(int i = 0; i < vars.length; i++){ vars[i].id = i; } diff --git a/core/src/mindustry/logic/LVar.java b/core/src/mindustry/logic/LVar.java index e6d6e0a7b7..ef3c913c04 100644 --- a/core/src/mindustry/logic/LVar.java +++ b/core/src/mindustry/logic/LVar.java @@ -107,6 +107,17 @@ public class LVar{ isobj = true; } + public void setlink(Object value){ + isobj = true; + if(value == null){ + objval = null; + constant = false; //convert a former linked block into a regular variable + }else{ + objval = value; + constant = true; //make this variable a linked block + } + } + public void set(LVar other){ isobj = other.isobj; // Setting a non-numeric value to @counter must preserve its numeric field diff --git a/core/src/mindustry/maps/filters/FilterOption.java b/core/src/mindustry/maps/filters/FilterOption.java index 1d7bd9f827..ccded80b0a 100644 --- a/core/src/mindustry/maps/filters/FilterOption.java +++ b/core/src/mindustry/maps/filters/FilterOption.java @@ -42,7 +42,7 @@ public abstract class FilterOption{ boolean display = true; SliderOption(String name, Floatp getter, Floatc setter, float min, float max){ - this(name, getter, setter, min, max, (max - min) / 200); + this(name, getter, setter, min, max, 0.00001f); } SliderOption(String name, Floatp getter, Floatc setter, float min, float max, float step){ diff --git a/core/src/mindustry/mod/DataImagePacker.java b/core/src/mindustry/mod/DataImagePacker.java index 718f9edad1..0eee692103 100644 --- a/core/src/mindustry/mod/DataImagePacker.java +++ b/core/src/mindustry/mod/DataImagePacker.java @@ -147,7 +147,6 @@ public class DataImagePacker{ packer.forceDispose(); //getRegions is intentionally not modified, it's a hassle to manage, O(n) to unapply, and not used anywhere important. there's no point. - //the drawable map isn't used, and thus not modified either Log.debug("[Patch Atlas] Time to pack: @ms", Time.elapsed()); } @@ -156,6 +155,7 @@ public class DataImagePacker{ if(addedRegions != null){ for(var region : addedRegions){ Core.atlas.getRegionMap().remove(region.name); + Core.atlas.getDrawables().remove(region.name); } addedRegions = null; } diff --git a/core/src/mindustry/ui/Menus.java b/core/src/mindustry/ui/Menus.java index a6a0095fc6..e7b7ee7fd2 100644 --- a/core/src/mindustry/ui/Menus.java +++ b/core/src/mindustry/ui/Menus.java @@ -379,8 +379,8 @@ public class Menus{ //internal use only @Remote(variants = Variant.both) public static void removeWorldLabel(int id){ - var label = Groups.label.getByID(id); - if(label != null){ + var label = Groups.sync.getByID(id); + if(label instanceof WorldLabelc){ label.remove(); } } diff --git a/core/src/mindustry/ui/builder/UiBuilder.java b/core/src/mindustry/ui/builder/UiBuilder.java index 477c46ab87..85746e1552 100644 --- a/core/src/mindustry/ui/builder/UiBuilder.java +++ b/core/src/mindustry/ui/builder/UiBuilder.java @@ -1,5 +1,6 @@ package mindustry.ui.builder; +import arc.graphics.*; import arc.struct.*; import arc.util.*; import arc.util.io.*; @@ -9,6 +10,7 @@ public class UiBuilder{ public static TableBuilder parse(String source){ return UiDslParser.parse(source); } public static TableBuilder table(){ return new TableBuilder(); } + public static StackBuilder stack(){ return new StackBuilder(); } public static PaneBuilder pane(){ return new PaneBuilder(); } public static LabelBuilder label(String text){ return new LabelBuilder().text(text); } public static ImageBuilder image(){ return new ImageBuilder(); } @@ -68,6 +70,8 @@ public class UiBuilder{ public T uniform(){ return prop(UiKey.uniform, true); } public T uniformX(){ return prop(UiKey.uniformX, true); } public T uniformY(){ return prop(UiKey.uniformY, true); } + public T color(Color color){ return prop(UiKey.color, color.toString()); } + public T color(String colorHex){ return prop(UiKey.color, colorHex); } public void write(Writes out){ out.b(type.ordinal()); @@ -95,6 +99,7 @@ public class UiBuilder{ return switch(type){ case table -> new TableBuilder(); case pane -> new PaneBuilder(); + case stack -> new StackBuilder(); case label -> new LabelBuilder(); case image -> new ImageBuilder(); case button -> new ButtonBuilder(); @@ -169,6 +174,11 @@ public class UiBuilder{ for(Entry e : entries) if(e.key == key && e.value instanceof Boolean b) return b; return def; } + + @Override + public String toString(){ + return UiDslWriter.write(this); + } } static class Entry{ @@ -212,6 +222,10 @@ public class UiBuilder{ } } + public static class StackBuilder extends ContainerBuilder{ + StackBuilder(){ super(UiKey.stack); } + } + public static class TableBuilder extends ContainerBuilder{ TableBuilder(){ super(UiKey.table); } @@ -249,6 +263,7 @@ public class UiBuilder{ public ButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); } public ButtonBuilder style(String style){ return prop(UiKey.style, style); } public ButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); } + public ButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } public static class ImageButtonBuilder extends NodeBuilder{ @@ -257,6 +272,7 @@ public class UiBuilder{ public ImageButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); } public ImageButtonBuilder style(String style){ return prop(UiKey.style, style); } public ImageButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); } + public ImageButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } public static class FieldBuilder extends NodeBuilder{ @@ -267,6 +283,7 @@ public class UiBuilder{ public FieldBuilder maxLength(int length){ return prop(UiKey.maxLength, (float)length); } public FieldBuilder style(String style){ return prop(UiKey.style, style); } public FieldBuilder enter(String result){ return prop(UiKey.enter, result); } + public FieldBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } public static class CheckBuilder extends NodeBuilder{ @@ -275,6 +292,7 @@ public class UiBuilder{ public CheckBuilder text(String text){ return prop(UiKey.text, text); } public CheckBuilder checked(boolean checked){ return prop(UiKey.checked, checked); } public CheckBuilder style(String style){ return prop(UiKey.style, style); } + public CheckBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } public static class SliderBuilder extends NodeBuilder{ @@ -285,6 +303,7 @@ public class UiBuilder{ public SliderBuilder step(float step){ return prop(UiKey.step, step); } public SliderBuilder defaultValue(float value){ return prop(UiKey.defaultValue, value); } public SliderBuilder style(String style){ return prop(UiKey.style, style); } + public SliderBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } public static class SpaceBuilder extends NodeBuilder{ @@ -300,5 +319,7 @@ public class UiBuilder{ public ButtonTableBuilder style(String style){ return prop(UiKey.style, style); } public ButtonTableBuilder clicked(String result){ return prop(UiKey.clicked, result); } + public ButtonTableBuilder margin(float m){ return prop(UiKey.margin, m); } + public ButtonTableBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } } } \ No newline at end of file diff --git a/core/src/mindustry/ui/builder/UiDslParser.java b/core/src/mindustry/ui/builder/UiDslParser.java index c0892103c1..71968d021f 100644 --- a/core/src/mindustry/ui/builder/UiDslParser.java +++ b/core/src/mindustry/ui/builder/UiDslParser.java @@ -20,7 +20,7 @@ public class UiDslParser{ UiDslParser p = new UiDslParser(source); p.parseStatementsInto(root); p.skipWs(); - if(!p.atEnd()) throw new SerializationException("Unexpected character '" + p.peek() + "' at index " + p.pos); + if(!p.atEnd()) throw new SerializationException("Unexpected character '" + p.peek() + "' at line " + p.line(p.pos)); return root; } @@ -34,17 +34,21 @@ public class UiDslParser{ private void parseStatement(NodeBuilder node){ skipWs(); - if(atEnd() || peek() == '"') throw new SerializationException("Expected identifier at index " + pos); + if(atEnd() || peek() == '"') throw new SerializationException("Expected identifier at line " + line(pos)); String ident = readBareToken(); - if(ident.isEmpty()) throw new SerializationException("Unexpected character '" + peek() + "' at index " + pos); + if(ident.isEmpty()) throw new SerializationException("Unexpected character '" + peek() + "' at line " + line(pos)); if(ident.equals("row")){ node.entries.add(new Entry(UiKey.row, true)); return; } - //TODO: throws on the client upon invalid value, that's probably fine? - UiKey key = UiKey.valueOf(ident); + UiKey key; + try{ + key = UiKey.valueOf(ident); + }catch(IllegalArgumentException e){ + throw new SerializationException("Unknown property: \"" + ident + "\" at line " + line(pos)); + } //TODO hack: everything before the row ordinal is a node type boolean isNodeType = key.ordinal() < UiKey.row.ordinal(); @@ -74,7 +78,7 @@ public class UiDslParser{ // "ident { ... }" - a node with a block if(!atEnd() && peek() == '{'){ pos++; - if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at index " + pos); + if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at line " + line(pos)); NodeBuilder child = NodeBuilder.create(key); parseStatementsInto(child); expect('}'); @@ -83,7 +87,7 @@ public class UiDslParser{ } // bare "ident" - childless node (e.g. "space") - if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at index " + pos); + if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at line " + line(pos)); node.entries.add(new Entry(key, NodeBuilder.create(key))); } @@ -117,7 +121,7 @@ public class UiDslParser{ } lastQuoted = false; String tok = readBareToken(); - if(tok.isEmpty()) throw new SerializationException("Expected value at index " + pos); + if(tok.isEmpty()) throw new SerializationException("Expected value at line " + line(pos)); return tok; } @@ -126,11 +130,16 @@ public class UiDslParser{ StringBuilder sb = new StringBuilder(); while(!atEnd() && peek() != '"'){ char c = src.charAt(pos); - if(c == '\\' && pos + 1 < src.length()){ sb.append(src.charAt(pos + 1)); pos += 2; continue; } + if(c == '\\' && pos + 1 < src.length()){ + char next = src.charAt(pos + 1); + sb.append(next == 'n' ? '\n' : next); //parse \n + pos += 2; + continue; + } sb.append(c); pos++; } - if(atEnd()) throw new SerializationException("Unterminated string"); + if(atEnd()) throw new SerializationException("Unterminated string starting at line " + line(pos)); pos++; // closing quote return sb.toString(); } @@ -159,7 +168,7 @@ public class UiDslParser{ } private void expect(char c){ - if(atEnd() || peek() != c) throw new SerializationException("Expected '" + c + "', found '" + (atEnd() ? "" : peek()) + "'"); + if(atEnd() || peek() != c) throw new SerializationException("Expected '" + c + "', found '" + (atEnd() ? "" : peek()) + "' at line " + line(pos)); pos++; } @@ -170,4 +179,14 @@ public class UiDslParser{ private char peek(){ return src.charAt(pos); } + + // counts newlines up to (not including) the given index to determine the 1-based line number + private int line(int index){ + int line = 1; + int end = Math.min(index, src.length()); + for(int i = 0; i < end; i++){ + if(src.charAt(i) == '\n') line++; + } + return line; + } } \ No newline at end of file diff --git a/core/src/mindustry/ui/builder/UiDslWriter.java b/core/src/mindustry/ui/builder/UiDslWriter.java new file mode 100644 index 0000000000..5ce4b90eb4 --- /dev/null +++ b/core/src/mindustry/ui/builder/UiDslWriter.java @@ -0,0 +1,127 @@ +package mindustry.ui.builder; + +import arc.struct.*; +import arc.util.*; +import mindustry.ui.builder.UiBuilder.*; + +/** Converts a parsed NodeBuilder tree back into UI DSL source text. */ +public class UiDslWriter{ + + /** Writes the root node's entries (no wrapping block, since root is an implicit table body). */ + public static String write(NodeBuilder root){ + StringBuilder sb = new StringBuilder(); + writeEntries(sb, root.entries, 0); + return sb.toString(); + } + + private static void writeEntries(StringBuilder sb, Seq entries, int depth){ + for(Entry e : entries){ + // "row" marker + if(e.key == UiKey.row && e.value instanceof Boolean){ + indent(sb, depth); + sb.append("row\n"); + continue; + } + + // nested node (child added via block, shorthand, or bare) + if(e.value instanceof NodeBuilder child){ + writeChildNode(sb, child, depth); + continue; + } + + // plain property: key: value + indent(sb, depth); + sb.append(e.key.name()).append(": ").append(formatValue(e.value)).append('\n'); + } + } + + private static void writeChildNode(StringBuilder sb, NodeBuilder child, int depth){ + indent(sb, depth); + sb.append(child.type.name()); + + UiKey shorthandKey = shorthandType(child.type); + Entry shorthand = null; + for(Entry e : child.entries){ + if(e.key == shorthandKey && e.value instanceof String){ + shorthand = e; + break; + } + } + + Seq rest = new Seq<>(); + for(Entry e : child.entries){ + if(e != shorthand) rest.add(e); + } + + if(shorthand != null){ + sb.append(": ").append(formatValue(shorthand.value)); + } + + if(rest.isEmpty()){ + sb.append('\n'); + }else{ + if(shorthand != null) sb.append(' '); + sb.append("{\n"); + writeEntries(sb, rest, depth + 1); + indent(sb, depth); + sb.append("}\n"); + } + } + + /** Mirrors UiDslParser's getShorthandType: image uses region, everything else uses text. */ + private static UiKey shorthandType(UiKey key){ + return switch(key){ + case image -> UiKey.region; + default -> UiKey.text; + }; + } + + private static String formatValue(Object value){ + if(value instanceof Boolean b) return b ? "true" : "false"; + if(value instanceof Float f) return formatFloat(f); + if(value instanceof String s) return needsQuotes(s) ? quote(s) : s; + throw new IllegalArgumentException("Unsupported value type: " + value); + } + + private static String formatFloat(float f){ + if(f == (long)f) return Long.toString((long)f); + return Float.toString(f); + } + + /** A bare token round-trips correctly only if the parser wouldn't reinterpret it as a bool/number/keyword. */ + private static boolean needsQuotes(String s){ + if(s.isEmpty()) return true; + if(s.equals("true") || s.equals("false")) return true; + if(!Float.isNaN(Strings.parseFloat(s, Float.NaN))) return true; + + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(Character.isWhitespace(c) || c == '{' || c == '}' || c == ':' || c == '"' || c == '\\'){ + return true; + } + } + // avoid accidentally starting a line comment + return s.startsWith("//"); + } + + private static String quote(String s){ + StringBuilder sb = new StringBuilder(); + sb.append('"'); + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(c == '"' || c == '\\'){ + sb.append('\\').append(c); + }else if(c == '\n'){ + sb.append("\\n"); + }else{ + sb.append(c); + } + } + sb.append('"'); + return sb.toString(); + } + + private static void indent(StringBuilder sb, int depth){ + for(int i = 0; i < depth; i++) sb.append(" "); + } +} \ No newline at end of file diff --git a/core/src/mindustry/ui/builder/UiHotReload.java b/core/src/mindustry/ui/builder/UiHotReload.java new file mode 100644 index 0000000000..a485f015ca --- /dev/null +++ b/core/src/mindustry/ui/builder/UiHotReload.java @@ -0,0 +1,64 @@ +package mindustry.ui.builder; + +import arc.files.*; +import arc.util.*; +import mindustry.*; +import mindustry.ui.*; +import mindustry.ui.dialogs.*; + +/** Utility class for showing a MSUI dialog box that automatically reloads changes for a certain file. + * Desktop only. Usage: Just run UiHotReload.show() in the JS console. */ +public class UiHotReload{ + + /** Shows a file picker for the dialog box. */ + public static void show(){ + + FileChooser.open("msui").submit(file -> { + long[] lastUpdate = {file.lastModified()}, debounceTime = {0}; + boolean[] modified = {false}; + + BaseDialog dialog = new BaseDialog("Mindustry DSL Preview"); + dialog.update(() -> { + if(lastUpdate[0] != file.lastModified()){ + lastUpdate[0] = file.lastModified(); + modified[0] = true; + debounceTime[0] = Time.millis(); + } + + if(modified[0] && Time.timeSinceMillis(debounceTime[0]) > 100){ + modified[0] = false; + reload(file, dialog); + } + }); + reload(file, dialog); + dialog.addCloseButton(); + dialog.show(); + }); + } + + static void reload(Fi file, BaseDialog dialog){ + try{ + dialog.cont.clear(); + dialog.cont.background(null); + UiTreeBuilder.build(dialog.cont, UiBuilder.parse(file.readString()), result -> { + Vars.ui.showInfo(result.toString()); + }); + }catch(Exception e){ + dialog.cont.clearChildren(); + if(e.getMessage() != null && e.getMessage().contains("line ")){ + try{ + String text = file.readString(); + int actualLine = Strings.parseInt(e.getMessage().substring(e.getMessage().indexOf("line ") + 5)); + if(actualLine > 0){ + String[] lines = text.split("\n"); + if(actualLine < lines.length){ + dialog.cont.add("[red]Error:[] " + e.getMessage() + "\n\nLine: [red]" + lines[actualLine - 1].trim()); + } + } + }catch(Exception ignored){ + } + } + if(dialog.cont.getChildren().isEmpty()) dialog.cont.add("[red]Error reading file![] \n" + Strings.getStackTrace(e)).center().labelAlign(Align.center); + } + } +} diff --git a/core/src/mindustry/ui/builder/UiKey.java b/core/src/mindustry/ui/builder/UiKey.java index 78b4abacf7..f5cae29005 100644 --- a/core/src/mindustry/ui/builder/UiKey.java +++ b/core/src/mindustry/ui/builder/UiKey.java @@ -3,10 +3,11 @@ package mindustry.ui.builder; /** Enum used for encoding properties as single bytes instead of passing strings around. */ public enum UiKey{ // node types - table, pane, label, image, button, imageButton, field, check, slider, space, defaults, buttonTable, row, //NOTE: all node types must be before 'row' + table, pane, stack, label, image, button, imageButton, field, check, slider, space, defaults, buttonTable, row, //NOTE: all node types must be before 'row' // node-specific properties - text, wrap, region, icon, scaling, background, margin, id, hint, maxLength, checked, min, max, step, defaultValue, clicked, enter, style, group, condition, + text, wrap, region, icon, scaling, background, margin, id, hint, maxLength, checked, min, max, step, + defaultValue, clicked, enter, style, group, condition, color, disabled, // cell properties grow, growX, growY, fill, fillX, fillY, expand, expandX, expandY, diff --git a/core/src/mindustry/ui/builder/UiTreeBuilder.java b/core/src/mindustry/ui/builder/UiTreeBuilder.java index 60bec1be2f..29f3fdaeda 100644 --- a/core/src/mindustry/ui/builder/UiTreeBuilder.java +++ b/core/src/mindustry/ui/builder/UiTreeBuilder.java @@ -2,6 +2,7 @@ package mindustry.ui.builder; import arc.*; import arc.func.*; +import arc.graphics.*; import arc.input.*; import arc.scene.*; import arc.scene.event.*; @@ -15,6 +16,7 @@ import arc.scene.ui.Slider.*; import arc.scene.ui.TextButton.*; import arc.scene.ui.TextField.*; import arc.scene.ui.layout.*; +import arc.scene.utils.*; import arc.struct.*; import arc.util.*; import mindustry.gen.*; @@ -51,7 +53,7 @@ public class UiTreeBuilder{ String cond = node.str(UiKey.condition); if(cond != null && !evalCondition(cond)) continue; // condition false - node is not added at all - Cell cell = addNode(table, entry.key, node, ctx); + Cell cell = entry.key == UiKey.space ? table.add() : table.add(addNode(entry.key, node, ctx)); if(cell == null) continue; // unknown/unsupported node - skip for forward compat for(Entry d : defaults) applyCellProp(cell, d.key, d.value); @@ -62,8 +64,7 @@ public class UiTreeBuilder{ String id = node.str(UiKey.id); if(id != null){ cell.name(id); // assigns element.name via Cell.name() - Object element = cell.get(); - if(element instanceof Element el) ctx.idElements.put(id, el); + ctx.idElements.put(id, cell.get()); } }else{ @@ -73,7 +74,7 @@ public class UiTreeBuilder{ } /** Constructs the element for one node, adds it to the parent table, and returns its cell. */ - private static Cell addNode(Table table, UiKey type, NodeBuilder node, BuildContext ctx){ + private static Element addNode(UiKey type, NodeBuilder node, BuildContext ctx){ return switch(type){ case table -> { String bg = node.str(UiKey.background); @@ -82,16 +83,38 @@ public class UiTreeBuilder{ Float margin = node.num(UiKey.margin); if(margin != null) t.margin(margin); - Cell cell = table.add(t); buildInto(t, node.entries, ctx); // recurse with a fresh defaults scope - yield cell; + yield t; + } + case stack -> { + Stack stack = new Stack(); + stack.touchable = Touchable.childrenOnly; + + //simplified creation compared to table; no cells are involved + for(Entry entry : node.entries){ + if(entry.value instanceof NodeBuilder child){ + String cond = child.str(UiKey.condition); + if(cond != null && !evalCondition(cond)) continue; + + var element = addNode(entry.key, child, ctx); + if(element == null) continue; // unknown/unsupported node - skip for forward compat + String id = node.str(UiKey.id); + + if(id != null){ + element.name = id; + ctx.idElements.put(id, element); + } + stack.add(element); + } + } + yield stack; } case pane -> { Table inner = new Table(); buildInto(inner, node.entries, ctx); ScrollPane pane = new ScrollPane(inner); applyStyle(node, ScrollPaneStyle.class, pane::setStyle); - yield table.add(pane); + yield pane; } case label -> { Label label = new Label(node.str(UiKey.text, "")); @@ -99,7 +122,7 @@ public class UiTreeBuilder{ String labelAlign = node.str(UiKey.labelAlign); if(labelAlign != null) label.setAlignment(parseAlign(labelAlign)); applyStyle(node, LabelStyle.class, label::setStyle); - yield table.add(label); + yield label; } case image -> { Image image = new Image(findDrawable(node.str(UiKey.region, node.str(UiKey.icon, "error")))); @@ -110,7 +133,7 @@ public class UiTreeBuilder{ image.setScaling(scaling); }catch(Exception ignored){} } - yield table.add(image); + yield image; } case button -> { TextButton btn = new TextButton(node.str(UiKey.text, "")); @@ -122,14 +145,14 @@ public class UiTreeBuilder{ btn.getCells().reverse(); } wireButton(btn, node, ctx); - yield table.add(btn); + yield btn; } case imageButton -> { String icon = node.str(UiKey.icon); ImageButton btn = new ImageButton(icon != null ? findDrawable(icon) : null); applyStyle(node, ImageButtonStyle.class, btn::setStyle); wireButton(btn, node, ctx); - yield table.add(btn); + yield btn; } case field -> { TextField field = new TextField(node.str(UiKey.text, "")); @@ -140,7 +163,7 @@ public class UiTreeBuilder{ String enter = node.str(UiKey.enter); if(enter != null) field.keyDown(KeyCode.enter, () -> fireResult(ctx, enter)); applyStyle(node, TextFieldStyle.class, field::setStyle); - yield table.add(field); + yield field; } case check -> { CheckBox box = new CheckBox(node.str(UiKey.text, "")); @@ -148,7 +171,7 @@ public class UiTreeBuilder{ String group = node.str(UiKey.group); if(group != null) ctx.group(group).add(box); applyStyle(node, CheckBoxStyle.class, box::setStyle); - yield table.add(box); + yield box; } case slider -> { Slider slider = new Slider(node.num(UiKey.min, 0f), node.num(UiKey.max, 1f), node.num(UiKey.step, 0.1f), false); @@ -173,17 +196,17 @@ public class UiTreeBuilder{ label.setAlignment(Align.center); label.touchable = Touchable.disabled; label.setStyle(Styles.outlineLabel); - yield table.add(new Stack(slider, label)); + yield new Stack(slider, label); } - case space -> table.add(); case buttonTable -> { Button btn = new Button(); //can use any button style if(!applyStyle(node, TextButtonStyle.class, btn::setStyle)) applyStyle(node, ImageButtonStyle.class, btn::setStyle); - Cell