HomeJavaModsKELP FRAMEWORK | Cross version plugins & boilerplate reduction
KELP FRAMEWORK | Cross version plugins & boilerplate reduction
ModsJava

KELP FRAMEWORK | Cross version plugins & boilerplate reduction

⬇ Download on Spigot
title.png

Kelp is a spigot framework that aims to reduce boilerplate code and make your plugin compatible with multiple server versions easily. This massively reduces maintenance costs for your plugins and allows developers to focus on their creative work and write more readable code in less time.

The problem with the current spigot plugin development process
The Spigot API has limited backwards compatibility. Material and sound names change over the versions, NMS packages are named after their version and require reflections to make them compatible, etc. Adjusting old code to new API versions requires lots of time & effort, which is why many developers don't upgrade their plugins anymore, although many server owners still rely on them. So server owners won't upgrade to the latest Minecraft versions and new ideas can't be realized.

Furthermore, developers have to use lots of workarounds to achieve certain goals. If you want to create a non-flickering sidebar, you have to save the updateable content into a team prefix, put it into the sidebar and update the prefix. New developers might not understand why there are teams in the scoreboard, which often causes confusion.

How Kelp solves all these problems
Kelp is built modularly. It consists of one core module, which only contains Java-logic and no version-specific code, so it will run on every spigot server version. It contains so-called template classes, which are basically dummies with empty methods doing version specific stuff. The server owner then puts a version-implementation module in a specific folder. This file contains implementations of all the template classes from the core module. It is detected by the core module and every template class is replaced with its version implementation class. So the core module can load version implementations for every version and can run on any version without changing any line in the code-base.




features.png

But please note that Kelp is still under heavy development and will contain bugs. Plugins you develop with the current version of Kelp might not work with future releases as the API code still subjects to change.

So Kelp should not be used in production environments yet, but only for testing purposes. I would really appreciate some people testing it and giving feedback on performance, usability, and code design to improve the framework and bring it to its first stable release.




demo.png
Below you can find some demos on what Kelp can do now and how the code roughly looks like.

Spoiler: NPCs
2021-01-02_22.05.42.png
Code (Java):
@EventHandler
  public void handlePlayerJoin (PlayerJoinEvent event ) {
    KelpPlayer player = playerRepository. getKelpPlayer (event. getPlayer ( ) ) ;

    // create new npc instance
    KelpNpc kelpNpc = npcFactory. newKelpNpc ( )
      . spawnLocation (player. getLocation ( ) )
      . itemInHand (kelpItemFactory. newKelpItem ( ) // the NPC is holding an apple
        . material (KelpMaterial. APPLE ) )     // in its hand
      . addTitleLine ( "§bThis is an epic NPC" ) // set titles above the npc
      . addTitleLine ( "§7Create as many lines as you want" )
      . skinTexture ( "ewogICJ0aW1lc3RhbXAiIDogMTYwOTYyMDczMTk4OSwKICAicHJvZmlsZUlkIiA6ICI4NThmZGJiNjg3MWM0OGZhYjhlODY5YmY2NmM2YTEwMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJweGF2IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2YyZmVhZThkMzFlYzc0NTQ4MTE5Y2E0NThmNWRhNTc2MzU0ZmE5NzUxYWE0YWU2M2Y1NDYwODJmODA2NTFiMmIiCiAgICB9CiAgfQp9" )
      . skinSignature ( "M8SC4wdKlAlAx4NjaWEFZV1qQfcnqRvZb9YKikVs7eOLHMbwlPb64TfiTq2eYb1vEg+4QbU0LTBk/FAl5WXILq0OAzicX9Sh2z6x9/tuVy3oRuSqJKUoqYvG8vg8I2r2426okLE7iznkLv7AplX1V0cKZjjg8mwb+9IpdM1/ai3hilBr84oLUWpI3s8WtXY45qpDnMgkJFFSKc9ZY13SmYK4DtgGPPpkN043OCsOoaADEmiUTWuNdQRqA4dDVTPODqn5qvhG2rZK58t7tK3xYqeARIMH522GQMYAi8g6cOF8CiL4WQuZQYsT0hUlZtNwFhT+odCoJqXZPuY8sHNRPaTj+65ZJXEzjDsVPzxP1HHHSy3fQk9RyhTqOB7pgcXlseGLMsWcu5rgJLvdc+QcFfItGJHAFW5Ysv025fmo/qwQJ3RrMX8DZ3IotSpWekrIx/8ACf0jxuyTDhIzFtJPArFnYq4ELDsgWMShmOUGY0YGvfryYEShH7S4p02zAf4TgwUve6OrRfh0yTmg5qFKKAADQg+MoCCdRIj6ubcW0FjKf22WA3EH+wtdmkf5LZTldvvj8dyFd+0mvrl9c/eUggqZFeyvnRwEL9rBkVuWw8PXxaVPPf7yyZVuqL+fzGHBbb3Lu6Exk2ywdatqdKmHLVKJU6ZDP5NN9hrSeaH4iDM=" )
      . imitateSneaking ( ) // the npc will sneak when the player sneaks
      . followHeadRotation ( ) ; // the npc will always look at the player

    // finally make the npc visible to the player
    kelpNpc. spawn (player ) ;

  }

