Jump to content

[1.14.4] Gui/Container for Player (No tile entity)


Recommended Posts

Posted

I'm attempting to override the default inventory GUI and replace it with my own. I have already created a simple Gui and have it implemented (opens on pressing k). I used it to test my code to override the vanilla inventory opening. In my client event subscriber I call

 

@SubscribeEvent
public static void onGuiOpenEvent(GuiOpenEvent event) {
	if (Minecraft.getInstance().player.getCapability(RPGProvider.RPG_CAP).orElse(null).isRPGActive() && event.getGui() instanceof InventoryScreen) {
  		event.setGui(new AbilityScoresScreen(new TranslationTextComponent("demo.help.title")));
    }
}

 

Not sure if that really is the best way, but it is working. Only downside is I can't seem to manage to have the new Gui closed when pressing "E".

 

Now I'm trying to actually create the new inventory screen and container. I'm not finding much information on how to implement this in 1.14 and all of what I have found deals with containers tied to block tile entities. Mcjty has one of the more descriptive examples but wasn't really what I needed and didn't match to what is largely mentioned and recommended by Desht. I have created a container that is largely a mimic of PlayerContainer, but places the slots in the correct x,y points and adds my many new equipment slots. It also skips all the logic for recipe book and crafting as my screen does not need them. I've now started on the new inventory screen and am kinda stuck.

 

The default InventoryScreen constructs with the code

 

public InventoryScreen(PlayerEntity player) {
	super(player.container, player.inventory, new TranslationTextComponent("container.crafting"));
	this.passEvents = true;
}

 

I can largely mimic this, but need access to my container instead of the default player.container. What is the correct way to be doing this?

 

For registering the container, or opening from the client, Desht states that I should use a constructor in the form of:

 

public MyContainer(int windowId, PlayerInv inv, PacketBuffer extraData) {
	super(ContainerType<?>, windowId);
	doThings();
}

And a second constructor for the server side. The vanilla inventory container uses the constructor below, which I mimicked and would be my server one?

 

public PlayerContainer(PlayerInventory playerInventory, boolean localWorld, PlayerEntity playerIn) {
	super((ContainerType<?>)null, 0);
	this.isLocalWorld = localWorld;
	this.player = playerIn;
    // All the code for placing slots is after this....  
}

I'm kinda stuck and not sure how to progress, any help would be much appreciated. As a summery my questions are:

 

  1. How do I get my container when calling the constructor for my inventory screen
  2. Proper registration of my container/correct set up of multiple constructors
  3. Not fully understanding why the client and server need separate constructors
  4. (Bonus and not really important) any recommendations on how to close the screen if "E" is pressed again. Doing a simple key bind on the "E" key doesn't ever fire. I'm guessing the default open inventory is capturing it and preventing it from continuing.

 

