Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update: v4 - full rewrite #113

Draft
wants to merge 41 commits into
base: master
Choose a base branch
from
Draft

update: v4 - full rewrite #113

wants to merge 41 commits into from

Conversation

LichtHund
Copy link
Member

@LichtHund LichtHund commented May 4, 2024

This update is a full re-write of the library, everything will change, so expect big breaking changes.
Things of note:

  • Spigot support will be dropped - Spigot has been in a stale state for a while now and I see no point in keeping supporting it as it has been more headache than it is worth.
  • Re-write is made with the intent to support multiple platforms other than Paper.
  • v3 will be put in a separate branch where it can still be kept up to date with updates but no new features will be added.

Try it out

This is very early developments so many features are missing and many bugs might be present, use only for testing purposes.

maven("https://repo.triumphteam.dev/snapshots")
implementation("dev.triumphteam:triumph-gui-paper:4.0.0-SNAPSHOT")

I have started working on docs for the new version: https://triumphteam.dev/docs/4.x.x/triumph-gui/introduction

Goals

The main goal of the update is to modernize the library, make it more powerful and allow for more customizability.
One of the biggest issues the library had was its "update" system, updating items in the inventory was always the main support question and the most confusing for most users.

v4 - Concepts and examples

This update took a lot of inspirations from other things, such as Compose (mostly), React, and Noxcrew's interfaces.

GUI View

Differently from the previous version, the GUI itself is only a blue-print for views, the view is what the player will see and interact with. The view is limited to a single viewer, you can show two different views for 2 different players that are identical, but never have 2 players seeing the same view. The view only exists while the GUI is opened.

Component

Starting of with the most important concept, a GuiComponent. Similar to React it describes how things will be rendered, in this case rendering means adding items to the Inventory.
A component will take note of what items was added to the inventory and when re-rendering it'll replace them.
Think of a component as a container of items that can be freely changed, once a change happens all items are re-added into their location.

Simple Example:

.component(component -> {
   
    component.render((container, player) -> {
        
        container.set(Slot.of(2, 2), item);
    }); 
})

When this component is rendered it'll set the item onto the slot 2, 2 (10 on a normal chest inventory).

Container

Simply put, the container is a holder for the items and their slots. There are different types of containers, such as Chest, Crafting, etc. The only difference about them is the mapping of the slots.

State

A "state" will control when a component re-renders.
There are two types of state:

  • State - Empty, does not contain any value, all it contains is a way to manually trigger a re-render by doing state.trigger().
  • MutableState - Stores any value and triggers a re-render when the value changes.

A mutable state also depends on a StateMutationPolicy, this will tell the state how it handles equality. When setting a new value to the state, if the value is equal (based on the policy), it'll NOT trigger a re-render of the component. The built in policies are:

  • ReferenceEquality - If the updated value is the equal reference wise it'll NOT trigger a re-render. Think of it like using old == new.
  • StructuralEquality - If the updated value is the equal structurally wise it'll NOT trigger a re-render. Think of it like using old.equals(new). This is the default behavior.
  • NeverEqual - No matter the value set, it'll always trigger a new re-render.

Using a state:

.component(component -> {

    // Create a mutable state with the starting value of "0"
    // This also binds the state to this component
    final var state = component.state(0);

    component.render((container, player) -> {
        container.set(
            Slot.of(2, 2),
            // Display the state as the item's name by using `state.getValue()`
            ItemBuilder.from(Material.POTATO).name(Component.text("State -> " + state.getValue())).asGuiItem((player, context) -> {
                // Increment the state value by 1
                // This will trigger the component to re-render, meaning that this item will be re-made
                state.setValue(state.getValue() + 1);
            })
        );
    });
})

A state does not need to be made within a functional component, it can be created outside, stored, re-used, etc. If a state is shared between two components it'll cause both to re-render. A state can also be shared between views, meaning you can trigger re-renders from one view to another.

Extending

The library is built around being able to change quite a lot of behaviors. For example, a GuiComponent by itself does nothing, it is paired with a GuiComponentRenderer, which can be changed for each GUI. This allows you to have completely different implementations of components, from Kotlin's coroutine's suspending components, to Completable future components, and much more.
The same process can be applied to clicks, a click action by itself does nothing and it uses a ClickHandler to implement how a click is run, so suspending clicks, async clicks, etc, are all possible.

Re-write state

The re-write is in very early stages so A LOT can change. But the main functionality is already done.
Things that are left that are very important are:

  • System to update the GUI title.
  • System to handle event cancelling, right now all clicks are always cancelled.
  • System to support "storage" like components, where players can freely take and add items to the inventory.
  • Click information, currently all you get is the player.
  • History system to navigate between views. (maybe)
  • Built in components, such as scrolling, pagination, etc.

Feedback is very important, so feel free to comment any addition, or change you'd like to see!