Spoiler: Paginated Inventories
DeepinScreenshot_select-area_20210102221419.png
Code (Java):
@EventHandler
  public void handleInteract (PlayerInteractEvent event ) {
    KelpPlayer player = playerRepository. getKelpPlayer (event. getPlayer ( ) ) ;

    AnimatedInventory inventory = kelpInventoryFactory. newAnimatedInventory ( ) ;
    inventory
      . size ( 54 )
      . title (textAnimationFactory. newBuildingTextAnimation ( )
        . text ( "§aFOOD SELECTION" )
        . ignoreSpaces ( ) ) ;
    inventory. addWidget (widgetFactory. newToggleableWidget ( )
      . player (player )
      . slot ( 42 )
      . condition ( ( ) -> player. getBukkitPlayer ( ). getGameMode ( ) == GameMode. CREATIVE )
      . whenFalse (kelpItemFactory. newKelpItem ( )
        . material (KelpMaterial. RED_WOOL )
        . displayName ( "§cIn Survival Mode" )
        . cancelInteractions ( ),
        ( ) -> {
          player. sendMessage ( "Your Gamemode has been updated" ) ;
          player. getBukkitPlayer ( ). setGameMode (GameMode. CREATIVE ) ;
        } )
      . whenTrue (kelpItemFactory. newKelpItem ( )
        . material (KelpMaterial. GREEN_WOOL )
        . displayName ( "§cIn creative mode" )
        . cancelInteractions ( ),
        ( ) -> {
          player. sendMessage ( "Your Gamemode has been updated." ) ;
          player. getBukkitPlayer ( ). setGameMode (GameMode. SURVIVAL ) ;
        } ) ) ;

    inventory. addWidget (widgetFactory. newPagination ( )
      . player (player )
      . contentSlots ( 3, 1, 2, 12, 10, 11 )
      . contentItems (kelpItemFactory. newKelpItem ( ). displayName ( "§6Cooked Beef" ). material (KelpMaterial. COOKED_BEEF ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§fCooked Chicken" ). material (KelpMaterial. COOKED_CHICKEN ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§cCooked Fish" ). material (KelpMaterial. COOKED_SALMON ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§fCooked Mutton" ). material (KelpMaterial. COOKED_MUTTON ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§6Golden Apple" ). material (KelpMaterial. GOLDEN_APPLE ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§cApple" ). material (KelpMaterial. APPLE ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§6Soup" ). material (KelpMaterial. MUSHROOM_STEW ) )
      . nextButton (kelpItemFactory. newKelpItem ( ). slot ( 21 ). displayName ( "§eNext Page" ). material (KelpMaterial. ARROW ). cancelInteractions ( ),
        ( ) -> player. sendMessage ( "§cYou are already on last page" ) )
      . previousButton (kelpItemFactory. newKelpItem ( ). slot ( 19 ). displayName ( "§eVorherige Seite" ). material (KelpMaterial. ARROW ). cancelInteractions ( ),
        ( ) -> player. sendMessage ( "§cYou are already on first page" ) )
    ) ;

    inventory. addWidget (widgetFactory. newPagination ( )
      . player (player )
      . contentSlots ( 5, 6, 7, 14, 15, 16 )
      . contentItems (kelpItemFactory. newKelpItem ( ). displayName ( "§bDiamond Sword" ). material (KelpMaterial. DIAMOND_SWORD ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§bDiamond Shovel" ). material (KelpMaterial. DIAMOND_SHOVEL ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§bDiamond Axe" ). material (KelpMaterial. DIAMOND_AXE ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§7Iron Sword" ). material (KelpMaterial. IRON_SWORD ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§7Chainmail Chestplate" ). material (KelpMaterial. CHAINMAIL_CHESTPLATE ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§7Iron ingot" ). material (KelpMaterial. IRON_INGOT ),
        kelpItemFactory. newKelpItem ( ). displayName ( "§6Gold" ). material (KelpMaterial. GOLD_INGOT ) )
      . nextButton (kelpItemFactory. newKelpItem ( ). slot ( 25 ). displayName ( "§eNext Page" ). material (KelpMaterial. ARROW ). cancelInteractions ( ),
        ( ) -> player. sendMessage ( "§cYou are already on last page" ) )
      . previousButton (kelpItemFactory. newKelpItem ( ). slot ( 23 ). displayName ( "§ePrevious Page" ). material (KelpMaterial. ARROW ). cancelInteractions ( ),
        ( ) -> player. sendMessage ( "§cYou are already on first page" ) )
    ) ;

    inventory. addWidget (widgetFactory. newItemWidget ( )
      . player (player )
      . item (kelpItemFactory. newKelpItem ( )
        . material (KelpMaterial. BOOK )
        . slot ( 38 )
        . displayName ( "§6§lA book" ) )
      . addItemListener (event1 -> player. sendMessage ( "You clicked the item." ) ) ) ;


    player. openInventory (inventory ) ;
  }

Spoiler: Interactive Messages
DeepinScreenshot_select-area_20210102222514.png
Code (Java):
@EventHandler
  public void handleIncomingReport (PlayerInteractEvent event ) {
    KelpPlayer player = playerRepository. getKelpPlayer (event. getPlayer ( ) ) ;
    String reportTarget = "§axX_ItzHackerPvP_Xx" ;
    player. sendMessage ( "§8[§2Kelp§8] §7A new report has been created!" ) ;
    player. sendMessage ( "§8[§2Kelp§8] §7Reason: §aHacking" ) ;
    player. sendMessage ( "§8[§2Kelp§8] §7Accused: §axX_ItzHackerPvP_Xx" ) ;
    player. sendMessage ( "§8[§2Kelp§8] §7Please select an action§8:" ) ;

    player. sendInteractiveMessage (
      InteractiveMessage. create ( )
        . addComponent (MessageComponent. create ( )
          . text ( "§8[§2Kelp§8] " ) ) // prefix component (not clickable/hoverable)
        . addComponent (MessageComponent. create ( )
          . text ( "§3§l● WATCH PLAYER  " )
          . showMessageOnHover ( "§aTeleport yourself to the player to validate the report" )
          . executeCommandOnClick ( "teleport " + reportTarget ) )
        . addComponent (MessageComponent. create ( )
          . text ( "§c§l✖ BAN PLAYER  " )
          . showMessageOnHover ( "§cBan the player immediately without confirming the report" )
          . suggestCommandOnClick ( "ban " + reportTarget + " " + "Hacking" ) )
        // only suggests the command to the player to that it can be modified by an admin optionally
        . addComponent (MessageComponent. create ( )
          . text ( "§b§l✚ INFO  " )
          . showMessageOnHover ( "§bOpens the web interface showing more information about the player" )
          . openURLOnClick ( "https://interface.your-server.com/info/" + reportTarget ) )
    ) ;

  }

Spoiler: Anvil Prompt
DeepinScreenshot_select-area_20210102223120.png
Code (Java):
@EventHandler
  public void handleIncomingReport (PlayerInteractEvent event ) {
    KelpPlayer player = playerRepository. getKelpPlayer (event. getPlayer ( ) ) ;

    player. openAnvilPrompt ( )
      . sourceMaterial (KelpMaterial. NAME_TAG )
      . initialText ( "Enter nickname..." )
      . withAsyncTimeout ( 30, TimeUnit. SECONDS,
        ( ) -> player. sendMessage ( "§cYou timed out of the prompt" ) )
      . onClose ( ( ) -> player. sendMessage ( "§cYou exited the prompt manually." ) )
      . handle (input -> {
        if (input. length ( ) > 16 ) {
          player. sendMessage ( "§cA name may not be longer than 16 chars!" ) ;
          return PromptResponseType. TRY_AGAIN ;
        }

        player. sendMessage ( "§aYour nickname has been set!" ) ;
        return PromptResponseType. ACCEPTED ;
      } ) ;

  }

More demos will follow in the future, feel free to experiment with the features by yourself!


installation.png

Requirements:

The installation of Kelp is pretty simple. At first, you need the latest "core"-Module file, which you can download from GitHub (the SpigotMC Download link will redirect you to the latest release on GitHub).


wiki.png

Kelp has a detailed wiki page for developers, which is constantly updated with the latest features. There you can find code examples and in-depth explanations of the different features and how kelp works under the hood.

If you plan to use Kelp in your plugins, I recommend reading it:
Kelp Wiki on GitHub

If you encountered a bug with kelp or have a feature suggestion, please post it to the GitHub Issue Tracker so that we have a better overview in development. We also accept contributions / pull request to the plugin, so feel free to create one or contact me about this.
Quick facts

Install steps are the general flow for this file type — How to install Minecraft Java mods & modpacks walks through it step by step.

KELP FRAMEWORK | Cross version plugins & boilerplate reduction is a free Minecraft Java mod. Compatible with Minecraft 1.8. Downloaded 765 times (via Spigot). Download it and open it directly in the game.

Explore more