Below is the entire code for my container and screen (although there isn't really anything to the screen yet).

 

RPGInventoryContainer.java

package com.github.wog890.wogmods.inventory.container;

import javax.annotation.Nullable;

import com.github.wog890.wogmods.enums.EEquipmentSlotType;
import com.github.wog890.wogmods.item.RPGItem;

import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

public class RPGInventoryContainer extends Container {
	
	private static final String [] ARMOR_SLOT_TEXTURES = new String[] {
			"item/empty_armor_slot_chestplate", "", "", "",
			"", "item/empty_armor_slot_boots", "", "item/empty_armor_slot_helmet",
			"", "", "",
			"", "", ""};
	
	
	private static final EEquipmentSlotType[] VALID_EQUIPMENT_SLOTS = new EEquipmentSlotType[] {
			EEquipmentSlotType.ARMOR, EEquipmentSlotType.BELT, EEquipmentSlotType.BODY, EEquipmentSlotType.CHEST,
			EEquipmentSlotType.EYES, EEquipmentSlotType.FEET, EEquipmentSlotType.HANDS, EEquipmentSlotType.HEAD,
			EEquipmentSlotType.HEADBAND, EEquipmentSlotType.NECK, EEquipmentSlotType.RING_LEFT,
			EEquipmentSlotType.RING_RIGHT, EEquipmentSlotType.SHOULDERS, EEquipmentSlotType.WRIST};
	
	public final boolean isLocalWorld;
	
	private final PlayerEntity player;
	
	public RPGInventoryContainer(int windowId, PlayerInventory inv, PacketBuffer extraData) {
		super((ContainerType<?>)null, windowId);
		player = inv.player;
              
        // Temporary to prevent error being fired. Will set this properly later
		isLocalWorld = false;
	}
	
	public RPGInventoryContainer(PlayerInventory invIn, boolean localWorld, PlayerEntity playerIn) {
		super((ContainerType<?>)null, 108);
		this.isLocalWorld = localWorld;
		this.player = playerIn;
		
		// Places the slots for the main player inventory
		// Index Range: 9 - 35
		for (int j=0; j<3; ++j) {
			for (int k=0; k<9; ++k) {
				this.addSlot(new Slot(invIn, k+(j+1)*9, 49+k*18, 92+j*18));
			}
		}
		
		// Places the slots for the main player hotbar
		// Index Range: 0 - 8
		for (int l=0; l<9; ++l) {
			this.addSlot(new Slot(invIn, l, 49+l*18, 150));
		}
		
		// Places the offhand slot
		// Index Range: 40
		this.addSlot(new Slot(invIn, 40, 122, 72) {
			@Nullable
			@OnlyIn(Dist.CLIENT)
			public String getSlotTexture() {
				return "item/empty_armor_slot_shield";
			}
		});
		
		// Places the many equipments slots
		// Index Range: 41 - 54
		for (int m=0; m<14; ++m) {
			final EEquipmentSlotType slotType = VALID_EQUIPMENT_SLOTS[m];
			this.addSlot(new Slot(invIn, slotType.getSlotIndex(), slotType.getX(), slotType.getY()) {
				
				public int getSlotStackLimit() {
					return 1;
				}
				
				public boolean isItemValid(ItemStack stack) {
					if (!(stack.getItem() instanceof RPGItem)) return false;
					return ((RPGItem)stack.getItem()).canEquip(slotType);
				}
				
				public boolean canTakeStack(PlayerEntity playerIn) {
					ItemStack itemstack = this.getStack();
					return !itemstack.isEmpty() && !playerIn.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.canTakeStack(playerIn);
				}
				
				@Nullable
				@OnlyIn(Dist.CLIENT)
				public String getSlotTexture() {
					return RPGInventoryContainer.ARMOR_SLOT_TEXTURES[slotType.getIndex()];
				}
				
			});
		}
	}
	
	@Override
	public boolean canInteractWith(PlayerEntity playerIn) {
		return true;
	}

}

 

InventoryScreen.java

package com.github.wog890.wogmods.client.gui.screen;

import com.github.wog890.wogmods.inventory.container.RPGInventoryContainer;

import net.minecraft.client.gui.DisplayEffectsScreen;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.text.TranslationTextComponent;

public class RPGInventoryScreen extends DisplayEffectsScreen<RPGInventoryContainer> {
	
	public RPGInventoryScreen(PlayerEntity player) {
  		//null needs to be my RPGInventoryContainer
		super(null, player.inventory, new TranslationTextComponent("containers.wogmods.inv"));
		this.passEvents = true;
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		// TODO Auto-generated method stub
		
	}

}

 

Posted
56 minutes ago, wog890 said:

DisplayEffectsScreen<RPGInventoryContainer>

Extend ContainerScreen instead. This will solve the E button problem.

56 minutes ago, wog890 said:

How do I get my container when calling the constructor for my inventory screen

PlayerEntity#openContainer assuming your container is the one open on the client side.

58 minutes ago, wog890 said:

Proper registration of my container/correct set up of multiple constructors

Show what you are doing now.

59 minutes ago, wog890 said:

Not fully understanding why the client and server need separate constructors

They don't. The only reason to have a PacketBuffer parameter in your constructor is to pass extra data over from the server. I dont think yours needs that.

 

I'm not at my IDE at the moment so I cant give you definitive answers with code, but I think I can point you in the proper direction, where to look, ect.

  • Like 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted (edited)
49 minutes ago, Animefan8888 said:

Extend ContainerScreen instead. This will solve the E button problem.

PlayerEntity#openContainer assuming your container is the one open on the client side.

Show what you are doing now.

They don't. The only reason to have a PacketBuffer parameter in your constructor is to pass extra data over from the server. I dont think yours needs that.

 

I'm not at my IDE at the moment so I cant give you definitive answers with code, but I think I can point you in the proper direction, where to look, ect.

Alrighty.

 

Updated RPGInventoryScreen

public class RPGInventoryScreen extends ContainerScreen<RPGInventoryContainer> {
	
	public RPGInventoryScreen(PlayerEntity player) {
		super((RPGInventoryContainer) player.openContainer, player.inventory, new TranslationTextComponent("containers.wogmods.inv"));
		this.passEvents = true;
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		// TODO Auto-generated method stub
		
	}

}

 

 

So I started attempting to register the container like so:

@SubscribeEvent
public static void registerContainers(final RegistryEvent.Register<ContainerType<?>> event) {
	event.getRegistry().registerAll(
		RPGInventoryContainer.class
	);
}

or
  
@SubscribeEvent
public static void registerContainers(final RegistryEvent.Register<ContainerType<?>> event) {
	event.getRegistry().registerAll(
    	// Just a test, would need to pass a proper inventory as the middle argument. Wasn't sure how to do this though.
      	// Minecraft.getInstance().player.inventory would be accessing it through the ClientEntityPlayer which seems wrong...
      	// Sides still trip me up a good bit :-/
  		new RPGInventoryContainer(0, null, null)
	);
}

 

This of course didn't work as the event is registering ContainerTypes not Containers. Once I realized this I actually looked at what ContainerType was. Looking at that I can see it registering containers through calling something like

public static final ContainerType<RepairContainer>  = register("anvil", RepairContainer::new);

The container type is being passed a key and creating an instance of the passed container (right), but each of these is connected to a block except for MerchantContainer.

(I'm kinda just assuming that MerchantContainer is attached to villagers who trade). PlayerContainer is not registered here and because of this when constructed it simply calls

super((ContainerType<?>)null, 0);

 

Because of this I'm unsure of if I should be registering my container or not.

 

Halfway through typing this I realized I hadn't look at McJty's example to see how he registered his container. That was stupid of me. Matching his code I now have this:

 

@SubscribeEvent
public static void registerContainers(final RegistryEvent.Register<ContainerType<?>> event) {
	event.getRegistry().register(IForgeContainerType.create((windowId, inv, data) -> {
		return new RPGInventoryContainer(windowId, inv, data);
	}).setRegistryName("rpg_inventory"));
}

@ObjectHolder("wogmods:rpg_inventory")
public static ContainerType<RPGInventoryContainer> RPG_INV_CONTAINER;

 

He also states that the ContainerType is only used client side. So I can register my container if needed.

 

Sorry for the rambling and long description. The only way I can think to describe my confusion is to go through my thought process. Thank you for the help!

Edited by wog890
Slight edits to registerContainers and adding @ObjectHolder
Posted
14 minutes ago, wog890 said:

He also states that the ContainerType is only used client side. So I can register my container if needed.

Well I cant think of any reason why you wouldn't register it, it's also needed in the Container class so I'm not sure that's entirely accurate.

 

15 minutes ago, wog890 said:

Matching his code I now have this:

That all looks good. Though unless you are sending specific data to the client like a TileEntity's BlockPos(which you are not) you dont need to worry about the PacketBuffer Parameter. You can also have one constructor and deserialize the data in the lambda.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted
On 9/30/2019 at 11:09 PM, Animefan8888 said:

Well I cant think of any reason why you wouldn't register it, it's also needed in the Container class so I'm not sure that's entirely accurate.

 

That all looks good. Though unless you are sending specific data to the client like a TileEntity's BlockPos(which you are not) you dont need to worry about the PacketBuffer Parameter. You can also have one constructor and deserialize the data in the lambda.

So I thought I had it, but its all gone soooo downhill. From your second comment I updated container registry to:

 

@SubscribeEvent
public static void registerContainers(final RegistryEvent.Register<ContainerType<?>> event) {
	event.getRegistry().register(IForgeContainerType.create(RPGInventoryContainer::new).setRegistryName("rpg_inventory"));
}

 

The screen is now successfully opening and looks great! I then started adding the slots and ran into a weird amount of issues. I have the feeling the issues are being caused by me creating/registering the screen/container wrong or opening it wrong. I'll try to describe the issues first and then add the code I've written for opening the gui. Any help would be much appreciated.

 

Quick description, my inventory has the default 9 slots for the hotbar, 27 slots for the inventory, 1 slot for the offhand, and then 14 custom equipment slots. None of these 14 custom slots match to a vanilla armor slot. For testing my inventory I would first use the Vanilla inventory screen to place one item in the first hotbar slot, and then 4 items in the main inventory (one in each corner). When opening my modded inventory this appears exactly as it should.

 

Bug 1: I immediately noticed that I could not move any of my items. When clicking on a slot containing an item, nothing would happen. I then realized that if you clicked the slot directly below a slot containing an item, it would pick up the item in the slot above it. Likewise, while holding an item, clicking an empty slot will place the item into the slot above the one clicked. This was persistent whether I had all slots (vanilla and my custom), just vanilla slots, or just a single slot.

Bug 2: All kinds of weird behavior on shift-click. Kinda hard to describe, so just noting it exists.

Bug 3: The game crashes when I click the majority of my custom equipment slots. The error is

java.lang.IndexOutOfBoundsException: Index: 46, Size: 46

I originally thought this was being caused because I was adding my equipment slots directly to the player inventory by running:

//This was actually done as a for loop, but I don't have that code any longer and this gets the point across.
this.player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(h -> {
	this.addSlot(new SlotItemHandler(h, slotType.getSlotIndex(), slotType.getX(), slotType.getY()));
});

I should've read the error better. I instead added an ItemStackHandler of size 14 to my main RPGCapability and added some small code to the storage class to handle reading and writing. I then updated my container's code to:

this.player.getCapability(RPGProvider.RPG_CAP).ifPresent(rpg -> {
  ItemStackHandler itemstackHandler = rpg.getItemStackHandler();
  
  // Places the many equipments slots
  // Index Range: 0 - 13
  for (int m=0; m<14; ++m) {
    final EEquipmentSlotType slotType = VALID_EQUIPMENT_SLOTS[m];
    this.addSlot(new SlotItemHandler(itemstackHandler, slotType.getSlotIndex(), slotType.getX(), slotType.getY()) {
      
      public int getSlotStackLimit() {
        return 1;
      }
      
      public boolean isItemValid(ItemStack stack) {
        if (!(stack.getItem() instanceof RPGItem)) return false;
        return ((RPGItem)stack.getItem()).canEquip(slotType);
      }
      
      public boolean canTakeStack(PlayerEntity playerIn) {
        ItemStack itemstack = this.getStack();
        return !itemstack.isEmpty() && !playerIn.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.canTakeStack(playerIn);
      }
      
      @Nullable
      @OnlyIn(Dist.CLIENT)
      public String getSlotTexture() {
        return RPGInventoryContainer.ARMOR_SLOT_TEXTURES[slotType.getIndex()];
      }
    });
  }
});

This in no way fixed the crashing and I decided that the slots were still being attached to my main player inventory. I spent an embarrassingly long time trying to fix this and then finally decided to look a little closer to the error log:

---- Minecraft Crash Report ----
// Ouch. That hurt :(

Time: 10/7/19 7:53 PM
Description: mouseClicked event handler

java.lang.IndexOutOfBoundsException: Index: 46, Size: 46
	at java.util.ArrayList.rangeCheck(Unknown Source) ~[?:1.8.0_181] {}
	at java.util.ArrayList.get(Unknown Source) ~[?:1.8.0_181] {}
	at net.minecraft.inventory.container.Container.slotClick(Container.java:258) ~[forge-1.14.4-28.0.11_mapped_snapshot_20190723-1.14.3-recomp.jar:?] {}

//Line 258 of Container.java
Slot slot6 = this.inventorySlots.get(slotId);

I've realized it is simply that the container's inventorySlots variable is what is actually being called as out of bounds exception. I've only just hit this point, so I haven't dug any deeper into this particular issue yet.

 

As my goal is to override the vanilla inventory screen, I am currently calling my screen by:

//This is within a client only event subscriber

@SubscribeEvent
public static void onGuiOpenEvent(GuiOpenEvent event) {
  	// I was having an issue with something being null and I got a little heavyhanded.....
	if (Minecraft.getInstance() != null && Minecraft.getInstance().player != null && event.getGui() != null) {
      	// This checks if the player is set to default vanilla behavior or if my mod should override
		if (Minecraft.getInstance().player.getCapability(RPGProvider.RPG_CAP).orElse(null).isRPGActive() && event.getGui() instanceof InventoryScreen) {
			ClientPlayerEntity player = Minecraft.getInstance().player;
          	// event.setGui(Screen gui)
          	// new RPGInventoryScreen(RPGInventoryContainer container, PlayerInventory inv, ITextComponent name)
          	// new RPGInventoryContainer(int windowId, PlayerInventory invIn)
			event.setGui(new RPGInventoryScreen(new RPGInventoryContainer(0, player.inventory), player.inventory, new TranslationTextComponent("demo.help.title")));
		}
	}
}

Once again, sorry for the long post and rambling. I appreciate any help provided. The equipment setup is the bottleneck for me to work on any other part of what I'm doing and I'm not really sure what is wrong here or what to dive deeper into.

Posted

I forgot to add the container code in case it is helpful:

 

RPGInventoryContainer.java

public class RPGInventoryContainer extends Container {
	
	private static final String [] ARMOR_SLOT_TEXTURES = new String[] {
			"item/empty_armor_slot_chestplate", "", "", "",
			"wogmods:item/empty_equip_slot_eyes", "item/empty_armor_slot_boots", "item/empty_equip_slot_glove", "item/empty_equip_slot_hat",
			"item/empty_armor_slot_helmet", "", "item/empty_equip_slot_ring",
			"item/empty_equip_slot_ring", "", "item/empty_equip_slot_bracelet"};
	
	
	private static final EEquipmentSlotType[] VALID_EQUIPMENT_SLOTS = new EEquipmentSlotType[] {
			EEquipmentSlotType.ARMOR, EEquipmentSlotType.BELT, EEquipmentSlotType.BODY, EEquipmentSlotType.CHEST,
			EEquipmentSlotType.EYES, EEquipmentSlotType.FEET, EEquipmentSlotType.HANDS, EEquipmentSlotType.HEAD,
			EEquipmentSlotType.HEADBAND, EEquipmentSlotType.NECK, EEquipmentSlotType.RING_LEFT,
			EEquipmentSlotType.RING_RIGHT, EEquipmentSlotType.SHOULDERS, EEquipmentSlotType.WRIST};
	
	private final PlayerEntity player;
	
	public RPGInventoryContainer(int windowId, PlayerInventory invIn) {
		this(windowId, invIn, null);
	}
	
	public RPGInventoryContainer(int windowId, PlayerInventory invIn, PacketBuffer extraData) {
		super(WogMods.RPG_INV_CONTAINER, windowId);
		player = invIn.player;
		
		// Places the slots for the main player inventory
		// Index Range: 9 - 35
		for (int j=0; j<3; ++j) {
			for (int k=0; k<9; ++k) {
				this.addSlot(new Slot(invIn, k+(j+1)*9, 30+k*18, 89+j*18));
			}
		}
		
		// Places the slots for the main player hotbar
		// Index Range: 0 - 8
		for (int l=0; l<9; ++l) {
			this.addSlot(new Slot(invIn, l, 30+l*18, 147));
		}
		
		// Places the offhand slot
		// Index Range: 40
		this.addSlot(new Slot(invIn, 40, 103, 69) {
			@Nullable
			@OnlyIn(Dist.CLIENT)
			public String getSlotTexture() {
				return "item/empty_armor_slot_shield";
			}
		});
		
		this.player.getCapability(RPGProvider.RPG_CAP).ifPresent(rpg -> {
			
			ItemStackHandler itemstackHandler = rpg.getItemStackHandler();
			
			// Places the many equipments slots
			// Index Range: 41 - 54
			for (int m=0; m<14; ++m) {
				final EEquipmentSlotType slotType = VALID_EQUIPMENT_SLOTS[m];
				this.addSlot(new SlotItemHandler(itemstackHandler, slotType.getSlotIndex(), slotType.getX(), slotType.getY()) {
								
					public int getSlotStackLimit() {
						return 1;
					}
								
					public boolean isItemValid(ItemStack stack) {
						if (!(stack.getItem() instanceof RPGItem)) return false;
						return ((RPGItem)stack.getItem()).canEquip(slotType);
					}
					
					public boolean canTakeStack(PlayerEntity playerIn) {
						ItemStack itemstack = this.getStack();
						return !itemstack.isEmpty() && !playerIn.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.canTakeStack(playerIn);
					}
					
					@Nullable
					@OnlyIn(Dist.CLIENT)
					public String getSlotTexture() {
						return RPGInventoryContainer.ARMOR_SLOT_TEXTURES[slotType.getIndex()];
					}
					
				});
			}
		});
	}
	
	@Override
	public boolean canInteractWith(PlayerEntity playerIn) {
		return true;
	}

}

 

Posted
38 minutes ago, wog890 said:

I forgot to add the container code in case it is helpful:

You are only ever changing the gui. This is a problem you must also change the container on the server.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted
19 minutes ago, Animefan8888 said:

You are only ever changing the gui. This is a problem you must also change the container on the server.

Ahhh, that does make sense. Would you know the correct way to do this.

 

I added player.openContainer(new RPGProvider()); just after event.setGui(); call.

 

With the RPGProvider class being:

public class RPGInventoryProvider implements INamedContainerProvider {

	@Override
	public Container createMenu(int windowId, PlayerInventory invIn, PlayerEntity player) {
		return new RPGInventoryContainer(windowId, invIn);
	}

	@Override
	public ITextComponent getDisplayName() {
		return new TranslationTextComponent("containers.wogmods.rpgInv");
	}
	
}

I'm guessing I might be messing up the sides and need to call player.openContainer() from the server side? Do I need to have my clientEventSubscriber send a packet to the server that then calls this instead?

Posted
8 minutes ago, wog890 said:

Do I need to have my clientEventSubscriber send a packet to the server that then calls this instead?

If I remember correctly there is a ContainerOpenEvent but part of me says that you are not allowed to change the container from inside it. Though you can check it might even say it in the javadoc.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted
13 minutes ago, Animefan8888 said:

If I remember correctly there is a ContainerOpenEvent but part of me says that you are not allowed to change the container from inside it. Though you can check it might even say it in the javadoc.

There is an event:

PlayerContainerEvent.Open

I'll mess with this some, but I did find a post from 2016 when googling OpenContainerEvent 

It seems like he is essentially doing the same thing! Diesieben07 says that using that event won't work as expected? The only other suggestion in the post is to send a packet and use IGuiHandler to open a custom container and gui? I'll tinker with both and see what happens :-).

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.