Welcome to the BukkitWiki!

This Wiki is home to Bukkit's documentation and regulations surrounding the Bukkit Project and it's services. Want to help out? We would love to have you! Signup to get started!

Common Coding Etiquette

From BukkitWiki
Jump to: navigation, search
Icon-remove.png
This page has been marked for deletion.

This page has been marked for deletion by mbaxter. For more information, see Category:Pages for deletion.
If you feel that this page should not be deleted, please discuss the article's status on its Talk page.
Reason for deletion: Covered elsewhere

TODOIcon.png
Working on it...

This page was just created! Please check back again later!


Introduction

Use the API: http://jd.bukkit.org/apidocs/

also the index: http://jd.bukkit.org/apidocs/index-all.html

Search on the resource forum: http://forums.bukkit.org/forums/resources.60/

and the Plugin Development forum: http://forums.bukkit.org/forums/plugin-development.5/

or use google ;)

Storage - Storing Primitive Data and Objects

It is important to cache data to reduce calculation times and make plugins perform better. The following code gives an example of how to apply this concept:

import java.util.ArrayList;
import java.util.List;
import org.bukkit.block.Sign;

public class SignStorageArray {
    private List<Sign> signsStorage;

    public SignStorageArray() {
        signsStorage = new ArrayList<Sign>(10);

    public void makeSomethingWithSigns() {
        loadSigns();
        for (int i = 0; i < signsStorage.size(); i++) {
            Sign tmpSign = signsStorage.get(i);
            tmpSign.setLine(0, "first line");
            tmpSign.setLine(1, "second line");
        }
    }

    private void loadSigns() {
        if (signsStorage.size() == 0) {
            loadSignsFromSomewhere();
        }
    }

    private void loadSignsFromSomewhere() {
        // Get the coordinates of Signs from somewhere and add them to signStorage.
        // This is more efficient than checking every block of every loaded chunk for signs.
    }
}

In the above example, the class uses a List (more specifically, an ArrayList) to store signs. A List has a dynamic size which means that elements can continually be added without potentially filling the List. Note that behind the scenes, the List will eventually fill up and be forced to copy all of its contents to a new location. Because this operation is (relatively) expensive, it is a good idea to give the List an initial capacity of roughly how large it should be. If this information isn't available, don't give it any value (signsStorage = new ArrayList<String>();). While this isn't as efficient, it is perfectly acceptable (and quite common). As far as other operations go, accessing an element by its index is very fast, but accessing an element by its name (as in searching for a sign in a list) is slower and takes longer as the List grows. Also remember that most of the time, adding a new element is very fast, but sometimes it can be slower (and takes longer as the List grows).


The above example is great for storing a simple list of Signs. However, it is usually very convenient to store a name of ID number with those signs. This is the perfect time to use a Map (more specifically, a HashMap. A Map excels in the two places that a List is not so great, adding elements and finding elements by their keys. Maps store elements in pairs. The first element is called a key and the second element is called a value. Maps are designed to store values based on their keys. In a List, a fast way to find an element was to search by its index (myList.get(3)). In a Map, a fast way to find an element is to search by its key. Seeing as List indecies are quite arbitrary, having sensible key names in a Map can be quite helpful. Below is an example that is similar to the one above. The biggest difference is this one uses a Map that uses a String as a key.

import java.util.HashMap;
import java.util.Map;
import org.bukkit.block.Sign;

public class SignStorageMap {
    private HashMap<String, Sign> signsStorage;

    public SignStorageMap() {
        this.signsStorage = new HashMap<String, Sign>();
    }

    public void makeSomethingWithSigns(String identifier) {
        loadSigns();
        Sign tmpSign = signsStorage.get(identifier);
        tmpSign.setLine(0, "first line");
        tmpSign.setLine(1, "second line");
    }

    private void loadSigns() {
        if (signsStorage.size() == 0) {
            loadSignsFromSomewhere();
        }
    }

    private void loadSignsFromSomewhere() {
        // Get the coordinates of Signs from somewhere and add them to signStorage.
        // This is more efficient than checking every block of every loaded chunk for signs.
    }
}

The above Map example behaves very similarly to the List example. The biggest difference is with makeSomethingWithSigns(...) because every sign is attached to a String, it is very easy to get a Sign based on its key and change it. In the previous example, figuring out which sign should change was quite difficult.

As far as performance is concerned, a Map is very fast when it comes to adding new elements. Finding an element is a bit slower and takes longer with more elements, but it scales slower than finding an element in a List (finding an element in a large Map is considerably faster than finding an element in a large List). Also, unlike a List, Maps never have occasional expensive add operations.

SaveBlocks - How to save blocks and restore them

A Block is more than only the Material (GLASS, IRON, ...). To save a Block correctly use a BlockState.

What is a block state:

"A BlockState is basically just what it says: a block's state at the point of time when it is created. Unlike the regular Block instance, it won't automatically update/change when the block itself changes and vice versa (the block won't update when the BlockState changes).

The main uses I can think of right now for BlockStates: Storing a snapshot of blocks to use it later - e.g. you set an area of blocks to something for your plugin, but you want to reset it to the original blocks later. Instead of manually storing the previous blockIDs/data (and TileEntity data like chest contents, sign text, etc.) you just store BlockState's and then update() them.

Getting access to enhanced block-specific methods for TileEntities (e.g. Signs, Chests, Furnaces, etc.) - common example is setting the contents of a Sign. The class "Sign" extends BlockState, so getting the BlockState, setting the lines, and then updating the sign is the way to go." ( from: Bone008 )


TODO: make an example.

Commands

How can I handle commands?

needed?

or is

http://forums.bukkit.org/threads/tutorial-basic-commands-want-to-learn-how-to-make-them-permission-api-support-updated.24635/

and

http://forums.bukkit.org/threads/tut-how-to-create-the-i-item-command-using-hashmaps.45378/

enough?

What to do if the CommandSender is not a Player

Quite a few people feel that passing a CommandSender to onCommand(...) is a hindrance and should just be a Player instead. However, accepting a CommandSender is really not that inconvenient and allows Command Blocks and server administrators from console to issue commands. Allowing commands from sources other than Players also makes plugins feel more polished and easier to use.

The biggest thing to keep in mind when working with a CommandSender (vs. a Player) is whether or not in-world features are important. For example, the command /kill doesn't make sense from console. Therefore, before processing that command, it is important to make sure that the sender is a Player. However, take the command /broadcast. It is perfectly acceptable for any type of CommandSender to broadcast a message across the server. Therefore, any CommandSender should be allowed to use it. The following code shows an example of each situation:

@Override
public boolean onCommand(CommandSender sender, Command cmd, String alias, String[] args)
{
    String name = cmd.getName().toLowerCase();
    if (name.equals("sethome")) {
        if (!(sender instanceof Player)) {
            sender.sendMessage("Only players can set homes. Why would anyone else want to?");
            return true;
        }

        // Actual home setting code goes here.
        return true;
    } else if (name.equals("healall")) {
        // Any sender is allowed to heal every player.
        // Actual healing code goes here.
        return true;
    }

    return false;
}

Events - How to react on events, what is special on BUKKIT event managment?

  • use ignorecancel Anotation

Ignore cancelled events

  • you can register Sub-Events

Register Sub Events

  • Unregister Listener

Unregistering of Events

  • Action comes before event

TODO


Discussion: BukkitForum

Loggers

Instead of creating your own loggers, use the logger supplied by bukkit. Loggers all have names, and should have a unique name, never create one named "Minecraft". Use JavaPlugin's getLogger method instead of creating your own logger from the logger factory.

File Locations

Your plugin's data folder need not always reside in the plugins directory on a server. Use JavaPlugin's getDataFolder method instead of hard coding your file paths to the plugin directory.

Further Reading

How to make your plugin better

Bukkit preview

Bukkit ressources