unldenis / holoeasy

a simple and modern Java and Kotlin minecraft hologram library for 1.8-1.20.4 servers.
https://unldenis.github.io/holoeasy/
GNU Lesser General Public License v3.0
86 stars 26 forks source link

HologramLib 3 #44

Closed unldenis closed 6 months ago

unldenis commented 10 months ago

I know that there is a need to update to the latest versions as well (which someone could help me with, there will only be something to change in the Packets).

Solving #41 is also a goal

To make the library safer, taking advantage of kotlin's null safety and its other features, I created a branch to rewrite the project in kotlin with other new features.

The aim is to create a powerful library that can be used by both java and kotlin users.

For example, to create holograms, right now the most convenient utility is HologramBuilder.

Hologram.builder(plugin, loc)                                                  // initialize the builder
        .addTextLine("Hello")                                                      // add a text line
        .addTextLine("%%player%%")                                                 // add another text line
        .addClickableTextLine("Click me", 0.5f, 5f)                                 // add a clickable text line
        .addBlockLine(new ItemStack(Material.GOLD_BLOCK))                           // add a block line
        .addBlockLine(new ItemStack(Material.DIAMOND_BLOCK), AnimationType.CIRCLE)  // add a block line with animation
        .placeholders(placeholders)                                                // add placeholders
        .loadAndBuild(pool); 

I took a cue from SparkJava and Javalin to make it possible to create holograms declaratively, similar to kotlin's DSLs, which can also be used in java. Example

hologram(pool, loc, () -> {
  name("this is an internal name");

  textline("First Line");
  clickableline("This is a clickable line");
  blockline(new ItemStack(Material.GOLD_BLOCK), Animation.AnimationType.CIRCLE);

  // custom library line, remember to support the IHologramLoader
  customline(plugin -> {
      var line = new Line(plugin);
      var textLine = new TextLine(line, "Click me");
      return new ClickableTextLine(textLine, 0.5f, 5f);
  });
});

Kotlin

hologram(pool, loc) {
    name("this is an internal name")
    textline("First Line")
    clickableline("This is a clickable line")
    blockline(ItemStack(Material.GOLD_BLOCK), AnimationType.CIRCLE)

    // custom library line, remember to support the IHologramLoader
    customline {
        val line = Line(it)
        val textLine = TextLine(line, "Click me")
        ClickableTextLine(textLine, 0.5f, 5f)
    }
}
unldenis commented 10 months ago

I've just reached a "buildable" version, nothing has been tested yet but in terms of design how do you think? Note that the Plugin/Pool can also be derived statically, but the location is a mandatory parameter. Right now the location of the hologram is inside the config.

Old api:


HologramPool _pool = new HologramPool(plugin, 70);
InteractiveHologramPool pool = new InteractiveHologramPool(_pool, 0.5f, 5f);

Hologram.builder(plugin, loc)                                                  // initialize the builder
    .addTextLine("Hello")                                                      // add a text line
    .addTextLine("%%player%%")                                                 // add another text line
    .addClickableTextLine("Click me", 0.5f, 5f)                                 // add a clickable text line
    .addBlockLine(new ItemStack(Material.GOLD_BLOCK))                           // add a block line
    .addBlockLine(new ItemStack(Material.DIAMOND_BLOCK), AnimationType.CIRCLE)  // add a block line with animation
    .placeholders(placeholders)                                                // add placeholders
    .loadAndBuild(pool);                                                       // load and build the hologram

New api:

Kotlin

import com.github.unldenis.hologram.builder.HologramBuilder.*

    fun init(plugin: Plugin) {
        startInteractivePool(plugin, 60.0, 0.5f, 5f)
    }

    fun code(loc: Location, placeholders: Placeholders) {
        hologram {
            config {
                it.location = loc
                it.placeholders = placeholders
            }
            textline("Hello")
            textline("%%player%%")
            clickable("Click me")

            block(ItemStack(Material.GOLD_BLOCK))
            block(ItemStack(Material.DIAMOND_BLOCK), AnimationType.CIRCLE)
        }
    }

