Make dynamic lights config less heavy (#2051)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-08-30 13:02:07 -07:00
1 parent 67f2484976
commit 269b7245d3
12 files changed
+535 -157

No files matched your search

@@ -0,0 +1,96 @@
package com.gtnewhorizons.angelica.client.gui;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
public record DynamicLightsEntityFilter(List<String> modPrefixes, List<String> nameTerms, DynamicLightsEntityFilter.State state) {
public enum State {
ANY,
ENABLED,
DISABLED
}
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
private static final DynamicLightsEntityFilter MATCH_ALL = new DynamicLightsEntityFilter(List.of(), List.of(), State.ANY);
public DynamicLightsEntityFilter {
modPrefixes = List.copyOf(modPrefixes);
nameTerms = List.copyOf(nameTerms);
}
public static DynamicLightsEntityFilter parse(String query) {
if (query == null) {
return MATCH_ALL;
}
final var normalized = query.strip().toLowerCase(Locale.ROOT);
if (normalized.isEmpty()) {
return MATCH_ALL;
}
final var modPrefixes = new ArrayList<String>();
final var nameTerms = new ArrayList<String>();
var wantEnabled = false;
var wantDisabled = false;
for (final var token : WHITESPACE.split(normalized)) {
switch (token) {
case "$on" -> wantEnabled = true;
case "$off" -> wantDisabled = true;
default -> {
if (token.length() > 1 && token.charAt(0) == '@') {
modPrefixes.add(token.substring(1));
} else {
nameTerms.add(token);
}
}
}
}
final var state = wantEnabled == wantDisabled ? State.ANY : wantEnabled ? State.ENABLED : State.DISABLED;
if (modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY) {
return MATCH_ALL;
}
return new DynamicLightsEntityFilter(modPrefixes, nameTerms, state);
}
public boolean isMatchAll() {
return modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY;
}
public boolean matches(String lowerName, String lowerModId, boolean enabled) {
return matchesState(enabled) && matchesAnyMod(lowerModId) && containsAllTerms(lowerName);
}
private boolean matchesState(boolean enabled) {
return switch (state) {
case ANY -> true;
case ENABLED -> enabled;
case DISABLED -> !enabled;
};
}
private boolean matchesAnyMod(String lowerModId) {
if (modPrefixes.isEmpty()) {
return true;
}
for (final var prefix : modPrefixes) {
if (lowerModId.startsWith(prefix)) {
return true;
}
}
return false;
}
private boolean containsAllTerms(String lowerName) {
for (final var term : nameTerms) {
if (!lowerName.contains(term)) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,242 @@
package com.gtnewhorizons.angelica.client.gui;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
import net.coderbot.iris.gui.element.IrisGuiSlot;
import net.coderbot.iris.gui.element.widget.IrisButton;
public class DynamicLightsEntityScreen extends GuiScreen {
private static final String ENABLED_PREFIX = EnumChatFormatting.GREEN + "[x] " + EnumChatFormatting.WHITE;
private static final String DISABLED_PREFIX = EnumChatFormatting.DARK_GRAY + "[ ] " + EnumChatFormatting.GRAY;
private static List<EntityTypeEntry> entityTypes;
private final GuiScreen parent;
private final String title = I18n.format("options.dynamiclights.entities.title");
private final String info = I18n.format("options.dynamiclights.entities.info");
private List<EntityTypeEntry> displayed;
private String countLabel;
private String search = "";
private boolean dirty;
private GuiTextField searchBox;
private EntityRowList rowList;
private IrisButton toggleButton;
public DynamicLightsEntityScreen(GuiScreen parent) {
this.parent = parent;
this.refilter();
}
private static List<EntityTypeEntry> entityTypes() {
if (entityTypes == null) {
entityTypes = EntityLightConfig.getAllEntityTypes();
}
return entityTypes;
}
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
this.searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 150, 30, 300, 20);
this.searchBox.setMaxStringLength(64);
this.searchBox.setText(this.search);
this.rowList = new EntityRowList();
this.toggleButton = new IrisButton(this.width / 2 - 154, this.height - 27, 150, 20, "", button -> this.toggleVisible());
this.updateToggleButton();
this.buttonList.add(this.toggleButton);
this.buttonList.add(new IrisButton(this.width / 2 + 4, this.height - 27, 150, 20, I18n.format("gui.done"), button -> this.onClose()));
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
private void onClose() {
if (this.dirty) {
EntityLightConfig.save();
this.dirty = false;
}
this.mc.displayGuiScreen(this.parent);
}
private void refilter() {
final var filter = DynamicLightsEntityFilter.parse(this.search);
if (filter.isMatchAll()) {
this.displayed = entityTypes();
} else {
final var matches = new ArrayList<EntityTypeEntry>();
for (final var entry : entityTypes()) {
if (filter.matches(entry.getSearchName(), entry.getSearchModId(), EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()))) {
matches.add(entry);
}
}
this.displayed = matches;
}
this.countLabel = this.displayed.size() + " / " + entityTypes().size();
this.updateToggleButton();
}
private boolean anyDisplayedEnabled() {
for (final var entry : this.displayed) {
if (EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass())) {
return true;
}
}
return false;
}
private void updateToggleButton() {
if (this.toggleButton == null) {
return;
}
final var subset = this.displayed.size() != entityTypes().size();
final var key = this.anyDisplayedEnabled()
? (subset ? "options.dynamiclights.entities.disableshown" : "options.dynamiclights.entities.disableall")
: (subset ? "options.dynamiclights.entities.enableshown" : "options.dynamiclights.entities.enableall");
this.toggleButton.displayString = I18n.format(key);
this.toggleButton.enabled = !this.displayed.isEmpty();
}
private void toggleVisible() {
final var enable = !this.anyDisplayedEnabled();
for (final var entry : this.displayed) {
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), enable);
}
this.dirty = true;
this.refilter();
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
this.drawDefaultBackground();
this.rowList.drawScreen(mouseX, mouseY, partialTicks);
super.drawScreen(mouseX, mouseY, partialTicks);
drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 0xFFFFFF);
drawCenteredString(this.fontRendererObj, this.info, this.width / 2, 20, 0xA0A0A0);
this.searchBox.drawTextBox();
if (!this.searchBox.isFocused() && this.search.isEmpty()) {
this.drawString(this.fontRendererObj, I18n.format("options.dynamiclights.entities.search"), this.width / 2 - 144, 36, 0x808080);
}
drawCenteredString(this.fontRendererObj, this.countLabel, this.width / 2, this.height - 39, 0x808080);
}
@Override
public void updateScreen() {
this.searchBox.updateCursorCounter();
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (!this.rowList.mouseClicked(mouseX, mouseY, mouseButton)) {
super.mouseClicked(mouseX, mouseY, mouseButton);
}
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
protected void mouseMovedOrUp(int mouseX, int mouseY, int state) {
if (state == -1 || !this.rowList.mouseReleased(mouseX, mouseY, Mouse.getEventButton())) {
super.mouseMovedOrUp(mouseX, mouseY, state);
}
}
@Override
protected void actionPerformed(GuiButton button) {
if (button.enabled && button instanceof IrisButton irisButton) {
irisButton.onPress();
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (this.searchBox.isFocused()) {
if (keyCode == Keyboard.KEY_ESCAPE) {
this.searchBox.setFocused(false);
return;
}
this.searchBox.textboxKeyTyped(typedChar, keyCode);
if (!this.searchBox.getText().equals(this.search)) {
this.search = this.searchBox.getText();
this.refilter();
}
return;
}
if (keyCode == Keyboard.KEY_ESCAPE) {
this.onClose();
return;
}
super.keyTyped(typedChar, keyCode);
}
private class EntityRowList extends IrisGuiSlot {
EntityRowList() {
super(DynamicLightsEntityScreen.this.mc, DynamicLightsEntityScreen.this.width, DynamicLightsEntityScreen.this.height, 56, DynamicLightsEntityScreen.this.height - 44, 18);
this.setRenderBackground(false);
}
@Override
protected int getSize() {
return displayed.size();
}
@Override
protected boolean isSelected(int index) {
return false;
}
@Override
protected void drawBackground() {}
@Override
public int getListWidth() {
return Math.min(400, DynamicLightsEntityScreen.this.width - 40);
}
@Override
protected boolean elementClicked(int index, boolean doubleClick, int mouseX, int mouseY, int button) {
if (button != 0 || index >= displayed.size()) {
return false;
}
final var entry = displayed.get(index);
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), !EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()));
dirty = true;
updateToggleButton();
return true;
}
@Override
protected void drawSlot(int index, int x, int y, int slotHeight, Tessellator tessellator, int mouseX, int mouseY) {
final var entry = displayed.get(index);
final var enabled = EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass());
final var label = (enabled ? ENABLED_PREFIX : DISABLED_PREFIX) + entry.getDisplayName() + EnumChatFormatting.DARK_GRAY + " (" + entry.getModId() + ")";
drawString(fontRendererObj, fontRendererObj.trimStringToWidth(label, this.getListWidth() - 8), x, y + 5, 0xFFFFFF);
}
}
}
@@ -1,105 +1,32 @@
package com.gtnewhorizons.angelica.client.gui;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfigStorage;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
import jss.notfine.core.Settings;
import me.jellysquid.mods.sodium.client.gui.options.Option;
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
import me.jellysquid.mods.sodium.client.gui.options.SubScreenOption;
import net.minecraft.client.resources.I18n;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class DynamicLightsOptionPages {
public static OptionPage dynamicLights() {
if (!DynamicLights.configEnabled) {
return null;
}
List<OptionGroup> groups = new ArrayList<>();
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS.option)
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
.build());
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
.build());
groups.add(OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
.build());
// Entity type toggles - grouped by mod ID
List<EntityTypeEntry> allEntities = EntityLightConfig.getAllEntityTypes();
Map<String, List<EntityTypeEntry>> byMod = new LinkedHashMap<>();
for (EntityTypeEntry entry : allEntities) {
byMod.computeIfAbsent(entry.getModId(), _ -> new ArrayList<>()).add(entry);
}
// Create a group for each mod's entities
for (Map.Entry<String, List<EntityTypeEntry>> modEntry : byMod.entrySet()) {
String modId = modEntry.getKey();
List<EntityTypeEntry> entities = modEntry.getValue();
OptionGroup.Builder groupBuilder = OptionGroup.createBuilder();
for (EntityTypeEntry entity : entities) {
Option<Boolean> option = createEntityOption(entity);
if (option != null) {
groupBuilder.add(option);
}
}
// Only add group if it has options
OptionGroup group = groupBuilder.build();
if (!group.getOptions().isEmpty()) {
groups.add(group);
}
}
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.copyOf(groups));
}
private static Option<Boolean> createEntityOption(EntityTypeEntry entry) {
final String className = entry.getClassName();
// Resolve class once during option creation, not on every get/set
Class<?> resolvedClass;
try {
resolvedClass = Class.forName(className);
} catch (ClassNotFoundException e) {
// Class not loaded, skip this option
return null;
}
final Class<?> entityClass = resolvedClass;
return OptionImpl.createBuilder(Boolean.class, EntityLightConfigStorage.INSTANCE)
.setName(entry.getDisplayName() + " (" + entry.getModId() + ")")
.setTooltip(I18n.format("options.dynamiclights.entity.tooltip", entry.getDisplayName()))
.setControl(TickBoxControl::new)
.setBinding(
(_, value) -> EntityLightConfig.setEntityTypeEnabled(entityClass, value),
storage -> EntityLightConfig.isEntityTypeEnabled(entityClass)
)
.setImpact(OptionImpact.LOW)
.build();
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.of(
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS.option)
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
.build(),
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
.build(),
OptionGroup.createBuilder()
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
.build(),
OptionGroup.createBuilder()
.add(new SubScreenOption(I18n.format("options.dynamiclights.entities"), I18n.format("options.dynamiclights.entities.tooltip"), DynamicLightsEntityScreen::new))
.build()));
}
}
@@ -71,7 +71,6 @@ public final class EntityLightConfig {
}
}
@SuppressWarnings("unchecked")
public static List<EntityTypeEntry> getAllEntityTypes() {
List<EntityTypeEntry> entries = new ArrayList<>();
Set<String> seenClasses = new HashSet<>();
@@ -88,9 +87,8 @@ public final class EntityLightConfig {
String modId = getModIdForEntity(entityClass, entityId);
String displayName = getDisplayName(entityId, entityClass);
boolean enabled = !disabledClassNames.contains(className);
entries.add(new EntityTypeEntry(displayName, className, modId, enabled));
entries.add(new EntityTypeEntry(displayName, entityClass, modId));
}
// Sort by mod ID, then by display name
@@ -1,20 +0,0 @@
package com.gtnewhorizons.angelica.dynamiclights.config;
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
public class EntityLightConfigStorage implements OptionStorage<EntityLightConfigStorage> {
public static final EntityLightConfigStorage INSTANCE = new EntityLightConfigStorage();
private EntityLightConfigStorage() {}
@Override
public EntityLightConfigStorage getData() {
return this;
}
@Override
public void save() {
EntityLightConfig.save();
}
}
@@ -1,42 +1,47 @@
package com.gtnewhorizons.angelica.dynamiclights.config;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
public class EntityTypeEntry {
private final String displayName;
private final String className;
private final String modId;
private boolean enabled;
public final class EntityTypeEntry {
public EntityTypeEntry(@NotNull String displayName, @NotNull String className, @NotNull String modId, boolean enabled) {
private final String displayName;
private final Class<?> entityClass;
private final String modId;
private final String searchName;
private final String searchModId;
public EntityTypeEntry(@NotNull String displayName, @NotNull Class<?> entityClass, @NotNull String modId) {
this.displayName = displayName;
this.className = className;
this.entityClass = entityClass;
this.modId = modId;
this.enabled = enabled;
this.searchName = displayName.toLowerCase(Locale.ROOT);
this.searchModId = modId.toLowerCase(Locale.ROOT);
}
public String getDisplayName() {
return displayName;
}
public String getClassName() {
return className;
public Class<?> getEntityClass() {
return entityClass;
}
public String getModId() {
return modId;
}
public boolean isEnabled() {
return enabled;
public String getSearchName() {
return searchName;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
public String getSearchModId() {
return searchModId;
}
@Override
public String toString() {
return displayName + " (" + modId + ") [" + (enabled ? "ON" : "OFF") + "]";
return displayName + " (" + modId + ")";
}
}
@@ -202,16 +202,7 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
}
private void updateControls() {
boolean hasChanges = this.getAllOptions()
.anyMatch(Option::hasChanged);
for (OptionPage page : this.pages) {
for (Option<?> option : page.getOptions()) {
if (option.hasChanged()) {
hasChanges = true;
}
}
}
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
this.applyButton.setEnabled(hasChanges);
this.undoButton.setVisible(hasChanges);
@@ -54,7 +54,6 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
protected OptionPage shaderPacks;
public SodiumOptionsGUI(GuiScreen prevScreen) {
this.prevScreen = prevScreen;
@@ -67,10 +66,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
// Add Dynamic Lights page if enabled
if (DynamicLights.configEnabled) {
var dlPage = DynamicLightsOptionPages.dynamicLights();
if (dlPage != null) {
this.pages.add(dlPage);
}
this.pages.add(DynamicLightsOptionPages.dynamicLights());
}
if (Iris.enabled) {
@@ -194,15 +190,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
.findFirst()
.orElse(null);
boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
for (OptionPage page : this.pages) {
for (Option<?> option : page.getOptions()) {
if (option.hasChanged()) {
hasChanges = true;
}
}
}
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
this.applyButton.setEnabled(hasChanges);
this.undoButton.setVisible(hasChanges);
@@ -0,0 +1,149 @@
package me.jellysquid.mods.sodium.client.gui.options;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import me.jellysquid.mods.sodium.client.gui.options.control.Control;
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
import me.jellysquid.mods.sodium.client.gui.options.control.element.ControlElementFactory;
import me.jellysquid.mods.sodium.client.gui.options.control.element.SodiumControlElement;
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
import me.jellysquid.mods.sodium.client.util.Dim2i;
/** "Option" that opens a sub screen */
public class SubScreenOption implements Option<Void> {
private static final int ARROW_HOVERED = 0xFF94E4D3;
private static final int ARROW_IDLE = 0xFFAAAAAA;
private static final String ARROW = ">";
private static final OptionStorage<Void> NO_STORAGE = new OptionStorage<Void>() {
@Override
public Void getData() {
return null;
}
@Override
public void save() {}
};
private final String name;
private final String tooltip;
private final Function<GuiScreen, GuiScreen> destination;
private final Control<Void> control = new SubScreenControl(this);
public SubScreenOption(String name, String tooltip, Function<GuiScreen, GuiScreen> destination) {
this.name = name;
this.tooltip = tooltip;
this.destination = destination;
}
private void open() {
final var mc = Minecraft.getMinecraft();
mc.displayGuiScreen(this.destination.apply(mc.currentScreen));
}
@Override
public String getName() {
return this.name;
}
@Override
public String getTooltip() {
return this.tooltip;
}
@Override
public OptionImpact getImpact() {
return null;
}
@Override
public Control<Void> getControl() {
return this.control;
}
@Override
public Void getValue() {
return null;
}
@Override
public void setValue(Void value) {}
@Override
public void reset() {}
@Override
public OptionStorage<?> getStorage() {
return NO_STORAGE;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public boolean hasChanged() {
return false;
}
@Override
public void applyChanges() {}
@Override
public Collection<OptionFlag> getFlags() {
return Collections.emptySet();
}
private record SubScreenControl(SubScreenOption target) implements Control<Void> {
@Override
public Option<Void> getOption() {
return this.target;
}
@Override
public ControlElement<Void> createElement(Dim2i dim, ControlElementFactory factory) {
return new SubScreenElement(this.target, dim);
}
@Override
public int getMaxWidth() {
return 30;
}
}
private static class SubScreenElement extends SodiumControlElement<Void> {
private final SubScreenOption target;
SubScreenElement(SubScreenOption option, Dim2i dim) {
super(option, dim);
this.target = option;
}
@Override
public void render(int mouseX, int mouseY, float delta) {
super.render(mouseX, mouseY, delta);
final var width = this.font.getStringWidth(ARROW);
this.drawString(ARROW, this.dim.getLimitX() - width - 6, this.dim.getCenterY() - 4, this.hovered ? ARROW_HOVERED : ARROW_IDLE);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button != 0 || !this.dim.containsCursor(mouseX, mouseY)) return false;
this.playClickSound();
this.target.open();
return true;
}
}
}
@@ -190,10 +190,14 @@ options.dynamic_lights_cull_timeout=Culling Timeout (ticks)
options.dynamic_lights_cull_timeout.tooltip=Maximum ticks a hidden chunk can wait before being forced to rebuild. Lower = more responsive but more GPU work.
options.dynamiclights.page=Dynamic Lights
options.dynamiclights.entities=Configure Entities...
options.dynamiclights.entities.tooltip=Choose which entity types emit dynamic light.
options.dynamiclights.entities.title=Dynamic Lights - Entities
options.dynamiclights.entities.info=Toggle which entity types emit dynamic light. Some entries are for mod compatibility.
options.dynamiclights.entities.search=Search (@mod, $on, $off)
options.dynamiclights.entity.tooltip=Toggle dynamic lighting for %s entities. Disable to improve performance if this entity type emits unwanted lights.
options.dynamiclights.entities.enableall=Enable All
options.dynamiclights.entities.disableall=Disable All
options.dynamiclights.entities.enableshown=Enable Shown
options.dynamiclights.entities.disableshown=Disable Shown
options.angelica.search.no_results=No matching options
options.angelica.fontconfig=Font Configuration...
@@ -161,7 +161,6 @@ options.dynamiclights.entities=エンティティの設定...
options.dynamiclights.entities.title=ダイナミックライト- エンティティ
options.dynamiclights.entities.info=ダイナミックライトを適用するエンティティの種類を設定します。一部の項目はMODとの互換性のために設定されています。
options.dynamiclights.entities.search=検索 (@mod, $on, $off)
options.dynamiclights.entity.tooltip=%sのエンティティのダイナミックライトを設定します。そのエンティティタイプが不要な光を発する場合は、パフォーマンス向上のために無効にしてください。
options.angelica.search.no_results=該当する設定が見つかりません
options.angelica.fontconfig=フォント設定...
@@ -164,7 +164,6 @@ options.dynamiclights.entities=配置实体……
options.dynamiclights.entities.title=动态光源 - 实体
options.dynamiclights.entities.info=切换哪些实体类型发射动态光。部分条目用于模组兼容性。
options.dynamiclights.entities.search=搜索(@模组, $开启, $关闭)
options.dynamiclights.entity.tooltip=切换 %s 实体的动态光照。如果此类型实体产生不必要光照,禁用以提升性能。
options.angelica.search.no_results=无匹配选项
options.angelica.fontconfig=字体配置...