Tutorials/Tutorial 1: The basics

Overview

Welcome, plugin developer! This tutorial will show you how to

  • create your own plugin using the Controllable Mobs API in eclipse
  • spawn entities (like Zombies)
  • taking control of these entities
  • create basic actions (like moving and attacking)
  • publish your plugin

The following things will be required:

  • you are familiar with the Java programming language
  • you know how to work with eclipse
  • you know how to set up a plugin for bukkit (you should have read and understood the bukkit plugin tutorial)

At the end of this tutorial we will have a plugin that will

  • Spawn one or more Zombies at locations the player can choose
  • Putting the spawned Zombies under player control
  • Let the Zombies move around or attack targets the player selects

Preparing a project

Creating the project

  1. download the latest Bukkit API recommended or beta build at dl.bukkit.org
  2. download the latest Controllable Mobs API at the files section
  3. place both files in the same folder, you will reference them later
  4. start eclipse
  5. open File > New > Java Project
  6. insert a name for your project. Click Next
  7. Switch to the Libraries tab
  8. Click on Add External JARS... and select the two files you downloaded previously. Select Open
  9. Now select the bukkit entry in the list. Click on the small dropdown-arrow to the left of the entry. Select Javadoc Location: (None) and click on Edit.... Insert http://jd.bukkit.org/apidocs/ after Javadoc location path and click OK
  10. Repeat the last step for the Controllable Mobs API entry but use http://projects.ntcomputer.de/javadoc/cmapi/ as the Javadoc location path
  11. Now your screen should look like this:

  12. Click Finish and switch to the Java workbench

Creating the backbone

  1. Create your main package
  2. Create the plugin class. It should look like this:

  3. Right-click the created class and select Source > Override/Implement Methods..., mark onEnable and onDisable in the list and click OK
  4. Now your class' code should look like this:
package de.ntcomputer.minecraft.cmapitutorial1;

import org.bukkit.plugin.java.JavaPlugin;

public final class TutorialPlugin1 extends JavaPlugin {

	@Override
	public void onDisable() {
		// TODO Auto-generated method stub
		super.onDisable();
	}

	@Override
	public void onEnable() {
		// TODO Auto-generated method stub
		super.onEnable();
	}

}

Last step: Create the plugin.yml file in your project directory. It should look like the following example. Be sure to use only spaces and no tabs!

name: CMAPI Tutorial 1
version: 1.0
description: A first plugin to demonstrate the usage of the Controllable Mobs API.
author: Cybran
website: http://dev.bukkit.org/server-mods/controllable-mobs-api/pages/tutorials/tutorial-1-the-basics/
main: de.ntcomputer.minecraft.cmapitutorial1.TutorialPlugin1

Spawning entities

We want to spawn a Zombie. So let's add a method to our main class to do so:

	private void spawnZombie(Location spawnLocation) {
		spawnLocation.getWorld().spawn(spawnLocation, Zombie.class);
	}

As you can see, we need a suitable location to spawn such a Zombie. Let the player decide about the location! For example, let's spawn a Zombie on every block the player right-clicks with a .. rotten flesh in his hands. Yeah, I think that fits. To achieve this, we have to add an event listener that will perform the checks, decrease the item stack the player is holding and spawn a Zombie:

	@EventHandler
	public void onBlockRightClick(PlayerInteractEvent event) {
		if(event.getAction()==Action.RIGHT_CLICK_BLOCK) {
			if(event.getPlayer().getItemInHand().getType()==Material.ROTTEN_FLESH) {
				int amount = event.getPlayer().getItemInHand().getAmount();
				if(amount==1) {
					event.getPlayer().getInventory().removeItem(event.getPlayer().getInventory().getItemInHand());
				} else {
					event.getPlayer().getItemInHand().setAmount(amount-1);
				}				
				this.spawnZombie(event.getClickedBlock().getLocation().add(0, 1, 0));
			}
		}
	}

Our event listener won't work until we tell the system to do so. Our class has to implement the "Listener" interface:

public final class TutorialPlugin1 extends JavaPlugin implements Listener {

Also, we have to change our onEnable method:

