Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. In most of the EntityAI classes there is a this.setMutexBits(someMask) statement. The description (when hovering in Eclipse) says: However, this is confusing for two reasons: First, what is this index really referring to? I don't see where any AI task is given an absolute index. In each Entity subclass there is a task list of AI, but those are just a priority list and can vary between entities. Secondly, it takes an int value but says it is a bitmask. But does this mean it can mask multiple AI tasks? And if so, then doesn't that mean that somewhere each task is assigned some 2^n bit value as an index? Otherwise how exactly is this a bit mask? In particular, my concern is that I'm adding custom AI classes and I realize there is value in ensuring some AI don't run concurrently, but I really don't see how this mutexBits works in vanilla let alone how I would use it.
  2. Yeah, I understand that what to render becomes ambiguous because they are both technically at same location. But since it is a computer, I'd assume that it is still a logical process that would be biased to doing one or the other. For example, the render order in my model class is a set order, so you would think that the last one rendered would be the one that would show in case of a "tie" in position. I'm thinking that maybe actually true, but maybe the screen refresh is occasionally happening between the render calls. Anyway, it does seem common in Minecraft so I suppose people aren't that picky about weird render issues (like heads of entities going inside blocks and such). That is part of the charm of Minecraft (that the graphics aren't sophisticated). I think it is possible to scale the parts to 99% before render, so maybe I'll try that. I had thought about that, but wanted to know first if the issue had some other obvious fix.
  3. Okay, I've been having fun making complicated entity models and animations. One thing that bothers me though is that in order to have a smooth joint between two parts I like to overlap them a bit. The problem is that the area of overlap will "flash" a bit where I mean that it shows the texture of either one or the other part "randomly". I'm not sure if this is some OpenGL issue when there are two planes perfectly overlapping, or whether it is a rendering code issue where the order of the rendering is changing. Or maybe the screen refresh rate is randomly occuring in between the renders of my body parts? Is this a display sync issue? What confuses me about this, is that in my custom model class I'm fairly certain that the order of the render calls is fixed. I basically "body.render()", "head.render()", etc. for all the body parts in order. If the render order is fixed, why is there the glitchy flashing effect on overlapping parts?
  4. I use Eclipse as my IDE, and have a Client run configruation that I just run every time I want to test my mod code. Question #1) When it runs, I end up with a random player name "Player90243" or similar (the number changes). Is ther a way to force the run to use a proper Minecraft user login? I'm assuming there is an argument I can put in the Run configuration? Question #2) Most of the time (maybe 7 out of 10 times), if I load a saved game it will continue where I left off (i.e. my player is in same position as when I last played, I have all my inventory, etc.). But the other times an error happens where it complains about not being able to cast the login handler and then I spawn in original spawn point without my stuff. Is this a known issue? I don't think it has anything to do with my code as it seems to be something related to the vanilla start up. Maybe there is some sort of race condition in the code? It happens frequently enough to be annoying. So my question is whether this is a known issue, and whether there is a known fix.
  5. Yeah, I was just going to post the same thing. The TileEntity is removed in the block's breakBlock() method. So you just need to Override that in your custom block where you save the data somehow before the tile entity is removed. Here's the vanilla method:
  6. I had looked at that one, but frankly it seemed overkill for my purpose. At least right now I'm not doing complex GUIs, but rather just doing special animations for some custom entities, so I just need some state variables synced on the client to control the rendering. So to simply transfer a Boolean across I didn't figure I needed pipelines, discriminators, and various handlers. I was able to get by with a fairly compact set of hooks into FML (The FMLProxyPacket, the ClientCustomPacket event, the ServerCustomPacket event) and avoided needing all the other network handlers. But yeah, after going through this I can see there would be a lot of different ways to approach it.
  7. Okay after a marathon coding session, I've done my own grounds up netty packet handling and it seems to be working well, thanks to the tips to those above. Just to describe my solution: I basically am using FMLProxyPacket class, using ByteBufOutputStream and ByteBufInputStream for their write and read methods. I've subscribed to the ClientCustomPacket and ServerCustomPacket events. I confirm that the channel is for my mod, that the side is correct, and then have methods in my custom packet class to handle each case. I'm actually only using one packet class, but have packet type ID to switch to appropriate processing code. I have another couple questions, but I'll start a new thread with the specific topics. I consider this thread SOLVED. For posterity to help others, here is some of the significant code I wrote. Note that I was specifically trying to send variables from server to client to aid in entity render animations -- in this case I wanted my herd animal to "rear up" when attacked. I register my networking channel with following method that I call in my common proxy: My server run configuration ServerPacketHandler class: My "combined client" run configuration ClientPacketHandler class: My registration of my server run config packet handler in the common proxy, plus a couple of override methods that seem to be necessary for the interface. I call the registerServerPacketHandler() in my common proxy init() method: My registration of my combined client run configuration packet handler in the client proxy's init() method: Then there is the class that actually makes packets to send, and decodes packets received. I use one class for all packets, and have a packet type as first int to switch the processing and for now I'm processing entities, so next int is entityID and I use that to figure out which further fields to encode / decode: And with all the above set up, whenever I want to sync client to server I simply have to do:
  8. I think increasing canvas size should work. But you also need to make sure the texture size in Java is set correctly -- you need to set the textureWidth and textureHeight. Did you do that? If it is squished with transparent parts it is definitely related to some problem with the 2:1 aspect ratio. You could try to set the texture size in Techne to the size you want 128x64 and export that and then see if that maps properly.
  9. Thanks, I respect all you guys as experts and will use some of what each of you said. I will send packets only when there is a change, but I agree with TGG that economizing the packet size isn't probably worth it (especially in my case) -- a few dozen bytes isn't a big deal in modern "network" and there is overhead on the packets themselves. So maybe I'll try the serialization. Not sure how I missed the writeBoolean() method, but yeah I'll use that. The ByteBufUtils is the kind of thing I was most looking for. I figured someone must have gotten tired of using bytes for Minecraft related info. The methods for NBT compounds are interesting, since it could let me merge my save/load code with the sync code. TheGreyGhost, with serialization I understand how you would send the serialized object but not quite confident on how to read it such that it is actually applied to the entity. So you can just set your object to equal the de-serialized object received in the packet?
  10. EntityMob extends EntityCreature which extends EntityLiving. EntityLiving has name tag methods as follow: public String getCustomNameTag() { return this.dataWatcher.getWatchableObjectString(10); } public boolean hasCustomNameTag() { return this.dataWatcher.getWatchableObjectString(10).length() > 0; } public void setAlwaysRenderNameTag(boolean par1) { this.dataWatcher.updateObject(11, Byte.valueOf((byte)(par1 ? 1 : 0))); } public boolean getAlwaysRenderNameTag() { return this.dataWatcher.getWatchableObjectByte(11) == 1; } Maybe somewhere in your code you're setting the always render name tag?
  11. Okay, so I understand that you need packets to keep client in sync with the server (and vice versa). And I understand how to send a packet in each direction. But I'm interested in people's opinion on how they actually use the byte buffer payload of their packets. My questions: 1) Since most of my entities only have a couple custom fields, should I just create one packet that updates all of them (even if only one may have changed), or should I have a packet class for synching each field separately? 2) Do people tend to just come up with their own arbitrary byte stream? Like just put your fields in whatever order you want and putInt(), putFloat() etc? And then just read back in that order when received? 3) For Boolean fields, can you just use putInt() directly or do you have to convert the Boolean to int? Or some other way? 4) I'm using FMLProxyPackets which extend Minecraft's Packet class. That has a writeBlob() method which allows a length then a sub-stream of bytes. Do people use those for strings? Or what do people use for strings? Just a loop of putChar()? Basically I'm confident I can do the low level byte by byte stuff, but I'm wondering if I missing any methods that are more convenient.
  12. Okay, so for the lang file, I put it in assets/modid/lang folder under the src for my mod. So for my wildanimals mod the full path is E:\Forge\eclipse\WildAnimals\src\assets\wildanimals\lang. Similarly for my textures, I have them in folders like: E:\Forge\eclipse\WildAnimals\src\assets\wildanimals\textures\entity\serpents I reference the textures in the renderer, so in my RenderSerpent class I have this: public class RenderSerpent extends RenderLiving { protected ResourceLocation serpentTexture; public RenderSerpent(ModelBase par1ModelBase, float par2) { super(par1ModelBase, par2); setEntityTexture(); } @Override protected void preRenderCallback(EntityLivingBase entity, float f){ preRenderCallbackSerpent((EntitySerpent) entity, f); } protected void preRenderCallbackSerpent(EntitySerpent entity, float f) { } protected void setEntityTexture() { serpentTexture = new ResourceLocation("wildanimals:textures/entity/serpents/python.png"); } /** * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. */ protected ResourceLocation getEntityTexture(EntitySerpent par1EntitySerpent) { return serpentTexture; } /** * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. */ @Override protected ResourceLocation getEntityTexture(Entity par1Entity) { return this.getEntityTexture((EntitySerpent)par1Entity); } } The code is a little bit indirect in terms of having a few similar nested methods to simply get the texture resource, but I basically followed some vanilla code and modified it and it works so that's the way I do it. Note particularly how the resource location refers to the texture location -- "wildanimals:textures/entity/serpents/python.png" which uses the modid (in my case "wildanimals" and a colon then the path including the textures folder. Anyway, that's how I do it. Hope that helps.
  13. Did you mean the lang directory, or the textures directory? It sounds like you have lang working so it is the textures you need help with? I'm at work now and am busy through the evening, so can't provide much more info today. I think (I'm not at my computer to verify) so you'll probably figure it out before I get chance to check my code, but if not I'll write more when I can. You should check to see for the error in the console about the missing texture, and that might give you a clue on where it is looking for the texture currently.
  14. Okay, the texture mapping isn't so hard but there are a couple sticky points. Basically you just need the offset (the last two parameters in the ModelRenderer() constructor for each box, and you need the template for how it wraps around the box. I use Techne to check and visualize the texture map, but there are a couple tricks. 1) The biggest gotcha I ran into which puzzled me for a long time is that the texture image dimensions MUST be 2:1 ratio. So if you're trying to do a big model or if you are doing HD texture, you can't just make the y-dimension longer to make more space. If you do, when it is mapped it will be scaled down in just the one dimension, causing weird squishing and invisibility. So 64:32 (default) or 128:64 and so on are good, but don't do a 128:128. 2) The biggest benefit of Techne is that once you load a texturemap, any edits to that file are immediately applied (don't need to load it again). So it allows quick checking. 3) I use GIMP (open source Photoshop type of program) for editing the texture map. I use the pencil tool at size 1.0 pixel. The paintbrush tool is not good because it will "blend" into neighboring pixels unless you are perfectly aligned to the pixel. 4) The most confusing thing with Techne is that initially all the texture offsets overlap because they're all at 0, 0 offset. I wish there was a button to "spread out texture offsets" to automatically space them all out. But you need to go through your blocks one by one and change the texture offsets until there is no overlap. If you have a bunch of parts of your model that use the same texturing, you can actually let them overlap but I found that in most cases you want them separate -- even with something like entity legs that you think are all the same, they're not really if you want the texture to have a bit of change of color on the inside, or if you want claws on the front, etc. 5) Once you have spaced out all the texture offsets in Techne, then you want to export the texture map. This will create a colored default texture map png file. I usually load it again right away, because you'll see that some of the faces of the blocks have markings to help indicate orientation. I usually put my own little markers on each face of the default texture map to get a sense of orientation. 6) While Techne is still up, load the png in GIMP (or similar pixel editor). Proceed slowly, getting each block correct. The main thing is figuring out the orientations and how the folds match up. In most advanced photshop type programs saving the file will save it in that program's format (.xcf for GIMP) so you need to export it back to png. If you use the same file name, it will immediately appear back in Techne. 7) That's about it, except when you're done you need to put it into the right asset folder and make sure the right file name is referenced in your Java code.
  15. This is something I've just gone through learning, and I am proud to say that I've got a fully animated slithering snake, and an elephant with flapping ears and articulated trunk. I am planning to write a tutorial soon. But here are a few tips that might help. First of all I found that using the Java exported directly from Techne was not a good idea. I use Techne to visualize the model and animation, and especially use it for figuring out texture map, but ultimately I ended up coding the Java myself. And that is pretty simple because it is simply a bunch of boxes. Actually the biggest benefit of coding it manually is that you get a good understanding of how the rotation points and offsets really work. Here are some ideas to think about: 1) your whole model will only be manageable if you orient it the right way -- I spent a lot of time on one model which had head pointed 90 degrees to what Minecraft considers the front and while it could be fixed with some overall rotation it is much better to simply orient it correctly in the first place. You can check the ModelCow in Techne to ensure you have the right axis orientation. 2) get your rotation point in proper place first, then figure out your offset. The rotation point should be the joint for something like an arm, and should be the center for something like a body. In particular, make sure each joint is working properly before adding the next part. I have found that implementing some simple debug functions for spinning a part are immensely useful to help visualize that you have the rotation point and offsets correct. 3) use a hierarchical model by associating blocks with the addChild() method. For examples, the ears and nose should be children of the head. Then when the head rotates they stay in proper place, and then the animations of the ears and nose can be programmed relatively (much easier to think about than using spatial transformation). 4) It is extremely useful to use the entity's tickExisted property to help control the cycles through an animation loop. I used the modulo (also called remainder function) to detect "every X ticks". For example, parEntity.ticksExisted%60==0 will be true for one tick every 3 seconds (there are 20 ticks per second). Even better, something like parEntity.ticksExisted%6 will work as an index through a six-step animation sequence. 5) don't be afraid to "hard code" an animation loop. Just like a cartoon artist, you only need a few set postions to give the illusion of motion. For example, for my snake I spent days trying to figure out a mathematical equation to control the slithering and then realized that with a very simple matrix of preset rotations I could achieve the same effect. 6) you may need to add public variables to your entities to control animations, in which case you need to ensure there are packets sent to client side (where rendering takes place) to control the animation. 7) a really important thing is that the default 0, 0, 0 position of an added box is on one corner, not in the center of the box. This means you will almost always want to offset by half the dimension of the sides for two (if you're rotating from an end) or three (if you're rotating around the middle) of the sides. 8.) don't worry about the texture until you get the model correct. I suggest working with a texture map that is filled in with a solid color -- then you are sure to see your whole shape, and not get distracted by pieces that are wrongly textured (or worse invisible). 9) for entity models, use the F3+B shortcut to show the bounding boxes of your entity. It is really important to check that the actual bounding box is positioned correctly within the model. Okay, here is my example model for the EntitySerpent: The first part that I want to bring attention to is the animation cycle I hard-coded: // create an animation cycle protected int cycleIndex = 0; protected static float[][] undulationCycle = new float[][]{ { 45F, -45F, -45F, 0F, 45F, 45F, 0F, -45F }, { 0F, 45F, -45F, -45F, 0F, 45F, 45F, 0F }, { -45F, 90F, 0F, -45F, -45F, 0F, 45F, 45F }, { -45F, 45F, 45F, 0F, -45F, -45F, 0F, 45F }, { 0F, -45F, 45F, 45F, 0F, -45F, -45F, 0F }, { 45F, -90F, 0F, 45F, 45F, 0F, -45F, -45F }, Basically, each row is a step in the animation and each column is one of the snake's body parts. I came up with this table by simply drawing a snake on a piece of paper and figuring out the relative angles between parts. Note that each body part in the snake is child of the previous, so that all the parts remain connected. Then for each body part, I use the following code structure to create each box: head = new ModelRenderer(this, 0, 0); head.addBox(-2.5F, -1F, -5F, 5, 2, 5); head.setRotationPoint(0F, 23F, -8F); head.setTextureSize(textureWidth, textureHeight); setRotation(head, 0F, 0F, 0F); For people that may not know: - the parameters in the ModelRender() constructor are the texture offset from Techne - the first three parameters in the addBox() method are the offset x, y, z from Techne - the second three parameters in the addBox() method are the dimensions x, y, z from Techne The next important thing is how I add children boxes. If you have anything that should move with something else but also have its own rotation, then I strongly suggest making it a child. Otherwise, you have to do a lot of trigonometry to keep it connected. Note that if you don't want to move the piece separately, you can add multiple boxes to one ModelRenderer. In any case, something like an ear should NOT be a fully separate box, but rather should be either a child or at least added to the head. Then when you rotate the head, it will all move together. In the case of the snake, I did the children thing a lot -- each segment of the snake's body is a child of the previous. So all I have to do is set the rotations (which are relative) of each body segment and then just render only the body (which contains all the segments). Next, notice that the second parameter passed in an entity render call is a float that indicates whether the entity is moving. I called it parSwingSuppress (i.e. I use it to suppress any arm swinging), but it is really I think more like a speed and should probably be named more appropriately. Anyway, in the case of my snake, I only wanted its body to wiggle when this float was something significant. Basically, this parameter is quite useful for entity animation of moving versus standing still. Okay, the next part I'm really proud of. It is really easy and really powerful animating technique: @Override public void setRotationAngles(float parTime, float parSwingSuppress, float par3, float parHeadAngleY, float parHeadAngleX, float par6, Entity parEntity) { // swingSuppress goes to 0 when still so gates the movement if (parSwingSuppress > 0.1F) { cycleIndex = parEntity.ticksExisted%6; // cycle through animations body2.rotateAngleY = degToRad(undulationCycle[cycleIndex][0]) ; body3.rotateAngleY = degToRad(undulationCycle[cycleIndex][1]) ; body4.rotateAngleY = degToRad(undulationCycle[cycleIndex][2]) ; body5.rotateAngleY = degToRad(undulationCycle[cycleIndex][3]) ; body6.rotateAngleY = degToRad(undulationCycle[cycleIndex][4]) ; body7.rotateAngleY = degToRad(undulationCycle[cycleIndex][5]) ; body8.rotateAngleY = degToRad(undulationCycle[cycleIndex][6]) ; body9.rotateAngleY = degToRad(undulationCycle[cycleIndex][7]) ; } All I'm doing is cycling through the hard-coded array of angles for the animation. There is no trigonometry at all! Since all the rotations are relative (the body parts are children of preceding part) and because I figured out the right angles through simply drawing what I wanted on a piece of paper (you can also position it in Techne), it just cycles through them. Lastly I have a couple helper functions. degToRad() is simply a float version of simple converter -- I just find a lot of math utils require casting if you're working in floats. But certainly you can use another math helper function, or if you can think in radians then you're a better mind than me! The spinX() etc. methods I find extremely important although they are not used in my final code. I use them to debug my model. I take each part in turn, and use the spin on them in the render (while disabling other rotations). This creates an obvious visual check that your rotation point and offsets are correct. Anyway, not sure if this is more confusing or helpful. If I can teach one thing I do want to promote the idea of using an array of angles to control your animation though -- after days of working through complex trigonometry I suddenly realized you don't really need math to animate something through a loop with only a few steps.
  16. Keep in mind that many mods may add extended properties even to your custom entities; my Zelda mod, for example, adds some extended properties to all entities that extend EntityLivingBase, which obviously also includes any modded entities that extend that class. Same goes for any other mods that do something similar, or to the innumerable mods (including my own) that add extended properties to EntityPlayer. I wouldn't ever assume that my properties are the only ones being added, unless you are sure that people will not be playing with any other mods installed Okay, that's also a good point. Right now my mods are mostly for my kids' personal entertainment, and my field names are usually quite unique/descriptive, so chances are low of conflict. But in anticipation of becoming hugely popular, I'll code my mods to play nicely with other mods!
  17. Okay, for compatibility between mods that makes sense. In my case, I'm adding these to my own entity so I don't think there is a chance of conflict with other mods, but probably good practice to do it your way so if I copy the method onto a vanilla entity I won't forget.
  18. CoolAlias, I was looking at your tutorial for the IExtendedEntityProperties. You had the following saveNBT() method: // Save any custom data that needs saving here @Override public void saveNBTData(NBTTagCompound compound) { // We need to create a new tag compound that will save everything for our Extended Properties NBTTagCompound properties = new NBTTagCompound(); // We only have 2 variables currently; save them both to the new tag properties.setInteger("CurrentMana", this.currentMana); properties.setInteger("MaxMana", this.maxMana); /*Now add our custom tag to the player's tag with a unique name (our property's name). This will allow you to save multiple types of properties and distinguish between them. If you only have one type, it isn't as important, but it will still avoid conflicts between your tag names and vanilla tag names. For instance, if you add some "Items" tag, that will conflict with vanilla. Not good. So just use a unique tag name.*/ compound.setTag(EXT_PROP_NAME, properties); } Why did you create a new compound rather than just adding your CurrentMana and MaxMana tags to the compound passed into the method? I understand that compounds can be hierarchical, but isn't the compound passed in already a compound specific to the extended entities? Anyway, except for hierarchy organization, is there any problem with adding the custom tags directly to the passed compound without creating a new sub-compound?
  19. I kinda figured that. I need info on client to control render animations. I was hoping that with a built in extended properties interface they might have taken the trouble to sync it too ... but guess IExtendedEntityProperties is mostly just meant to provide consistency to NBT methods.
  20. I understand what IExtendedEntityProperties does in terms of NBTCompound for saving and loading. But do I still need to send packets between client-server if I'm keeping these extended properties up to date? Or is the client-server synchronization also handled automatically?
  21. Thanks. That was the conclusion I've come to as well.
  22. This is one of the tricky points to understand when first modding -- the "Client" is actually what TheGreyGhost would call the "Combined Client" because it also includes a server. It really shouldn't be called "Client" because it causes exactly the confusion you mentioned. Basically, when Run your mod with @SideOnly(Side.SERVER) it won't be available in the combined client even though there is actually still a server operating there (that may need the class). I think actually that @SideOnly(Side.SERVER) is fairly useless for most of us because the Server Run Configuration is a subset of Client Run Configuration -- if you used this and you're going to run a Client Run Configuration you'd have to add a bunch of extra proxy side checking to ensure that the suppressed class is never called. On the other hand @SideOnly(Side.CLIENT) is more useful since the Client Run Configuration is a superset, and there are several client-side classes that don't make sense on the server. However, I'm beginning to think the @SideOnly is sort of pointless generally as long as you don't call the class (and your proxy and side checking can take care of that). Who cares if there is a Renderer class on the server as long as it is never registered (your proxy will ensure that)? The disk space and memory used by the unused classes is likely insignificant in most cases. Anyone have a good reason why you should use @SideOnly except as an optimization?
  23. Actually, I solved it. There is actually an AI task on the entity called AIPanic. Removing that did the trick. I simply didn't know the word "panic" was something to search for ...
  24. Thanks. I actually already found that which is why I said "it is a wander AI plus a movement modifier." In fact if you compare my method code above with that vanilla code you posted, you'd see that I actually already deleted that portion of the code. Also, the code in vanilla seems to only execute if isAIEnabled() is false, but in my entity it is always true. I've found a lot of little things that are related, but not the actual variable that indicates it is fleeing. That is why I suspect that instead they are initiating an AIWander at a higher movement speed. The related variables I've found related to timing after an attack does damage are: fleeingTick seems to be a timer to prevent entity from attacking the player for 3 seconds after being hurt hurtResistanceTime seems to be a timer to prevent rapid hits hurtTime seems to be a timer for the red tint effect after entity is hurt recentlyHit seems to be a timer to determine how many drops the entity will give when it dies None of those seem to be used for any logic related to movement speed. I'm thinking that I'm going to poke around in the AIWander. I'll first try to confirm that it is invoked at that point, and if so, maybe I'll override it and create one where if it is hurt that the AIWander will be canceled.
×
×
  • Create New...

Important Information

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