L2UI icon

L2UI -----

GUI APIU



L2UI
Welcome to this plugin.. :)

What is L2UI?

Well L2UI (Short for L2's User Interface) is a spigot tool for creating User Interfaces. It is an API designed around making any sort of UI you need without dealing with the nuances of inventories.

Let's face it, a majority of plugins need some sort of graphical user interface. If you are thinking yours doesn't well, you are wrong! GUI's in minecraft help by adding a great overall user experience to the player.


So, how does L2UI help you in this aspect?

Easy. I provide a pretty robust API (that I actually use in almost all of my plugins) for creating said graphical user interfaces. I can provide all the robust-ness without limiting what you can do, you still have access to all the normal inventory features.

What does it support:
  • Multiple users per inventory
  • Individual Screens per person (great for games)
  • Actions! (It will handle all events for you, and give them to you whenever you need them)
  • Animated GUI's! (that's right, you can have some pretty fancy animations if you want)
  • Auto-Pagination (this one I am especially proud of. It will auto paginate, for displaying lists of items. It will handle all the logic, all you do is tell it what slots you want it to appear in, and provide the list!)
  • Presistance (Want to create a crafting menu? sure! This will allow your UI to not accidentally ovewrite items in the UI that the player may have placed in there)
  • Callbacks (Have a lambda function you want to run after the user has done something easy! A great way to have interlinking UI's to pass data around)
  • Auto-Handles (player joins another UI? we got you covered. Player leaves? Leave it to me. Want to kick a player? Yup! I handle a lot of the player management)
Okay okay, enough about the features, lets dive into the code!
Here are some code examples. Unfortunately I haven't gotten around to doing java-docs or anything of that nature. I'll eventally add pictures and what-not.

Enjoy!

Code (Java):
public class ExampleStatic extends UserInteface {
    //This is the base structure of a class.
    //In this example, one screen for all players.
    private Screen screen ;
    public ExampleStatic ( ) {
        //Lets setup the screen
        this. screen = new DefaultScreen ( ). setName ( "Example!" ). setSize ( 9 ) ;
    }
    //This is called whenever a screen updates and/or when a player joins.
    @Override
    public Screen getScreen (Player player ) {
        //lets return the screen to the player.
        return screen ;
    }

    //Called before the screen is opened.
    @Override
    public boolean onJoin (Player player ) {
        //Make sure this is true!
        return true ;
    }
    //Called when the ui is officially dead.
    @Override
    public void onClose ( ) {

    }
    //Calls when a player leaves the UI
    @Override
    public void onLeave (OfflinePlayer player ) {

    }
    //Called when a player joins for the first time, before the onJoin. This is a great place to setup a screen
    @Override
    public void onInitialize ( ) {
        //First we want to make sure all actions are cancelled by default.
        getL2UI ( ). setCanceledByDefault ( true ) ;
        //lets add an icon..
        screen. addIcon ( 0, getInfoIcon ( ) ) ;
    }
    //Helper methods for this example

    private Icon getInfoIcon ( ) {
        Icon icon = new DefaultIcon (getInfoItemStack ( ) ) ;
        //Lets send a message when the user selects this icon...
        icon. setActionHandler ( (action -> {
            //For this example, we want to know what type of action happened...
            if (action. getEvent ( ) instanceof InventoryClickEvent ) {
                //great it's a click!
                InventoryClickEvent event = (InventoryClickEvent ) action. getEvent ( ) ;
                if (event. isLeftClick ( ) ) {
                    //Grab the player that clicked via the action
                    action. getPlayer ( ). sendMessage ( "You left clicked me!" ) ;
                } else if (event. isRightClick ( ) ) {
                    action. getPlayer ( ). sendMessage ( "You right clicked me!" ) ;
                }
            }
        } ) ) ;
        return icon ;
    }

    private ItemStack getInfoItemStack ( ) {
        //L2UI has a built in itembuilder class..
        ItemBuilder itemBuilder = new ItemBuilder (Material. ENCHANTED_BOOK ) ;
        itemBuilder. setDisplayName ( "&aClick me to see some information!" ) ;
        itemBuilder. setLore ( "&7Left click for message", "&cRight click for different message" ) ;
        return itemBuilder. build ( ) ;
    }
}
 
Code (Java):
public class CallableInterface extends UserInteface implements Callback <Boolean > {
    private static final int [ ] wall = new int [ ] { 1, 2, 4, 6, 7, 8 } ;
    private final Screen screen ;
    private Consumer <Boolean > result ;
    private final String message ;

    public CallableInterface ( String message ) {
        this. screen = new DefaultScreen ( ). setName ( "Confirm your action" ). setSize ( 9 ) ;
        this. message = message ;
    }
    //New function to support the callback.
    @Override
    public CallableInterface setCallback (Consumer <Boolean > consumer ) {
        this. result = consumer ;
        return this ;
    }

    @Override
    public Screen getScreen (Player player ) {
        return screen ;
    }

    @Override
    public boolean onJoin (Player player ) {
        return true ;
    }

    @Override
    public void onClose ( ) {

    }

    @Override
    public void onLeave (OfflinePlayer offlinePlayer ) {
        //You may want to add some logic if they leave without answering.
    }

    @Override
    public void onInitialize ( ) {
        //Cancel by default
        getL2UI ( ). setCanceledByDefault ( true ) ;
        screen. addIcon ( 0, new DefaultIcon ( new ItemBuilder (Material. WRITTEN_BOOK ). setDisplayName (ChatColor. GRAY + message ). build ( ) ) ) ;
        //[0][1][2][][4][][6][7][8]
        //Just to make it pretty, lets fill in the void.
        for ( Integer i : wall ) {
            screen. addIcon (i, new DefaultIcon ( new ItemStack (Material. BLACK_STAINED_GLASS_PANE ) ) ) ;
        }
        //Add the first yes
        screen. addIcon ( 3, new DefaultIcon ( new ItemBuilder (Material. GREEN_STAINED_GLASS_PANE ). setDisplayName ( "&aYes" ). build ( ) ). setActionHandler ( (action -> {
            //the callback function will be true
            result. accept ( true ) ;
        } ) ) ) ;
        //add the no icon
        screen. addIcon ( 5, new DefaultIcon ( new ItemBuilder (Material. RED_STAINED_GLASS_PANE ). setDisplayName ( "&cNo" ). build ( ) ). setActionHandler ( (action -> {
            //the callback function will be false.
            result. accept ( false ) ;
        } ) ) ) ;
    }
}
Code (Java):
public class CraftingMenu extends UserInteface implements PersistantInterface {
        //In an effort to help save memory, I make these static & final as they will be used on all Crafting Menu instances.
        private static final int [ ] border = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28, 29, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 } ;
        private static final int [ ] craftingArea = { 12, 13, 14, 21, 22, 23, 30, 31, 32 } ;
        private static final int cancelSlot = 43 ;
        private static final int saveSlot = 44 ;
        private final Screen screen ;
        //We should keep track of the items.
        private final HashMap < Integer, ItemStack > items ;
        public CraftingMenu ( ) {
            screen = new DefaultScreen ( 45 ). setName ( "&bRecipe Builder" ) ;
            this. items = new HashMap <> ( ) ;
        }

        @Override
        public Screen getScreen (Player player ) {
            return screen ;
        }

        @Override
        public boolean onJoin (Player player ) {
            return true ;
        }

        @Override
        public void onClose ( ) {

        }

        @Override
        public void onLeave (OfflinePlayer offlinePlayer ) {
            //If the player leaves without crafting lets give the items back.
            for ( int i : craftingArea ) {
                if (screen. getIcon (i ) == null ) {
                    //If they are offline, well.. I'll let you figure out what to do :)
                    if (offlinePlayer. isOnline ( ) ) {
                        //TODO: This doesn't give the item back because there is no icon
                        Inventory inventory = Objects. requireNonNull (offlinePlayer. getPlayer ( ) ). getInventory ( ) ;
                        for (ItemStack itemStack : items. values ( ) ) {
                            inventory. addItem (itemStack ) ;
                        }
                        items. clear ( ) ;
                    }
                }
            }
        }

        @Override
        public void onInitialize ( ) {
            //Set everything up!
            setupBorder ( ) ;
            setupCraftingMenu ( ) ;
            setupBottomSlots ( ) ;
            setupButtons ( ) ;
        }

        private void setupButtons ( ) {
            //If they cancel, we will kick them from the UI
            screen. addIcon (cancelSlot, new DefaultIcon ( new ItemBuilder (Material. REDSTONE_BLOCK ). setDisplayName ( "&cCancel" ). build ( ) ). setActionHandler ( (action -> {
                //We need to actually cancel the action
                if (action. getEvent ( ) instanceof Cancellable ) {
                    ( (Cancellable ) action. getEvent ( ) ). setCancelled ( true ) ;
                }
                getL2UI ( ). kick (action. getPlayer ( ) ) ;
            } ) ) ) ;
            //This can be whatever you choose. but for this button, this will build the item
            screen. addIcon (saveSlot, new DefaultIcon ( new ItemBuilder (Material. EMERALD_BLOCK ). setDisplayName ( "&aBuild" ). build ( ) ). setActionHandler ( (action -> {
                //Cancel the action because we cannot use cancelByDefault
                if (action. getEvent ( ) instanceof Cancellable ) {
                    ( (Cancellable ) action. getEvent ( ) ). setCancelled ( true ) ;
                }
                //Using action we can grab the raw inventory. We want the top item since this is where our crafting menu is.
                Inventory inventory = action. getRawInventory ( ). getTopInventory ( ) ;
                //Default setup array
                ItemStack [ ] itemStacks = new ItemStack [ 9 ] ;
                //Iterate over the crafting slots
                for ( int i = 0 ; i < craftingArea. length ; i ++ ) {
                    //Is it null?
                    if (inventory. getItem (craftingArea [i ] ) != null ) {
                        itemStacks [i ] = inventory. getItem (craftingArea [i ] ) ;
                    } else {
                        itemStacks [i ] = new ItemStack (Material. AIR ) ;
                    }
                }
                //You now have the items in the crafting area you can figure out what you want to do.
                //Remove one of each? and give item? sure!

            } ) ) ) ;
        }

        private void setupBorder ( ) {
            //Border to make it pretty.
            for ( int i : border ) {
                screen. addIcon (i, new DefaultIcon ( new ItemStack (Material. BLACK_STAINED_GLASS_PANE ) ) ) ;
            }
        }

        private void setupCraftingMenu ( ) {
            //Iterate over the crafting areas
            for ( int i : craftingArea ) {
                //We want to set a raw action handler for the slot, without the icon! Super important!!!!
                //The reason we set a tick delay is because we need to make sure the item is actually placed.
                screen. setSlotActionHandler (i, (a ) -> Bukkit. getScheduler ( ). scheduleSyncDelayedTask ( <YourPlugin >, ( ) -> {
                    //Lets grab the item.
                    ItemStack itemToAdd = a. getRawInventory ( ). getTopInventory ( ). getItem (a. getSlot ( ) ) ;
                    //Are they placing or adding?
                    if (itemToAdd == null ) {
                        //if they are removing, then remove from items.
                        items. remove (a. getSlot ( ) ) ;
                    } else {
                        //if they are adding, add it.
                        items. put (a. getSlot ( ), itemToAdd ) ;
                    }
                }, 5L ) ) ;
            }
        }
        //Last but not least, we need to make sure they can't shift click or drag this could mess up the UI
        private void setupBottomSlots ( ) {
            //Iterate over their inventory slots.
            for ( int i = 45 ; i < 81 ; i ++ ) {
                //Set a raw action handler
                screen. setSlotActionHandler (i, (a ) -> {
                    //Cancel those annoying events.
                    if (a. getEvent ( ) instanceof InventoryDragEvent ) {
                        ( (InventoryDragEvent ) a. getEvent ( ) ). setCancelled ( true ) ;
                    }
                    if (a. getEvent ( ) instanceof InventoryClickEvent ) {
                        if ( ( (InventoryClickEvent ) a. getEvent ( ) ). isShiftClick ( ) ) {
                            ( (InventoryClickEvent ) a. getEvent ( ) ). setCancelled ( true ) ;
                        }
                    }
                } ) ;
            }
        }

    }
}
Code (Java):
public class PaginatedUI extends PaginatedMenu {
    private final List <String > strings ;
    //We need the items that we want to display.
    public PaginatedUI (List <String > strings ) {
        //The menu title, the reason we declare it in super is the paginatedMenu class will do a lot of the setup for us.
        super ( "Paginated String Menu" ) ;
        // Assign
        this. strings = strings ;
        //Lets sort it, to make the users life easier
        strings. sort ( String ::compareTo ) ;
    }
    @Override
    public boolean onJoin (Player player ) {
        //As you may notice, there is no getScreen Method, the paginated menu only supports 1 player at a time!!!
        //I like to do loading here so that we know what player is viewing (in case you don't want the user to see everything
        ///Or if you just need permission checks.
        load (player ) ;
        return true ;
    }

