Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hud Render Events #4119

Merged
merged 37 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ec3bf3a
Add HudRenderEvents
kevinthegreat1 Sep 23, 2024
574ad81
Add HudRenderEventsTests and deprecate HudRenderCallback
kevinthegreat1 Sep 25, 2024
ae6c6a1
Update tests
kevinthegreat1 Sep 25, 2024
439dda3
Add client parameter and apply suggestions
kevinthegreat1 Sep 25, 2024
780949e
Split HudRenderEvents into separate interfaces
kevinthegreat1 Oct 3, 2024
7123e37
Fix before chat and last
kevinthegreat1 Oct 3, 2024
3a0552d
Add after sleep overlay event and update after main hud injection point
kevinthegreat1 Oct 3, 2024
05b2ac8
Add comments for injection points
kevinthegreat1 Oct 11, 2024
0b7dab7
Revert splitting HudRenderEvents into separate interfaces
kevinthegreat1 Oct 11, 2024
e365ec6
Use vanilla layered drawer layer interface
kevinthegreat1 Oct 23, 2024
bbc964f
Cleanup InGameHudMixin
kevinthegreat1 Oct 25, 2024
aefe222
POC of hud modification
modmuss50 Jan 9, 2025
22c98ce
Implement HudLayerRegistrationCallback
kevinthegreat1 Jan 10, 2025
2f59a02
Delete HudRenderEvents
kevinthegreat1 Jan 10, 2025
9aaeb5c
Fix sub drawers and add basic documentation
kevinthegreat1 Jan 10, 2025
6926072
Fix checkstyle
kevinthegreat1 Jan 11, 2025
493db65
Apply suggestions from code review
kevinthegreat1 Jan 15, 2025
e5f5e7a
Add Javadocs
kevinthegreat1 Jan 16, 2025
cf9ffa6
Add more unit tests
kevinthegreat1 Jan 16, 2025
5c28e2b
Apply suggestions from code review
kevinthegreat1 Jan 16, 2025
56bf8e1
Javadoc oddities
kevinthegreat1 Jan 18, 2025
481e826
Add client gametests
kevinthegreat1 Jan 27, 2025
b4048f0
Finish client gametests
kevinthegreat1 Jan 28, 2025
4303293
Change method and add documentation
kevinthegreat1 Jan 28, 2025
e1a5c9b
Ensure test environment is correct
kevinthegreat1 Jan 28, 2025
2c79985
Move test class to same package
kevinthegreat1 Jan 28, 2025
f2bf391
Add render condition for tests
kevinthegreat1 Jan 28, 2025
b4f8aab
Merge branch '1.21.4' into hud-render-events
kevinthegreat1 Jan 29, 2025
d75f794
Fix merge conflicts
kevinthegreat1 Jan 29, 2025
c838fcf
Update javadocs
kevinthegreat1 Jan 29, 2025
8209d7f
Small bug fixes, documentation, and sub drawer tests
kevinthegreat1 Jan 31, 2025
bcbbb27
Update javadocs some more
kevinthegreat1 Jan 31, 2025
cd138b3
Add contract and get around return value not used warnings
kevinthegreat1 Jan 31, 2025
97f8874
Apply suggestions from code review
kevinthegreat1 Jan 31, 2025
e3f65ea
Migrate AtomicBoolean to MutableBoolean
kevinthegreat1 Feb 4, 2025
3384eae
Update javadocs on render condition
kevinthegreat1 Feb 4, 2025
a4c4bd7
Use ListIterator#set
kevinthegreat1 Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ static TestScreenshotComparisonOptions of(NativeImage templateImage) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public TestScreenshotComparisonOptionsImpl(NativeImage templateImage) {
this.templateImage = Either.right(templateImage);
}

@Override
public TestScreenshotComparisonOptions save() {
return saveWithFileName(getTemplateImagePath());
}

@Override
public TestScreenshotComparisonOptions saveWithFileName(String fileName) {
Preconditions.checkNotNull(fileName, "fileName");
Expand Down
1 change: 1 addition & 0 deletions fabric-rendering-v1/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ version = getSubprojectVersion(project)
moduleDependencies(project, ['fabric-api-base'])

testDependencies(project, [
':fabric-client-gametest-api-v1',
':fabric-object-builder-api-v1'
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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}:
* <pre>{@code
* HudLayerRegistrationCallback.EVENT.register(layeredDrawer -> layeredDrawer.attachLayerAfter(IdentifiedLayer.MISC_OVERLAYS, Identifier.of("example", "example_layer_after_misc_overlays"), (context, tickDelta) -> {
* // Your rendering code here
* }));
* }</pre>
kevinthegreat1 marked this conversation as resolved.
Show resolved Hide resolved
*
* @see LayeredDrawerWrapper
*/
public interface HudLayerRegistrationCallback {
kevinthegreat1 marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
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);
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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>For common use cases, see {@link LayeredDrawerWrapper}.
*/
public interface IdentifiedLayer extends LayeredDrawer.Layer {
kevinthegreat1 marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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 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>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>
*/
public interface LayeredDrawerWrapper {
/**
* Adds a layer to the end of the layered drawer.
*
* @param layer the layer to add
* @return this layered drawer
*/
LayeredDrawerWrapper addLayer(IdentifiedLayer layer);
kevinthegreat1 marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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.
kevinthegreat1 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param beforeThis the identifier of the layer to add the new layer before
* @param layer the layer to add
* @return this layered drawer
*/
LayeredDrawerWrapper attachLayerBefore(Identifier beforeThis, IdentifiedLayer layer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good name 👍


/**
* 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
*/
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
*/
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
*/
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
*/
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
*/
LayeredDrawerWrapper replaceLayer(Identifier identifier, Function<IdentifiedLayer, IdentifiedLayer> replacer);
}
Loading