Java

import static com.github.unldenis.hologram.builder.HologramBuilder.*;

    public void init(Plugin plugin) {
        HologramLib.startInteractivePool(plugin, 60, 0.5f, 5f);
    }

    public void code(Location loc, Placeholders placeholders) {
        hologram(() -> {
            config(context -> {
                context.location = loc;
                context.placeholders = placeholders;
            });

            textline("Hello");
            textline("%%player%%");
            clickable("Click me");

            block(new ItemStack(Material.GOLD_BLOCK));
            block(new ItemStack(Material.DIAMOND_BLOCK), AnimationType.CIRCLE);

        });
    }
unldenis commented 10 months ago

This is the latest version. Events that can be managed via callback have been added, as well as "HologramKey" that identify holograms and can be used to search for them.

Java version

class Testj {

    private final IHologramPool pool;

    public Testj(Plugin plugin) {
        pool = HologramLib.startInteractivePool(plugin, 60, 0.5f, 5f);
    }

    public void code(Location loc, String id) {

        hologram(new HologramKey(pool, id), loc, () -> {

            textline("Hello");

            textline("{} Stats", Player::getName);
            textline("Score {} - {}", $ -> 0, $ -> 1);
            clickable("Click me")
                    .onClick(p -> p.sendMessage("Hi"));

            item(new ItemStack(Material.GOLDEN_APPLE));
            item(new ItemStack(Material.DIAMOND_BLOCK));
        }).onHide(player -> player.sendMessage("Hi im " + id));
    }

    public void getAndAddHideEvent(HologramKey key) {
        Hologram hologram = pool.get(key);
        hologram.onHide(player -> player.sendMessage("See you again..."));
    }
}

Kotlin version

class Test(plugin: Plugin) {

    private val pool: IHologramPool

    init {
        pool = startInteractivePool(plugin, 60.0, 0.5f, 5f)
    }

    fun code(loc: Location, id: String) {
        hologram(HologramKey(pool, id), loc) {
            textline("Hello")
            textline("{} Stats", Player::getName)
            textline("Score {} - {}", { 0 }, { 1 })
            clickable("Click me")
                .onClick { it.sendMessage("Hi") }

            item(ItemStack(Material.GOLDEN_APPLE))
            item(ItemStack(Material.DIAMOND_BLOCK))
        }.onHide { it.sendMessage("Hi im $id") }
    }

    fun getAndAddHideEvent(key: HologramKey) {
        val hologram = pool.get(key)
        hologram.onHide { it.sendMessage("See you again...") }
    }
}
unldenis commented 10 months ago

Tested everything in 1.20.2 except event handling. The packets follow the package documentation from wiki.vg but also need to be tested in 1.16.5 for old open issues due to spawn issues.

ThomasWega commented 9 months ago

Hey! Just wanted to say that it's awesome what you are still doing this lib! Happy someone still puts time and love into this, I use it a ton and appreciate it! <3

unldenis commented 9 months ago

Hey! Just wanted to say that it's awesome what you are still doing this lib! Happy someone still puts time and love into this, I use it a ton and appreciate it! <3

I appreciate someone interacting more, otherwise it feels like I'm doing something for no one! Thank you very much!

unldenis commented 9 months ago

Released! Tested with 1.16.5 and 1.20.2.

Guarmanda commented 9 months ago

will try it, because I'm currently adding it to my plugin, and 3 days ago it wasn't working with 1.13 to 1.20, nothing was showing

Guarmanda commented 9 months ago

Do you want me to test it in other versions

unldenis commented 9 months ago

Do you want me to test it in other versions

Of course and possibly open some other issues if there is something wrong!

Guarmanda commented 9 months ago

