Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.14.4] GUI Without Container?


JayZX535
 Share

Recommended Posts

Hey all,

 

I'm working on implementing a lore book to my mod.  It works similarly to vanilla books, except the text is all predefined.  As such, I'm trying to figure out how to create and access a GUI for it.  My question is, I'm not sure if I need to use a container for it, or if it even needs serverside access at all.  I'm planning on using info that's drawn from the mod's lang file and a player capability (which I've already coded to sync to the client), and it shouldn't need to send any info back to the server (nothing the player can edit except the currently viewed page, which I can easily handle via the client) so if I'm understanding everything right, it doesn't actually need to pull any data from the server.  As such, I'm not sure if I need to use a container at all, or if I should just make it a screen.  However, I want to ensure that I'm doing this properly.

 

I see that ClientPlayerEntity uses the following method... (which is called from the book items)

public void openBook(ItemStack stack, Hand hand) {
      Item item = stack.getItem();
      if (item == Items.WRITABLE_BOOK) {
         this.mc.displayGuiScreen(new EditBookScreen(this, stack, hand));
      }
   }

 

So I'm guessing I may need to do something like that.  Is this.mc.displayGUIScreen() the right method to use for modded GUIs?  Do I need to register the GUI anywhere, or if it's not linked to a container can I just open it like this?  I assume I need a client safe method to call to open this, so I'm guessing I should run that method through my proxy for safety?  Is there anything I should be aware of when it comes to using a client-only screen, and is there any reason I might still want to opt to use a container in this case?  If the method is clientside only, is there anything special I need to do to block movement (or anything else noteworthy)?

 

Thanks, and have a great day.

Link to comment
Share on other sites

1 hour ago, diesieben07 said:

If you just want a client-side GUI all you need to do is extend the Screen class and call displayGuiScreen. Just make sure to use DistExecutor (and possibly World#isRemote) to run it on the client only.

Thanks for the confirmation!  Works like a charm on both server and client.  Ended up using both checks just to be extra certain and it all seems just fine.

Link to comment
Share on other sites

4 minutes ago, JayZX535 said:

Thanks for the confirmation!  Works like a charm on both server and client.  Ended up using both checks just to be extra certain and it all seems just fine.

Nothing to do with "extra certain". They do different things, you should learn what these things do.

Link to comment
Share on other sites

1 hour ago, diesieben07 said:

Nothing to do with "extra certain". They do different things, you should learn what these things do.

I'm aware they do different things and I'm aware of what they do.  I'm being "extra certain" by covering both possibilities.

Link to comment
Share on other sites

DistExecutor is used for physical side, whether you are using a minecraft client or dedicated server (in this case, this is the relevant information as only physical clients do rendering)

 

isRemote references the logical side, where true is the logical client. Logical client just means if it's not running the gameserver instance on that thread, so isRemote could return false on clients on the server thread. Also, isRemote checks do not properly avoid classloading, unlike DistExecutor which avoids them by utilizing the double supplier trick.

Link to comment
Share on other sites

Just now, diesieben07 said:

Just as a note, it doesn't actually do this properly.

Does it not? I've used the double supplier trick in a lot of code and it's all worked just fine on dedicated servers.

Link to comment
Share on other sites

Just now, hiotewdew said:

Does it not? I've used the double supplier trick in a lot of code and it's all worked just fine on dedicated servers.

It works, most of the time. To understand when it does not work, you have to know a lot about how lambdas are implemented and how the JVM loads class files.

If you write a lambda, that code becomes a static method in the same class. If you write two lambdas (even if they are nested within each other), the code is still present in the containing class file, as two static methods. The JDK will then at runtime use whatever means it sees fit (currently it just creates new class files using ASM) to turn those static methods into actual instances of the interface.

Now, when the JVM loads your class file it has to verify it, so that there are no unsound type operations in the bytecode, for example.

So, if you e.g. do:

ClientWorld world = Minecraft.getMinecraft().world 

The JVM has to check that the type of Minecraft#world (ClientWorld) is actually assignable to ClientWorld. In this case this is trivial, because obviously ClientWorld is-a ClientWorld.

 

In this case the JVM can verify this code without loading any classes (i.e. it doesn't need to load ClientWorld). Your class (containing the two static methods for the lambdas) loads fine, even on the server.

 

Now, let's do a very subtle change:

World world = Minecraft.getMinecraft().world

Suddenly, your code will crash on the server. Why? Because the JVM has to now load World and ClientWorld to see if ClientWorld is assignable to World. It will fail to find ClientWorld and boom, your code crashes with a NoClassDefFoundError on a server.

 

This is why I really do not like this approach, because it is brittle and requires deep understanding of many mechanics that you normally never need to worry about in Java development to use correctly.

Yes, it's argueably a lot more convenient than @SidedProxy, but @SidedProxy made it very clear that we are dealing with classes that are potentially absent here, thus reflection must be used, it is the only truly safe way to interact with classes that are potentially absent.

It is also still unclear if this optimization the JVM does (not loading the classes at all if they are equal) is actually specified in the JVM specification, or if this whole approach relies on an implementation detail that HotSpot (OpenJDK's main JVM) does.

Link to comment
Share on other sites

1 hour ago, diesieben07 said:

So, if you e.g. do:


ClientWorld world = Minecraft.getMinecraft().world 

The JVM has to check that the type of Minecraft#world (ClientWorld) is actually assignable to ClientWorld. In this case this is trivial, because obviously ClientWorld is-a ClientWorld.

 

In this case the JVM can verify this code without loading any classes (i.e. it doesn't need to load ClientWorld). Your class (containing the two static methods for the lambdas) loads fine, even on the server.

Out of curiosity, wouldn't this trigger the class loading of Minecraft since a method of Minecraft is being invoked?

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • Okay, so i am playing on a online hosting with around 80 mods, this just randomly happens when we play. Help please?    [15:47:08] [Server thread/ERROR] [ne.mi.ev.EventBus/EVENTBUS]: Exception caught during firing event: net.minecraft.entity.LivingEntity.func_70016_h(DDD)V Index: 2 Listeners: 0: NORMAL 1: ASM: com.bobmowzie.mowziesmobs.server.ServerEventHandler@13a5bc1a onLivingJump(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V 2: ASM: class com.deltajay.tonsofenchants.enchantments.normalench.feet.Springy jumpHigher(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V 3: ASM: class com.ma.events.CommonEventHandler onLivingJump(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V 4: ASM: blusunrize.immersiveengineering.common.EventHandler@24572eae onLivingJump(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V 5: LOW 6: ASM: class io.github.apace100.origins.OriginForgeEventHandler livingJump(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V 7: LOWEST 8: ASM: alpvax.mc.goprone.GoProne@17346c35 onPlayerJump(Lnet/minecraftforge/event/entity/living/LivingEvent$LivingJumpEvent;)V java.lang.NoSuchMethodError: net.minecraft.entity.LivingEntity.func_70016_h(DDD)V at com.deltajay.tonsofenchants.enchantments.normalench.feet.Springy.jumpHigher(Springy.java:70) at net.minecraftforge.eventbus.ASMEventHandler_114_Springy_jumpHigher_LivingJumpEvent.invoke(.dynamic) at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) at net.minecraftforge.common.ForgeHooks.onLivingJump(ForgeHooks.java:460) at net.minecraft.entity.LivingEntity.func_70664_aZ(LivingEntity.java:1877) at net.minecraft.entity.LivingEntity.func_70636_d(LivingEntity.java:2430) at net.minecraft.entity.MobEntity.func_70636_d(MobEntity.java:488) at net.minecraft.entity.monster.MonsterEntity.func_70636_d(SourceFile:43) at net.minecraft.entity.monster.AbstractSkeletonEntity.func_70636_d(AbstractSkeletonEntity.java:116) at net.minecraft.entity.LivingEntity.func_70071_h_(LivingEntity.java:2158) at net.minecraft.entity.MobEntity.func_70071_h_(MobEntity.java:300) at net.minecraft.world.server.ServerWorld.func_217479_a(ServerWorld.java:611) at net.minecraft.world.World.func_217390_a(World.java:554) at net.minecraft.world.server.ServerWorld.func_72835_b(ServerWorld.java:404) at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:851) at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:291) at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:787) at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:642) at net.minecraft.server.MinecraftServer.func_240783_a_(MinecraftServer.java:232) at java.lang.Thread.run(Thread.java:748) [15:47:08] [Server thread/FATAL] [ne.mi.co.ForgeMod/]: Preparing crash report with UUID 5f4e4d65-4ba2-4d02-90c3-f5df12470a80 [15:47:08] [Server thread/ERROR] [minecraft/MinecraftServer]: Encountered an unexpected exception net.minecraft.crash.ReportedException: Ticking entity at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:855) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.terraforged.json:common.MixinMinecraftServer,pl:mixin:APP:betterendforge.mixins.json:MinecraftServerMixin,pl:mixin:APP:structure_gel.mixins.json:MinecraftServerMixin,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:291) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:787) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.terraforged.json:common.MixinMinecraftServer,pl:mixin:APP:betterendforge.mixins.json:MinecraftServerMixin,pl:mixin:APP:structure_gel.mixins.json:MinecraftServerMixin,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:642) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.terraforged.json:common.MixinMinecraftServer,pl:mixin:APP:betterendforge.mixins.json:MinecraftServerMixin,pl:mixin:APP:structure_gel.mixins.json:MinecraftServerMixin,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.server.MinecraftServer.func_240783_a_(MinecraftServer.java:232) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.terraforged.json:common.MixinMinecraftServer,pl:mixin:APP:betterendforge.mixins.json:MinecraftServerMixin,pl:mixin:APP:structure_gel.mixins.json:MinecraftServerMixin,pl:mixin:A,pl:runtimedistcleaner:A} at java.lang.Thread.run(Thread.java:748) [?:1.8.0_242] {} Caused by: java.lang.NoSuchMethodError: net.minecraft.entity.LivingEntity.func_70016_h(DDD)V at com.deltajay.tonsofenchants.enchantments.normalench.feet.Springy.jumpHigher(Springy.java:70) ~[tonsofenchants:1.0] {re:classloading} at net.minecraftforge.eventbus.ASMEventHandler_114_Springy_jumpHigher_LivingJumpEvent.invoke(.dynamic) ~[?:?] {} at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) ~[eventbus-4.0.0.jar:?] {} at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) ~[eventbus-4.0.0.jar:?] {} at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) ~[eventbus-4.0.0.jar:?] {} at net.minecraftforge.common.ForgeHooks.onLivingJump(ForgeHooks.java:460) ~[forge:?] {re:mixin,re:classloading,pl:mixin:APP:origins.forge.mixins.json:ForgeHooksMixin,pl:mixin:A} at net.minecraft.entity.LivingEntity.func_70664_aZ(LivingEntity.java:1877) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:blue_skies.mixins.json:LivingEntityMixin,pl:mixin:APP:caelus.mixins.json:LivingEntityMixin,pl:mixin:APP:citadel.mixins.json:LivingEntityMixin,pl:mixin:APP:curioofundying.mixins.json:LivingEntityMixin,pl:mixin:APP:assets/offhandcombat/offhandcombat.mixins.json:SwingEntityMixin,pl:mixin:APP:origins.mixins.json:LivingEntityMixin,pl:mixin:APP:origins.forge.mixins.json:LivingEntityMixin,pl:mixin:APP:enhancedcelestials.mixins.json:MixinLivingEntity,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.entity.LivingEntity.func_70636_d(LivingEntity.java:2430) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:blue_skies.mixins.json:LivingEntityMixin,pl:mixin:APP:caelus.mixins.json:LivingEntityMixin,pl:mixin:APP:citadel.mixins.json:LivingEntityMixin,pl:mixin:APP:curioofundying.mixins.json:LivingEntityMixin,pl:mixin:APP:assets/offhandcombat/offhandcombat.mixins.json:SwingEntityMixin,pl:mixin:APP:origins.mixins.json:LivingEntityMixin,pl:mixin:APP:origins.forge.mixins.json:LivingEntityMixin,pl:mixin:APP:enhancedcelestials.mixins.json:MixinLivingEntity,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.entity.MobEntity.func_70636_d(MobEntity.java:488) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.entity.monster.MonsterEntity.func_70636_d(SourceFile:43) ~[?:?] {re:mixin,re:classloading,pl:mixin:APP:betterendforge.mixins.json:MonsterEntityMixin,pl:mixin:A} at net.minecraft.entity.monster.AbstractSkeletonEntity.func_70636_d(AbstractSkeletonEntity.java:116) ~[?:?] {re:classloading,pl:accesstransformer:B,xf:fml:forge:bows.5,xf:fml:forge:bows.4} at net.minecraft.entity.LivingEntity.func_70071_h_(LivingEntity.java:2158) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:blue_skies.mixins.json:LivingEntityMixin,pl:mixin:APP:caelus.mixins.json:LivingEntityMixin,pl:mixin:APP:citadel.mixins.json:LivingEntityMixin,pl:mixin:APP:curioofundying.mixins.json:LivingEntityMixin,pl:mixin:APP:assets/offhandcombat/offhandcombat.mixins.json:SwingEntityMixin,pl:mixin:APP:origins.mixins.json:LivingEntityMixin,pl:mixin:APP:origins.forge.mixins.json:LivingEntityMixin,pl:mixin:APP:enhancedcelestials.mixins.json:MixinLivingEntity,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.entity.MobEntity.func_70071_h_(MobEntity.java:300) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.world.server.ServerWorld.func_217479_a(ServerWorld.java:611) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:abnormals_core.mixins.json:ServerWorldMixin,pl:mixin:APP:betternether.mixins.json:ServerWorldMixin,pl:mixin:APP:immersiveengineering.mixins.json:coremods.ServerWorldMixin,pl:mixin:APP:enhancedcelestials.mixins.json:MixinServerWorld,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.world.World.func_217390_a(World.java:554) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,xf:fml:twilightforest:hitbox,pl:mixin:APP:sereneseasons.mixins.json:MixinWorld,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.world.server.ServerWorld.func_72835_b(ServerWorld.java:404) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:abnormals_core.mixins.json:ServerWorldMixin,pl:mixin:APP:betternether.mixins.json:ServerWorldMixin,pl:mixin:APP:immersiveengineering.mixins.json:coremods.ServerWorldMixin,pl:mixin:APP:enhancedcelestials.mixins.json:MixinServerWorld,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:851) ~[?:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.terraforged.json:common.MixinMinecraftServer,pl:mixin:APP:betterendforge.mixins.json:MinecraftServerMixin,pl:mixin:APP:structure_gel.mixins.json:MinecraftServerMixin,pl:mixin:A,pl:runtimedistcleaner:A} ... 5 more
    • ReplayMod is a fabric-only mod, why do they even have it...
    • You can save the color of your entity in it's class (make sure it's saved to the NBT and is synced) and after that use the instance that's passed to the method for the texture in your model to check for the color and pass a different texture depending on that. You can have an enum with all your colors and then save the ordinal of the color to the NBT.
    • I'm working on an entity, which texture should have several variations. You know, there is blue sheep, black sheep, white sheep. I want to do the same thing. How should I do that? I'm working with Geckolib.
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.