    @Override
    public void onClose ( ) {

    }

    @Override
    public void onLeave (OfflinePlayer player ) {

    }

    public void load (Player player ) {
        //Due to loading with each player, we may need to update the list.
        //In order to do this, we must remove everything (aka clear) and re-add.
        getScreen ( ). clearPages ( ) ;
        //Lets iterate over everything we want to add
        for ( String s : strings ) {
            //Just as an example, if you only want to display the icon if they have permission
            if (player. hasPermission ( "yourplugin." +s ) ) {
                //Add to list will allow us to add an item..
                getScreen ( ). addToList (generateIcon (s ) ) ;
            }
        }

    }
    //Helper method to clean up code..
    private Icon generateIcon ( String s ) {
        return new DefaultIcon ( new ItemBuilder (Material. PAPER ). setDisplayName (ChatColor. GRAY + s ). build ( ) ). setActionHandler ( (a ) -> {
            //Do something when they select it, if you want.
        } ) ;
    }

    @Override
    public void onInitialize ( ) {
        //Lets cancel all events by default.
        getL2UI ( ). setCanceledByDefault ( true ) ;
        //Now lets setup our paginated menu.
        //The update Indexes method will be the UI slots that we want to populate with our items (don't worry about making it exact)
        getScreen ( ). updateIndexes ( new int [ ] { 1, 2, 3, 4, 5, 6, 7 } ) ;
        //The menu size, must be divisable by 9!
        getScreen ( ). setSize ( 9 ) ;
        //The paginated menu also needs the slots for the 'next page' and 'previous page' icon.  must be within the bounds
        //In our case, at the beginning and end of the menu.
        getScreen ( ). setPreviousSlot ( 0 ) ;
        getScreen ( ). setNextSlot ( 8 ) ;
    }
}
 