I use this piece of code to manage my holograms. I only do one-line holograms without effects. This was working with armorstands at first, but I then switched to HologramLib, and it worked too, but not from 1.13-1.20. Now I have something very similar, but with your new API, but it doesn't show anything (I tested in 1.16)

    private static IHologramPool pool;

    private static IHologramPool getPool() {
        if(pool == null) {
            pool = HoloEasy.startPool((Plugin)Main.getInstance(), 1200);
        }
        return pool;
    }

    private Hologram hologram;

    /**
     * Kills the hologram
     */
    public void remove() {
        if(Main.getVersion()>7){
            if(runnable != null) {
                runnable.cancel();
                runnable = null;
            }
            if(hologram == null) return;
            Hologram as = getHologram();
            if(as!=null) {
                pool.remove(as.getKey());
                hologram = null;
            }
        }
    }

    /**
     * @param name The text displayed by the hologram
     */
    public void setText(String name) {
        if(!NULL_NAME.contains(name)) {
            hologram = getHologram();
            setLine(Utils.color(name));
        }else {
            remove();
        }
    }

    /**
     * Manage hologram lines to display / change its name
     * @param name
     */
    private void setLine(String name){
        hologram.getLines().clear();
        // compose a TextLine hologram
        TextLine textLine = new TextLine((Plugin)Main.getInstance(), name, null, false);
        hologram.load(textLine);
    }

    /**
     * @return Creates the hologram 
     */
    private Hologram createHologram() {
        HologramKey key = new HologramKey(getPool(), chest.getName());
        Hologram holo =  new Hologram(key, location, new TextSequentialLoader());
        hologram = holo;
        return holo;
    }

    /**
     * @return The hologram
     */
    private Hologram getHologram() {
        if(hologram==null) {
            createHologram();
        }
        return hologram;
    }
unldenis commented 9 months ago

I use this piece of code to manage my holograms. I only do one-line holograms without effects. This was working with armorstands at first, but I then switched to HologramLib, and it worked too, but not from 1.13-1.20. Now I have something very similar, but with your new API, but it doesn't show anything (I tested in 1.16)

There are two main problems.

The first is that the hologram has not been added to the Pool. There is a pool method called #takeCareOf that adds the hologram to the pool. (and also why are you creating them this way?, I created a very convenient DSL for this).

The second problem is how the text of the lines changes. Lines have a #setObj and #update(players) method to update the text.

By the way, I'm working on 3.1.0 which will allow you to update the lines (text and item) in a reactive and intuitive way.

Guarmanda commented 9 months ago

Well I thought that since the hologram belongs to a key, and the key belongs to a pool, it would work without using "takecareof". Trying to apply your correction gave me a new problem: if I code this:

    private Hologram createHologram() {
        HologramKey key = new HologramKey(getPool(), chest.getName());
        Hologram holo =  new Hologram(key, location, new TextSequentialLoader());
        getPool().takeCareOf(key, holo);
        hologram = holo;
        return holo;
    }

It gives me this: java.lang.NullPointerException: Parameter specified as non-null is null: method org.holoeasy.pool.HologramPool.takeCareOf, parameter key Which is totally unlogical :/

I create them this way because these methods are used in several places in my plugin to move/create/remove holograms, so I prefer to not change these methods and their behavior, so that they continue to work as before

EDIT: using holo.getKey() for the takecareof gives the same result, implying the key into hologram is already null, but that's logically impossible... my brain is fucked

Guarmanda commented 9 months ago

The key is not null, I can log its value to console. Maybe you shouldn't have mixed kotlin and java code together, takecareof method seems to be broken

unldenis commented 9 months ago

3.1.0: Reactive holograms released.

And also, now you (@Guarmanda) don't have to manually add the hologram to the pool.

Guarmanda commented 9 months ago

The takecareof thing was because of me... I had another call to takecareof with null for first parameter and idk why^^ I was tired maybe

Guarmanda commented 9 months ago

Holograms are working with the new update without using takecareof, thank you, I will test all versions

Guarmanda commented 9 months ago

works on all versions except 1.19.4 (and maybe other 1.19) and 1.8-1.8.8

Guarmanda commented 9 months ago

Looking forward to fixes so I can post my plugin update :D

unldenis commented 9 months ago

Check the 3.1.1 release!