Jump to content

Recommended Posts

Posted (edited)

Hello! I don't know how to access my mob's container with my gui. I want my button on this gui to fire a series of functions if there are enough emeralds in the gui slot. I do not know to access the slots to check this.

 

Container:

public class ContainerIvVillagerHireNitwit extends Container{

	private IvVillager villager;
	private IItemHandler handler = villager.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
	
	public ContainerIvVillagerHireNitwit(IvVillager villager, IInventory playerInv){
		IItemHandler handler = villager.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);

		this.addSlotToContainer(new SlotItemHandler(handler, 3, 76, 47));
		int xPos = 8;
		int yPos = 84;
		
		for (int y = 0; y < 3; ++y) {
			for (int x = 0; x < 9; ++x) {
				this.addSlotToContainer(new Slot(playerInv, x + y * 9 + 9, xPos + x * 18, yPos + y * 18));
			}
		}
				
		for (int x = 0; x < 9; ++x) {
			this.addSlotToContainer(new Slot(playerInv, x, xPos + x * 18, yPos + 58));
		}
	}
	@Override
	public boolean canInteractWith(EntityPlayer player) {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public ItemStack transferStackInSlot(EntityPlayer playerIn, int fromSlot) {
	    ItemStack previous = ItemStack.EMPTY;
	    Slot slot = (Slot) this.inventorySlots.get(fromSlot);

	    if (slot != null && slot.getHasStack()) {
	        ItemStack current = slot.getStack();
	        previous = current.copy();

	        if (fromSlot < this.handler.getSlots()) {
	            // From the block breaker inventory to player's inventory
	            if (!this.mergeItemStack(current, handler.getSlots(), handler.getSlots() + 36, true))
	                return ItemStack.EMPTY;
	        } else {
	            // From the player's inventory to block breaker's inventory
	            if (!this.mergeItemStack(current, 0, handler.getSlots(), false))
	                return ItemStack.EMPTY;
	        }

	        if (current.getCount() == 0) //Use func_190916_E() instead of stackSize 1.11 only 1.11.2 use getCount()
	            slot.putStack(ItemStack.EMPTY); //Use ItemStack.field_190927_a instead of (ItemStack)null for a blank item stack. In 1.11.2 use ItemStack.EMPTY
	        else
	            slot.onSlotChanged();

	        if (current.getCount() == previous.getCount())
	            return null;
	        slot.onTake(playerIn, current);
	    }
	    return previous;
	}
}

 

GUI

public class GuiIvVillagerHireNitwit extends GuiContainer{

	private IvVillager villager;
	private IInventory playerInv;
	
	public GuiIvVillagerHireNitwit(IvVillager villager, IInventory playerInv) {
		super(new ContainerIvVillagerHireNitwit(villager, playerInv));
		
		this.xSize = 176;
		this.ySize = 166;
		
		this.villager = villager;
		this.playerInv = playerInv;
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
		this.mc.getTextureManager().bindTexture(new ResourceLocation(Reference.MOD_ID, "gui/hire_nitwit.png"));
		this.drawTexturedModalRect(this.getGuiLeft(), this.getGuiTop(), 0, 0, this.xSize, this.ySize);
	}
	
	@Override
	protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY)
    {
        String s = this.villager.getName();
        Boolean has_emeralds;
        this.mc.fontRenderer.drawString(s, this.xSize / 2 - this.mc.fontRenderer.getStringWidth(s) / 2, 6, 4210752);
        this.mc.fontRenderer.drawString(this.playerInv.getDisplayName().getFormattedText(), 8, 72, 4210752);
        if (){ //this is where the container needs to be accessed
        	has_emeralds = true;
        }
        else 
        {
        	has_emeralds = false;
        }
        this.addButton(new Button_Hire(0, 115, 20, 40, 25, "Hire", this.villager, has_emeralds));
    }

}

As well, does addSlotToContainer id need to be different across containers? 

Edited by OrangeVillager61
Posted

The GuiContainer#inventorySlots field stores the Container that you pass to the GuiContainer constructor. You could have found this out for yourself by looking at the GuiContainer class in your IDE.

 

Slot IDs only have to be unique within the Container instance. Two different Container instances can each have their own Slot instance with ID 1.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
20 minutes ago, Choonster said:

The GuiContainer#inventorySlots field stores the Container that you pass to the GuiContainer constructor. You could have found this out for yourself by looking at the GuiContainer class in your IDE.

 

