From 8866d171c04355f11bdefbcec267050d362e69ed Mon Sep 17 00:00:00 2001 From: FireInstall Date: Sun, 6 Oct 2024 10:40:43 +0200 Subject: [PATCH 1/4] Changed class based item grouping to tag based. --- .../daniking/backtools/BackToolsConfig.java | 25 +++-- .../com/daniking/backtools/ClientSetup.java | 5 +- .../com/daniking/backtools/ConfigHandler.java | 94 ++++++++++--------- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/daniking/backtools/BackToolsConfig.java b/src/main/java/com/daniking/backtools/BackToolsConfig.java index 5312cc3..5603b64 100644 --- a/src/main/java/com/daniking/backtools/BackToolsConfig.java +++ b/src/main/java/com/daniking/backtools/BackToolsConfig.java @@ -15,20 +15,29 @@ public class BackToolsConfig implements ConfigData { @Comment(value = "\nThese options affect only the client that loads the mod.\nIt is not possible to override the environment of the mod.") public final String environment = EnvType.CLIENT.name(); - @Comment(value = "What items should render on your belt.") + @Comment(value = "What items should render on your belt by their resource name. Eg: minecraft:diamond_hoe") public List beltTools = new ArrayList<>(); @Comment(value = "Enabled tools, by their resource name. Eg: minecraft:diamond_hoe. Putting any entry in here converts BackTools to a whitelist-only mod. Disabled Tools will be ignored.") public List enabledTools = new ArrayList<>(); @Comment(value = "Disabled tools, by their resource name. Eg: minecraft:diamond_hoe") public List disabledTools = new ArrayList<>(); - @Comment(value = "Tool orientation, by class file and degrees. Separate with \":\" . See defaults for examples.") + @Comment(value = + """ + Tool orientation, by class file and degrees. + Entries starting with "#" are tags (https://minecraft.wiki/w/Tag) + Leading namespace (e.g. minecraft:) is optional. + Separate with ":" for rotation. + Later occurrences of the same item override the previous once (Like in hoes override their config values set in mining). + Item types not listed here will default to 0. + "See defaults for examples.""") public List toolOrientation = Arrays.asList( - "net.minecraft.item.MiningToolItem" + ":0", - "net.minecraft.item.HoeItem" + ":0", - "net.minecraft.item.FishingRodItem" + ":0", - "net.minecraft.item.TridentItem" + ":0", - "net.minecraft.item.MaceItem" + ":-22.5", - "net.minecraft.item.RangedWeaponItem" + ":90"); + "#minecraft:enchantable/mining" + ":0", + "#minecraft:hoes" + ":0", + "#minecraft:enchantable/fishing" + ":0", + "#minecraft:enchantable/trident" + ":0", + "mace" + ":-22.5", + "bow" + ":90", + "crossbow" + ":90"); @Comment(value = "Get in swimming position and your tools go \"Weeee\"") public boolean helicopterMode = false; @Comment(value = "If true, tools render with capes") diff --git a/src/main/java/com/daniking/backtools/ClientSetup.java b/src/main/java/com/daniking/backtools/ClientSetup.java index 87c5523..ad37191 100644 --- a/src/main/java/com/daniking/backtools/ClientSetup.java +++ b/src/main/java/com/daniking/backtools/ClientSetup.java @@ -5,6 +5,7 @@ import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents; import java.util.Map; import java.util.WeakHashMap; @@ -18,6 +19,8 @@ public class ClientSetup implements ClientModInitializer { public void onInitializeClient() { AutoConfig.register(BackToolsConfig.class, JanksonConfigSerializer::new); config = AutoConfig.getConfigHolder(BackToolsConfig.class).getConfig(); - ConfigHandler.init(); + + // since we depend on item tags, our config can't load until the tags are loaded first. (creating / joining worlds) + CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> ConfigHandler.init()); } } diff --git a/src/main/java/com/daniking/backtools/ConfigHandler.java b/src/main/java/com/daniking/backtools/ConfigHandler.java index 9a48ba4..db5b15a 100644 --- a/src/main/java/com/daniking/backtools/ConfigHandler.java +++ b/src/main/java/com/daniking/backtools/ConfigHandler.java @@ -2,38 +2,35 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; import net.minecraft.item.*; import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.registry.entry.RegistryEntryList; +import net.minecraft.registry.tag.TagKey; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Environment(EnvType.CLIENT) public class ConfigHandler { - private static final HashMap, Float> TOOL_ORIENTATIONS = new HashMap<>(); - private static final HashSet ENABLED_TOOLS = new HashSet<>(); + private static final HashSet BELT_TOOLS = new HashSet<>(); + private static final @NotNull Pattern TOOL_ORIENTATION_PATTERN = Pattern.compile("^(?#)?(?:(?minecraft):)?(?.+?):(?.+?)$"); + private static final Map TOOL_ORIENTATIONS = new LinkedHashMap<>(); + private static final Set ENABLED_TOOLS = new HashSet<>(); private static final Set DISABLED_TOOLS = new HashSet<>(); private static boolean HELICOPTER_MODE = false; private static boolean RENDER_WITH_CAPES = true; - public static final HashSet BELT_TOOLS = new HashSet<>(); public static float getToolOrientation(@NotNull Item item) { - return getToolOrientation(item.getClass()); - } + Float orientation = TOOL_ORIENTATIONS.get(item); - public static float getToolOrientation(@NotNull Class object) { - if (object.equals(Item.class)) { - return 0; - } - if (!TOOL_ORIENTATIONS.containsKey(object)) { - TOOL_ORIENTATIONS.put(object, getToolOrientation(object.getSuperclass())); - } - return TOOL_ORIENTATIONS.get(object); + return Objects.requireNonNullElse(orientation, 0.0F); } public static boolean isItemEnabled(final Item item) { @@ -84,41 +81,51 @@ public static void init() { private static void parseOrientation() { TOOL_ORIENTATIONS.clear(); - MappingResolver resolver = FabricLoader.getInstance().getMappingResolver(); for (String configText : ClientSetup.config.toolOrientation) { - final String[] split = new String[2]; - final int i = configText.indexOf(':'); - if (i == -1) { - BackTools.LOGGER.error("[CONFIG_FILE]: Tool orientation class file and degrees must be separated with \":\"!"); - } else { - split[0] = configText.substring(0, i);//chunk of the text, contains the file class. - split[1] = configText.substring(i + 1);//orientation - } + Matcher matcher = TOOL_ORIENTATION_PATTERN.matcher(configText); - Class path = null; - for (String namespace : resolver.getNamespaces()) { + if (matcher.matches()) { + float orientation; try { - path = Class.forName(resolver.unmapClassName(namespace, split[0])); - - // if no error was thrown, we were successful! - break; - } catch (ClassNotFoundException ignored) { + orientation = Float.parseFloat(matcher.group("orientation")); + } catch (NumberFormatException exception) { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not load config option, because string \"{}\" was not an integer!", matcher.group("orientation")); + continue; } - } - if (path != null) { - try { - if (Item.class.isAssignableFrom(path)) { - TOOL_ORIENTATIONS.put(path, Float.parseFloat(split[1])); + final @Nullable String nameSpace = matcher.group("namespace"); + if (matcher.group("isTag") == null) { // not a tag + final @NotNull String itemID = matcher.group("itemOrTag"); // item id can't be null or the pattern didn't match + + final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), itemID); + final @NotNull Optional> optionalRegistryEntry = Registries.ITEM.getOptional(RegistryKey.of( + Registries.ITEM.getKey(), identifier + )); + + if (optionalRegistryEntry.isPresent()) { + TOOL_ORIENTATIONS.put(optionalRegistryEntry.get().value(), orientation); + } else { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item with identifier of {}", identifier); + } + } else { // is a tag + final @NotNull String tagID = matcher.group("itemOrTag"); // tag id can't be null or the pattern didn't match + + final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), tagID); + TagKey tag = TagKey.of(RegistryKeys.ITEM, identifier); + + Optional> optionalRegistryEntries = Registries.ITEM.getOptional(tag); + + if (optionalRegistryEntries.isPresent()) { + for (RegistryEntry registryEntry : optionalRegistryEntries.get()){ + TOOL_ORIENTATIONS.put(registryEntry.value(), orientation); + } } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Invalid Tool class file: {}", split[0]); + BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item tag with identifier of {}", identifier); } - } catch (NumberFormatException e) { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not parse text: {}", configText); } } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not find class to add orientation: {}", split[0]); + BackTools.LOGGER.error("[CONFIG_FILE]: Could not read tool configuration \"{}\"!", configText); } } } @@ -126,5 +133,6 @@ private static void parseOrientation() { public static boolean isHelicopterModeOn() { return HELICOPTER_MODE; } + public static boolean isRenderWithCapesTrue() { return RENDER_WITH_CAPES; } } From b983318f53bffb85b317e44ddcb4a9d397991ded Mon Sep 17 00:00:00 2001 From: FireInstall Date: Mon, 10 Mar 2025 03:31:15 +0100 Subject: [PATCH 2/4] I rebased this tag-branch onto the main branch and this commit re-adds the now lost clean up of the 1.20.5 commit --- .../backtools/BackToolFeatureRenderer.java | 12 ++++---- .../com/daniking/backtools/ConfigHandler.java | 2 +- .../daniking/backtools/HeldItemContext.java | 29 +++++++------------ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java index 0b916ac..f4af875 100644 --- a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java +++ b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java @@ -42,7 +42,8 @@ public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsume matrixStack.push(); this.getContextModel().body.rotate(matrixStack); boolean isHelicopterMode = ConfigHandler.isHelicopterModeOn() && (playerRenderState.isSwimming || playerRenderState.isGliding); - this.renderItem(!playerRenderState.equippedChestStack.isEmpty() ? 1.0F : playerRenderState.jacketVisible ? 0.5F : 0F, matrixStack, vertexConsumerProvider, light, isHelicopterMode ? playerRenderState.age : 0); + this.renderItem(!playerRenderState.equippedChestStack.isEmpty() ? 1.0F : playerRenderState.jacketVisible ? 0.5F : 0F, + matrixStack, vertexConsumerProvider, light, isHelicopterMode ? playerRenderState.age : 0); matrixStack.pop(); } } @@ -55,8 +56,8 @@ private void renderItem(float offset, MatrixStack matrices, VertexConsumerProvid if (this.mainArm == Arm.RIGHT) { matrices.scale(-1F, 1F, -1F); } - boolean bl = this.mainStack.getItem() instanceof ShieldItem; - if (bl) { + + if (this.mainStack.getItem() instanceof ShieldItem) { float scale = 1.5F; matrices.scale(scale, scale, scale); if (this.mainArm == Arm.LEFT) { @@ -67,11 +68,11 @@ private void renderItem(float offset, MatrixStack matrices, VertexConsumerProvid matrices.translate(-1F / 16F, 0.25F / 16F, 1.0F / 16F); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(25F)); } - } - if (!bl) { + } else { final float i = ConfigHandler.getToolOrientation(this.mainStack.getItem()); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(i)); } + if (ConfigHandler.isBeltTool(this.mainStack.getItem())) { float swordScale = 0.8F; matrices.scale(swordScale, swordScale, swordScale); @@ -84,6 +85,7 @@ private void renderItem(float offset, MatrixStack matrices, VertexConsumerProvid matrices.translate(0.19F, 0.6F, 0.33F); } } + if (age > 0) { matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F)); } diff --git a/src/main/java/com/daniking/backtools/ConfigHandler.java b/src/main/java/com/daniking/backtools/ConfigHandler.java index db5b15a..eb69b17 100644 --- a/src/main/java/com/daniking/backtools/ConfigHandler.java +++ b/src/main/java/com/daniking/backtools/ConfigHandler.java @@ -19,11 +19,11 @@ @Environment(EnvType.CLIENT) public class ConfigHandler { - private static final HashSet BELT_TOOLS = new HashSet<>(); private static final @NotNull Pattern TOOL_ORIENTATION_PATTERN = Pattern.compile("^(?#)?(?:(?minecraft):)?(?.+?):(?.+?)$"); private static final Map TOOL_ORIENTATIONS = new LinkedHashMap<>(); private static final Set ENABLED_TOOLS = new HashSet<>(); private static final Set DISABLED_TOOLS = new HashSet<>(); + private static final HashSet BELT_TOOLS = new HashSet<>(); private static boolean HELICOPTER_MODE = false; private static boolean RENDER_WITH_CAPES = true; diff --git a/src/main/java/com/daniking/backtools/HeldItemContext.java b/src/main/java/com/daniking/backtools/HeldItemContext.java index 9183f4f..42640f0 100644 --- a/src/main/java/com/daniking/backtools/HeldItemContext.java +++ b/src/main/java/com/daniking/backtools/HeldItemContext.java @@ -11,9 +11,7 @@ public class HeldItemContext { public ItemStack activeMain = ItemStack.EMPTY; public ItemStack activeOff = ItemStack.EMPTY; - public void tick(ItemStack main, ItemStack off) { - if (droppedEntity != null && !droppedEntity.getStack().isEmpty()) { this.reset(droppedEntity.getStack()); droppedEntity = null; @@ -21,15 +19,15 @@ public void tick(ItemStack main, ItemStack off) { } //check to see if we should remove the main hand back tool - if(areStacksEqual(main, previousMain) || areStacksEqual(off, previousMain)) { + if(ItemStack.areItemsAndComponentsEqual(main, previousMain) || ItemStack.areItemsAndComponentsEqual(off, previousMain)) { previousMain = ItemStack.EMPTY; } - if(areStacksEqual(main, previousOff) || areStacksEqual(off, previousOff)) { + if(ItemStack.areItemsAndComponentsEqual(main, previousOff) || ItemStack.areItemsAndComponentsEqual(off, previousOff)) { previousOff = ItemStack.EMPTY; } //set back tool if main tool was an item, and we don't see that item anymore. - if(!activeMain.isEmpty() && !areStacksEqual(main, activeMain) && !areStacksEqual(off, activeMain)) { + if(!activeMain.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeMain) && !ItemStack.areItemsAndComponentsEqual(off, activeMain)) { previousMain = activeMain; activeMain = ItemStack.EMPTY; } @@ -37,45 +35,38 @@ public void tick(ItemStack main, ItemStack off) { // //set back tool if offhand tool was an item, and we don't see that item anymore. // this.updatePreviousStacks(main, off); - if(!activeOff.isEmpty() && !areStacksEqual(main, activeOff) && !areStacksEqual(off, activeOff)) { + if(!activeOff.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeOff) && !ItemStack.areItemsAndComponentsEqual(off, activeOff)) { previousOff = activeOff; activeOff = ItemStack.EMPTY; } if(ConfigHandler.isItemEnabled(main.getItem())) { activeMain = main; - if(areStacksEqual(activeMain, activeOff)) { + if(ItemStack.areItemsAndComponentsEqual(activeMain, activeOff)) { activeOff = ItemStack.EMPTY; } } if(ConfigHandler.isItemEnabled(off.getItem())) { activeOff = off; - if(areStacksEqual(activeOff, activeMain)) { + if(ItemStack.areItemsAndComponentsEqual(activeOff, activeMain)) { activeMain = ItemStack.EMPTY; } } } private void reset(ItemStack entityStack) { - if (areStacksEqual(entityStack, previousMain)) { + if (ItemStack.areItemsAndComponentsEqual(entityStack, previousMain)) { previousMain = ItemStack.EMPTY; } - if (areStacksEqual(entityStack, activeMain)) { + if (ItemStack.areItemsAndComponentsEqual(entityStack, activeMain)) { activeMain = ItemStack.EMPTY; } //Check to see if we should remove the offhand BackTool - if (areStacksEqual(entityStack, previousOff)) { + if (ItemStack.areItemsAndComponentsEqual(entityStack, previousOff)) { previousOff = ItemStack.EMPTY; } - if (areStacksEqual(entityStack, activeOff)) { + if (ItemStack.areItemsAndComponentsEqual(entityStack, activeOff)) { activeOff = ItemStack.EMPTY; } } - - public static boolean areStacksEqual(final ItemStack a, final ItemStack b) { - return !a.isEmpty() && !b.isEmpty() && - (a.getComponents().isEmpty() || !b.getComponents().isEmpty()) && - (!a.getComponents().isEmpty() || b.getComponents().isEmpty()) && - a.getItem() == b.getItem(); - } } From 79420e54aa3093af7d5814ede96f22d199cf12db Mon Sep 17 00:00:00 2001 From: FireInstall Date: Fri, 28 Feb 2025 23:12:03 +0100 Subject: [PATCH 3/4] - removed white and black list from config and instead allowing items to get removed from the very same liste by prepending it with a minus. This should make the config more intuitive, since the orientation is set in the same list where it gets used. - added some default belt items - added special case for fishing rod and OnAStickItems - made shield offset configurable - deduplicated code - allow belt items to work wit offhand - fixed bug if both offhand and mainhand items get rendered, affecting each other (bow + shield) - update cloth config version - removed Herobrine from the minecraft movie - fixed bug of some items not getting rotated correctly (bow) --- .../backtools/BackToolFeatureRenderer.java | 138 ++++++------- .../daniking/backtools/BackToolsConfig.java | 72 ++++--- .../com/daniking/backtools/ClientSetup.java | 2 +- .../com/daniking/backtools/ConfigHandler.java | 191 +++++++++--------- 4 files changed, 220 insertions(+), 183 deletions(-) diff --git a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java index f4af875..02cc744 100644 --- a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java +++ b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java @@ -9,13 +9,11 @@ import net.minecraft.client.render.entity.feature.PlayerHeldItemFeatureRenderer; import net.minecraft.client.render.entity.model.PlayerEntityModel; import net.minecraft.client.render.entity.state.PlayerEntityRenderState; -import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ModelTransformationMode; -import net.minecraft.item.ShieldItem; +import net.minecraft.item.*; import net.minecraft.util.Arm; import net.minecraft.util.math.RotationAxis; +import org.jetbrains.annotations.NotNull; @Environment(EnvType.CLIENT) public class BackToolFeatureRenderer extends PlayerHeldItemFeatureRenderer { @@ -29,8 +27,8 @@ public BackToolFeatureRenderer(FeatureRendererContext 0) { - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F)); - } - MinecraftClient.getInstance().getItemRenderer().renderItem(this.mainStack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0); - } - if (!this.offStack.isEmpty()) { - if (this.mainArm == Arm.LEFT) { - matrices.scale(-1F, 1F, -1F); - } - boolean isShield = this.offStack.getItem() instanceof ShieldItem; - if (isShield) { - float scale = 1.5F; - matrices.scale(scale, scale, scale); - if (this.mainArm == Arm.RIGHT) { - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F)); - matrices.translate(-2.5F/16F, 2F/16F, 1.25F/16F); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-25F)); + matrices.translate(1 / 16F, 3 / 16F, 0.025F + offset / 16F); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ)); + } else { + matrices.translate(-1 / 16F, 3 / 16F, 0.025F + offset / 16F); + matrices.multiply(RotationAxis.NEGATIVE_Z.rotationDegrees(90 + orientationZ)); + } + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); + } else { + matrices.translate(0F, 4F / 16F, 1.91F / 16F + 0.025F + offset / 16F); + + if (isInverted) { + // tiny difference to avoid z-fighting if main and offhand item get rendered at the same time + matrices.translate(0F, 0, 0.001); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F)); + } + + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ)); + + // again special case for fishing rod and alike since they look wierd with the fishing line defying gravity + if (item instanceof FishingRodItem || item instanceof OnAStickItem) { + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180F)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90F)); + } + + if (age > 0) { + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F)); + } + } } else { - matrices.translate(-1F / 16F, 0.25F / 16F, 1.0F / 16F); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(25F)); + BackTools.LOGGER.info("Item {} was marked as enabled, but was neither a back nor a belt tool!", stack.getItem()); + matrices.pop(); + return; // Early return, without render, if neither back nor belt tool } } - if (!isShield) { - final float i = ConfigHandler.getToolOrientation(this.mainStack.getItem()); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(i)); - } - if (age > 0) { - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F)); - } - MinecraftClient.getInstance().getItemRenderer().renderItem(this.offStack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0); + + MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0); + matrices.pop(); } } diff --git a/src/main/java/com/daniking/backtools/BackToolsConfig.java b/src/main/java/com/daniking/backtools/BackToolsConfig.java index 5603b64..a8ec847 100644 --- a/src/main/java/com/daniking/backtools/BackToolsConfig.java +++ b/src/main/java/com/daniking/backtools/BackToolsConfig.java @@ -6,38 +6,64 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Environment(EnvType.CLIENT) @Config(name = "BackTools") public class BackToolsConfig implements ConfigData { - @Comment(value = "\nThese options affect only the client that loads the mod.\nIt is not possible to override the environment of the mod.") + @Comment(value = """ + These options affect only the client that loads the mod. + It is not possible to override the environment of the mod.""") public final String environment = EnvType.CLIENT.name(); - @Comment(value = "What items should render on your belt by their resource name. Eg: minecraft:diamond_hoe") - public List beltTools = new ArrayList<>(); - @Comment(value = "Enabled tools, by their resource name. Eg: minecraft:diamond_hoe. Putting any entry in here converts BackTools to a whitelist-only mod. Disabled Tools will be ignored.") - public List enabledTools = new ArrayList<>(); - @Comment(value = "Disabled tools, by their resource name. Eg: minecraft:diamond_hoe") - public List disabledTools = new ArrayList<>(); @Comment(value = """ - Tool orientation, by class file and degrees. - Entries starting with "#" are tags (https://minecraft.wiki/w/Tag) - Leading namespace (e.g. minecraft:) is optional. - Separate with ":" for rotation. - Later occurrences of the same item override the previous once (Like in hoes override their config values set in mining). - Item types not listed here will default to 0. - "See defaults for examples.""") - public List toolOrientation = Arrays.asList( - "#minecraft:enchantable/mining" + ":0", - "#minecraft:hoes" + ":0", - "#minecraft:enchantable/fishing" + ":0", - "#minecraft:enchantable/trident" + ":0", - "mace" + ":-22.5", - "bow" + ":90", - "crossbow" + ":90"); + Back tool orientation, by item id, or tag and degrees for rotation, separated by a colon ":". + What items should render on your back + + - Entries starting with a hash "#" are tags (https://minecraft.wiki/w/Tag) + - Entries without a leading hash "#" are item identifiers + - Entries starting with a minus "-" will remove them from the list. + E.g. + > "#minecraft:hoes", -iron_hoe" will do all hoes but the iron one. + > "minecraft:iron_hoe, -#hoes" will do no hoes at all. + > "-iron_hoe #minecraft:hoes" will do all hoes, + since the iron one is removed without being in the list in the list in the first place, + and then added with all the other hoes in the tag. + - Later occurrences of the same item will override all the previous ones + - Leading namespace (e.g. minecraft:) is optional. + - See defaults for examples""") + public List backTools = Arrays.asList( + "#minecraft:pickaxes:0", + "#minecraft:axes:0", + "#minecraft:shovels:0", + "#minecraft:hoes:0", + "minecraft:fishing_rod:0", + "minecraft:carrot_on_a_stick:0", + "minecraft:warped_fungus_on_a_stick:0", + "minecraft:shears:0", + "#minecraft:swords:0", + "minecraft:mace:-22.5", + "minecraft:trident:0", + "minecraft:bow:90", + "minecraft:crossbow:90", + "minecraft:shield:65"); + @Comment(value = """ + Belt tool orientation, by item id, or tag and degrees for rotation, separated by a colon ":". + What items should render on your belt, overwriting a possible occurrence in the backTools list, + with one exception: a negation here will not remove it from the backtools. + So, if for example "iron_hoe" is added to backTools, and "-iron_hoe" is added to beltTools, + it will stay in the backTools list. + However, if both have a "iron_hoe" entry, the hoe will render on the belt. + + Else wise same rules as above.""") + public List beltTools = Arrays.asList( + "#minecraft:bundles:180", + "minecraft:potion:180", + "minecraft:splash_potion:180", + "minecraft:lingering_potion:180", + "minecraft:lead:180" + ); @Comment(value = "Get in swimming position and your tools go \"Weeee\"") public boolean helicopterMode = false; @Comment(value = "If true, tools render with capes") diff --git a/src/main/java/com/daniking/backtools/ClientSetup.java b/src/main/java/com/daniking/backtools/ClientSetup.java index ad37191..ce480e4 100644 --- a/src/main/java/com/daniking/backtools/ClientSetup.java +++ b/src/main/java/com/daniking/backtools/ClientSetup.java @@ -21,6 +21,6 @@ public void onInitializeClient() { config = AutoConfig.getConfigHolder(BackToolsConfig.class).getConfig(); // since we depend on item tags, our config can't load until the tags are loaded first. (creating / joining worlds) - CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> ConfigHandler.init()); + CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> ConfigHandler.reload()); } } diff --git a/src/main/java/com/daniking/backtools/ConfigHandler.java b/src/main/java/com/daniking/backtools/ConfigHandler.java index eb69b17..1378bb5 100644 --- a/src/main/java/com/daniking/backtools/ConfigHandler.java +++ b/src/main/java/com/daniking/backtools/ConfigHandler.java @@ -1,5 +1,6 @@ package com.daniking.backtools; +import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.item.*; @@ -16,123 +17,131 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Environment(EnvType.CLIENT) public class ConfigHandler { - private static final @NotNull Pattern TOOL_ORIENTATION_PATTERN = Pattern.compile("^(?#)?(?:(?minecraft):)?(?.+?):(?.+?)$"); - private static final Map TOOL_ORIENTATIONS = new LinkedHashMap<>(); - private static final Set ENABLED_TOOLS = new HashSet<>(); - private static final Set DISABLED_TOOLS = new HashSet<>(); - private static final HashSet BELT_TOOLS = new HashSet<>(); - private static boolean HELICOPTER_MODE = false; - private static boolean RENDER_WITH_CAPES = true; - - public static float getToolOrientation(@NotNull Item item) { - Float orientation = TOOL_ORIENTATIONS.get(item); - - return Objects.requireNonNullElse(orientation, 0.0F); + // matches every common float + private static final String FLOAT_PATTERN_STR = "[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)"; + // matches every column ":" with optional whitespace around it + private static final String WHITESPACED_COLUMN = "\\s*:\\s*"; + // optional # at start, optional namespace : itemId or tagId + private static final String BASE_PATTERN_STR = "(?#)?(?:(?.+?)" + WHITESPACED_COLUMN + ")?(?.+?)"; + // always starts with a minus "-", make use of the base pattern, may or may not be appended by a column and a number + private static final @NotNull Pattern NEGATIVE_PATTERN = Pattern.compile("^-" + BASE_PATTERN_STR + "(?:" + WHITESPACED_COLUMN + FLOAT_PATTERN_STR + ")?$"); + // always starts with the base pattern, followed by a column and a float + private static final @NotNull Pattern ORIENTATION_PATTERN = Pattern.compile("^" + BASE_PATTERN_STR + WHITESPACED_COLUMN + "(?" + FLOAT_PATTERN_STR + ")$"); + private static @NotNull Object2FloatOpenHashMap<@NotNull Item> backConfigurations = new Object2FloatOpenHashMap<>(); + private static @NotNull Object2FloatOpenHashMap<@NotNull Item> beltConfigurations = new Object2FloatOpenHashMap<>(); + private static boolean helicopterMode = false; + private static boolean renderWithCapes = true; + + public static boolean isItemEnabled(final @NotNull Item item) { + return backConfigurations.containsKey(item) || beltConfigurations.containsKey(item); } - public static boolean isItemEnabled(final Item item) { - final Identifier registryName = Registries.ITEM.getId(item); - if (!ConfigHandler.ENABLED_TOOLS.isEmpty()) {//whitelist only - return ConfigHandler.ENABLED_TOOLS.contains(registryName); - } - //at this point whitelist is empty - //let's then check blacklist - if (DISABLED_TOOLS.contains(registryName)) { - return false; - } - //else allow default items - return item instanceof SwordItem || - item instanceof TridentItem || - item instanceof BowItem || - item instanceof CrossbowItem || - item instanceof MaceItem || - item instanceof ShieldItem || - item instanceof MiningToolItem || - item instanceof ShearsItem || - item instanceof FishingRodItem; + public static float getBackOrientation(final @NotNull Item item) { + return backConfigurations.getFloat(item); + } + /// returns the configurated orientation for the belt, or {@link Float#MIN_VALUE} if not found. + public static float getBeltOrientation(final @NotNull Item item) { + return beltConfigurations.getFloat(item); } - public static boolean isBeltTool(final Item item) { - var itemId = Registries.ITEM.getId(item); - ClientSetup.config.beltTools.forEach(beltTool -> BELT_TOOLS.add(Identifier.of(beltTool))); - return BELT_TOOLS.contains(itemId); + /// returns the configurated orientation for the belt, or {@link Float#MIN_VALUE} if not found. + public static boolean isHelicopterModeOn() { + return helicopterMode; } - public static void init() { - //whitelist only mods - ENABLED_TOOLS.clear(); - ClientSetup.config.enabledTools.forEach(enabledTool -> ENABLED_TOOLS.add(Identifier.of(enabledTool))); - //if nothing in whitelist, check our blacklist - if (ENABLED_TOOLS.isEmpty()) { - DISABLED_TOOLS.clear(); - ClientSetup.config.disabledTools.forEach(disabledTool -> DISABLED_TOOLS.add(Identifier.of(disabledTool))); - } - ConfigHandler.parseOrientation(); + public static boolean shouldRenderWithCapes() { + return renderWithCapes; + } + + public static void reload() { + // parse configurated Items + backConfigurations = parseOrientation(ClientSetup.config.backTools); + beltConfigurations = parseOrientation(ClientSetup.config.beltTools); // load easter egg setting - HELICOPTER_MODE = ClientSetup.config.helicopterMode; + helicopterMode = ClientSetup.config.helicopterMode; //render with capes setting - RENDER_WITH_CAPES = ClientSetup.config.renderWithCapes; + renderWithCapes = ClientSetup.config.renderWithCapes; } - private static void parseOrientation() { - TOOL_ORIENTATIONS.clear(); + private static @NotNull Object2FloatOpenHashMap<@NotNull Item> parseOrientation(final @NotNull List<@NotNull String> listToParse){ + final @NotNull Object2FloatOpenHashMap<@NotNull Item> result = new Object2FloatOpenHashMap<>(listToParse.size()); + result.defaultReturnValue(Float.MIN_VALUE); // we need something to indicate the state "not found", and null would defeat the point of a fastutil map - for (String configText : ClientSetup.config.toolOrientation) { - Matcher matcher = TOOL_ORIENTATION_PATTERN.matcher(configText); + for (final @NotNull String configText : listToParse) { + final @NotNull Matcher neagtiveMatcher = NEGATIVE_PATTERN.matcher(configText); - if (matcher.matches()) { - float orientation; - try { - orientation = Float.parseFloat(matcher.group("orientation")); - } catch (NumberFormatException exception) { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not load config option, because string \"{}\" was not an integer!", matcher.group("orientation")); - continue; + if (neagtiveMatcher.matches()) { // if it's starts with a minus remove it from the map + for (final @NotNull Item item : fetchItems(neagtiveMatcher)) { + result.removeFloat(item); } + } else { + final @NotNull Matcher positiveMatcher = ORIENTATION_PATTERN.matcher(configText); + if (positiveMatcher.matches()) { + float orientation; + try { + orientation = Float.parseFloat(positiveMatcher.group("orientation")); + } catch (NumberFormatException exception) { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not load config option, because string \"{}\" was not a float!", positiveMatcher.group("orientation")); + continue; + } - final @Nullable String nameSpace = matcher.group("namespace"); - if (matcher.group("isTag") == null) { // not a tag - final @NotNull String itemID = matcher.group("itemOrTag"); // item id can't be null or the pattern didn't match + for (final @NotNull Item item : fetchItems(positiveMatcher)) { + result.put(item, orientation); + } + } else { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not read tool configuration \"{}\"!", configText); + } + } + } - final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), itemID); - final @NotNull Optional> optionalRegistryEntry = Registries.ITEM.getOptional(RegistryKey.of( - Registries.ITEM.getKey(), identifier - )); + return result; + } - if (optionalRegistryEntry.isPresent()) { - TOOL_ORIENTATIONS.put(optionalRegistryEntry.get().value(), orientation); - } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item with identifier of {}", identifier); - } - } else { // is a tag - final @NotNull String tagID = matcher.group("itemOrTag"); // tag id can't be null or the pattern didn't match + /** + * @param matcher created matching either the {@link #NEGATIVE_PATTERN} or {@link #ORIENTATION_PATTERN} + * @return + * - all items in a tag, if the String was a tag,
+ * - a Set of one, if the String was item id
+ * - an empty Set, if invalid + */ + private static @NotNull Set<@NotNull Item> fetchItems (final @NotNull Matcher matcher) { + final @Nullable String nameSpace = matcher.group("namespace"); + + if (matcher.group("isTag") == null) { // not a tag + final @NotNull String itemID = matcher.group("itemOrTag"); // item id can't be null or the pattern didn't match + + final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), itemID); + final @NotNull Optional> optionalRegistryEntry = Registries.ITEM.getOptional(RegistryKey.of( + Registries.ITEM.getKey(), identifier + )); + + if (optionalRegistryEntry.isPresent()) { + return Set.of(optionalRegistryEntry.get().value()); + } else { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item with identifier of {}", identifier); + return Set.of(); + } + } else { // is a tag + final @NotNull String tagID = matcher.group("itemOrTag"); // tag id can't be null or the pattern didn't match - final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), tagID); - TagKey tag = TagKey.of(RegistryKeys.ITEM, identifier); + final @Nullable Identifier identifier = Identifier.of(Objects.requireNonNullElse(nameSpace, Identifier.DEFAULT_NAMESPACE), tagID); + TagKey tag = TagKey.of(RegistryKeys.ITEM, identifier); - Optional> optionalRegistryEntries = Registries.ITEM.getOptional(tag); + Optional> optionalRegistryEntries = Registries.ITEM.getOptional(tag); - if (optionalRegistryEntries.isPresent()) { - for (RegistryEntry registryEntry : optionalRegistryEntries.get()){ - TOOL_ORIENTATIONS.put(registryEntry.value(), orientation); - } - } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item tag with identifier of {}", identifier); - } - } + if (optionalRegistryEntries.isPresent()) { + return optionalRegistryEntries.get().stream().map(RegistryEntry::value).collect(Collectors.toSet()); } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not read tool configuration \"{}\"!", configText); + BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item tag with identifier of {}", identifier); + + return Set.of(); } } } - - public static boolean isHelicopterModeOn() { - return HELICOPTER_MODE; - } - - public static boolean isRenderWithCapesTrue() { return RENDER_WITH_CAPES; } } From e49b812658c85f2953f5ed5da3176a6f0bbc53a1 Mon Sep 17 00:00:00 2001 From: FireInstall Date: Sun, 9 Mar 2025 18:18:12 +0100 Subject: [PATCH 4/4] add some missing annotations --- .../backtools/BackToolFeatureRenderer.java | 13 ++++++++----- .../com/daniking/backtools/HeldItemContext.java | 16 ++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java index 02cc744..a9f7ef8 100644 --- a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java +++ b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java @@ -13,6 +13,7 @@ import net.minecraft.item.*; import net.minecraft.util.Arm; import net.minecraft.util.math.RotationAxis; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @Environment(EnvType.CLIENT) @@ -21,12 +22,14 @@ public class BackToolFeatureRenderer extends Playe public ItemStack offStack = ItemStack.EMPTY; public Arm mainArm = Arm.RIGHT; - public BackToolFeatureRenderer(FeatureRendererContext context) { + @Contract(pure = true) + public BackToolFeatureRenderer(final @NotNull FeatureRendererContext context) { super(context); } @Override - public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, PlayerEntityRenderState playerRenderState, float limbAngle, float limbDistance) { + public void render(final @NotNull MatrixStack matrixStack, final @NotNull VertexConsumerProvider vertexConsumerProvider, final int light, + final @NotNull PlayerEntityRenderState playerRenderState, final float limbAngle, final float limbDistance) { if (!(playerRenderState.capeVisible && playerRenderState.skinTextures.capeTexture() != null && !ConfigHandler.shouldRenderWithCapes()) && !playerRenderState.invisible && playerRenderState.sleepingDirection == null && // todo render belt tools when sleeping and regardless of cape ClientSetup.HELD_TOOLS.containsKey(playerRenderState.name)) { @@ -41,8 +44,8 @@ public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsume final float age = ConfigHandler.isHelicopterModeOn() && (playerRenderState.isSwimming || playerRenderState.isGliding) ? playerRenderState.age : 0; final float offset = !playerRenderState.equippedChestStack.isEmpty() ? 1.0F : playerRenderState.jacketVisible ? 0.5F : 0F; - renderItem(this.mainStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.RIGHT, age, light); // Main stack - renderItem(this.offStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.LEFT, age, light); // Off stack + renderItem(this.mainStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.RIGHT, age, light); // Mainhand stack + renderItem(this.offStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.LEFT, age, light); // Offhand stack } } @@ -121,7 +124,7 @@ private void renderItem(final @NotNull ItemStack stack, final @NotNull MatrixSta } } - private void setRenders(final ItemStack mainStack, final ItemStack offStack, final Arm side) { + private void setRenders(final @NotNull ItemStack mainStack, final @NotNull ItemStack offStack, final @NotNull Arm side) { this.mainStack = mainStack; this.offStack = offStack; this.mainArm = side; diff --git a/src/main/java/com/daniking/backtools/HeldItemContext.java b/src/main/java/com/daniking/backtools/HeldItemContext.java index 42640f0..7cc4a60 100644 --- a/src/main/java/com/daniking/backtools/HeldItemContext.java +++ b/src/main/java/com/daniking/backtools/HeldItemContext.java @@ -19,15 +19,15 @@ public void tick(ItemStack main, ItemStack off) { } //check to see if we should remove the main hand back tool - if(ItemStack.areItemsAndComponentsEqual(main, previousMain) || ItemStack.areItemsAndComponentsEqual(off, previousMain)) { + if (ItemStack.areItemsAndComponentsEqual(main, previousMain) || ItemStack.areItemsAndComponentsEqual(off, previousMain)) { previousMain = ItemStack.EMPTY; } - if(ItemStack.areItemsAndComponentsEqual(main, previousOff) || ItemStack.areItemsAndComponentsEqual(off, previousOff)) { + if (ItemStack.areItemsAndComponentsEqual(main, previousOff) || ItemStack.areItemsAndComponentsEqual(off, previousOff)) { previousOff = ItemStack.EMPTY; } //set back tool if main tool was an item, and we don't see that item anymore. - if(!activeMain.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeMain) && !ItemStack.areItemsAndComponentsEqual(off, activeMain)) { + if (!activeMain.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeMain) && !ItemStack.areItemsAndComponentsEqual(off, activeMain)) { previousMain = activeMain; activeMain = ItemStack.EMPTY; } @@ -35,20 +35,20 @@ public void tick(ItemStack main, ItemStack off) { // //set back tool if offhand tool was an item, and we don't see that item anymore. // this.updatePreviousStacks(main, off); - if(!activeOff.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeOff) && !ItemStack.areItemsAndComponentsEqual(off, activeOff)) { + if (!activeOff.isEmpty() && !ItemStack.areItemsAndComponentsEqual(main, activeOff) && !ItemStack.areItemsAndComponentsEqual(off, activeOff)) { previousOff = activeOff; activeOff = ItemStack.EMPTY; } - if(ConfigHandler.isItemEnabled(main.getItem())) { + if (ConfigHandler.isItemEnabled(main.getItem())) { activeMain = main; - if(ItemStack.areItemsAndComponentsEqual(activeMain, activeOff)) { + if (ItemStack.areItemsAndComponentsEqual(activeMain, activeOff)) { activeOff = ItemStack.EMPTY; } } - if(ConfigHandler.isItemEnabled(off.getItem())) { + if (ConfigHandler.isItemEnabled(off.getItem())) { activeOff = off; - if(ItemStack.areItemsAndComponentsEqual(activeOff, activeMain)) { + if (ItemStack.areItemsAndComponentsEqual(activeOff, activeMain)) { activeMain = ItemStack.EMPTY; } }