Jump to content

Starless

Members
  • Posts

    107
  • Joined

  • Last visited

Everything posted by Starless

  1. I talked about implementing those interfaces because my TileEntity is a chest
  2. So Minecraft itself doesn't use the methods defined by those interfaces?
  3. So if I don't expose it, players will still be able to interact with it? And should I implement IItemHandler , IItemHandlerModifiable and INBTSerializable<NBTTagCompound> ?
  4. I have an inventory TileEntity. How do I make sure no automation mods can interact with it (like BuildCraft pipes)?
  5. I'm having a little trouble getting the name of the player: @SubscribeEvent public void onAttachCapabilityEntity(AttachCapabilitiesEvent.Entity event) { if(event.getEntity() instanceof EntityPlayer) { System.out.println(event.getEntity().getName());//getName() throws a NullPointerException here } } why does getName() throws a NullPointerException at that point?
  6. By the way, I use a lazy getter. Still, in case of my hash it wouldn't be so bad if somehow I'd load it twice, it would only hurt performance. In case of an UUID (as I'll need later for another aspect of my project: certain TileEntity instances will have their own UUID ), any capability that would get a random value via a UUID.randomUUID() call instead of reading its saved value would utterly and completely ruin the logic of my mod. I would really appreciate if someone would kind of read my classes (the second post with code, not the first) and copy paste everything that is correct while overwritting anything that's wrong. I posted the whole comprehensive code because I was hoping someone would actually take a look at it. My java code writting skills may not be superb, but I try to keep it clean and easy to read.
  7. Each external object you attach the capability to (whether it's an Entity , TileEntity , ItemStack or World ) must have its own provider instance. Each provider instance must store its own instance of the capability ( IUUIDCapability ). Your capability will be read from NBT (i.e. INBTSerializable#deserializeNBT will be called on your ICapabilityProvider ) at some point after AttachCapabilitiesEvent is fired. Like I said before, consider lazy-loading the expensive operation instead of running it in the parameterless constructor. If it helps, you can look at the capabilities provided by Forge (look for usages of CapabilityManager#register ), the capability test mod or my own mod's capabilities (API, implementation). I'll try later tomorrow. Thank you.
  8. Indeed it is. Very true. I'm sorry, but I don't understand how it works. I know you are trying to help, but comments such as those are not very helpful. It is clear to me that I fundamentally don't understand how this works. Could someone offer me a deeper explanation than what is presented in the documentation? If there's someone who really understands Capabilities, they should suggest a full re-writing of the relevant page in the documentation, because it is not too clear.
  9. Since the parameterless constructor of IUUIDCapability.Implementation generates a new random UUID , the only occasion when it must be called is when a player first enters the world, and never again. Also, the ICapabilityProvider holds one instance of the capability for each player? I don't understand. What If I needed one for each TileEntity (as I will need in the future)? None of this is remotely clear in the documentation. Maybe a tutorial would be in order.
  10. but the instance is unique for each player. If I give everyone the same instance it won't work. Also, I can instantiate an Implementation using the parameterless construct only once for each player during the whole existence of a world (In fact, even more than that).
  11. Can you show me how would I do that with the classes I have?
  12. Ok, this is what I have so far. Is it correct? Only thing missing is the implementation of a handler to PlayerEvent.Clone that will let the capability persist after player death. public final class EventHandler { private EventHandler() { } @SubscribeEvent public void onAttachCapabilityEntity(AttachCapabilitiesEvent.Entity event) { if(event.getEntity() instanceof EntityPlayer) { event.addCapability(new ResourceLocation(TheMod.MODID, "UUIDCapability"), new IUUIDCapability.Provider((EntityPlayer)event.getEntity()))); } } @SubscribeEvent public void onPlayerClone(PlayerEvent.Clone e) { //TODO: STILL DON'T KNOW HOW TO PERSIST THE CAPABILITY. } public static void init(FMLInitializationEvent event) { MinecraftForge.EVENT_BUS.register(new EventHandler()); } } Most of the file bellow remained unchanged. I just added a clone method to IUUIDCapability.Implementation (That I still don't use, but I think will be useful when implementing the handler to PlayerEvent.Clone ). I also refactored IUUIDCapability.Provider to keep a reference to its EntityPlayer owner and to use that reference to read and write to the actual Capability (via EntityPlayer#getCapability ) public interface IUUIDCapability { UUID getUUID(); void setUUID(UUID uuid); public static final class Storage implements Capability.IStorage<IUUIDCapability> { @Override public NBTTagCompound writeNBT(Capability<IUUIDCapability> capability, IUUIDCapability instance, EnumFacing side) { NBTTagCompound tag = new NBTTagCompound(); tag.setLong("HI", instance.getUUID().getMostSignificantBits()); tag.setLong("LO", instance.getUUID().getLeastSignificantBits()); return tag; } @Override public void readNBT(Capability<IUUIDCapability> capability, IUUIDCapability instance, EnumFacing side, NBTBase nbt) { NBTTagCompound tag = (NBTTagCompound)nbt; long hi = tag.getLong("HI"); long low = tag.getLong("LO"); instance.setUUID(new UUID(hi, low)); } } public static final class Factory implements Callable<IUUIDCapability> { @Override public IUUIDCapability call() throws Exception { return new Implementation(); } } public static final class Implementation implements IUUIDCapability { private UUID uuid; public Implementation() { //this.uuid = UUID.randomUUID(); } public Implementation(UUID uuid) { this.uuid = uuid; } @Override public UUID getUUID() { if(uuid != null) { return uuid; } else { uuid = UUID.randomUUID(); return uuid; } } @Override public void setUUID(UUID uuid) { this.uuid = uuid; } @Override protected Implementation clone() { return new Implementation(uuid); } } public static final class Provider implements ICapabilitySerializable<NBTTagCompound> { private EntityPlayer owner; public Provider(EntityPlayer owner) { this.owner = owner; } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { return capability == ModCapabilities.UUIDCapability; } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if(capability == ModCapabilities.UUIDCapability) { return (T) owner.getCapability(capability, facing); } return null; } @Override public NBTTagCompound serializeNBT() { NBTTagCompound tag = new NBTTagCompound(); IUUIDCapability cap = owner.getCapability(ModCapabilities.UUIDCapability, EnumFacing.DOWN); long hi = cap.getUUID().getMostSignificantBits(); long low = cap.getUUID().getLeastSignificantBits(); tag.setLong("HI", hi); tag.setLong("LO", low); return tag; } @Override public void deserializeNBT(NBTTagCompound tag) { IUUIDCapability cap = owner.getCapability(ModCapabilities.UUIDCapability, EnumFacing.DOWN); long hi = tag.getLong("HI"); long low = tag.getLong("LO"); UUID uuid = new UUID(hi, low); cap.setUUID(uuid); } } } Will that work? The points that I'm worried about: I have to generate a random UUID only once. This UUID must persist through Minecraft sessions (I'm using a lazy getter, as instructed by Choonster). Not only the UUID must persist through sessions, to do so it has to, of course, persist through player death, and I don't know how to implement that part (The handler of PlayerEvent.Clone).
  13. I know, this is just an example. I will use UUIDs for a certain type of TileEntity in the future though. This is a replacement for IEEP. How do I give players a capability?
  14. Note: While I use UUIDs in the code bellow, this is just a test. The truth is I'll have to hash a file in the player's client machine and store that hash in a field. This is what I have so far. It's kind of a IEEP. //the class below is to register the capabilities and to store their unique instances. public final class ModCapabilities { @CapabilityInject(IUUIDCapability.class) public static Capability<IUUIDCapability> UUIDCapability = null; private ModCapabilities(){} public static void preInit() { CapabilityManager.INSTANCE.register(IUUIDCapability.class, new IUUIDCapability.Storage(), new IUUIDCapability.Factory()); } } // the class bellow needs to add the capability to each player. In it I have identified a problem: I don't know how to handle the PlayerEvent.Clone public final class EventHandler { private EventHandler(){} @SubscribeEvent public void onPlayerClone(PlayerEvent.Clone event) { //TODO: don't know what to do here. } @SubscribeEvent public void onAttachCapabilityEntity(AttachCapabilitiesEvent.Entity event) { if(event.getEntity() instanceof EntityPlayer) { event.addCapability(new ResourceLocation(TheMod.MODID, "UUIDCapability"), UUIDCapabilityProvider); } } public static void init(FMLInitializationEvent event) { MinecraftForge.EVENT_BUS.register(new EventHandler()); } } // bellow is the rest. I don't know what is right and what is wrong here. My main problem is the Provider. I don't know how to implement it. public interface IUUIDCapability { UUID getUUID(); void setUUID(UUID uuid); public static final class Storage implements Capability.IStorage<IUUIDCapability> { @Override public NBTTagCompound writeNBT(Capability<IUUIDCapability> capability, IUUIDCapability instance, EnumFacing side) { NBTTagCompound tag = new NBTTagCompound(); tag.setLong("HI", instance.getUUID().getMostSignificantBits()); tag.setLong("LO", instance.getUUID().getLeastSignificantBits()); return tag; } @Override public void readNBT(Capability<IUUIDCapability> capability, IUUIDCapability instance, EnumFacing side, NBTBase nbt) { NBTTagCompound tag = (NBTTagCompound)nbt; long hi = tag.getLong("HI"); long low = tag.getLong("LO"); instance.setUUID(new UUID(hi, low)); } } public static final class Factory implements Callable<IUUIDCapability> { @Override public IUUIDCapability call() throws Exception { return new Implementation(); } } public static final class Implementation implements IUUIDCapability { private UUID uuid; public Implementation() { this.uuid = UUID.randomUUID(); } public Implementation(UUID uuid) { this.uuid = uuid; } @Override public UUID getUUID() { return uuid; } @Override public void setUUID(UUID uuid) { this.uuid = uuid; } } public static final class Provider implements ICapabilitySerializable<NBTTagCompound> { @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { return capability == ModCapabilities.UUIDCapability; } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if(capability == ModCapabilities.UUIDCapability) { //don't know how to get it. } } @Override public NBTTagCompound serializeNBT() { NBTTagCompound tag = new NBTTagCompound(); UUID uuid = null;//don't know where to get the UUID from tag.setLong("HI", uuid.getMostSignificantBits()); tag.setLong("LO", uuid.getLeastSignificantBits()); return tag; } @Override public void deserializeNBT(NBTTagCompound tag) { long hi = tag.getLong("HI"); long low = tag.getLong("LO"); UUID uuid = new UUID(hi, low);//should be setting something else, not a local variable } } } I can only instantiate an Implementation using the parameterless constructor once. Even though my immediate problem wouldn't break if I was to instantiate my hash more than once, it is an expensive operation and I would like if it would run just once (and only run again if I explicitly send a package for the client to do so). But I will need UUID s for certain TileEntities in my mod, so it is crucial that I only generate an UUID if something doesn't already have one, otherwise the whole thing will break.
  15. So, I'm thinking about my mod having those chests where your items go after you die, but I want an algorithm to place the chest correctly (meaning not inside lava or water or inside a wall, etc) Anyone has come up with the perfect algorithm for that? If so, please link it to me?
  16. I want to be able to grab the 0 sized stacks, I just don't want to be able to put them anywhere other than in a SlotFake.
  17. I was hoping for something way more exact than that... Anyway, I have a somewhat satisfactory Container, but it still has an issue (look at the comment on top of slotClickFake method): @Override public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player) { Slot slot= null; ItemStack stack = null; if(slotId > 0) { slot = (Slot) this.inventorySlots.get(slotId); stack = slot.getStack(); } else { slot = null; } if (slot instanceof SlotFake) { return slotClickFake((SlotFake)slot, dragType, clickTypeIn, player); } return super.slotClick(slotId, dragType, clickTypeIn, player); } //A SlotFake is basically a slot to mark things like filters (e.g only consider ItemStacks that are equal to the ItemStack in the fake slot). //when you are holding an ItemStack with your mouse, you set the fake slot to have a stack of 0 size equal to that item, //without affecting the ItemStack being held (You don't actually put anything in a SlotFake). //when you are not holding an ItemStack with your mouse, you pick up the zero sized ItemStack in the SlotFake (if the slot has one). //this 0 sized ItemStack is supposed to be valid for other SlotFakes, but, as it is, there is a minor nuissance: //you can put these 0 sized ItemStacks in your normal inventory. They disappear when clicked, but I wish this behaviour wouldn't exist. private ItemStack slotClickFake(SlotFake slot, int dragType, ClickType clickTypeIn, EntityPlayer player) { InventoryPlayer playerInv = player.inventory; ItemStack itemInHand = playerInv.getItemStack(); ItemStack itemInSlot = slot.getStack(); ItemStack result = itemInSlot != null ? itemInSlot.copy() : null; switch(clickTypeIn) { case CLONE: if(itemInSlot != null) { if(player.capabilities.isCreativeMode) { if(itemInHand != null && itemInHand.stackSize < itemInHand.getMaxStackSize() && ItemHandlerHelper.canItemStacksStack(itemInHand, itemInHand)) { itemInHand.stackSize = itemInHand.getMaxStackSize(); } else { slot.putStack(null); } } else { slot.putStack(null); } } break; case PICKUP: case PICKUP_ALL: case QUICK_CRAFT: case QUICK_MOVE: if(itemInHand != null && slot.isItemValid(itemInHand)) { ItemStack copy = itemInHand.copy(); slot.putStack(copy); result = copy; } else if(itemInSlot != null) { playerInv.setItemStack(itemInSlot); slot.putStack(null); } break; case SWAP://I don't know what to do in these cases break; case THROW: break; default: break; } return result; }
  18. Container.slotClick was what I meant to say. The method is too long and it has too many conditionals. I want to know the method's contract, meaning, what arguments the method expects and what are the results the caller can expect from the method: if the arguments set is A, invoking the method will have R as return and will cause S as a set of side effects. If anyone could tell me what is Container.slotClick contract I would really appreciate it.
  19. I learned a lot since I asked this question a couple of days ago. My GUI is almost working 100%. But I still feel I need to know exactly what each dragMode and clickType means and when [s]Slot.slotClick[/s] Container.slotClick is invoked with what parameters. For example, I notice the middle mouse buttom gives a dragMode of 2 and a clickType of CLONE , if you middle click a slot, but it gives a dragMode of 9 if you click and drag (with the same clickType of CLONE . Right clicking: dragMode of 1 and clickType of PICKUP , but sometimes dragMode of 5 and clickType of QUICK_CRAFT . I feel that if I knew what's going on with these parameters I would be able to implement my GUIs free of bugs. I can't be sure if my GUIs will always work as intended if I don't know this method's contract.
  20. I can't move items in the hotbar. I'm not even close to finish the GUI and the container, and I already ran into this problem... This is a super class implementing some basics. As it is, the players hotbar is part of the rectangle drawn. I will move it out so some GUIs can choose not to display the hotbar. This class apparently works fine (the entire drawing process works as intended. What is not working is the interaction: to pick a hot bar item and put it somewhere else. Also, the inventory seems to be duplicating stacks) public class GuiBasicContainer extends GuiContainer { /** * The {@code ResourceLocation} for the basic GUI elements */ private static final ResourceLocation texture = new ResourceLocation(TheMod.MODID, "textures/gui/gui_01.png"); private int windowX; private int windowY; public GuiBasicContainer(Container inventorySlotsIn) { super(inventorySlotsIn); } @Override protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(texture); windowX = (this.width - this.xSize) / 2; windowY = (this.height - this.ySize) / 2; this.drawTexturedModalRect(windowX, windowY, 0, 0, this.xSize, this.ySize); } protected void drawPlayerInventory(int relativeX, int relativeY) { drawTexturedModalRect(windowX + relativeX, windowY + relativeY, 0, 166, 161, 53); } } This is the GUI. As said before, it seems to work. public class GuiExtractor extends GuiBasicContainer { public GuiExtractor(ContainerExtractor inventorySlotsIn) { super(inventorySlotsIn); } @Override protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { super.drawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY); drawPlayerInventory(7, 83); } } Bellow is a class that is supposed to implement basic behaviours to be used by its derived classes. public abstract class ContainerBasic extends Container { protected final EntityPlayer player; protected int inventorySize; public ContainerBasic(EntityPlayer player, int inventorySize) { this.inventorySize = inventorySize; this.player = player; } public EntityPlayer getPlayer() { return player; } protected void addHotBar(int x, int y) { for(int i = 0; i < 9; ++i) { this.addSlotToContainer(new Slot(player.inventory, i, x + i * 18, y)); } } protected void addPlayerInventory(int x, int y) { for(int line = 0; line < 3; ++line) { for(int row = 0; row < 9; ++row) { int slotID = 9 + line * 9 + row; this.addSlotToContainer(new Slot(player.inventory, slotID, x + row * 18, y + line * 18)); } } } //I believe this is the problem. I don't understand the new set of arguments of this method. @Override public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player) { Slot slot; if(slotId > 0) { slot = (Slot) this.inventorySlots.get(slotId); } else { slot = null; } if (slot instanceof SlotFake) { return slotClickFake(slot, dragType, clickTypeIn, player); } return super.slotClick(slotId, dragType, clickTypeIn, player); } private ItemStack slotClickFake(Slot slot, int mouseButton, ClickType clickTypeIn, EntityPlayer player) { System.out.println("Clicked Fake Slot"); return null; } } I don't think this class is the problem: public class ContainerExtractor extends ContainerBasic { private final TileExtractor extractor; private final InventoryFake filter; public ContainerExtractor(EntityPlayer player, TileExtractor extractor) { super(player, 5); this.extractor = extractor; filter = extractor.getFilter(); addHotBar(8, 142); addPlayerInventory(8, 84); this.addSlotToContainer(new SlotItemHandler(filter, 0, 44, 53)); this.addSlotToContainer(new SlotItemHandler(filter, 1, 44 + 16, 53)); this.addSlotToContainer(new SlotItemHandler(filter, 2, 44 + 32, 53)); this.addSlotToContainer(new SlotItemHandler(filter, 3, 44 + 48, 53)); this.addSlotToContainer(new SlotItemHandler(filter, 4, 44 + 64, 53)); } @Override public boolean canInteractWith(EntityPlayer playerIn) { return extractor.isUsableByPlayer(playerIn); } } The whole GUI is a mess, things disapear, things get dupped, the game crashes, things can't be moved. It's all broken.
  21. I have a block with 7 different AABBs, depending on its state (it's a directional block), 6 of which are rotations of one another, and the seventh is a "not connected" state, with a different bounding box. My approach is to create all AABBs as static final fields. I wanted to know if there's helper method somewhere that rotates an AABB around an axis, so I don't need to input the hardcode in the coordinates of the several AABB static fields, thus, if I change one (for instance, if I decide to change the block model), I automatically change all.
×
×
  • Create New...

Important Information

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