Skip to content

Commit

Permalink
New plugin reload setup
Browse files Browse the repository at this point in the history
1. Plugin reloads will now interrupt existing reloading tasks if a new plugin reload has been requested
2. Plugin reloads will be automatically interrupted when the player leaves the level / the level is removed
3. More logging in DisplayRegistryImpl showing the stats of displays
4. Failure in filling recipes will now not stop the caching of display lookup
5. Slightly improve performance of checking display visibility on display lookup
  • Loading branch information
shedaniel committed Sep 6, 2024
1 parent 16fdd8d commit 1de901a
Show file tree
Hide file tree
Showing 16 changed files with 665 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
package me.shedaniel.rei.api.common.plugins;

import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
import me.shedaniel.rei.api.common.registry.ReloadStage;
import me.shedaniel.rei.impl.ClientInternals;
import me.shedaniel.rei.impl.Internals;
import me.shedaniel.rei.impl.common.plugins.PluginReloadContext;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -62,18 +62,25 @@ public void registerPlugin(REIPluginProvider<? extends P> plugin) {
}

@Override
public void pre(ReloadStage stage) {
PluginView.this.pre(stage);
public void pre(PluginReloadContext context) throws InterruptedException {
PluginView.this.pre(context);
}

@Override
public void post(ReloadStage stage) {
PluginView.this.post(stage);
public void reload(PluginReloadContext context) throws InterruptedException {
PluginView.this.reload(context);
}

@Override
public void post(PluginReloadContext context) throws InterruptedException {
PluginView.this.post(context);
}
};
}

void pre(ReloadStage stage);
void pre(PluginReloadContext context) throws InterruptedException;

void reload(PluginReloadContext context) throws InterruptedException;

void post(ReloadStage stage);
void post(PluginReloadContext context) throws InterruptedException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This file is licensed under the MIT License, part of Roughly Enough Items.
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 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.impl.common.plugins;

