-
Posts
867 -
Joined
-
Last visited
-
Days Won
3
Everything posted by JimiIT92
-
[1.16.3] Creating and rendering a custom tooltip
JimiIT92 replied to JimiIT92's topic in Modder Support
I was going for the RenderTooltipEvent, but it looks like this event is not fired when I hover my mouse to an ItemStack. I have this event inside a class @SubscribeEvent public static void onRenderTooltip(final RenderTooltipEvent.Pre event) { if(BundleItemUtils.isBundle(event.getStack())) { //DO STUFF } } which I register during the FMLCommonSetupEvent like this MinecraftForge.EVENT_BUS.register(BundleEvents.class); Any idea on why this doesn't get called? -
Ok, I'm slowly understanding how this works. Unfortunately I've never maged the inventory directly for a case like this, where the item stack you are dragging around with you mouse will be modified when you click but not put into the inventory yet. Now I made it kinda work, by essentially doing the same operation both on the client and on the server. I already know this is bad, however I don't know how to optimize this. Plus for some reason is working fine only inside the Player container and Creative Container and other fews. If I am, for example, inside the Furnace Container and left click a Bundle, the Items will be taken out of the Bundle, but the Bundle itself won't be updated, leading to the situation where if you spam the left click you will dupe the items inside the Bundle. I've setup a GitHub repository so you can look up at what exactly is going on, from what I understand it has to do something with the fact that I'm setting the player inventory ItemStack (which is the item stack the player is dragging with its mouse) but if I remove entirely this then it doesn't get synced Here is the repository https://github.com/JimiIT92/BundlesMod Please I'm really lost into this
-
Ok, so what I'm doing now is this. In the event handle, instead of calling the BundleItemUtils, I send a message to the server BundleResources.NETWORK.sendToServer(new BundleServerMessage(draggedItemStack, Math.max(slot.slotNumber, slot.getSlotIndex()), false)); Which when handled will do this private static void processMessage(BundleServerMessage message, ServerPlayerEntity playerEntity) { Container container = playerEntity.openContainer; Slot slot = container.getSlot(message.slotId); ItemStack slotStack = slot.getStack(); if(message.empty) { BundleItemUtils.emptyBundle(message.bundle, playerEntity, container); } else { BundleItemUtils.addItemStackToBundle(message.bundle, slotStack, playerEntity, container); slot.putStack(slotStack); playerEntity.inventory.setItemStack(message.bundle); } } But I'm still having some quarkiness where Items gets duped now. Do I need to send something to the Client? If so, what should I send to it?
-
[1.16.3] Creating and rendering a custom tooltip
JimiIT92 replied to JimiIT92's topic in Modder Support
Yes, I've looked at how the renderTooltipFunction works and I was aware that I need to do that while catching the RenderTooltipEvent. What I specifically want to do is to create a custom component that I can then reuse for other Items as well, included vanilla ones, like for example the StringTextComponent. But looking at the classes I haven't seen a function related to how this component is rendered -
Ok, so right now I do this @SubscribeEvent(priority = EventPriority.HIGHEST) public static void onMouseReleased(final GuiScreenEvent.MouseReleasedEvent event) { if(!event.isCanceled() && event.getGui() instanceof ContainerScreen<?>) { ContainerScreen<?> containerScreen = (ContainerScreen<?>)event.getGui(); Slot slot = containerScreen.getSlotUnderMouse(); if(slot != null && !(slot instanceof CraftingResultSlot)) { PlayerEntity player = Minecraft.getInstance().player; if(player != null) { ItemStack draggedItemStack = player.inventory.getItemStack(); ItemStack slotStack = slot.getStack(); Container container = containerScreen.getContainer(); if(slot.canTakeStack(player) && slot.isEnabled() && container.canMergeSlot(draggedItemStack, slot) && slot.isItemValid(draggedItemStack) && slot.getHasStack() && event.getButton() == 0 && BundleItemUtils.isBundle(draggedItemStack) && BundleItemUtils.canAddItemStackToBundle(draggedItemStack, slotStack)) { BundleItemUtils.addItemStackToBundle(draggedItemStack, slotStack, player, container); event.setResult(Event.Result.DENY); event.setCanceled(true); } } } } } From what I understand, instead of calling directly BundleItemUtils I have to send a packet to the server with all the informations I need (draggedItemStack, slotStack, player and container) and then call the addItemStackToBundle while being on the server, am I right?
-
During Minecraft Live we saw how the new Bundles have a new style of tooltip, where item textures are rendered inside the tooltip. So I wonder, is it currently possible to create a custom tooltip that can render the item textures (where the items are taken from the Bundle Item NBT Tags). And how would you create this tooltip and attach to the item, to make it show instead of the default one?
-
I am attempting to create a mod that adds the new 1.17 Bundles. To do this I listen to the mouse click event on an Inventory slot. Now, inside the Player container everything works fine, if I am in creative mode. However if I use the Bundle when another container is open (for example a Chest), or if the player is just in survival mode, whatever changes I made to the inventory won't be reflected as soon as I close the container or update it somehow. For example, I have this survival inventory I then take the Bundle (the item near the crafting table) and click on the stack of 40 stone. When I click, only 32 items of the stack will go inside the bundle. The other items will remain inside the Inventory As you can see everything is right, but as soon as I click the Bundle again, I got this situation (if I close and open again the Inventory I still see the stone stack been shrinked to This is the method I use to add the Items to the Bundle public static void addItemStackToBundle(ItemStack bundle, ItemStack stack, PlayerEntity player, Container container) { if(!isBundle(bundle) || isFull(bundle) || isBundle(stack)) { return; } ItemStack stackToAdd = stack.copy(); int maxItemsToAdd = bundle.getMaxDamage() - getBundleItemsCount(bundle); stackToAdd.setCount(Math.min(getMaxStackSizeForBundleToInsert(stackToAdd), maxItemsToAdd)); CompoundNBT bundleTag = bundle.getOrCreateTag(); ListNBT items = bundleTag.getList(BundleResources.BUNDLE_ITEMS_LIST_NBT_RESOURCE_LOCATION, Constants.NBT.TAG_COMPOUND); CompoundNBT itemStackNbt = new CompoundNBT(); ItemStack stackFromBundle = getItemStackFor(bundle, stackToAdd.getItem()); int index = getItemStackIndex(bundle, stackFromBundle); if(!stackFromBundle.isEmpty()) { stackToAdd.setCount(Math.min(stackToAdd.getCount(), getMaxStackSizeForBundle(stack) - stackFromBundle.getCount())); } if(index != -1) { stackFromBundle.setCount(stackFromBundle.getCount() + stackToAdd.getCount()); stackFromBundle.write(itemStackNbt); items.remove(index); items.add(index, itemStackNbt); } else { stackToAdd.write(itemStackNbt); items.add(itemStackNbt); } bundleTag.put(BundleResources.BUNDLE_ITEMS_LIST_NBT_RESOURCE_LOCATION, items); bundle.setTag(bundleTag); stack.setCount(stack.getCount() - stackToAdd.getCount()); bundle.setDamage(bundle.getMaxDamage() - getBundleItemsCount(bundle)); container.detectAndSendChanges(); player.playSound(SoundEvents.ITEM_ARMOR_EQUIP_LEATHER, 1.0F, 1.0F); } I pass in the Container object for the slot that has been clicked and in the end call the detectAndSendChanges method, which should sync the inventory content between client and server (as the Click event is only fired client side, all I do here won't be visible to the server until I sync). Do I need to send a custom packet to the server to sync the inventory?
-
Which class handles the rendering of a tooltip?
JimiIT92 replied to Tavi007's topic in Modder Support
I believe you should look at how the Hotbar works to find out how to render a Screen constantly. The first thing I can think of is to not make the Screen a ContainerScreen, because of course that will be bind to a Container, which is something as far as I understand you don't need -
I think the closest thing to what you want to achieve is to search how particles are rendered, since they are just a textures that gets draw on the screen in a certain location in most cases. Or you can always create an entity that has the texture of your image and spawn that entity when you want to see the image, but I don't think this will be the optimal approach (but it will sure get the job done quickly)
-
Which class handles the rendering of a tooltip?
JimiIT92 replied to Tavi007's topic in Modder Support
I can't look into the code right now, but I think you can search the renderToolTip function inside a ContainerScreen and see what that function does. From there you should be able to figure out how to render your own tooltip. By the way, if you want to render it inside a screen, just use the renderToolTip or override it to do what you want it to do -
Ok, but how can I do that? Because I can't just do this @SubscribeEvent public static void onPlayerLoggedIn(final PlayerEvent.PlayerLoggedInEvent event) { event.getPlayer().container = new CustomContainer(); } since the container property is final. I could also just add the listener here, using the addListener, but I noticed that the code from my listener is executed after the code from the existing listeners. What I'm trying to achieve is to intercept when the player changes an ItemStack into a slot and cancel the event or do some stuff accordingly. But when the code from my listener runs, the slot content has already been changed, so I can't determine what was in the slot and what I'm trying to put inside. Is there a way to set a priority for listeners, so my listener will run before any other? EDIT: Figured out I have to change the value for openContainer. By doing this I can now set a custom implementation of the PlayerContainer. Thank you for the help, but I'm still curious about the listeners priority, and if there is such a way to do it (to avoid using a custom implementation)
-
I'm trying to attach a listener to the Player Inventory, to do something when the Player change an ItemStack inside a slot. To do this I'm adding the listeners to the container that is opened using the PlayerContainerEvent.Open event. This works for basically any container, except the one I want, which is the Player Container (more specifically the Hotbar and Player Inventory). This is what I've done @SubscribeEvent public static void onInventoryOpen(final PlayerContainerEvent.Open event) { Container container = event.getContainer(); if(!event.isCanceled() && (container instanceof PlayerContainer || container instanceof CreativeScreen.CreativeContainer)) { container.addListener(new BundleContainerListener()); } } So I'm checking if the Container that has been opened is a PlayerContainer or a CreativeContainer (because PlayerContainer is just the Survival Inventory and I want this listener to work in creative mode too). If it is I then add my listener to the Container, from which I'm able to do whatever I want public class BundleContainerListener implements IContainerListener { @Override public void sendAllContents(Container containerToSend, NonNullList<ItemStack> itemsList) { } @Override public void sendSlotContents(Container containerToSend, int slotInd, ItemStack stack) { System.out.println("CONTAINER: " + containerToSend + " - SLOT: " + slotInd + " - STACK: " + stack); } @Override public void sendWindowProperty(Container containerIn, int varToUpdate, int newValue) { } } If I open a Chest and set a breakpoint at the beggining of the onInventoryOpen event, the breakpoint gets hit. However if I open the Player inventory it doesn't. The interesting part is that the PlayerContainerEvent.Close event gets called. So if I change the onInventoryOpen function to this @SubscribeEvent public static void onInventoryOpen(final PlayerContainerEvent.Close event) { Container container = event.getContainer(); if(!event.isCanceled() && (container instanceof PlayerContainer || container instanceof CreativeScreen.CreativeContainer)) { container.addListener(new BundleContainerListener()); } } and open the Player Inventory, the event gets called and the listener added as well. So when I reopen the Player Inventory, the code from the Listener gets called. But this requires the Player to open and close the Inventory the first time, which of course is something I want to avoid. Any idea on why the Open event doesn't gets called for Player Inventory, but the Close one does? Also, since I don't need the other two methods from the listener, is it fine to leave them as empty or should I call something in the container class? I didn't see any issue, but I want to know just to make things right
-
So that's mandatory now... Ok, registering attributes using the enqueWork method works. Thank you for the help
-
I've setup a very simple mod for Forge 1.16.3 where I'm trying to implement the new mobs shown into the Mob Vote. But when I try to summon an entity I get this error [23:22:02] [Server thread/WARN] [minecraft/EntityType]: Exception loading entity: java.lang.NullPointerException: null at net.minecraft.entity.ai.attributes.AttributeModifierManager.func_233795_c_(AttributeModifierManager.java:67) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.LivingEntity.func_233637_b_(LivingEntity.java:1849) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.LivingEntity.getMaxHealth(LivingEntity.java:1610) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.LivingEntity.<init>(LivingEntity.java:209) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.MobEntity.<init>(MobEntity.java:108) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.entity.CreatureEntity.<init>(CreatureEntity.java:13) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.AgeableEntity.<init>(AgeableEntity.java:21) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.passive.AnimalEntity.<init>(AnimalEntity.java:37) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.passive.CowEntity.<init>(CowEntity.java:35) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at com.mobvote.entity.MoobloomEntity.<init>(MoobloomEntity.java:19) ~[main/:?] {re:classloading} at net.minecraft.entity.EntityType.create(EntityType.java:448) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.EntityType.lambda$loadEntityUnchecked$1(EntityType.java:459) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at java.util.Optional.map(Optional.java:215) ~[?:1.8.0_221] {} at net.minecraft.entity.EntityType.loadEntityUnchecked(EntityType.java:458) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.EntityType.loadEntity(EntityType.java:516) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.entity.EntityType.func_220335_a(EntityType.java:498) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.command.impl.SummonCommand.summonEntity(SummonCommand.java:50) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at net.minecraft.command.impl.SummonCommand.lambda$register$1(SummonCommand.java:34) ~[forge-1.16.3-34.1.0_mapped_snapshot_20200514-1.16-recomp.jar:?] {re:classloading} at com.mojang.brigadier.CommandDispatcher.execute(CommandDispatcher.java:262) ~[brigadier-1.0.17.jar:?] {re:classloading} at net.minecraft.command.Commands.handleCommand(Commands.java:222) ~[?:?] {re:classloading} at net.minecraft.network.play.ServerPlayNetHandler.handleSlashCommand(ServerPlayNetHandler.java:1079) ~[?:?] {re:classloading} at net.minecraft.network.play.ServerPlayNetHandler.processChatMessage(ServerPlayNetHandler.java:1059) ~[?:?] {re:classloading} at net.minecraft.network.play.client.CChatMessagePacket.processPacket(CChatMessagePacket.java:40) ~[?:?] {re:classloading} at net.minecraft.network.play.client.CChatMessagePacket.processPacket(CChatMessagePacket.java:8) ~[?:?] {re:classloading} at net.minecraft.network.PacketThreadUtil.lambda$checkThreadAndEnqueue$0(PacketThreadUtil.java:19) ~[?:?] {re:classloading} at net.minecraft.util.concurrent.TickDelayedTask.run(TickDelayedTask.java:20) ~[?:?] {re:classloading} at net.minecraft.util.concurrent.ThreadTaskExecutor.run(ThreadTaskExecutor.java:139) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.util.concurrent.RecursiveEventLoop.run(RecursiveEventLoop.java:22) ~[?:?] {re:classloading} at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:754) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:156) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.util.concurrent.ThreadTaskExecutor.driveOne(ThreadTaskExecutor.java:109) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.driveOneInternal(MinecraftServer.java:737) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.driveOne(MinecraftServer.java:731) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.util.concurrent.ThreadTaskExecutor.drainTasks(ThreadTaskExecutor.java:97) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.runScheduledTasks(MinecraftServer.java:716) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:663) ~[?:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.lambda$func_240784_a_$0(MinecraftServer.java:230) ~[?:?] {re:classloading,pl:accesstransformer:B} at java.lang.Thread.run(Thread.java:748) [?:1.8.0_221] {} The entity is just an extension to the Cow, and it's class is this /** * Moobloom Entity */ public class MoobloomEntity extends CowEntity { /** * Constructor. Set the Entity Type and the World * * @param type Entity Type * @param worldIn World */ public MoobloomEntity(EntityType<? extends CowEntity> type, World worldIn) { super(type, worldIn); } } Which is registerd like this /** * Entity Registry */ public static final DeferredRegister<EntityType<?>> ENTITY_REGISTRY = DeferredRegister.create(ForgeRegistries.ENTITIES, MobVote.MOD_ID); public static final RegistryObject<EntityType<MoobloomEntity>> MOOBLOOM_ENTITY = ENTITY_REGISTRY.register("moobloom", () -> EntityType.Builder.create(MoobloomEntity::new, EntityClassification.CREATURE) .size(0.9f, 1.4f) .build(new ResourceLocation(MobVote.MOD_ID, "moobloom").toString())); The register method for the Entity Registry is called from the mod constructor public MobVote() { final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); MobVoteEntities.ENTITY_REGISTRY.register(modEventBus); MinecraftForge.EVENT_BUS.register(this); } I used to do this in 1.15.2, has something changed in 1.16 or am I missing something?
-
That's unfortunate, I understand why is it like this, but I still find it very limiting I eventually thought that you can write your custom block with it's own ID and then replace it whrn is placed in the world, but is not the nicest solution I guess
-
That's what the error says, but if I really need to add a property to a vanilla block, like in this case make doors waterloggable, how could you do that, if there is a way to do it?
-
I tried this in 1.15.2, but I get the same result as the first post. I can't change a vanilla block property. For example, I created a waterloggable door class package com.lavalogged.blocks; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.DoorBlock; import net.minecraft.block.IWaterLoggable; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluids; import net.minecraft.fluid.IFluidState; import net.minecraft.state.StateContainer; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.DoorHingeSide; import net.minecraft.state.properties.DoubleBlockHalf; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; import net.minecraft.world.World; /** * @author JimiIT92 */ public class WaterLoggableDoorBlock extends DoorBlock implements IWaterLoggable { public WaterLoggableDoorBlock(Properties builder) { super(builder); this.setDefaultState(this.stateContainer.getBaseState().with(FACING, Direction.NORTH).with(OPEN, false).with(HINGE, DoorHingeSide.LEFT).with(POWERED, false).with(HALF, DoubleBlockHalf.LOWER).with(BlockStateProperties.WATERLOGGED, true)); } protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) { builder.add(HALF, FACING, OPEN, HINGE, POWERED, BlockStateProperties.WATERLOGGED); } public IFluidState getFluidState(BlockState state) { return Fluids.WATER.getStillFluidState(false); } public boolean receiveFluid(IWorld worldIn, BlockPos pos, BlockState state, IFluidState fluidStateIn) { return IWaterLoggable.super.receiveFluid(worldIn, pos, state, fluidStateIn); } public boolean canContainFluid(IBlockReader worldIn, BlockPos pos, BlockState state, Fluid fluidIn) { return true; } @Deprecated public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, BlockPos currentPos, BlockPos facingPos) { if (!stateIn.get(BlockStateProperties.WATERLOGGED)) { this.receiveFluid(worldIn, currentPos, stateIn, Fluids.WATER.getStillFluidState(false)); } worldIn.getPendingFluidTicks().scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickRate(worldIn)); return super.updatePostPlacement(stateIn, facing, facingState, worldIn, currentPos, facingPos); } public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) { return VoxelShapes.empty(); } public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { worldIn.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(worldIn)); } public Fluid pickupFluid(IWorld worldIn, BlockPos pos, BlockState state) { return Fluids.EMPTY; } } And then register with this blockRegistryEvent.getRegistry().register(new WaterLoggableDoorBlock(Block.Properties.from(Blocks.OAK_DOOR)) .setRegistryName(new ResourceLocation("minecraft", "oak_door"))); But the log says this Registry replacements for vanilla block 'minecraft:oak_door' must not change the number or order of blockstates. Old: facing={north,south,west,east};half={upper,lower};hinge={left,right};open={true,false};powered={true,false} New: facing={north,south,west,east};half={upper,lower};hinge={left,right};open={true,false};powered={true,false};waterlogged={true,false} So if we can't change or add properties to vanilla blocks like this, how can we do this?
-
[SOLVED][1.14.4] Persist Player attributes on respawn
JimiIT92 replied to JimiIT92's topic in Modder Support
But then how can i retrieve the capability of the "original" player, the entity that is dead? Since there is no "getOriginal" or something like that in the respawn event -
[SOLVED][1.14.4] Persist Player attributes on respawn
JimiIT92 replied to JimiIT92's topic in Modder Support
Thank you, i was able to do this catching the Clone event Ps: looks like notifications are broken, i haven't got one for this thread -
Any idea? I've kinda solved this by causing the food level to update, but of course is not something i want to keep /** * Handle the Packet * @param packet Packet * @param context Context */ public static void handle(final MaxHealthPacket packet, Supplier<NetworkEvent.Context> context) { context.get().enqueueWork(() -> { PlayerEntity player = null; if (context.get().getDirection() == NetworkDirection.PLAY_TO_CLIENT) { player = Minecraft.getInstance().player; } else { player = context.get().getSender(); } assert player != null; double baseAmount = player.getMaxHealth() - player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).getBaseValue(); AttributeModifier modifier = new AttributeModifier(Settings.MAX_HEALTH_ATTRIBUTE_MODIFIER_ID, Settings.MAX_HEALTH_ATTRIBUTE_NAME, baseAmount + packet.health , AttributeModifier.Operation.ADDITION); player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).removeModifier(Settings.MAX_HEALTH_ATTRIBUTE_MODIFIER_ID); player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).applyModifier(modifier); if(player.getMaxHealth() % 2 == 1) { player.getFoodStats().setFoodLevel(player.getFoodStats().getFoodLevel() - 1); if (context.get().getDirection() == NetworkDirection.PLAY_TO_CLIENT) { Minecraft.getInstance().player.getFoodStats().setFoodLevel(player.getFoodStats().getFoodLevel() - 1); } } }); context.get().setPacketHandled(true); }
-
As the title says i want to persist some modified player attributes when the player respawn. Say i've modified the strength or the health of the player, how can i reapply those modified values when the player respawn?
-
Look strange, the code for handling the event seems ok, it could be a bug with the latest mdk version
-
I've made a packet that changes the player max health value. The packet itself should work fine, if i add some value to the max health the new hearts are displayed correctly. However if i remove some value from the max health, say i go from 10 hearts to 9.5, the new hearts values in GUI did not update... But if i go from 10 to 9 it did, so just removing half heart is not showing properly. This is the packet i send to the client or the server when i change the max health package com.antiblaze.network; import com.antiblaze.settings.Settings; import net.minecraft.client.Minecraft; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; /** * @author JimiIT92 */ public class MaxHealthPacket { /** * Health value to add or remove from the max health */ float health; /** * Constructor. Set the health amount * @param health Health amount */ public MaxHealthPacket (float health) { this.health = health; } /** * Write the health amount to the buffer * @param packet Packet * @param buffer Buffer */ public static void encode(MaxHealthPacket packet, PacketBuffer buffer) { buffer.writeFloat(packet.health); } /** * Get the health value from the buffer * @param buffer Buffer * @return MaxHealth Packet */ public static MaxHealthPacket decode(PacketBuffer buffer) { float health = buffer.readFloat(); return new MaxHealthPacket(health); } /** * Handler inner class */ public static class Handler{ /** * Handle the Packet * @param packet Packet * @param context Context */ public static void handle(final MaxHealthPacket packet, Supplier<NetworkEvent.Context> context) { context.get().enqueueWork(() -> { PlayerEntity player = null; if (context.get().getDirection() == NetworkDirection.PLAY_TO_CLIENT) { player = Minecraft.getInstance().player; } else { player = context.get().getSender(); } assert player != null; double baseAmount = player.getMaxHealth() - player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).getBaseValue(); AttributeModifier modifier = new AttributeModifier(Settings.MAX_HEALTH_ATTRIBUTE_MODIFIER_ID, Settings.MAX_HEALTH_ATTRIBUTE_NAME, baseAmount + packet.health , AttributeModifier.Operation.ADDITION); player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).removeModifier(Settings.MAX_HEALTH_ATTRIBUTE_MODIFIER_ID); player.getAttribute(SharedMonsterAttributes.MAX_HEALTH).applyModifier(modifier); }); context.get().setPacketHandled(true); } } } What should i do to make so when the max health is a "middle value" (like 9.5 hearts, 8.5 hearts, 11.5 hearts ecc...) the hearts in the GUI are displayed correctly?