4
\$\begingroup\$

I have created a lot of spigot plugins in my "career" as a developer for a bunch of Minecraft servers, but every time I start writing a new plugin, I basically "reinvent" the structure of my main plugin class, always so it fits best into what I consider to be a readable plugin, that also other developers that will work with these plugins after me can do so without getting a headache.

My most recent Main-Class looks like this:

package com.clanplugin;

import java.math.RoundingMode;
import java.util.HashMap;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scoreboard.Scoreboard;

import com.earth2me.essentials.api.UserDoesNotExistException;

import com.clanplugin.commands.ClanCommand;
import com.clanplugin.commands.CommandRegistry;
import com.clanplugin.commands.implementation.AcceptCommand;
import com.clanplugin.commands.implementation.CreateCommand;
import com.clanplugin.commands.implementation.DeleteCommand;
import com.clanplugin.commands.implementation.DerankCommand;
import com.clanplugin.commands.implementation.InfoCommand;
import com.clanplugin.commands.implementation.InviteCommand;
import com.clanplugin.commands.implementation.InvitesCommand;
import com.clanplugin.commands.implementation.KickCommand;
import com.clanplugin.commands.implementation.LeaveCommand;
import com.clanplugin.commands.implementation.ListCommand;
import com.clanplugin.commands.implementation.MoneyCommand;
import com.clanplugin.commands.implementation.UprankCommand;
import com.clanplugin.commands.implementation.RejectCommand;
import com.clanplugin.commands.implementation.RevokeCommand;
import com.clanplugin.commands.implementation.SetCbCommand;
import com.clanplugin.commands.implementation.SetLeaderCommand;
import com.clanplugin.commands.implementation.SetNameCommand;
import com.clanplugin.commands.implementation.SetTagCommand;
import com.clanplugin.commands.implementation.ShowMaxClanMemberCommand;
import com.clanplugin.commands.implementation.ToggleMoneyCommand;
import com.clanplugin.commands.implementation.ToplistCommand;
import com.clanplugin.database.DatabaseConnector;
import com.clanplugin.database.DatabaseMethods;
import com.clanplugin.listener.PlayerConnectListener;
import com.clanplugin.listener.PlayerDisconnectListener;
import com.clanplugin.listener.TagSetterListener;
import com.clanplugin..manager.ClantagCache;
import com.clanplugin..manager.MessageManager;
import net.ess3.api.Economy;

public class Main extends JavaPlugin {
    
    private static CommandRegistry commandRegistry;
    
    private static Main plugin;
    public static HashMap<Player, Long> lastUseOfCommand = new HashMap<Player, Long>();

    public void onEnable() {
        plugin = this;

        saveDefaultConfig();
        registerCommands();
        registerListeners();
        loadDatabase();
        setDataUponReload();
    }

    public void onDisable() {
        DatabaseConnector.disconnect();

        System.out.println(MessageManager.disabledPluginConsoleMessage());
        plugin = null;
    }

    public static Main getInstance() {
        return plugin;
    }

    private void loadDatabase() {
        DatabaseConnector.connect();
        DatabaseMethods.initialiseDatabaseTables();
    }
    
    private void registerCommands() {
        this.getCommand("clan").setExecutor(new ClanCommand());
        
        commandRegistry = new CommandRegistry();
        
        commandRegistry.registerCommand(new ShowMaxClanMemberCommand());
        commandRegistry.registerCommand(new ListCommand());
        commandRegistry.registerCommand(new LeaveCommand());
        commandRegistry.registerCommand(new DeleteCommand());
        commandRegistry.registerCommand(new InvitesCommand());
        commandRegistry.registerCommand(new ToplistCommand());
        commandRegistry.registerCommand(new MoneyCommand());
        commandRegistry.registerCommand(new ToggleMoneyCommand());
        
        commandRegistry.registerCommand(new InviteCommand());
        commandRegistry.registerCommand(new AcceptCommand());
        commandRegistry.registerCommand(new RejectCommand());
        commandRegistry.registerCommand(new KickCommand());
        commandRegistry.registerCommand(new RevokeCommand());
        commandRegistry.registerCommand(new InfoCommand());
        commandRegistry.registerCommand(new SetTagCommand());
        commandRegistry.registerCommand(new SetNameCommand());
        commandRegistry.registerCommand(new SetLeaderCommand());
        commandRegistry.registerCommand(new UprankCommand());
        commandRegistry.registerCommand(new DerankCommand());
        commandRegistry.registerCommand(new SetCbCommand());

        commandRegistry.registerCommand(new CreateCommand());
        
        
    }
    
    public static CommandRegistry getCommandRegistry() {
        return commandRegistry;
    }
    
    private void registerListeners() {
        getServer().getPluginManager().registerEvents(new PlayerConnectListener(), this);
        getServer().getPluginManager().registerEvents(new PlayerDisconnectListener(), this);
        getServer().getPluginManager().registerEvents(new TagSetterListener(), this);
    }
    