Slot IDs only have to be unique within the Container instance. Two different Container instances can each have their own Slot instance with ID 1.

Okay, thanks, however, when I compare the slot.getStack and the new ItemStack I want with a standard >=, it is undefined and I cannot seem to find an alternative.

Posted
7 minutes ago, OrangeVillager61 said:

Okay, thanks, however, when I compare the slot.getStack and the new ItemStack I want with a standard >=, it is undefined and I cannot seem to find an alternative.

>= compares numbers, you can't compare ItemStacks with it.

  • Like 1
Posted
2 minutes ago, OrangeVillager61 said:

Okay, thanks, however, when I compare the slot.getStack and the new ItemStack I want with a standard >=, it is undefined and I cannot seem to find an alternative.

 

Java doesn't allow operator overloading, so you can't use relational operators like >= to compare non-numeric values.

 

Use the static equality methods in the ItemStack class to check if two ItemStacks are equal or use the getter methods to get each part of the ItemStack (e.g. ItemStack#getItem to get the ItemItemStack#getCount to get the count/stack size).

 

 

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Alright, when I try to test the GUI I get errors that arrays cannot be cast to IvVillager on the line below.

return new ContainerIvVillagerHauler((IvVillager) world.getEntitiesWithinAABB(IvVillager.class, vilSearch), player.inventory);

Since I require an IvVillager as an argument, how can I get the IvVillager and select it?

Posted (edited)
23 minutes ago, OrangeVillager61 said:

Alright, when I try to test the GUI I get errors that arrays cannot be cast to IvVillager on the line below.


return new ContainerIvVillagerHauler((IvVillager) world.getEntitiesWithinAABB(IvVillager.class, vilSearch), player.inventory);

Since I require an IvVillager as an argument, how can I get the IvVillager and select it?

 

World#getEntitiesWithinAABB returns a List<T>, where T is the class you pass as the first argument or any super class up to Entity. You can't cast a List<T> to T, you need to get an individual element from the list.

 

This is basic Java knowledge.

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted (edited)

Ah, okay, however, whenever I call the UI, it can't find anything so the list is empty. I think it is my AxisAlignedBB since I'm not sure if I specified the right area.

 

Where the error occurs:

@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
		AxisAlignedBB vilSearch = new AxisAlignedBB(x, y - 1.0D, z, x, y + 2.0D, z);
		if (ID == Villager_Hire){
			return new ContainerIvVillagerHireNitwit((IvVillager)world.getEntitiesWithinAABB(IvVillager.class, vilSearch).get(0), player.inventory);
		}
}

Where it is called:

@Override
public boolean processInteract(EntityPlayer player, EnumHand hand){
        if (this.getHired() == false && this.getProfession() == 5 && !world.isRemote && !this.isChild())
        {
    		BlockPos blockpos = new BlockPos(this);
        	player.openGui(Iv.instance, GuiHandler.Villager_Hire, world, blockpos.getX(), blockpos.getY(), blockpos.getZ());
        	return true;
        }
}

 

Edit:

As well, when I try to register the data parameter that holds the player id, I get this:

[13:45:40] [Server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.EntityJoinWorldEvent@44fae74c:
java.lang.IllegalArgumentException: Duplicate id value for 15!
	at net.minecraft.network.datasync.EntityDataManager.register(EntityDataManager.java:105) ~[EntityDataManager.class:?]
	at orangeVillager61.ImprovedVillagers.Entities.IvVillager.entityInit(IvVillager.java:210) ~[IvVillager.class:?]

 

Erroring code:

        this.getDataManager().register(OWNER_DEFINED_ID, Optional.<UUID>absent());

 

Edited by OrangeVillager61
Posted (edited)
6 hours ago, OrangeVillager61 said:

Ah, okay, however, whenever I call the UI, it can't find anything so the list is empty. I think it is my AxisAlignedBB since I'm not sure if I specified the right area.

 

Where the error occurs:


@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
		AxisAlignedBB vilSearch = new AxisAlignedBB(x, y - 1.0D, z, x, y + 2.0D, z);
		if (ID == Villager_Hire){
			return new ContainerIvVillagerHireNitwit((IvVillager)world.getEntitiesWithinAABB(IvVillager.class, vilSearch).get(0), player.inventory);
		}
}

Where it is called:


@Override
public boolean processInteract(EntityPlayer player, EnumHand hand){
        if (this.getHired() == false && this.getProfession() == 5 && !world.isRemote && !this.isChild())
        {
    		BlockPos blockpos = new BlockPos(this);
        	player.openGui(Iv.instance, GuiHandler.Villager_Hire, world, blockpos.getX(), blockpos.getY(), blockpos.getZ());
        	return true;
        }
}

 

 

