Skip to content

Latest commit

 

History

History
500 lines (340 loc) · 13.8 KB

README.md

File metadata and controls

500 lines (340 loc) · 13.8 KB

Visualkit

The Minecraft GUI framework

Version Adapter Licence

Eye-catching

Visualkit is a free, open-source GUI framework for Paper server.

Maximum respect to Bram Moolenaar, the developer of Vim.

I also sympathized with his philanthropic side, so I decided to add the same text as the Vim startup screen to the Visualkit banner.

Banner

"Help poor children in Uganda!"

Donate to the future of Uganda's children!

Get Started Get Started

Add Visualkit to your plugin's dependencies.

Visualkit is available on Maven Central. To add dependency using Gradle, write the following in your build.gradle ( Groovy DSL)

dependencies {
    compileOnly 'com.tksimeji:visualkit:x.y.z'
}

or in the build.gradle.kts file (Kotlin DSL)

dependencies {
    compileOnly("com.tksimeji:visualkit:x.y.z")
}

To add a dependency using Maven, write the following in pom.xml

<dependency>
    <groupId>com.tksimeji</groupId>
    <artifactId>visualkit</artifactId>
    <version>x.y.z</version>
    <scope>provided</scope>
</dependency>

Next, specify the plugin dependencies. Write the following in plugin.yml.

depend:
  - Visualkit

However, the case of paper-plugin.yml seems to be slightly different.

For a plugin that uses Visualkit to work, Visualkit must be installed as a plugin on the server along with the plugin.

Installing it on the server is no different from a normal plugin. Just download the jar file from "Releases" and place it in the plugins directory on you server.

Release

Placeholders Placeholders

In GUIs created with Visualkit, you can use placeholders for text.

VisualkitElement
        .create(Material.NAME_TAG)
        .title(Component.text("Hello, ${name}."));

The placeholders are replaced with field values from the GUI implementation class at rendering time and automatically updated.

If these values are of type object, they will be processed with Object#toString(), expect that the implementation of net.kyori.adventure.text.Component will be embedded as is.

You can also use the "&" symbol to decorate text.

e.g: &aHello, world!

Localize Localize

The ability for players to view the UI in their own language is an important part of the UX. Visualkit makes it easy to implement displays that match the player's language settings.

Translations are defined by language files.

The language file must be placed directly under the jar as lang/{Minecraft locale code}.json. In a standard configuration for build systems such as Maven or Gradle, it will look like this: src/main/resources/lang/en_us.json.

A language file has the following syntax:

{
  "greeting": "&d&lHello, ${name}!"
}

As you can see, every translation has a key. There are no strict rules for the key, but dotted snake case is recommended.

In this way, translations are loaded into Visualkit by defining the language file in a specific location and syntax. Next, let's look at how to reference the translations defined in the language file from code.

// Note: These return a net.kyori.adventure.text.Component

// Get by locale code and full translation key
Language.translate(new NamespacedKey(plugin, "key"), MinecraftLocale.EN_US, "arg1=value1", "arg2=value2");

// Get by locale code and translation key
Language.translate(MinecraftLocale.EN_US, "key", "arg1=value1", "arg2=value2");

// Get by player and full translation key
Languate.translate(new NamespacedKey(plugin, "key"), player, "arg1=value1", "arg2=value2");

// Get by player and translation key
Language.translate("key", player, "arg1=value1", "arg2=value2");

If you pass a translation key without specifying a namespace, the namespace of the calling plugin will be automatically picked up.

Also, if a player is passed as an argument, the language setting of that player will be applied.

Chest GUI Create a chest GUI

Create a user interface using a chest.

1. Create a class that extends com.tksimeji.visualkit.ChestUI

You need to implement a title() method that returns net.kyori.adventure.text.Component and a size() method that defines the size of the chest.

public class MyChestUI extends ChestUI {
    public MyChestUI(@NotNull Player player) {
        super(player);
    }

    @Override
    public @NotNull Component title() {
        return Component.text("Cookie clicker").decorate(TextDecoration.BOLD);
    }

    @Override
    public @NotNull Size size() {
        return Size.SIZE_9;
    }    
}