Code (Java):
public class MysteryBox extends UserInteface implements Callback <ItemStack > {
    //Slots in which to get the item.
    private static final int [ ] randoms = { 10, 11, 12, 13, 14, 15, 16 } ;
    //Actionalble slot
    private static final int middle = 13 ;
    //Beacons
    private static final int top = 4 ;
    private static final int bottom = 22 ;
    private final Queue <Material > queue ;
    private final Screen screen ;
    private Consumer <ItemStack > itemStackConsumer ;
    private boolean finished = false ;

    public MysteryBox ( ) {
        //Set it up
        this. screen = new DefaultScreen ( ). setSize ( 27 ). setName (ChatColor. BLUE + "Mystery box" ) ;
        this. queue = new LinkedList <> ( ) ;
    }

    @Override

    public MysteryBox setCallback (Consumer <ItemStack > consumer ) {
        this. itemStackConsumer = consumer ;
        return this ;
    }

    @Override
    public Screen getScreen (Player player ) {
        //Update screen on each refresh
        updateScreen ( ) ;
        return screen ;
    }

    @Override
    public boolean onJoin (Player player ) {
        return true ;
    }

    @Override
    public void onClose ( ) {

    }

    @Override
    public void onLeave (OfflinePlayer offlinePlayer ) {

    }

