Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. Yep, that's the whole joy in modding! Glad you were able to fix it.
  2. One thing is I think it is wise to make sure that the texture size in the Java matches your actual texture file. I'm not entirely sure what happens if these mismatch -- I think it will scale, so keeping 2:1 should still be okay -- but best to make it match. Anyway, does it look correct in Techne? Even if you've modified your model a lot directly in Java (which is my preference) you can use Techne to quickly check texture mapping. You don't need to put the full model into Techne, just the block you're checking the mapping. What you need to do is to: 1. Start a new Techne model, with ModelBase. Set the texture size correctly for the project! 2. Create a block that matches the Java for the part of your model you care about. Put in the rotation point, dimensions, offset and texture offset. 3. Load your texture.png into Teche Then play around with it there until you get it right. Then modify your Java (or your texture file) accordingly.
  3. So what was the correct code fix?
  4. Oh right, I didn't scroll down and only saw the one method. I would suggest that you put in a bunch more System.out.println() statements to help you follow the flow of the code. For example, inside each if block, so you can see if it is testing true and such. I also tend to keep the conditions simple at first -- do you really need to be sneaking for the packet to be sent (for test purposes)? Anyway, with a bunch of println() statements you can follow every hop of the code and see where it is failing.
  5. Yeah, perfect. Brandon, events usually pass world and player to you. Always good to check the fields and methods of the event to use them well.
  6. brandon, In your onBlockActivated you're testing for if the world is NOT remote, meaning that you're on the server. But I thought your thread here is trying to send from the client? Where is your code that sends a packet if the world IS remote?
  7. Hi brandon, sorry for the delay -- I had a crazy day yesterday. As you mentioned, I've been concentrating on sending from server to client as I'm mostly using it to control entity animations based on AI states. But at a glance, like what some others say above, I think you're using some methods that are not appropriate to the server side. I'm pretty sure the Minecraft.getMinecraft(). type methods aren't right for the server side. I think what you need to do is your server packet needs to have information on the player, use that on server side to to look up the player and get the world they're on, then you're off and running. I'll look at it when I get home -- in a few hours.
  8. The problem is the code you gave example for would only keep the blades at a fixed rotation. To make the blades rotate, you want some method to know how many ticks have gone by. You can do this with a custom counter (just count each render) or you can use the ticksExisted() method for the entity passed to the render. I usually use the ticksExisted. For pure rotation (like this case) you can probably just use a += to increase the angle each time but in the general case an animation needs some sense of time elapsed. Note also that if you want the rotor to stop sometimes (like if the helicopter can land) then you'll need some boolean field or test to control the rotation -- i.e. only rotate if you know it is supposed to. Anyway, my point is that for it to actually rotate you need to change the rotation in each render pass and that means you need some sense of progression via a counter or some sort.
  9. How did you register your AI to your entity? Can you show that code? (You should be adding it to the tasks list and assign it an appropriate priority.) I think you should take out the randomization until you have it working a bit more. Your current code will only on average fire once every six seconds! Also, if you're having this AI run together with other AI then you may need to look at how you set the mutexBits. I recently figured out how those work. Basically, you don't want two different movement AI active at the same time or each will be interfering with the other, and same with attacking, etc. Swimming is compatible with some things but not others. Anyway, see the last post on this thread about how to set the mutexBits: http://www.minecraftforge.net/forum/index.php?topic=18777.0. In your case I think it mutexBits should be set to 1 (like other movement AI). Lastly, you should put console System.out.println() statements in your code to confirm whether it is executing at all, and how. I usually put in a statement in each method to at least indicate that the method was run with something like "running Flock AI startExecuting()" type statements. And I would put such statements inside each if-else-if so you can trace which path is followed. It is usually pretty clear once you see those what is happening. Overall, for your AI to execute it needs to be in the task list, it needs to have higher priority (lower number) in that list than other movement AI tasks, and the shouldExecute() method needs to return true. Also, you the continueExecuting() method needs to return true for as long as the AI is supposed to continue.
  10. Well, as larsgerrit said for TileEntities they are specifically made for syncing extra data between client and server (for instance they are the intermediary for container GUIs and such. So you don't need to do my packets for that. But you still can, and mine are a useful general packet that I use a lot for regular Entities. Anyway, for proxies you can do it a few different ways. This is one of the tricky points to understand when first modding -- the proxies are related to your run configuration (i.e. in Eclipse). When you run Minecraft, there is always a server but you may or not have a client running locally. When you use the Client run configuration, it also includes the server classes. So this idea of proxy helps organize the case where your run configuration is only server, or also server with client. The server stuff goes in the "Common" proxy because it is common to both the Client and the Server run configurations. Then any client-specific stuff goes into a "Client" proxy, but that actually extends "Common" proxy to cover both sides that will execute in the run configuration. I know that is kinda confusing, but keep reading it over until it makes sense. Okay, with that being said I personally organize my proxy like this. Note I took out a lot of stuff to make so you can just see the organization. CommonProxy: And ClientProxy: Then in my mod's main class the organization is very simple and clean. Basically you call the proxy's related method as appropriate. Here's just the event handler stuff (not quite my whole main class):
  11. Yeah, I see now he's talking about TileEntities not regular Entities. TileEntities do have built in packet handling methods.
  12. Thanks. I've just learned modding recently and finally have confidence to share my understanding on a couple topics. I'm just figuring out best place to post my tutorials and plan to post a few. I think this packet handling approach deserves a tutorial, and also my entity modeling has gotten pretty good (check out my snake and elephant entities in this video: ). I don't like posting mis-information though, so want to make sure I thoroughly understand something before deciding to teach others.
  13. The problem is that your texture file needs to have a 2:1 ratio. It looks like yours is square. So it needs to be 64x32 or 128x64, etc. It cannot be 128x128 or similar because it will get scaled in the one direction and that will "squash" the pixels and leave transparent areas. You need to (a) size your PNG to be 2:1 and (b) specify the texture size correctly in your model with the setTextureSize() method.
  14. NBT is mostly for saving and loading information either when game starts/quits or when chunks get loaded/unloaded. Syncing client with server is entirely different. The client side and the server side (as far as I understand) are pretty much separate programs and don't know anything about the classes in the other side. So you need to send packets. It is a little bit complicated, but there are a few tutorials out there. I recently just learned it myself -- I had a render animation that I needed to do on the client but the information was based on entity AI (which runs on the server). Here's how I did it (trust me, my way is one of the more simple, but feel free to look at other tutorials). I used a system that took advantage of the FMLProxyPacket class and subscribed to some available packet events. Basically, you’ll need a packet system that: a. Registers a networking channel specific for your mod (this allows you to ignore packets that may come from other mods) b. Has packet handler classes with Subscribes to the onClientPacketReceived and the onServerPacketReceived events. c. Has a packet class that can distinguish packet type and what side it is on and process the packet accordingly d. Sends the packets as needed (ideally just when there are changes, which is often in a setter method). (In my examples, my mod is called WildAnimals -- wherever you see that you should replace with something relevant to your mod.) Register a networking channel: - in your mod's main class create variables: public static final String networkChannelName = "WildAnimals"; // put the name of your mod here public static FMLEventChannel channel; - in your mod's init event handler method (probably in your proxy class) you need: WildAnimals.channel = NetworkRegistry.INSTANCE.newEventDrivenChannel(WildAnimals.networkChannelName); // replace WildAnimals with the name of your main class Create and Register Your Packet Handler Classes: - also in same init event handler method register your packet handler classes. In the common proxy init() you need: WildAnimals.channel.register(new ServerPacketHandler()); // replace WildAnimals with name of your main class - and in your client proxy init() you need to additionally register the packet handler class: WildAnimals.channel.register(new ClientPacketHandler()); // replace WildAnimals with name of your main class Create a new class called ServerPacketHandler which contains subscription to a server packet event with something like: package wildanimals.networking; import java.io.IOException; import wildanimals.WildAnimals; import wildanimals.network.entities.PacketWildAnimals; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.network.FMLNetworkEvent.ServerCustomPacketEvent; public class ServerPacketHandler { protected String channelName; @SubscribeEvent public void onServerPacket(ServerCustomPacketEvent event) throws IOException { channelName = event.packet.channel(); if (channelName == WildAnimals.networkChannelName) { PacketWildAnimals.processPacketOnServerSide(event.packet.payload(), event.packet.getTarget()); } } } And create a ClientPacketHandler class that extends the ServerPacketHandler class and subscribes to a client packet event: package wildanimals.networking; import java.io.IOException; import wildanimals.WildAnimals; import wildanimals.network.entities.PacketWildAnimals; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent; // Remember client run configuration includes server side too public class ClientPacketHandler extends ServerPacketHandler { @SubscribeEvent public void onClientPacket(ClientCustomPacketEvent event) throws IOException { channelName = event.packet.channel(); if (channelName == WildAnimals.networkChannelName) { PacketWildAnimals.processPacketOnClientSide(event.packet.payload(), event.packet.getTarget()); } } } Create a Packet Class Create a packet class that allows you to generate and process received packets as needed. Note that the actual data you create and receive will need to be changed for your particular need. Hopefully you can understand how my example packet could be modified for your need. There are a few things to understand -- a packet has a payload that consists of a byte stream. It is totally up to you what bytes you put in the packet, but you have to come up with a useful format. I expect to have other packet types so my first data sent consists of a packet type identifier. I know it is a packet from my mod because of the channel it comes on (I check for the channel). After that, I want to be able to put information about any of my entities into the packet so my next information is the entityID. But since I have multiple types of entities, I have to get the class from the entityID and then change my processing based on that. In that processing I simply take the information I want to send and put it in. The key thing is to read back the data in the same order that you put it in when you create the packet. package wildanimals.network.entities; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; import java.io.IOException; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.world.World; import wildanimals.WildAnimals; import wildanimals.entities.bigcats.EntityBigCat; import wildanimals.entities.herdanimals.EntityHerdAnimal; import wildanimals.entities.serpents.EntitySerpent; import cpw.mods.fml.common.network.internal.FMLProxyPacket; import cpw.mods.fml.relauncher.Side; // this class is intended to be sent from server to client to keep custom entities synced public class PacketWildAnimals { // define IDs for custom packet types public final static int packetTypeIDEntity = 1; public PacketWildAnimals() { // don't need anything here } public static FMLProxyPacket createEntityPacket(Entity parEntity) throws IOException { // DEBUG System.out.println("Sending PacketWildAnimals on Server Side"); ByteBufOutputStream bbos = new ByteBufOutputStream(Unpooled.buffer()); // create payload by writing to data stream // first identity packet type bbos.writeInt(packetTypeIDEntity); // write entity instance id (not the class registry id!) bbos.writeInt(parEntity.getEntityId()); // now write entity-specific custom fields // process herd animals if (parEntity instanceof EntityHerdAnimal) { EntityHerdAnimal entityHerdAnimal = (EntityHerdAnimal)parEntity; bbos.writeFloat(entityHerdAnimal.getScaleFactor()); bbos.writeBoolean(entityHerdAnimal.isRearing()); } // process serpents else if (parEntity instanceof EntitySerpent) { EntitySerpent entitySerpent = (EntitySerpent)parEntity; bbos.writeFloat(entitySerpent.getScaleFactor()); } // process big cats else if (parEntity instanceof EntityBigCat) { EntityBigCat entityBigCat = (EntityBigCat)parEntity; bbos.writeFloat(entityBigCat.getScaleFactor()); } // put payload into a packet FMLProxyPacket thePacket = new FMLProxyPacket(bbos.buffer(), WildAnimals.networkChannelName); // don't forget to close stream to avoid memory leak bbos.close(); return thePacket; } public static void processPacketOnClientSide(ByteBuf parBB, Side parSide) throws IOException { if (parSide == Side.CLIENT) { // DEBUG System.out.println("Received PacketWildAnimals on Client Side"); World theWorld = Minecraft.getMinecraft().theWorld; ByteBufInputStream bbis = new ByteBufInputStream(parBB); // process data stream // first read packet type int packetTypeID = bbis.readInt(); switch (packetTypeID) { case packetTypeIDEntity: // a packet sent from server to sync entity custom fields { // find entity instance int entityID = bbis.readInt(); // DEBUG System.out.println("Entity ID = "+entityID); Entity foundEntity = getEntityByID(entityID, theWorld); // DEBUG System.out.println("Entity Class Name = "+foundEntity.getClass().getSimpleName()); // process based on type of entity class // process herd animals if (foundEntity instanceof EntityHerdAnimal) { EntityHerdAnimal foundEntityHerdAnimal = (EntityHerdAnimal)foundEntity; // apply custom fields to entity instance foundEntityHerdAnimal.setScaleFactor(bbis.readFloat()); foundEntityHerdAnimal.setRearing(bbis.readBoolean()); // DEBUG System.out.println("Is rearing = "+foundEntityHerdAnimal.isRearing()); } // process serpents else if (foundEntity instanceof EntitySerpent) { EntitySerpent foundEntitySerpent = (EntitySerpent)foundEntity; // apply custom fields to entity instance foundEntitySerpent.setScaleFactor(bbis.readFloat()); } // process big cats else if (foundEntity instanceof EntityBigCat) { EntityBigCat foundEntityBigCat = (EntityBigCat)foundEntity; // apply custom fields to entity instance foundEntityBigCat.setScaleFactor(bbis.readFloat()); } break; } } // don't forget to close stream to avoid memory leak bbis.close(); } } public static void processPacketOnServerSide(ByteBuf payload, Side parSide) { if (parSide == Side.SERVER) { // currently haven't defined any packets from client } } // some helper functions public static Entity getEntityByID(int entityID, World world) { for(Object o: world.getLoadedEntityList()) { if(((Entity)o).getEntityId() == entityID) { System.out.println("Found the entity"); return ((Entity)o); } } return null; } } Send the Packet When Needed: Now all you need to do is to send the packet when you need to. You could just send it in the onUpdate() method of your entity, but that is a bad idea because it is wasteful -- it sends a packet every tick. Instead you should send it every time the variables you are tracking change. Personally it can be tricky to remember to send a packet every time, so I rely on encapsulation -- I only change my variables by using a "setter" method and I put the sending packet inside that sender. Anyway, it is up to you when you want to send the packets to sync up the client. The code you need depends on whether you're sending it to just one player (e.g. for their personal GUI) or to all the players (like in my case where I want all the players to see the entity animations at the same time). Here are some examples of the code you can use to send the packet: // method to send sync packet from server to client, should send whenever a custom field is set (from setter method) // send sync packet to client, if on server side public void sendSyncPacket() { if (!this.worldObj.isRemote) { try { WildAnimals.channel.sendToAll(PacketWildAnimals.createEntityPacket(this)); } catch (IOException e) { e.printStackTrace(); } } } You should check out the other methods for the channel, because you can sendToServer, etc. Hope this helps. I know it is annoying that you have to do so much work in order to simply send a couple values to the client, but trust me that you have to do it and you might as well learn. I won't feel bad if you use a different tutorial to do it, but I'm pretty happy with my implementation so far.
  15. Not sure you need a mod for this, like MrArcane says you may be able to do it just with command blocks in a map. If you actually want to mod it, then you should look at these methods: All players on the server: MinecraftServer.getServer().getConfigurationManager.playerEntityList All players in a world: world.playerEntities You can get the lists, get the size, generate a random number between 0 and size-1 and then get the playerEntity that has that position in the list.
  16. CoolBoy above already mentioned an event that looks like it would work. However, based on your questions it sounds like you've never done a mod that has intercepted an event before. So maybe you need to try out some tutorials on that first. No offense but when you ask for help you need to show you know enough that you can use the help and that you're not being too lazy. So create an event handler and try to intercept the event that CoolBoy mentioned and see what happens. If it doesn't work, post your code that you tried here and we can help further.
  17. If you mean have a for loop that loops just to cause delay, then NO! You don't want to create delays in any method of your mod, as that will delay the whole game processing (well within the thread anyway). The proper way to do delays is to count ticks somehow and ignore firing until enough have passed. Like what TheGreyGhost posted.
  18. This is one of my biggest pieces of advice to new modders -- you have to know when you should extend a class versus when you should copy a class. If you extend a class, you need to understand the implication -- your new class will always still be considered a member of the class you're extending. When you are first modding, it seems really easy to just extend a class and modify a couple things. However it will have unintended consequences. For example, if you extend EntitySheep to make an EntityGiraffe you will have wolves still attack EntityGiraffe because they will still be also EntitySheep. So very often, if you want to make something that is similar to a vanilla class it is better to copy the code rather than extend it. A gun is not an extension of a sword, and probably not even an extension of a bow. Anyway, remember that there is vanilla code that is testing against vanilla classes and if you extend a vanilla class your new class will be considered by vanilla code (and other mods!) to be the class you extended.
  19. Solution for those who may be interested: In most cases of movement, targeting and attack you want your AI to be exclusive -- you wouldn't want your AI to set a target then another one to set different target in same tick. But there are a couple types of AI that vanilla Minecraft allows to be compatible and you may want to consider compatibility with your AI. As a simple guideline: - If you are extending or copying a vanilla AI, you'll typically be okay using same mutexBits setting but consider the following: - If you want to make it compatible with swimming, begging and watching closest then you should set mutexBits to 1. - If you want to make it compatible with swimming, but not begging and watching closest you should set mutexBits to 3. - If you want to make it compatible with begging and watching closest, but incompatible with swimming you should set mutexBits to 5. - If you want to make a new AI that is incompatible with everything vanilla, then you should set the mutexBits to 7. - If you want to make a new AI that is compatible with everything vanilla, then you can choose to set mutexBits to 8 (or any larger power of 2).
  20. Yeah, it is late at night for me (3am) so wasn't thinking clearly before, but I understand it now. As a general method it is a good idea, and I agree that bitwise operator is probably good performance which you want for something that is being run multiple times per entity per tick. Basically the bitwise mask is like running a few Boolean checks simultaneously, so is efficient. I think I got a bit bothered on the topic because we're reverse engineering this stuff so you kinda have to list out all the values before you understand the logic of them (i.e. that 1 seems to be related to movement AI, and so on). It wasn't directly readable (as opposed to a Boolean that like compatibleWithSwimming would be), although now with the list I can readily figure out what to use in my custom AI.
  21. I understand that bitwise operator might have been used for performance reasons -- I assume it is fast. Just wasn't very readable when other types of comparators (like <) could have been used for some of the testing. So basically, since 1, 3, 5, 7 all have some overlap they are not compatible with each other. So the interesting part is around the fairly sparse 2 (beg, watchClosest) and 4 (swimming) which are compatible with lower categories. So you can swim while doing almost everything, but can't beg while attacking with arrow for example. I guess the good news is that it does look like you can use it in custom AI classes, just need to figure out which category you feel the custom AI belongs to. If you want your AI to be compatible with everything, then value of 8 (and higher binary place values) is available (assuming another mod doesn't have conflict, but it would be hard to really prevent some sort of problem with interaction with unknown custom AI anyway).
  22. Yeah, I was just looking at this code. Like you said, it isn't a simple case of each AI task taking on a bit place value but rather it seems that they simply categorized. The code for testing if tasks can run concurrently (from EntityAITasks class): /** * Returns whether two EntityAITaskEntries can be executed concurrently */ private boolean areTasksCompatible(EntityAITasks.EntityAITaskEntry par1EntityAITaskEntry, EntityAITasks.EntityAITaskEntry par2EntityAITaskEntry) { return (par1EntityAITaskEntry.action.getMutexBits() & par2EntityAITaskEntry.action.getMutexBits()) == 0; } To really understand how this works in practice, we need to look at the list of all AI and their category. Here are the categories (classes are listed in alphabetical order, I think later I might try to organize them in vanilla priority). Category 1 (seems to be most "targeted movement" type AI) AvoidEntity CreeperSwell DefendVillage FleeSun HurtByTarget MoveIndoors MoveThroughVillage MoveTowardsRestriction MoveTowardsTarget NearestAttackableTarget OwnerHurtByTarget OwnerHurtTarget Panic Play RunAroundLikeCrazy TargetNonTamed Wander Category 2: Beg WatchClosest LookAtTradePlayer Category 3 (seems to be "interactive" AI): ArrowAttack AttackOnCollide FollowGollum FollowOwner LookAtVillager LookIdle Mate OcelotAttack Tempt VillagerMate WatchClosest2 Category4 (swimming is category on its own, and is compatible with a lot of stuff) Swimming Category 5: LeapAtTarget OcelotSit Sit TradePlayer Category 7: ControlledByPlayer EatGrass Uncategorized (I'm not actually sure how these are handled, but I assume they are compatible with everything): BreakDoor DoorInteract FollowParent OpenDoor RestrictOpenDoor RestrictSun
  23. Thanks, but actually I'm a semiconductor designer so I understand it well. That is why I mention 2^n in my original post. I think "index" is an appropriate word still though because to me that just means a unique progression of identifiers, but it is good to clarify the terminology. So thanks for the detailed post (I'm sure it is helpful to others who may someday check out this thread), but I understand that a bitmask of 3 would be masking out two AI classes, and 5 would be masking out a different two as well. For the masking, somewhere the class itself should be associated with some binary bit place value useful for masking. I think GotoLink's answer is probably what I want, but haven't had time to follow the code he pointed out.
  24. Where do you register your renderer? I see you have a method ClientProxy.registerRenderThings() but do you call that in your main mod preInit() event handler? I do my ClientProxy differently -- I have an actual preInit() method there and in my main mod class's preInit() event handler I call proxy.preInit(). The latter contains the code that actually calls the registration methods.
  25. The problem is that the AI tasks don't actually indicate their own ID (as far as I can tell). The mask is to indicate what other tasks are to be considered non-concurrent. Most of them set the mutexBits mask to either 1 or 3. Maybe I'm not seeing it, but I don't see any public value that is related to it. Like you say I think it is some sort of internal thing. I suppose I could just override all the vanilla AI with copies that have an ID that I can work with.
×
×
  • Create New...

Important Information

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