2. Add element

Let's add elements to the GUI.

The simplest way to declare an element is to define a field in the class.

Add com.tksimeji.visualkit.api.Element to a field of type com.tksimeji.visualkit.element.VisualkitElement.

private int count;

@Element(13)
private final VisualkitElement cookieButton = VisualkitElement
        .create(Material.COOKIE)
        .title(Component.text("Click me!").color(NamedTextColor.GREEN).decorate(TextDecoration.BOLD))
        .lore(Component.text("Clicks: ${count}"))
        .sound(Sound.UI_BUTTON_CLICK, 1.0f, 1.2f);

// Note: The player head API was added in 0.2.x.

VisualkitElement.head()
    .url("https");

VisualkitElement.head("http://textures.minecraft.net/texture/a60ed3827ed16f34b367ff96fdd6a56cb365f522e58122c147fc919fa90b208c");

VisualkitElement.head(UUID.fromString("ee54c324-9ab4-472e-aa4d-392f15b820fb"));

VisualkitElement.head(Bukkit.getPlayer("tksimeji"));

Alternatively, you can specify an org.bukkit.inventory.ItemStack.

@Element(13)
private final ItemStack cookieButton = new ItemStack(Material.COOKIE, 1);

The annotation parameter specifies the index in the GUI.

You can place it in multiple slots:

@Element({2, 3, 5, 7, 11, 13, 17, 19})

You can also use asm (Advanced Slot Mapping) for more advanced specifications.

@Element(asm = {@Asm(from = 0, to = 8), @Asm(from = 18, to = 26), @Asm({27, 28})}, value = {29, 30})

If you want to dynamically add or remove elements, use com.tksimeji.visualkit.ChestUI#setElement(...)

// You can specify an VisualkitElement
setElement(0, VisualkitElement.create(Material.COOKIE).title(Component.text("Click me!")));

// You can specify an ItemStack
setElement(0, new ItemStack(Material.COOKIE, 1));

// Empty a slot
setElement(0, null);

3. Add policy

A policy defines the behavior of a slot.

Tip

The default policy is SlotPolicy.FIXATION

You can control drops outside the window by specifying -1 in the slot.

Defines a filed with a com.tksimeji.visualkit.api.Policy annotation:

// The method for specifying the scope is the same as for @Element

@Policy(1)
private final SlotPolicy policy = SlotPolicy.VARIATION;

// It can be applied to the UI as well as the player's inventory
@Policy(1, target = PolicyTarget.INVENTORY)
private final SlotPolicy policy = SlotPolicy.VARIATION;

Setting policies from code:

// The target will automatically be PolicyTarget.UI
@NotNull SlotPolicy getPolicy(int slot);

@NotNull SlotPolicy getPolicy(int slot, @NotNull PolicyTarget target);

// The target will automatically be PolicyTarget.UI
void setPolicy(int slot, @NotNull SlotPolicy policy);

void setPolicy(int slot, @NotNull SlotPolicy policy, @NotNull PolicyTarget target);

4. Add handler

Define a method to handle clicks on any slot.

Add a method with the annotation com.tksimeji.visualkit.api.Handler. In addition to slots, you can add action and mouse conditions to the Handler annotation.

@Handler(index = 13, action = Action.SINGLE, mouse = {Mouse.LEFT, Mouse.RIGHT})
public void onCookieClick() {
    count ++;
}

// You can also specify whether to allow clicks in the return value

@Handler(index = 13, action = Action.SINGLE, mouse = {Mouse.LEFT, Mouse.RIGHT})
public boolean onCookieClick() {
    count ++;
}

// Alternatively, handlers can be specified directly on the element.

@Element(13)
private final VisualkitElement cookieButton = VisualkitElement
        .create(Material.COOKIE)
        .handler(() -> count ++);

// It can also take arguments

@Element(13)
private final VisualkitElement cookieButton = VisualkitElement
        .create(Material.COOKIE)
        .handler((slot, action, mouse) -> samething());

Of course, you can also use asm to specify the slot.

@Handler(asm = {@Asm(from = 0, to = 8)}, index = {9, 10})

