Jump to content

lyn mimistrobell

Members
  • Posts

    24
  • Joined

  • Last visited

Posts posted by lyn mimistrobell

  1. It seems there are a number events/methods used when a block is placed:

     

    canPlaceBlockOnSide

    getStateForPlacement

    onBlockPlacedBy

     

    Are there any more? Are these always called in this order? I have a block where I want to remember the TE against which the block was placed - let's call it targetTE - (so it can act as an interface to that targetTE). I implemented canPlaceBlockOnSide to verify that the user actually clicked a TE and not a regular block; e.g. it returns false if you try to place the block against dirt but true if you place the block (shift-click) against a chest. I implemented getStateForPlacement to rotate my block the correct way to face the targetTE. I believe both methods are called before the block is actually placed. I also implemented neighborChanged to break (and drop) my block if the targetTE is removed.

     

    Which method would be the best method to store the targetTE? onBlockPlacedBy doesn't provide the EnumFacing I need to find the block (and TE) the user clicked.

    Should I store the targetTE or determine it every time I need it (using my blocks PropertyDirection) - which may be every tick?

     

    I couldn't find any documentation on the order of events for placing a block - is there any?

  2. I did add more debugging and found the issue is with ItemStack.deserializeNBT which doesn't handle the NBT (created with ItemStack.serializeNBT) properly (created a separate topic for that).

     

    I use Eclipse Neon  2 and I know how to set breakpoints (unfortunately, they didn't help for this problem) - I didn't think use the property as watchpoint but I'll do that next time. Thanks.

  3. ItemStack:

        public NBTTagCompound serializeNBT()
        {
            NBTTagCompound ret = new NBTTagCompound();
            this.writeToNBT(ret);
            return ret;
        }
    
        public void deserializeNBT(NBTTagCompound nbt)
        {
            // TODO do this better while respecting new rules
            final ItemStack itemStack = new ItemStack(nbt);
            this.stackTagCompound = itemStack.stackTagCompound;
            this.capNBT = itemStack.capNBT;
        }

     

    So effectively, that's what those methods already do...

     

  4. I have an ItemStack with a single piece of e.g. coal; when I call itemStack.serializeNBT I get:

    {id:"minecraft:coal",Count:1b,Damage:0s}

     

    When I then try to deserialize the tag I get:

    1xtile.air@0

     

    I found that deserializeNBT calls new ItemStack(compound); it calls Item.getByNameOrId("minecraft:coal")  which returns 1xitem.coal@0. For some reason,  this fails and causes ItemStack to have 1xtile.air@0 :

    this.item = Item.getByNameOrId("minecraft:coal");

    This works, however:

    itemStack = new ItemStack(Item.getByNameOrId("minecraft:coal"));

     

    Am I doing something wrong calling

    nbt.setTag("mystack", itemStack.serializeNBT());

    and

    itemStack.deserializeNBT(tag.getTag("mystack"));

    ?

     

  5. Hi,

     

    For my mod I created a custom Capability that handles fuel burning (initial burn time, remaining burn time, etc.):

    Spoiler
    
    package net.mimistrobell.lyn.example.capabilities;
    
    import net.mimistrobell.lyn.example.Logger;
    import net.minecraft.init.Items;
    import net.minecraft.item.ItemStack;
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.tileentity.TileEntityFurnace;
    import net.minecraftforge.common.util.INBTSerializable;
    
    public class FurnaceGeneratorHandler implements IFurnaceHandler, INBTSerializable<NBTTagCompound> {
    
      /**
       * For items with a vanilla burn time of 2000 ticks or more the initial burn time is set to 200
       * ticks, otherwise the initial burn time is set to 60 ticks.
       */
      private static final int VANILLA_BURNTIME_THRESHHOLD = 2000;
      private static final int BURN_TIME_LOW = 60;
      private static final int BURN_TIME_HIGH = 200;
    
    
    
      /** The current fuel item */
      private static final String NBT_FUEL_ITEM = "fuelItem";
    
      private ItemStack fuelItem = ItemStack.EMPTY;
    
    
    
      /** The remaining burn time in ticks for the current fuel item. */
      private static final String NBT_REMAINING_BURN_TIME = "remainingBurnTime";
    
      private int remainingBurnTime = 0;
    
    
      /**
       * Returns the current fuel item.
       * 
       * @return The fuel item
       */
      @Override
      public ItemStack getFuelItem() {
        return this.fuelItem;
      }
    
      /**
       * Sets the fuel item, only fuel items or buckets are accepted and only if there is no current
       * fuel item.
       * 
       * @param fuelItemIn The ItemStack with the fuelItem.
       */
      @Override
      public void setFuelItem(final ItemStack fuelItemIn) {
        Logger.info("fuelItem: {}", fuelItemIn);
        if (!this.fuelItem.isEmpty() || fuelItemIn.isEmpty())
          return;
        if (!TileEntityFurnace.isItemFuel(fuelItemIn) && fuelItemIn.getItem() != Items.BUCKET)
          return;
    
        this.fuelItem = fuelItemIn.copy();
        this.fuelItem.setCount(1);
        this.remainingBurnTime = getInitialBurnTime();
        Logger.info("this.fuelItem: {}, initialBurnTime:{}", this.fuelItem, getInitialBurnTime());
      }
    
    
    
      /**
       * Returns the initial burn time in ticks for the fuel item.
       * 
       * @return The initial burn time
       */
      @Override
      public int getInitialBurnTime() {
        if (this.fuelItem.isEmpty())
          return 0;
    
        final int vanillaBurnTime = TileEntityFurnace.getItemBurnTime(this.fuelItem);
        return vanillaBurnTime < VANILLA_BURNTIME_THRESHHOLD ? BURN_TIME_LOW : BURN_TIME_HIGH;
      }
    
      /**
       * Returns the amount of Forge Energy generated per tick.
       * 
       * @return FE/t
       */
      public int getEnergyPerTick() {
        //Logger.info("fuelItem: {}", this.fuelItem);
        if (this.fuelItem.isEmpty())
          return 0;
    
        final int vanillaBurnTime = TileEntityFurnace.getItemBurnTime(this.fuelItem);
        final int energyPerTick = Math.round((float) vanillaBurnTime / (float) getInitialBurnTime());
        Logger.info("energyPerTick: {}, vanillaBurnTime: {}, initialBurnTime: {}", energyPerTick, vanillaBurnTime, getInitialBurnTime());
        return Math.round((float) vanillaBurnTime / (float) getInitialBurnTime());
      }
    
    
    
      /**
       * Returns true if there is a fuel item and there is burn time remaining.
       * 
       * @return True if burning
       */
      @Override
      public boolean isBurning() {
        return this.remainingBurnTime > 0;
      }
    
      /**
       * Returns the remaining burn time in ticks for the current fuel item.
       * 
       * @return The remaining burn time
       */
      @Override
      public int getRemainingBurnTime() {
        return this.remainingBurnTime;
      }
    
      /**
       * Decreases the remaining burn time with 1 tick.
       */
      public void decreaseRemainingBurnTime() {
        this.remainingBurnTime = Math.max(0, this.remainingBurnTime - 1);
        if (this.remainingBurnTime == 0) {
          Logger.info("Finished burning");
          this.fuelItem = ItemStack.EMPTY;
        }
      }
    
      /**
       * Returns the remaining burn time relative to the initial burn time; 0 is no time, 1 = full time.
       * 
       * @return The relative remaining burn time
       */
      public float getRelativeRemainingBurnTime() {
        if (this.remainingBurnTime == 0)
          return 0.0f;
    
        if (getInitialBurnTime() == 1)
          return 1.0f;
    
        return (float) this.remainingBurnTime / (float) getInitialBurnTime();
    
      }
    
      /**
       * Returns the spent burn time relative to the initial burn time; 0 is no time, 1 = full time.
       * 
       * @return The relative spent burn time
       */
      public float getRelativeSpentBurnTime() {
        return 1.0f - getRelativeRemainingBurnTime();
      }
    
    
    
      /**
       * Returns the properties of this Capability as Named Binary Tag.
       * 
       * {@see INBTSerializable}
       * 
       * @return The NBT
       */
      @Override
      public NBTTagCompound serializeNBT() {
        Logger.info("this.fuelItem={}", this.fuelItem);
        NBTTagCompound tag = new NBTTagCompound();
        tag.setTag(NBT_FUEL_ITEM, this.fuelItem.serializeNBT());
        tag.setInteger(NBT_REMAINING_BURN_TIME, this.remainingBurnTime);
        Logger.info("tag={}", tag);
        return tag;
      }
    
      /**
       * Sets the properties of this Capability from the Named Binary Tag.
       * 
       * {@see INBTSerializable}
       * 
       * @param tag The NBT
       */
      @Override
      public void deserializeNBT(NBTTagCompound tag) {
        Logger.info("tag={}", tag);
        if (tag.hasKey(NBT_FUEL_ITEM))
          this.fuelItem.deserializeNBT((NBTTagCompound) tag.getTag(NBT_FUEL_ITEM));
        Logger.info("this.fuelItem={}", this.fuelItem);
        this.remainingBurnTime = tag.getInteger(NBT_REMAINING_BURN_TIME);
      }
    
    }

     

     

    My TE calls setFuelItem with an ItemStack that has coal; the log shows that the coal was accepted:

    [18:32:02] [Server thread/INFO] [RadioTools]: [TileEntityFurnaceGenerator.update] fuelItemStack: 1xtile.blockCoal@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.setFuelItem] fuelItem: 1xtile.blockCoal@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.setFuelItem] this.fuelItem: 1xtile.blockCoal@0, initialBurnTime:200

     

    Next, sendBlockEntity calls TE.getUpdatePacket which calls TE.getUpdateTag which tries to get the NBT of my Handler, but for some reason this.fuelItem was reset:

    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] this.fuelItem=0xtile.air@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] tag={remainingBurnTime:200,fuelItem:{id:"minecraft:air",Count:0b,Damage:0s}}

     

    The only places where I expect the fuel to be updated are setFuelItem and decreaseRemainingBurnTime when it finished burning but that doesn't happen until 10 seconds later:

    [18:32:12] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.decreaseRemainingBurnTime] Finished burning

     

    I'm probably missing something obvious but I right now I'm blinded and I can't find it...

    The only other logging was from the client which (of course) deserialized a tag with fuelItem air; deserializeNBT is never called on the server.

     

    Full log:

    Spoiler
    
    [18:32:02] [Server thread/INFO] [RadioTools]: [TileEntityFurnaceGenerator.update] fuelItemStack: 1xtile.blockCoal@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.setFuelItem] fuelItem: 1xtile.blockCoal@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.setFuelItem] this.fuelItem: 1xtile.blockCoal@0, initialBurnTime:200
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] this.fuelItem=0xtile.air@0
    [18:32:02] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] tag={remainingBurnTime:200,fuelItem:{id:"minecraft:air",Count:0b,Damage:0s}}
    [18:32:02] [main/INFO] [RadioTools]: [FurnaceGeneratorHandler.deserializeNBT] tag={remainingBurnTime:200,fuelItem:{id:"minecraft:air",Count:0b,Damage:0s}}
    [18:32:02] [main/INFO] [RadioTools]: [FurnaceGeneratorHandler.deserializeNBT] fuelItem=1xtile.air@0
    [18:32:12] [main/INFO] [RadioTools]: [FurnaceGeneratorHandler.decreaseRemainingBurnTime] Finished burning
    [18:32:12] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.decreaseRemainingBurnTime] Finished burning
    [18:32:12] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] this.fuelItem=1xtile.air@0
    [18:32:12] [Server thread/INFO] [RadioTools]: [FurnaceGeneratorHandler.serializeNBT] tag={remainingBurnTime:0,fuelItem:{id:"minecraft:air",Count:1b,Damage:0s}}
    [18:32:12] [main/INFO] [RadioTools]: [FurnaceGeneratorHandler.deserializeNBT] tag={remainingBurnTime:0,fuelItem:{id:"minecraft:air",Count:1b,Damage:0s}}
    [18:32:12] [main/INFO] [RadioTools]: [FurnaceGeneratorHandler.deserializeNBT] fuelItem=1xtile.air@0

     

     

    Anybody with good eyes? :)

     

    Linda

     

  6. Hi,

     

    It's my understanding that one of the key benefits of the Capability system is that other mods can provide the Capability you designed - is that right?

     

    So far I've discovered that Forge provides  Capability<IAnimationStateMachine>, Capability<IEnergyStorage>, Capability<IFluidHandler>, Capability<IFluidHandlerItem> and Capability<IItemHandler>. I suspect many people have created their own Capabilities by now and I'm sure some (almost) duplicate Capabilities exist out there.

     

    Perhaps we should be sharing and documenting Capabilities rather than each invent our own. For example, I created Capability<IFuelHandler> which provides basic furnace functionalities related to remainingBurnTime, initialBurnTime, etc. I can imagine a number of implementations; e.g. one that provides the vanilla Furnace implementation, one (that I made) that provides higher burnrates (with more FE/t) for "higher" fuels (coke, buckets of lava, etc.), etc.

     

    Is there any kind of sharing for Minecraft (other than bitbucket) "snippets" like Capabilities, GUI helpers (haven't we all made one?), etc.?

     

    Linda

    • Like 1
  7. I've implemented:
     - getTileEntityClass
     - getTileEntity
     - hasTileEntity (returns true)
     - createTileEntity(world, state) (though deprecated)
     - createTileEntity(world, meta) (though deprecated)

    My Block and TE class have a constructor without parameters.

    One thing that bugs me is that all registrations happen through event.getRegistry; except for the TE registration which uses GameRegistry.registerTileEntity - seems inconsistent.

  8. 2 hours ago, Draco18s said:

    Show your code.

    I provided all the relevant code... In the log I see it does:

     - preInit (on Client)

     - RegistrationHandler.registerBlocks (on Client)

     - GameRegistry.registerTileEntity (on Client)

     - RegistrationHandler.registerItems (on Client)

     - RegistrationHandler.registerItems (on Client, for the models)

     - init (on Client)

     - postInit (on Client)

     

    Then when I right-click my block:

     - onBlockActivated (on Server)

     - TE.onLoad (on Server)

     - TE.onLoad (on Client)

     

    Anything unexpected here? The  block itself is registered on RegistrationHandler.registerBlocks; is that a problem?

     

     

  9. I'm having the same issue and I'm wondering about the different events that are being used.

     

    My main class implements:

      @Mod.EventHandler
      public void preInit(FMLPreInitializationEvent event)
      @Mod.EventHandler
      public void init(FMLInitializationEvent event)
      @Mod.EventHandler
      public void postInit(FMLPostInitializationEvent event)

     

    It also defines this class

      @Mod.EventBusSubscriber
      public static class RegistrationHandler

    implementing these methods:

        @SubscribeEvent
        public static void registerItems(RegistryEvent.Register<Item> event)
       @SubscribeEvent
        public static void registerItems(ModelRegistryEvent event)
        @SubscribeEvent
        public static void registerBlocks(RegistryEvent.Register<Block> event)

     

    I got this from some tutorials and I was under the impression that the RegistrationHandler was the new preferred implementation.

     

    I tried this in both the preInit and the registerBlocks events:

    GameRegistry.registerTileEntity(MyTileEntity.class, "mymod:my_tile_entity")

    And still my TE doesn't trigger onLoad until I right-click the block to open the GUI. Previously I had seen some "Skipping BlockEntity with id " (and no id was printed there) which leads me to believe my TE's weren't storing their NBT but after moving the registryTileEntity to preInit that error is gone.

     

    1. What is the preferred implementation for the registration events; in which event should I register the Block and the TE?

    2. Why is my TE not loaded on world load?

     

     

  10. I want an ITickable class to keep track of and regularly update multiple blocks and possibly items; serverside. It shouldn't be dependent on things like chunk loading and actually I actually don't event want an inworld block for it.

     

    Is there a recommended way to imlement this? Can I use a TileEntity and can I just initialize this in my mod postInit? Would it be able to store NBT data?

  11. 42 minutes ago, diesieben07 said:

    Unless you want other mods to interact with this functionality of your tile entity, you do not need to make a capability for it.

    I found it to be the best way without the need to expose new methods (requiring me to cast the TE to my own TE class). I didn't feel like doing this just for the IItemHandler issue but I suspect a Progress capability can will be useful more often.

  12. I was able to get it working with the facing option (of course, it restricts me to 6 IItemHandlers but for now I just needed 3 anyway). I feel it's a hack/workaround and not a proper solution tho...

     

    I did create a custom capability for my furnace progress. The vanilla furnace uses IInventory which offers getField and setField, I feel that's missing from the standard capabilities; perhaps I'll create a generic IStorage<IProperties> capability to offer generic properties handling to any TE...

     

    For now, I created a Progress capability (with minValue, maxValue, value and a method to get actual progress) that my TE sets to 0, initialBurnTime, remainingBurnTime and from which my Gui calls getProgress(34) (my progress bar is 34 pixels).

    Am I correct that every custom capability needs all this?

     - Capability interface (ICapabilityProgress)

     - Capability class (CapabilityProgress implements ICapabilityProgress)

     - Serializer (CapabilityProgressProvider implements ICapabilitySerializable<NBTBase>)

     - Storage class (CapabilityProgressStorage implements IStorage<ICapabilityProgress>)

     

  13. I'm building a TileEntity which I want to have multiple ItemStackHandler for different slots.

    I'm trying to add capability constants to use in getCapability:

      @CapabilityInject(IItemHandler.class)
      public static Capability<IItemHandler> CAPABILITY_SET1 = null;
    
      @CapabilityInject(IItemHandler.class)
      public static Capability<IItemHandler> CAPABILITY_SET2 = null;

     

    On runtime, both CAPABILITY_SET1 and CAPABILITY_SET2 reference the same Capability object so I can't distinguish between them.

    I tried this:

      @CapabilityInject(IItemHandler1.class)
      public static Capability<IItemHandler1> CAPABILITY_SET1 = null;
    
      @CapabilityInject(IItemHandler2.class)
      public static Capability<IItemHandler2> CAPABILITY_SET2 = null;

    Where both IItemHandler1 and IItemHandler2 are empty interfaces with extends IItemHandler. On runtime, CAPABILITY_SET1 and CAPABILITY_SET2 remain null.

     

    Do I really have to build a custom Capability with custom writeNBT, custom readNBT, custom Factory, custom implementation, etc.?

  14. I noticed and I already had a similar approach; I have like a BlockMachineBase, ContainerMachineBase, GuiMachineBase (extends GuiInventoryBase which extends GuiContainer) and TileEntityMachine base and each machine extends that, e.g. BlockFurnaceGenerator, ContainerFurnaceGenerator, etc.

    But you don't have Client Gui -> Server Container communication, as far as I could tell.

  15. I've refactored my blocks to use IItemHandler(I actually used your ReasonableRealism as an example/guide, Draco18s, thanks for that), this also meant I lost the IIventory's setField and getField.  So, back to my original question:

     

    How do I communicate from Gui  (Client) to the Container (Server) which needs this info for transferStackInSlot?

     

    OOTB the Container seems to use updateProgressBar (called from IContainerListener.sendWindowProperty, looks like) as a setField but it's marked as @SideOnly(Side.CLIENT).

     

    1) I could again call a TileEntity setter method from an IMessageHandler and call a TileEntity getter method from my Container. 

    2) I could create a custom method in my Container and use player.openContainer to get the Container instance on the server; I'm not sure that's the best approach tho

    3) Perhaps I can create a custom INetHandler Packet with listener, seems a bit overengineered.

     

    Any suggestions? I looked at ContainerModChest but it just calls TileEntity methods from the Container... !?

     

    For now, I've changed updateProgressBar to be both server and client. Gui calls client side container updateProgressBar and uses  SimpleNetworkWrapper.sendToServer with a custom IMessage; its handler uses player.openContainer updateProgressBar to set the same on the server. Any feedback is appreciated.

     

  16. I've been looking for examples that use IItemHandler capability (nothing in the decompiled source seems to use this) with a Container and GUI. I believe I understand how it can replace IInventory tho I currently fail to see the benefits.

     

    More importantly: How do I achieve my multiple-gui solution through IItemHandler?

  17. I implemented as follows (my TE's also implements IInventory which has getField/setField methods):

    The class MessageUpdateTileEntityField implements IMessage, constructor like: MessageUpdateTileEntityField(TileEntity te, int field, int value) which creates a message with TE's pos, dimension, field id and value.

     

    On click of the button in the GUI it calls te.setField(1, id) (of course, this is CLIENT), in the TE class, setField triggers a simpleNetworkWrapper.sendToServer call with the new MessageUpdateTileEntityField(this, 1, id).

    The internal IMessageHandler class of MessageUpdateTileEntityField, on receiving a message, finds the TE based on pos/dimension (world.getTileEntity(pos)) and calls setField(fieldId, value) (this is on the SERVER).

    My Container class has a reference to the TE, it overrides transferStackInSlot and calls te.getField(1) to get the GUI ID and adjust its behaviour.

    With this, there's no need to reinitialize the Container and TE to open the new GUI.

     

    I based the setup of my TE, Container and GUI classes on the vanilla Furnace.

     

    Edit: Oh, and this is working... worth mentioning ?

     

  18. Hi,

     

    I'm building a machine with a GUI that has multiple screens with different slots on each screen. That means the Container method transferStackInSlot needs to behave differently for the different screens.  My approach so far is:

     - GUI calls TileEntity.setField(1, guiId) to set the ID on switching

     - Container calls TileEntity.getField(1) to get the ID in transferStackInSlot

    Unfortunately, the GUI call happens only on Client, the Container call happens on both Server and Client but of course only the Client  gets the updated value.

     

    I was looking at an IMessage example which should be triggered by TileEntity.setField in case of !world.isRemote which would send the ID to the Server TileEntity; is that the best approach? Any other suggestions on how to achieve this?

     

    Lyn

×
×
  • Create New...

Important Information

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