Jump to content

coolAlias

Members
  • Posts

    2805
  • Joined

  • Last visited

Posts posted by coolAlias

  1. Typically people don't set out to 'make an API' for something they don't themselves intend to do. If you're not making an engine or some such that will need this API, chances are your API will not be very good for the simple fact that you aren't actually using it, so there will be lots of cases that you won't think of and it may not even provided the functionality modders will ultimately need.

     

    Also, consider that any mod already containing engines and such things are 99.9999% likely to continue doing it the way they always have been, even if your API turned out to be the best thing since canned meat. You should really think about who your target audience is, if any, for this API, and if it is even needed. Is there a compatibility problem you are trying to solve? How many 'standards' have already been developed, and what is their current usage? Do you think we need another one?

     

    I'm not trying to be a downer here, just posing some realistic questions you should ask yourself before undertaking what could prove to be a complete waste of your time, unless of course you are doing it to learn, in which case go right ahead.

     

    If you do go ahead with it, stuff like this:

    if(parent.region.getBlockState(pos).getBlock().equals(Blocks.iron_block)){
    

    is going to be a big problem. Sure, that's for a vanilla block, but wherever you can, you want to use interfaces. If you can't for whatever reason (such as for vanilla blocks), then you need to code in a way that lets you leave the public-facing API the same while still being able to tweak the internals.

     

    So, for example, the two if statements inside your code block there that check the blockstate, I would rewrite to be something like this:

    Block block = parent.region.getBlockState(pos).getBlock();
    Vector newForce = null;
    if (block instanceof IForceBlock) {
      newForce = ((IForceBlock) block).getForce(world, state, pos); // and whatever other parameters you need
    } else {
      newForce = getForceForVanillaBlock(state, pos); // and whatever other parameters you need
    }
    if (newForce != null) { // assuming both are handled the same
      // apply rotation, add force to collection, etc.
    }
    

    `getForceForVanillaBlock` could simply return a value from an immutable hashmap containing Block:Vector pairs that you populate at run-time; you could even allow mods to add their own blocks to this map via inter-mod messages, if you want.

     

    There are a lot of ways you can go about it, but always prefer abstraction and interfaces to hard-coding specific values.

  2. Are you sure you are getting a matching entry based on the entity's display name? What does your 'entities' array contain, and where did you get those names from?

     

    Using the display name is a bad idea anyway - what if I'm playing in Chinese? The server has no idea what language I'm using, so it's expecting 'entity.zombie.name' or something like that, but I'm sending it 殭屍? Not gonna work.

  3. When / where should the structure generate? During world gen, when using an item, or somewhere else? That makes a big difference in how you initiate the execution of your code, i.e. where you get your variables from, but once you have that, it literally is just #setBlock.

     

    You need to take the time to plan out your structure, though, and figure out where each block needs to be in relation to some point of origin, or you can get fancier and allow your structure to rotate - again, look at vanilla village structures for some fine examples of how to do it, and look especially at the various helper methods they have created e.g. fillWithBlocks - these make your life MUCH easier.

     

    Pseudocode example creating a cube filled with air, i.e. a basic 'room'-like structure, assuming you have written a method named 'fillWithBlocks' that sets every block within an area relative to a BlockPos to a specific block:

    // method you make sure is called from somewhere
    public boolean generate(World world, BlockPos pos) {
      fillWithBlocks(world, pos, 0, 0, 0, 5, 5, 5, Blocks.cobblestone);
      fillWithBlocks(world, pos, 1, 1, 1, 4, 4, 4, Blocks.air); // leave 1 block on all sides
      BlockPos doorPos = pos.east(2).up(); // 2 blocks to the east and 1 block up should be in the center of the south wall
      world.setBlockState(doorPos, Blocks.air.getDefaultState(), 2); // just an open-air doorway for now
      world.setBlockState(doorPos.up(), Blocks.air.getDefaultState(), 2);
    }
    

    Now all you have to do is figure out how to write a method 'fillWithBlocks' like I described (shouldn't be that difficult) or figure out how to use the vanilla StructureComponent class and methods, and you can have yourself a cobblestone hut.

     

    More complicated structures are obviously more difficult, even more so if you have blocks that care about metadata / rotation, or you want your structure to face in random directions, or you want to add loot and other things, but at the core of it all is world#setBlock (or, in 1.8, world#setBlockState).

  4. What I'm saying is: Can you have individual data for each instance of some class?

    That's what a class instance is - an object containing individual data, whether that data is in the form of integers, Strings, arrays, NBT, whatever, doesn't matter.

     

    Are your armor modules items? Store data in the ItemStack NBT like every other Item. Are they some other class? Store data directly in the class. Do you need an instance of that class to be readable and writable to NBT, e.g. for storage and retrieval from the ItemStack's NBT tag? Make some methods that do that and call them when needed, e.g. (in pseudo-code):

    ItemStack armor; // from whatever Item method you are in, e.g. onUpdate
    NBTTagList modules = getModulesListFromArmorStack(armor);
    for each (tag in modules) {
      ArmorModule module = ArmorModule.loadFromNBT(tag);
      // now you have your individual module instance with individual data loaded from NBT format
    }
    

    There's nothing magical about NBT - it's just a group of utility classes Minecraft uses to facilitate reading and writing data from and to disk, which also happens to be used frequently with ItemStacks because Items don't have any equivalent to TileEntities.

  5. There is also a pretty decent manual which, if you haven't already read it, is an excellent starting point and reference for later. Always RTFM, and in the case of git, you may want to read it multiple times and actually do the examples in it - make a throwaway project and branch, fork, clone, rebase, reset --hard, etc. the hell out of it until you are more comfortable with the git workflow.

     

    Speaking of workflows, you may want to search for 'Git workflow' and read a few articles on it, such as this. It's also covered in the manual, but it often helps to read several different perspectives.

     

    I've 'used' the Github desktop application for quite some time, but more often than not I'm unable to do what I want with it and end up on the command line.

     

    I switched (mostly) to SourceTree about half a year ago, and I can wholeheartedly second diesieben's recommendation of that software - it is miles and miles better than the Github app, and much more intuitive to use than the command line. I still use the command line fairly often for more complex operations, but SourceTree will usually get you by for about 70-85% of what you want to accomplish.

  6. It would be best not to change NBT data while the item is in use, e.g. by using a nextWorldTime for your cooldown instead of an actual counter.

     

    That said, it looks like you are also doing other things with the NBT which you may not be able to get away from. You could try setting the item back in use after the NBT is set, but it may still interrupt the action, particularly on the client-side when the new ItemStack is set.

     

    If the above doesn't work, you're going to have to come up with an alternate storage method such as using IExtendedEntityProperties to store data about the current item in use, update THAT data each tick instead of actually updating the item, and then (if you still need the data stored on the ItemStack, that is) write that data back to the item when #onPlayerStoppedUsing is called. It's a somewhat cumbersome workaround, but I can vouch for its effectiveness.

  7. It's caused because the blockstate is not always that of the block whose method was called, even though it should be.

     

    In my particular case, I was handling explosion resistance; for some reason vanilla adds the entity's eyeHeight:

    public float getExplosionResistance(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn)
        {
            return blockStateIn.getBlock().getExplosionResistance(worldIn, pos.add(0, getEyeHeight(), 0), this, explosionIn);
        }
    

    0.o Very weird.

     

    Anyway, the trick is to make sure the blockstate is actually for your block before trying to access any of your custom properties:

    IBlockState state = world.getBlockState(pos);
    if (state.getBlock() != this) {
      return;
    }
    // safe to access your custom properties now
    

  8. KeyInputEvent fires both when a key is pressed and when it is released, AND it fires for every single key on the keyboard, not just ones you may want to listen to.

     

    You need to check Keyboard.getEventKey() and compare it to the keyCodes of your KeyBindings, and also check Keyboard.getEventState() which is true when the key is pressed.

  9. Ok did some testing it turns out that when I press the key (only once) the method is called a LOT and keeps getting called over and over. What could make this happen?

    Hard to say without seeing your code, but I bet you're doing something like setting a field saying the key was pressed, then sending a packet as long as that is true.

     

    What are you using to handle key presses? KeyInputEvent or one of the TickEvents? Show your whole class (or at least every piece of it related to handling keys) and any related classes.

  10. @jeffryfisher I play lots of sounds in my mod and I've NEVER had to do anything nearly as complicated as you are making it to get them working. As diesiben and I have both said: use your proxy.

     

    Checking if the world is remote is usually sufficient, even when calling @SideOnly(Side.CLIENT) methods from within your class. If you try to create a reference to a client-only class, however, that's usually where you run into problems, and in your case it's the reference to the SoundHandler* that is ruining your day. Put it in your proxy, and then combine your TileEntity classes into one.

     

    * SideOnly classes cannot be inline-instantiated unless as members of a class that is only present on that side, i.e. also has the @SideOnly annotation. By instantiating it inline, you force the code to run on both sides, causing it to fail on the one where the class isn't present.

     

    This is the same issue people had when declaring multiple IIcons for their items and blocks, e.g. IIcon[] icons = new IIcon[4]; causes crashes, but just declaring it WITH the annotation, and then initializing it in the client-side only registerIcons method, works fine, and no one needed to create separate client- and server- versions of their Blocks and Items.

  11. It sounds like you are making things way more complicated than they need to be. Classes such as TileEntities that are included in both server and client jars can and do use @SideOnly(Side.CLIENT) methods, they just need to segregate them smartly.

     

    In your case, you can simply check if the world is remote before doing any of your client-side sound stuff, or if you're really worried about it, funnel the call through your proxy via a NON-static method. Static methods and variables are typically design flaws, so if you find yourself using them a lot (which it sounds like you are), you may want to rethink how you're doing things.

     

    Anyways, you most definitely should NOT have different classes for the same TileEntity depending on which side you expect it to be on. Not only is there no reason to ever do that, but Minecraft isn't set up to handle it. It's just asking for trouble.

  12. You don't need to specify a password unless you want to use an actual account (which will require an internet connection!) - I typically just supply the --username argument so my name is consistent and I don't have to worry if I'm connected or not.

     

    @OreCruncher A typical use case for wanting a consistent name is testing custom tames or some such where the owner/player is stored by UUID while offline and needs it to be the same next time they log in.

  13. You shouldn't need to check if the world is remote in your transferStackInSlot method - it can run the same on both sides.

     

    #onInventoryChanged is when you are supposed to iterate through all the stacks and replace any that have stack size of 0 with null.

     

    Calling super on an interface method (e.g. #onInventoryChanged) does absolutely nothing - interfaces do not provide implementations.

     

    You really shouldn't need to constantly mark your block for an update. Look at TileEntityFurnace and you'll see it never calls that method; you probably shouldn't, either. Use your Container properly for sending data to the client side via #detectAndSendChanges etc.

     

    In fact, the best advice I can give you is seriously look more closely at TileEntityFurnace and model your methods off of those. You are all over the place in there.

     

    Also, don't use FMLCommonHandler#getEffectiveSide when you already have a world object; world.isRemote gives you the side much more efficiently.

×
×
  • Create New...

Important Information

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