Selaa lähdekoodia

Some changes to partitioning search and optimisations to asFormattedText in ItemEntryStack.

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 4 vuotta sitten
vanhempi
sitoutus
37f0e7ae46

+ 1 - 1
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/ConfigObject.java

@@ -155,7 +155,7 @@ public interface ConfigObject {
     boolean shouldAsyncSearch();
     
     @ApiStatus.Experimental
-    int getNumberAsyncSearch();
+    int getAsyncSearchPartitionSize();
     
     @ApiStatus.Experimental
     boolean doDebugSearchTimeRequired();

+ 2 - 2
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/TextRepresentable.java

@@ -25,11 +25,11 @@ package me.shedaniel.rei.api;
 
 import me.shedaniel.math.impl.PointHelper;
 import me.shedaniel.rei.api.widgets.Tooltip;
+import me.shedaniel.rei.utils.FormattingUtils;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.text.LiteralText;
 import net.minecraft.text.Text;
-import net.minecraft.util.Formatting;
 import org.jetbrains.annotations.NotNull;
 
 @Environment(EnvType.CLIENT)
@@ -46,6 +46,6 @@ public interface TextRepresentable {
     
     @NotNull
     default Text asFormatStrippedText() {
-        return new LiteralText(Formatting.strip(asFormattedText().getString()));
+        return new LiteralText(FormattingUtils.stripFormatting(asFormattedText().getString()));
     }
 }

+ 33 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/utils/CollectionUtils.java

@@ -25,9 +25,11 @@ package me.shedaniel.rei.utils;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.common.collect.UnmodifiableIterator;
 import me.shedaniel.rei.api.EntryStack;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
+import net.minecraft.util.math.MathHelper;
 
 import java.util.*;
 import java.util.function.Function;
@@ -275,4 +277,35 @@ public class CollectionUtils {
         }
         return sum;
     }
+    
+    public static <T> Iterable<Iterable<T>> partition(List<T> list, int size) {
+        return () -> new UnmodifiableIterator<Iterable<T>>() {
+            int i = 0;
+            int partitionSize = MathHelper.ceil(list.size() / (float) size);
+            
+            @Override
+            public boolean hasNext() {
+                return i < partitionSize;
+            }
+            
+            @Override
+            public Iterable<T> next() {
+                UnmodifiableIterator<T> iterator = new UnmodifiableIterator<T>() {
+                    int cursor = i++ * size;
+                    int curSize = cursor + Math.min(list.size() - cursor, size);
+                    
+                    @Override
+                    public boolean hasNext() {
+                        return cursor < curSize;
+                    }
+                    
+                    @Override
+                    public T next() {
+                        return list.get(cursor++);
+                    }
+                };
+                return () -> iterator;
+            }
+        };
+    }
 }

+ 48 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/utils/FormattingUtils.java

@@ -0,0 +1,48 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.utils;
+
+public final class FormattingUtils {
+    public static String stripFormatting(String string) {
+        StringBuilder builder = new StringBuilder();
+        boolean lastSpecial = false;
+        for (char c : string.toCharArray()) {
+            if (lastSpecial) {
+                lastSpecial = false;
+                if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == 'r' || (c >= 'A' && c <= 'F') || c == 'R')) {
+                    builder.append('§');
+                    builder.append(c);
+                }
+            } else if (c == '§') {
+                lastSpecial = true;
+            } else {
+                builder.append(c);
+            }
+        }
+        if (lastSpecial) {
+            builder.append('§');
+        }
+        return builder.toString();
+    }
+}

+ 84 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/utils/ImmutableLiteralText.java

@@ -0,0 +1,84 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.utils;
+
+import net.minecraft.text.*;
+import net.minecraft.util.Language;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public final class ImmutableLiteralText implements Text {
+    public static final ImmutableLiteralText EMPTY = new ImmutableLiteralText("");
+    private final String content;
+    private OrderedText orderedText;
+    
+    public ImmutableLiteralText(String content) {
+        this.content = content;
+    }
+    
+    @Override
+    public Style getStyle() {
+        return Style.EMPTY;
+    }
+    
+    @Override
+    public String asString() {
+        return content;
+    }
+    
+    @Override
+    public List<Text> getSiblings() {
+        return Collections.emptyList();
+    }
+    
+    @Override
+    public MutableText copy() {
+        return new LiteralText(content);
+    }
+    
+    @Override
+    public MutableText shallowCopy() {
+        return copy();
+    }
+    
+    @Override
+    public <T> Optional<T> visit(Visitor<T> visitor) {
+        return visitSelf(visitor);
+    }
+    
+    @Override
+    public <T> Optional<T> visit(StyledVisitor<T> styledVisitor, Style style) {
+        return visitSelf(styledVisitor, style);
+    }
+    
+    @Override
+    public OrderedText asOrderedText() {
+        if (orderedText != null) {
+            orderedText = Language.getInstance().reorder(this);
+        }
+        return orderedText;
+    }
+}

