Debugging the code

As you develop this plugin as well as other Bukkit plugins, there will be times when the code that you have written does not function as you would expect. This is caused by an error that lies somewhere in the code. These errors are referred to as bugs, and the process of finding these bugs and removing them is called debugging.

Learning from your mistakes

Do not be discouraged when your plugin does not work on the first try. Even experienced programmers encounter bugs throughout their code. Just because your software does not work perfectly does not mean that you are a poor developer. Being able to discover bugs and fix them is a huge part of software development. The more mistakes you make, the more you can learn from them. This is apparent in the following example.

Some day, you may write a plugin that has a list of players in it. You may then write the following for loop to loop through each player and remove those who are in the CREATIVE mode:

for (Player player : playerList) {
  if (player.getGameMode() == GameMode.CREATIVE) {
    playerList.remove(player);
  }
}

When you test this code, an error will likely occur. The error that is thrown will be a ConcurrentModificationException method. The name of the exception may not mean much to you, but it will help you narrow down the issue.

Tip

Developers need not know how to fix every error, but they should know where to find information about those errors so that they can figure out how to fix them. This is usually found in the software's documentation or public message boards. Most developers will make a note of the error message and search for information about it using Google. The top results will often be the official documentation or posts from other people who have encountered the same issue.

To know more about the error, you can search for ConcurrentModificationException; you may find the following statement from Oracle's Javadoc:

"For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. "

The official javadoc can prove to be useful. But sometimes, they are still difficult to understand. Luckily, there exist websites such as stackoverflow.com, which allow programmers to help each other debug code. If you refer back to the search results, you will see links to Stack Overflow questions as well as posts from similar websites. These links can be very helpful because often there are people just like you who have run into the same error. If you look at the questions and the answers provided by others, you can learn why this error occurs and how to remedy it.

After reading through the questions related to the concurrent modification bug, you will eventually learn that in most cases, the exception occurs when you attempt to modify a list or collection while looping through it. You will also find that you must use an iterator in order to avoid this scenario. Usually, there are examples of how to fix the bug too. In this case, there are explanations of how to correctly use an iterator to remove objects from a list. If an explanation is not present, then you can research iterators within Oracle's javadoc just as you would use Bukkit's javadoc. We can fix the previous code by using an iterator, as follows:

Iterator<Player> itr = playerList.iterator();
while (itr.hasNext()) {
  Player palyer = itr.next();
  if (player.getGameMode() == GameMode.CREATIVE) {
    itr.remove();
  }
}

After fixing the concurrent modification bug that was present in the code, you are now a more experienced programmer. You will know how to avoid this issue in the future and you have even learned how to use an iterator in the process.

When researching is not enough

Sometimes, looking through documentation and reading message boards is not enough to fix a bug. Errors such as NullPointerException are very common and can be caused by a variety of things. By researching, you will discover that NullPointerException occurs when you attempt to access a method or field of a null object. "Null" refers to the lack of a value. Therefore, a null object is an object that does not exist. However, knowing this does not always help you find exactly which object has a null value and why it is a null value in the first place. To aid in finding bugs, here are some steps to follow to pinpoint the troublesome code.

Reading the stack trace

Most errors in Java are presented in the form of a stack trace. A stack trace informs you of the lines of code that were being executed before the error occurred. On a Spigot server, these stack traces will appear similar to the following screenshot:

Reading the stack trace

Tip

If your server is hosted somewhere else and you are viewing the console through an online browser tool, the stack trace may be printed in the reverse order.

Whenever there is an exception on your server, Spigot logs the error along with the plugin that caused it. Sometimes, there are even details of which event was in progress when the error occurred. With the stack trace shown in the preceding screenshot, we can see that the error was caused by MyFirstBukkitPlugin version 0.3. If the version does not match the version that you have in your IDE, you will want to update the server with the latest version of the plugin. This way, you can be certain that the code running on the server is the same code that you have in NetBeans. We can also see that the exception was thrown when the plugin was being enabled. On the next line, we see the specific error, which was NullPointerException. On the line following that, we are told the exact line of code that caused the error. It happened within the onEnable method of the MyFirstBukkitPlugin class. In parenthesis, it states MyFirstBukkitPlugin.java:27. This tells us that the error was on line 27 of the MyFirstBukkitPlugin class, which of course is in the onEnable method. The first three lines of the stack trace are the most useful to us. You rarely have to look at the further lines for explanation. Sometimes, you will not see any of the class names in the code at the start of the stack trace. However, if you look further, you will probably see a familiar class and method name.

