1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

[API] Network API.

Discussion in 'Plugins/Codes' started by Checksum, Jun 17, 2015.

  1. Checksum

    Checksum Member

    Joined:
    Jun 17, 2015
    Messages:
    6
    Likes Received:
    1
    Trophy Points:
    3
    Gender:
    Male
    Hello Folks.

    My name's Nick. I'm a[n] experienced Java developer and have loved to explore my way through the several different server APIs for Minecraft, including but not limited to, BukkitAPI, BungeeAPI and Granite. I also have tons of experienced with program MySQL, and the SQL language.

    I created this Network Lib a-couple days ago, I'd consider it very efficient if the desired user isn't using a Proxying Software for their server that has the ability to send network messages, e.g. BungeeCord.


    Network Lib
    Firstly, grab the player like so..
    NPlayer data = NicksLib.getInstance().players.get(player);
    Once you have your data variable, you're able todo quite a bit.

    Code:
    data.getPlayer();
    Returns Player
    data.getPlayerUUID();
    Returns UUID
    data.getLanguage();
    Returns Language String.
    data.setLanguage(String lang);
    Requires String, sets language.
    /*
    Level methods
    */
    data.getLevel();
    returns int
    data.addLevel(int level);
    Requires int, adds to level.
    data.removeLevel(int level);
    Requires int, removes level.
    data.setLevel(int level);
    Sets the player level.
    /*
    Experience related methods
    */
    data.getExperience();
    returns float.
    data.addExperience(float experience)
    requires float, adds to experience.
    data.removeExperience(float experience)
    requires float removes from experience.
    data.setExperience(float experience)
    requires float sets experience.
    /*
    Credits related methods
    */
    data.getCredits();
    returns int
    data.addCredits(int credits);
    requires int, adds credits
    data.removeCredits(int credits)
    requires int, removes credits.
    data.setCredits(int credits)
    requires int, sets credits.
    /*
    Achievement Points
    */
    data.getAchievementPoints();
    returns int.
    data.addAchievementPoints(int amount)
    requires int adds to achievement points.
    data.removeAchievementPoints(int amount);
    requires int removes from achievement points.
    data.setAchievementPoints(int amount)
    requires int sets achievement points

    This class contains several useful methods, like retrieving the player, saving the player.
    Code:
    package com.nicksums.lib.api;
    
    import com.mysql.jdbc.Statement;
    import com.nicksums.lib.NicksLib;
    import com.nicksums.lib.mechanics.NPlayer;
    import org.bukkit.entity.Player;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
    * Created by Nick on 6/15/2015.
    */
    public class API {
    
        /*
        This class will contain all of our MySQL methods, excluding the few that are harbored
        inside the main class, that extends JavaPlugin.
         */
    
        /*
        Saving the player
         */
        public static void savePlayer(final Player player) {
            PreparedStatement state = null;
            try {
                /*
                Now, time to grab the storage of our player.
                 */
                NPlayer data = NicksLib.getInstance().players.get(player);
    
                state = NicksLib.getInstance().database.prepareStatement("UPDATE `" + NicksLib.getInstance().table + "` SET lang=?,level=?,experience=?,credits=?,achievement_points=? WHERE id=?;");
                /*
                Here we're setting the ?'s, with PreparedStatement's Methods. set(This,That).
                 */
                state.setString(1, data.getLanguage());
                state.setInt(2, data.getLevel());
                state.setFloat(3, data.getExperience());
                state.setInt(4, data.getCredits());
                state.setInt(5, data.getAchievementPoints());
    
                state.setInt(6, data.getUniqueId());
                state.execute();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    state.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /*
        This method, will grab the players uniqueId if the player is new.
         */
        public static int addNewPlayer(final Player player) {
            PreparedStatement state = null;
            ResultSet set = null;
    
            try {
                state = NicksLib.getInstance().database.prepareStatement("INSERT INTO `" + NicksLib.getInstance().table + "` (uuid) VALUES (?);", Statement.RETURN_GENERATED_KEYS);
                /*
                state.setString() ask for an (int) which corresponds to the statement above, 1 represents the first question mark.
                Starting from left to right. (?).
                And we're setting that question mark = to player.getUniqueId().toString(). Since the uuid is stored as a Varchar
                corresponding to a String via Java.
                 */
                state.setString(1, player.getUniqueId().toString());
                state.execute();
                set = state.getGeneratedKeys();
    
                if (set.next()) {
                    return set.getInt(1);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    state.close();
                    set.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            /*
            If we're unable to do this, let's return a form of volatile data to we can check elseware.
             */
            return -1;
        }
    
        /*
        This method will fetch the player from the MySQL Server.
        It's a list of Objects because we'll be returning several different
        types of data to store volatile, and save later.
         */
        public static Object[] getPlayer(final Player player) {
            /*
            Set the size of the Object.
            Remember the Object[SIZE] has to be 1 larger than your final input.
             */
            Object[] obj = new Object[8];
    
            /*
            Set the first list of the Object[] to a boolean value.
            This value will be set to false before the SQL is contacted.
            Let's say for example the player doesn't exist in the MySQL.
            Then the first value of the object[] will be a boolean of type false.
            Later in the statement we'll set this obj[0] = true;
             */
            obj[0] = false;
    
            /*
            DBMS can run PreparedStatement without SQL statement without having to compile it first.
            ResultSet usually means you're retrieving data from MySQL, data is returned in the form of a
            (ResultSet)!
    
            Both are declared, null. So later in the method after a finally{} they can be halted.
             */
            PreparedStatement state = null;
            ResultSet set = null;
    
            try {
                /*
                Set the state equal to the instantiation of the main class database variable.
                Make sure to always end a MySQL PreparedStatement with a ';'.
                 */
                state = NicksLib.getInstance().database.prepareStatement("SELECT id,uuid,lang,level,experience,credits,achievement_points FROM `" + NicksLib.getInstance().table + "` WHERE uuid=?;");
                /*
                Set the first question mark in the PreparedStatement = to the StringValue of the UniqueId.
                 */
                state.setString(1, player.getUniqueId().toString());
    
                /*
                Execute Query is a return type of ResultSet, which is (set).
                 */
                set = state.executeQuery();
    
                /*
                If the database has results to return from the PreparedStatement the results will be returned inside this if.
                 */
                if (set.next()) {
                    /*
                    Set the object as defined above, = to something useful.
                    For the sake of this lib, we're requesting an
    
                    INT id,
                    VARCHAR uuid //VarChar is like.. a String.
                    VARCHAR lang,
                    INT level,
                    FLOAT experience,
                    INT credits,
                    INT achievement_points
                     */
    
                    obj[0] = true;
                    /*
                    Make sure to checkout ResultSet, methods.
                    set as defined above . has abunch of useful methods to
                    fetch data from a given ResultSet. e.g. getInt, getFloat, getString..
                     */
                    obj[1] = set.getInt("id");
                    obj[2] = set.getString("uuid");
                    obj[3] = set.getString("lang");
                    obj[4] = set.getInt("level");
                    obj[5] = set.getFloat("experience");
                    obj[6] = set.getInt("credits");
                    obj[7] = set.getInt("achievement_points");
                }
    
                return obj;
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    state.close();
                    set.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            return obj;
        }
    
        /*
        Nice little Reflection method to grab the players default Language.
        The language the player chooses at the GuiMainMenu of Minecraft.
         */
        public static String getLanguage(Player p) {
            Object ep = null;
            try {
                ep = getMethod("getHandle", p.getClass()).invoke(p, (Object[]) null);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            Field f = null;
            try {
                f = ep.getClass().getDeclaredField("locale");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
            f.setAccessible(true);
            String language = null;
            try {
                language = (String) f.get(ep);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return language;
        }
    
        /*
        Dependency method for above.
         */
        private static Method getMethod(String name, Class<?> clazz) {
            for (Method m : clazz.getDeclaredMethods()) {
                if (m.getName().equals(name))
                    return m;
            }
            return null;
        }
    
    }
    
    The only listener class in the plugin.
    Code:
    package com.nicksums.lib.listeners;
    
    import com.nicksums.lib.NicksLib;
    import com.nicksums.lib.api.API;
    import com.nicksums.lib.mechanics.InvalidUniqueIdException;
    import com.nicksums.lib.mechanics.NPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.Listener;
    import org.bukkit.event.player.PlayerLoginEvent;
    import org.bukkit.event.player.PlayerQuitEvent;
    
    /**
    * Created by Nick on 6/15/2015.
    */
    public class LibListener implements Listener {
    
        /*
        Declare the method as an event to be watched with @EventHandler.
        Declare the method as a higher level priority event. via EventPriority.
        So that other plugins on the server can't take our priority of making sure
        that the player is existing or not.
        PlayerLoginEvent is executed before PlayerJoinEvent.
         */
        @EventHandler(priority = EventPriority.HIGH)
        public void onLogin(PlayerLoginEvent event) {
            /*
            Create the player variable, set instantiate.
             */
            Player player = event.getPlayer();
    
            /*
            Declare a Object[]
    
            Remember that our API.getPLayer() returns type of Object[]
             */
            Object[] thePlayer = API.getPlayer(player);
    
            /*
            The player exist!
             */
            if (thePlayer[0] == true) {
                /*
                So.. Since the player exist in the database, we'll just grab the Object[]'s contents and set it to the volatile storage of the HashMap<>.
                 */
                NicksLib.getInstance().players.put(player, new NPlayer(player, (int) thePlayer[1], (String)thePlayer[3], (int) thePlayer[4], (float) thePlayer[5], (int) thePlayer[6], (int) thePlayer[7]));
            }
            /*
            The player doesn't exist!
             */
            else if (thePlayer[0] == false) {
                int uniqueId = API.addNewPlayer(player);
                if (uniqueId != -1) {
                    /*
                    Perfect, the api was able to return is a valid number and wasn't -1.
                    Now, we have a fresh player and must set the players defaults.
                    Check constructor of (NPlayer) for more information regarding this.
    
                    Let's remember that NPlayer Constructor contains
                    Player, UniqueId, Level, Experience, Credits, Achievement Points.
                     */
                    NicksLib.getInstance().players.put(player, new NPlayer(player, uniqueId, API.getLanguage(player), 0, 0f, 0, 0));
                }else {
                    new InvalidUniqueIdException("Unable to handle PlayerJoinEvent() Event for Player: " + player.getName() + " with uniqueIdentifier " + uniqueId);
                }
            }
    
        }
    
    
        @EventHandler
        public void onQuit(PlayerQuitEvent event) {
            Player player = event.getPlayer();
            /*
            Now, since the player has left, let's remove the players storage.
            Else, the server will hold this information in memory as volaitle
            data until the memory is flushed. (Restarted)!
             */
            if(NicksLib.getInstance().players.containsKey(player)) {
                API.savePlayer(player);
                NicksLib.getInstance().players.remove(player);
            }
        }
    
    }
    

    Explanation, this API is for testing purposes. If you're wanting to mess around with MySQL storing, and not want to convert your volatile data to Configuration (.yml) or otherwise Stream related files.

    How it works: The code is very detailed, and shows for the most part everything that you need to know!

    You can download the .jar here.
    https://nicksums.com/plugins/networklib/NicksLib.jar

    You can download the .zip src here.
    https://nicksums.com/plugins/networklib/networklib-src.zip
     
  2. not2excel

    not2excel Member

    Joined:
    Jun 24, 2015
    Messages:
    35
    Likes Received:
    27
    Trophy Points:
    18
    Few things you should either 1) take note of or 2) give a thought to switching to.

    Greedy naming - while every developer loves their credit, greedy naming will turn away some people. (CheckSumLib being much less greedy that NicksLib)
    Only reason I say this is that Nick not only is a first name, but a common one at that.

    Git - pls learn to use it. If you plan on releasing anything publicly, utilize git and a git repository. Not only does it make it easier for people to suggest changes via pull requests, it'll also benefit you in the future when it comes job applications (if you're going into a CompSci field)

    License - I cannot say this enough, even if you make the license as restrictive as possible via the whole "You're allowed to use it and that's it." statement.

    Proper javadocs - if you're going to release something, please do not release it with notes saying SomeClass#someMethod - does this
    That's the whole reason javadocs are used. And there's no need to explaining what a simple getter/setter does either (unless ofc, there's more happening under the hood that purely modifying a mutable field)

    There's little actual usage information. You just dumped a few example classes, and expect people to find out if they could possibly use this by having to read through the examples. Instead of just saying it does X, Y, and Z, and then the potential user could easily find out whether they wish to use it or not.

    Now, I don't say any of this in a condescending or degrading matter at all. I'm simply providing insight on what I feel you should do as the first statement says. All it will do is make you a better developer in the end.
     
    Connor Maine likes this.