	@Override
	public void onEnable() {
		this.getServer().getPluginManager().registerEvents(this, this);
	}

Now, our event listener will be enabled when the plugin is being enabled.

Spawning Zombies should work now. Our complete code looks as follows:

package de.ntcomputer.minecraft.cmapitutorial1;

import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Zombie;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.plugin.java.JavaPlugin;

public final class TutorialPlugin1 extends JavaPlugin implements Listener {

	@Override
	public void onDisable() {
		// TODO Auto-generated method stub
		super.onDisable();
	}

	@Override
	public void onEnable() {
		this.getServer().getPluginManager().registerEvents(this, this);
	}
	
	private void spawnZombie(Location spawnLocation) {
		spawnLocation.getWorld().spawn(spawnLocation, Zombie.class);
	}
	
	@EventHandler
	public void onBlockRightClick(PlayerInteractEvent event) {
		if(event.getAction()==Action.RIGHT_CLICK_BLOCK) {
			if(event.getPlayer().getItemInHand().getType()==Material.ROTTEN_FLESH) {
				int amount = event.getPlayer().getItemInHand().getAmount();
				if(amount==1) {
					event.getPlayer().getInventory().removeItem(event.getPlayer().getInventory().getItemInHand());
				} else {
					event.getPlayer().getItemInHand().setAmount(amount-1);
				}				
				this.spawnZombie(event.getClickedBlock().getLocation().add(0, 1, 0));
			}
		}
	}

}

Control the entities

Assuming control

Until now the problem is: The Zombie is a Zombie. And it will attack the player who spawned him. Because we don't like this, it's going to be changed in our spawnZombie method. Let's put the Zombie under our control, removing any default behaviors it had. It's like brain-washing (well.. bad example for a Zombie). But before we do that, we need a place to save all the zombie instances (so we can interact with them later). Add the following to our main class:

	private HashMap<Player,ControllableMob<Zombie>> zombieMap;

Add the following to our onEnable method to initialize the map:

	this.zombieMap = new HashMap<Player,ControllableMob<Zombie>>();

Now, let's change the spawnZombie method:

	private void spawnZombie(Player owner, Location spawnLocation) {
		Zombie zombie = spawnLocation.getWorld().spawn(spawnLocation, Zombie.class);
		ControllableMob<Zombie> controlledZombie = ControllableMobs.assign(zombie, true);
		this.zombieMap.put(owner, controlledZombie);
	}

As you can see, we will bind each zombie to its owner. Therefore, we have to change the spawnZombie-call in onBlockRightClick to:

	this.spawnZombie(event.getPlayer(), event.getClickedBlock().getLocation().add(0, 1, 0));
Quote:

Calling ControllableMobs.assign() will put an entity under developer control. If the second argument is set to true, the entities default behaviors will be removed. It will return an instance of ControllableMob which can be used to move the entity around, let it attack or customize its AI. You will see!

Some cleanup

The first thing we have to worry about is the stability and accuracy of our code. We have to be aware that the player can leave the server at any moment or our plugin can be disabled. So let's implement some cleanup.

Quote:

If the server restarts, the entities you had controlled will be reset and act normal again.

I suggest to just kill the Zombies when a player leaves or the plugin is being disabled. Change the onDisable method to the following:

	@Override
	public void onDisable() {
		for(ControllableMob<Zombie> controlledZombie: this.zombieMap.values()) {
			controlledZombie.getActions().die();
		}
		this.zombieMap.clear();
		this.zombieMap = null;
	}

and add the following event methods that catches a player disconnect:

	private void cleanZombie(Player owner) {
		if(this.zombieMap.containsKey(owner)) {
			ControllableMob<Zombie> controlledZombie = this.zombieMap.get(owner);
			controlledZombie.getActions().die();
			this.zombieMap.remove(owner);
		}
	}
	
	@EventHandler
	public void onPlayerLeave(PlayerQuitEvent event) {
		this.cleanZombie(event.getPlayer());
	}

Also, let's change the spawnZombie method once again to kill any previously spawned Zombie by adding the following line at the beginning:

	this.cleanZombie(owner);

Publish your plugin

Resources


Comments

  • To post a comment, please or register a new account.
Posts Quoted:
Reply
Clear All Quotes