+ 2 - 1
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java

@@ -43,6 +43,7 @@ import me.shedaniel.rei.impl.InternalWidgets;
 import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.impl.widgets.PanelWidget;
 import me.shedaniel.rei.utils.CollectionUtils;
+import me.shedaniel.rei.utils.ImmutableLiteralText;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.Screen;
@@ -285,7 +286,7 @@ public class RecipeViewingScreen extends Screen implements RecipeScreen {
             RecipeViewingScreen.this.choosePageActivated = true;
             RecipeViewingScreen.this.init();
         }).onRender((matrices, label) -> {
-            label.setText(new LiteralText(String.format("%d/%d", page + 1, getTotalPages(selectedCategory))));
+            label.setText(new ImmutableLiteralText(String.format("%d/%d", page + 1, getTotalPages(selectedCategory))));
             label.setClickable(getTotalPages(selectedCategory) > 1);
         }).tooltipSupplier(label -> label.isClickable() ? I18n.translate("text.rei.choose_page") : null));
         widgets.add(recipeNext = Widgets.createButton(new Rectangle(bounds.getMaxX() - 17, bounds.getY() + 19, 12, 12), new TranslatableText("text.rei.right_arrow"))

+ 4 - 3
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/gui/credits/CreditsScreen.java

@@ -27,6 +27,7 @@ import com.google.common.collect.Lists;
 import me.shedaniel.rei.gui.credits.CreditsEntryListWidget.TextCreditsItem;
 import me.shedaniel.rei.gui.credits.CreditsEntryListWidget.TranslationCreditsItem;
 import me.shedaniel.rei.impl.ScreenHelper;
+import me.shedaniel.rei.utils.ImmutableLiteralText;
 import net.fabricmc.loader.api.FabricLoader;
 import net.fabricmc.loader.api.metadata.CustomValue;
 import net.minecraft.client.gui.screen.Screen;
@@ -100,16 +101,16 @@ public class CreditsScreen extends Screen {
         for (String line : String.format("§lRoughly Enough Items (v%s)\n§7Originally a fork for Almost Enough Items.\n\n§lLanguage Translation\n%s\n\n§lLicense\n§7Roughly Enough Items is licensed under MIT.", FabricLoader.getInstance().getModContainer("roughlyenoughitems").map(mod -> mod.getMetadata().getVersion().getFriendlyString()).orElse("Unknown"), "%translators%").split("\n"))
             if (line.equalsIgnoreCase("%translators%")) {
                 if (exception[0] != null) {
-                    entryListWidget.creditsAddEntry(new TextCreditsItem(new LiteralText("Failed to get translators: " + exception[0].toString())));
+                    entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableLiteralText("Failed to get translators: " + exception[0].toString())));
                     for (StackTraceElement traceElement : exception[0].getStackTrace())
-                        entryListWidget.creditsAddEntry(new TextCreditsItem(new LiteralText("  at " + traceElement)));
+                        entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableLiteralText("  at " + traceElement)));
                 } else {
                     int maxWidth = translatorsMapped.stream().mapToInt(pair -> textRenderer.getStringWidth(pair.getLeft())).max().orElse(0) + 5;
                     for (Pair<String, String> pair : translatorsMapped) {
                         entryListWidget.creditsAddEntry(new TranslationCreditsItem(new TranslatableText(pair.getLeft()), new TranslatableText(pair.getRight()), i - maxWidth - 10, maxWidth));
                     }
                 }
