-
Notifications
You must be signed in to change notification settings - Fork 427
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
Add Hud Render Events #4119
Conversation
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/HudRenderEventsTests.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
Not sure this really goes far enough. The system used by NeoForge has always been pretty ideal (although quite invasive): splitting gui rendering into all the different parts (health, hotbar, status effects, etc.) and allowing mods to render their custom gui layers anywhere in-between. Also it would be awesome to finally get shared height parameters for bars (health bar + armor bar on the left side, food + air on the right side) rendered above the hotbar in Fabric Api. So that multiple mods adding their own bars know at what height to render without interfering with others. |
I agree that Forge's implementation (last when I saw it) is much more versatile and comprehensive. It would be great to be able to cancel certain components too. However as you mentioned it is quite invasive, and in the past, this injection point hasn't exactly been the most stable. A more comprehensive api would need a bit more discussion with the maintainers, but I'd be happy to see it.
The height problem is just reading the code to see the height where bars and components render right? Are you suggesting we provide z constants for different HUD elements? |
No. NeoForge adds |
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not sure events like this are the best way to do this. An API that allows you to register LayeredDrawer.Layer's in the desired order may be better?
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudRenderEvents.java
Outdated
Show resolved
Hide resolved
...-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/InGameHudMixin.java
Outdated
Show resolved
Hide resolved
/** | ||
* Called at the start of HUD rendering, right before anything is rendered. | ||
*/ | ||
public static final Event<Start> START = EventFactory.createArrayBacked(Start.class, listeners -> (client, context, tickCounter) -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are events like this the best solution here? Would it be better to allow mods to register their own LayeredDrawer.Layer in a similar way to how the vanilla hud elements work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the code to use one interface, but I don't see a difference with registration methods. Wouldn't it still be implemented with an event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking they could just be static? I dont think there is a need for an even unless im missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was under the impression that not using events is more complicated than just using events? So there's no need for registration methods? Are you saying just implement it with a synchronized list? Events also allow phase ordering if some mod wants to render before another.
Please let me know if you want to discuss this more |
This please! It would be really great if mods were to properly register their GUI layers with an identifier that allows for ordering with callbacks firing before and after those layers render, as is done on NeoForge. That way other mods can interact with those new layers. Ideally vanilla layers should also be accessible via an identifier, although not sure how to implement that in a way that’s not too invasive. |
I had a discussion with him on discord here, and I think you're misunderstanding his proposal. Under his idea, the API would still have the same capability as the events here, since what you're describing uses many more mixins and is significantly more invasive. I have looked at neo's API, and the number of patches required will probably make it not acceptible for fabric. |
Yes, as discussed on discord I think it would be better to statically register (or maybe register in events similar to the ones in the current state of the PR a vanilla |
Done in 0d2bcf2. |
...-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/InGameHudMixin.java
Outdated
Show resolved
Hide resolved
I think what modmuss meant here is that we would have a |
He said it’s fine to register in events. I used events because it allows ordering the layers within one phase/event. Also, directly adding to the vanilla list would require similarly many injection points or rely on the indices which is even more undesirable. |
/** | ||
* Called after the entire HUD is rendered. | ||
*/ | ||
public static final Event<LayeredDrawer.Layer> LAST = createEventForStage(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe END? There is START, not FIRST or smth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The names are taken from WorldRenderEvents
, which has both LAST
and END
, and LAST
is the last place where you should render stuff in most cases. However, I'm happy to change this if modmuss agrees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good now, I have just a few comments (mostly about my own changes :D ) Do let me know if you have any questions or disagree.
...ring-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/FabricLayeredDrawer.java
Outdated
Show resolved
Hide resolved
...ring-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/FabricLayeredDrawer.java
Outdated
Show resolved
Hide resolved
...rc/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudLayerRegistrationCallback.java
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/IdentifiedLayer.java
Outdated
Show resolved
Hide resolved
Identifier STATUS_EFFECTS = Identifier.ofVanilla("status_effects"); | ||
Identifier BOSSBAR = Identifier.ofVanilla("bossbar"); | ||
Identifier DEMO_TIMER = Identifier.ofVanilla("demo_timer"); | ||
Identifier DEBUG_HUD = Identifier.ofVanilla("debug_hud"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe remove hud
from the names? Might be worth looking over these names in general to make sure they are consitent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to update the names to be consistent.
...-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/InGameHudMixin.java
Outdated
Show resolved
Hide resolved
...-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/InGameHudMixin.java
Outdated
Show resolved
Hide resolved
...ring-v1/src/test/java/net/fabricmc/fabric/test/client/rendering/FabricLayeredDrawerTest.java
Outdated
Show resolved
Hide resolved
...1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/HudRenderEventsTests.java
Outdated
Show resolved
Hide resolved
import net.fabricmc.fabric.api.client.rendering.v1.HudLayerRegistrationCallback; | ||
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer; | ||
|
||
public class HudRenderEventsTests implements ClientModInitializer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what automated testing we can do with the new client tests? Might need to wait until #4381 has been merged. Don't let this block this PR, but its a nice thing to keep in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I was planning for that. All the tests in here can be easily made automated after the screenshots stuff become available.
36da421
to
f2bf391
Compare
Fixed the random commits, I didn't notice that branches already diverged when I made this pr. |
@@ -49,6 +49,11 @@ public TestScreenshotComparisonOptionsImpl(NativeImage templateImage) { | |||
this.templateImage = Either.right(templateImage); | |||
} | |||
|
|||
@Override | |||
public TestScreenshotComparisonOptions save() { | |||
return saveWithFileName(getTemplateImagePath()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this is wrong in a couple of ways. Firstly, it writes the template image before reading it, causing the test to always succeed (at least in dev). Secondly this path points to the runtime resources of the test mod, and not the location where you probably want to write it which is the resource folder in the gradle source set. At dev time this will actually be a folder, but in production this will be a path pointing inside your test mod jar and therefore not writable.
There is already a mechanism to automatically save these templates, I'd suggest you rely on that instead. If you set the system property fabric.client.gametest.testModResourcesPath
to the path to your test mod resources folder, then screenshot comparisons will write the grabbed screenshot to the templates directory for next time instead of failing. This system property might not be super easy to discover right now but Loom 1.10 will be setting it by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but I don't understand your comment. I just wanted a shorthand for saving the screenshot with the same name as the template name, .saveWithFileName(templateName)
, and this addition seems to work as expected for my case. It saves the screenshot with templateName
in the screenshot directory as expected. The test fails normally in the same way as using .saveWithFileName(templateName)
. getTemplateImagePath()
just seems to return the templateName
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, okay I thought this was something else. The name getTempalteImagePath()
misled me into thinking it's the full path, that function should probably be renamed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to add this shorthand in your clean-up pr and rename getTemplateImagePath
? I think that would be best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I don't know, @modmuss50 which would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please rename it in the test PR, thats ready to merge whenever, so I can do that fairly swiftly to not block this for too long.
// Set up required test environment | ||
context.getInput().resizeWindow(1708, 960); // Twice the default dimensions | ||
context.runOnClient(client -> { | ||
client.options.hudHidden = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be necessary as the client gametest runner resets all game options to the defaults before each test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test failed a previous time without these two lines. (4303293
fails on github actions.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on Discord, the GUI scale was the problem, which makes sense because GUI scale 2 isn't the default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great now, thanks for persisting with this, Im glad we spent the time on it and got something thats really good in the end. I also love the tests. 👍
* @param layer the layer to add | ||
* @return this layered drawer | ||
*/ | ||
LayeredDrawerWrapper attachLayerBefore(Identifier beforeThis, IdentifiedLayer layer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good name 👍
Small bug fix where the return value of |
...rc/client/java/net/fabricmc/fabric/api/client/rendering/v1/HudLayerRegistrationCallback.java
Outdated
Show resolved
Hide resolved
...g-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/LayeredDrawerWrapperImpl.java
Outdated
Show resolved
Hide resolved
...dering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/LayerInjectionPoint.java
Outdated
Show resolved
Hide resolved
...ing-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/LayeredDrawerWrapper.java
Show resolved
Hide resolved
...ing-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/LayeredDrawerWrapper.java
Show resolved
Hide resolved
Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
...g-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/LayeredDrawerWrapperImpl.java
Outdated
Show resolved
Hide resolved
...endering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/IdentifiedLayer.java
Show resolved
Hide resolved
...g-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/LayeredDrawerWrapperImpl.java
Outdated
Show resolved
Hide resolved
* 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 44a0820)
Originally discussed here.
Deprecate
HudRenderCallback
.Fixes #3908.