How to Thread?

Discussion in 'Plugin Development' started by javoris767, Apr 7, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    javoris767

    My plugin seems the lag I guess and someone suggested that I needed to thread it or something. Could someone explain a bit clearer XD. Right now I'm looking the the Schedule Programming wiki page.


    Code: here
     
  2. Offline

    javoris767

    Bump o-o
     
  3. Offline

    MrTwiggy

    I didn't look too much into your code, but generally the way I would handle it is to wait until a Vote event occurs, and then schedule an Asynchronous task using the Bukkit scheduler, and perform the majority of your calculations within that (make sure everything you do is thread safe). Once you have the results or you need to modify something else (that isn't thread safe) you can add it to a list updates that can be polled every X seconds and updated.

    The idea is to transfer your intensive calculations/operations to a separate thread so that the main thread in which Minecraft and most bukkit plugins operate on isn't slowed.
     
  4. Offline

    Scizzr

    I think what they're talking about is that you should execute SQL queries on another thread so that the server doesn't lag while it's querying the database. If you're using either MySQL or SQLite, this is what I use.

    Note: This can only be used for writing data since I haven't made any way to retrieve the query data after execution.

    Code:
    String name = "Scizzr";
    int deaths = 42;
    String query = String.format("UPDATE `users` SET `deaths` = %d WHERE `player` = '%s'", deaths, name);
    new Thread(new MySQLWriter(Main.plugin, query)).start();
    

    And here's all of the things you need to get this to work:

    Main.class (Plugin itself)
    Show Spoiler

    Code:
    public class Main extends JavaPlugin {
        public static Main plugin;
        public DbMySQL dbconn;
     
        public void onEnable() {
            plugin = this;
         
            //Change these to fit your database settings
            dbconn = new DBMySQL(this, "localhost", 3306, "minecraft", "password");
        }
    }
    


    DBMySQL.class (MySQL database object)
    Show Spoiler

    Code:
    public class DbMySQL {
        private final Plugin plugin;
        private final String url;
        private Logger log;
     
        /**
        * Connect to a MySQL database
        *
        * @param plugin
        *            the plugin handle
        * @param host
        *            MySQL server IP or hostname
        * @param port
        *            MySQL server port
        * @param database
        *            MySQL database name
        * @param user
        *            MySQL access username
        * @param password
        *            MySQL access password
        */
        public DbMySQL(final Plugin plugin, final String host, final int port, final String user, final String password, final String database) {
            this.plugin = plugin;
            //url = "jdbc:mysql://" + host + ":" + port + "/" + database + "?user=" + user + "&password=" + password;
            url = String.format("jdbc:mysql://%s:%s/%s?user=%s&password=%s&allowMultiQueries=true", host, port, database, user, password);
         
            log = plugin.getServer().getLogger();
     
            initDriver("com.mysql.jdbc.Driver");
        }
     
        /**
        * Connect/create a SQLite database
        *
        * @param plugin
        *            the plugin handle
        * @param filePath
        *            database storage path/name.extension
        */
        public DbMySQL(final Plugin plugin, final String filePath) {
            this.plugin = plugin;
            url = "jdbc:sqlite:" + new File(filePath).getAbsolutePath();
            log = plugin.getServer().getLogger();
     
            initDriver("org.sqlite.JDBC");
        }
     
        private void initDriver(final String driver) {
            try {
                Class.forName(driver);
            } catch (final Exception e) {
                log.severe("Database driver error:" + e.getMessage());
            }
        }
     
        /**
        * Get a string from query(), automatically checks for null.
        *
        * @param result
        *            the returned value of query()
        * @param column
        *            the column number, starts from 1
        * @return integer value of the column
        */
        public CopyOnWriteArrayList<Integer> resultInt(ResultSet result, int column) {
            CopyOnWriteArrayList<Integer> data = new CopyOnWriteArrayList<Integer>();
     
            if (result == null) {
                return null;
            }
     
            try {
                synchronized (data) {
                    while (result.next()) {
                        data.add(result.getInt(column));
                    }
                }
                return data;
            } catch (SQLException e) {
                log.severe("Database result error: " + e.getMessage());
            }
     
            return null;
        }
     
        /**
        * Get a string from query(), automatically checks for null.
        *
        * @param result
        *            the returned value of query()
        * @param column
        *            the column number, starts from 1
        * @return string value of the column or null
        */
        public CopyOnWriteArrayList<String> resultString(ResultSet result, int column) {
            CopyOnWriteArrayList<String> data = new CopyOnWriteArrayList<String>();
     
            if (result == null) {
                return null;
            }
     
            try {
                synchronized (data) {
                    while (result.next()) {
                        data.add(result.getString(column));
                    }
                }
                return data;
            } catch (SQLException e) {
                log.severe("Database result error: " + e.getMessage());
            }
     
            return null;
        }
     
        /**
        * Sends a query to the SQL Returns a ResultSet if there's anything to
        * return
        *
        * @param query
        *            the SQL query string
        * @return ResultSet or null
        */
        public ResultSet query(final String query) {
            return query(query, false);
        }
     
        /**
        * Sends a query to the SQL Returns a ResultSet if there's anything to
        * return
        *
        * @param query
        *            the SQL query string
        * @param retry
        *            set to true to retry query if locked
        * @return ResultSet or null
        */
        public ResultSet query(final String query, final boolean retry) {
            try {
                final Connection connection = DriverManager.getConnection(url);
                final PreparedStatement statement = connection.prepareStatement(query);
     
                if (statement.execute()) {
                    return statement.getResultSet();
                }
            } catch (final SQLException e) {
                final String msg = e.getMessage();
     
                log.severe("Database query error: " + msg);
     
                if (retry && msg.contains("_BUSY")) {
                    log.severe("Retrying query...");
     
                    plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
                        @Override
                        public void run() {
                            query(query);
                        }
                    }, 20);
                }
            }
            return null;
        }
    }
    


    MySQLWriter.class (MySQL writer object)
    Show Spoiler

    Code:
    public class MySQLWriter implements Runnable {
        Main plugin;
        String query;
       
        public MySQLWriter(Main plugin, String query) {
            this.plugin = plugin;
            this.query = query;
        }
       
        public void run() {
            DbMySQL conn = plugin.dbconn;
            conn.query(query);
        }
    }
    


    Let me know how it goes. ^_^
     
  5. Offline

    javoris767

    I was looking through my friend's plugin and thought it was the way to do it. Although, I think its not passing the even at all. Also, the sql is fine. Its not doing much work.

    Code:
    package me.javoris767.votesql.listeners;
     
    import java.util.List;
     
    import me.javoris767.votesql.VoteSQL;
    import me.javoris767.votesql.utils.Functions;
    import me.javoris767.votesql.utils.VoteSQLAPI;
    import me.javoris767.votesql.utils.VoteSQLChat;
     
    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.Listener;
     
    import com.vexsoftware.votifier.model.Vote;
    import com.vexsoftware.votifier.model.VotifierEvent;
     
    public class VotingListener implements Listener
    {
        private VoteSQL _plugin;
     
        public VotingListener(VoteSQL plugin)
        {
            _plugin = plugin;
        }
     
     
        @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled=true)
        public void onVote(final VotifierEvent event)
        {
            final Vote vote = event.getVote();
            final String siteVotedOn = vote.getServiceName();
            final String username = vote.getUsername();
            final Player player = Bukkit.getPlayer(username);
            final int money = _plugin.getConfig().getInt("VoteSQL.currency.Amount");
     
            // Broadcast Vote
            if (_plugin.getConfig().getBoolean("VoteSQL.onVote.messageEnabled") == true)
            {
                Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable() {
     
                    public void run() {
                        VoteSQLChat.broadcastVoteMessage(username, siteVotedOn);
                    }
                }, 1L);
            }
     
            // Currency
            if (Bukkit.getPluginManager().getPlugin("Vault") == null) {
                VoteSQLChat.logSevere("Vault not found!");
            }else
                if(_plugin.getConfig().getBoolean("VoteSQL.currency.Enabled") == true)
                {
                    Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable() {
     
                        public void run() {
                            VoteSQLChat.sendCurrencyReveivedMessage(player, username, money);
                            Functions.addMoney(player, _plugin.getConfig().getInt("VoteSQL.currency.Amount"));
                        }
                    }, 1L);
                }
     
     
            // Add to SQL
            if (_plugin.getConfig().getBoolean("VoteSQL.MySQL.Enabled") == true)
            {
                Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable() {
     
                    public void run() {
                        if(username == "" || username == null) {
                            VoteSQLChat.logInfo("Empty vote string");
                        }else{
                            Functions.addData(username);
                        }
                    }
                }, 1L);
            }
     
            // Custom Commands
            //final List<String> commands = new ArrayList<String>();
            final List<String> commands = _plugin.getConfig().getStringList("VoteSQL.onVote.Commands");
            if(_plugin.getConfig().getBoolean("VoteSQL.onVote.commandsEnabled") == true)
            {
                Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable() {
                    public void run() {
                        for (String command : commands) {
     
                            if(command.contains("%P")) {
                                command = command.replace("%P", player.getName());
                            }
     
                            command = Functions.formatMessage(command, vote);
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
                        }
                    }
                }, 1L);
            }
     
            // Add to FlatFile
            if (_plugin.getConfig().getBoolean("VoteSQL.FlatFile.Enabled") == true)
            {
                Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable() {
     
                    public void run() {
                        Integer numberOfVotes = VoteSQLAPI.voteMap.get(username
                                .toLowerCase());
                        if(username == "" || username == null) {
     
                        }else{
                            numberOfVotes++;
                            VoteSQLAPI.voteMap.put(username.toLowerCase(), numberOfVotes);
                            VoteSQLAPI.saveDataFile();
                        }
                    }
                }, 1L);
            }
        }
    
     
Thread Status:
Not open for further replies.

Share This Page