It can also take slot, action, and mouse state as arguments. However, these arguments are injected only if the following conditions are met:

Type
int / java.lang.Integer
com.tksimeji.visualkit.api.Action
com.tksimeji.visualkit.api.Mouse
org.bukkit.inventory.ItemStack

This is useful when you specify a broad conditions in the annotation.

@Handler(index = 0)
public void onClick(int slot, Click action, Mouse mouse, ItemStack itemStack) {
    // do something
}

4. Display the GUI

All you have to do is create an instance. Basically, you need to pass in the player as an argument.

new MyChestUI(player);

The GUI will be displayed to the player specified as an argument.

Anvil GUI Create a Anvil GUI

This is a GUI that uses an anvil. It is intended to be used as a text input screen.

1. Create a class that extends com.tksimeji.visualkit.AnvilUI

public class MyAnvilUI extends AnvilUI {
    // do something
}

2. Set up placeholders and dummy item

Their implementation is optional.

@Override
public @Nullable String placeholder() {
    return "Enter your search term";
}

@Override
public @NotNull Material dummy() {
    return Material.COMPASS;
}

3. Add processing when character input is completed

@Override
public void onTyped(@NotNull String string) {
    player.sendMessage(Component.text("You typed \"" + string + "\"."));
}

Merchant GUI Create a Merchant GUI

The Merchant GUI uses the GUI used to trade with villagers and wandering traders.

1. Create a class that extends com.tksimeji.visualkit.MerchantUI

public class MyMerchantUI extends MerchantUI {
    // do something
}

2. Add displayed transactions

If it is always constant, you can also declare it as a field.

@Target(index = 0)
private final @NotNull VisualkitTrade = VisualkitTrade.create(new ItemStack(Material.DIAMOND), 2)
        .requirement(new ItemStack(Material.EMERALD, 16))
        .onSelect(() -> doSomething())
        .onPurchare(() -> doSomething());

Of course, you can add and remove them dynamically.

setTrade(0, VisualkitTrade.create(new ItemStack(Material.REDSTONE)));

addTrade(VisualkitTrade.create(new ItemStack(Material.REDSTONE)));

removeTrade(0);

removeTrade(trade);

3. Add a handler

Add methods that will be called when selecting and purchasing a trade.

@Handler(index = 0, action = Action.SELECT)
void handler1() {}

@Handler(index = 0, action = Action.PURCHARE)
void handler2() {}

Details can be passed as arguments.

type description
int / java.lang.Integer index
com.tksimeji.visualkit.trade.VisualkitTrade trade

You can also specify whether to pass the event through by returning a boolean value.

Panel GUI Create a Panel GUI

The Panel GUI is a user interface that utilizes the scoreboard sidebar.

1. Create a class that extends com.tksimeji.visualkit.SharedPanelUI

public class MyPanelUI extends PanelUI {
    // do something
}

2. Write text on the panel

Here we'll use the constructor to write to it.

Let's display the player's information as an example. Placeholder are used to embed values.

The values is updated using the onTick method.

private String name;
private int health;
private int ping;

public MyPanelUI(@NotNull Player player) {
    super(player);
    
    setTitle(Component.text("INFO").color(NamedTextColor.YELLOW));
    
    addLine(Component.text("Hello, ${name}!"));
    addLine();
    addLine(Component.text("Health:").appendSpace().append(Component.text("${health}♥").color(NamedTextColor.RED)));
    setLine(3, Component.text("Ping:").appendSpace().append(Component.text("${ping} ms").color(NamedTextColor.GREEN)));
}

// Some code omitted //

@Override
public void onTick() {
    name = player.getName();
    health = (int) player.getHealth();
    ping = player.getPing();
}

This time we created a panel for one player, but if you want to display the same panel for multiple players, try extending com.tksimeji.visualkit.SharedPanelUI.

3. Display the GUI

// If you extend com.tksimeji.visualkit.PanelUI

new MyPanelUI(player);

// If you extend com.tksimeji.visualkit.SharedPanelUI

new MyPanelUI(player1, player2);

new MyPanelUI(List.of(player1, player2));

new MyPanelUI().addAudience(player);