    @Override
    public void onInitialize ( ) {
        //Cancel by default
        getL2UI ( ). setCanceledByDefault ( true ) ;
        //set refresh rate
        ( (AnimatedL2UI ) getL2UI ( ) ). setRefreshRate ( 2 ) ;
        //Add starting icons
        for ( int i = 0 ; i < randoms. length ; i ++ ) {
            Material m = getRandomMaterial ( ) ;
            this. queue. add (m ) ;
            this. screen. addIcon (randoms [i ], new DefaultIcon ( new ItemStack (m ) ) ) ;
        }
        //Actionable slot
        this. screen. setSlotActionHandler (middle, (action -> {
            //If it is finished, we don't want the action to happen anymore
            if (finished ) return ;
            //Grab the item, make sure it's final
            final ItemStack itemStack = action. getIcon ( ). getRepresentation ( ) ;
            //Stop the refresh
            ( (AnimatedL2UI ) getL2UI ( ) ). setRefreshRate ( 0 ) ;
            finished = true ;
            //Clear everything but the middle slot (in this case, the slot that was selected
            for ( int slot : randoms ) {
                if (slot != action. getSlot ( ) ) {
                    this. screen. getIcon (slot ). update ( new ItemStack (Material. AIR ) ) ;
                }
            }
            //force one more refresh.
            getL2UI ( ). simpleRefresh ( ) ;
            //Set a delay to make the transition smoother
            Bukkit. getScheduler ( ). scheduleSyncDelayedTask (Mystic. getInstance ( ), ( ) -> {
                itemStackConsumer. accept (itemStack ) ;
            }, 30L ) ;
        } ) ) ;
        //setup beacons
        this. screen. addIcon (top, new DefaultIcon ( new ItemStack (Material. BEACON ) ) ) ;
        this. screen. addIcon (bottom, new DefaultIcon ( new ItemStack (Material. BEACON ) ) ) ;
    }

