OuiOuiCroissant Posted June 15, 2020 Posted June 15, 2020 (edited) I am trying to create an entity with a gui that displays slots for their armor, mainhand, offhand, and a 10 slot inventory. Currently my gui will open when the entity is right-clicked by the player, but it is unfortunately NOT interactable. Entity Super Class - SiegeEntity: Spoiler package com.siegemod.entities; import com.siegemod.container.PeasantContainer; import net.minecraft.entity.AgeableEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ILivingEntityData; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.goal.HurtByTargetGoal; import net.minecraft.entity.ai.goal.LookAtGoal; import net.minecraft.entity.ai.goal.LookRandomlyGoal; import net.minecraft.entity.ai.goal.MoveThroughVillageGoal; import net.minecraft.entity.ai.goal.MoveTowardsVillageGoal; import net.minecraft.entity.ai.goal.SwimGoal; import net.minecraft.entity.ai.goal.WaterAvoidingRandomWalkingGoal; import net.minecraft.entity.merchant.villager.AbstractVillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.ItemStack; import net.minecraft.item.MerchantOffer; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; public abstract class SiegeEntity extends AbstractVillagerEntity { protected final double WALK_SPEED = 0.4D; // Convert to ItemHandler //private final Inventory PEASANT_INVENTORY = new Inventory(16); // 0 = wander, 1 = follow, 2 = locked follow, 3 = guard, 4 = locked guard. protected double stance = 0; protected PlayerEntity king; protected PlayerEntity[] allies; protected BlockPos bedPosition; protected BlockPos destination; protected ItemStack targetItem; public SiegeEntity(EntityType<? extends AbstractVillagerEntity> entityType, World worldIn, PlayerEntity kingIn) { super(entityType, worldIn); // this.king = kingIn; } @Override protected void registerGoals() { this.goalSelector.addGoal(0, new SwimGoal(this)); // We're going to need a custom avoid entity goal // We're going to need a custom choose target goal this.goalSelector.addGoal(3, new MoveTowardsVillageGoal(this, WALK_SPEED)); this.goalSelector.addGoal(3, new MoveThroughVillageGoal(this, WALK_SPEED, false, 4, () -> { return true; })); this.goalSelector.addGoal(6, new WaterAvoidingRandomWalkingGoal(this, WALK_SPEED)); this.goalSelector.addGoal(7, new LookAtGoal(this, PlayerEntity.class, 6.0F)); this.goalSelector.addGoal(8, new LookRandomlyGoal(this)); this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); } @Override protected void registerAttributes() { super.registerAttributes(); this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(20.0D); this.getAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(64.0D); this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.6F); this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(1.0D); this.getAttribute(SharedMonsterAttributes.ARMOR).setBaseValue(0.0D); this.getAttribute(SharedMonsterAttributes.ARMOR_TOUGHNESS).setBaseValue(0.0D); this.getAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE).setBaseValue(10.0F); } @Override public boolean attackEntityAsMob(Entity entityIn) { this.swingArm(getActiveHand()); return super.attackEntityAsMob(entityIn); } public void livingTick() { this.updateArmSwingProgress(); super.livingTick(); } @Override public ILivingEntityData onInitialSpawn(IWorld worldIn, DifficultyInstance difficultyIn, SpawnReason reason, ILivingEntityData spawnDataIn, CompoundNBT dataTag) { // Temporary method of choosing king. Implement join kingdom feature in future. if (reason == SpawnReason.COMMAND) { this.king = world.getClosestPlayer(this.getPosX(), this.getPosY(), this.getPosZ(), 64, false); } return super.onInitialSpawn(worldIn, difficultyIn, reason, spawnDataIn, dataTag); } @Override public void writeAdditional(CompoundNBT compound) { super.writeAdditional(compound); } @Override public void readAdditional(CompoundNBT compound) { super.readAdditional(compound); } public PlayerEntity getKing() { return king; } @Override public AgeableEntity createChild(AgeableEntity ageable) { return null; } @Override protected void onVillagerTrade(MerchantOffer offer) { } @Override protected void populateTradeData() { } @Override public final boolean processInteract(PlayerEntity player, Hand hand) { if (hand == Hand.MAIN_HAND) { if (getKing() == null) { // Open join kingdom request screen. // OpenGui only accepts ServerPlayerEntity // Also works to ensure we are serverside System.out.println("king is null"); } else if (player == getKing() && player instanceof ServerPlayerEntity) { System.out.println("king is " + player.toString()); NetworkHooks.openGui((ServerPlayerEntity) player, new SiegeContainerProvider(this)); return true; } } return super.processInteract(player, hand); } public static class SiegeContainerProvider implements INamedContainerProvider { private final SiegeEntity PEASANT; public SiegeContainerProvider(SiegeEntity peasantIn) { this.PEASANT = peasantIn; } @Override public Container createMenu(int containerId, PlayerInventory playerInv, PlayerEntity player) { return new PeasantContainer(player, PEASANT); } @Override public ITextComponent getDisplayName() { return PEASANT.getDisplayName(); } } } Entity Class - PeasantEntity: Spoiler package com.siegemod.entities.friendly; import com.siegemod.entities.SiegeEntity; import com.siegemod.entities.enemy.EnemyEntity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ai.goal.AvoidEntityGoal; import net.minecraft.entity.merchant.villager.AbstractVillagerEntity; import net.minecraft.entity.monster.ZombieEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.World; public class PeasantEntity extends SiegeEntity { public PeasantEntity(EntityType<? extends AbstractVillagerEntity> entityType, World worldIn) { this(entityType, worldIn, null); } public PeasantEntity(EntityType<? extends AbstractVillagerEntity> entityType, World worldIn, PlayerEntity kingIn) { super(entityType, worldIn, kingIn); } @Override protected void registerGoals() { this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, EnemyEntity.class, 16.0F, WALK_SPEED, WALK_SPEED * 1.2D)); this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, ZombieEntity.class, 16.0F, WALK_SPEED, WALK_SPEED * 1.2D)); } } Container Class: Spoiler package com.siegemod.container; import com.siegemod.entities.SiegeEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; public class PeasantContainer extends Container { public SiegeEntity peasant; public Inventory peasantInventory; public PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) { super(SiegeContainerTypes.PEASANT_CONTAINER.get(), containerId); } public PeasantContainer(PlayerEntity player, SiegeEntity peasantIn) { super(SiegeContainerTypes.PEASANT_CONTAINER.get(), 0); // replace with ItemHandler in future this.peasantInventory = new Inventory(16); //peasantInventory.getPeasantInventory.getInv(); assertInventorySize(peasantInventory, 16); this.peasant = peasantIn; /* // Armor Inventory int armorInvYPos = 8; int sizePerSlot = 18; for (int column = 0; column < 4; ++column) { this.addSlot(new Slot(this.peasantInventory, 4, 8, armorInvYPos + (column * sizePerSlot))); } // MainHand this.addSlot(new Slot(this.peasantInventory, 4, 32, 62)); // OffHand this.addSlot(new Slot(this.peasantInventory, 5, 32, 62)); // Main Inventory int invXPos = 80; int invYPos = 8; int slotsInEachRow = 5; for (int row = 0; row < 5; ++row) { for (int column = 0; column < 4; ++column) { this.addSlot(new Slot(this.peasantInventory, row + column * slotsInEachRow + 6, invXPos + (column * sizePerSlot), invYPos + (row * sizePerSlot))); } }*/ // Temporary layout //ARMOR this.addSlot(new Slot(this.peasantInventory, 0, 8, 8)); this.addSlot(new Slot(this.peasantInventory, 1, 8, 26)); this.addSlot(new Slot(this.peasantInventory, 2, 8, 44)); this.addSlot(new Slot(this.peasantInventory, 3, 8, 62)); //MAINHAND & OFFHAND this.addSlot(new Slot(this.peasantInventory, 4, 32, 62)); this.addSlot(new Slot(this.peasantInventory, 5, 56, 62)); //MAIN this.addSlot(new Slot(this.peasantInventory, 6, 80, 8)); this.addSlot(new Slot(this.peasantInventory, 7, 98, 8)); this.addSlot(new Slot(this.peasantInventory, 8, 116, 8)); this.addSlot(new Slot(this.peasantInventory, 9, 134, 8)); this.addSlot(new Slot(this.peasantInventory, 10, 152, 8)); this.addSlot(new Slot(this.peasantInventory, 11, 80, 26)); this.addSlot(new Slot(this.peasantInventory, 12, 98, 26)); this.addSlot(new Slot(this.peasantInventory, 13, 116, 26)); this.addSlot(new Slot(this.peasantInventory, 14, 134, 26)); this.addSlot(new Slot(this.peasantInventory, 15, 152, 26)); // Player Main Inventory for (int row = 0; row < 3; ++row) { for (int column = 0; column < 9; ++column) { this.addSlot(new Slot(player.inventory, column + row * 9 + 9, 8 + column * 18, 102 + row * 18 + -18)); } } // Player Hotbar for (int row = 0; row < 9; ++row) { this.addSlot(new Slot(player.inventory, row, 8 + row * 18, 142)); } } @Override public boolean canInteractWith(PlayerEntity playerIn) { return true;// peasant != null && playerIn.getDistance(peasant) <= PlayerEntity.REACH_DISTANCE.getDefaultValue() + 1; } public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) { ItemStack itemstack = ItemStack.EMPTY; Slot slot = this.inventorySlots.get(index); if (slot != null && slot.getHasStack()) { ItemStack itemstack1 = slot.getStack(); itemstack = itemstack1.copy(); if (index < this.peasantInventory.getSizeInventory()) { if (!this.mergeItemStack(itemstack1, this.peasantInventory.getSizeInventory(), this.inventorySlots.size(), true)) { return ItemStack.EMPTY; } } else if (!this.mergeItemStack(itemstack1, 0, this.peasantInventory.getSizeInventory(), false)) { return ItemStack.EMPTY; } if (itemstack1.isEmpty()) { slot.putStack(ItemStack.EMPTY); } else { slot.onSlotChanged(); } } return itemstack; } public SiegeEntity getPeasant() { return this.peasant; } } Container Screen Class: Spoiler package com.siegemod.container; import com.mojang.blaze3d.systems.RenderSystem; import com.siegemod.SiegeMod; import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class PeasantScreen extends ContainerScreen<PeasantContainer> { private static final ResourceLocation PEASANT_GUI = new ResourceLocation(SiegeMod.MODID, "gui/peasant_gui.png"); public PeasantScreen(PeasantContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) { super(screenContainer, inv, titleIn); this.guiLeft = 0; this.guiTop = 0; this.xSize = 176; this.ySize = 166; } @Override protected void init() { super.init(); } @Override protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); this.getMinecraft().getTextureManager().bindTexture(PEASANT_GUI); int x = (this.width - this.xSize) / 2; int y = (this.height - this.ySize) / 2; this.blit(x, y, 0, 0, this.xSize, this.ySize); } @Override public void render(int mouseX, int mouseY, float partialTicks) { this.renderBackground(); super.render(mouseX, mouseY, partialTicks); this.renderHoveredToolTip(mouseX, mouseY); } } ContainerTypes Class: Spoiler package com.siegemod.container; import com.siegemod.SiegeMod; import net.minecraft.inventory.container.ContainerType; import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.fml.RegistryObject; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; public class SiegeContainerTypes { public static final DeferredRegister<ContainerType<?>> CONTAINER_TYPES = new DeferredRegister<>(ForgeRegistries.CONTAINERS, SiegeMod.MODID); public static final RegistryObject<ContainerType<PeasantContainer>> PEASANT_CONTAINER = CONTAINER_TYPES.register(SiegeMod.MODID, () -> { return IForgeContainerType.create(PeasantContainer::new); }); } ClientEventBusSubscriber: Spoiler package com.siegemod.util; import com.siegemod.SiegeMod; import com.siegemod.container.PeasantScreen; import com.siegemod.container.SiegeContainerTypes; import com.siegemod.entities.SiegeEntityTypes; import com.siegemod.entities.render.PeasantRenderer; import net.minecraft.client.gui.ScreenManager; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @Mod.EventBusSubscriber(modid = SiegeMod.MODID, bus = Bus.MOD, value = Dist.CLIENT) public class ClientEventBusSubscriber { @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { ScreenManager.registerFactory(SiegeContainerTypes.PEASANT_CONTAINER.get(), PeasantScreen::new); RenderingRegistry.registerEntityRenderingHandler(SiegeEntityTypes.PEASANT.get(), PeasantRenderer::new); } } Main Class: Spoiler package com.siegemod; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.siegemod.container.SiegeContainerTypes; import com.siegemod.entities.SiegeEntityTypes; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @Mod("siegemod") public class SiegeMod { // this is what you'll use to output anything to the log (instead of sysout if // you like) public static final Logger LOGGER = LogManager.getLogger(); public static final String MODID = "siegemod"; public static SiegeMod instance; public static Minecraft mc = Minecraft.getInstance(); public SiegeMod() { final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); instance = this; FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); MinecraftForge.EVENT_BUS.register(this); SiegeEntityTypes.ENTITY_TYPES.register(modEventBus); SiegeContainerTypes.CONTAINER_TYPES.register(modEventBus); } private void setup(final FMLCommonSetupEvent event) { } private void doClientStuff(final FMLClientSetupEvent event) { } @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { } // You can use EventBusSubscriber to automatically subscribe events on the // contained class (this is subscribing to the MOD // Event bus for receiving Registry Events) @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { } } I will be converting from Inventory to ItemHandler soon, but for now I just want the container to open. --SOLUTION-- Main Entity Class: Spoiler package com.siegemod.entities; import java.util.UUID; import java.util.function.Consumer; import com.siegemod.container.PeasantContainer; import com.siegemod.container.PeasantInventory; import net.minecraft.entity.AgeableEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ILivingEntityData; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.goal.HurtByTargetGoal; import net.minecraft.entity.ai.goal.LookAtGoal; import net.minecraft.entity.ai.goal.LookRandomlyGoal; import net.minecraft.entity.ai.goal.MoveThroughVillageGoal; import net.minecraft.entity.ai.goal.MoveTowardsTargetGoal; import net.minecraft.entity.ai.goal.MoveTowardsVillageGoal; import net.minecraft.entity.ai.goal.SwimGoal; import net.minecraft.entity.merchant.villager.AbstractVillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.ItemStack; import net.minecraft.item.MerchantOffer; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; import net.minecraft.network.PacketBuffer; import net.minecraft.pathfinding.GroundPathNavigator; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; public abstract class SiegeEntity extends AbstractVillagerEntity { private final PeasantInventory peasantInventory; private final SiegeEntityInventoryContainerProvider containerProvider; public SiegeEntity(EntityType<? extends AbstractVillagerEntity> entityType, World worldIn, PlayerEntity kingIn) { super(entityType, worldIn); this.peasantInventory = new PeasantInventory(new Inventory(16), this); this.containerProvider = new SiegeEntityInventoryContainerProvider(this); } @Override public final boolean processInteract(PlayerEntity player, Hand hand) { if (hand == Hand.MAIN_HAND && player instanceof ServerPlayerEntity && !world.isRemote) { SiegeEntity peasant = this; NetworkHooks.openGui((ServerPlayerEntity) player, this.containerProvider, new Consumer<PacketBuffer>() { @Override public void accept(PacketBuffer buffer) { buffer.writeInt(peasant.getEntityId()); } }); return true; } return super.processInteract(player, hand); } @Override public void writeAdditional(CompoundNBT compound) { super.writeAdditional(compound); ListNBT listnbt = new ListNBT(); for (int i = 0; i < this.peasantInventory.getSlots(); ++i) { ItemStack itemstack = this.peasantInventory.getStackInSlot(i); if (!itemstack.isEmpty()) { listnbt.add(itemstack.write(new CompoundNBT())); } } compound.put("PeasantInventory", listnbt); } @Override public void readAdditional(CompoundNBT compound) { super.readAdditional(compound); ListNBT listnbt = compound.getList("PeasantInventory", 10); for (int i = 0; i < listnbt.size(); ++i) { ItemStack itemstack = ItemStack.read(listnbt.getCompound(i)); if (!itemstack.isEmpty()) { this.peasantInventory.insertItem(i, itemstack, false); } } } public PeasantInventory getPeasantInventory() { return peasantInventory; } public class SiegeEntityInventoryContainerProvider implements INamedContainerProvider { private final SiegeEntity peasant; public SiegeEntityInventoryContainerProvider(SiegeEntity peasantIn) { this.peasant = peasantIn; } @Override public Container createMenu(int containerId, PlayerInventory playerInv, PlayerEntity player) { return new PeasantContainer(containerId, playerInv, this.peasant); } @Override public ITextComponent getDisplayName() { return peasant.getDisplayName(); } } ContainerFactory: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantContainer; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.IContainerFactory; public class SiegeContainerFactory implements IContainerFactory<Container> { @Override public Container create(int windowId, PlayerInventory inv, PacketBuffer data) { return new PeasantContainer(windowId, inv, data); } } ScreenFactory: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantScreen; import net.minecraft.client.gui.ScreenManager.IScreenFactory; import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.util.text.ITextComponent; public class SiegeScreenFactory implements IScreenFactory<Container, ContainerScreen<Container>> { @Override public ContainerScreen<Container> create(Container container, PlayerInventory inv, ITextComponent ITextComponent) { return new PeasantScreen(container, inv, ITextComponent); } } Container Class: Spoiler package com.siegemod.container; import com.mojang.datafixers.util.Pair; import com.siegemod.entities.SiegeEntity; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; public class PeasantContainer extends Container { public SiegeEntity peasant; public IInventory peasantInventory; public PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) { this(containerId, playerInv, (SiegeEntity) playerInv.player.world.getEntityByID(containerBuffer.readInt())); } public PeasantContainer(int containerId, PlayerInventory playerInv, SiegeEntity peasantIn) { super(SiegeContainerTypes.PEASANT_CONTAINER.get(), containerId); this.peasant = peasantIn; this.peasantInventory = peasant.getPeasantInventory().getInv(); this.peasantInventory.openInventory(playerInv.player); // Entity Slots // Player Main Inventory for (int row = 0; row < 3; ++row) for (int column = 0; column < 9; ++column) this.addSlot(new Slot(playerInv, column + row * 9 + 9, 8 + column * 18, 102 + row * 18 + -18)); // Player Hotbar for (int row = 0; row < 9; ++row) { this.addSlot(new Slot(playerInv, row, 8 + row * 18, 142)); } } @Override public boolean canInteractWith(PlayerEntity playerIn) { return true; } @Override public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) { ItemStack itemstack = ItemStack.EMPTY; Slot slot = this.inventorySlots.get(index); if (slot != null && slot.getHasStack()) { ItemStack itemstack1 = slot.getStack(); itemstack = itemstack1.copy(); if (index < this.peasantInventory.getSizeInventory()) { if (!this.mergeItemStack(itemstack1, this.peasantInventory.getSizeInventory(), this.inventorySlots.size(), true)) { return ItemStack.EMPTY; } } else if (!this.mergeItemStack(itemstack1, 1, this.peasantInventory.getSizeInventory(), false)) { return ItemStack.EMPTY; } if (itemstack1.isEmpty()) { slot.putStack(ItemStack.EMPTY); } else { slot.onSlotChanged(); } } return itemstack; } public SiegeEntity getPeasant() { return this.peasant; } } Screen Class: Spoiler package com.siegemod.container; import com.mojang.blaze3d.systems.RenderSystem; import com.siegemod.SiegeMod; import com.siegemod.entities.SiegeEntity; import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.client.gui.screen.inventory.InventoryScreen; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class PeasantScreen extends ContainerScreen<Container> { private static final ResourceLocation PEASANT_GUI = new ResourceLocation(SiegeMod.MODID, "gui/peasant_gui.png"); private float mousePosx; private float mousePosY; private final SiegeEntity peasant; public PeasantScreen(Container screenContainer, PlayerInventory inv, ITextComponent titleIn) { super((PeasantContainer) screenContainer, inv, titleIn); this.peasant = ((PeasantContainer) screenContainer).getPeasant(); this.guiLeft = 0; this.guiTop = 0; this.xSize = 176; this.ySize = 166; } @Override protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); this.getMinecraft().getTextureManager().bindTexture(PEASANT_GUI); int i = (this.width - this.xSize) / 2; int j = (this.height - this.ySize) / 2; this.blit(i, j, 0, 0, this.xSize, this.ySize); InventoryScreen.drawEntityOnScreen(i + 52, j + 50, 22, (float) (i + 51) - this.mousePosx, (float) (j + 75 - 50) - this.mousePosY, this.peasant); } @Override public void render(int p_render_1_, int p_render_2_, float p_render_3_) { this.renderBackground(); this.mousePosx = (float) p_render_1_; this.mousePosY = (float) p_render_2_; super.render(p_render_1_, p_render_2_, p_render_3_); this.renderHoveredToolTip(p_render_1_, p_render_2_); } } Edited June 19, 2020 by OuiOuiCroissant Updated with solution Quote
OuiOuiCroissant Posted June 15, 2020 Author Posted June 15, 2020 Update! I have discovered that I MUST use the required constructor: Spoiler public PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) { However, I need both the Screen and Container classes to identify the entity they belong to. I bet I can pass getEntityID through the PacketBuffer parameter of Container class. Do I make a new PacketBuffer in the createMenu method and give it Unpooled.buffer()? Spoiler @Override public Container createMenu(int containerId, PlayerInventory playerInv, PlayerEntity player) { return new PeasantContainer(containerId, playerInv, new PacketBuffer(Unpooled.buffer().writeInt(PEASANT.getEntityId()))); } When I read it in the Container class it throws an IndexOutOfBoundsException. I will need to read up on how PacketBuffer works. I will update if I figure it out. Spoiler public PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) { super(SiegeContainerTypes.PEASANT_CONTAINER.get(), containerId); this.peasant = (SiegeEntity) playerInv.player.world.getEntityByID(containerBuffer.readInt()); Quote
OuiOuiCroissant Posted June 15, 2020 Author Posted June 15, 2020 IContainerFactory doesn't have a PacketBuffer field, it only exists as a parameter in its create() method. Where does an instance of PacketBuffer come from? Or should I create my own static instance in my container factory? Implementing IContainerFactory on my container factory has me implement a create() method: Spoiler @Override public Container create(int windowId, PlayerInventory inv, PacketBuffer data) { return null; } } But I cannot make a static instance of my container, right? And create() is not a static method so I can't call it from somewhere else. I have nothing to pass into widowId or playerInv from here either. Spoiler package com.siegemod.container; import com.siegemod.SiegeMod; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; import net.minecraft.network.PacketBuffer; import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.fml.RegistryObject; import net.minecraftforge.fml.network.IContainerFactory; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; public class SiegeContainerTypes implements IContainerFactory<Container> { public static final DeferredRegister<ContainerType<?>> CONTAINER_TYPES = new DeferredRegister<>(ForgeRegistries.CONTAINERS, SiegeMod.MODID); public static final RegistryObject<ContainerType<PeasantContainer>> PEASANT_CONTAINER = CONTAINER_TYPES.register(SiegeMod.MODID, () -> { return IForgeContainerType.create(PeasantContainer::new); }); @Override public Container create(int windowId, PlayerInventory inv, PacketBuffer data) { return null; } } Quote
OuiOuiCroissant Posted June 18, 2020 Author Posted June 18, 2020 I just meant a static field to pass into NetworkHooks.openGui(ServerPlayerEntity, INamedContainerProvider, Consumer<PacketBuffer>) like so: Spoiler public static final PacketBuffer CONTAINER_PACKET = new PacketBuffer(Unpooled.buffer()); So far my container factory looks like: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantContainer; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.IContainerFactory; public class SiegeContainerFactory implements IContainerFactory<Container> { @Override public Container create(int windowId, PlayerInventory inv, PacketBuffer data) { System.out.println("IContainerFactory#CREATE WAS RUN"); return new PeasantContainer(windowId, inv, data); } } And I made a screen factory: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantScreen; import net.minecraft.client.gui.ScreenManager.IScreenFactory; import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.util.text.ITextComponent; public class SiegeScreenFactory implements IScreenFactory<Container, ContainerScreen<Container>> { @Override public ContainerScreen<Container> create(Container container, PlayerInventory inv, ITextComponent ITextComponent) { System.out.println("ScreenFactory#CREATE WAS RUN"); return new PeasantScreen(container, inv, ITextComponent); } } Will both of these create() methods be run if I run NetworkHooks.openGui(ServerPlayerEntity, INamedContainerProvider, Consumer<PacketBuffer>) on the server side? Thank you so much for your patience and help. Quote
OuiOuiCroissant Posted June 19, 2020 Author Posted June 19, 2020 Something I implement? As in I implement an interface? Or do you mean I create a new instance of PacketBuffer like so: ContainerProvider: Spoiler public class SiegeContainerProvider implements INamedContainerProvider { private final SiegeEntity peasant; public SiegeContainerProvider(SiegeEntity peasantIn) { this.peasant = peasantIn; } @Override public Container createMenu(int containerId, PlayerInventory playerInv, PlayerEntity player) { PacketBuffer containerPacket = new PacketBuffer(Unpooled.buffer()); containerPacket.writeInt(peasant.getEntityId()); return new PeasantContainer(containerId, playerInv, containerPacket); } @Override public ITextComponent getDisplayName() { return peasant.getDisplayName(); } } Here are my other classes as they currently are: EntityClass: Spoiler package com.siegemod.entities; import java.util.UUID; import com.siegemod.container.PeasantContainer; import com.siegemod.container.PeasantInventory; import io.netty.buffer.Unpooled; import net.minecraft.entity.AgeableEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ILivingEntityData; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.goal.HurtByTargetGoal; import net.minecraft.entity.ai.goal.LookAtGoal; import net.minecraft.entity.ai.goal.LookRandomlyGoal; import net.minecraft.entity.ai.goal.MoveThroughVillageGoal; import net.minecraft.entity.ai.goal.MoveTowardsTargetGoal; import net.minecraft.entity.ai.goal.MoveTowardsVillageGoal; import net.minecraft.entity.ai.goal.SwimGoal; import net.minecraft.entity.merchant.villager.AbstractVillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.ItemStack; import net.minecraft.item.MerchantOffer; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; import net.minecraft.network.PacketBuffer; import net.minecraft.pathfinding.GroundPathNavigator; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; public abstract class SiegeEntity extends AbstractVillagerEntity { protected final double walkSpeed = 0.4D; // Convert to ItemHandler private final PeasantInventory peasantInventory; private final SiegeContainerProvider containerProvider; // 0 = wander, 1 = follow, 2 = locked follow, 3 = guard, 4 = locked guard. protected double stance = 0; // The main controller of this peasant protected UUID kingUniqueID; // Allowed to control this peasant and access inventory protected UUID[] royalFamily; // Will not attack/be attacked by the soldiers of people on this list protected UUID[] allies; protected BlockPos destination; protected ItemStack targetItem; public SiegeEntity(EntityType<? extends AbstractVillagerEntity> entityType, World worldIn, PlayerEntity kingIn) { super(entityType, worldIn); this.peasantInventory = new PeasantInventory(new Inventory(16), this); this.containerProvider = new SiegeContainerProvider(this); if (kingIn != null) this.kingUniqueID = kingIn.getUniqueID(); ((GroundPathNavigator) this.getNavigator()).setBreakDoors(true); this.getNavigator().setCanSwim(true); this.setCanPickUpLoot(true); } @Override public ILivingEntityData onInitialSpawn(IWorld worldIn, DifficultyInstance difficultyIn, SpawnReason reason, ILivingEntityData spawnDataIn, CompoundNBT dataTag) { if (!world.isRemote && reason == SpawnReason.COMMAND) this.setKing(world.getClosestPlayer(this, 999).getUniqueID()); if (this.rand.nextInt(10) == 0) this.setLeftHanded(true); return super.onInitialSpawn(worldIn, difficultyIn, reason, spawnDataIn, dataTag); } @Override protected void registerGoals() { super.registerGoals(); this.goalSelector.addGoal(0, new SwimGoal(this)); // We're going to need a custom avoid entity goal // We're going to need a custom choose target goal // We're going to need a custom "wander near player bed while unclaimed but eventually wander away and despawn" goal this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, this.walkSpeed, 32.0F)); this.goalSelector.addGoal(2, new MoveTowardsVillageGoal(this, this.walkSpeed)); this.goalSelector.addGoal(3, new MoveThroughVillageGoal(this, this.walkSpeed, false, 4, () -> { return false; })); this.goalSelector.addGoal(7, new LookAtGoal(this, PlayerEntity.class, 6.0F)); this.goalSelector.addGoal(8, new LookRandomlyGoal(this)); this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); } @Override protected void registerAttributes() { super.registerAttributes(); this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(20.0D); this.getAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(64.0D); this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(this.walkSpeed); this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(1.0D); this.getAttribute(SharedMonsterAttributes.ARMOR).setBaseValue(0.0D); this.getAttribute(SharedMonsterAttributes.ARMOR_TOUGHNESS).setBaseValue(0.0D); this.getAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE).setBaseValue(10.0F); } @Override public boolean attackEntityAsMob(Entity entityIn) { this.swingArm(getActiveHand()); return super.attackEntityAsMob(entityIn); } public void livingTick() { this.updateArmSwingProgress(); super.livingTick(); } public void writeAdditional(CompoundNBT compound) { super.writeAdditional(compound); ListNBT listnbt = new ListNBT(); for (int i = 0; i < this.peasantInventory.getSlots(); ++i) { ItemStack itemstack = this.peasantInventory.getStackInSlot(i); if (!itemstack.isEmpty()) { listnbt.add(itemstack.write(new CompoundNBT())); } } compound.put("PeasantInventory", listnbt); if (this.kingUniqueID != null) compound.putUniqueId("KingId", this.kingUniqueID); } public void readAdditional(CompoundNBT compound) { super.readAdditional(compound); ListNBT listnbt = compound.getList("PeasantInventory", 10); for (int i = 0; i < listnbt.size(); ++i) { ItemStack itemstack = ItemStack.read(listnbt.getCompound(i)); if (!itemstack.isEmpty()) { this.peasantInventory.insertItem(i, itemstack, false); } } if (this.kingUniqueID == null && compound.hasUniqueId("KingId")) { this.setKing(compound.getUniqueId("KingId")); } } public PlayerEntity getKing() { return world.getPlayerByUuid(this.kingUniqueID); } public void setKing(UUID playerUUIDIn) { this.kingUniqueID = playerUUIDIn; } @Override public final boolean processInteract(PlayerEntity player, Hand hand) { if (hand == Hand.MAIN_HAND && !world.isRemote) { if (player.getUniqueID() == this.kingUniqueID && player instanceof ServerPlayerEntity && !world.isRemote) { NetworkHooks.openGui((ServerPlayerEntity) player, this.containerProvider); return true; } } return super.processInteract(player, hand); } public PeasantInventory getPeasantInventory() { return peasantInventory; } /* public DyeColor getPrimaryColor() { return DyeColor.byId(this.dataManager.get(COLLAR_COLOR)); } public void setSecondaryColor(DyeColor collarcolor) { this.dataManager.set(COLLAR_COLOR, collarcolor.getId()); }*/ @Override public AgeableEntity createChild(AgeableEntity ageable) { return null; } @Override protected void onVillagerTrade(MerchantOffer offer) { } @Override protected void populateTradeData() { } } Container Factory: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantContainer; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.IContainerFactory; public class SiegeContainerFactory implements IContainerFactory<Container> { @Override public Container create(int windowId, PlayerInventory inv, PacketBuffer data) { return new PeasantContainer(windowId, inv, data); } } Screen Factory: Spoiler package com.siegemod.util; import com.siegemod.container.PeasantScreen; import net.minecraft.client.gui.ScreenManager.IScreenFactory; import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.util.text.ITextComponent; public class SiegeScreenFactory implements IScreenFactory<Container, ContainerScreen<Container>> { @Override public ContainerScreen<Container> create(Container container, PlayerInventory inv, ITextComponent ITextComponent) { return new PeasantScreen(container, inv, ITextComponent); } } Container: Spoiler package com.siegemod.container; import com.mojang.datafixers.util.Pair; import com.siegemod.entities.SiegeEntity; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; public class PeasantContainer extends Container { public SiegeEntity peasant; public IInventory peasantInventory; public static final EquipmentSlotType[] VALID_EQUIPMENT_SLOTS = new EquipmentSlotType[] { EquipmentSlotType.HEAD, EquipmentSlotType.CHEST, EquipmentSlotType.LEGS, EquipmentSlotType.FEET }; private static final ResourceLocation[] ARMOR_SLOT_TEXTURES = new ResourceLocation[] { PlayerContainer.EMPTY_ARMOR_SLOT_BOOTS, PlayerContainer.EMPTY_ARMOR_SLOT_LEGGINGS, PlayerContainer.EMPTY_ARMOR_SLOT_CHESTPLATE, PlayerContainer.EMPTY_ARMOR_SLOT_HELMET }; public PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) { super(SiegeContainerTypes.PEASANT_CONTAINER.get(), containerId); this.peasant = (SiegeEntity) playerInv.player.world.getEntityByID(containerBuffer.readInt()); this.peasantInventory = peasant.getPeasantInventory().getInv(); this.peasantInventory.openInventory(playerInv.player); // MainHand 0 this.addSlot(new Slot(this.peasantInventory, 0, 32, 62) { @Override public void onSlotChanged() { super.onSlotChanged(); peasant.getPeasantInventory().markDirty(); } }); //Armor 1-4 for (int row = 0; row < 4; row++) { final EquipmentSlotType equipmentslottype = VALID_EQUIPMENT_SLOTS[row]; this.addSlot(new Slot(this.peasantInventory, row + 1, 8, 8 + row * 18) { @Override public void onSlotChanged() { super.onSlotChanged(); peasant.getPeasantInventory().markDirty(); } @Override public int getSlotStackLimit() { return 1; } @Override public boolean isItemValid(ItemStack stack) { return stack.canEquip(equipmentslottype, peasant); } @Override public boolean canTakeStack(PlayerEntity playerIn) { ItemStack itemstack = this.getStack(); return !itemstack.isEmpty() && !playerIn.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.canTakeStack(playerIn); } @OnlyIn(Dist.CLIENT) public Pair<ResourceLocation, ResourceLocation> func_225517_c_() { return Pair.of(PlayerContainer.LOCATION_BLOCKS_TEXTURE, ARMOR_SLOT_TEXTURES[equipmentslottype.getIndex()]); } }); } // OffHand 5 this.addSlot(new Slot(this.peasantInventory, 5, 56, 62) { @Override public void onSlotChanged() { super.onSlotChanged(); peasant.getPeasantInventory().markDirty(); } @OnlyIn(Dist.CLIENT) public Pair<ResourceLocation, ResourceLocation> func_225517_c_() { return Pair.of(PlayerContainer.LOCATION_BLOCKS_TEXTURE, PlayerContainer.EMPTY_ARMOR_SLOT_SHIELD); } }); // Main Inventory 6-15 final int invXPos = 80; final int invYPos = 8; final int height = 2; final int length = 5; for (int row = 0; row < height; row++) for (int column = 0; column < length; column++) this.addSlot(new Slot(this.peasantInventory, (row * length) + column + 6, invXPos + column * 18, invYPos + row * 18)); // Player Main Inventory for (int row = 0; row < 3; ++row) for (int column = 0; column < 9; ++column) this.addSlot(new Slot(playerInv, column + row * 9 + 9, 8 + column * 18, 102 + row * 18 + -18)); // Player Hotbar for (int row = 0; row < 9; ++row) { this.addSlot(new Slot(playerInv, row, 8 + row * 18, 142)); } } @Override public boolean canInteractWith(PlayerEntity playerIn) { return peasant != null && ((playerIn == peasant.getKing() && playerIn.getDistance(peasant) <= PlayerEntity.REACH_DISTANCE.getDefaultValue() + 1) || playerIn.isCreative()); } @Override public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) { ItemStack itemstack = ItemStack.EMPTY; Slot slot = this.inventorySlots.get(index); if (slot != null && slot.getHasStack()) { ItemStack itemstack1 = slot.getStack(); itemstack = itemstack1.copy(); if (index < this.peasantInventory.getSizeInventory()) { if (!this.mergeItemStack(itemstack1, this.peasantInventory.getSizeInventory(), this.inventorySlots.size(), true)) { return ItemStack.EMPTY; } } else if (!this.mergeItemStack(itemstack1, 1, this.peasantInventory.getSizeInventory(), false)) { return ItemStack.EMPTY; } if (itemstack1.isEmpty()) { slot.putStack(ItemStack.EMPTY); } else { slot.onSlotChanged(); } } return itemstack; } public SiegeEntity getPeasant() { return this.peasant; } } Quote
OuiOuiCroissant Posted June 19, 2020 Author Posted June 19, 2020 Like so? Spoiler @Override public final boolean processInteract(PlayerEntity player, Hand hand) { if (hand == Hand.MAIN_HAND && player instanceof ServerPlayerEntity && !world.isRemote) { if (this.hasKing() && this.isKing(player)) { SiegeEntity peasant = this; NetworkHooks.openGui((ServerPlayerEntity) player, this.containerProvider, new Consumer<PacketBuffer>() { @Override public void accept(PacketBuffer buffer) { buffer.writeInt(peasant.getEntityId()); } }); return true; } else if (!this.hasKing()) { return super.processInteract(player, hand); } } return super.processInteract(player, hand); } Quote
OuiOuiCroissant Posted June 19, 2020 Author Posted June 19, 2020 My god, it works. Thank you, you absolute legend. Updated post with solution. Quote
Recommended Posts
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.