-            } else entryListWidget.creditsAddEntry(new TextCreditsItem(new LiteralText(line)));
+            } else entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableLiteralText(line)));
         entryListWidget.creditsAddEntry(new TextCreditsItem(NarratorManager.EMPTY));
         children.add(buttonDone = new AbstractPressableButtonWidget(width / 2 - 100, height - 26, 200, 20, new TranslatableText("gui.done")) {
             @Override

+ 2 - 6
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java

@@ -441,15 +441,11 @@ public class EntryListWidget extends WidgetWithBounds {
             List<EntryStack> stacks = EntryRegistry.getInstance().getPreFilteredList();
             if (stacks instanceof CopyOnWriteArrayList && !stacks.isEmpty()) {
                 if (ConfigObject.getInstance().shouldAsyncSearch()) {
-                    int size = ConfigObject.getInstance().getNumberAsyncSearch();
                     List<CompletableFuture<List<EntryStack>>> completableFutures = Lists.newArrayList();
-                    for (int i = 0; i < stacks.size(); i += size) {
-                        int[] start = {i};
+                    for (Iterable<EntryStack> partitionStacks : CollectionUtils.partition(stacks, ConfigObject.getInstance().getAsyncSearchPartitionSize())) {
                         completableFutures.add(CompletableFuture.supplyAsync(() -> {
-                            int end = Math.min(stacks.size(), start[0] + size);
                             List<EntryStack> filtered = Lists.newArrayList();
-                            for (; start[0] < end; start[0]++) {
-                                EntryStack stack = stacks.get(start[0]);
+                            for (EntryStack stack : partitionStacks) {
                                 if (canLastSearchTermsBeAppliedTo(stack)) {
                                     if (workingItems != null && !workingItems.contains(stack.hashIgnoreAmount()))
                                         continue;

+ 3 - 3
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java

@@ -317,8 +317,8 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
     
     @Override
     @ApiStatus.Experimental
-    public int getNumberAsyncSearch() {
-        return advanced.search.numberAsyncSearch;
+    public int getAsyncSearchPartitionSize() {
+        return advanced.search.asyncSearchPartitionSize;
     }
     
     @Override
@@ -452,7 +452,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
             @Comment("Declares whether search time should be debugged.") private boolean debugSearchTimeRequired = false;
             @Comment("Declares whether REI should search async.") private boolean asyncSearch = true;
             @Comment("Declares how many entries should be grouped one async search.") @ConfigEntry.BoundedDiscrete(min = 25, max = 400)
-            private int numberAsyncSearch = 50;
+            private int asyncSearchPartitionSize = 100;
         }
         
         public static class Commands {

+ 5 - 5
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/FluidEntryStack.java

@@ -32,6 +32,8 @@ import me.shedaniel.rei.api.EntryStack;
 import me.shedaniel.rei.api.fractions.Fraction;
 import me.shedaniel.rei.api.widgets.Tooltip;
 import me.shedaniel.rei.utils.CollectionUtils;
+import me.shedaniel.rei.utils.FormattingUtils;
+import me.shedaniel.rei.utils.ImmutableLiteralText;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.Tessellator;
@@ -44,8 +46,6 @@ import net.minecraft.fluid.Fluid;
 import net.minecraft.fluid.Fluids;
 import net.minecraft.text.LiteralText;
 import net.minecraft.text.Text;
-import net.minecraft.text.TranslatableText;
-import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.Matrix4f;
 import net.minecraft.util.registry.Registry;
@@ -206,7 +206,7 @@ public class FluidEntryStack extends AbstractEntryStack {
             final String modId = ClientHelper.getInstance().getModFromIdentifier(id);
             boolean alreadyHasMod = false;
             for (Text s : toolTip)
-                if (Formatting.strip(s.getString()).equalsIgnoreCase(modId)) {
+                if (FormattingUtils.stripFormatting(s.getString()).equalsIgnoreCase(modId)) {
                     alreadyHasMod = true;
                     break;
                 }
@@ -247,7 +247,7 @@ public class FluidEntryStack extends AbstractEntryStack {
     public Text asFormattedText() {
         Identifier id = Registry.FLUID.getId(fluid);
         if (I18n.hasTranslation("block." + id.toString().replaceFirst(":", ".")))
-            return new TranslatableText("block." + id.toString().replaceFirst(":", "."));
-        return new LiteralText(CollectionUtils.mapAndJoinToString(id.getPath().split("_"), StringUtils::capitalize, " "));
+            return new ImmutableLiteralText(I18n.translate("block." + id.toString().replaceFirst(":", ".")));
+        return new ImmutableLiteralText(CollectionUtils.mapAndJoinToString(id.getPath().split("_"), StringUtils::capitalize, " "));
     }
 }

+ 14 - 12
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/ItemEntryStack.java

@@ -26,6 +26,8 @@ package me.shedaniel.rei.impl;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.systems.RenderSystem;
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ReferenceSet;
 import me.shedaniel.math.Point;
 import me.shedaniel.math.Rectangle;
 import me.shedaniel.rei.api.ClientHelper;
@@ -33,6 +35,8 @@ import me.shedaniel.rei.api.ConfigObject;
 import me.shedaniel.rei.api.EntryStack;
 import me.shedaniel.rei.api.fractions.Fraction;
 import me.shedaniel.rei.api.widgets.Tooltip;
+import me.shedaniel.rei.utils.FormattingUtils;
+import me.shedaniel.rei.utils.ImmutableLiteralText;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.item.TooltipContext;
 import net.minecraft.client.render.DiffuseLighting;
@@ -40,6 +44,7 @@ import net.minecraft.client.render.OverlayTexture;
 import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.render.model.json.ModelTransformation;
+import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.texture.SpriteAtlasTexture;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.Item;
@@ -47,10 +52,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.nbt.ListTag;
 import net.minecraft.nbt.Tag;
-import net.minecraft.text.LiteralText;
 import net.minecraft.text.Text;
-import net.minecraft.text.TranslatableText;
-import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.registry.Registry;
@@ -284,7 +286,7 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
             final String modId = ClientHelper.getInstance().getModFromItem(getItem());
             boolean alreadyHasMod = false;
             for (Text s : toolTip)
-                if (Formatting.strip(s.getString()).equalsIgnoreCase(modId)) {
+                if (FormattingUtils.stripFormatting(s.getString()).equalsIgnoreCase(modId)) {
                     alreadyHasMod = true;
                     break;
                 }
@@ -377,32 +379,32 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
         }
     }
     
-    private static final List<Item> SEARCH_BLACKLISTED = Lists.newArrayList();
+    private static final ReferenceSet<Item> SEARCH_BLACKLISTED = new ReferenceOpenHashSet<>();
     
     @Override
     public @NotNull Text asFormattedText() {
-        if (!SEARCH_BLACKLISTED.contains(getItem()))
+        if (!SEARCH_BLACKLISTED.contains(itemStack.getItem()))
             try {
-                return getItemStack().getName();
+                return itemStack.getName();
             } catch (Throwable e) {
                 e.printStackTrace();
-                SEARCH_BLACKLISTED.add(getItem());
+                SEARCH_BLACKLISTED.add(itemStack.getItem());
             }
         try {
-            return new TranslatableText("item." + Registry.ITEM.getId(getItem()).toString().replace(":", "."));
+            return new ImmutableLiteralText(I18n.translate("item." + Registry.ITEM.getId(itemStack.getItem()).toString().replace(":", ".")));
         } catch (Throwable e) {
             e.printStackTrace();
         }
-        return new LiteralText("ERROR");
+        return new ImmutableLiteralText("ERROR");
     }
     
     private List<Text> tryGetItemStackToolTip(boolean careAboutAdvanced) {
-        if (!SEARCH_BLACKLISTED.contains(getItem()))
+        if (!SEARCH_BLACKLISTED.contains(itemStack.getItem()))
             try {
                 return itemStack.getTooltip(MinecraftClient.getInstance().player, MinecraftClient.getInstance().options.advancedItemTooltips && careAboutAdvanced ? TooltipContext.Default.ADVANCED : TooltipContext.Default.NORMAL);
             } catch (Throwable e) {
                 e.printStackTrace();
-                SEARCH_BLACKLISTED.add(getItem());
+                SEARCH_BLACKLISTED.add(itemStack.getItem());
             }
         return Lists.newArrayList(asFormattedText());
     }

+ 7 - 8
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/filtering/rules/SearchFilteringRule.java

@@ -31,6 +31,7 @@ import me.shedaniel.rei.impl.SearchArgument;
 import me.shedaniel.rei.impl.filtering.AbstractFilteringRule;
 import me.shedaniel.rei.impl.filtering.FilteringContext;
 import me.shedaniel.rei.impl.filtering.FilteringResult;
+import me.shedaniel.rei.utils.CollectionUtils;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.client.gui.screen.Screen;
@@ -40,7 +41,10 @@ import net.minecraft.text.TranslatableText;
 import net.minecraft.util.Formatting;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -109,15 +113,10 @@ public class SearchFilteringRule extends AbstractFilteringRule<SearchFilteringRu
     }
     
     private void processList(Collection<EntryStack> stacks, List<CompletableFuture<List<EntryStack>>> completableFutures) {
-        int size = 100;
-        Iterator<EntryStack> iterator = stacks.iterator();
-        for (int i = 0; i < stacks.size(); i += size) {
-            int[] start = {i};
+        for (Iterable<EntryStack> partitionStacks : CollectionUtils.partition((List<EntryStack>) stacks, 100)) {
             completableFutures.add(CompletableFuture.supplyAsync(() -> {
-                int end = Math.min(stacks.size(), start[0] + size);
                 List<EntryStack> output = Lists.newArrayList();
-                for (; start[0] < end; start[0]++) {
-                    EntryStack stack = ((List<EntryStack>) stacks).get(start[0]);
+                for (EntryStack stack : partitionStacks) {
                     boolean shown = SearchArgument.canSearchTermsBeAppliedTo(stack, arguments);
                     if (shown) {
                         output.add(stack);

+ 1 - 1
gradle.properties

@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx3G
-mod_version=5.2.3
+mod_version=5.2.4
 supported_version=1.16.2
 minecraft_version=1.16.2-rc1
 yarn_version=1.16.2-rc1+build.4+legacy.20w09a+build.8