    private void updateScreen ( ) {
        //If it is finished we don't need to do this anymore
        if ( !finished ) {
            //remove first
            queue. poll ( ) ;
            //add new item
            queue. add (getRandomMaterial ( ) ) ;
            //Iterate over the queue
            Iterator <Material > materialIterator = queue. iterator ( ) ;
            int index = 0 ;
            while (materialIterator. hasNext ( ) ) {
                Material m = materialIterator. next ( ) ;
                //update each slot.
                this. screen. getIcon (randoms [index ] ). update ( new ItemStack (m ) ) ;
                index ++;
            }
        }
    }

    private Material getRandomMaterial ( ) {
        Material [ ] materialArr = Material. values ( ) ;
        Material m = null ;
        do {
            m = materialArr [getRandomNumberUsingNextInt ( 0, materialArr. length ) ] ;
        } while (m. isAir ( ) && !m. isItem ( ) ) ;
        return m ;
    }

    public int getRandomNumberUsingNextInt ( int min, int max ) {
        Random random = new Random ( ) ;
        return random. nextInt (max - min ) + min ;
    }
}

Code (Java):
  public void showUI (Player player ) {
        //First lets instantiate the UI
        ExampleStatic exampleStatic = new ExampleStatic ( ) ;
        //We want this to be a static. Although animated is available with .getAnimatedL2UserInterface(<ui>);
        L2UserInterface l2UI = new L2UI ( ). getRegistrar ( ). getStaticL2UserInterface (exampleStatic ) ;
        //all they need to do now is join it.
       l2UI. join (player ) ;
    }

    public void callableUI (Player player ) {
        CallableInterface callableInterface = new CallableInterface ( "Are you having fun?" ) ;
        //Set the callback
        callableInterface. setCallback ( (bol ) -> {
            if (bol ) {
                System. out. println (player. getName ( ) + " is having fun!" ) ;
            } else {
                System. out. pr [SPOILER = "Example" ] [ /SPOILER ]intln (player. getName ( ) + " is not having fun :(" ) ;
            }
        } ) ;
        //We want this to be a static. Although animated is available with .getAnimatedL2UserInterface(<ui>);
        L2UserInterface l2UI = new L2UI ( ). getRegistrar ( ). getStaticL2UserInterface (callableInterface ) ;
        //all they need to do now is join it.
        l2UI. join (player ) ;
    }

    public void animatedUI (Player player ) {
        ExampleStatic exampleStatic = new ExampleStatic ( ) ;
        AnimatedL2UI animatedL2UI = (AnimatedL2UI ) new L2UI ( ). getRegistrar ( ). getAnimatedL2UserInterface (exampleStatic ) ;
        //refresh every second. (20 ticks in a second)
        animatedL2UI. setRefreshRate ( 20 ) ;
        animatedL2UI. join (player ) ;
    }
 
    public void paginatedUI (Player player, List <String > stringList ) {
        PaginatedUI paginatedUI = new PaginatedUI (stringList ) ;
        //We want this to be a static. Although animated is available with .getAnimatedL2UserInterface(<ui>);
        L2UserInterface l2UI = new L2UI ( ). getRegistrar ( ). getStaticL2UserInterface (paginatedUI ) ;
        //all they need to do now is join it.
        l2UI. join (player ) ;
    }

Examples:
I will eventually add more documentation and API docs... but I am lazy.. lol
Resource Information
Author:
----------
Total Downloads: 86
First Release: Aug 23, 2021
Last Update: Aug 25, 2021
Category: ---------------
All-Time Rating:
0 ratings
Version -----
Released: --------------------
Downloads: ------
Version Rating:
----------------------
-- ratings