Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

LeeCrafts

Members
  • Joined

  • Last visited

Everything posted by LeeCrafts

  1. Solved. I needed to change the player's animationSpeed upon handling a RenderLivingEvent event.
  2. I am trying to implement a goofy mechanic in which--when a player tries to run--it runs in place for like 1 second before taking off with greater speed. Currently I am able to cancel the player's movement temporarily, but the player does not display a running animation (i.e. arms and legs are not swinging) while being "frozen." Is there any way to make a player look like its running in place without having to manually render custom animations?
  3. I want to give a player a status effect whenever it presses a certain key. This was my first attempt: @SubscribeEvent public static void pressCKey(InputEvent.KeyInputEvent event) { LocalPlayer localPlayer = Minecraft.getInstance().player; if (localPlayer != null && event.getKey() == 67 && event.getAction() == GLFW.GLFW_PRESS) { if (localPlayer.getActiveEffectsMap() != null) localPlayer.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SPEED, 1000)); } } I quickly realized this didn't work because it was adding the effect to the client-side player (LocalPlayer), and not the server-side player. I then searched through some older forums and found out that I would need to send a packet to the server whenever a key is pressed. There was a vanilla packet called ServerboundKeyPacket, but it confused me when I saw its constructor. I am unsure if this packet is the right one to use because it apparently does not consider which player pressed the key. Should I just use a custom packet instead?
  4. Bump. Just wanna make sure I'm doing this memory leak prevention correctly.
  5. Learning the basics of Java is essential. There are a lot of good tutorials out there (and this is a great starting point), but without knowing Java...there's not much you can do. Good luck on your modding journey.
  6. Got the rotation working. I had to look at LivingEntityRenderer#render and setupRotations a little more closely, and I realized that I needed to change the custom entity's yBodyRot and yBodyRotO. private static boolean renderingCustomEntity = false; private static CustomEntity customEntity; private static void refreshCustomEntity(LocalPlayer localPlayer, LivingEntity livingEntity) { // If the level changes, remove the custom entity by assigning its reference to a new custom entity. // The previous custom entity will eventually be garbage collected. if (customEntity == null || customEntity.level != livingEntity.level) { customEntity = ModEntities.CUSTOM_ENTITY.get().create(localPlayer.clientLevel); } } @SubscribeEvent public static void replaceMobModel(RenderLivingEvent.Pre<LivingEntity, EntityModel<LivingEntity>> event) { LocalPlayer localPlayer = Minecraft.getInstance().player; if (localPlayer != null && event.getEntity() instanceof Mob mob) { if (/* put your own condition here */) { if (!renderingCustomEntity) { try { renderingCustomEntity = true; event.setCanceled(true); refreshCustomEntity(localPlayer, mob); // (truth be told, my custom entity does not have a "head", so these lines aren't needed) // customEntity.setYHeadRot(mob.getYHeadRot()); // customEntity.yHeadRotO = mob.yHeadRotO; customEntity.setYBodyRot(mob.yBodyRot); customEntity.yBodyRotO = mob.yBodyRotO; customEntity.setPose(mob.getPose()); Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(customEntity).render( customEntity, customEntity.yBodyRot, event.getPartialTick(), event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight() ); } finally { renderingCustomEntity = false; } } } } } As shown in my code, if the mob whose model is replaced moves to a different dimension, I unload the custom entity with Entity#discard and then reinitialize it on the next tick. I wonder if I also have to unload the custom entity when the mob it is "replacing" dies. ^ Edit: I have changed the code due to @diesieben07's most recent comment.
  7. Although I am not exactly sure how a try-finally block would help (i.e. what code I should run even if a runtime exception is thrown), I have made some progress. The custom mob renders, but it does not rotate--even when I apply rotations to it before calling render(). private static boolean renderingCustomEntity = false; private static CustomEntity customEntity; private static void initializeCustomEntityIfNull(LocalPlayer localPlayer) { if (customEntity == null) { customEntity = ModEntities.CUSTOM_ENTITY.get().create(localPlayer.clientLevel); } } @SubscribeEvent public static void replaceMobModel(RenderLivingEvent.Pre<LivingEntity, EntityModel<LivingEntity>> event) { LocalPlayer localPlayer = Minecraft.getInstance().player; if (localPlayer != null && event.getEntity() instanceof Mob mob) { try { if (!renderingCustomEntity) { renderingCustomEntity = true; event.setCanceled(true); initializeCustomEntityIfNull(localPlayer); // I try to apply rotations to the custom entity before it rendered, but it still does not rotate at all. customEntity.setYRot(mob.getYRot()); customEntity.setYHeadRot(mob.getYHeadRot()); customEntity.setPose(mob.getPose()); // For some reason, the custom mob did not render when I called EntityRenderDispatcher#render. // So I called render() from EntityRenderDispatcher#getRenderer instead. Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(customEntity).render( customEntity, customEntity.getYRot(), event.getPartialTick(), event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight() ); } } finally { renderingCustomEntity = false; } } } What would I be missing? Thank you for the help.
  8. I suggest looking here: https://youtu.be/dloDcKe3Hn8?t=32
  9. Hello, I am trying to replace every rendered mob's model (not the mob itself) with the model of a custom entity, under certain conditions. This is how it would work: @SubscribeEvent public static void replaceMobModel(RenderLivingEvent.Pre<LivingEntity, EntityModel<LivingEntity>> event) { if (condition A is reached) { // replace every rendered mob's model with the model of a custom entity } // if condition A is not met, then every rendered mob's model reverts back to normal (e.g. villagers start looking like villagers again) } I was looking at this forum (https://forums.minecraftforge.net/topic/75986-114-how-to-change-rendered-entity/) and found out that I need to use EntityRenderManager#renderEntity. But since that is from a previous version, I assume it would be EntityRenderDispatcher#render. Here's what I tried to do so far: @SubscribeEvent public static void replaceMobModel(RenderLivingEvent.Pre<LivingEntity, EntityModel<LivingEntity>> event) { LocalPlayer localPlayer = Minecraft.getInstance().player; if (localPlayer != null && event.getEntity() instanceof Mob mob) { // I'll set up the conditions later once I get this working event.setCanceled(true); CustomEntity customEntity = ModEntities.CUSTOM_ENTITY.get().create(localPlayer.clientLevel); if (customEntity != null) { customEntity.setXRot(mob.getXRot()); customEntity.setYRot(mob.getYRot()); Minecraft.getInstance().getEntityRenderDispatcher().render( customEntity, mob.getX(), mob.getY(), mob.getZ(), mob.getViewYRot(event.getPartialTick()), event.getPartialTick(), event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight() ); } } } This results in a stack overflow (EntityRenderDispatcher#render seems to cause RenderLivingEvent to fire, causing an infinite recursion). Is this even the right thing to do? Or do I have to go to my CustomEntity renderer class and override render()? If that's the case, I don't know what I should put in that method.
  10. Just now I have made my repo private again for privacy reasons, but if you wanna take a look, lemme know if you want it to be public again. Here is my repo Event handler in which the size is changed Custom effect class Note: The effect that I used to make the entity larger is actually called "fat."
  11. When I tried using EntityEvent.Size, this is the code: @SubscribeEvent public static void entityHitboxSizeChange(EntityEvent.Size event) { if (event.getEntity() instanceof LivingEntity livingEntity && livingEntity.getActiveEffectsMap() != null) { EntityDimensions entityDimensions = livingEntity.getDimensions(livingEntity.getPose()); if (livingEntity.hasEffect(ModEffects.BIG.get())) { event.setNewSize(entityDimensions.scale(8, 2)); } else { event.setNewSize(entityDimensions); } } } // When I refreshDimensions in removeAttributeModifiers(), the size still does not revert immediately unless the pose changes (i.e. player crouching). // This is under the assumption that refreshDimensions() always triggers an EntityEvent.Size event @Override public void addAttributeModifiers(@NotNull LivingEntity pLivingEntity, @NotNull AttributeMap pAttributeMap, int pAmplifier) { if (!pLivingEntity.level.isClientSide()) { // other stuff ... pLivingEntity.refreshDimensions(); } super.addAttributeModifiers(pLivingEntity, pAttributeMap, pAmplifier); } @Override public void removeAttributeModifiers(@NotNull LivingEntity pLivingEntity, @NotNull AttributeMap pAttributeMap, int pAmplifier) { if (!pLivingEntity.level.isClientSide()) { // other stuff ... pLivingEntity.refreshDimensions(); } super.removeAttributeModifiers(pLivingEntity, pAttributeMap, pAmplifier); }
  12. (updated the code in my previous comment) The reason why I have been reluctant to use EntityEvent.Size is because whenever I increase the hitbox size of the living entity via setNewSize(), the enlarged hitbox starts jittering whenever it touches a solid block. Would there be anything else I have to do besides call setNewSize()?
  13. Solved and it works consistently. Calling Entity::refreshDimensions every tick would be problematic, so I called it only when I needed to. Thank you both for the help! // edit: lol forgot to use SRG name private static final Field dimensionsField = ObfuscationReflectionHelper.findField(Entity.class, "f_19815_"); public ModEvents() { dimensionsField.setAccessible(true); } // this event handler increases the living entity's hitbox size if it has the "big" effect @SubscribeEvent public static void entityHitboxSizeChange(LivingEvent.LivingUpdateEvent event) throws IllegalAccessException { LivingEntity livingEntity = event.getEntityLiving(); if (livingEntity.getActiveEffectsMap() != null) { EntityDimensions entityDimensions = livingEntity.getDimensions(livingEntity.getPose()); boolean isBig = livingEntity.hasEffect(ModEffects.BIG.get()); // Refresh dimensions if the living entity does not have the "big" effect AND its AABB has not changed back to normal. double aabbWidth = roundDigits(livingEntity.getBoundingBox().getXsize(), 4); double bigWidth = roundDigits(8 * entityDimensions.width, 4); if (!isBig && aabbWidth == bigWidth) livingEntity.refreshDimensions(); // change dimensions of living entity using reflections if (isBig) { dimensionsField.set(livingEntity, entityDimensions.scale(8.0F, 2.0F)); EntityDimensions newEntityDimensions = (EntityDimensions) dimensionsField.get(livingEntity); livingEntity.setBoundingBox(newEntityDimensions.makeBoundingBox( livingEntity.getX(), livingEntity.getY(), livingEntity.getZ() )); } } } // little helper function that rounds a double to n digits private static double roundDigits(double num, int digits) { double tenPower = Math.pow(10, digits); return Math.round(num * tenPower) / tenPower; }
  14. Are you using Fabric? I'm not sure if this forum would be the right place then.
  15. Update: I realized I don't need capabilities. In fact, I don't need to override applyEffectTick at all. But it seems like I need reflections. The increased hitbox size now works for both entity and block collision--with a little drawback. This is what it looks like so far: // this event handler checks every tick if the living entity has the "big" effect @SubscribeEvent public static void entitySizeChange(LivingEvent.LivingUpdateEvent event) { LivingEntity livingEntity = event.getEntityLiving(); if (livingEntity.getActiveEffectsMap() != null && livingEntity.hasEffect(ModEffects.BIG.get())) { EntityDimensions newDims = livingEntity.getDimensions(livingEntity.getPose()).scale(8.0F, 2.0F); try { // using reflection Field field = Entity.class.getDeclaredField("dimensions"); field.setAccessible(true); field.set(livingEntity, newDims); // setting the living entity's EntityDimensions EntityDimensions newEntityDimensions = (EntityDimensions) field.get(livingEntity); livingEntity.setBoundingBox(newEntityDimensions.makeBoundingBox( // setting the living entity's AABB livingEntity.getX(), livingEntity.getY(), livingEntity.getZ() )); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } } The aforementioned drawback is that, when the effect wears off, the living entity still keeps its bigger hitbox until it changes pose (i.e. my player crouching). Given that the EntityEvent.Size event fires whenever an entity changes pose, should I manually fire it with EVENT_BUS::post when that entity's effect wears off? If so, where? I tried manually firing EntityEvent.Size on my overridden removeAttributeModifiers(), but to no avail.
  16. Hello, I was playing around with changing the size of the AABB and discovered something that stumped me. As seen in my previous posts, I have been able to increase the size of the player with a custom status effect. I have been trying to do the same for all living entities, not just the player. Therefore, I--ideally--would not have to change the AABB by hardcoded values, like below: // overriding MobEffect::applyEffectTick in my custom effect class pLivingEntity.setBoundingBox(new AABB(pLivingEntity.getX()+2.5, pLivingEntity.getY()+3.6, pLivingEntity.getZ()+2.5, pLivingEntity.getX()-2.5, pLivingEntity.getY(), pLivingEntity.getZ()-2.5)); Simply using something like "pLivingEntity.setBoundingBox(pLivingEntity.getBoundingBox().inflate(2.5, 1.8, 2.5))" would be dangerous because the entity's AABB would expand to astronomical proportions. So I tried using a capability that stores the "original" dimensions of the entity's AABB when that entity joins the world (e.g. EntityJoinWorldEvent). This is what it looked like: // custom capability that stores the original AABB dimensions of the entity pLivingEntity.getCapability(ModCapabilities.HITBOX_CAPABILITY).ifPresent(iHitbox -> { Hitbox hitbox = (Hitbox) iHitbox; pLivingEntity.setBoundingBox(new AABB( pLivingEntity.getX() + hitbox.originalXSize * 4.2, pLivingEntity.getY() + hitbox.originalYSize * 2, pLivingEntity.getZ() + hitbox.originalZSize * 4.2, pLivingEntity.getX() - hitbox.originalXSize * 4.2, pLivingEntity.getY(), pLivingEntity.getZ() - hitbox.originalZSize * 4.2) ); }); When I tested this, the changed hitbox collides with entities but not with blocks. I am quite confused because changing the AABB by hardcoded values worked for both entity and block collision. How? Previous post for reference (where I set the bounding box): https://forums.minecraftforge.net/topic/111159-version-1182-solved-custom-status-effect-that-changes-the-players-size-part-ii/?do=findComment&comment=496252
  17. Edit: My working solution is in my previous comment. Good to know. Then would I still need the my packet handler class? If so, I am having trouble in the init() method. When I insert ClientboundUpdateMobEffectPacket::handle (same for ClientboundRemoveMobEffectPacket) as the argument for consumer(), I get an error saying "Reference to 'handle' is ambiguous, both 'handle(ClientGamePacketListener)' and 'handle(T)' match". I understand what this error is, but I do not know how to fix it. And when I tried to run the code, I get a compiler error saying "incompatible types: Supplier<Context> cannot be converted to ClientGamePacketListener)". How would I solve this if ClientboundUpdateMobEffectPacket::handle takes in a ClientGamePacketListener argument instead of a Supplier<Context> argument? // The packet handler class itself public class PacketHandler { private static final String PROTOCOL_VERSION = "1"; public static SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel( new ResourceLocation(ExampleMod.MOD_ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); private PacketHandler() {} public static void init() { int index = 0; // "Reference to 'handle' is ambiguous, both 'handle(ClientGamePacketListener)' and 'handle(T)' match" INSTANCE.messageBuilder(ClientboundUpdateMobEffectPacket.class, index++, NetworkDirection.PLAY_TO_CLIENT) .encoder(ClientboundUpdateMobEffectPacket::write).decoder(ClientboundUpdateMobEffectPacket::new) .consumer(ClientboundUpdateMobEffectPacket::handle).add(); INSTANCE.messageBuilder(ClientboundRemoveMobEffectPacket.class, index++, NetworkDirection.PLAY_TO_CLIENT) .encoder(ClientboundRemoveMobEffectPacket::write).decoder(ClientboundRemoveMobEffectPacket::new) .consumer(ClientboundRemoveMobEffectPacket::handle).add(); } } On the other hand, if I do not need this packet handler class, how else would I send packets? The only way I know is calling PacketHandler.INSTANCE.send() (and Level::sendPacketToServer, but this problem is about clientbound packets, not serverbound).
  18. I am not sure what you mean by this. Do you mean hosting a LAN server (like when you click "Open to LAN" in the pause menu)?
  19. ----- THIS IS WHERE MY SOLUTION IS ----- Solved. As diesieben07 mentioned, capabilities are not needed. So I no longer used them. My packet now stores a player's UUID and whether that player has the "big" effect. A client has an internal hash map to store the relevant data respective to each player. UPDATE: Solved. See the most recent comments. Neither capabilities nor custom packets are needed. I used the vanilla packets ClientboundUpdateMobEffectPacket and ClientboundRemoveMobEffectPacket. After some testing on a server, I realized that I also did not need the PlayerEvent.StartTracking event. Below is all the code I needed: // edit: lol forgot to use SRG name private static final Field dimensionsField = ObfuscationReflectionHelper.findField(Entity.class, "f_19815_"); public ModEvents() { dimensionsField.setAccessible(true); } // changing the living entity's HITBOX size @SubscribeEvent public static void entityHitboxSizeChange(LivingEvent.LivingUpdateEvent event) throws IllegalAccessException { LivingEntity livingEntity = event.getEntityLiving(); if (livingEntity.getActiveEffectsMap() != null) { EntityDimensions entityDimensions = livingEntity.getDimensions(livingEntity.getPose()); boolean isBig = livingEntity.hasEffect(ModEffects.BIG.get()); // Refresh dimensions if the living entity does not have the "big" effect AND its AABB has not changed back to normal. double aabbWidth = roundDigits(livingEntity.getBoundingBox().getXsize(), 4); double bigWidth = roundDigits(8 * entityDimensions.width, 4); if (!isBig && aabbWidth == bigWidth) livingEntity.refreshDimensions(); // change dimensions of living entity using reflections if (isBig) { dimensionsField.set(livingEntity, entityDimensions.scale(8.0F, 2.0F)); EntityDimensions newEntityDimensions = (EntityDimensions) dimensionsField.get(livingEntity); livingEntity.setBoundingBox(newEntityDimensions.makeBoundingBox( livingEntity.getX(), livingEntity.getY(), livingEntity.getZ() )); } } } // little helper function that rounds a double to n digits private static double roundDigits(double num, int digits) { double tenPower = Math.pow(10, digits); return Math.round(num * tenPower) / tenPower; } // client-side event handler // changing the living entity's RENDER size @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, value = Dist.CLIENT) public class ModClientEvents { @SubscribeEvent public static void entityRenderSizeChange(RenderLivingEvent.Pre<LivingEntity, EntityModel<LivingEntity>> event) { LivingEntity livingEntity = event.getEntity(); if (livingEntity != null && livingEntity.getActiveEffectsMap() != null) { if (livingEntity.hasEffect(ModEffects.BIG.get())) event.getPoseStack().scale(8.0F, 2.0F, 8.0F); } } } // Custom status effect class. addAttributeModifiers and removeAttributeModifiers are overridden because we want packets to be sent to tracking clients whenever a living entity gains or loses the custom status effect. public class BigEffect extends MobEffect { public BigEffect(MobEffectCategory mobEffectCategory, int color) { super(mobEffectCategory, color); } @Override public boolean isDurationEffectTick(int pDuration, int pAmplifier) { return true; } @Override public void addAttributeModifiers(@NotNull LivingEntity pLivingEntity, @NotNull AttributeMap pAttributeMap, int pAmplifier) { MobEffectInstance mobEffectInstance = pLivingEntity.getEffect(ModEffects.BIG.get()); if (mobEffectInstance != null) { PacketDistributor.TRACKING_ENTITY.with(() -> pLivingEntity).send( new ClientboundUpdateMobEffectPacket(pLivingEntity.getId(), mobEffectInstance) ); } super.addAttributeModifiers(pLivingEntity, pAttributeMap, pAmplifier); } @Override public void removeAttributeModifiers(@NotNull LivingEntity pLivingEntity, @NotNull AttributeMap pAttributeMap, int pAmplifier) { PacketDistributor.TRACKING_ENTITY.with(() -> pLivingEntity).send( new ClientboundRemoveMobEffectPacket(pLivingEntity.getId(), ModEffects.BIG.get()) ); super.removeAttributeModifiers(pLivingEntity, pAttributeMap, pAmplifier); } } It works, but I am not sure if it is the most graceful solution. That is, regarding the ClientboundMobSizeUpdatePacketHandler class. I feel like there is a less crude way than just using a HashMap and getting players from the client by their UUID's. If there is, please let me know.
  20. Thank you. I just had to go to my run directory and edit server.properties so that online-mode = false. Edit: I tried having more than 1 client on the server (i.e. doing runClient on multiple terminal windows), but it did not work because I "logged in from another location." So I just used my normal Minecraft account to join, and I now have 2 players on my server :)))). I just have to install my custom mod in the launcher I use.
  21. Hello, I have tried to test my mod on a dedicated server. On one terminal window, I run "./gradlew runServer". The server runs successfully. Now on another terminal window, I run "./gradlew runClient". In that client, I go to "Direct Connection" and enter localhost. However, I am unable to join my own dedicated server; it just says "Failed to log in: Invalid session." In the terminal window running the server, this message gets printed out every time I try to join through a client: [11:54:02] [Netty Server IO #1/DEBUG] [ne.mi.ne.HandshakeHandler/FMLHANDSHAKE]: Starting new modded impl connection. Found 18 messages to dispatch. [11:54:03] [Server thread/INFO] [minecraft/ServerLoginPacketListenerImpl]: com.mojang.authlib.GameProfile@48ce4fbf[id=<null>,name=Dev,properties={},legacy=false] (/127.0.0.1:58640) lost connection: Disconnected Meawhile, the client terminal window just prints out: [11:54:43] [Render thread/INFO] [minecraft/ConnectScreen]: Connecting to localhost, 25565 [11:54:43] [Netty Client IO #4/DEBUG] [ne.mi.ne.HandshakeHandler/FMLHANDSHAKE]: Starting new vanilla impl connection. What could possibly be causing this? Is this even how you test your mod on a server?
  22. The reason I'm using a capability is because I just want to learn how to sync capability data across clients in general. I feel like that's an important skill to learn. To answer your other questions: In that case, I guess I should just use orElse() instead. That is, player.getCapability(ModCapabilities.BIG_CAPABILITY).orElse(new Big()). I am not entirely sure if this would be sufficient, though. Haha, I was following a tutorial on YouTube, and I guess using an AtomicBoolean would be a "safe" option. Perhaps I should change that line of code to big.setBig(nbt.getBoolean("big")).
  23. Thank you for the reply, I'll be testing the the needed changes. Meanwhile...I don't know how I didn't find this out earlier, but the server player events (StartTracking, PlayerLoggedInEvent, PlayerRespawnEvent, and PlayerChangedDimensionEvent) never actually fire. Is it because the value argument in EventBusSubscriber is Dist.DEDICATED_SERVER? I have been testing my game via the ./gradlew runClient command, and I wonder if that's the reason. Anyways, I removed the Dist.DEDICATED_SERVER part and the events do fire...but the game crashes right afterwards. The error message says: java.lang.NullPointerException: Cannot invoke "net.minecraft.world.entity.Entity.getCommandSenderWorld()" because "entity" is null ...even when I check if event.getPlayer() is not null. How do I solve this?
  24. Hello, I have been able to properly increase the player's hitbox size whenever that player gains a custom status effect (named "Big"). But, I have yet to increase the player's render size. CMIIW, this is how it should work: Each (server) player should have its own capability that just stores a boolean, which I name isBig. isBig is true if a player has the Big status effect. I am unsure if I have to check--for every tick--if the player has the effect. Each player must send a packet to every other player's client. The packet stores the player's capability's provided NBT (which only contains a boolean, "isBig"). In other words, capability data must be synced across clients. Several events are needed for this: StartTracking, PlayerLoggedInEvent, PlayerRespawnEvent, and PlayerChangedDimensionEvent. Finally, using RenderPlayerEvent.Pre, each client-side player's capability is checked. If isBig == true, then that player's render size is increased. ^ lol dont read this Unfortunately, I have trouble executing this by code. (A lot of) relevant code is below: // Capability interface public interface IBig { void setBig(boolean isBig); boolean getBig(); void sync(Big big, ServerPlayer player); } // Capability implementing the interface public class Big implements IBig { private boolean isBig; public Big() { this.isBig = false; } @Override public void setBig(boolean isBig) { this.isBig = isBig; } @Override public boolean getBig() { return this.isBig; } @Override public void sync(Big big, ServerPlayer player) { // checks if player has the Big status effect big.setBig(player.getActiveEffectsMap() != null && player.hasEffect(ModEffects.BIG.get())); CompoundTag nbt = new CompoundTag(); nbt.putBoolean("big", big.getBig()); PacketHandler.INSTANCE.send( PacketDistributor.TRACKING_ENTITY_AND_SELF.noArg(), new ClientboundMobSizeUpdatePacket(nbt) ); } } // Provider for the capability public class BigProvider implements ICapabilitySerializable<CompoundTag> { private final Big big = new Big(); private final LazyOptional<IBig> bigLazyOptional = LazyOptional.of(() -> big); @NotNull @Override public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) { // return bigLazyOptional.cast(); return ModCapabilities.BIG_CAPABILITY.orEmpty(cap, bigLazyOptional); } @Override public CompoundTag serializeNBT() { if (ModCapabilities.BIG_CAPABILITY == null) { return new CompoundTag(); } CompoundTag nbt = new CompoundTag(); nbt.putBoolean("big", big.getBig()); return nbt; } @Override public void deserializeNBT(CompoundTag nbt) { if (ModCapabilities.BIG_CAPABILITY != null) { big.setBig(nbt.getBoolean("big")); } } public void invalidate() { bigLazyOptional.invalidate(); } } public class ModCapabilities { // other capabilities... // instance of the Big capability public static Capability<IBig> BIG_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){}); } // Events for registering and attaching capabilities @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID) public class ModEvents { @SubscribeEvent public static void registerCapabilities(RegisterCapabilitiesEvent event) { event.register(IBig.class); } @SubscribeEvent public static void onAttachCapabilitiesEventBig(AttachCapabilitiesEvent<Entity> event) { if (event.getObject() instanceof Player && !event.getObject().getCommandSenderWorld().isClientSide) { BigProvider bigProvider = new BigProvider(); event.addCapability(new ResourceLocation(ExampleMod.MOD_ID, "big"), bigProvider); event.addListener(bigProvider::invalidate); } } } // FMLCommonSetupEvent for initializing the packet handler @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModCommonEvents { @SubscribeEvent public static void commonSetup(FMLCommonSetupEvent event) { event.enqueueWork(PacketHandler::init); } } // The packet handler class itself public class PacketHandler { private static final String PROTOCOL_VERSION = "1"; public static SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel( new ResourceLocation(ExampleMod.MOD_ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private PacketHandler() { } public static void init() { int index = 0; INSTANCE.messageBuilder(ClientboundMobSizeUpdatePacket.class, index++, NetworkDirection.PLAY_TO_CLIENT) .encoder(ClientboundMobSizeUpdatePacket::encode).decoder(ClientboundMobSizeUpdatePacket::new) .consumer(ClientboundMobSizeUpdatePacket::handle).add(); } } // Packet class public class ClientboundMobSizeUpdatePacket { private CompoundTag nbt; public ClientboundMobSizeUpdatePacket(CompoundTag nbt) { this.nbt = nbt; } public ClientboundMobSizeUpdatePacket(FriendlyByteBuf buffer) { this(buffer.readNbt()); } public void encode(FriendlyByteBuf buffer) { buffer.writeNbt(this.nbt); } public boolean handle(Supplier<NetworkEvent.Context> ctx) { final var success = new AtomicBoolean(false); ctx.get().enqueueWork(() -> { if (ctx.get().getDirection().getReceptionSide().isClient() && ctx.get().getDirection().getOriginationSide().isServer()) { // the player variable should be a client-side player whose capability is to be updated final LocalPlayer player = Minecraft.getInstance().player; DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { Big big = (Big) iBig; big.setBig(new BigProvider().serializeNBT().getBoolean("big")); }); }); success.set(true); } }); ctx.get().setPacketHandled(success.get()); return success.get(); } } // Event handler for client-side players. This is where the render size change should happen. @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, value = Dist.CLIENT) public class ModClientEvents { // must be client-side only @SubscribeEvent public static void big(RenderPlayerEvent.Pre event) { // Do I need to use Minecraft.getInstance() to get the player? Just wondering because we're dealing with the client side here. // Player player = event.getPlayer(); final Player player = Minecraft.getInstance().level.getPlayerByUUID(event.getPlayer().getUUID()); if (player != null) { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { // Through some debugging, I have found out that this section never gets reached Big big = (Big) iBig; if (big.getBig()) { event.getPoseStack().scale(8.0F, 2.0F, 8.0F); } }); } } } // Event handlers for server-side players // I must set the value to Dist.DEDICATED_SERVER otherwise a crash will occur @Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.DEDICATED_SERVER) public class ForgeCommonEvents { @SubscribeEvent public static void startTracking(PlayerEvent.StartTracking event) { Player player = (Player) event.getTarget(); ServerPlayer target = (ServerPlayer) event.getPlayer(); if (!player.level.isClientSide()) { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { Big big = (Big) iBig; // checks if player has the Big status effect big.setBig(player.getActiveEffectsMap() != null && player.hasEffect(ModEffects.BIG.get())); CompoundTag nbt = new CompoundTag(); nbt.putBoolean("big", big.getBig()); PacketHandler.INSTANCE.send( PacketDistributor.PLAYER.with(() -> target), new ClientboundMobSizeUpdatePacket(nbt) ); }); } } @SubscribeEvent public static void playerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { ServerPlayer player = (ServerPlayer) event.getPlayer(); if (!player.level.isClientSide()) { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { Big big = (Big) iBig; iBig.sync(big, player); }); } } @SubscribeEvent public static void playerRespawn(PlayerEvent.PlayerRespawnEvent event) { ServerPlayer player = (ServerPlayer) event.getPlayer(); if (!player.level.isClientSide()) { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { Big big = (Big) iBig; iBig.sync(big, player); }); } } @SubscribeEvent public static void playerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) { ServerPlayer player = (ServerPlayer) event.getPlayer(); if (!player.level.isClientSide()) { player.getCapability(ModCapabilities.BIG_CAPABILITY).ifPresent(iBig -> { Big big = (Big) iBig; iBig.sync(big, player); }); } } } When the player gets the Big status effect, it does not grow like it should. As indicated by the last section of code, the client-side player's capability is never present for some reason. Is my logic incorrect? I appreciate the help, thank you in advance.

Important Information

By using this site, you agree to our Terms of Use.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.