Wednesday, July 16, 2014

FMod - File Handle Confusion

The wonderful Bio Menace modder who appeared on the Keen:Modding forums a while ago has given me some help with Abiathar. He reported a crash-corruption bug (!) involved with copying tiles. Though that particular instance of task was not really suited for the copy/paste mechanism and he was actually triggering entirely different functionality than what he wanted, there was still quite a bit of a problem in the program.

He was pressing Enter, probably thinking that it completes the copy, but it actually causes the graphics to reload, like in Mindbelt. That function was causing an IOException to be thrown, citing a file handle being open in another process. I looked all throughout the graphics loading code to find a leaked handle, even liberally sprinkling Using blocks around IDisposable instances. I did that for almost an hour until I discovered that something was wrong in the reload-graphics wrapper, not the graphics loader itself. After the call to DestroyGraphics, the reloader explicitly created handles for the EGA resources! That was an artifact from before bitmap graphics were allowed, and it was definitely the cause of the error. I removed them (they were just throwing the handles away anyway) and the problem was solved.

Also, I did some better clean-up of the selection plane so that it does not show the previous selection in a level when you switch back to the copy tool after canceling a copy.

Monday, July 14, 2014

Minecraft Forge 1.7 - Setting up Networking Components (newEventDrivenChannel)

After core Minecraft changed to using Netty for its network operations, Forge's network model changed considerably. They did a great job changing it, but the full road to getting packet dispatching working is documented literally nowhere. The wiki tutorials that claim to be the new way all have a message from the Forge team telling people that they are wrong.

I think I've done a decent job figuring out what needs to happen, so I'll document it here. Yay, internet learning!

First, you'll need to register a packet channel. Unlike in the pre-1.7 days when you had to register a bunch of different channels for each packet type, we will differentiate them with a map of integers to packet subclasses. Declare an FMLEventChannel variable in your mod class, preferably static:

public static FMLEventChannel NetworkCon;

Then, in your mod's handler for FMLServerStartingEvent, register the channel and assign it to that variable, like this:

@EventHandler
public void serverLoad(FMLServerStartingEvent event) {
FMLEventChannel net = NetworkRegistry.INSTANCE.newEventDrivenChannel(MODID);
NetworkCon = net;
}