    private void setDataUponReload() {
        for(Player player : Bukkit.getOnlinePlayers()) {
            if (DatabaseMethods.isPlayerInClan(player.getUniqueId())) {
                String playerUUID = DatabaseMethods.getPlayerUUID(player.getUniqueId());
                if (playerUUID.equals(player.getUniqueId().toString()))
                    DatabaseMethods.addPlayerName(player.getName(), player.getUniqueId());

                ClantagCache.put(player, DatabaseMethods.getClanTagByClanID(DatabaseMethods.getClanIDByPlayerUuid(player.getUniqueId())));
            }
            
            PlayerConnectListener.joinTimes.put(player, System.currentTimeMillis());
        }
    }
    
    @SuppressWarnings("deprecation")
    public static void updateScoreBoard(Player player) {
        try {

        if (player == null || player.getScoreboard() == null) {
            return;
        }

        Scoreboard board = player.getScoreboard();

        if (Bukkit.getOnlinePlayers().size() == 0) {

            board.getTeam("onlineplayers").setPrefix(" " + "0" + "/" + "" + Bukkit.getServer().getMaxPlayers());

        } else {

            board.getTeam("onlineplayers")
                    .setPrefix("" + Bukkit.getOnlinePlayers().size() + "/" + Bukkit.getServer().getMaxPlayers());

        }

        try {
            board.getTeam("Kontostandcheck")
                    .setPrefix("" + Economy.getMoneyExact(player.getName()).setScale(2, RoundingMode.DOWN) + "$");
        } catch (IllegalStateException | IllegalArgumentException | UserDoesNotExistException e) {
            e.printStackTrace();
        }
        
        } catch (Exception e) {
            System.out.println("[ClanSystem] This is not the live environement or the scoreboard configuration has changed. Please review carefully.");
            e.printStackTrace();
        }

    }

}

It would be great to get some fresh idea's what can be improved, maybe not only regarding the spigot-related stuff, but also maybe some general bad habits I might have. Thanks a lot in advance! If you have any questions to some functionality of the classes feel free to ask me about it in the comments :)

\$\endgroup\$
4
\$\begingroup\$

Your formatting is partly off, use a code-formatter.


public class Main extends JavaPlugin {

I'm not familiar anymore with the Bukkit naming convetions, but this should most likely be called SpigotPlugin. Main is kinda reserved for main classes which are the main entry point for applications.


Do you require the static instance mechanic? If you can, you should avoid it.


public static HashMap<Player, Long> lastUseOfCommand = new HashMap<Player, Long>();

This seems to be never used.

Also, given that is a static map, one can most likely come up with a better API when required. For example, handing the current instance to the created listeners.


System.out.println(MessageManager.disabledPluginConsoleMessage());

If I remember right, Bukkit does sport a logging solution, you should use that.


this.getCommand("clan").setExecutor(new ClanCommand());

You're inconsistent regarding your this usage.


PlayerConnectListener.joinTimes.put(player, System.currentTimeMillis());

I'm not sure if that is part of your code, but be aware that currentTimeMillis is wall-clock time. That means it observes leap-seconds, time zone changes and the like. So given the following setup:

long start = System.currentTimeMillis();

// Let 5 seconds pass.

long elapsed = System.currentTimeMillis() - start;

elapsed can be any value, from most likely "5000" to "500000" or even "-25000".


board.getTeam("onlineplayers").setPrefix(" " + "0" + "/" + "" + Bukkit.getServer().getMaxPlayers());
// ...
board.getTeam("onlineplayers")
                    .setPrefix("" + Bukkit.getOnlinePlayers().size() + "/" + Bukkit.getServer().getMaxPlayers());

What are these constructs? I'm not a friend of "empty string casts", if you can, be explicit about it, for example with Integer.toString(), or use String.format() or similar:

board.getTeam("onlineplayers").setPrefix(String.format("0/%i",
    Bukkit.getServer().getMaxPlayers()));
// ...
board.getTeam("onlineplayers").setPrefix(String.format("%i/%i",
    Integer.valueOf(Bukkit.getOnlinePlayers().size()),
    Integer.valueOf(Bukkit.getServer().getMaxPlayers()));

if (Bukkit.getOnlinePlayers().size() == 0) {

There's not really a point in observing this just to build the string differently, just alw3ays use the else path.


        try {
            board.getTeam("Kontostandcheck")
                    .setPrefix("" + Economy.getMoneyExact(player.getName()).setScale(2, RoundingMode.DOWN) + "$");
        } catch (IllegalStateException | IllegalArgumentException | UserDoesNotExistException e) {
            e.printStackTrace();
        }

As said previously, you want to log errors properly.

        } catch (Exception e) {
            System.out.println("[ClanSystem] This is not the live environement or the scoreboard configuration has changed. Please review carefully.");
            e.printStackTrace();
        }

Same here.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.