import me.shedaniel.rei.api.common.registry.ReloadStage;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public interface PluginReloadContext {
ReloadStage stage();

ReloadInterruptionContext interruptionContext();

static PluginReloadContext of(ReloadStage stage, ReloadInterruptionContext interruptionContext) {
return new PluginReloadContext() {
@Override
public ReloadStage stage() {
return stage;
}

@Override
public ReloadInterruptionContext interruptionContext() {
return interruptionContext;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* This file is licensed under the MIT License, part of Roughly Enough Items.
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 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.impl.common.plugins;

import me.shedaniel.rei.impl.common.InternalLogger;
import org.jetbrains.annotations.ApiStatus;

@FunctionalInterface
@ApiStatus.Internal
public interface ReloadInterruptionContext {
boolean isInterrupted();

default void checkInterrupted() throws InterruptedException {
if (isInterrupted()) {
InternalLogger.getInstance().debug("Plugin reload interrupted!");
throw new InterruptedException();
}
}

default ReloadInterruptionContext withJob(Runnable ifInterrupted) {
return new ReloadInterruptionContext() {
@Override
public boolean isInterrupted() {
return ReloadInterruptionContext.this.isInterrupted();
}

@Override
public void checkInterrupted() throws InterruptedException {
try {
ReloadInterruptionContext.this.checkInterrupted();
} catch (InterruptedException e) {
ifInterrupted.run();
throw e;
}
}
};
}

static ReloadInterruptionContext ofNever() {
return () -> false;
}
}
4 changes: 3 additions & 1 deletion fabric/src/main/resources/roughlyenoughitems.accessWidener
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ accessible field net/minecraft/tags/Tag$TagEntry id Lnet/minecraft/resources/Res
accessible class net/minecraft/tags/Tag$OptionalElementEntry
accessible field net/minecraft/tags/Tag$OptionalElementEntry id Lnet/minecraft/resources/ResourceLocation;
accessible class net/minecraft/tags/Tag$ElementEntry
accessible field net/minecraft/tags/Tag$ElementEntry id Lnet/minecraft/resources/ResourceLocation;
accessible field net/minecraft/tags/Tag$ElementEntry id Lnet/minecraft/resources/ResourceLocation;
accessible field net/minecraft/client/multiplayer/ClientLevel connection Lnet/minecraft/client/multiplayer/ClientPacketListener;
accessible field net/minecraft/client/multiplayer/MultiPlayerGameMode connection Lnet/minecraft/client/multiplayer/ClientPacketListener;
4 changes: 3 additions & 1 deletion forge/src/main/resources/META-INF/accesstransformer.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ public net.minecraft.client.renderer.RenderType$CompositeState
public net.minecraft.tags.Tag$OptionalTagEntry f_13373_ # id
public net.minecraft.tags.Tag$TagEntry f_13383_ # id
public net.minecraft.tags.Tag$OptionalElementEntry f_13363_ # id
public net.minecraft.tags.Tag$ElementEntry f_13349_ # id
public net.minecraft.tags.Tag$ElementEntry f_13349_ # id
public net.minecraft.client.multiplayer.ClientLevel f_104561_ # connection
public net.minecraft.client.multiplayer.MultiPlayerGameMode f_105190_ # connection
32 changes: 4 additions & 28 deletions runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,10 @@
import dev.architectury.registry.ReloadListenerRegistry;
import dev.architectury.utils.Env;
import dev.architectury.utils.EnvExecutor;
import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.annotation.Nullable;
import me.shedaniel.rei.api.common.entry.type.EntryType;
import me.shedaniel.rei.api.common.plugins.PluginManager;
import me.shedaniel.rei.api.common.plugins.PluginView;
import me.shedaniel.rei.api.common.plugins.REIPlugin;
import me.shedaniel.rei.api.common.plugins.REIServerPlugin;
import me.shedaniel.rei.api.common.registry.ReloadStage;
import me.shedaniel.rei.impl.Internals;
import me.shedaniel.rei.impl.common.InternalLogger;
import me.shedaniel.rei.impl.common.category.CategoryIdentifierImpl;
Expand All @@ -53,6 +50,8 @@
import me.shedaniel.rei.impl.common.logging.performance.PerformanceLogger;
import me.shedaniel.rei.impl.common.logging.performance.PerformanceLoggerImpl;
import me.shedaniel.rei.impl.common.plugins.PluginManagerImpl;
import me.shedaniel.rei.impl.common.plugins.ReloadInterruptionContext;
import me.shedaniel.rei.impl.common.plugins.ReloadManagerImpl;
import me.shedaniel.rei.impl.common.registry.RecipeManagerContextImpl;
import me.shedaniel.rei.impl.common.transfer.MenuInfoRegistryImpl;
import me.shedaniel.rei.impl.common.transfer.SlotAccessorRegistryImpl;
Expand Down Expand Up @@ -135,7 +134,7 @@ public static void attachCommonInternals() {
UnaryOperator.identity(),
new EntryTypeRegistryImpl(),
new EntrySettingsAdapterRegistryImpl(),
new RecipeManagerContextImpl<>(RecipeManagerContextImpl.supplier()),
new RecipeManagerContextImpl<>(),
new ItemComparatorRegistryImpl(),
new FluidComparatorRegistryImpl(),
new DisplaySerializerRegistryImpl(),
Expand All @@ -147,28 +146,6 @@ public static void attachCommonInternals() {
new SlotAccessorRegistryImpl()), "serverPluginManager");
}

public static void _reloadPlugins(@Nullable ReloadStage stage) {
if (stage == null) {
for (ReloadStage reloadStage : ReloadStage.values()) {
_reloadPlugins(reloadStage);
}
return;
}
try {
for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) {
instance.view().pre(stage);
}
for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) {
instance.startReload(stage);
}
for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) {
instance.view().post(stage);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

public void onInitialize() {
PluginDetector detector = getPluginDetector();
detector.detectCommonPlugins();
Expand All @@ -179,8 +156,7 @@ public void onInitialize() {
MutableLong lastReload = new MutableLong(-1);
ReloadListenerRegistry.register(PackType.SERVER_DATA, (preparationBarrier, resourceManager, profilerFiller, profilerFiller2, executor, executor2) -> {
return preparationBarrier.wait(Unit.INSTANCE).thenRunAsync(() -> {
PERFORMANCE_LOGGER.clear();
RoughlyEnoughItemsCore._reloadPlugins(null);
ReloadManagerImpl.reloadPlugins(null, ReloadInterruptionContext.ofNever());
}, executor2);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@
package me.shedaniel.rei;

import com.google.common.collect.Lists;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.DataResult;
import dev.architectury.event.Event;
import dev.architectury.event.EventFactory;
import dev.architectury.event.EventResult;
import dev.architectury.event.events.client.ClientGuiEvent;
import dev.architectury.event.events.client.ClientPlayerEvent;
import dev.architectury.event.events.client.ClientRecipeUpdateEvent;
import dev.architectury.event.events.client.ClientScreenInputEvent;
import dev.architectury.networking.NetworkManager;
Expand Down Expand Up @@ -68,7 +67,6 @@
import me.shedaniel.rei.impl.client.entry.renderer.EntryRendererRegistryImpl;
import me.shedaniel.rei.impl.client.favorites.DelegatingFavoriteEntryProviderImpl;
import me.shedaniel.rei.impl.client.favorites.FavoriteEntryTypeRegistryImpl;
import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl;
import me.shedaniel.rei.impl.client.gui.modules.entries.SubMenuEntry;
import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry;
import me.shedaniel.rei.impl.client.gui.widget.InternalWidgets;
Expand All @@ -90,6 +88,8 @@
import me.shedaniel.rei.impl.common.entry.type.collapsed.CollapsibleEntryRegistryImpl;
import me.shedaniel.rei.impl.common.entry.type.types.EmptyEntryDefinition;
import me.shedaniel.rei.impl.common.plugins.PluginManagerImpl;
import me.shedaniel.rei.impl.common.plugins.ReloadManagerImpl;
import me.shedaniel.rei.impl.common.util.InstanceHelper;
import me.shedaniel.rei.impl.common.util.IssuesDetector;
import me.shedaniel.rei.plugin.test.REITestPlugin;
import net.fabricmc.api.EnvType;
Expand Down Expand Up @@ -124,7 +124,6 @@
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
Expand All @@ -136,15 +135,6 @@ public class RoughlyEnoughItemsCoreClient {
public static final Event<ClientRecipeUpdateEvent> PRE_UPDATE_RECIPES = EventFactory.createLoop();
public static final Event<Runnable> POST_UPDATE_TAGS = EventFactory.createLoop();
public static boolean isLeftMousePressed = false;
private static final ExecutorService RELOAD_PLUGINS = Executors.newSingleThreadScheduledExecutor(task -> {
Thread thread = new Thread(task, "REI-ReloadPlugins");
thread.setDaemon(true);
thread.setUncaughtExceptionHandler(($, exception) -> {
InternalLogger.getInstance().throwException(exception);
});
return thread;
});
private static final List<Future<?>> RELOAD_TASKS = new CopyOnWriteArrayList<>();

public static void attachClientInternals() {
InternalWidgets.attach();
Expand Down Expand Up @@ -320,24 +310,25 @@ private static boolean _shouldReturn(Screen screen) {
private void registerEvents() {
Minecraft client = Minecraft.getInstance();
final ResourceLocation recipeButtonTex = new ResourceLocation("textures/gui/recipe_button.png");
MutableLong startReload = new MutableLong(-1);
MutableLong endReload = new MutableLong(-1);
PRE_UPDATE_RECIPES.register(recipeManager -> {
RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear();
reloadPlugins(startReload, ReloadStage.START);
reloadPlugins(null, ReloadStage.START);
});
ClientRecipeUpdateEvent.EVENT.register(recipeManager -> {
reloadPlugins(endReload, ReloadStage.END);
});
ClientPlayerEvent.CLIENT_PLAYER_QUIT.register(player -> {
InternalLogger.getInstance().debug("Player quit, clearing reload tasks!");
endReload.setValue(-1);
ReloadManagerImpl.terminateReloadTasks();
});
ClientGuiEvent.INIT_PRE.register((screen, access) -> {
List<ReloadStage> stages = ((PluginManagerImpl<REIPlugin<?>>) PluginManager.getInstance()).getObservedStages();

if (Minecraft.getInstance().level != null && Minecraft.getInstance().player != null && stages.contains(ReloadStage.START)
&& !stages.contains(ReloadStage.END) && !PluginManager.areAnyReloading() && screen instanceof AbstractContainerScreen) {
for (Future<?> task : RELOAD_TASKS) {
if (!task.isDone()) {
return EventResult.pass();
}
if (ReloadManagerImpl.countRunningReloadTasks() > 0) {
return EventResult.pass();
}

InternalLogger.getInstance().error("Detected missing stage: END! This is possibly due to issues during client recipe reload! REI will force a reload of the recipes now!");
Expand Down Expand Up @@ -476,27 +467,12 @@ public static boolean resetFocused(Screen screen) {
public static void reloadPlugins(MutableLong lastReload, @Nullable ReloadStage start) {
if (Minecraft.getInstance().level == null) return;
if (lastReload != null) {
if (lastReload.getValue() > 0 && System.currentTimeMillis() - lastReload.getValue() <= 5000) {
if (lastReload.getValue() > 0 && System.currentTimeMillis() - lastReload.getValue() <= 1000) {
InternalLogger.getInstance().warn("Suppressing Reload Plugins of stage " + start);
return;
}
lastReload.setValue(System.currentTimeMillis());
}
InternalLogger.getInstance().debug("Starting Reload Plugins of stage " + start, new Throwable());
if (ConfigObject.getInstance().doesRegisterRecipesInAnotherThread()) {
Future<?>[] futures = new Future<?>[1];
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> RoughlyEnoughItemsCore._reloadPlugins(start), RELOAD_PLUGINS)
.whenComplete((unused, throwable) -> {
// Remove the future from the list of futures
if (futures[0] != null) {
RELOAD_TASKS.remove(futures[0]);
futures[0] = null;
}
});
futures[0] = future;
RELOAD_TASKS.add(future);
} else {
RoughlyEnoughItemsCore._reloadPlugins(start);
}
ReloadManagerImpl.reloadPlugins(start, () -> InstanceHelper.connectionFromClient() == null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
package me.shedaniel.rei.impl.client.gui.config.options;

import me.shedaniel.clothconfig2.api.ModifierKeyCode;
import me.shedaniel.rei.RoughlyEnoughItemsCore;
import me.shedaniel.rei.RoughlyEnoughItemsCoreClient;
import me.shedaniel.rei.api.client.config.entry.EntryStackProvider;
import me.shedaniel.rei.api.client.gui.config.*;
Expand Down Expand Up @@ -249,7 +248,6 @@ static <T> CompositeOption<T> make(String id, Function<ConfigObjectImpl, T> bind
.enabledDisabled();
CompositeOption<Object> RELOAD_PLUGINS = make("reset.reload_plugins", i -> null, (i, v) -> new Object())
.reload((access, option, onClose) -> {
RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear();
RoughlyEnoughItemsCoreClient.reloadPlugins(null, null);
while (!PluginManager.areAnyReloading()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
import java.util.Set;

public interface DisplayCache {
int cachedSize();

int notCachedSize();

boolean doesCache();

boolean isCached(Display display);
Expand Down
Loading

0 comments on commit 1de901a

Please sign in to comment.