@LichtHund LichtHund added this to the v4 milestone May 4, 2024
@LichtHund LichtHund marked this pull request as draft May 4, 2024 20:00
@LoJoSho
Copy link

LoJoSho commented May 20, 2024

Can't wait to try this rewrite out once it is released! Glad yall are also dropping Spigot support, doesn't make sense since most servers are already running Paper!

@xMrAfonso
Copy link

Can't wait to try this rewrite out once it is released! Glad yall are also dropping Spigot support, doesn't make sense since most servers are already running Paper!
You can already try it out and report any issues you find. There is already a public snapshot.

@EncryptSL
Copy link

I see container have set function but what addItem or something like that ?

Is planned add this feature or this missing for now.

@LichtHund
Copy link
Member Author

I see container have set function but what addItem or something like that ?

Is planned add this feature or this missing for now.

What usage for that? Asking because there might be a replacement feature already.

@EncryptSL
Copy link

In example you can see i use container.set(slot, item...) But what if i want only addItem without set()..
I must add variable slot for get number, i think for this example is good addItem.

Now i don't know if is behaviour of asGuiItem { player, context -> } only in this snapshot. If i want simply asGuiItem() without funcion this not allow me add.

        gui.component { component ->
            component.render { container, player ->
                var slot = 0
                for (material in Material.entries) {
                    if (!config.contains("level.ores.${material.name}")) continue
                    container.set(slot, ItemBuilder.from(itemStack.create()).asGuiItem { player, context -> })
                    slot++
                }
            }
        }.build().open(player)

@Keylessboi
Copy link

Are there updated docs yet?

@LichtHund
Copy link
Member Author

LichtHund commented May 21, 2024

In example you can see i use container.set(slot, item...) But what if i want only addItem without set().. I must add variable slot for get number, i think for this example is good addItem.

Now i don't know if is behaviour of asGuiItem { player, context -> } only in this snapshot. If i want simply asGuiItem() without funcion this not allow me add.

        gui.component { component ->
            component.render { container, player ->
                var slot = 0
                for (material in Material.entries) {
                    if (!config.contains("level.ores.${material.name}")) continue
                    container.set(slot, ItemBuilder.from(itemStack.create()).asGuiItem { player, context -> })
                    slot++
                }
            }
        }.build().open(player)

I'll give this some thought, you can play around with Layouts as they can provide something similar.

@LichtHund
Copy link
Member Author

Are there updated docs yet?

Not yet, as this is too early in development and a lot of stuff might change, so keeping docs up to date would be a nightmare. I will add some examples at some point though.

@SamB440
Copy link

SamB440 commented May 24, 2024

The pom seems to be including annotations, guava, adventure, slf4j as compile. This was leading to large jars - I had to set the dependency to isTransitive = false and manually depend on core and nova as well.

Also, loving the look of this so far, very happy that it will be multi-platform now. I am willing to add support for Sponge (I actually have a working version already).

One thing I would like to see is stuff like component.render not requiring inclusion of the player argument, I'm just naming the player variable something random as it's useless to me, but it looks a bit ugly. (I guess Java 22 improves this with unnamed variables?)

@LichtHund
Copy link
Member Author

The pom seems to be including annotations, guava, adventure, slf4j as compile. This was leading to large jars - I had to set the dependency to isTransitive = false and manually depend on core and nova as well.

Also, loving the look of this so far, very happy that it will be multi-platform now. I am willing to add support for Sponge (I actually have a working version already).

One thing I would like to see is stuff like component.render not requiring inclusion of the player argument, I'm just naming the player variable something random as it's useless to me, but it looks a bit ugly. (I guess Java 22 improves this with unnamed variables?)

Those are set as compile only transitive, usually they wouldn't be shaded, but that was only done for testing and I'll make them not transitive in soon.

Feel free to work on the sponge support but I'd probably recommend waiting a bit longer since internal changes can happen and you might constantly need to update your implementation.

And yes that is an intended change already, and instead it'll give you the GuiView, which you can get the player instance from if needed.

@LichtHund
Copy link
Member Author

I have also moved the discussion for this into #116 so feel free to continue the conversations there.

@utmmm
Copy link

utmmm commented Aug 31, 2024

Hello, you are doing a very interesting project, but as I see it, active development has been discontinued. Perhaps in the future you will be able to finish this?

@LichtHund
Copy link
Member Author

Hello, you are doing a very interesting project, but as I see it, active development has been discontinued. Perhaps in the future you will be able to finish this?

Hello, it hasn't been abandoned, just haven't had much time to work on it recently, I am hoping to resume work on it this month.

This was referenced Dec 8, 2024
* Ported the following from v3:
- SkullBuilder
- SkullUtil
- Mojang AuthLib
- Added `meta()` getter/setter
- Added `skull()` method

* Moved AuthLib to the version catalog and pumped the version number.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants