Skip to content

Commit

Permalink
Advanced config logger & prefix/suffix support
Browse files Browse the repository at this point in the history
  • Loading branch information
sisby-folk committed Apr 4, 2024
1 parent ffeafca commit cdaac8a
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 71 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ In fact, some fully non-vanilla actions are possible for convenience, such as:
Inventory Tabs 4 is designed from the ground up to be friendlier to modpacks.<br/>
It's configured via `config/inventory_tabs.toml`, which includes comments providing extra context - like what each tab provider does.

You can enable the `configLogging` option to log helpful information for setting up the mod for your modpack when loading into a world.
By default, helpful information for setting up the mod for your modpack is logged when loading into a world. Toggle `configLogging` to disable this.

If tabs are appearing on a screen they don't fit well with, the screen can be blacklisted:

Expand All @@ -58,6 +58,7 @@ If tabs are being made for an inappropriate block, you can manually disable thei
```
[blockProviderOverrides]
"cool_mod:incompatible_block" = ""
"really_cool_mod:*" = ""
```

Or manually override it to a relevant one:
Expand All @@ -66,6 +67,7 @@ Or manually override it to a relevant one:
[blockProviderOverrides]
"#cool_mod:crafting_stations" = "inventory_tabs:block_unique"
"cool_mod:single_chest" = "inventory_tabs:block_simple"
"cool_mod:*_cabinet" = "inventory_tabs:block_simple_storage"
"cool_mod:doubleable_chest" = "inventory_tabs:block_chest"
```

Expand All @@ -80,7 +82,7 @@ If too many inappropriate blocks are being matched, you may want to disable the
"inventory_tabs:block_simple" = false
```

The `block_simple` provider uses a blacklist instead of a whitelist, so it generates a lot of false-positive tabs. It's enabled by default to help with finding good/bad tabs - but if you're making a modpack, you'll probably turn it off!
The `block_simple` provider uses a blacklist instead of a whitelist, so it generates a lot of false-positive tabs. It's enabled by default to help with finding good/bad tabs.

---

Expand All @@ -100,7 +102,7 @@ repositories {
maven { url "https://repo.sleeping.town/" }
}
dependencies {
modImplementation "folk.sisby:inventory-tabs:1.1.1"
modImplementation "folk.sisby:inventory-tabs:1.2.0"
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.gradle.configureondemand=true
# Enable advanced multi-module optimizations (share tiny-remaper instance between projects)
fabric.loom.multiProjectOptimisation=true
# Mod Properties
baseVersion = 1.1.8
baseVersion = 1.2.0
defaultBranch = 1.20
branch = 1.20
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ public class InventoryTabsConfig extends WrappedConfig {
@Comment("Emits helpful information for setting up this config when joining a world")
@Comment("Logs all registered screen handler IDs for use in screen overrides")
@Comment("Logs all registry tab provider contents (blocks etc) to help find bad tabs")
public final Boolean configLogging = false;
public final Boolean configLogging = true;

@Comment("Whether to log vanilla tab provider contents")
@Comment("For development purposes, not modpack configuration")
public final Boolean configLoggingVanilla = false;

@Comment("Whether to show tabs on screens that aren't specified below")
public final Boolean allowScreensByDefault = true;
Expand Down Expand Up @@ -77,6 +81,7 @@ public class InventoryTabsConfig extends WrappedConfig {
@Comment("")
public final Map<String, String> blockProviderOverrides = ValueMap.builder("")
.put("minecraft:crafting_table", "inventory_tabs:block_unique")
.put("#minecraft:doors", "")
.put("minecraft:fletching_table", "")
.build();

Expand Down
15 changes: 10 additions & 5 deletions src/main/java/folk/sisby/inventory_tabs/ScreenSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.ingame.HorseScreen;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;

Expand All @@ -18,16 +20,19 @@ public class ScreenSupport {
public static Map<Identifier, Predicate<HandledScreen<?>>> ALLOW = new HashMap<>();
public static Map<Identifier, Pair<Integer, Integer>> SCREEN_BOUND_OFFSETS = new HashMap<>();

public static Boolean allowTabs(RegistryKey<ScreenHandlerType<?>> type) {
if (InventoryTabs.CONFIG.screenOverrides.entrySet().stream().filter(e -> !e.getValue()).anyMatch(e -> Objects.equals(e.getKey(), type.getValue().toString()))) return false;
if (InventoryTabs.CONFIG.screenOverrides.entrySet().stream().filter(Map.Entry::getValue).anyMatch(e -> Objects.equals(e.getKey(), type.getValue().toString()))) return true;
return null;
}

public static boolean allowTabs(Screen screen) {
if (screen instanceof HandledScreen<?> hs && hs.getScreenHandler() != null) {
if (DENY.values().stream().anyMatch(p -> p.test(hs))) return false;
if (ALLOW.values().stream().anyMatch(p -> p.test(hs))) return true;
try {
Identifier handlerId = Registries.SCREEN_HANDLER.getId(hs.getScreenHandler().getType());
if (InventoryTabs.CONFIG.screenOverrides.entrySet().stream().filter(e -> !e.getValue()).anyMatch(e -> Objects.equals(e.getKey(), handlerId.toString())))
return false;
if (InventoryTabs.CONFIG.screenOverrides.entrySet().stream().filter(Map.Entry::getValue).anyMatch(e -> Objects.equals(e.getKey(), handlerId.toString())))
return true;
Boolean override = allowTabs(Registries.SCREEN_HANDLER.getKey(hs.getScreenHandler().getType()).orElseThrow());
if (override != null) return override;
} catch (UnsupportedOperationException ignored) {
}
return InventoryTabs.CONFIG.allowScreensByDefault;
Expand Down
101 changes: 86 additions & 15 deletions src/main/java/folk/sisby/inventory_tabs/TabProviders.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import folk.sisby.inventory_tabs.providers.BlockTabProvider;
import folk.sisby.inventory_tabs.providers.ChestBlockTabProvider;
import folk.sisby.inventory_tabs.providers.SimpleStorageBlockTabProvider;
import folk.sisby.inventory_tabs.providers.EnderChestTabProvider;
import folk.sisby.inventory_tabs.providers.EntityTabProvider;
import folk.sisby.inventory_tabs.providers.ItemTabProvider;
Expand All @@ -14,24 +17,29 @@
import folk.sisby.inventory_tabs.providers.SimpleBlockTabProvider;
import folk.sisby.inventory_tabs.providers.SimpleEntityTabProvider;
import folk.sisby.inventory_tabs.providers.SimpleItemTabProvider;
import folk.sisby.inventory_tabs.providers.SimpleStorageBlockTabProvider;
import folk.sisby.inventory_tabs.providers.SneakEntityTabProvider;
import folk.sisby.inventory_tabs.providers.TabProvider;
import folk.sisby.inventory_tabs.providers.UniqueBlockTabProvider;
import folk.sisby.inventory_tabs.providers.UniqueItemTabProvider;
import folk.sisby.inventory_tabs.providers.VehicleInventoryTabProvider;
import folk.sisby.inventory_tabs.util.RegistryValue;
import folk.sisby.inventory_tabs.util.RegistryMatcher;
import net.minecraft.entity.EntityType;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.util.Identifier;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -65,8 +73,11 @@ public static void reload(DynamicRegistryManager manager) {
InventoryTabs.LOGGER.info("[InventoryTabs] Reloading tab providers.");
refreshConfigPlaceholders();
if (InventoryTabs.CONFIG.configLogging) {
InventoryTabs.LOGGER.info("[Inventory Tabs] Registered Screen Handlers:");
manager.get(RegistryKeys.SCREEN_HANDLER).getKeys().forEach(id -> InventoryTabs.LOGGER.info("[Inventory Tabs] {}", id.getValue().toString()));
Map<String, List<RegistryKey<ScreenHandlerType<?>>>> types = manager.get(RegistryKeys.SCREEN_HANDLER).getKeys().stream().filter(k -> ScreenSupport.allowTabs(k) == null && InventoryTabs.CONFIG.allowScreensByDefault).collect(Collectors.groupingBy(k -> k.getValue().getNamespace()));
if (!types.isEmpty()) {
InventoryTabs.LOGGER.warn("[Inventory Tabs] {} Automatically tabbed screen handlers:", types.values().stream().mapToInt(Collection::size).sum());
types.forEach((namespace, ids) -> InventoryTabs.LOGGER.info(" | {}: {}", namespace, ids.stream().map(RegistryKey::getValue).map(Identifier::getPath).collect(Collectors.joining(", "))));
}
}
reloadRegistryProviders(manager, RegistryKeys.BLOCK, getProviders(BlockTabProvider.class), InventoryTabs.CONFIG.blockProviderOverrides);
warmEntities = reloadRegistryProviders(manager, RegistryKeys.ENTITY_TYPE, getProviders(EntityTabProvider.class), InventoryTabs.CONFIG.entityProviderOverrides);
Expand All @@ -81,48 +92,108 @@ public static <T extends RegistryTabProvider<?>> Map<Identifier, T> getProviders

public static <T> Set<T> reloadRegistryProviders(DynamicRegistryManager manager, RegistryKey<Registry<T>> registryKey, Map<Identifier, ? extends RegistryTabProvider<T>> providers, Map<String, String> overrideConfig) {
Set<T> warmValues = new HashSet<>();
Set<String> valueNamespaces = new HashSet<>();
Multiset<TagKey<T>> tagSizes = HashMultiset.create();
Map<TagKey<T>, Identifier> tagProviders = new HashMap<>();
Multimap<Identifier, TagKey<T>> providerTags = HashMultimap.create();
Multimap<Identifier, Identifier> providerValues = HashMultimap.create();
providers.values().forEach(p -> p.values.clear());
// Construct override map
Map<RegistryValue<T>, RegistryTabProvider<T>> overrides = new HashMap<>();
Map<RegistryMatcher<T>, RegistryTabProvider<T>> unsortedOverrides = new HashMap<>();
for (Map.Entry<String, String> override : overrideConfig.entrySet()) {
RegistryValue<T> registryValue = RegistryValue.fromRegistryString(manager, registryKey, override.getKey());
if (registryValue == null) {
RegistryMatcher<T> registryMatcher = RegistryMatcher.fromRegistryString(manager, registryKey, override.getKey());
if (registryMatcher == null) {
InventoryTabs.LOGGER.warn("[Inventory Tabs] Unknown override registry value ID {}, skipping...", override.getKey());
continue;
}
if (override.getValue().isEmpty()) {
overrides.put(registryValue, null);
unsortedOverrides.put(registryMatcher, null);
continue;
}
if (Identifier.tryParse(override.getValue()) == null || providers.get(Identifier.tryParse(override.getValue())) == null) {
InventoryTabs.LOGGER.warn("[Inventory Tabs] Unknown override tab provider ID {}, skipping...", override.getValue());
continue;
}
overrides.put(registryValue, providers.get(new Identifier(override.getValue())));
unsortedOverrides.put(registryMatcher, providers.get(new Identifier(override.getValue())));
}
Map<RegistryMatcher<T>, RegistryTabProvider<T>> overrides = new LinkedHashMap<>();
unsortedOverrides.entrySet().stream().sorted(Comparator.comparingInt(e -> e.getKey().priority())).forEach(e -> overrides.put(e.getKey(), e.getValue()));

// Add values to providers
if (InventoryTabs.CONFIG.configLogging) InventoryTabs.LOGGER.info("[Inventory Tabs] Starting provider freeze for {}", registryKey.getValue());
for (Map.Entry<RegistryKey<T>, T> entry : manager.get(registryKey).getEntrySet()) {
RegistryEntry<T> holder = manager.createRegistryLookup().getOptional(registryKey).orElseThrow().getOrThrow(entry.getKey());

Optional<Map.Entry<RegistryValue<T>, RegistryTabProvider<T>>> override = overrides.entrySet().stream().filter(e -> e.getKey().is(holder)).findFirst();
Optional<Map.Entry<RegistryMatcher<T>, RegistryTabProvider<T>>> override = overrides.entrySet().stream().filter(e -> e.getKey().is(holder)).findFirst();
if (override.isPresent()) {
if (override.get().getValue() != null) {
if (InventoryTabs.CONFIG.configLogging) InventoryTabs.LOGGER.info("[Inventory Tabs] {} -> {}", entry.getKey().getValue(), REGISTRY.inverse().get(override.get().getValue()));
Identifier providerId = REGISTRY.inverse().get(override.get().getValue());
if (InventoryTabs.CONFIG.configLogging && (InventoryTabs.CONFIG.configLoggingVanilla || !entry.getKey().getValue().getNamespace().equals("minecraft"))) {
holder.streamTags().forEach(tag -> {
if (tagProviders.containsKey(tag) && !providerId.equals(tagProviders.get(tag))) {
if (tagProviders.get(tag) != null) {
providerTags.remove(tagProviders.get(tag), tag);
tagProviders.put(tag, null);
}
} else {
tagProviders.put(tag, providerId);
}
});
}
override.get().getValue().values.add(entry.getValue());
}
continue;
}
for (Map.Entry<Identifier, ? extends RegistryTabProvider<T>> provider : providers.entrySet()) {
if (!InventoryTabs.CONFIG.registryProviderDefaults.getOrDefault(provider.getKey().toString(), true))
continue;
if (!InventoryTabs.CONFIG.registryProviderDefaults.getOrDefault(provider.getKey().toString(), true)) continue;
if (provider.getValue().consumes(entry.getValue())) {
if (InventoryTabs.CONFIG.configLogging) InventoryTabs.LOGGER.info("[Inventory Tabs] {} -> {}", entry.getKey().getValue(), provider.getKey());
if (InventoryTabs.CONFIG.configLogging && (InventoryTabs.CONFIG.configLoggingVanilla || !entry.getKey().getValue().getNamespace().equals("minecraft"))) {
Identifier providerId = provider.getKey();
holder.streamTags().forEach(tag -> {
if (tagProviders.containsKey(tag) && !providerId.equals(tagProviders.get(tag))) {
if (tagProviders.get(tag) != null) {
tagSizes.setCount(tag, 0);
providerTags.remove(tagProviders.get(tag), tag);
tagProviders.put(tag, null);
}
} else {
tagSizes.add(tag);
providerTags.put(providerId, tag);
tagProviders.put(tag, providerId);
}
});
providerValues.put(providerId, entry.getKey().getValue());
valueNamespaces.add(entry.getKey().getValue().getNamespace());
}
break;
}
}
warmValues.add(entry.getValue());
}
if (InventoryTabs.CONFIG.configLogging) {
providerTags.asMap().values().forEach(tags -> tags.removeIf(tag -> !"c".equals(tag.id().getNamespace()) && !valueNamespaces.contains(tag.id().getNamespace())));
if (!providerTags.isEmpty()) {
InventoryTabs.LOGGER.warn("[Inventory Tabs] {} Re-assignable provider tags for {}:", providerTags.size(), registryKey.getValue());
providerTags.asMap().forEach((provider, tags) -> {
if (!tags.isEmpty()) {
InventoryTabs.LOGGER.info(" | {}", provider);
tags.stream().collect(Collectors.groupingBy(t -> t.id().getNamespace())).entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {
InventoryTabs.LOGGER.info(" | | #{} - {}", e.getKey(), e.getValue().stream().sorted(Comparator.<TagKey<T>>comparingInt(tagSizes::count).reversed()).map(s -> "%s (%s)".formatted(s.id().getPath(), tagSizes.count(s))).collect(Collectors.joining(", ")));
});
}
});
}
if (!providerValues.isEmpty()) {
InventoryTabs.LOGGER.warn("[Inventory Tabs] {} Re-assignable provider values for {}:", providerValues.size(), registryKey.getValue());
providerValues.asMap().forEach((provider, values) -> {
if (!values.isEmpty()) {
InventoryTabs.LOGGER.info(" | {}", provider);
values.stream().collect(Collectors.groupingBy(Identifier::getNamespace)).entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {
InventoryTabs.LOGGER.info(" | | {} - {}", e.getKey(), e.getValue().stream().map(Identifier::getPath).sorted().collect(Collectors.joining(", ")));
});
}
});
}
}
return warmValues;
}

Expand Down
44 changes: 44 additions & 0 deletions src/main/java/folk/sisby/inventory_tabs/util/RegistryMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package folk.sisby.inventory_tabs.util;

import com.mojang.datafixers.util.Either;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import org.jetbrains.annotations.Nullable;

public record RegistryMatcher<T>(Either<Either<RegistryEntry<T>, TagKey<T>>, Pair<String, String>> value) {
public int priority() {
return value.left().isPresent() ? (value.left().orElseThrow().left().isPresent() ? 0 : 1) : 2;
}

public static @Nullable <T> RegistryMatcher<T> fromRegistryString(DynamicRegistryManager manager, RegistryKey<? extends Registry<T>> registry, String value) {
if (value.contains("*")) { // Prefix/Suffix
String[] split = value.split("\\*");
if (split.length == 1) {
if (value.startsWith("*")) { // Suffix
return new RegistryMatcher<>(Either.right(new Pair<>("", split[0])));
} else if (value.endsWith("*")) { // Prefix
return new RegistryMatcher<>(Either.right(new Pair<>(split[0], "")));
}
} else if (split.length == 2) {
return new RegistryMatcher<>(Either.right(new Pair<>(split[0], split[1])));
}
return null;
} else if (value.startsWith("#")) {
Identifier tagId = Identifier.tryParse(value.substring(1));
return tagId != null ? new RegistryMatcher<>(Either.left(Either.right(TagKey.of(registry, tagId)))) : null;
} else {
Identifier id = Identifier.tryParse(value);
if (id == null) return null;
return manager.createRegistryLookup().getOptional(registry).orElseThrow().getOptional(RegistryKey.of(registry, id)).map(h -> new RegistryMatcher<>(Either.left(Either.left(h)))).orElse(null);
}
}

public boolean is(RegistryEntry<T> value) {
return this.value.map(e -> e.map((v) -> v.equals(value), value::isIn), pair -> value.getKey().orElseThrow().getValue().toString().startsWith(pair.getLeft()) && value.getKey().orElseThrow().getValue().toString().endsWith(pair.getRight()));
}
}
Loading

0 comments on commit cdaac8a

Please sign in to comment.