Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mods screen initialization being slow with many mods #790

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/main/java/com/terraformersmc/modmenu/ModMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -59,21 +60,31 @@ public class ModMenu implements ClientModInitializer {
public static final boolean DEV_ENVIRONMENT = FabricLoader.getInstance().isDevelopmentEnvironment();
public static final boolean TEXT_PLACEHOLDER_COMPAT = FabricLoader.getInstance().isModLoaded("placeholder-api");

public static Screen getConfigScreen(String modid, Screen menuScreen) {
public static boolean hasConfigScreen(String modId) {
return getConfigScreenFactory(modId) != null;
}

public static @Nullable Screen getConfigScreen(String modId, Screen parent) {
ConfigScreenFactory<?> factory = getConfigScreenFactory(modId);
if (factory != null) {
return factory.create(parent);
}
return null;
}

private static @Nullable ConfigScreenFactory<?> getConfigScreenFactory(String modId) {
if (ModMenuConfig.HIDDEN_CONFIGS.getValue().contains(modId)) {
return null;
}

for (ModMenuApi api : apiImplementations) {
var factoryProviders = api.getProvidedConfigScreenFactories();
if (!factoryProviders.isEmpty()) {
factoryProviders.forEach(configScreenFactories::putIfAbsent);
}
}
if (ModMenuConfig.HIDDEN_CONFIGS.getValue().contains(modid)) {
return null;
}
ConfigScreenFactory<?> factory = configScreenFactories.get(modid);
if (factory != null) {
return factory.create(menuScreen);
}
return null;

return configScreenFactories.get(modId);
}

@Override
Expand Down
55 changes: 28 additions & 27 deletions src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,26 +102,6 @@ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmou

@Override
protected void init() {
for (Mod mod : ModMenu.MODS.values()) {
String id = mod.getId();
if (!modHasConfigScreen.containsKey(id)) {
try {
Screen configScreen = ModMenu.getConfigScreen(id, this);
modHasConfigScreen.put(id, configScreen != null);
} catch (java.lang.NoClassDefFoundError e) {
LOGGER.warn(
"The '" + id + "' mod config screen is not available because " + e.getLocalizedMessage() +
" is missing.");
modScreenErrors.put(id, e);
modHasConfigScreen.put(id, false);
} catch (Throwable e) {
LOGGER.error("Error from mod '" + id + "'", e);
modScreenErrors.put(id, e);
modHasConfigScreen.put(id, false);
}
}
}

int paneY = ModMenuConfig.CONFIG_MODE.getValue() ? 48 : 48 + 19;
this.paneWidth = this.width / 2 - 8;
this.rightPaneX = this.width - this.paneWidth;
Expand Down Expand Up @@ -203,9 +183,8 @@ protected void init() {
if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) {
this.configureButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ScreenTexts.EMPTY, button -> {
final String id = Objects.requireNonNull(selected).getMod().getId();
if (modHasConfigScreen.get(id)) {
Screen configScreen = ModMenu.getConfigScreen(id, this);
client.setScreen(configScreen);
if (getModHasConfigScreen(id)) {
this.safelyOpenConfigScreen(id);
} else {
button.active = false;
}
Expand Down Expand Up @@ -540,9 +519,9 @@ public void updateSelectedEntry(ModListEntry entry) {

if (this.configureButton != null) {

this.configureButton.active = modHasConfigScreen.get(modId);
this.configureButton.active = getModHasConfigScreen(modId);
this.configureButton.visible =
selected != null && modHasConfigScreen.get(modId) || modScreenErrors.containsKey(modId);
selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId);

if (modScreenErrors.containsKey(modId)) {
Throwable e = modScreenErrors.get(modId);
Expand Down Expand Up @@ -646,7 +625,29 @@ private static boolean isFabricMod(Path mod) {
}
}

public Map<String, Boolean> getModHasConfigScreen() {
return this.modHasConfigScreen;
public boolean getModHasConfigScreen(String modId) {
if (this.modScreenErrors.containsKey(modId)) {
return false;
} else {
return this.modHasConfigScreen.computeIfAbsent(modId, ModMenu::hasConfigScreen);
}
}

public void safelyOpenConfigScreen(String modId) {
try {
Screen screen = ModMenu.getConfigScreen(modId, this);

if (screen != null) {
this.client.setScreen(screen);
}
} catch (java.lang.NoClassDefFoundError e) {
LOGGER.warn(
"The '" + modId + "' mod config screen is not available because " + e.getLocalizedMessage() +
" is missing.");
modScreenErrors.put(modId, e);
} catch (Throwable e) {
LOGGER.error("Error from mod '" + modId + "'", e);
modScreenErrors.put(modId, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) {
addedMods.clear();
Collection<Mod> mods = ModMenu.MODS.values().stream().filter(mod -> {
if (ModMenuConfig.CONFIG_MODE.getValue()) {
Map<String, Boolean> modHasConfigScreen = parent.getModHasConfigScreen();
var hasConfig = modHasConfigScreen.get(mod.getId());
if (!hasConfig) {
return false;
}
return !parent.getModHasConfigScreen(mod.getId());
}

return !mod.isHidden();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ public void render(
}

if (!(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue() && (this.list.getParent()
.getModHasConfigScreen()
.get(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) {
.getModHasConfigScreen(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) {
final int textureSize = ModMenuConfig.COMPACT_LIST.getValue() ?
(int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) :
256;
Expand Down Expand Up @@ -167,9 +166,7 @@ public void render(
@Override
public boolean mouseClicked(double mouseX, double mouseY, int delta) {
list.select(this);
if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent()
.getModHasConfigScreen()
.get(this.mod.getId())) {
if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent().getModHasConfigScreen(this.mod.getId())) {
int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE;
if (mouseX - list.getRowLeft() <= iconSize) {
this.openConfig();
Expand All @@ -182,7 +179,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int delta) {
}

public void openConfig() {
MinecraftClient.getInstance().setScreen(ModMenu.getConfigScreen(mod.getId(), list.getParent()));
this.list.getParent().safelyOpenConfigScreen(mod.getId());
}

public Mod getMod() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ private static int passesFilters(ModsScreen screen, Mod mod, String query) {
|| deprecated.contains(query) && mod.getBadges()
.contains(Mod.Badge.DEPRECATED) // Search for deprecated mods
|| clientside.contains(query) && mod.getBadges().contains(Mod.Badge.CLIENT) // Search for clientside mods
|| configurable.contains(query) && screen.getModHasConfigScreen()
.get(modId) // Search for mods that can be configured
|| configurable.contains(query) && screen.getModHasConfigScreen(modId) // Search for mods that can be configured
|| hasUpdate.contains(query) && mod.hasUpdate() // Search for mods that have updates
) {
return 1;
Expand Down
Loading