Unknown пре 6 година
родитељ
комит
6bb5b8d721

+ 5 - 1
CHANGELOG.md

@@ -1,6 +1,10 @@
 View full changelog [here](https://github.com/shedaniel/RoughlyEnoughItems/blob/1.14/CHANGELOG.md).
+## v2.7.11.97
+- Fixed [#72](https://github.com/shedaniel/RoughlyEnoughItems/issues/72): Crash on clicking craftable items filter
+- New: Load REI Plugins via entry points, old way works too but support will be dropped
+- Removed: 'Enable Legacy Plugin Support' Option
 ## v2.7.10.96
-- Fixed [#69](https://github.com/shedaniel/RoughlyEnoughItems/issues/67): Weird Search Field
+- Fixed [#69](https://github.com/shedaniel/RoughlyEnoughItems/issues/69): Weird Search Field
 ## v2.7.10.95
 - Fixed [#67](https://github.com/shedaniel/RoughlyEnoughItems/issues/67): Item Panel Crashes
 ## v2.7.9.94

+ 1 - 0
build.gradle

@@ -63,4 +63,5 @@ dependencies {
     included "blue.endless:jankson:${project.jankson_version}"
 
     modCompile "io.github.prospector.modmenu:ModMenu:${modmenu_version}"
+    compile "org.lwjgl:lwjgl-jemalloc:3.2.1"
 }

+ 2 - 2
gradle.properties

@@ -1,4 +1,4 @@
-mod_version=2.7.10+build.96
+mod_version=2.7.11+build.97
 minecraft_version=1.14 Pre-Release 4
 yarn_version=1.14 Pre-Release 4+build.2
 fabric_version=0.2.7+build.123
@@ -6,4 +6,4 @@ fabricloader_version=0.4.1+build.126
 jankson_version=1.1.0
 cloth_events_version=0.3.1.23
 cloth_config_version=0.1.3.7
-modmenu_version=1.3.5-69
+modmenu_version=1.4.0-71

+ 25 - 0
src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java

@@ -0,0 +1,25 @@
+package me.shedaniel.rei;
+
+import io.github.prospector.modmenu.api.ModMenuApi;
+import net.minecraft.client.gui.Screen;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+public class REIModMenuEntryPoint implements ModMenuApi {
+    
+    @Override
+    public String getModId() {
+        return "roughlyenoughitems";
+    }
+    
+    @Override
+    public Optional<Supplier<Screen>> getConfigScreen(Screen screen) {
+        return Optional.of(() -> getScreen(screen));
+    }
+    
+    public Screen getScreen(Screen parent) {
+        return RoughlyEnoughItemsCore.getConfigManager().getConfigScreen(parent);
+    }
+    
+}

+ 20 - 18
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -7,7 +7,6 @@ import com.google.gson.JsonObject;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.cloth.hooks.ClothClientHooks;
 import me.shedaniel.rei.api.*;
-import me.shedaniel.rei.client.ConfigManager;
 import me.shedaniel.rei.client.*;
 import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.CreativePlayerInventoryScreenHooks;
@@ -16,7 +15,6 @@ import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.loader.api.FabricLoader;
 import net.fabricmc.loader.api.ModContainer;
 import net.fabricmc.loader.api.metadata.ModMetadata;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.ContainerScreen;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.ingame.CreativePlayerInventoryScreen;
@@ -31,7 +29,6 @@ import net.minecraft.util.Pair;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.lang.reflect.Method;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -45,8 +42,9 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
     private static final PluginDisabler PLUGIN_DISABLER = new PluginDisablerImpl();
     private static final ItemRegistry ITEM_REGISTRY = new ItemRegistryImpl();
     private static final DisplayHelper DISPLAY_HELPER = new DisplayHelperImpl();
-    private static final Map<Identifier, REIPlugin> plugins = Maps.newHashMap();
-    private static ConfigManager configManager;
+    private static final Map<Identifier, REIPluginEntry> plugins = Maps.newHashMap();
+    public static boolean reiIsOnServer = false;
+    private static ConfigManagerImpl configManager;
     
     static {
         LOGGER = LogManager.getFormatterLogger("REI");
@@ -72,18 +70,18 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         return DISPLAY_HELPER;
     }
     
-    public static REIPlugin registerPlugin(Identifier identifier, REIPlugin plugin) {
+    public static REIPluginEntry registerPlugin(Identifier identifier, REIPluginEntry plugin) {
         plugins.put(identifier, plugin);
         RoughlyEnoughItemsCore.LOGGER.info("[REI] Registered plugin %s from %s", identifier.toString(), plugin.getClass().getSimpleName());
         plugin.onFirstLoad(getPluginDisabler());
         return plugin;
     }
     
-    public static List<REIPlugin> getPlugins() {
+    public static List<REIPluginEntry> getPlugins() {
         return new LinkedList<>(plugins.values());
     }
     
-    public static Optional<Identifier> getPluginIdentifier(REIPlugin plugin) {
+    public static Optional<Identifier> getPluginIdentifier(REIPluginEntry plugin) {
         for(Identifier identifier : plugins.keySet())
             if (identifier != null && plugins.get(identifier).equals(plugin))
                 return Optional.of(identifier);
@@ -92,26 +90,30 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
     
     @Override
     public void onInitializeClient() {
-        configManager = new ConfigManager();
+        configManager = new ConfigManagerImpl();
         
         registerClothEvents();
-        
-        if (FabricLoader.getInstance().isModLoaded("modmenu")) {
+        discoverOldPlugins();
+        discoverPluginEntries();
+    }
+    
+    private void discoverPluginEntries() {
+        for(REIPluginEntry reiPlugin : FabricLoader.getInstance().getEntrypoints("rei_plugins", REIPluginEntry.class)) {
             try {
-                Class<?> clazz = Class.forName("io.github.prospector.modmenu.api.ModMenuApi");
-                Method method = clazz.getMethod("addConfigOverride", String.class, Runnable.class);
-                method.invoke(null, "roughlyenoughitems", (Runnable) () -> getConfigManager().openConfigScreen(MinecraftClient.getInstance().currentScreen));
+                if (reiPlugin instanceof REIPlugin)
+                    throw new IllegalStateException("REI Plugins on Entry Points should not implement REIPlugin");
+                registerPlugin(reiPlugin.getPluginIdentifier(), reiPlugin);
             } catch (Exception e) {
-                RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to add config override for ModMenu!", e);
+                e.printStackTrace();
+                RoughlyEnoughItemsCore.LOGGER.error("[REI] Can't load REI plugins from %s: %s", reiPlugin.getClass(), e.getLocalizedMessage());
             }
         }
-        
-        discoverPlugins();
     }
     
-    private void discoverPlugins() {
+    private void discoverOldPlugins() {
         List<Pair<Identifier, String>> list = Lists.newArrayList();
         for(ModMetadata metadata : FabricLoader.getInstance().getAllMods().stream().map(ModContainer::getMetadata).filter(metadata -> metadata.containsCustomElement("roughlyenoughitems:plugins")).collect(Collectors.toList())) {
+            RoughlyEnoughItemsCore.LOGGER.warn("[REI] %s(%s) is still using the old way to register its plugin! Support will be dropped in the future!", metadata.getName(), metadata.getId());
             try {
                 JsonElement pluginsElement = metadata.getCustomElement("roughlyenoughitems:plugins");
                 if (pluginsElement.isJsonArray()) {

+ 17 - 0
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java

@@ -1,8 +1,11 @@
 package me.shedaniel.rei;
 
 import me.shedaniel.rei.gui.widget.ItemListOverlay;
+import net.fabricmc.api.EnvType;
 import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
 import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
+import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.item.ItemStack;
 import net.minecraft.server.network.ServerPlayerEntity;
@@ -14,6 +17,7 @@ public class RoughlyEnoughItemsNetwork implements ModInitializer {
     
     public static final Identifier DELETE_ITEMS_PACKET = new Identifier("roughlyenoughitems", "delete_item");
     public static final Identifier CREATE_ITEMS_PACKET = new Identifier("roughlyenoughitems", "create_item");
+    public static final Identifier REI_ON_SERVER_PACKET = new Identifier("roughlyenoughitems", "rei_on_server");
     
     @Override
     public void onInitialize() {
@@ -30,6 +34,19 @@ public class RoughlyEnoughItemsNetwork implements ModInitializer {
             else
                 player.addChatMessage(new TranslatableTextComponent("text.rei.failed_cheat_items"), false);
         });
+        ClientSidePacketRegistry.INSTANCE.register(REI_ON_SERVER_PACKET, (packetContext, packetByteBuf) -> {
+            if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
+                try {
+                    Class.forName("me.shedaniel.rei.RoughlyEnoughItemsCore").getDeclaredField("reiIsOnServer").set(null, true);
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                } catch (NoSuchFieldException e) {
+                    e.printStackTrace();
+                } catch (ClassNotFoundException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
     }
     
 }

+ 2 - 0
src/main/java/me/shedaniel/rei/api/ConfigManager.java

@@ -19,4 +19,6 @@ public interface ConfigManager {
     
     void openConfigScreen(Screen parent);
     
+    Screen getConfigScreen(Screen parent);
+    
 }

+ 7 - 20
src/main/java/me/shedaniel/rei/api/REIPlugin.java

@@ -1,24 +1,11 @@
 package me.shedaniel.rei.api;
 
-public interface REIPlugin {
-    
-    default void onFirstLoad(PluginDisabler pluginDisabler) {}
-    
-    default void registerItems(ItemRegistry itemRegistry) {}
-    
-    default void registerPluginCategories(RecipeHelper recipeHelper) {}
-    
-    default void registerRecipeDisplays(RecipeHelper recipeHelper) {}
-    
-    @Deprecated
-    default void registerSpeedCraft(RecipeHelper recipeHelper) {}
-    
-    default void registerBounds(DisplayHelper displayHelper) {}
-    
-    default void registerOthers(RecipeHelper recipeHelper) {}
-    
-    default int getPriority() {
-        return 0;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import net.minecraft.util.Identifier;
+
+public interface REIPlugin extends REIPluginEntry {
+    @Override
+    default Identifier getPluginIdentifier() {
+        return RoughlyEnoughItemsCore.getPluginIdentifier(this).orElse(Identifier.create("null"));
     }
-    
 }

+ 28 - 0
src/main/java/me/shedaniel/rei/api/REIPluginEntry.java

@@ -0,0 +1,28 @@
+package me.shedaniel.rei.api;
+
+import net.minecraft.util.Identifier;
+
+public interface REIPluginEntry {
+    
+    default void onFirstLoad(PluginDisabler pluginDisabler) {}
+    
+    default void registerItems(ItemRegistry itemRegistry) {}
+    
+    default void registerPluginCategories(RecipeHelper recipeHelper) {}
+    
+    default void registerRecipeDisplays(RecipeHelper recipeHelper) {}
+    
+    @Deprecated
+    default void registerSpeedCraft(RecipeHelper recipeHelper) {}
+    
+    default void registerBounds(DisplayHelper displayHelper) {}
+    
+    default void registerOthers(RecipeHelper recipeHelper) {}
+    
+    default int getPriority() {
+        return 0;
+    }
+    
+    Identifier getPluginIdentifier();
+    
+}

+ 40 - 36
src/main/java/me/shedaniel/rei/client/ConfigManager.java → src/main/java/me/shedaniel/rei/client/ConfigManagerImpl.java

@@ -5,6 +5,7 @@ import blue.endless.jankson.JsonObject;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.api.ConfigManager;
 import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Screen;
@@ -14,12 +15,11 @@ import net.minecraft.text.StringTextComponent;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.List;
 
-public class ConfigManager implements me.shedaniel.rei.api.ConfigManager {
+public class ConfigManagerImpl implements ConfigManager {
     
     private static final Gson GSON = new GsonBuilder().create();
     private static final Jankson JANKSON = Jankson.builder().build();
@@ -27,7 +27,7 @@ public class ConfigManager implements me.shedaniel.rei.api.ConfigManager {
     private ConfigObject config;
     private boolean craftableOnly;
     
-    public ConfigManager() {
+    public ConfigManagerImpl() {
         this.veryOldConfigFile = new File(FabricLoader.getInstance().getConfigDirectory(), "rei.json");
         this.oldConfigFile = new File(FabricLoader.getInstance().getConfigDirectory(), "roughlyenoughitems/config.json");
         this.configFile = new File(FabricLoader.getInstance().getConfigDirectory(), "roughlyenoughitems/config.json5");
@@ -126,45 +126,49 @@ public class ConfigManager implements me.shedaniel.rei.api.ConfigManager {
     
     @Override
     public void openConfigScreen(Screen parent) {
+        MinecraftClient.getInstance().openScreen(getConfigScreen(parent));
+    }
+    
+    @Override
+    public Screen getConfigScreen(Screen parent) {
         if (FabricLoader.getInstance().isModLoaded("cloth-config")) {
             try {
-                Class.forName("me.shedaniel.rei.utils.ClothScreenRegistry").getDeclaredMethod("openConfigScreen", Screen.class).invoke(null, parent);
-            } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
+                return Screen.class.cast(Class.forName("me.shedaniel.rei.utils.ClothScreenRegistry").getDeclaredMethod("getConfigScreen", Screen.class).invoke(null, parent));
+            } catch (Exception e) {
                 e.printStackTrace();
             }
-        } else {
-            MinecraftClient.getInstance().openScreen(new Screen(new StringTextComponent("")) {
-                @Override
-                public void render(int int_1, int int_2, float float_1) {
-                    renderDirtBackground(0);
-                    List<String> list = minecraft.textRenderer.wrapStringToWidthAsList(I18n.translate("text.rei.no_config_api"), width - 100);
-                    int y = (int) (height / 2 - minecraft.textRenderer.fontHeight * 1.3f / 2 * list.size());
-                    for(int i = 0; i < list.size(); i++) {
-                        String s = list.get(i);
-                        drawCenteredString(minecraft.textRenderer, s, width / 2, y, -1);
-                        y += minecraft.textRenderer.fontHeight;
-                    }
-                    super.render(int_1, int_2, float_1);
-                }
-                
-                @Override
-                protected void init() {
-                    super.init();
-                    addButton(new net.minecraft.client.gui.widget.ButtonWidget(width / 2 - 100, height - 26, 200, 20, I18n.translate("gui.done"), buttonWidget -> {
-                        this.minecraft.openScreen(parent);
-                    }));
+        }
+        return new Screen(new StringTextComponent("")) {
+            @Override
+            public void render(int int_1, int int_2, float float_1) {
+                renderDirtBackground(0);
+                List<String> list = minecraft.textRenderer.wrapStringToWidthAsList(I18n.translate("text.rei.config_api_failed"), width - 100);
+                int y = (int) (height / 2 - minecraft.textRenderer.fontHeight * 1.3f / 2 * list.size());
+                for(int i = 0; i < list.size(); i++) {
+                    String s = list.get(i);
+                    drawCenteredString(minecraft.textRenderer, s, width / 2, y, -1);
+                    y += minecraft.textRenderer.fontHeight;
                 }
-                
-                @Override
-                public boolean keyPressed(int int_1, int int_2, int int_3) {
-                    if (int_1 == 256 && this.shouldCloseOnEsc()) {
-                        this.minecraft.openScreen(parent);
-                        return true;
-                    }
-                    return super.keyPressed(int_1, int_2, int_3);
+                super.render(int_1, int_2, float_1);
+            }
+            
+            @Override
+            protected void init() {
+                super.init();
+                addButton(new net.minecraft.client.gui.widget.ButtonWidget(width / 2 - 100, height - 26, 200, 20, I18n.translate("text.rei.back"), buttonWidget -> {
+                    this.minecraft.openScreen(parent);
+                }));
+            }
+            
+            @Override
+            public boolean keyPressed(int int_1, int int_2, int int_3) {
+                if (int_1 == 256 && this.shouldCloseOnEsc()) {
+                    this.minecraft.openScreen(parent);
+                    return true;
                 }
-            });
-        }
+                return super.keyPressed(int_1, int_2, int_3);
+            }
+        };
     }
     
 }

+ 2 - 2
src/main/java/me/shedaniel/rei/client/ConfigObject.java

@@ -46,8 +46,8 @@ public class ConfigObject {
     
     public boolean preferVisibleRecipes = false;
     
-    @Comment("Enable support for old REI plugins which uses registerSpeedCraft")
-    public boolean enableLegacySpeedCraftSupport = false;
+    //    @Comment("Enable support for old REI plugins which uses registerSpeedCraft")
+    //    public boolean enableLegacySpeedCraftSupport = false;
     
     @Comment("Force enable 2019 REI April Fools' joke")
     public boolean aprilFoolsFish2019 = false;

+ 3 - 7
src/main/java/me/shedaniel/rei/client/RecipeHelperImpl.java

@@ -186,26 +186,22 @@ public class RecipeHelperImpl implements RecipeHelper {
         this.displayVisibilityHandlers.clear();
         ((DisplayHelperImpl) RoughlyEnoughItemsCore.getDisplayHelper()).resetCache();
         long startTime = System.currentTimeMillis();
-        List<REIPlugin> plugins = new LinkedList<>(RoughlyEnoughItemsCore.getPlugins());
+        List<REIPluginEntry> plugins = new LinkedList<>(RoughlyEnoughItemsCore.getPlugins());
         plugins.sort((first, second) -> {
             return second.getPriority() - first.getPriority();
         });
-        RoughlyEnoughItemsCore.LOGGER.info("[REI] Loading %d plugins: %s", plugins.size(), String.join(", ", plugins.stream().map(plugin -> {
-            return RoughlyEnoughItemsCore.getPluginIdentifier(plugin).map(Identifier::toString).orElseGet(() -> "null");
-        }).collect(Collectors.toList())));
+        RoughlyEnoughItemsCore.LOGGER.info("[REI] Loading %d plugins: %s", plugins.size(), plugins.stream().map(REIPluginEntry::getPluginIdentifier).map(Identifier::toString).collect(Collectors.joining(", ")));
         Collections.reverse(plugins);
         RoughlyEnoughItemsCore.getItemRegisterer().getModifiableItemList().clear();
         PluginDisabler pluginDisabler = RoughlyEnoughItemsCore.getPluginDisabler();
         plugins.forEach(plugin -> {
-            Identifier identifier = RoughlyEnoughItemsCore.getPluginIdentifier(plugin).orElseGet(() -> new Identifier("null"));
+            Identifier identifier = plugin.getPluginIdentifier();
             if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_ITEMS))
                 plugin.registerItems(RoughlyEnoughItemsCore.getItemRegisterer());
             if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_CATEGORIES))
                 plugin.registerPluginCategories(this);
             if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_RECIPE_DISPLAYS))
                 plugin.registerRecipeDisplays(this);
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().enableLegacySpeedCraftSupport && pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_SPEED_CRAFT))
-                plugin.registerSpeedCraft(this);
             if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_BOUNDS))
                 plugin.registerBounds(RoughlyEnoughItemsCore.getDisplayHelper());
             if (pluginDisabler.isFunctionEnabled(identifier, PluginFunction.REGISTER_OTHERS))

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java

@@ -219,7 +219,7 @@ public class ItemListOverlay extends Widget {
         });
         if (splitSearchTerm.length == 0)
             stacks.addAll(os);
-        List<ItemStack> workingItems = RoughlyEnoughItemsCore.getConfigManager().isCraftableOnlyEnabled() && inventoryItems.size() > 0 ? Lists.newArrayList() : Collections.unmodifiableList(ol);
+        List<ItemStack> workingItems = RoughlyEnoughItemsCore.getConfigManager().isCraftableOnlyEnabled() && inventoryItems.size() > 0 ? Lists.newArrayList() : Lists.newArrayList(ol);
         if (RoughlyEnoughItemsCore.getConfigManager().isCraftableOnlyEnabled()) {
             RecipeHelper.getInstance().findCraftableByItems(inventoryItems).forEach(workingItems::add);
             workingItems.addAll(inventoryItems);

+ 25 - 0
src/main/java/me/shedaniel/rei/mixin/MixinClientConnection.java

@@ -0,0 +1,25 @@
+package me.shedaniel.rei.mixin;
+
+import io.netty.channel.Channel;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import net.minecraft.network.ClientConnection;
+import net.minecraft.text.TextComponent;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ClientConnection.class)
+public class MixinClientConnection {
+    
+    @Shadow
+    private Channel channel;
+    
+    @Inject(method = "disconnect", at = @At("HEAD"))
+    public void disconnect(TextComponent reason, CallbackInfo callback) {
+        if (channel.isOpen())
+            RoughlyEnoughItemsCore.reiIsOnServer = false;
+    }
+    
+}

+ 19 - 0
src/main/java/me/shedaniel/rei/mixin/MixinClientPlayNetworkHandler.java

@@ -0,0 +1,19 @@
+package me.shedaniel.rei.mixin;
+
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.client.network.packet.GameJoinS2CPacket;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ClientPlayNetworkHandler.class)
+public class MixinClientPlayNetworkHandler {
+    
+    @Inject(method = "onGameJoin", at = @At("HEAD"))
+    public void onGameJoin(GameJoinS2CPacket packet, CallbackInfo callbackInfo) {
+        RoughlyEnoughItemsCore.reiIsOnServer = false;
+    }
+    
+}

+ 21 - 0
src/main/java/me/shedaniel/rei/mixin/MixinPlayerManager.java

@@ -0,0 +1,21 @@
+package me.shedaniel.rei.mixin;
+
+import io.netty.buffer.Unpooled;
+import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
+import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
+import net.minecraft.network.ClientConnection;
+import net.minecraft.server.PlayerManager;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.util.PacketByteBuf;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(PlayerManager.class)
+public class MixinPlayerManager {
+    @Inject(method = "onPlayerConnect", at = @At("TAIL"))
+    public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo callback) {
+        ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, RoughlyEnoughItemsNetwork.REI_ON_SERVER_PACKET, new PacketByteBuf(Unpooled.buffer()));
+    }
+}

+ 6 - 1
src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java

@@ -36,7 +36,7 @@ import java.awt.*;
 import java.util.*;
 import java.util.List;
 
-public class DefaultPlugin implements REIPlugin {
+public class DefaultPlugin implements REIPluginEntry {
     
     public static final Identifier CRAFTING = new Identifier("roughlyenoughitems", "plugins/crafting");
     public static final Identifier SMELTING = new Identifier("roughlyenoughitems", "plugins/smelting");
@@ -53,6 +53,11 @@ public class DefaultPlugin implements REIPlugin {
         BREWING_DISPLAYS.add(display);
     }
     
+    @Override
+    public Identifier getPluginIdentifier() {
+        return PLUGIN;
+    }
+    
     @Override
     public void onFirstLoad(PluginDisabler pluginDisabler) {
         if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {

+ 5 - 5
src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java

@@ -19,7 +19,7 @@ import java.io.IOException;
 
 public class ClothScreenRegistry {
     
-    public static void openConfigScreen(Screen parent) {
+    public static Screen getConfigScreen(Screen parent) {
         ConfigScreenBuilder builder = ConfigScreenBuilder.create(parent, "text.rei.config.title", savedConfig -> {
             try {
                 RoughlyEnoughItemsCore.getConfigManager().saveConfig();
@@ -54,15 +54,15 @@ public class ClothScreenRegistry {
         modules.addOption(new BooleanListEntry("text.rei.config.enable_craftable_only", RoughlyEnoughItemsCore.getConfigManager().getConfig().enableCraftableOnlyButton, "text.cloth-config.reset_value", () -> true, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().enableCraftableOnlyButton = bool));
         modules.addOption(new BooleanListEntry("text.rei.config.enable_util_buttons", RoughlyEnoughItemsCore.getConfigManager().getConfig().showUtilsButtons, "text.cloth-config.reset_value", () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().showUtilsButtons = bool));
         modules.addOption(new BooleanListEntry("text.rei.config.disable_recipe_book", RoughlyEnoughItemsCore.getConfigManager().getConfig().disableRecipeBook, "text.cloth-config.reset_value", () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().disableRecipeBook = bool));
-        ConfigScreenBuilder.CategoryBuilder advanced = builder.addCategory("text.rei.config.advanced");
-        advanced.addOption(new BooleanListEntry("text.rei.config.enable_legacy_speedcraft_support", RoughlyEnoughItemsCore.getConfigManager().getConfig().enableLegacySpeedCraftSupport, "text.cloth-config.reset_value", () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().enableLegacySpeedCraftSupport = bool));
+        //        ConfigScreenBuilder.CategoryBuilder advanced = builder.addCategory("text.rei.config.advanced");
+        //        advanced.addOption(new BooleanListEntry("text.rei.config.enable_legacy_speedcraft_support", RoughlyEnoughItemsCore.getConfigManager().getConfig().enableLegacySpeedCraftSupport, "text.cloth-config.reset_value", () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().enableLegacySpeedCraftSupport = bool));
         ConfigScreenBuilder.CategoryBuilder aprilFools = builder.addCategory("text.rei.config.april_fools");
         aprilFools.addOption(new BooleanListEntry("text.rei.config.april_fools.2019", RoughlyEnoughItemsCore.getConfigManager().getConfig().aprilFoolsFish2019, "text.cloth-config.reset_value", () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().aprilFoolsFish2019 = bool));
-        MinecraftClient.getInstance().openScreen(builder.build(screen -> {
+        return builder.build(screen -> {
             ButtonWidget w = new ButtonWidget(6, 6, 60, 20, I18n.translate("text.rei.credits"), widget -> MinecraftClient.getInstance().openScreen(new CreditsScreen(MinecraftClient.getInstance().currentScreen)));
             ((ScreenHooks) screen).cloth_getButtonWidgets().add(0, w);
             ((ScreenHooks) screen).cloth_getChildren().add(0, w);
-        }));
+        });
     }
     
 }

+ 2 - 1
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -84,7 +84,8 @@
   "text.rei.config.item_cheating_mode.rei_like": "Normal",
   "text.rei.config.item_cheating_mode.jei_like": "Reversed",
   "text.rei.config.light_gray_recipe_border": "Light Gray Recipe Border:",
-  "text.rei.no_config_api": "Cloth Config API does not exist!\nPlease install it in order to show an in-game config screen!",
+  "text.rei.config_api_failed": "You arrived here either if Cloth Config API failed or you don't have it installed!\nUpdate / Install the API and report to the bug tracker.",
+  "text.rei.back": "Back",
   "_comment": "Don't change / translate the credit down below if you are doing it :)",
   "text.rei.credit.text": "§lRoughly Enough Items\n§7Originally a fork for Almost Enough Items.\n\n§lDevelopers\n  - Originally by ZenDarva\n  - Created by Danielshe\n  - Plugin Support by TehNut\n\n§lLanguage Translation\n  English - Danielshe\n  Simplified Chinese - Danielshe\n  Traditional Chinese - hugoalh, gxy17886 & Danielshe\n  French - Yanis48\n  German - MelanX\n  Estonian - Madis0\n  Portuguese - thiagokenis\n  LOLCAT - Danielshe\n  Upside Down - Danielshe\n  Brazilian Portuguese - thiagokenis\n  Bulgarian - geniiii\n\n§lLicense\n§7Roughly Enough Items is using MIT."
 }

+ 7 - 7
src/main/resources/fabric.mod.json

@@ -23,6 +23,12 @@
     ],
     "main": [
       "me.shedaniel.rei.RoughlyEnoughItemsNetwork"
+    ],
+    "modmenu": [
+      "me.shedaniel.rei.REIModMenuEntryPoint"
+    ],
+    "rei_plugins": [
+      "me.shedaniel.rei.plugin.DefaultPlugin"
     ]
   },
   "requires": {
@@ -37,12 +43,6 @@
     "roughlyenoughitems.mixins.json"
   ],
   "custom": {
-    "modmenu:clientsideOnly": true,
-    "roughlyenoughitems:plugins": [
-      {
-        "id": "default_plugin",
-        "class": "me.shedaniel.rei.plugin.DefaultPlugin"
-      }
-    ]
+    "modmenu:clientsideOnly": true
   }
 }

+ 6 - 2
src/main/resources/roughlyenoughitems.mixins.json

@@ -3,12 +3,16 @@
   "package": "me.shedaniel.rei.mixin",
   "minVersion": "0.7.11",
   "compatibilityLevel": "JAVA_8",
-  "mixins": [],
+  "mixins": [
+    "MixinPlayerManager"
+  ],
   "client": [
     "MixinContainerScreen",
     "MixinBrewingRecipeRegistry",
     "MixinRecipeBookGui",
-    "MixinCreativePlayerInventoryScreen"
+    "MixinCreativePlayerInventoryScreen",
+    "MixinClientPlayNetworkHandler",
+    "MixinClientConnection"
   ],
   "injectors": {
     "defaultRequire": 1