Now that you have the class name and line number, you can look back at your code to see if you notice why you are getting NullPointerException. Within NetBeans, I can see that line 27 is as follows:

Bukkit.getPlayer("Codisimus").sendMessage("Hello, there are " + Bukkit.getOnlinePlayers().size() + " player(s) online and " + Bukkit.getWorlds().size() + " world(s) loaded.");

Breaking down the code

The troublesome line is a very long line of code. So, it is not apparent which object has a null value. If you find yourself in a similar situation, I recommend that you break down the code into multiple lines. This will give us the following code:

Player player = Bukkit.getPlayer("Codisimus");
int playerCount = Bukkit.getOnlinePlayers().size();
int worldCount = Bukkit.getWorlds().size();
player.sendMessage("Hello, there is " + playerCount + " player(s) online and " + worldCount + " world(s) loaded.");

After installing and running this new code, you should see the same error in the console, but it will point you to a different line of code. With this new code, you will now see that the exception is thrown on line 30, which is the last line of the preceding code segment.

Adding debug messages

There is still a lot going on in that single line of code. Therefore, you may not be sure about which variable is null. Is it player, playerCount, or worldCount? If you need some additional help, you can add what we call debug messages to your code. These messages can print information to the console log to indicate what is happening throughout the code. There are a few ways to log messages in a Bukkit plugin. The easiest way is to use the System.out.println(String string) method. However, it is a better practice to utilize the logger that the Spigot server assigns to your plugin. The logger can be retrieved with the getLogger method. This method is in the JavaPlugin class. You can access it from within the onEnable method. These debug messages will only be temporary. Therefore, you can use whichever method you prefer. But I do suggest that you try using the logger as it also prints out plugin information. In our example, we will use the logger.

Now that we know how to print messages, let's log the values of each variable, as follows:

Player player = Bukkit.getPlayer("Codisimus");int playerCount = Bukkit.getOnlinePlayers().size();int worldCount = Bukkit.getWorlds().size();Logger logger = getLogger();logger.warning("player: " + player);logger.warning("playerCount: " + playerCount);logger.warning("worldCount: " + worldCount);player.sendMessage("Hello, there is " + playerCount + " player(s) online and " + worldCount + " world(s) loaded.");

Tip

Note that we have added the debug messages before the faulty line of code. Once an exception is thrown, the computer stops executing the code. Therefore, if the debug messages were after the sendMessage call, the messages would never be printed.

Adding debug messages

Once you install and run the updated code, you will see the debug messages within the console:

Now, we can clearly see that player has a null value.

Referring back to the Javadoc

If you look back at the line of code where player is set and then read the Bukkit javadoc, you will learn that player has a value of null because the requested player, Codisimus, is not online. If the player cannot be found, null is returned.

Referring back to the Javadoc

As you can see, the bug in the code may not be exactly at the line given in the stack trace. In this scenario, the null value is set a few lines earlier.

Fixing the bug only after you understand it

Now that the bug is exposed, we can fix it. In the case of NullPointerException, there are two solutions. Do not simply fix the bug a certain way because you can. You should strive to understand why the bug is present and how the code should function instead. Maybe, the variable player is never supposed to have a null value. If I know that the player Codisimus will always be online, then perhaps, I misspelled the username. However, we know that Codisimus will not always be online. So, within this plugin, the player variable will sometimes have a null value. In that scenario, we do not want to try to send a message to the player, since it will throw NullPointerException. To remedy this, we can put the line of code within an if statement, which developers typically refer to as a null check:

Player player = Bukkit.getPlayer("Codisimus");
int playerCount = Bukkit.getOnlinePlayers().size();
int worldCount = Bukkit.getWorlds().size();
//Logger logger = getLogger();
//logger.warning("player: " + player);
//logger.warning("playerCount: " + playerCount);
//logger.warning("worldCount: " + worldCount);
if (player != null) {
    player.sendMessage("Hello, there is " + playerCount + " player(s) online and " + worldCount + " world(s) loaded.");
}

Tip

Note that I have changed the debug messages to comments by prepending them with // so that those messages are not printed to the log. Alternatively, I can remove these lines completely if I feel that I will never need them again.

Now that we have added the null check, the message will only be sent if the player is not null.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.119.148