mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-12 06:54:27 -04:00
Add Hud Render Events (#4119)
* Add HudRenderEvents
* Add HudRenderEventsTests and deprecate HudRenderCallback
* Update tests
* Add client parameter and apply suggestions
* Split HudRenderEvents into separate interfaces
* Fix before chat and last
* Add after sleep overlay event and update after main hud injection point
* Add comments for injection points
* Revert splitting HudRenderEvents into separate interfaces
* Use vanilla layered drawer layer interface
* Cleanup InGameHudMixin
* POC of hud modification
* Implement HudLayerRegistrationCallback
* Delete HudRenderEvents
* Fix sub drawers and add basic documentation
* Fix checkstyle
* Apply suggestions from code review
* Add Javadocs
* Add more unit tests
* Apply suggestions from code review
- Update Javadocs
- Remove vanilla sub drawer flattening
- Improve LayeredDrawerWrapperImpl internals
* Javadoc oddities
* Add client gametests
* Finish client gametests
* Change method and add documentation
* Ensure test environment is correct
* Move test class to same package
* Add render condition for tests
* Fix merge conflicts
* Update javadocs
* Small bug fixes, documentation, and sub drawer tests
* Update javadocs some more
* Add contract and get around return value not used warnings
* Apply suggestions from code review
Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
* Migrate AtomicBoolean to MutableBoolean
* Update javadocs on render condition
* Use ListIterator#set
---------
Co-authored-by: modmuss50 <modmuss50@gmail.com>
Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
(cherry picked from commit 44a0820dd2
)
This commit is contained in:
parent
19c9635598
commit
a3630f92b3
24 changed files with 1269 additions and 2 deletions
fabric-client-gametest-api-v1/src/client/java/net/fabricmc/fabric
api/client/gametest/v1/screenshot
impl/client/gametest/screenshot
fabric-rendering-v1
build.gradle
src
client
java/net/fabricmc/fabric
api/client/rendering/v1
HudLayerRegistrationCallback.javaHudRenderCallback.javaIdentifiedLayer.javaLayeredDrawerWrapper.java
impl/client/rendering
mixin/client/rendering
resources
test/java/net/fabricmc/fabric/impl/client/rendering
testmod/resources
testmodClient
java/net/fabricmc/fabric/test/rendering/client
resources/templates
|
@ -69,6 +69,17 @@ public interface TestScreenshotComparisonOptions extends TestScreenshotCommonOpt
|
|||
return new TestScreenshotComparisonOptionsImpl(templateImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally save the screenshot which was compared against with the template image name.
|
||||
* This method only works when a template image name instead of a {@link NativeImage} is used.
|
||||
* This method works as if by calling {@link ClientGameTestContext#takeScreenshot(TestScreenshotOptions)}
|
||||
* with these screenshot options, except that the screenshot saved is from the same render of the game
|
||||
* as the one that is compared against in this screenshot comparison.
|
||||
*
|
||||
* @return This screenshot comparison options instance
|
||||
*/
|
||||
TestScreenshotComparisonOptions save();
|
||||
|
||||
/**
|
||||
* Additionally save the screenshot which was compared against. This method works as if by calling
|
||||
* {@link ClientGameTestContext#takeScreenshot(TestScreenshotOptions)} with these screenshot options, except that
|
||||
|
|
|
@ -50,6 +50,11 @@ public final class TestScreenshotComparisonOptionsImpl extends TestScreenshotCom
|
|||
this.templateImage = Either.right(templateImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestScreenshotComparisonOptions save() {
|
||||
return saveWithFileName(getTemplateImagePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestScreenshotComparisonOptions saveWithFileName(String fileName) {
|
||||
Preconditions.checkNotNull(fileName, "fileName");
|
||||
|
|
|
@ -6,5 +6,6 @@ moduleDependencies(project, [
|
|||
])
|
||||
|
||||
testDependencies(project, [
|
||||
':fabric-client-gametest-api-v1',
|
||||
':fabric-object-builder-api-v1'
|
||||
])
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Callback for when hud layers are registered.
|
||||
*
|
||||
* <p>To register a layer, register a listener to this event and register your layers in the listener.
|
||||
* For common use cases, see {@link LayeredDrawerWrapper}.
|
||||
*
|
||||
* <p>For example, the following code registers a layer after {@link IdentifiedLayer#MISC_OVERLAYS}:
|
||||
* {@snippet :
|
||||
* // @link region substring=HudLayerRegistrationCallback target=HudLayerRegistrationCallback
|
||||
* // @link region substring=EVENT target="HudLayerRegistrationCallback#EVENT"
|
||||
* // @link region substring=layeredDrawer target="LayeredDrawerWrapper"
|
||||
* // @link region substring=attachLayerAfter target="LayeredDrawerWrapper#attachLayerAfter"
|
||||
* // @link region substring=IdentifiedLayer target=IdentifiedLayer
|
||||
* // @link region substring=MISC_OVERLAYS target="IdentifiedLayer#MISC_OVERLAYS"
|
||||
* // @link region substring=Identifier target="net.minecraft.util.Identifier"
|
||||
* // @link region substring=of target="net.minecraft.util.Identifier#of"
|
||||
* // @link region substring=context target="net.minecraft.client.gui.DrawContext"
|
||||
* // @link region substring=tickCounter target="net.minecraft.client.render.RenderTickCounter"
|
||||
* HudLayerRegistrationCallback.EVENT.register(layeredDrawer -> layeredDrawer.attachLayerAfter(IdentifiedLayer.MISC_OVERLAYS, Identifier.of("example", "example_layer_after_misc_overlays"), (context, tickCounter) -> {
|
||||
* // Your rendering code here
|
||||
* }));
|
||||
* // @end @end @end @end @end @end @end @end @end @end
|
||||
* }
|
||||
*
|
||||
* @see LayeredDrawerWrapper
|
||||
*/
|
||||
public interface HudLayerRegistrationCallback {
|
||||
Event<HudLayerRegistrationCallback> EVENT = EventFactory.createArrayBacked(HudLayerRegistrationCallback.class, callbacks -> layeredDrawer -> {
|
||||
for (HudLayerRegistrationCallback callback : callbacks) {
|
||||
callback.register(layeredDrawer);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called when registering hud layers.
|
||||
*
|
||||
* @param layeredDrawer the layered drawer to register layers to
|
||||
* @see LayeredDrawerWrapper
|
||||
*/
|
||||
void register(LayeredDrawerWrapper layeredDrawer);
|
||||
}
|
|
@ -22,10 +22,14 @@ import net.minecraft.client.render.RenderTickCounter;
|
|||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link HudLayerRegistrationCallback} instead. For common use cases, see {@link LayeredDrawerWrapper}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface HudRenderCallback {
|
||||
Event<HudRenderCallback> EVENT = EventFactory.createArrayBacked(HudRenderCallback.class, (listeners) -> (matrixStack, delta) -> {
|
||||
Event<HudRenderCallback> EVENT = EventFactory.createArrayBacked(HudRenderCallback.class, (listeners) -> (context, tickCounter) -> {
|
||||
for (HudRenderCallback event : listeners) {
|
||||
event.onHudRender(matrixStack, delta);
|
||||
event.onHudRender(context, tickCounter);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.rendering.WrappedLayer;
|
||||
|
||||
/**
|
||||
* A hud layer that has an identifier attached for use in {@link LayeredDrawerWrapper}.
|
||||
*
|
||||
* <p>The identifiers in this interface are the vanilla hud layers in the order they are drawn in.
|
||||
* The first layer is drawn first, which means it is at the bottom.
|
||||
* All vanilla layers except {@link #SLEEP} are in sub drawers and have a render condition attached ({@link net.minecraft.client.option.GameOptions#hudHidden}).
|
||||
* Operations relative to any layer will generally inherit that layer's render condition.
|
||||
* There is currently no mechanism to change the render condition of a layer.
|
||||
*
|
||||
* <p>For common use cases and more details on how this API deals with render condition, see {@link LayeredDrawerWrapper}.
|
||||
*/
|
||||
public interface IdentifiedLayer extends LayeredDrawer.Layer {
|
||||
/**
|
||||
* The identifier for the vanilla miscellaneous overlays (such as vignette, spyglass, and powder snow) layer.
|
||||
*/
|
||||
Identifier MISC_OVERLAYS = Identifier.ofVanilla("misc_overlays");
|
||||
/**
|
||||
* The identifier for the vanilla crosshair layer.
|
||||
*/
|
||||
Identifier CROSSHAIR = Identifier.ofVanilla("crosshair");
|
||||
/**
|
||||
* The identifier for the vanilla hotbar, spectator hud, experience bar, and status bars layer.
|
||||
*/
|
||||
Identifier HOTBAR_AND_BARS = Identifier.ofVanilla("hotbar_and_bars");
|
||||
/**
|
||||
* The identifier for the vanilla experience level layer.
|
||||
*/
|
||||
Identifier EXPERIENCE_LEVEL = Identifier.ofVanilla("experience_level");
|
||||
/**
|
||||
* The identifier for the vanilla status effects layer.
|
||||
*/
|
||||
Identifier STATUS_EFFECTS = Identifier.ofVanilla("status_effects");
|
||||
/**
|
||||
* The identifier for the vanilla boss bar layer.
|
||||
*/
|
||||
Identifier BOSS_BAR = Identifier.ofVanilla("boss_bar");
|
||||
/**
|
||||
* The identifier for the vanilla sleep overlay layer.
|
||||
*/
|
||||
Identifier SLEEP = Identifier.ofVanilla("sleep");
|
||||
/**
|
||||
* The identifier for the vanilla demo timer layer.
|
||||
*/
|
||||
Identifier DEMO_TIMER = Identifier.ofVanilla("demo_timer");
|
||||
/**
|
||||
* The identifier for the vanilla debug hud layer.
|
||||
*/
|
||||
Identifier DEBUG = Identifier.ofVanilla("debug");
|
||||
/**
|
||||
* The identifier for the vanilla scoreboard layer.
|
||||
*/
|
||||
Identifier SCOREBOARD = Identifier.ofVanilla("scoreboard");
|
||||
/**
|
||||
* The identifier for the vanilla overlay message layer.
|
||||
*/
|
||||
Identifier OVERLAY_MESSAGE = Identifier.ofVanilla("overlay_message");
|
||||
/**
|
||||
* The identifier for the vanilla title and subtitle layer.
|
||||
*
|
||||
* <p>Note that this is not the sound subtitles.
|
||||
*/
|
||||
Identifier TITLE_AND_SUBTITLE = Identifier.ofVanilla("title_and_subtitle");
|
||||
/**
|
||||
* The identifier for the vanilla chat layer.
|
||||
*/
|
||||
Identifier CHAT = Identifier.ofVanilla("chat");
|
||||
/**
|
||||
* The identifier for the vanilla player list layer.
|
||||
*/
|
||||
Identifier PLAYER_LIST = Identifier.ofVanilla("player_list");
|
||||
/**
|
||||
* The identifier for the vanilla sound subtitles layer.
|
||||
*/
|
||||
Identifier SUBTITLES = Identifier.ofVanilla("subtitles");
|
||||
|
||||
/**
|
||||
* @return the identifier of the layer
|
||||
*/
|
||||
Identifier id();
|
||||
|
||||
/**
|
||||
* Wraps a hud layer in an identified layer.
|
||||
*
|
||||
* @param id the identifier to give the layer
|
||||
* @param layer the layer to wrap
|
||||
* @return the identified layer
|
||||
*/
|
||||
static IdentifiedLayer of(Identifier id, LayeredDrawer.Layer layer) {
|
||||
return new WrappedLayer(id, layer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* A layered drawer that has an identifier attached to each layer and methods to add layers in specific positions.
|
||||
*
|
||||
* <p>Operations relative to a layer will generally inherit that layer's render condition.
|
||||
* The render condition for all vanilla layers except {@link IdentifiedLayer#SLEEP} is {@link net.minecraft.client.option.GameOptions#hudHidden}.
|
||||
* Only {@link #addLayer(IdentifiedLayer)} will not inherit any render condition.
|
||||
* There is currently no mechanism to change the render condition of a layer.
|
||||
* For vanilla layers, see {@link IdentifiedLayer}.
|
||||
*
|
||||
* <p>Common places to add layers (as of 1.21.4):
|
||||
* <table>
|
||||
* <tr>
|
||||
* <th>Injection Point</th>
|
||||
* <th>Use Case</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Before {@link IdentifiedLayer#MISC_OVERLAYS MISC_OVERLAYS}</td>
|
||||
* <td>Render before everything</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>After {@link IdentifiedLayer#MISC_OVERLAYS MISC_OVERLAYS}</td>
|
||||
* <td>Render after misc overlays (vignette, spyglass, and powder snow) and before the crosshair</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>After {@link IdentifiedLayer#EXPERIENCE_LEVEL EXPERIENCE_LEVEL}</td>
|
||||
* <td>Render after most main hud elements like hotbar, spectator hud, status bars, experience bar, status effects overlays, and boss bar and before the sleep overlay</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Before {@link IdentifiedLayer#DEMO_TIMER DEMO_TIMER}</td>
|
||||
* <td>Render after sleep overlay and before the demo timer, debug HUD, scoreboard, overlay message (action bar), and title and subtitle</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Before {@link IdentifiedLayer#CHAT CHAT}</td>
|
||||
* <td>Render after the debug HUD, scoreboard, overlay message (action bar), and title and subtitle and before {@link net.minecraft.client.gui.hud.ChatHud ChatHud}, player list, and sound subtitles</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>After {@link IdentifiedLayer#SUBTITLES SUBTITLES}</td>
|
||||
* <td>Render after everything</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @see HudLayerRegistrationCallback
|
||||
*/
|
||||
public interface LayeredDrawerWrapper {
|
||||
/**
|
||||
* Adds a layer to the end of the layered drawer.
|
||||
*
|
||||
* @param layer the layer to add
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
LayeredDrawerWrapper addLayer(IdentifiedLayer layer);
|
||||
|
||||
/**
|
||||
* Attaches a layer before the layer with the specified identifier.
|
||||
*
|
||||
* <p>The render condition of the layer being attached to, if any, also applies to the new layer.
|
||||
*
|
||||
* @param beforeThis the identifier of the layer to add the new layer before
|
||||
* @param layer the layer to add
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_, _ -> this")
|
||||
LayeredDrawerWrapper attachLayerBefore(Identifier beforeThis, IdentifiedLayer layer);
|
||||
|
||||
/**
|
||||
* Attaches a layer before the layer with the specified identifier.
|
||||
*
|
||||
* <p>The render condition of the layer being attached to, if any, also applies to the new layer.
|
||||
*
|
||||
* @param beforeThis the identifier of the layer to add the new layer before
|
||||
* @param identifier the identifier of the new layer
|
||||
* @param layer the layer to add
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_, _, _ -> this")
|
||||
default LayeredDrawerWrapper attachLayerBefore(Identifier beforeThis, Identifier identifier, LayeredDrawer.Layer layer) {
|
||||
return attachLayerBefore(beforeThis, IdentifiedLayer.of(identifier, layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a layer after the layer with the specified identifier.
|
||||
*
|
||||
* <p>The render condition of the layer being attached to, if any, also applies to the new layer.
|
||||
*
|
||||
* @param afterThis the identifier of the layer to add the new layer after
|
||||
* @param layer the layer to add
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_, _ -> this")
|
||||
LayeredDrawerWrapper attachLayerAfter(Identifier afterThis, IdentifiedLayer layer);
|
||||
|
||||
/**
|
||||
* Attaches a layer after the layer with the specified identifier.
|
||||
*
|
||||
* <p>The render condition of the layer being attached to, if any, also applies to the new layer.
|
||||
*
|
||||
* @param afterThis the identifier of the layer to add the new layer after
|
||||
* @param identifier the identifier of the new layer
|
||||
* @param layer the layer to add
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_, _, _ -> this")
|
||||
default LayeredDrawerWrapper attachLayerAfter(Identifier afterThis, Identifier identifier, LayeredDrawer.Layer layer) {
|
||||
return attachLayerAfter(afterThis, IdentifiedLayer.of(identifier, layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a layer with the specified identifier.
|
||||
*
|
||||
* @param identifier the identifier of the layer to remove
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
LayeredDrawerWrapper removeLayer(Identifier identifier);
|
||||
|
||||
/**
|
||||
* Replaces a layer with the specified identifier.
|
||||
*
|
||||
* <p>The render condition of the layer being replaced, if any, also applies to the new layer.
|
||||
*
|
||||
* @param identifier the identifier of the layer to replace
|
||||
* @param replacer a function that takes the old layer and returns the new layer
|
||||
* @return this layered drawer
|
||||
*/
|
||||
@Contract("_, _ -> this")
|
||||
LayeredDrawerWrapper replaceLayer(Identifier identifier, Function<IdentifiedLayer, IdentifiedLayer> replacer);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||
import org.spongepowered.asm.mixin.injection.InjectionPoint;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionPointData;
|
||||
import org.spongepowered.asm.mixin.injection.struct.MemberInfo;
|
||||
|
||||
public final class LayerInjectionPoint extends InjectionPoint {
|
||||
private final MemberInfo target;
|
||||
|
||||
public LayerInjectionPoint(InjectionPointData data) {
|
||||
super(data);
|
||||
this.target = (MemberInfo) data.getTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean find(String desc, InsnList insns, Collection<AbstractInsnNode> nodes) {
|
||||
List<AbstractInsnNode> targetNodes = new ArrayList<>();
|
||||
|
||||
ListIterator<AbstractInsnNode> iterator = insns.iterator();
|
||||
|
||||
outer: while (iterator.hasNext()) {
|
||||
AbstractInsnNode insn = iterator.next();
|
||||
|
||||
if (insn.getOpcode() == Opcodes.INVOKEDYNAMIC && matchesInvokeDynamic((InvokeDynamicInsnNode) insn)) {
|
||||
// We have found our target InvokeDynamicInsnNode, now we need to find the next INVOKEVIRTUAL
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
insn = iterator.next();
|
||||
|
||||
if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
targetNodes.add(insn);
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes.addAll(targetNodes);
|
||||
return !targetNodes.isEmpty();
|
||||
}
|
||||
|
||||
private boolean matchesInvokeDynamic(InvokeDynamicInsnNode insnNode) {
|
||||
for (Object bsmArg : insnNode.bsmArgs) {
|
||||
if (bsmArg instanceof Handle handle && matchesHandle(handle)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchesHandle(Handle handle) {
|
||||
return handle.getOwner().equals(target.getOwner())
|
||||
&& handle.getName().equals(target.getName())
|
||||
&& handle.getDesc().equals(target.getDesc());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.LayeredDrawerWrapper;
|
||||
import net.fabricmc.fabric.mixin.client.rendering.LayeredDrawerAccessor;
|
||||
|
||||
public final class LayeredDrawerWrapperImpl implements LayeredDrawerWrapper {
|
||||
private final LayeredDrawer base;
|
||||
|
||||
public LayeredDrawerWrapperImpl(LayeredDrawer base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
private static List<LayeredDrawer.Layer> getLayers(LayeredDrawer drawer) {
|
||||
return ((LayeredDrawerAccessor) drawer).getLayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredDrawerWrapper addLayer(IdentifiedLayer layer) {
|
||||
validateUnique(layer);
|
||||
getLayers(this.base).add(layer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredDrawerWrapper attachLayerAfter(Identifier afterThis, IdentifiedLayer layer) {
|
||||
validateUnique(layer);
|
||||
|
||||
boolean didChange = findLayer(afterThis, (l, iterator) -> {
|
||||
iterator.add(layer);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!didChange) {
|
||||
throw new IllegalArgumentException("Layer with identifier " + afterThis + " not found");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredDrawerWrapper attachLayerBefore(Identifier beforeThis, IdentifiedLayer layer) {
|
||||
validateUnique(layer);
|
||||
boolean didChange = findLayer(beforeThis, (l, iterator) -> {
|
||||
iterator.previous();
|
||||
iterator.add(layer);
|
||||
iterator.next();
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!didChange) {
|
||||
throw new IllegalArgumentException("Layer with identifier " + beforeThis + " not found");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredDrawerWrapper removeLayer(Identifier identifier) {
|
||||
boolean didChange = findLayer(identifier, (l, iterator) -> {
|
||||
iterator.remove();
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!didChange) {
|
||||
throw new IllegalArgumentException("Layer with identifier " + identifier + " not found");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredDrawerWrapper replaceLayer(Identifier identifier, Function<IdentifiedLayer, IdentifiedLayer> replacer) {
|
||||
boolean didChange = findLayer(identifier, (l, iterator) -> {
|
||||
iterator.set(replacer.apply((IdentifiedLayer) l));
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!didChange) {
|
||||
throw new IllegalArgumentException("Layer with identifier " + identifier + " not found");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void validateUnique(IdentifiedLayer layer) {
|
||||
visitLayers((l, iterator) -> {
|
||||
if (matchesIdentifier(l, layer.id())) {
|
||||
throw new IllegalArgumentException("Layer with identifier " + layer.id() + " already exists");
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a layer with the given identifier was found
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean findLayer(Identifier identifier, LayerVisitor visitor) {
|
||||
MutableBoolean found = new MutableBoolean(false);
|
||||
|
||||
visitLayers((l, iterator) -> {
|
||||
if (matchesIdentifier(l, identifier)) {
|
||||
found.setTrue();
|
||||
return visitor.visit(l, iterator);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return found.booleanValue();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean visitLayers(LayerVisitor visitor) {
|
||||
return visitLayers(getLayers(base), visitor);
|
||||
}
|
||||
|
||||
private boolean visitLayers(List<LayeredDrawer.Layer> layers, LayerVisitor visitor) {
|
||||
MutableBoolean modified = new MutableBoolean(false);
|
||||
ListIterator<LayeredDrawer.Layer> iterator = layers.listIterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
LayeredDrawer.Layer layer = iterator.next();
|
||||
|
||||
if (visitor.visit(layer, iterator)) {
|
||||
modified.setTrue();
|
||||
continue; // Skip visiting children if the current layer was modified
|
||||
}
|
||||
|
||||
if (layer instanceof SubLayer subLayer) {
|
||||
modified.setValue(visitLayers(getLayers(subLayer.delegate()), visitor));
|
||||
}
|
||||
}
|
||||
|
||||
return modified.booleanValue();
|
||||
}
|
||||
|
||||
private static boolean matchesIdentifier(LayeredDrawer.Layer layer, Identifier identifier) {
|
||||
return layer instanceof IdentifiedLayer il && il.id().equals(identifier);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
interface LayerVisitor {
|
||||
/**
|
||||
* @return true if the list has been modified, false if not modified
|
||||
*/
|
||||
boolean visit(LayeredDrawer.Layer layer, ListIterator<LayeredDrawer.Layer> iterator);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
|
||||
/**
|
||||
* A layer that wraps another layered drawer that can be added to {@link net.fabricmc.fabric.api.client.rendering.v1.LayeredDrawerWrapper LayeredDrawerWrapper}.
|
||||
*
|
||||
* <p>This wraps the vanilla sub drawers, so we can retrieve sub layers as needed in the layered drawer wrapper.
|
||||
*
|
||||
* @param delegate the layered drawer to wrap
|
||||
* @param shouldRender a boolean supplier that determines if the layer should render
|
||||
*/
|
||||
public record SubLayer(LayeredDrawer delegate, BooleanSupplier shouldRender) implements LayeredDrawer.Layer {
|
||||
@Override
|
||||
public void render(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (shouldRender.getAsBoolean()) {
|
||||
delegate.render(context, tickCounter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
|
||||
|
||||
/**
|
||||
* A simple layer that wraps a {@link LayeredDrawer.Layer} that can be added to {@link net.fabricmc.fabric.api.client.rendering.v1.LayeredDrawerWrapper LayeredDrawerWrapper}.
|
||||
*
|
||||
* @param id the identifier of the layer
|
||||
* @param layer the layer to wrap
|
||||
*/
|
||||
public record WrappedLayer(Identifier id, LayeredDrawer.Layer layer) implements IdentifiedLayer {
|
||||
@Override
|
||||
public void render(DrawContext context, RenderTickCounter tickCounter) {
|
||||
layer.render(context, tickCounter);
|
||||
}
|
||||
}
|
|
@ -16,21 +16,182 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.client.rendering;
|
||||
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.BOSS_BAR;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.CHAT;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.CROSSHAIR;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.DEBUG;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.DEMO_TIMER;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.EXPERIENCE_LEVEL;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.HOTBAR_AND_BARS;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.MISC_OVERLAYS;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.OVERLAY_MESSAGE;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.PLAYER_LIST;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.SCOREBOARD;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.SLEEP;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.STATUS_EFFECTS;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.SUBTITLES;
|
||||
import static net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer.TITLE_AND_SUBTITLE;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.client.gui.hud.InGameHud;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudLayerRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
|
||||
import net.fabricmc.fabric.impl.client.rendering.LayeredDrawerWrapperImpl;
|
||||
|
||||
@Mixin(InGameHud.class)
|
||||
public class InGameHudMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private LayeredDrawer layeredDrawer;
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
public void render(DrawContext drawContext, RenderTickCounter tickCounter, CallbackInfo callbackInfo) {
|
||||
HudRenderCallback.EVENT.invoker().onHudRender(drawContext, tickCounter);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderMiscOverlays(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapMiscOverlays(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(MISC_OVERLAYS, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderCrosshair(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapCrosshair(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(CROSSHAIR, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderMainHud(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapHotbarAndBars(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(HOTBAR_AND_BARS, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderExperienceLevel(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapExperienceLevel(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(EXPERIENCE_LEVEL, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderStatusEffectOverlay(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapStatusEffects(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(STATUS_EFFECTS, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;method_55808(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapBossBar(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(BOSS_BAR, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderDemoTimer(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapDemoTimer(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(DEMO_TIMER, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;method_55807(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapDebug(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(DEBUG, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderScoreboardSidebar(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapScoreboard(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(SCOREBOARD, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderOverlayMessage(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapOverlayMessage(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(OVERLAY_MESSAGE, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderTitleAndSubtitle(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapTitleAndSubtitle(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(TITLE_AND_SUBTITLE, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderChat(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapChat(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(CHAT, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderPlayerList(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapPlayerList(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(PLAYER_LIST, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;method_55806(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V")
|
||||
)
|
||||
private LayeredDrawer wrapSubtitlesHud(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(SUBTITLES, instance, layer);
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>",
|
||||
at = @At(
|
||||
value = "net.fabricmc.fabric.impl.client.rendering.LayerInjectionPoint",
|
||||
target = "Lnet/minecraft/client/gui/hud/InGameHud;renderSleepOverlay(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V"
|
||||
)
|
||||
)
|
||||
private LayeredDrawer wrapSleepOverlay(LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return wrap(SLEEP, instance, layer);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void registerLayers(CallbackInfo ci) {
|
||||
HudLayerRegistrationCallback.EVENT.invoker().register(new LayeredDrawerWrapperImpl(layeredDrawer));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static LayeredDrawer wrap(Identifier identifier, LayeredDrawer instance, LayeredDrawer.Layer layer) {
|
||||
return instance.addLayer(IdentifiedLayer.of(identifier, layer));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.rendering;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
|
||||
@Mixin(LayeredDrawer.class)
|
||||
public interface LayeredDrawerAccessor {
|
||||
@Accessor
|
||||
List<LayeredDrawer.Layer> getLayers();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.rendering;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.rendering.SubLayer;
|
||||
|
||||
@Mixin(LayeredDrawer.class)
|
||||
public abstract class LayeredDrawerMixin {
|
||||
@Shadow
|
||||
public abstract LayeredDrawer addLayer(LayeredDrawer.Layer layer);
|
||||
|
||||
@Inject(method = "addSubDrawer", at = @At("HEAD"), cancellable = true)
|
||||
private void wrapSubDrawer(LayeredDrawer drawer, BooleanSupplier shouldRender, CallbackInfoReturnable<LayeredDrawer> cir) {
|
||||
addLayer(new SubLayer(drawer, shouldRender));
|
||||
cir.setReturnValue((LayeredDrawer) (Object) this);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
"EntityModelsMixin",
|
||||
"EntityRenderersMixin",
|
||||
"InGameHudMixin",
|
||||
"LayeredDrawerAccessor",
|
||||
"LayeredDrawerMixin",
|
||||
"LivingEntityRendererAccessor",
|
||||
"SpecialModelTypesMixin",
|
||||
"TooltipComponentMixin",
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.LayeredDrawer;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
|
||||
|
||||
public class LayeredDrawerWrapperTest {
|
||||
private List<String> drawnLayers;
|
||||
private LayeredDrawer base;
|
||||
private LayeredDrawerWrapperImpl layers;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
drawnLayers = new ArrayList<>();
|
||||
base = new LayeredDrawer();
|
||||
layers = new LayeredDrawerWrapperImpl(base);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addLayer() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"));
|
||||
|
||||
assertOrder(base, List.of("layer1", "layer2", "layer3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addBefore() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"));
|
||||
|
||||
layers.attachLayerBefore(testIdentifier("layer1"), testLayer("before1"));
|
||||
|
||||
assertOrder(base, List.of("before1", "layer1", "layer2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAfter() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"));
|
||||
|
||||
layers.attachLayerAfter(testIdentifier("layer1"), testLayer("after1"));
|
||||
|
||||
assertOrder(base, List.of("layer1", "after1", "layer2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeLayer() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"))
|
||||
.addLayer(testLayer("layer4"));
|
||||
|
||||
layers.removeLayer(testIdentifier("layer2"))
|
||||
.removeLayer(testIdentifier("layer4"));
|
||||
|
||||
assertOrder(base, List.of("layer1", "layer3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceLayer() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"));
|
||||
|
||||
layers.replaceLayer(testIdentifier("layer2"), layer -> testLayer("temp"))
|
||||
.replaceLayer(testIdentifier("temp"), layer -> testLayer("replaced"));
|
||||
|
||||
assertOrder(base, List.of("layer1", "replaced", "layer3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateUnique() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"));
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> layers.validateUnique(testLayer("layer4")));
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> layers.validateUnique(testLayer("layer2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findLayer() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"));
|
||||
|
||||
Assertions.assertTrue(layers.findLayer(testIdentifier("layer2"), (layer, iterator) -> {
|
||||
iterator.add(testLayer("found"));
|
||||
return true;
|
||||
}));
|
||||
|
||||
assertOrder(base, List.of("layer1", "layer2", "found", "layer3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void visitLayers() {
|
||||
layers.addLayer(testLayer("layer1"))
|
||||
.addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3"));
|
||||
|
||||
Assertions.assertTrue(layers.visitLayers((layer, iterator) -> {
|
||||
String name = ((IdentifiedLayer) layer).id().getPath();
|
||||
iterator.add(testLayer("visited" + name.substring(name.length() - 1)));
|
||||
return true;
|
||||
}));
|
||||
|
||||
assertOrder(base, List.of("layer1", "visited1", "layer2", "visited2", "layer3", "visited3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceSubLayer() {
|
||||
layers.addLayer(testLayer("layer1"));
|
||||
base.addLayer(new SubLayer(
|
||||
new LayeredDrawer().addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3")),
|
||||
() -> true
|
||||
));
|
||||
layers.addLayer(testLayer("layer4"));
|
||||
|
||||
layers.replaceLayer(testIdentifier("layer2"), layer -> testLayer("replaced"));
|
||||
|
||||
assertOrder(base, List.of("layer1", "replaced", "layer3", "layer4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void visitSubLayers() {
|
||||
layers.addLayer(testLayer("layer1"));
|
||||
base.addLayer(new SubLayer(
|
||||
new LayeredDrawer().addLayer(testLayer("layer2"))
|
||||
.addLayer(testLayer("layer3")),
|
||||
() -> true
|
||||
));
|
||||
layers.addLayer(testLayer("layer4"));
|
||||
|
||||
// Return true when we encounter layer3, which is in a sub drawer
|
||||
// Even though it's not modified. This is just for testing.
|
||||
Assertions.assertTrue(layers.visitLayers((layer, iterator) -> layer instanceof IdentifiedLayer il && il.id().equals(testIdentifier("layer3"))));
|
||||
|
||||
assertOrder(base, List.of("layer1", "layer2", "layer3", "layer4"));
|
||||
}
|
||||
|
||||
private IdentifiedLayer testLayer(String name) {
|
||||
return IdentifiedLayer.of(testIdentifier(name), (context, tickCounter) -> drawnLayers.add(name));
|
||||
}
|
||||
|
||||
private Identifier testIdentifier(String name) {
|
||||
return Identifier.of("test", name);
|
||||
}
|
||||
|
||||
private void assertOrder(LayeredDrawer drawer, List<String> expectedLayers) {
|
||||
DrawContext drawContext = mock(DrawContext.class);
|
||||
RenderTickCounter tickCounter = mock(RenderTickCounter.class);
|
||||
MatrixStack matrixStack = mock(MatrixStack.class);
|
||||
|
||||
when(drawContext.getMatrices()).thenReturn(matrixStack);
|
||||
|
||||
drawnLayers.clear();
|
||||
drawer.render(drawContext, tickCounter);
|
||||
assertEquals(drawnLayers, expectedLayers);
|
||||
}
|
||||
}
|
|
@ -18,9 +18,13 @@
|
|||
"net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.FeatureRendererTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.HudAndShaderTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.HudLayerTests",
|
||||
"net.fabricmc.fabric.test.rendering.client.SpecialBlockRendererTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.TooltipComponentTests",
|
||||
"net.fabricmc.fabric.test.rendering.client.WorldRenderEventsTests"
|
||||
],
|
||||
"fabric-client-gametest": [
|
||||
"net.fabricmc.fabric.test.rendering.client.HudLayerTests"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.rendering.client;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Colors;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.context.TestSingleplayerContext;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.screenshot.TestScreenshotComparisonOptions;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudLayerRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
|
||||
|
||||
public class HudLayerTests implements ClientModInitializer, FabricClientGameTest {
|
||||
private static final String MOD_ID = "fabric";
|
||||
private static final String BEFORE_MISC_OVERLAY = "test_before_misc_overlay";
|
||||
private static final String AFTER_MISC_OVERLAY = "test_after_misc_overlay";
|
||||
private static final String AFTER_EXPERIENCE_LEVEL = "test_after_experience_level";
|
||||
private static final String BEFORE_DEMO_TIMER = "test_before_demo_timer";
|
||||
private static final String BEFORE_CHAT = "test_before_chat";
|
||||
private static final String AFTER_SUBTITLES = "test_after_subtitles";
|
||||
private static boolean shouldRender = false;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
HudLayerRegistrationCallback.EVENT.register(layeredDrawer -> layeredDrawer
|
||||
.attachLayerBefore(IdentifiedLayer.MISC_OVERLAYS, Identifier.of(MOD_ID, BEFORE_MISC_OVERLAY), HudLayerTests::renderBeforeMiscOverlay)
|
||||
.attachLayerAfter(IdentifiedLayer.MISC_OVERLAYS, Identifier.of(MOD_ID, AFTER_MISC_OVERLAY), HudLayerTests::renderAfterMiscOverlay)
|
||||
.attachLayerAfter(IdentifiedLayer.EXPERIENCE_LEVEL, Identifier.of(MOD_ID, AFTER_EXPERIENCE_LEVEL), HudLayerTests::renderAfterExperienceLevel)
|
||||
.attachLayerBefore(IdentifiedLayer.DEMO_TIMER, Identifier.of(MOD_ID, BEFORE_DEMO_TIMER), HudLayerTests::renderBeforeDemoTimer)
|
||||
.attachLayerBefore(IdentifiedLayer.CHAT, Identifier.of(MOD_ID, BEFORE_CHAT), HudLayerTests::renderBeforeChat)
|
||||
.attachLayerAfter(IdentifiedLayer.SUBTITLES, Identifier.of(MOD_ID, AFTER_SUBTITLES), HudLayerTests::renderAfterSubtitles)
|
||||
);
|
||||
}
|
||||
|
||||
private static void renderBeforeMiscOverlay(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a blue rectangle at the top right of the screen, and it should be blocked by misc overlays such as vignette, spyglass, and powder snow
|
||||
context.fill(context.getScaledWindowWidth() - 200, 0, context.getScaledWindowWidth(), 30, Colors.BLUE);
|
||||
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, "1. Blue rectangle blocked by overlays", context.getScaledWindowWidth() - 196, 10, Colors.WHITE);
|
||||
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, "such as powder snow", context.getScaledWindowWidth() - 111, 20, Colors.WHITE);
|
||||
}
|
||||
|
||||
private static void renderAfterMiscOverlay(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a red square in the center of the screen underneath the crosshair
|
||||
context.fill(context.getScaledWindowWidth() / 2 - 10, context.getScaledWindowHeight() / 2 - 10, context.getScaledWindowWidth() / 2 + 10, context.getScaledWindowHeight() / 2 + 10, Colors.RED);
|
||||
context.drawCenteredTextWithShadow(MinecraftClient.getInstance().textRenderer, "2. Red square underneath crosshair", context.getScaledWindowWidth() / 2, context.getScaledWindowHeight() / 2 + 10, Colors.WHITE);
|
||||
}
|
||||
|
||||
private static void renderAfterExperienceLevel(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a green rectangle at the bottom of the screen, and it should block the hotbar and status bars
|
||||
context.fill(context.getScaledWindowWidth() / 2 - 50, context.getScaledWindowHeight() - 50, context.getScaledWindowWidth() / 2 + 50, context.getScaledWindowHeight() - 10, Colors.GREEN);
|
||||
context.drawCenteredTextWithShadow(MinecraftClient.getInstance().textRenderer, "3. This green rectangle should block the hotbar and status bars.", context.getScaledWindowWidth() / 2, context.getScaledWindowHeight() - 40, Colors.WHITE);
|
||||
}
|
||||
|
||||
private static void renderBeforeDemoTimer(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a yellow rectangle at the right of the screen, and it should be above the sleep overlay but below the scoreboard
|
||||
context.fill(context.getScaledWindowWidth() - 240, context.getScaledWindowHeight() / 2 - 10, context.getScaledWindowWidth(), context.getScaledWindowHeight() / 2 + 10, Colors.YELLOW);
|
||||
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, "4. This yellow rectangle should be above", context.getScaledWindowWidth() - 236, context.getScaledWindowHeight() / 2 - 10, Colors.WHITE);
|
||||
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, "the sleep overlay but below the scoreboard.", context.getScaledWindowWidth() - 236, context.getScaledWindowHeight() / 2, Colors.WHITE);
|
||||
}
|
||||
|
||||
private static void renderBeforeChat(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a blue rectangle at the bottom left of the screen, and it should be blocked by the chat
|
||||
context.fill(0, context.getScaledWindowHeight() - 40, 300, context.getScaledWindowHeight() - 50, Colors.BLUE);
|
||||
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, "5. This blue rectangle should be blocked by the chat.", 0, context.getScaledWindowHeight() - 50, Colors.WHITE);
|
||||
}
|
||||
|
||||
private static void renderAfterSubtitles(DrawContext context, RenderTickCounter tickCounter) {
|
||||
if (!shouldRender) return;
|
||||
// Render a yellow rectangle at the top of the screen, and it should block the player list
|
||||
context.fill(context.getScaledWindowWidth() / 2 - 150, 0, context.getScaledWindowWidth() / 2 + 150, 15, Colors.YELLOW);
|
||||
context.drawCenteredTextWithShadow(MinecraftClient.getInstance().textRenderer, "6. This yellow rectangle should block the player list.", context.getScaledWindowWidth() / 2, 0, Colors.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTest(ClientGameTestContext context) {
|
||||
// Set up required test environment
|
||||
context.getInput().resizeWindow(1708, 960); // Twice the default dimensions
|
||||
context.runOnClient(client -> {
|
||||
client.options.hudHidden = false;
|
||||
client.options.getGuiScale().setValue(2);
|
||||
});
|
||||
shouldRender = true;
|
||||
|
||||
try (TestSingleplayerContext singleplayer = context.worldBuilder().create()) {
|
||||
// Set up the test world
|
||||
singleplayer.getServer().runCommand("/tp @a 0 -60 0");
|
||||
singleplayer.getServer().runCommand("/scoreboard objectives add hud_layer_test dummy");
|
||||
singleplayer.getServer().runCommand("/scoreboard objectives setdisplay list hud_layer_test"); // Hack to show player list
|
||||
singleplayer.getServer().runCommand("/scoreboard objectives setdisplay sidebar hud_layer_test"); // Hack to show sidebar
|
||||
singleplayer.getServer().runOnServer(server -> server.getOverworld().setBlockState(new BlockPos(0, -59, 0), Blocks.POWDER_SNOW.getDefaultState()));
|
||||
|
||||
// Wait for stuff to load
|
||||
singleplayer.getClientWorld().waitForChunksRender();
|
||||
singleplayer.getServer().runOnServer(server -> server.getPlayerManager().broadcast(Text.of("hud_layer_" + BEFORE_CHAT), false)); // Chat messages disappear in 200 ticks so we send one 150 ticks in advance to test the before chat layer
|
||||
context.waitTicks(150); // The powder snow frosty vignette takes 140 ticks to fully appear, so we additionally wait for a total of 150 ticks
|
||||
|
||||
// Take and assert screenshots
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + BEFORE_MISC_OVERLAY).withRegion(1308, 0, 400, 60).save());
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + AFTER_MISC_OVERLAY).withRegion(668, 460, 372, 56).save());
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + AFTER_EXPERIENCE_LEVEL).withRegion(754, 860, 200, 80).save());
|
||||
|
||||
// The sleep overlay takes 100 ticks to fully appear, so we start sleeping and wait for 100 ticks
|
||||
context.runOnClient(client -> client.player.setSleepingPosition(new BlockPos(0, -59, 0)));
|
||||
context.waitTicks(100);
|
||||
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + BEFORE_DEMO_TIMER).withRegion(1228, 460, 480, 40).save());
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + BEFORE_CHAT).withRegion(0, 860, 600, 20).save());
|
||||
|
||||
context.runOnClient(client -> client.player.clearSleepingPosition());
|
||||
context.waitTick();
|
||||
context.getInput().holdKey(InputUtil.GLFW_KEY_TAB); // Show player list
|
||||
context.waitTick();
|
||||
context.assertScreenshotEquals(TestScreenshotComparisonOptions.of("hud_layer_" + AFTER_SUBTITLES).withRegion(554, 0, 600, 30).save());
|
||||
}
|
||||
|
||||
shouldRender = false;
|
||||
}
|
||||
}
|
Binary file not shown.
After ![]() (image error) Size: 1.6 KiB |
Binary file not shown.
After ![]() (image error) Size: 2.2 KiB |
Binary file not shown.
After ![]() (image error) Size: 2.4 KiB |
Binary file not shown.
After ![]() (image error) Size: 2.8 KiB |
Binary file not shown.
After ![]() (image error) Size: 9.4 KiB |
Binary file not shown.
After ![]() (image error) Size: 4.3 KiB |
Loading…
Add table
Reference in a new issue