OuiOuiCroissant
-
Posts
13 -
Joined
-
Last visited
Posts posted by OuiOuiCroissant
-
-
That makes sense. I'm still getting errors on the last 3 parameters of INSTANCE.registerMessage() in my packet handler. Is there a new way to register packets in 1.15.2?
QuoteThe method registerMessage(int, Class<MSG>, BiConsumer<MSG,PacketBuffer>, Function<PacketBuffer,MSG>, BiConsumer<MSG,Supplier<NetworkEvent.Context>>) in the type SimpleChannel is not applicable for the arguments (int, Class<SetKingPacket>, SetKingPacket::encode, SetKingPacket::new, SetKingPacket.Handler::handle)
My Packet:
package com.siegemod.packets; import com.google.common.base.Supplier; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; public class SetKingPacket { private final int data; public SetKingPacket(int dataIn) { this.data = dataIn; } public static void encode(SetKingPacket msg, PacketBuffer buf) { buf.writeInt(msg.data); } public static SetKingPacket decode(PacketBuffer buf) { int data = buf.readInt(); return new SetKingPacket(data); } public static class Handler { public static void handle(SetKingPacket msg, Supplier<NetworkEvent.Context> ctx) { ctx.get().enqueueWork(() -> { // Work that needs to be threadsafe (most work) ServerPlayerEntity sender = ctx.get().getSender(); // the client that sent this packet // do stuff }); ctx.get().setPacketHandled(true); } } }
My Packet Handler:
package com.siegemod.packets; import com.siegemod.SiegeMod; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.simple.SimpleChannel; public final class SiegePacketHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(new ResourceLocation(SiegeMod.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private static int id; public static void register() { INSTANCE.registerMessage(id++, SetKingPacket.class, SetKingPacket::encode, SetKingPacket::new, SetKingPacket.Handler::handle); } } /* // Sending to one player INSTANCE.send(PacketDistributor.PLAYER.with(playerMP),new MyMessage()); // Send to all players tracking this chunk INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(chunk),new MyMessage()); // Sending to all connected players INSTANCE.send(PacketDistributor.ALL.noArg(),new MyMessage()); */
-
I just learned how to do packets for the first time by following the forge documentation, but it only got me so far. I don't understand what SimpleChannel.registerMessages() should take as parameters.
In a lot of examples and tutorials they give it static void methods from the message class, but it won't accept those in my code. It "is not applicable for the arguments". I see people implementing IMessage into their packet class, but there is no IMessage interface! Has it been replaced with MSG? Do I have to create it?
If you know a lot about packets, can you tell me what I'm doing wrong?
My Packet Handler:
package com.siegemod.util; import com.siegemod.SiegeMod; import com.siegemod.packets.SetKingPacket; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.simple.SimpleChannel; public class SiegePacketHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(new ResourceLocation(SiegeMod.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private static int id; public static void registerPackets() { INSTANCE.registerMessage(id++, SetKingPacket.class, SetKingPacket::encode, SetKingPacket::decode, SetKingPacket::handle); } } /* // Sending to one player INSTANCE.send(PacketDistributor.PLAYER.with(playerMP),new MyMessage()); // Send to all players tracking this chunk INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(chunk),new MyMessage()); // Sending to all connected players INSTANCE.send(PacketDistributor.ALL.noArg(),new MyMessage()); */
My Message:
package com.siegemod.packets; import java.nio.ByteBuffer; import org.lwjgl.system.windows.MSG; import com.google.common.base.Supplier; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; public class SetKingPacket extends MSG { private String kingUUID; public SetKingPacket(ByteBuffer buffer) { super(buffer); } public static void encode(SetKingPacket msg, PacketBuffer buf) { buf.writeString(msg.kingUUID); } public static void decode(PacketBuffer buf, SetKingPacket msg) { msg.kingUUID = buf.readString(); } public static void handle(SetKingPacket msg, Supplier<NetworkEvent.Context> ctx) { ctx.get().enqueueWork(() -> { // Work that needs to be threadsafe (most work) ServerPlayerEntity sender = ctx.get().getSender(); // the client that sent this packet // do stuff }); ctx.get().setPacketHandled(true); } }
-
My god, it works. Thank you, you absolute legend. Updated post with solution.
-
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);
} -
Something I implement? As in I implement an interface? Or do you mean I create a new instance of PacketBuffer like so:
ContainerProvider:
Spoilerpublic 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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;
}
} -
I just meant a static field to pass into NetworkHooks.openGui(ServerPlayerEntity, INamedContainerProvider, Consumer<PacketBuffer>) like so:
Spoilerpublic static final PacketBuffer CONTAINER_PACKET = new PacketBuffer(Unpooled.buffer());
So far my container factory looks like:
Spoilerpackage 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:
Spoilerpackage 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.
-
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.
Spoilerpackage 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;
}
} -
Update!
I have discovered that I MUST use the required constructor:Spoilerpublic 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.
Spoilerpublic PeasantContainer(int containerId, PlayerInventory playerInv, PacketBuffer containerBuffer) {
super(SiegeContainerTypes.PEASANT_CONTAINER.get(), containerId);
this.peasant = (SiegeEntity) playerInv.player.world.getEntityByID(containerBuffer.readInt()); -
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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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:
Spoilerpackage 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_);
}
} -
Hello!
Title.
The Forge Documentation page doesn't show examples of how to save/load data from other classes (like MyEventHandler), it kind of explains which methods to use, but I'm still unsure how to do it properly and I want to do it properly.
Both the MyWorldSavedData#write/readNBTTagCompounds() are run, and MyEventHandler#countFullMoons() is run properly.
MyWorldSavedData:
Spoilerpackage com.ani.s;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraft.world.storage.MapStorage;
import net.minecraft.world.storage.WorldSavedData;public class MyWorldSavedData extends WorldSavedData {
static final String DATA_NAME = MyVariousVariables.MODID + "_MyWorldSavedData";
// Do I need to create a new variable in this class for every variable I want to save?
private int amount_of_full_moons;public MyWorldSavedData() {
super(DATA_NAME);
}public MyWorldSavedData(String s) {
super(s);
}@Override
public void readFromNBT(NBTTagCompound nbt) {
amount_of_full_moons = nbt.getInteger("amount_of_full_moons");System.out.println("READ FROM NBT " + amount_of_full_moons);
}@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
nbt.setInteger("amount_of_full_moons", amount_of_full_moons);System.out.println("WRITE TO NBT " + amount_of_full_moons);
return nbt;
}public static MyWorldSavedData get(World world) {
MapStorage storage = world.getPerWorldStorage();
MyWorldSavedData instance = (MyWorldSavedData) storage.getOrLoadData(MyWorldSavedData.class, DATA_NAME);
if (instance == null) {
instance = new MyWorldSavedData();
storage.setData(DATA_NAME, instance);
}
return instance;
}
}MyEventHandler:
Spoilerpackage com.ani.s;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.WorldTickEvent;@Mod.EventBusSubscriber
public class MyEventHandler {public int amount_of_full_moons;
@SubscribeEvent
public void countFullMoons(WorldTickEvent event) {
if (event.phase == event.phase.START && event.world.provider.isSurfaceWorld()) {
// How do I properly use MyWorldSavedData to load 'amount_of_full_moons' here?if (event.world.getWorldTime() == 13000 && event.world.getCurrentMoonPhaseFactor() >= 1F) {
++amount_of_full_moons;// How do I properly use MyWorldSavedData to save 'amount_of_full_moons' here?
MyWorldSavedData.get(event.world).markDirty();
}
}
}
} -
10 minutes ago, Draco18s said:
This will always be true. Minecraft, the class, does not exist on the server.
Ahhh thank you. What's the easiest way to call a specific entity using an entity ID on the server?
I'm using this. ctx is the MessageContext parameter in #onMessage().ctx.getServerHandler().player.world.getEntityByID(message.peasant_id);
-
Hello!
I followed SimpleImpl as closely as I could, but my ServerMessageHandler is being run on the client side!
It is printing out "Remote = true" after I add this to it:
System.out.println("Remote = " + Minecraft.getMinecraft().world.isRemote);
Here is everything:
Registering my ServerMessageHandler:
Spoilerpackage com.ani.s;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.minecraftforge.fml.relauncher.Side;import com.ani.s.packets.ClientMessage;
import com.ani.s.packets.ClientMessageHandler;
import com.ani.s.packets.ServerMessage;
import com.ani.s.packets.ServerMessageHandler;
import com.ani.s.proxies.CommonProxy;@Mod(modid = SiegeVariables.MODID, name = SiegeVariables.NAME, version = SiegeVariables.VERSION)
public class SiegeMod {@SidedProxy(serverSide = SiegeVariables.SERVER_SIDE, clientSide = SiegeVariables.CLIENT_SIDE)
public static CommonProxy proxy;
@Instance(SiegeVariables.MODID)
public static SiegeMod instance;public static int handler_id;
public static final SimpleNetworkWrapper MESSAGE_HANDLER_INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel(SiegeVariables.MODID);
public static final SiegeModTab CREATIVETAB = new SiegeModTab();
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
SiegeModEntityRegistry.init();
SiegeModRecipes.init();
proxy.registerModels();
proxy.register();
MESSAGE_HANDLER_INSTANCE.registerMessage(ClientMessageHandler.class, ClientMessage.class, handler_id++, Side.CLIENT);
MESSAGE_HANDLER_INSTANCE.registerMessage(ServerMessageHandler.class, ServerMessage.class, handler_id++, Side.SERVER);
}@EventHandler
public void init(FMLInitializationEvent event) {
}@EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
}
My ServerMessage:
Spoilerpackage com.ani.s.packets;
import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;public class ServerMessage implements IMessage {
public int peasant_id;
public int stance;
public ServerMessage() {
}public ServerMessage(int p_peasant_id) {
peasant_id = p_peasant_id;
}public void toBytes(ByteBuf buf) {
buf.writeInt(peasant_id);
buf.writeInt(stance);
}public void fromBytes(ByteBuf buf) {
peasant_id = buf.readInt();
stance = buf.readInt();
}
}And my ServerMessageHandler (ignore all the "!= null"s, I've had... issues):
Spoilerpackage com.ani.s.packets;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;import com.ani.s.entity.EntityCitizen;
public class ServerMessageHandler implements IMessageHandler<ServerMessage, IMessage> {
public IMessage onMessage(ServerMessage message, MessageContext ctx) {
if (message != null) {
System.out.println("Remote = " + Minecraft.getMinecraft().world.isRemote);
if (Minecraft.getMinecraft().world != null && Minecraft.getMinecraft().world.getEntityByID(message.peasant_id) != null) {
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.peasant_id);
if (entity != null && entity instanceof EntityCitizen) {
EntityCitizen citizen = (EntityCitizen) entity;
citizen.cycleStance(message.stance);
}
}
}
return null;
}
}Packet is sent from WorkerGUI Class (WorkerGUI is opened when interacting with EntityCitizen) in #mouseReleased()
Spoilerpackage com.ani.s.inventory;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.EntityList;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;import com.ani.s.SiegeMod;
import com.ani.s.SiegeVariables;
import com.ani.s.entity.EntityCitizen;
import com.ani.s.packets.ServerMessage;@SideOnly(Side.CLIENT)
public class WorkerGUI extends GuiContainer {public final ResourceLocation TEXTURE = new ResourceLocation(SiegeVariables.MODID + ":textures/gui/worker_gui.png");
private EntityCitizen owner;
private final int SIZE_X = 176;
private final int SIZE_Y = 186;
private int button_id;public WorkerGUI(final EntityCitizen ent, IInventory playerInventory) {
super(new WorkerContainer(ent, ent.getWorkerInventory(), playerInventory));
owner = ent;
}@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
GlStateManager.color(1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(TEXTURE);
this.drawModalRectWithCustomSizedTexture(this.guiLeft, this.guiTop, 0, 0, SIZE_X, SIZE_Y, 256, 256);
int pos = this.guiLeft + 30;GuiButton butt = new GuiButton(++button_id, this.guiLeft + 10, this.guiTop + 18, 156, 20, owner.getStringFromStance(owner.client_message.stance)) {
@Override
public void mouseReleased(int mouseX, int mouseY) {
if (mousePressed(mc, mouseX, mouseY)) {
ServerMessage server_message = new ServerMessage(owner.getEntityId());
server_message.stance = owner.stance + 1;
SiegeMod.MESSAGE_HANDLER_INSTANCE.sendToServer(server_message);
}
}@Override
public void playPressSound(SoundHandler soundHandlerIn) {
}
};
this.addButton(butt);drawCenteredString(fontRenderer, EntityList.getEntityString(owner), pos, this.guiTop + 5, 0xFFFFFF);
drawCenteredString(fontRenderer, owner.client_message.king_name, pos + ((SIZE_X / 3)), this.guiTop + 5, 0xFFFFFF);
drawCenteredString(fontRenderer, "H: " + owner.client_message.saturation, pos + ((SIZE_X / 3) * 2), this.guiTop + 5, 0xFFFFFF);
}
}Thanks!
[1.15.2] What Should I Give the SimpleChannel.registerMessages() Parameters?
in Modder Support
Posted
It's still giving me those same 3 errors on the underlined bits:
INSTANCE.registerMessage(id++, SetKingPacket.class, SetKingPacket::encode, SetKingPacket::decode, SetKingPacket.Handler::handle);
"The type SetKingPacket does not define encode(MSG, PacketBuffer) that is applicable here"
"The type of decode(PacketBuffer) from the type SetKingPacket is SetKingPacket, this is incompatible with the descriptor's return type: MSG"
"The type SetKingPacket.Handler does not define handle(MSG, Supplier<NetworkEvent.Context>) that is applicable here"
SetKingPacket.encode() is a static void method, but SimpleChannel.registerMessage() wants a BiConsumer<MSG,PacketBuffer> in that parameter. How would encode ever work?
Handler:
package com.siegemod.packets; import com.siegemod.SiegeMod; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.simple.SimpleChannel; public class SiegePacketHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(new ResourceLocation(SiegeMod.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private static int id; public static void registerPackets() { INSTANCE.registerMessage(id++, SetKingPacket.class, SetKingPacket::encode, SetKingPacket::decode, SetKingPacket.Handler::handle); } } /* // Sending to one player INSTANCE.send(PacketDistributor.PLAYER.with(playerMP),new MyMessage()); // Send to all players tracking this chunk INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(chunk),new MyMessage()); // Sending to all connected players INSTANCE.send(PacketDistributor.ALL.noArg(),new MyMessage()); */
Packet: