question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

KnotClassLoader does not remove lambda functions from @Environment methods

See original GitHub issue

The problem

I recently found myself in a situation, where I have a method, annotated with @Environment(EnvType.Client), which contains a lambda expression, referencing a client-only class (in my case EntityRenderer). The code produces no exceptions on the client, but on a dedicated server, I get this exception.

java.lang.RuntimeException: Cannot load class net.minecraft.client.render.entity.EntityRenderer in environment type SERVER

The interesting thing is, if I replace the lambda expression with an anonymous class executing the same logic, no exception is thrown.

The suspected cause

I suppose this problem occurs, because the KnotClassLoader does not respect the compile-time generated lambda method in it’s environment stripping process.

I found that, in the bytecode of the class, there is a generated lambda function:

...
// access flags 0x100A
private static synthetic lambda$registerClient$0(Lnet/minecraft/client/render/entity/EntityRenderDispatcher;Lnet/fabricmc/fabric/api/client/rendereregistry/v1/EntityRendererRegistry$Context;)Lnet/minecraft/client/render/entity/EntityRenderer;
...

When using an anonymous class, an inner class is generated instead:

...
public static abstract INNERCLASS net/fabricmc/fabric/api/client/rendereregistry/v1/EntityRendererRegistry$Factory net/fabricmc/fabric/api/client/rendereregistry/v1/EntityRendererRegistry Factory
...

I guess the environment strip happens on the inner class, but not on the lambda method. This causes the class loader to throw an exception, because the lambda causes class loading of client-only classes, in my case.

Details

  • fabric-loader version 0.11.3
  • Minecraft version 1.16.5
  • Fabric version 0.32.5+1.16
  • Java version 1.8 (AdoptOpenJDK\jdk-8.0.282.8-hotspot)
  • Operating System: Windows 10

Steps to reproduce

In my case, I created “modules” which have separate register() methods which are called from the common entry and the client entry of my mod. These “modules” all implement two interfaces, declaring those register methods. The client method is annotated with @Environment(EnvType.CLIENT), of course.

The general steps would be:

  1. Create a class with a client-only method, as well with some other use case (why one would load the class on a dedicated server in the first place, see my example)
  2. Call a method which requires a functional interface, which can be represented as a lambda function
  3. Reference a client-only class from the lambda
  4. Run a dedicated server

My Repository for reference

My version of the class, in a real use case, can be found here. NOTE however, that the version in this commit does not break. One has to modify these lines:

EntityRendererRegistry.INSTANCE.register(stonelingType, new EntityRendererRegistry.Factory() {
    @Override
    public EntityRenderer<? extends Entity> create(EntityRenderDispatcher manager, EntityRendererRegistry.Context context) {
        return new StonelingRenderer(manager);
    }
});

to these:

EntityRendererRegistry.INSTANCE.register(stonelingType, (manager, context) -> new StonelingRenderer(manager));

The use case is well justified, in my opinion, since one might want to organize such module based logic (like entity registering) in one class.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
JamiesWhiteShirtcommented, Dec 8, 2021

There is still a case to be made for resolving this issue. The conventional and overall safer method is to isolate client-only logic in separate classes (which you may optionally annotate with @Environment). However, in cases where Minecraft unavoidably bridges client-side logic from common logic, such as cases where you have to override various rendering-related Block methods, method-level @Environment annotations are more appropriate. For these cases it might be nice to avoid class loading errors caused by synthetic lambda methods.

Last I checked, MinecraftForge deals with this problem at runtime in their environment stripping transformer by transitively stripping synthetic methods that are solely referenced by LambdaMetaFactory invokedynamics in stripped methods. This might be an option for Fabric Loader, too, but it could have a noticeable class loading performance impact.

In my opinion, transitively applying @Environment annotations to synthetic lambda methods as a compile-time processing step would be a better option.

0reactions
LCLPYTcommented, Dec 8, 2021

Thank you for making this clear, I’ll consider separating my client and server code from now on.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Best practices for working with AWS Lambda functions
Delete Lambda functions that you are no longer using. By doing so, the unused functions won't needlessly count against your deployment package size...
Read more >
AWS Lambda Functions - Serverless Framework
All of the Lambda functions in your serverless service can be found in serverless.yml ... Currently, the Framework will not remove older versions...
Read more >
LAMBDA function - Microsoft Support
Use a LAMBDA function to create custom, reusable functions and call them by a friendly name. The new function is available throughout the...
Read more >
Lambda Expressions (The Java™ Tutorials > Learning the ...
Lambda expressions let you express instances of single-method classes more ... The method is so simple that it might not be worth it...
Read more >
Removals by version - GitLab Docs
Removed in 15.6. NFS as Git repository storage is no longer supported. As of November 22, 2022, we have removed support for customers...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found