You're creating an AABB with the x and z coordinates of the BlockPos as both the minimum and maximum coordinates, so the entity will only be found if its bounding box overlaps the northwest corner of the block it's standing on (the area represented by the AABB).

 

You should instead create the AABB with the x and z coordinates of the BlockPos as the minimum coordinates and the x and z coordinates plus 1 as the maximum coordinates. This way the AABB will cover the entire block instead of just the northwest corner. The  AxisAlignedBB(BlockPos) constructor does this for you, you can then use AxisAlignedBB#expand to expand it by 1 in each direction of the y axis (i.e. up and down).

 

I created this item to experiment with corner- and edge-based AABBs passed to World#getEntitiesWithinAABB.

 

Side note: You don't need to cast the result of the List#get call to IvVillager; the result of World#getEntitiesWithinAABB is a List<IvVillager> (because of the first argument), so the List#get call returns an IvVillager.

 

 

Quote

Edit:

As well, when I try to register the data parameter that holds the player id, I get this:


[13:45:40] [Server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.EntityJoinWorldEvent@44fae74c:
java.lang.IllegalArgumentException: Duplicate id value for 15!
	at net.minecraft.network.datasync.EntityDataManager.register(EntityDataManager.java:105) ~[EntityDataManager.class:?]
	at orangeVillager61.ImprovedVillagers.Entities.IvVillager.entityInit(IvVillager.java:210) ~[IvVillager.class:?]

 

Erroring code:


        this.getDataManager().register(OWNER_DEFINED_ID, Optional.<UUID>absent());

 

 

Are you registering the data parameter more than once? Post the IvVillager class using Gist or Pastebin.

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

I still get nullpointerexceptions on the return hire container (I can't use Blockpos since the game requires it to be x, y and z).

 

	@Override
	public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
		AxisAlignedBB vilSearch = new AxisAlignedBB(x, y - 1, z, x + 1, y + 2, z + 1);
		if (ID == Villager_Hire){
			return new ContainerIvVillagerHireNitwit((IvVillager)world.getEntitiesWithinAABB(IvVillager.class, vilSearch).get(0), player.inventory); //Erroring code
		}
		else if (ID == Hauler){
			return new ContainerIvVillagerHauler((IvVillager)world.getEntitiesWithinAABB(IvVillager.class, vilSearch).get(0), player.inventory);
		}
		else {
			return null;
		}
	}

 

Posted (edited)
7 hours ago, OrangeVillager61 said:

 

Data parameter IDs are automatically assigned per class hierarchy. You're passing EntityTameable.class as the first argument of EntityDataManager.createKey, so the DataParameter is being assigned the next ID for the EntityTameable class instead of the next ID for the IvVillager class. When you try to register it for the IvVillager instance, there's already a DataParamter registered for that ID so the game crashes with an IllegalArgumentException.

 

In future, please select the appropriate syntax highlighting when posting code on a site like Gist or Pastebin. To get syntax highlighting on Gist, give each file the appropriate extension (.java for Java code). To get syntax highlighting on Pastebin, select the language from the dropdown at the bottom of the page.

 

The Adult_Age data parameter is Stringly Typed. I suggest replacing the strings with an enum.

 

 

7 hours ago, OrangeVillager61 said:

I still get nullpointerexceptions on the return hire container

 

I can't see any obvious reason for this. If you haven't already, please create a Git repository for your mod, push it to a site like GitHub and link it here so I can debug it locally.

 

See my mod and its .gitignore file for an example of the repository structure to use and the files to include.

 

 

Quote

(I can't use Blockpos since the game requires it to be x, y and z).

 

 

You can still create a BlockPos from the individual coordinates.

Edited by Choonster

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
27 minutes ago, Choonster said:

 

Data parameter IDs are automatically assigned per class hierarchy. You're passing EntityTameable.class as the first argument of EntityDataManager.createKey, so the DataParameter is being assigned the next ID for the EntityTameable class instead of the next ID for the IvVillager class. When you try to register it for the IvVillager instance, there's already a DataParamter registered for that ID so the game crashes with an IllegalArgumentException.

 

Huh, interesting, I wonder when I am registering for EntityTameable? I went through entityInit() dataParameters and there is no EntityTameable between IvVillager and EntityLiving and most of all, I renamed the variable from OWNER_UNIQUE_ID to OWNER_DEFINED_ID to prevent this specific issue.

 

27 minutes ago, Choonster said:

In future, please select the appropriate syntax highlighting when posting code on a site like Gist or Pastebin. To get syntax highlighting on Gist, give each file the appropriate extension (.java for Java code). To get syntax highlighting on Pastebin, select the language from the dropdown at the bottom of the page.

 

The Adult_Age data parameter is Stringly Typed. I suggest replacing the strings with an enum.

 

Thanks for the advice.

27 minutes ago, Choonster said:

 

I can't see any obvious reason for this. If you haven't already, please create a Git repository for your mod, push it to a site like GitHub and link it here so I can debug it locally.

 

See my mod and its .gitignore file for an example of the repository structure to use and the files to include.

 

 

 

I'll make a comit containing the current code on my mod's github, https://github.com/Orange1861/Improved-Villagers/pull/4

 

Posted (edited)
On 2017-5-29 at 1:33 AM, OrangeVillager61 said:

Huh, interesting, I wonder when I am registering for EntityTameable? I went through entityInit() dataParameters and there is no EntityTameable between IvVillager and EntityLiving and most of all, I renamed the variable from OWNER_UNIQUE_ID to OWNER_DEFINED_ID to prevent this specific issue.

 

As I said:

On 2017-5-28 at 11:40 PM, Choonster said:

 You're passing EntityTameable.class as the first argument of EntityDataManager.createKey,

 

This is in the OWNER_DEFINED_ID field initialiser.

 

The name of the field doesn't matter, you could call it FOO_BAR_BAZ and the issue would still be present.

 

 

On 2017-5-29 at 1:33 AM, OrangeVillager61 said:

I'll make a comit containing the current code on my mod's github, https://github.com/Orange1861/Improved-Villagers/pull/4

 

Please include your buildscript (build.gradle and gradle.properties) in the repository.

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted (edited)

I'll start debugging it now.

 

I also recommend using a proper Git client (either the CLI or a GUI client like GitKraken or your IDE) rather than using GitHub's upload system.

 

Edit: You should also include the Gradle wrapper (gradlew, gradlew.bat and the gradle directory) in your repository, though this isn't as essential as the buildscript.

 

Edit 2: You should also include a .gitignore file to ensure only the required files are included in the repository. I linked an example in this post.

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted (edited)

I figured out the issue: In your Container constructors you were trying to use the villager field, but you never assigned it a value so it was always null. You were also assigning the IItemHandler to a handler local variable instead of the handler field. I fixed these issues in this commit.

 

I also fixed several other issues and changed GuiHandler to use the entity ID instead of the entity's coordinates (which avoids the potential of clicking one villager and opening a GUI for another standing in the same space). You can view and/or merge my changes here.

Edited by Choonster
Fixed link to changes
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
6 hours ago, Choonster said:

I figured out the issue: In your Container constructors you were trying to use the villager field, but you never assigned it a value so it was always null. You were also assigning the IItemHandler to a handler local variable instead of the handler field. I fixed these issues in this commit.

 

I also fixed several other issues and changed GuiHandler to use the entity ID instead of the entity's coordinates (which avoids the potential of clicking one villager and opening a GUI for another standing in the same space). You can view and/or merge my changes here.

Thank you so much! This fixed the bulk of my issues. However, my skill with GUI (especially with MC/Java) is limited and I have a major issue where when I put an item into the hire slot and the item disappears, I suspect that this may happen with the other GUI for the hired Villager which is supposed to act as a moving chest.

Posted
17 minutes ago, OrangeVillager61 said:

Thank you so much! This fixed the bulk of my issues. However, my skill with GUI (especially with MC/Java) is limited and I have a major issue where when I put an item into the hire slot and the item disappears, I suspect that this may happen with the other GUI for the hired Villager which is supposed to act as a moving chest.

 

I also noticed that, I believe it's due to the IItemHandler returned by EntityLivingBase#getCapability being a wrapper of EntityLivingBase#handInventory and EntityLivingBase#armorArray.

 

EntityLivingBase#onUpdate replaces the contents of these each tick with the ItemStacks returned by EntityLivingBase#getItemStackFromSlot (which EntityLiving implements using its own lists: EntityLiving#inventoryHands and EntityLiving#inventoryArmor), so any changes made through the IItemHandler are overwritten the next tick.

 

I'm going to see if I can reproduce this and create a Forge issue/PR for it.

 

In the meantime, you should create your own IItemHandler field in IvVillager and expose it via hasCapability/getCapability. Currently you're using slot 0 of the combined armour/hands inventories, which is the feet slot.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

So something like this? Or do I need to create a capability for the items? I did have a fully functioning capability system but now since it is unused parts of it have been removed (mainly proxy references)

    public IItemHandler item_handler;

 

	public IItemHandler getCapability()
	{
		return this.item_handler;
	}

 

Posted

You need to initialise the IItemHandler field with an instance of an IItemHandler implementation. The default implementation of IItemHandler is ItemStackHandler, which will probably suit your needs. The field should also be private.

 

Override EntityLivingBase#hasCapability (which implements ICapabilityProvider#hasCapability) to return true if the Capability argument is CapabilityItemHandler.ITEM_HANDLER_CAPABILITY or return the result of the super method if it's not

 

Override EntityLivingBase#getCapability (which implements ICapabilityProvider#getCapability) to return the IItemHandler instance if the Capability argument is CapabilityItemHandler.ITEM_HANDLER_CAPABILITY or return the result of the super method if it's not. Due to limitations of Java's generics, you'll need to call Capability#cast on CapabilityItemHandler.ITEM_HANDLER_CAPABILITY with the IItemHandler as the argument to cast it to the return type.

 

Calling the super method allows capabilities to be provided by super classes or attached with AttachCapabilityEvent.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
18 minutes ago, Choonster said:

Due to limitations of Java's generics, you'll need to call Capability#cast on CapabilityItemHandler.ITEM_HANDLER_CAPABILITY with the IItemHandler as the argument to cast it to the return type.

Okay, I understand the rest of the text, but I don't really understand this. Is the below what you meant?

	@Override
	public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, @Nullable net.minecraft.util.EnumFacing facing)	{
		if (capability.equals(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY))
		{
			return true;
		}
		else
		{
			return super.hasCapability(capability, facing);
		}
	}

	@SuppressWarnings("unchecked")
    @Override
    @Nullable
    public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable net.minecraft.util.EnumFacing facing)
    {
		if (capability.equals((Capability)CapabilityItemHandler.ITEM_HANDLER_CAPABILITY))
		{
			return (T) this.item_handler;
		}
		else
		{
			return super.getCapability(capability, facing);
		}
    }

 

