Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. Okay, that seems to have solved it. Now calls updateContainingBlockInfo() as expected. I'm not sure why it worked for me in 1.7.10 though if you say it works the same. Oh well. Thanks!
  2. Okay, I have a custom crop that I do something special when it finishes growing. So in 1.7.10 I had a tile entity associated with the block and the tile entity checked for finished growing and performed the code. The problem I'm having in 1.8 is that in the grow() method of my crop block class I use the setBlockState() method to update the growth. This is copied from the vanilla BlockCrop. However, I noticed that the tile entity gets recreated every time the setBlockState() method is called. This is a problem since I have other fields in the tile entity that are supposed to be preserved over time. I am determining that it is recreating a tile entity by putting a console statement in the createTileEntity() method in my block class. The weird thing is that the Block code seems to be correct -- it checks for a TileEntity before creating it: if (block.hasTileEntity(p_177436_2_)) { tileentity = this.func_177424_a(p_177436_1_, Chunk.EnumCreateEntityType.CHECK); if (tileentity == null) { tileentity = block.createTileEntity(this.worldObj, p_177436_2_); this.worldObj.setTileEntity(p_177436_1_, tileentity); } if (tileentity != null) { tileentity.updateContainingBlockInfo(); } } So my question is -- how can you change a block's state without regenerating the tile entity?
  3. I recently struggled through this myself. It is annoying that the game will show default textures without any console error or warning. Makes it difficult to debug. But the main thing I found is that you have to be very careful with the naming and cross-referencing, as well as the actual assets locations. Can you show what the actual names of all your files are, a picture of your asset folder structure, and also the code where you register the block item renderer?
  4. EntityLiving is for "living" things like mobs and cows. EntityThrowable doesn't extend EntityLiving so you can't cast it as such.
  5. Okay, I guess I'll use a hybrid of the IEntityAdditionalSpawnData (for the initialization) and custom packets for the additional sync (I can probably safely assume that entity will be spawned later in the game) and maybe put in some graceful handling of the case where entity isn't found. I already tried this on one entity and it seemed I was able to get it working. Will keep looking at it.
  6. Thanks, that thread has some great info and is exactly the sort of thing I was running into.
  7. Okay, that would explain it. Is the IEntityAdditionalSpawnData synced continuously or just during spawn? If it is synced continuously (my custom field values can change during game) I'll try the additional spawn data approach. I haven't been using that because I have had for a long time a successful similar implementation of my own (NBT on each side for saving/loading custom fields plus custom packet to sync) but with this new issue of synchronization the additional spawn data seems much preferred.
  8. Okay, I solved it. In order to avoid cut and paste types I used unlocalized name in several places (like when registering the renderers) but forgot that the getUnlocalizedName() method prepends a "tile." or "item." to the name -- I hate it when setter and getter methods are not reciprocal! Anyway, by adding .substring(5) to each name it strips that off and now it works!
  9. Sure enough I put a debug statement in the constructor of the entity. I found that the constructor fires on the server and then the packet is created on server and received on client before the constructor is called on the client. I actually found that occasionally the constructor on client was called before the received messages and in those cases no NPE crash happened. So I guess I can no longer count on the spawning of an entity to immediately send a spawn packet. I guess I'll play around with adding some delay, but I'd like to ideally have the ordering logically controlled. I suppose I could create some sort of message queue on the client where if an entity isn't found then it is retried each tick until it is found (or times out).
  10. I've had a solid packet system based on SimpleNetworkWrapper working for a while. One thing I do, is whenever I spawn an entity (on server) I also send a packet to sync up the values of some custom fields in that entity. In the message, I include the entity ID based on the getEntityId() method for entities. In the past, this seemed to be the way to reference the same entity on both client and server, so when client receives the message it looks up the entity using the following code: public static Entity getEntityByID(int entityID, World world) { for(Object o: world.getLoadedEntityList()) { if(((Entity)o).getEntityId() == entityID) { // DEBUG // System.out.println("Found the entity"); return ((Entity)o); } } return null; } This worked great in 1.7.10 and it always found the entity. Now I'm updating to 1.8 and getting a null pointer exception when trying to create an entity using the received ID. Sure enough, at that time there is no such ID on the client. So I'm wondering, did the timing of the spawning of an entity on client change such that if I send a packet referencing the entity immediately after creating it on the server it doesn't exist yet? Basically I wondering if my packet gets sent before the built-in packets that sync the client entities. Alternatively, has the entity ID scheme changed in 1.8 such that the numbers no longer match on client and server?
  11. In 1.7.10 I think the function is called func_145780_a(). Override that to return nothing or a different sound if you wish.
  12. If it is your own custom block, you just need to override those methods. Like return true for the isFlammable() method. Basically just copy the two methods from a flammable block into your block's code.
  13. Okay, I expect there is a simple typo somewhere, but basically the loader isn't finding any of my block or item model definitions. Here is the error report: Now I think I have my JSONs in correct assets directories: And I think I understand the idea -- the blockstates maps the variants to the models based on BlockState properties. For the magicbeanstalk.json I have the following (note the repetition of the stages is intentional): { "variants": { "age=0": { "model": "magicbeans:magicbeanstalk_stage_0" }, "age=1": { "model": "magicbeans:magicbeanstalk_stage_0" }, "age=2": { "model": "magicbeans:magicbeanstalk_stage_1" }, "age=3": { "model": "magicbeans:magicbeanstalk_stage_1" }, "age=4": { "model": "magicbeans:magicbeanstalk_stage_2" }, "age=5": { "model": "magicbeans:magicbeanstalk_stage_2" }, "age=6": { "model": "magicbeans:magicbeanstalk_stage_3" }, "age=7": { "model": "magicbeans:magicbeanstalk_stage_3" }, } } And for magicbeanstalk_stage_0.json I have: { "parent": "block/cross", "textures": { "cross": "magicbeans:blocks/magicbeanstalk_stage_0" } } And I think the actual textures are matching in name and are in right location: And my modid I think is correct and consistent throughout -- here is the code for MODID in my main class: @Mod(modid = MagicBeans.MODID, name = MagicBeans.MODNAME, version = MagicBeans.MODVERSION, guiFactory = "com.blogspot.jabelarminecraft."+MagicBeans.MODID+".gui.GuiFactoryMagicBeans") public class MagicBeans { public static final String MODID = "magicbeans"; public static final String MODNAME = "Magic Beans"; public static final String MODVERSION = "1.0.0"; Lastly, I think the block has proper property "age" set up with values 0 to 7. Here is the relevant code from the block's class: public class BlockCropMagicBeans extends BlockBush implements IGrowable { // @SideOnly(Side.CLIENT) // protected IIcon[] iconArray; public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 7); protected boolean isFullyGrown = false; public BlockCropMagicBeans() { super(); // Basic block setup setTickRandomly(true); setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); setCreativeTab((CreativeTabs)null); setHardness(0.0F); setStepSound(soundTypeGrass); disableStats(); setDefaultState(blockState.getBaseState().withProperty(AGE, Integer.valueOf(0))); } The launcher is obviously reading my variants JSON in the blockstates because the error log shows it looking for the right assets associated with the property. So what am I doing wrong? I know it is probably a simple typo somewhere, but I've been staring at this all week...
  14. For backwards compatibility the metadata is mapped the same. And yes, for storage using the metadata is perfectly fine. Keep it the same. Metadata for storage is fine, that's what it's there for. Thanks, I was hoping I could keep using it that way.
  15. I understand that for new mods I would do it in a 1.8-based way. But it seems that upgrading a metadata-based mod would be easier using the getMetaFromState() and getStateFromMeta() functions. My specific problem is that I have a custom structure scheme and the way I had it in 1.7.10 was that I have a text file for the structure and it includes the blocks and their metadata value. So I think the easiest would be to still read that metadata and get it converted to state, although I guess I'm not confident that the metadata values are even mapped the same. I will have to embark on a serious conversion effort if I change the code to have the state saved in the text file. In fact, I'm not quite sure how to do that since for each type of block there may be different properties that have to be looked up. In other words, with metadata in 1.7.10 there was a consistent way for every block to get/set the metadata, but with the change to states I think there is a lot more involved. So I guess I'm looking for a shortcut to do the following -- I have 1.7.10 custom text file holding blocks and associated metadata values. I'd like to read that into 1.8 and place blocks based on that info. Ideally, I'd like to use the same text file as I did in 1.7.10.
  16. Use getDefaultState. Sorry, I meant to ask: getBlockState() versus getDefaultState()? Looking through the code, I think some of my confusion is that some of these are World functions, some are Block functions, and some are IBlockState functions. getBlockState() is a block function, but setBlockState() is a world function, and getBaseState() is an IBlockState function. I guess I'll study the vanilla code further to see if I can really understand proper practice. I think: In custom block class: - create a final static property to represent the metadata. - in constructor of block use setDefaultState(). - make methods for getMetaFromState() and getStateFromMeta() using the property - if you want to access metadata within the block code, do something like: getMetaFromState(parWorld.getBlockState(parBlockPos)) In main class: - instantiate the block once. you don't need to set default value if you have that in the block contructor In other classes: - if you have world instance, use the getBlockState(parBlockPos) method to get the state. - If you want to test what kind of block is in the position, use the state.getBlock() method. - If you want to test the metadata of the block in the position, use the state.getValue() method. Alternatively, use the block's getMetaFromState() method.
  17. Okay, so I'm still a bit confused then. Is default state the one I should be setting when I want to change "meta data"? If so, then they are badly named -- "default" doesn't seem like it should be "current". Basically, when should I use each of the following: - setBlockState() versus setDefaultState()? - getBaseState() versus getDefaultState()?
  18. Okay, I'm finally getting around to updating my 1.7.10 mods to 1.8. I am pretty good at using the IDE to fix any errors created by the changes, but in the case of blocks I've got a couple questions. I've looked at a number of tutorials, but haven't quite figured out a couple things. If I want to add a block in the world, we use the setBlockState() method, right? But that method doesn't actually take a block type parameter but rather just an IBlockState parameter so the actual block placed will come from the IBlockState? So if I'm trying to create a new block, it means that I should retrieve the default state of the block and pass that into setBlockState()? So something like this would be correct and sufficient to create a block one position above? worldObj.setBlockState(pos.add(0, 1, 0), MagicBeans.blockMagicBeanStalk.getDefaultState()); Next, I have a question about getBaseState(): Is getBaseState() just the current state? (The term "base" is a bit confusing.) Next, I have a block that had custom metadata. So I assume that has to be converted to state properties. Is this the proper steps for setting this up? 1. Create a property, possibly enumerated. Something like: public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 7); 2. Everywhere I used to set metadata with notify, I just have to do something like: setDefaultState(blockState.getBaseState().withProperty(AGE, Integer.valueOf(5))); 3. Everywhere I used to get metadata, I just have to use something like: blockState.getBaseState().getValue(AGE); I guess the property can be static and final because in Minecraft there is actually only one instance of each block and that is mapped into the positions and states within the world?
  19. If you can solve it on your own, no. If you're asking for help, yes. Any time you do something unusual, you need to have good understanding of how to do it, plus it needs to be worth the effort and risk. Remember that these names are strings, so if you have a lot of them that are similar, you can probably code up some way to created the string for you. Or you can just create them all once in a txt file and have that as a resource. Basically, is the time it takes to type up all your names really longer than the time to code and debug an alternate solution?
  20. Okay, I solved it. Apparently I had already overridden the getEyeHeight() and was multiplying it by a scale factor I was using in the rendering (I was planning to have the bounding box set by the model's scale factor but never got around to properly adjusting that). There was some blocks higher up above the entity, which I guess after scaling caused the inWall damage. But I guess the thing I learned was that the inWall damage is only calculated in the block at the eye height.
  21. Okay, I have a custom monster placer (spawn egg) which pretty much just extends/copies the vanilla monster placer code. I have a custom entity that is a giant, so the bounding box size is set to setSize(1.0F, 4.5F). I found that when placing the giant right next to something, it will start to take damage of DamageSource.inWall (I check this by printing out console statements in the LivingAttackEvent) until it dies. However, if visually the bounding box doesn't really look like it is in the wall. See pic: http://i.imgur.com/L2GyMHK.png So I followed the code for the inWall damage and it is caused by a check to isEntityInsideOpaqueBlock() which has kinda interesting code as follows: public boolean isEntityInsideOpaqueBlock() { for (int i = 0; i < 8; ++i) { float f = ((float)((i >> 0) % 2) - 0.5F) * this.width * 0.8F; float f1 = ((float)((i >> 1) % 2) - 0.5F) * 0.1F; float f2 = ((float)((i >> 2) % 2) - 0.5F) * this.width * 0.8F; int j = MathHelper.floor_double(this.posX + (double)f); int k = MathHelper.floor_double(this.posY + (double)this.getEyeHeight() + (double)f1); int l = MathHelper.floor_double(this.posZ + (double)f2); if (this.worldObj.getBlock(j, k, l).isNormalCube()) { return true; } } return false; } I noticed that it includes a getEyeHeight() call and in my custom entity I didn't override that so it is inherited from EntityLivingBase which sets eye height to 85% of the bounding box height (which is 4.5F, so 85% is 3.82F) . I tried reducing the height of the bounding box and voila! the damage stopped if I had the height at at 3.5F. I can't quite wrap my head around the bit shifting operations though. But I'm suspicious about it. It only counts to 7, gets shifted by 1 (which I think is equivalent to divide by 2), then does a % (modulus) with 2 which gives the remainder after dividing by 2 again. If I step through the iteration of the for loop, the float f1 = ((float)((i >> 1) % 2) - 0.5F) * 0.1F should be like this: i = 0, then f1 = -0.05F i = 1, then f1 = -0.05F i = 2, then f1 = -0.05F i = 3, then f1 = -0.05F i = 4, then f1 = -0.05F i = 5, then f1 = -0.05F i = 6, then f1 = 0.05F i = 7, then f1 = 0.05F Not sure what it means, but I guess it is just a clever way of checking just a little bit around the eye height location. Other than that, I'm stumped. I'm going to check to see if for some reason the isNormalCube() function is actually returning true in the eye height location. I can work around this by just canceling the damage from inWall in the LivingHurtEvent, but I'd like to understand what's happening. I'm also going to play around with overriding the getEyeHeight() to see if my hunch is correct that it is related to the issue.
  22. Like I said, it is a tradeoff. By the time you check to see if the entity is in distance to enable/disable the AI, you're already doing some processing. On a modern computer, only really crazy stuff can cause performance hits. Think about all the checks that Minecraft is already doing every tick. Most mods are just a drop in the bucket. Note that I think you can set entities to despawn if they're out of range for a long time, and the chunk loading and unloading also help manage the overall active portion of the world. This is the way Minecraft naturally ensures there isn't unlimited entities. In my experience, while it is good to be sensitive to performance issues you only really need to get fancy if you find an actual performance issue. Performance issues usually have a Pareto distribution, meaning that a couple issues usually cause >80% of the problem. So just spend time on those issues, and usually you can only identify the worst offenders after you've tried it out.
  23. Registration of recipes only happens during the loading process. So it will only check on the day you start the server and not every day. If this is just a single-player mod for you and your friends, then it would probably work okay though. In other words, if someone using the mod will relaunch Minecraft every day your approach is acceptable.
  24. I don't know that the AI actually turns off. The logic within each AI task may check distance to the player, but it still checks every tick I think. In other words, by the time you check the distance you're already technically executing the AI! I think the main thing is to simply make the AI do as little as possible when out of range. So it will still run, you'll still have to check range, but then you can short circuit the execution to minimize processing.
  25. You need to handle the HarvestDropsEvent. If you haven't used events before, you might want to check my tutorial here: http://jabelarminecraft.blogspot.com/p/minecraft-forge-172-event-handling.html Events are actually really easy once you figure them out, and are actually the best way to modify most vanilla behavior. So it is worth while taking the time to figure them out.
×
×
  • Create New...

Important Information

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