diff --git a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java index 0b916ac..a9f7ef8 100644 --- a/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java +++ b/src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java @@ -9,13 +9,12 @@ 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.Contract; +import org.jetbrains.annotations.NotNull; @Environment(EnvType.CLIENT) public class BackToolFeatureRenderer extends PlayerHeldItemFeatureRenderer { @@ -23,14 +22,16 @@ 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) { - if (!(playerRenderState.capeVisible && playerRenderState.skinTextures.capeTexture() != null && !ConfigHandler.isRenderWithCapesTrue()) && - !playerRenderState.invisible && playerRenderState.sleepingDirection == null && + 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)) { final HeldItemContext ctx = ClientSetup.HELD_TOOLS.get(playerRenderState.name); @@ -39,85 +40,91 @@ public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsume return; } this.setRenders(ctx.previousMain, ctx.previousOff, playerRenderState.mainArm); - 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); - matrixStack.pop(); + 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); // Mainhand stack + renderItem(this.offStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.LEFT, age, light); // Offhand stack } } - private void renderItem(float offset, MatrixStack matrices, VertexConsumerProvider provider, int light, final float age) { - matrices.translate(0F, 4F/16F, 1.91F/16F + (offset / 16F)); - matrices.translate(0F, 0F, 0.025F); + private void renderItem(final @NotNull ItemStack stack, final @NotNull MatrixStack matrices, final @NotNull VertexConsumerProvider provider, float offset, final boolean isInverted, final float age, int light) { + if (!stack.isEmpty()) { + final Item item = stack.getItem(); + matrices.push(); - if (!this.mainStack.isEmpty()) { - if (this.mainArm == Arm.RIGHT) { - matrices.scale(-1F, 1F, -1F); - } - boolean bl = this.mainStack.getItem() instanceof ShieldItem; - if (bl) { - float scale = 1.5F; + float orientationZ = ConfigHandler.getBeltOrientation(item); + if (orientationZ != Float.MIN_VALUE) { + // always do scaling and translations first before rotating, since the coordinate system rotates with the item + // and makes translations afterwards so much harder! + final float scale = 0.6F; matrices.scale(scale, scale, scale); - if (this.mainArm == Arm.LEFT) { - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F)); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-25F)); - matrices.translate(-2.5F/16F, 2F/16F, 1.25F/16F); - } else { - matrices.translate(-1F / 16F, 0.25F / 16F, 1.0F / 16F); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(25F)); - } - } - if (!bl) { - 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); - if (this.mainArm == Arm.LEFT) { - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90F)); - matrices.translate(0.19F, 0.6F, -0.33F); + if (isInverted) { + matrices.translate(-6 / 16F - 0.025F - offset / 16F, 1F, -0.5 / 16F); } else { - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(270F)); - matrices.translate(0.19F, 0.6F, 0.33F); + matrices.translate(6 / 16F + 0.025F + offset / 16F, 1F, -0.5 / 16F); } - } - if (age > 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.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90F)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ)); + } else { + orientationZ = ConfigHandler.getBackOrientation(item); + + if (orientationZ != Float.MIN_VALUE) { + // default shield doesn't look good. So we scale it up and + if (item instanceof ShieldItem) { + float scale = 1.5F; + matrices.scale(scale, scale, scale); + + 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.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(); } } - 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/BackToolsConfig.java b/src/main/java/com/daniking/backtools/BackToolsConfig.java index 5312cc3..a8ec847 100644 --- a/src/main/java/com/daniking/backtools/BackToolsConfig.java +++ b/src/main/java/com/daniking/backtools/BackToolsConfig.java @@ -6,29 +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.") - 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.") - 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"); + @Comment(value = + """ + 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 87c5523..ce480e4 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.reload()); } } diff --git a/src/main/java/com/daniking/backtools/ConfigHandler.java b/src/main/java/com/daniking/backtools/ConfigHandler.java index 9a48ba4..1378bb5 100644 --- a/src/main/java/com/daniking/backtools/ConfigHandler.java +++ b/src/main/java/com/daniking/backtools/ConfigHandler.java @@ -1,130 +1,147 @@ package com.daniking.backtools; +import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; 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; +import java.util.stream.Collectors; @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 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()); + // 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 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); + public static float getBackOrientation(final @NotNull Item item) { + return backConfigurations.getFloat(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; + /// 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); + } + /// returns the configurated orientation for the belt, or {@link Float#MIN_VALUE} if not found. + public static boolean isHelicopterModeOn() { + return helicopterMode; } - 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); + public static boolean shouldRenderWithCapes() { + return renderWithCapes; } - 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 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(); - MappingResolver resolver = FabricLoader.getInstance().getMappingResolver(); + 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) { - 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 - } - - Class path = null; - for (String namespace : resolver.getNamespaces()) { - try { - path = Class.forName(resolver.unmapClassName(namespace, split[0])); + for (final @NotNull String configText : listToParse) { + final @NotNull Matcher neagtiveMatcher = NEGATIVE_PATTERN.matcher(configText); - // if no error was thrown, we were successful! - break; - } catch (ClassNotFoundException ignored) { + 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; + } - if (path != null) { - try { - if (Item.class.isAssignableFrom(path)) { - TOOL_ORIENTATIONS.put(path, Float.parseFloat(split[1])); - } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Invalid Tool class file: {}", split[0]); + for (final @NotNull Item item : fetchItems(positiveMatcher)) { + result.put(item, orientation); } - } catch (NumberFormatException e) { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not parse text: {}", configText); + } else { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not read tool configuration \"{}\"!", configText); } - } else { - BackTools.LOGGER.error("[CONFIG_FILE]: Could not find class to add orientation: {}", split[0]); } } + + return result; } - public static boolean isHelicopterModeOn() { - return HELICOPTER_MODE; + /** + * @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); + + Optional> optionalRegistryEntries = Registries.ITEM.getOptional(tag); + + if (optionalRegistryEntries.isPresent()) { + return optionalRegistryEntries.get().stream().map(RegistryEntry::value).collect(Collectors.toSet()); + } else { + BackTools.LOGGER.error("[CONFIG_FILE]: Could not find any item tag with identifier of {}", identifier); + + return Set.of(); + } + } } - public static boolean isRenderWithCapesTrue() { return RENDER_WITH_CAPES; } } diff --git a/src/main/java/com/daniking/backtools/HeldItemContext.java b/src/main/java/com/daniking/backtools/HeldItemContext.java index 9183f4f..7cc4a60 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())) { + if (ConfigHandler.isItemEnabled(main.getItem())) { activeMain = main; - if(areStacksEqual(activeMain, activeOff)) { + if (ItemStack.areItemsAndComponentsEqual(activeMain, activeOff)) { activeOff = ItemStack.EMPTY; } } - if(ConfigHandler.isItemEnabled(off.getItem())) { + 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(); - } }