Posted
1 minute ago, OrangeVillager61 said:

Okay, I understand the rest of the text, but I don't really understand this. Is the below what you meant?

 

Almost, but not quite. Instead of casting this.item_handler directly to T and suppressing the unchecked warnings resulting from it, call CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.cast with this.item_handler as the argument.

 

Capability objects are singletons, compare them with the equality operator (==) rather than the Object#equals method.

 

CapabilityItemHandler.ITEM_HANDLER_CAPABILITY is a field of type Capability (more specifically Capability<IItemHandler>), there's no reason to cast it to Capability. In addition to that, there's no Capability#equals method with a Capability parameter; only Object#equals with an Object parameter. Only cast when it's required by the compiler.

 

Import classes like Capability and EnumFacing instead of using fully-qualified names. Forge only uses fully-qualified names in vanilla patches to reduce the patch size (by removing the need for an import statement).

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
5 minutes ago, Choonster said:

 

Almost, but not quite. Instead of casting this.item_handler directly to T and suppressing the unchecked warnings resulting from it, call CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.cast with this.item_handler as the argument.

 

Capability objects are singletons, compare them with the equality operator (==) rather than the Object#equals method.

 

CapabilityItemHandler.ITEM_HANDLER_CAPABILITY is a field of type Capability (more specifically Capability<IItemHandler>), there's no reason to cast it to Capability. In addition to that, there's no Capability#equals method with a Capability parameter; only Object#equals with an Object parameter. Only cast when it's required by the compiler.

 

Import classes like Capability and EnumFacing instead of using fully-qualified names. Forge only uses fully-qualified names in vanilla patches to reduce the patch size (by removing the need for an import statement).

Oh, okay. Do I still cast this.item_handler to T since I get errors when I don't?

Posted
1 minute ago, OrangeVillager61 said:

Oh, okay. Do I still cast this.item_handler to T since I get errors when I don't?

 

1 minute ago, OrangeVillager61 said:

Instead of casting this.item_handler directly to T and suppressing the unchecked warnings resulting from it, call CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.cast with this.item_handler as the argument.

 

Capability#cast casts it for you without the unchecked warning.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

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

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • 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?
  • Topics

×
×
  • Create New...

Important Information

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