Anti Rage-Induced-Accidental-Disconnect Minecraft Client Mod

We've all been there: You're playing on your favorite Minecraft PvP server, ready to get revenge on the player who just killed you, when suddenly, you're staring blankly at Minecraft's title screen.  How could such a thing happen?  Well, maybe Mr. McMillan has something to say about the matter:

 

Yes, Mr. McMillan, precisely.  The death screen GUI has "Respawn" and "Title screen" buttons that are way too close to each other, resulting in players sometimes unintentionally leaving the game when they actually want to respawn.  Now, this isn't something that happens all the time, but it happens occasionally and it's annoying.

So, in the first and only installment of the new (and old, by the time you read this) series "Weekly 1 Minute Minecraft Mods", we can fix the problem.

We will change this:

 

Before

into this:

 

After

(Texture pack is the beautiful Plast Pack.)

The new layout isn't pretty, but in the case of PvP I'll definitely take function over fashion (with the exception of gold pants).

Making the Mod

You need to download and install Minecraft Coder Pack (MCP).  There is a lot of information about how to do this online.

Once MCP is ready to go, find the file "GuiGameOver.java" in the minecraft/src subdirectory.  In this file, there are only two lines we need to modify:

1
2
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 72, I18n.getString("deathScreen.respawn")));
this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 4 + 96, I18n.getString("deathScreen.titleScreen")));

These files add the "Respawn" and "Title screen" buttons.  The only thing we need to change is the third argument to the GuiButton constructor.  We can shift the buttons up and down by changing the values of 72 and 96, with larger values corresponding to a lower position on the screen.  The "after" photo shown above uses values of 70 and 120:

1
2
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 70, I18n.getString("deathScreen.respawn")));
this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 4 + 120, I18n.getString("deathScreen.titleScreen")));

Once this is changed, save it and then proceed as you would with any other mod (recompile, reobfuscate, and drop the class in the appropriate Minecraft .jar file after backing everything up, etc.). Now you can crank up your mouse's sensitivity and fail to unintentionally disconnect from your favorite server with ease!

Note that this does involve changing core Minecraft files and so may result in issues if there is an overlap with another mod.  Also, it seems this won't be an issue when 1.7 comes out.

Minecraft Minigame Match Dynamics

As a physics student, it can be difficult to not think of things as point particles - even players engaging in minecraft minigame matches on the Overcast Network.

So as a Saturday afternoon project, I made a simple client mod that allows you to write all of the player positions on the current world to a CSV file. Then, using  Mathematica, I processed the data and made some simple visualizations:

This shows the player positions, colored by team.  This match takes place on a map called Warlock (one of my favorites), and the goal is to be the first team to break the other team's monument (which is made out of two pieces of obsidian).  Red team won this match, and you can see a red guy sneaking near the blue's monument (probably underground) in the bottom left for a few minutes before finally breaking it.

See more visualizations here (I don't want to lag up the page with gifs), and view the source code here.

Minecraft Bukkit Server Ascii Chat Filter

I took on another small Minecraft bukkit plugin project this past Friday night (yes, it was a fun way to spend a Friday night, thanks for asking).  The server I primarily play on, Project Ares, has been getting extremely popular over the past several months, due mostly to how awesome and addicting it is.  There are hundreds of people from around the world divided among the 12 servers at any given time.  This is great, but one downside is that the chat gets very busy, and it's often filled with different languages that most of the users can't speak.  There are tons of chat related bukkit plugins that already exist, many of which have fancy features like multiple channels for different languages.

I decided to create a very simple and lightweight plugin that, when toggled on, filters out all non-ascii characters (so, most foreign languages).  Of course, it would be silly (and kind of a jerk move) if this simply muted all non English speakers server-wide.  It would have to work on a per-person basis, meaning that those with it toggled on see only ascii characters, and those with it toggled off see all characters.  I ended up with this.   You can check out the source there, but I'll briefly go through it here (because it's short, and also because I just got a WordPress plugin with allows you to insert code into posts and it's pretty neat).

 

The plugin is composed of three classes: one that registers the toggle command, one that actually filters the chat message, and one main class that puts the two together.  The main class is quite boring as it only registers the event handler and command executor (lines 6 and 7):

1
2
3
4
5
6
7
8
9
10
11
public class AsciiOnlyChatFilter extends JavaPlugin{
 
	public static ArrayList<Player> enabledPlayers = new ArrayList<Player>();
 
	public void onEnable(){
		getCommand("asciionly").setExecutor(new AsciiOnlyChatCommand());		
		this.getServer().getPluginManager().registerEvents(new AsciiOnlyChatListener(), this);
	}
	public void onDisable(){		
	}	
}

It also creates a list called enabledPlayers (line 3), which is where those who have the filter turned on are stored.  The second class handles the command ("/asciionly") if someone toggles it on:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AsciiOnlyChatCommand implements CommandExecutor{
 
	public boolean onCommand(CommandSender sender, Command command,String label, String[] args) {
		if(sender instanceof Player == false){
			sender.sendMessage(ChatColor.RED + "This command can only be used in-game.");
			return true;
		}
		Player p = (Player) sender;
 
		if(command.getName().equalsIgnoreCase("asciionly")){
			if(AsciiOnlyChatFilter.enabledPlayers.contains(p)){
				AsciiOnlyChatFilter.enabledPlayers.remove(p);
				p.sendMessage(ChatColor.GREEN + "AsciiOnly filter deactivated");	
			}else{
				AsciiOnlyChatFilter.enabledPlayers.add(p);
				p.sendMessage(ChatColor.GREEN + "AsciiOnly filter activated");
			}
			return true;
		}
		return false;
	}
}

It basically just does some boring checks and then adds the player to the enabledPlayers list if they are turning the filter on, or removes them if they are turning it off.  The final class actually filters the chat for those players with the filter on:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AsciiOnlyChatListener implements Listener {
 
	@EventHandler 
	public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event){
		String msg = event.getMessage();
		Set&lt;Player&gt; msgRecipients =  event.getRecipients();
		CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder();
 
		//If chat message is not ascii, do not display it to those with /asciionly activated
		if(!asciiEncoder.canEncode(msg)){
			for(Player enabledPlayer : AsciiOnlyChatFilter.enabledPlayers){
		    	if(msgRecipients.contains(enabledPlayer) && enabledPlayer != event.getPlayer()){
		    		event.getRecipients().remove(enabledPlayer);
		    	}		    	
			}
		}
	}
}

When it intercepts a chat message, it checks to see if it contains only ascii characters (line 11).  After this, it modifies the set that contains the message recipients, removing all of those that are on the enabledPlayers list (lines 12-14).  This seems to do the job pretty well!