Jump to content

Recommended Posts

Posted

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
image.png.0c446d58aef6500fe0cf25ba605a04ce.png

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

image.png.6c7ebc9db05eb186089a2c92ffa52af1.pngimage.png.dd7cc661b535600a65768b1acc417df5.png

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 8)

image.png.d98f8f7e17c5843c2b1ce352538ad48b.png

 

 

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?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
1 hour ago, JimiIT92 said:

To do this I listen to the mouse click event on an Inventory slot.

Handling this on the wrong side. You would send the information to the server which would modify the values and affect the creative screen respectively.

1 hour ago, JimiIT92 said:

Do I need to send a custom packet to the server to sync the inventory?

Most likely as the handling would need to be custom.

Posted

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?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted (edited)
2 hours ago, poopoodice said:

Imo instead of sending stacks, you should probably send the slot numbers and let the server check then modify them.

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?

Edited by JimiIT92
Updated code

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
1 hour ago, JimiIT92 said:

slot.putStack(slotStack); playerEntity.inventory.setItemStack(message.bundle);

If you're in the player container, you shouldn't need to set anything directly to the inventory. Set it only to the container slot stacks, those will update the inventory. Second, if the item is getting duplicated, you probably aren't removing the stack on click. That should be handled cliently where when you click, you send the bundle and the slot it clicks. You remove the bundle from the slot clicked in the screen, but you don't add the other stack in its place if it's not empty. Similarly on the server, you grab the stack in the slot where you clicked, put it into the bundle, and put your stack in the slot replacing the cobblestone. Please make sure that you copied the stack you're adding or else you will dupe the bundle most likely.

Posted

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 :/ 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

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