mirror of
https://github.com/MinecraftForge/MinecraftForge
synced 2026-08-22 04:26:10 -04:00
Add ForgeLayeredDraw & AddGuiOverlayLayersEvent as API for gui overlay management. (#10579)
This commit is contained in:
parent
39f41fcda9
commit
3ce2f03cf2
7 changed files with 678 additions and 1 deletions
|
|
@ -9,6 +9,40 @@
|
|||
private final SubtitleOverlay subtitleOverlay;
|
||||
private final SpectatorGui spectatorGui;
|
||||
private final PlayerTabOverlay tabList;
|
||||
@@ -167,7 +_,7 @@
|
||||
private int lastBubblePopSoundPlayed;
|
||||
private float autosaveIndicatorValue;
|
||||
private float lastAutosaveIndicatorValue;
|
||||
- private final LayeredDraw layers = new LayeredDraw();
|
||||
+ private final LayeredDraw layers = new net.minecraftforge.client.gui.overlay.ForgeLayeredDraw(net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.VANILLA_ROOT, net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.COMBINE_LIST);
|
||||
private float scopeScale;
|
||||
|
||||
public Gui(Minecraft p_330021_) {
|
||||
@@ -179,14 +_,14 @@
|
||||
this.bossOverlay = new BossHealthOverlay(p_330021_);
|
||||
this.subtitleOverlay = new SubtitleOverlay(p_330021_);
|
||||
this.resetTitleTimes();
|
||||
- LayeredDraw layereddraw = new LayeredDraw()
|
||||
+ LayeredDraw layereddraw = new net.minecraftforge.client.gui.overlay.ForgeLayeredDraw(net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.PRE_SLEEP_STACK, net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.PRE_LIST)
|
||||
.add(this::renderCameraOverlays)
|
||||
.add(this::renderCrosshair)
|
||||
.add(this::renderHotbarAndDecorations)
|
||||
.add(this::renderExperienceLevel)
|
||||
.add(this::renderEffects)
|
||||
.add((p_325313_, p_342678_) -> this.bossOverlay.render(p_325313_));
|
||||
- LayeredDraw layereddraw1 = new LayeredDraw()
|
||||
+ LayeredDraw layereddraw1 = new net.minecraftforge.client.gui.overlay.ForgeLayeredDraw(net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.POST_SLEEP_STACK,net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.POST_LIST)
|
||||
.add(this::renderDemoOverlay)
|
||||
.add((p_325315_, p_344521_) -> {
|
||||
if (this.debugOverlay.showDebugScreen()) {
|
||||
@@ -203,6 +_,7 @@
|
||||
.add(layereddraw, () -> !p_330021_.options.hideGui)
|
||||
.add(this::renderSleepOverlay)
|
||||
.add(layereddraw1, () -> !p_330021_.options.hideGui);
|
||||
+ ((net.minecraftforge.client.gui.overlay.ForgeLayeredDraw) layers).resolveLayers();
|
||||
}
|
||||
|
||||
public void resetTitleTimes() {
|
||||
@@ -345,7 +_,7 @@
|
||||
Window window = this.minecraft.getWindow();
|
||||
int i = Mth.floor(this.minecraft.mouseHandler.xpos() * (double)window.getGuiScaledWidth() / (double)window.getScreenWidth());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Fired when the {@linkplain ForgeLayeredDraw#VANILLA_ROOT}'s order is resolved during{@link ForgeLayeredDraw#resolveLayers().
|
||||
* This can be used to add additional or move gui layers and entire layer stacks as needed.
|
||||
*
|
||||
* <p> This event is not {@linkplain net.minecraftforge.eventbus.api.Cancelable cancellable} and does not {@linkplain net.minecraftforge.eventbus.api.Event.HasResult have a result}</p>
|
||||
*
|
||||
* <p> This event is fired on the {@linkplain IModBusEvent mod event bus},
|
||||
* only on the {@linkplain net.minecraftforge.fml.LogicalSide logical client}.</p>
|
||||
*/
|
||||
public final class AddGuiOverlayLayersEvent extends Event implements IModBusEvent {
|
||||
@NotNull
|
||||
private final ForgeLayeredDraw layeredDraw;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public AddGuiOverlayLayersEvent(ForgeLayeredDraw layeredDraw) {
|
||||
this.layeredDraw = layeredDraw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layered draw.
|
||||
* @return the draw layer stack
|
||||
*/
|
||||
@NotNull
|
||||
public ForgeLayeredDraw getLayeredDraw() {
|
||||
return layeredDraw;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
|
|
@ -305,7 +306,11 @@ public final class ForgeEventFactoryClient {
|
|||
// This is on the mod bus because it happens during the initial texture reload, which is while the Forge bus is shut down.
|
||||
return fireModBus(new ModelEvent.RegisterModelStateDefinitions());
|
||||
}
|
||||
|
||||
|
||||
public static void onComputeLayerOrder(ForgeLayeredDraw layeredDraw) {
|
||||
fireModBus(new AddGuiOverlayLayersEvent(layeredDraw));
|
||||
}
|
||||
|
||||
public static void onLastFramePassAddition(FrameGraphBuilder builder, LevelTargetBundle bundle) {
|
||||
post(new AddFramePassEvent(builder, bundle));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.gui.overlay;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.FieldsAreNonnullByDefault;
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.LayeredDraw;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.event.ForgeEventFactoryClient;
|
||||
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.*;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/**
|
||||
* As vanilla has switched to a layered drawing system for overlays, this system replaces ForgeGui and its associated headaches.
|
||||
* Vanilla will now have resource locations to represent its render layers which modders can order against.
|
||||
* This class is effectively a pseudo-registry for Layers. Add what you need during {@linkplain AddGuiOverlayLayersEvent}
|
||||
* Layers must be uniquely named per ForgeLayeredDraw, but may be duplicated across different instances.
|
||||
* Any change which would result in a duplicate will not be applied.
|
||||
* All methods which return a {@linkplain ForgeLayeredDraw} will return its caller's instance.
|
||||
* To select a specific instance, use {@linkplain ForgeLayeredDraw#locateStack(ResourceLocation)} before adding
|
||||
* or use the methods that include a stack identifier.
|
||||
*/
|
||||
@FieldsAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
@ParametersAreNonnullByDefault
|
||||
public final class ForgeLayeredDraw extends LayeredDraw {
|
||||
private static int unknown = 0;
|
||||
private final Map<ResourceLocation, Layer> namedLayers = new HashMap<>();
|
||||
private final Map<ResourceLocation, Map.Entry<LayeredDraw, BooleanSupplier>> subLayerStacks = new HashMap<>();
|
||||
private final List<ResourceLocation> order = new LinkedList<>();
|
||||
private final List<ResourceLocation> expectedNames = new ArrayList<>();
|
||||
private final ResourceLocation name;
|
||||
|
||||
public static final ResourceLocation PRE_SLEEP_STACK = ResourceLocation.withDefaultNamespace("pre_sleep_phase");
|
||||
public static final ResourceLocation CAMERA_OVERLAY = ResourceLocation.withDefaultNamespace("camera_overlay");
|
||||
public static final ResourceLocation CROSSHAIR = ResourceLocation.withDefaultNamespace("crosshair");
|
||||
public static final ResourceLocation HOTBAR = ResourceLocation.withDefaultNamespace("hotbar");
|
||||
public static final ResourceLocation EXPERIENCE = ResourceLocation.withDefaultNamespace("experience");
|
||||
public static final ResourceLocation POTION_EFFECTS = ResourceLocation.withDefaultNamespace("potion_effects");
|
||||
public static final ResourceLocation BOSS_OVERLAY = ResourceLocation.withDefaultNamespace("boss_overlay");
|
||||
|
||||
public static final ResourceLocation POST_SLEEP_STACK = ResourceLocation.withDefaultNamespace("post_sleep_phase");
|
||||
public static final ResourceLocation DEMO_OVERLAY = ResourceLocation.withDefaultNamespace("demo");
|
||||
public static final ResourceLocation DEBUG_OVERLAY = ResourceLocation.withDefaultNamespace("debug");
|
||||
public static final ResourceLocation SCOREBOARD = ResourceLocation.withDefaultNamespace("scoreboard");
|
||||
public static final ResourceLocation HOTBAR_MESSAGE = ResourceLocation.withDefaultNamespace("hotbar_message");
|
||||
public static final ResourceLocation TITLE_OVERLAY = ResourceLocation.withDefaultNamespace("title");
|
||||
public static final ResourceLocation CHAT_OVERLAY = ResourceLocation.withDefaultNamespace("chat_overlay");
|
||||
public static final ResourceLocation TAB_LIST = ResourceLocation.withDefaultNamespace("tab_list");
|
||||
public static final ResourceLocation SUBTITLE_OVERLAY = ResourceLocation.withDefaultNamespace("subtitle");
|
||||
|
||||
public static final ResourceLocation VANILLA_ROOT = ResourceLocation.withDefaultNamespace("vanilla_root");
|
||||
public static final ResourceLocation SLEEP_OVERLAY = ResourceLocation.withDefaultNamespace("sleep_overlay");
|
||||
|
||||
|
||||
public static final ImmutableList<ResourceLocation> PRE_LIST = ImmutableList.of(CAMERA_OVERLAY, CROSSHAIR, HOTBAR, EXPERIENCE, POTION_EFFECTS, BOSS_OVERLAY);
|
||||
public static final ImmutableList<ResourceLocation> POST_LIST = ImmutableList.of(DEMO_OVERLAY, DEBUG_OVERLAY, SCOREBOARD, HOTBAR_MESSAGE, TITLE_OVERLAY, CHAT_OVERLAY, TAB_LIST, SUBTITLE_OVERLAY);
|
||||
public static final ImmutableList<ResourceLocation> COMBINE_LIST = ImmutableList.of(PRE_SLEEP_STACK, SLEEP_OVERLAY, POST_SLEEP_STACK);
|
||||
|
||||
/**
|
||||
* Creates a stack of pre-named layers for vanilla.
|
||||
* Not intended for modder use. Use {@linkplain ForgeLayeredDraw(ResourceLocation, Boolean, String...)} instead.
|
||||
* @param name Stack identifier
|
||||
* @param layers locations of layers to add
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public ForgeLayeredDraw(ResourceLocation name, List<ResourceLocation> layers) {
|
||||
this.name = name;
|
||||
expectedNames.addAll(layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty draw list. Add entries with {@linkplain ForgeLayeredDraw#add(ResourceLocation, Layer)}
|
||||
* @param name marker for which phase this is.
|
||||
*/
|
||||
public ForgeLayeredDraw(ResourceLocation name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a full, pre-named draw stack.
|
||||
* @param layeredDraw layer stack to be added.
|
||||
* @param booleanSupplier requirement for it to render.
|
||||
* @deprecated Modders should be using {@linkplain ForgeLayeredDraw#add(ResourceLocation, LayeredDraw, BooleanSupplier)}
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public ForgeLayeredDraw add(LayeredDraw layeredDraw, BooleanSupplier booleanSupplier) {
|
||||
return add(getNextPreName(), layeredDraw, booleanSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a full draw stack with its provided condition.
|
||||
* Use {@linkplain ForgeLayeredDraw#putAbove} and {@linkplain ForgeLayeredDraw#putBelow} for fine location adjustment.
|
||||
* @param name RL of the name to identify this stack with.
|
||||
* @param layeredDraw the draw stack
|
||||
* @param supplier condition for this stack to render
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw add(ResourceLocation name, LayeredDraw layeredDraw, BooleanSupplier supplier) {
|
||||
if (isNameAvailable(name)) {
|
||||
subLayerStacks.put(name, Map.entry(layeredDraw, supplier));
|
||||
order.add(name);
|
||||
} else {
|
||||
nameTakenWarning(name);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a layer with an already known name provided by {@linkplain ForgeLayeredDraw(ResourceLocation, List)}
|
||||
* The layer will be rendered last (on top) of already added layers.
|
||||
* @param layer layer render code, see {@linkplain Layer} and example usages in {@linkplain Gui}
|
||||
* @deprecated Use {@linkplain ForgeLayeredDraw#add(ResourceLocation, Layer)} for adding individual layers.
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public LayeredDraw add(Layer layer) {
|
||||
add(getNextPreName(), layer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layer to the layer list. This layer will be at the end of the list, which means
|
||||
* it will be rendered last (on top) of already added layers.
|
||||
* @param name RL for other mods to order against.
|
||||
* @param layer layer render code, see {@linkplain Layer} and example usages in {@linkplain Gui}
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw add(ResourceLocation targetStack, ResourceLocation name, Layer layer) {
|
||||
locateStack(targetStack).ifPresentOrElse((stack) -> stack.add(name, layer), () -> stackNotPresentWarning(targetStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method, assumes the intended draw stack instance is the caller.
|
||||
* Use any of the non-deprecated add methods if you want a specific instance,
|
||||
* or call {@linkplain ForgeLayeredDraw#locateStack(ResourceLocation)} to get a reference to an instance.
|
||||
* @param name RL of layer to add
|
||||
* @param layer layer render code, see {@linkplain Layer} and example usages in {@linkplain Gui}
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw add(ResourceLocation name, Layer layer) {
|
||||
if (isNameAvailable(name)) {
|
||||
namedLayers.put(name, layer);
|
||||
order.add(name);
|
||||
} else {
|
||||
nameTakenWarning(name);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to specify where your custom draw stack should go. Can also be used to re-order layers.
|
||||
* Both target and destination must be in the same draw stack.
|
||||
* @param expectedStack Which ForgeLayeredDraw the target is expected to be in. If you already have a reference to it, you can use {@linkplain ForgeLayeredDraw#move(ResourceLocation, ResourceLocation, LayerOffset)}
|
||||
* @param target layer name to move
|
||||
* @param destination layer name to order against
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw putAbove(ResourceLocation expectedStack, ResourceLocation target, ResourceLocation destination) {
|
||||
locateStack(expectedStack).ifPresentOrElse((stack) -> stack.move(target, destination, LayerOffset.ABOVE), () -> stackNotPresentWarning(expectedStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to specify where your custom draw stack should go. Can also be used to re-order layers.
|
||||
* Both target and destination must be in the same draw stack.
|
||||
* @param expectedStack Which ForgeLayeredDraw the target is expected to be in. If you already have a reference to it, you can use {@linkplain ForgeLayeredDraw#move(ResourceLocation, ResourceLocation, LayerOffset)}
|
||||
* @param target layer name to move
|
||||
* @param destination layer name to order against
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw putBelow(ResourceLocation expectedStack, ResourceLocation target, ResourceLocation destination) {
|
||||
locateStack(expectedStack).ifPresentOrElse((stack) -> stack.move(target, destination, LayerOffset.BELOW), () -> stackNotPresentWarning(expectedStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method, assumes the intended draw stack instance is the caller.
|
||||
* Moves pre-existing names around within the order.
|
||||
* @param target Layer being moved.
|
||||
* @param destination Layer being ordered against
|
||||
* @param offset Self-explanatory
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw move(ResourceLocation target, ResourceLocation destination, LayerOffset offset) {
|
||||
if (!order.contains(target)) {
|
||||
layerNotPresentWarning(target);
|
||||
return this;
|
||||
}
|
||||
int loc = order.indexOf(destination);
|
||||
if (loc == -1) {
|
||||
layerNotPresentWarning(destination);
|
||||
return this;
|
||||
}
|
||||
order.remove(target);
|
||||
order.add(loc + (offset == LayerOffset.ABOVE ? 1 : 0), target);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an overlay layer to be rendered above the other provided layer.
|
||||
* To render "above" another layer means thisLayer will be rendered after otherLayer
|
||||
* If the current stack does not contain otherLayer, no changes will be made.
|
||||
* @param newLayer name of the layer to be added
|
||||
* @param otherLayer name of the layer being ordered against
|
||||
* @param layer layer render code, see {@linkplain Layer} and example usages in {@linkplain Gui}
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addAbove(ResourceLocation expectedStack, ResourceLocation newLayer, ResourceLocation otherLayer, Layer layer) {
|
||||
locateStack(expectedStack).ifPresentOrElse((stack) -> {
|
||||
if (!stack.isNameAvailable(otherLayer)) {
|
||||
stack.add(newLayer, layer).move(newLayer, otherLayer, LayerOffset.ABOVE);
|
||||
} else {
|
||||
layerNotPresentWarning(otherLayer);
|
||||
}
|
||||
}, () -> stackNotPresentWarning(expectedStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method, assumes intended draw stack is the caller
|
||||
*/
|
||||
public ForgeLayeredDraw addAbove(ResourceLocation newLayer, ResourceLocation otherLayer, Layer layer) {
|
||||
return addAbove(name, newLayer, otherLayer, layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an overlay layer to be rendered below the other provided layer.
|
||||
* To render "below" another layer means thisLayer will be rendered before otherLayer
|
||||
* If the current stack does not contain otherLayer, no changes will be made.
|
||||
* @param newLayer name of the layer to be added
|
||||
* @param otherLayer name of the layer being ordered against
|
||||
* @param layer layer render code, see {@linkplain Layer} and example usages in {@linkplain Gui}
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addBelow(ResourceLocation expectedStack, ResourceLocation newLayer, ResourceLocation otherLayer, Layer layer) {
|
||||
locateStack(expectedStack).ifPresentOrElse((stack) -> {
|
||||
if (!stack.isNameAvailable(otherLayer)) {
|
||||
stack.add(newLayer, layer).move(newLayer, otherLayer, LayerOffset.BELOW);
|
||||
} else {
|
||||
layerNotPresentWarning(otherLayer);
|
||||
}
|
||||
}, () -> stackNotPresentWarning(expectedStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method, assumes intended draw stack is the caller
|
||||
*/
|
||||
public ForgeLayeredDraw addBelow(ResourceLocation newLayer, ResourceLocation otherLayer, Layer layer) {
|
||||
return addBelow(name, newLayer, otherLayer, layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new layer that will only be rendered when the condition is met. The layer will be added
|
||||
* at the end of the list, which means it will render last (on top) of already added layers
|
||||
* @param name name of the layer to be added
|
||||
* @param layer render code of layer being added
|
||||
* @param condition supplier for the condition
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addWithCondition(ResourceLocation targetStack, ResourceLocation name, Layer layer, BooleanSupplier condition) {
|
||||
locateStack(targetStack).ifPresentOrElse((stack) -> stack.addWithCondition(name, layer, condition), () -> stackNotPresentWarning(targetStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes the correct stack is the caller of this method, otherwise functions the same as other method.
|
||||
* @param name name of layer to be added
|
||||
* @param layer render code of layer being added
|
||||
* @param condition supplier for the condition
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addWithCondition(ResourceLocation name, Layer layer, BooleanSupplier condition) {
|
||||
add(name, layer).addConditionTo(name, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a condition to a pre-existing layer in the specified stack, its render order is not changed.
|
||||
* If the target is not present, no changes are made.
|
||||
* @param targetStack name of the draw stack the target is expected to be in
|
||||
* @param targetLayer name of layer to add a condition to
|
||||
* @param condition supplier for the condition
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addConditionTo(ResourceLocation targetStack, ResourceLocation targetLayer, BooleanSupplier condition) {
|
||||
locateStack(targetStack).ifPresentOrElse((stack) -> stack.addConditionTo(targetLayer, condition), () -> stackNotPresentWarning(targetStack));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes the correct stack is the caller of this method.
|
||||
* Use {@linkplain ForgeLayeredDraw#addConditionTo(ResourceLocation, ResourceLocation, BooleanSupplier)}
|
||||
* if you do not have a reference to the draw stack you want.
|
||||
* @param targetLayer name of layer to add condition to
|
||||
* @param condition supplier for the condition
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw addConditionTo(ResourceLocation targetLayer, BooleanSupplier condition) {
|
||||
var result = namedLayers.computeIfPresent(targetLayer,
|
||||
(name, layer) -> (guiGraphics, deltaTracker) -> {
|
||||
if (condition.getAsBoolean()) {
|
||||
layer.render(guiGraphics, deltaTracker);
|
||||
}
|
||||
});
|
||||
if (result == null) {
|
||||
layerNotPresentWarning(targetLayer);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate the layer order down to the inner render list after providing modders an opportunity to alter the list as they wish.
|
||||
* @apiNote Modders should <emph>NEVER</emph> be calling this method.
|
||||
* @return this
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public ForgeLayeredDraw resolveLayers() {
|
||||
if (!order.isEmpty()) {
|
||||
ForgeEventFactoryClient.onComputeLayerOrder(this);
|
||||
}
|
||||
if (namedLayers.size() + subLayerStacks.size() < order.size()) {
|
||||
LogUtils.getLogger().warn("Found {} unbound pre-defined layer names when resolving gui overlay order. This is not an error, but potentially indicates a mod directly modifying Gui instead of using this api.", order.size() - namedLayers.size());
|
||||
}
|
||||
resolveNested();
|
||||
order.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the layer order per stack, recursively as needed.
|
||||
* Parent layer stack {@linkplain ForgeLayeredDraw#VANILLA_ROOT} holds the results.
|
||||
*/
|
||||
private void resolveNested() {
|
||||
for (ResourceLocation layerName : order) {
|
||||
if (subLayerStacks.containsKey(layerName)) {
|
||||
var entry = subLayerStacks.get(layerName);
|
||||
if (entry.getKey() instanceof ForgeLayeredDraw resolveable) {
|
||||
resolveable.resolveNested();
|
||||
// Although unlikely, a LayeredDraw could be in the list, and we can't call resolveNested on one.
|
||||
}
|
||||
super.add((gg, tr) -> {
|
||||
if (entry.getValue().getAsBoolean()) entry.getKey().render(gg,tr);
|
||||
});
|
||||
} else {
|
||||
super.add(namedLayers.get(layerName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to locate a particular draw stack. Search starts at caller's instance. For global search call on VANILLA_ROOT
|
||||
* i.e. {@linkplain net.minecraftforge.client.event.AddGuiOverlayLayersEvent#getLayeredDraw()}
|
||||
* Entries which don't extend ForgeLayeredDraw may not have the proper fields to support internal list adjustment, so they are skipped.
|
||||
* @param targetStack Name of ForgeLayeredDraw to find
|
||||
* @return Filled Optional if target exists, empty otherwise.
|
||||
*/
|
||||
public Optional<ForgeLayeredDraw> locateStack(ResourceLocation targetStack) {
|
||||
if (!name.equals(targetStack)) {
|
||||
for (Map.Entry<LayeredDraw, BooleanSupplier> value : subLayerStacks.values()) {
|
||||
if (value.getKey() instanceof ForgeLayeredDraw searchable) {
|
||||
var res = searchable.locateStack(targetStack);
|
||||
if(res.isPresent()) return res;
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LayeredDraw getChild(ResourceLocation childName) {
|
||||
return locateStack(childName).orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Layer getLayer(ResourceLocation layerName) {
|
||||
return namedLayers.get(layerName);
|
||||
}
|
||||
|
||||
private ResourceLocation getNextPreName() {
|
||||
return expectedNames.isEmpty() ? ResourceLocation.fromNamespaceAndPath("unknown", "layer_" + unknown++) : expectedNames.removeFirst();
|
||||
}
|
||||
|
||||
public ResourceLocation getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private void stackNotPresentWarning(ResourceLocation stackName) {
|
||||
LogUtils.getLogger().warn("Target stack {} was not present anywhere. Is your ResourceLocation correct?", stackName);
|
||||
}
|
||||
|
||||
private void layerNotPresentWarning(ResourceLocation layer) {
|
||||
LogUtils.getLogger().warn("Expected layer {} was not found in stack {}, no layer modifications have been made.", layer, name);
|
||||
}
|
||||
|
||||
private void nameTakenWarning(ResourceLocation layer) {
|
||||
LogUtils.getLogger().warn("Name {} was already present in {} and cannot be re-used.", layer, name);
|
||||
}
|
||||
|
||||
private boolean isNameAvailable(ResourceLocation name) {
|
||||
return !namedLayers.containsKey(name) && !subLayerStacks.containsKey(name);
|
||||
}
|
||||
|
||||
public enum LayerOffset {
|
||||
ABOVE,
|
||||
BELOW
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.client;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDraw;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
|
||||
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
|
||||
import net.minecraftforge.common.extensions.IForgeGameTestHelper;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import static net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.*;
|
||||
|
||||
|
||||
@Mod(ModifyOverlayTest.MODID)
|
||||
@GameTestHolder("forge." + ModifyOverlayTest.MODID)
|
||||
public class ModifyOverlayTest extends BaseTestMod {
|
||||
public static final String MODID = "modify_overlay_test";
|
||||
|
||||
private static final ResourceLocation myStackName = name("my_stack_name");
|
||||
private static final ForgeLayeredDraw myLayerStack = new ForgeLayeredDraw(myStackName);
|
||||
|
||||
private static final Layer notAddedLayer = (gg,tr) -> {};
|
||||
private static final Layer layerA = (gg,tr) -> {};
|
||||
private static final ResourceLocation layerAName = name("layer_a");
|
||||
private static final Layer layerB = (gg,tr) -> {};
|
||||
private static final ResourceLocation layerBName = name("layer_b");
|
||||
private static final Layer layerC = (gg,tr) -> {};
|
||||
private static final ResourceLocation layerCName = name("layer_c");
|
||||
|
||||
|
||||
private static final IForgeGameTestHelper.BoolFlag detectConditionFlag = new IForgeGameTestHelper.BoolFlag("det_cond_flag");
|
||||
private static final IForgeGameTestHelper.BoolFlag detectConditionStackFlag = new IForgeGameTestHelper.BoolFlag("det_cond_stack_flag");
|
||||
private static final IForgeGameTestHelper.BoolFlag enableConditionFlag = new IForgeGameTestHelper.BoolFlag("en_cond_flag");
|
||||
private static final IForgeGameTestHelper.BoolFlag enableConditionStackFlag = new IForgeGameTestHelper.BoolFlag("en_cond_stack_flag");
|
||||
|
||||
private static ForgeLayeredDraw drawStack;
|
||||
|
||||
public ModifyOverlayTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
context.getModEventBus().addListener(this::overlayTestListener);
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void not_present_in_stack(GameTestHelper helper) {
|
||||
// Test that we can't order against non-existent layers.
|
||||
List<Layer> internalLayersList = null;
|
||||
try {
|
||||
internalLayersList = getInternalLayersList(drawStack);
|
||||
} catch (Exception e) {
|
||||
helper.fail("Threw a " + e.getMessage() + " when trying to get the inner layer list.");
|
||||
}
|
||||
helper.assertFalse(internalLayersList.contains(notAddedLayer), "Found our layer when we shouldn't have. Not good!");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void add_condition(GameTestHelper helper) {
|
||||
// Test that we can add conditions to pre-existing layers.
|
||||
helper.assertFalse(detectConditionFlag.getBool(), "Conditional rendering ran when it shouldn't have.");
|
||||
enableConditionFlag.set(true);
|
||||
helper.runAfterDelay(5, () -> {
|
||||
boolean result = detectConditionFlag.getBool();// Just in case of weird race conditions.
|
||||
enableConditionFlag.set(false);
|
||||
helper.assertTrue(result, "");
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void ordered_layers(GameTestHelper helper) {
|
||||
// Test that layers are in the correct order.
|
||||
List<Layer> internalLayersList = null;
|
||||
Map<ResourceLocation, Layer> check = null;
|
||||
try {
|
||||
Class<?> cls = drawStack.getClass();
|
||||
var field = cls.getDeclaredField("subLayerStacks");
|
||||
var field1 = cls.getDeclaredField("namedLayers");
|
||||
field.setAccessible(true);
|
||||
field1.setAccessible(true);
|
||||
Map<ResourceLocation, Map.Entry<LayeredDraw, BooleanSupplier>> VROOT = ((Map<ResourceLocation, Map.Entry<LayeredDraw, BooleanSupplier>>) field.get(drawStack));
|
||||
var PSS = ((ForgeLayeredDraw) VROOT.get(PRE_SLEEP_STACK).getKey());
|
||||
check = ((Map<ResourceLocation, Layer>) field1.get(PSS));
|
||||
internalLayersList = getInternalLayersList(PSS);
|
||||
} catch (Exception e) {
|
||||
helper.fail("Threw a " + e.getMessage() + " when trying to get the inner layer list.");
|
||||
}
|
||||
int locationOfPotionEffects = internalLayersList.indexOf(check.get(POTION_EFFECTS));
|
||||
int locationOfLayerA = internalLayersList.indexOf(check.get(layerAName));
|
||||
int locationOfLayerB = internalLayersList.indexOf(check.get(layerBName));
|
||||
int locationOfLayerC = internalLayersList.indexOf(check.get(layerCName));
|
||||
helper.assertTrue(locationOfPotionEffects == locationOfLayerA - 1, "Layer offset from vanilla -> A was incorrect");
|
||||
helper.assertTrue(locationOfLayerB == locationOfLayerA + 1, "Layer offset from A -> B was incorrect");
|
||||
helper.assertTrue(locationOfLayerC == locationOfLayerB + 1, "Layer offset from B -> C was incorrect");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void full_stack_condition(GameTestHelper helper) {
|
||||
// Test conditional rendering & whether a full stack can be added in the first place.
|
||||
helper.assertFalse(detectConditionStackFlag.getBool(), "Conditional rendering ran when it shouldn't have.");
|
||||
enableConditionStackFlag.set(true);
|
||||
helper.runAfterDelay(5, () -> {
|
||||
boolean result = detectConditionStackFlag.getBool();// Just in case of weird race conditions.
|
||||
enableConditionStackFlag.set(false);
|
||||
helper.assertTrue(result, "");
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void overlayTestListener(AddGuiOverlayLayersEvent event) {
|
||||
drawStack = event.getLayeredDraw();
|
||||
var layeredDraw = event.getLayeredDraw();
|
||||
layeredDraw.addAbove(VANILLA_ROOT, name("i_won_t_exist"), name("non_existent_target_layer"), notAddedLayer);
|
||||
var pEffects = layeredDraw.locateStack(PRE_SLEEP_STACK).get();
|
||||
// Test if layers may be ordered against other layers.
|
||||
// Layers have to be present to be ordered against, of course, but we tested for that already above.
|
||||
pEffects.addAbove(layerBName, POTION_EFFECTS, layerB);
|
||||
pEffects.addAbove(layerCName, layerBName, layerC);
|
||||
layeredDraw.addBelow(PRE_SLEEP_STACK, layerAName, layerBName, layerA);
|
||||
layeredDraw.addConditionTo(PRE_SLEEP_STACK, BOSS_OVERLAY, () -> {
|
||||
if (enableConditionFlag.getBool()) {
|
||||
detectConditionFlag.set(true);
|
||||
return true;
|
||||
} else {
|
||||
detectConditionFlag.set(false);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
myLayerStack.add(name("my_inner_layer_name"), (gg, tr) -> {
|
||||
detectConditionStackFlag.set(true);
|
||||
});
|
||||
// Demonstrates that entire stacks can have conditions attached
|
||||
event.getLayeredDraw()
|
||||
.add(myStackName, myLayerStack, () -> {
|
||||
if (enableConditionStackFlag.getBool()) {
|
||||
return true;
|
||||
} else {
|
||||
detectConditionStackFlag.set(false);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static ResourceLocation name(String name) {
|
||||
return ResourceLocation.fromNamespaceAndPath(MODID, name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Layer> getInternalLayersList(ForgeLayeredDraw stack) throws ClassCastException, NoSuchFieldException, IllegalAccessException {
|
||||
Class<?> cls = stack.getClass().getSuperclass();
|
||||
var layersField = cls.getDeclaredField("layers");
|
||||
layersField.setAccessible(true);
|
||||
return (List<Layer>) layersField.get(stack);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
clientSideOnly=true
|
||||
Loading…
Add table
Add a link
Reference in a new issue