(Replace MODID with your mod's ID name or use a constant you defined already.) Now, create a class to hold event handlers related to networking, I'll call mine NetworkHandler.

public class NetworkHandler {
@SubscribeEvent
public void onServerPacket(ServerCustomPacketEvent e) {

}
@SubscribeEvent
public void onClientPacket(ClientCustomPacketEvent e) {

}
}


Register that class as a receiver for the channel. If you get an error on the registration later, annotate the NetworkHandler class with @Sharable. Place this in the handler for FMLServerStartingEvent:

net.register(new NetworkHandler());

It's now up to you to register your packet types, which will be in another article - or you could create your own implementation! You can send them with the obviously named methods in the channel object and handle them in the packet event handlers. On the server, you can get the player by casting e.handler to NetHandlerPlayServer and accessing playerEntity, which is not obvious at all. I have this little utility method:

private EntityPlayerMP getSendingPlayer(ServerCustomPacketEvent e) {
return ((NetHandlerPlayServer)e.handler).playerEntity;
}

Happy modding!

Sunday, July 13, 2014

The Wither Mod - v1.7.10

Today I noticed that Forge has a version for Minecraft 1.7.10, which is considerably newer than the 1.7.2 I had been developing for. So, I used the Gradle script to update to it, but then all the sounds (including vanilla's) were broken. Apparently, I need to provide an --assetsDir switch in the run configuration. After doing that, I tried to run the thing and immediately discovered that Forge had yet again changed and broken my mod (I don't know what I expected). The main issues here were that IEntityOwnable's ID system had changed from using names to UUIDs and that some things referenced by my DiscriminatingExplosion lost their nice names (went back to the obfuscated Searge names). I fixed all those things without much trouble.

Then, in an attempt to make the Wither more interesting, I did some more messing around with its AI tasks. Before, it would frequently get stuck on trees or otherwise find itself unable to get to its target. This was due to EntityAIArrowAttack exiting if the Wither's senses did not have a clear line of sight to the target. I removed that check, and owned Withers will now shoot through barriers to get to their target!

Saturday, July 12, 2014

The Wither Mod - Stronger Bond

In the three hours I had today between waking up after oversleeping and going to church, I did some more things in the Wither-taming mod which has yet to be named. (Internally, it's named "WitherWand" but that was the first thing I thought of when I needed a package ID, it will probably not be kept.) I did a lot of looking at the wolf class and some investigation of the AI task system to see how "actions" are assigned to entities and triggered.

I applied my trick of copy/pasting entire source files on a few AI classes (specifically FollowOwner, OwnerHurtByTarget, and OwnerHurtTarget) and then changing the types to work with my Wither class. Those AI tasks have been registered in my class, causing an owned Wither to follow (and teleport to, if necessary) its owner. In addition, the Wither will give attack priority to first entities that attack the owner, then entities that the owner attacks, then entities that attack the Wither, and finally whatever entities it finds around the world and decides to destroy.

There's one bug with this arrangement: as the Wither goes around attacking whatever it wants, it may leave the player's ownership range, causing it to teleport back, but then it continues trying to attack the entity. This results in a strange rubber-band effect. I will eventually fix this when the owner is allowed to give the Wither instructions (in this case, to not destroy everything).

Friday, July 11, 2014

The Wither Mod - Proof of Concept

Today, since I didn't have a lot of college work to do, I did some things on the Wither-taming mod I was thinking about making. I abandoned trying to subclass EntityWither and instead copy/pasted the whole class's code so I could have control over the private variables as well. To make that work, I had to do the same thing for the renderer and the render model, changing type names. (This will eventually have to happen for some AI tasks that take a class on a different hierarchy instead of an interface like they should.) Once it was all done, I could change things in all the methods I need to, specifically replacing EntityWitherSkull with my own DiscriminatingSkull.

With that done, I have completed the proof of concept! The resulting owned Wither will go destroy all creatures (and I mean all - I changed the selector) except its owner. If one of the skulls does hit the owner, its explosion will not damage him and a few seconds of Regeneration (instead of Wither) will be given. I also removed some checks for creature type, allowing the owned Withers to attack other Withers.

Thursday, July 10, 2014

Possibly a New Mod

I had a great idea for a Minecraft mod while trying to go to sleep last night. I personally think it would be pretty awesome to tame a Wither and have it follow you around, obeying your commands or possibly just destroying everything around while protecting you. I imagine carrying some sort of baton to point the Wither toward what needs to be destroying, feeding it some sort of arcane scroll to teach it new behaviors - maybe sacrificing its health to fill mine?

I was fairly busy with important college things today, but I started messing around with this idea. I opened up a new ForgeGradle installation and threw together some super-messy subclasses of the Wither, its skull projectile, and the explosion class. Right now, I'm just trying to get a Wither to spawn that remembers its owner and doesn't deal damage to it. This is currently not working, but I think I have a separate type of entity spawning (I really quickly made an item to produce one of these guardian Withers with the user set as the owner.) Strange Java behavior when shadowing variables has not helped debugging, and I still can't figure out why the projectiles are still damaging the owner - it did get registered correctly at the spawning.

Since this will be a very small project, it might be the first mod I actually finish!

Wednesday, July 9, 2014

Minecraft: Defeating a Rogue Wither

Everyone knows it's easy to defeat Minecraft's Wither boss if you build it in a claustrophobic space or fight it above the Nether. However, the Wither is much more of a challenge in the varied Overworld surface, where trees get in its way and passive mobs are around to get pummeled and regenerate its health. I, with my friends on the Bare Roots Server, are currently suffering major losses due to many deaths to one escaped Wither. Unfortunately, all our super-powerful gear has already been destroyed, so we need to do something different contain this guy.

I came up with a plan. It doesn't require lots of powerful diamond gear, but a decent supply of gunpowder. Here's the shopping list with recommended quantities:
  • 128 TNT
  • 27 normal bows
  • 64 arrows
  • 2 Ender pearls
  • 1 bed
  • 1 iron pick
  • 1 flint & steel
  • 2 players
  • 2 suits of iron armor
  • 1 normal iron sword
  • 1 good bow
  • 1 good sword
First, in a location somewhat far away from the Wither (it won't rage on you until you directly attack it), dig a 1x1 hole a decent way - say, 50 blocks - down and fill it with TNT. Light it, and get away. Then, at the bottom of the exploded hole, TNT out sideways, producing an artificial cavern. Optionally, light it up, but the lighting will probably get destroyed later. Set up a bed at the surface with the supply chests containing bows and the arrows. Optionally, protect it with cobble you got from the blast.

Now, for this next step, bring nothing but one bow and 2 or 3 arrows. Go shoot the Wither once or twice, just to get it following you. Jump (or Ender pearl, if you have a dispensable supply of them) down to the bottom, making sure the Wither is following. Try to lure it away from the surface opening, but you'll probably die pretty quickly. Repeat this paragraph until the Wither is underground in your artificial cave, a decent way (20 blocks?) away from the surface hole.

At this point, if you aren't confident enough to go in and fight it (or if you like to live dangerously with a Wither below you), you could seal the hole and not worry about it ever again. It will eventually break out (those blue skulls can do a lot over a period of time), but by then you'll probably be geared up.

Here, get the nice bow (optimally Power IV or above, Punch is nice too), put on the iron armor, prepare an iron sword, take some potions if you have them, and go down. Shoot the Wither as much as you can, then go in with the sword, but you're probably close to dead by now. Once you die, grab the other set of iron armor, the good sword, and a normal bow, and finish off the Wither with a sword.