diff --git a/.gitignore b/.gitignore index 57be55c5..b2d4192d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,125 @@ +test-plugin/ -gui/build/ +# Created by https://www.toptal.com/developers/gitignore/api/gradle,intellij+all +# Edit at https://www.toptal.com/developers/gitignore?templates=gradle,intellij+all -test-plugin/build/ +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -.idea/ +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf -.gradle/ +# AWS User-specific +.idea/**/aws.xml -temp.java +# Generated files +.idea/**/contentModel.xml -temp.java.asc +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml -gradle.properties +# Gradle +.idea/**/gradle.xml +.idea/**/libraries -test-plugin/ +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr -build/ +# CMake +cmake-build-*/ +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Gradle ### +.gradle +build/ run/ -fabric-test/ +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.toptal.com/developers/gitignore/api/gradle,intellij+all + +*.salive diff --git a/LICENSE b/LICENSE index b4bac0e2..9a0bff4d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 TriumphTeam +Copyright (c) 2024 TriumphTeam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 00000000..51fb213e --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,19 @@ +import dev.triumphteam.root.root + +plugins { + `kotlin-dsl` + id("dev.triumphteam.root.logic") version "0.0.14" +} + +dependencies { + // Hack to allow version catalog inside convention plugins + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + + // Bundled plugins + implementation(libs.bundles.build) + + // Bundled example plugins + implementation(libs.bundles.paper.examples) + + root("0.0.14") +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 00000000..61d53387 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,31 @@ +import dev.triumphteam.root.localLibs +import dev.triumphteam.root.releasesRepo + +rootProject.name = "build-logic" + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + gradlePluginPortal() + mavenCentral() + releasesRepo() + } + + versionCatalogs { + + register("libs") { + from(files(localLibs)) + } + } +} + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://repo.triumphteam.dev/releases") + } +} + +plugins { + id("dev.triumphteam.root.settings") version "0.0.14" +} diff --git a/build-logic/src/main/kotlin/gui.base.gradle.kts b/build-logic/src/main/kotlin/gui.base.gradle.kts new file mode 100644 index 00000000..8fca780b --- /dev/null +++ b/build-logic/src/main/kotlin/gui.base.gradle.kts @@ -0,0 +1,47 @@ +import org.gradle.accessors.dm.LibrariesForLibs + +// Hack which exposes `libs` to this convention plugin +val libs = the() + +plugins { + `java-library` + kotlin("jvm") + id("dev.triumphteam.root") + id("com.github.hierynomus.license") +} + +repositories { + mavenCentral() + maven("https://triumphteam.dev/snapshots/") +} + +dependencies { + compileOnly(libs.annotations) +} + + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + withSourcesJar() + withJavadocJar() +} + +kotlin { + explicitApi() +} + +license { + header = rootProject.file("LICENSE") + encoding = "UTF-8" + useDefaultMappings = true + + include("**/*.kt") + include("**/*.java") +} + +tasks { + withType { + options.encoding = "UTF-8" + options.compilerArgs.add("-parameters") + } +} diff --git a/build-logic/src/main/kotlin/gui.library.gradle.kts b/build-logic/src/main/kotlin/gui.library.gradle.kts new file mode 100644 index 00000000..ae20ea75 --- /dev/null +++ b/build-logic/src/main/kotlin/gui.library.gradle.kts @@ -0,0 +1,30 @@ +import dev.triumphteam.root.configure.PublishConfigure + +plugins { + `maven-publish` + signing + id("dev.triumphteam.root") +} + +root { + configurePublishing { + configure { + + from(components["java"]) + + snapshotsRepo(PublishConfigure.TRIUMPH_SNAPSHOTS) { + username = providers.gradleProperty("triumph.repo.user").orNull ?: "" + password = providers.gradleProperty("triumph.repo.token").orNull ?: "" + } + + releasesRepo(PublishConfigure.CENTRAL) { + username = providers.gradleProperty("central.repo.user").orNull ?: "" + password = providers.gradleProperty("central.repo.password").orNull ?: "" + } + + /*signing { + sign(publishing.publications["maven"]) + }*/ + } + } +} diff --git a/build-logic/src/main/kotlin/gui.paper-example.gradle.kts b/build-logic/src/main/kotlin/gui.paper-example.gradle.kts new file mode 100644 index 00000000..bedc541a --- /dev/null +++ b/build-logic/src/main/kotlin/gui.paper-example.gradle.kts @@ -0,0 +1,23 @@ +import org.gradle.accessors.dm.LibrariesForLibs + +// Hack which exposes `libs` to this convention plugin +val libs = the() + +plugins { + `java-library` + id("xyz.jpenilla.run-paper") + id("xyz.jpenilla.resource-factory-bukkit-convention") + id("com.gradleup.shadow") +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(libs.paper) +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} diff --git a/build-logic/src/main/kotlin/gui.parent.gradle.kts b/build-logic/src/main/kotlin/gui.parent.gradle.kts new file mode 100644 index 00000000..a8b3fb23 --- /dev/null +++ b/build-logic/src/main/kotlin/gui.parent.gradle.kts @@ -0,0 +1,4 @@ +plugins { + `java-library` + kotlin("jvm") +} diff --git a/build.gradle.kts b/build.gradle.kts index 0db7c0b3..8627a0b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,49 +1,3 @@ plugins { - id("java-library") - id("com.github.hierynomus.license") version "0.16.1" -} - -allprojects { - repositories { - mavenLocal() - mavenCentral() - maven("https://repo.papermc.io/repository/maven-public/") - } -} - -subprojects { - - apply { - plugin("java-library") - plugin("com.github.hierynomus.license") - } - - group = "dev.triumphteam" - version = "3.1.7" - - dependencies { - compileOnly("org.jetbrains:annotations:21.0.1") - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - - val adventureVersion = "4.14.0" - api("net.kyori:adventure-api:$adventureVersion") - api("net.kyori:adventure-text-serializer-legacy:$adventureVersion") - api("net.kyori:adventure-text-serializer-gson:$adventureVersion") - } - - license { - header = rootProject.file("LICENSE") - encoding = "UTF-8" - mapping("java", "JAVADOC_STYLE") - include("**/*.java") - } - - tasks { - withType { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - options.encoding = "UTF-8" - } - } - + id("gui.parent") } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e2c57f88..9f549850 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,106 +1,12 @@ plugins { - `maven-publish` - signing -} - -repositories { - mavenCentral() - maven("https://libraries.minecraft.net/") + id("gui.base") + id("gui.library") } dependencies { - compileOnly("com.mojang:authlib:1.5.25") -} - -val javaComponent: SoftwareComponent = components["java"] - -tasks { - - val sourcesJar by creating(Jar::class) { - archiveClassifier.set("sources") - from(sourceSets.main.get().allSource) - } - - val javadocJar by creating(Jar::class) { - dependsOn.add(javadoc) - archiveClassifier.set("javadoc") - from(javadoc) - } - - publishing { - publications { - create("maven") { - from(javaComponent) - artifact(sourcesJar) - artifact(javadocJar) - versionMapping { - usage("java-api") { - fromResolutionOf("runtimeClasspath") - } - usage("java-runtime") { - fromResolutionResult() - } - } - pom { - name.set("Triumph GUI") - description.set("Library for easy creation of GUIs for Bukkit plugins.") - url.set("https://github.com/TriumphTeam/triumph-gui") - - licenses { - license { - name.set("MIT License") - url.set("http://www.opensource.org/licenses/mit-license.php") - } - } - - developers { - developer { - id.set("matt") - name.set("Mateus Moreira") - organization.set("TriumphTeam") - organizationUrl.set("https://github.com/TriumphTeam") - } - } - - scm { - connection.set("scm:git:git://github.com/TriumphTeam/triumph-gui.git") - developerConnection.set("scm:git:ssh://github.com:TriumphTeam/triumph-gui.git") - url.set("https://github.com/TriumphTeam/triumph-gui") - } - } - } - } - - repositories { - maven { - if (version.toString().contains("SNAPSHOT")) { - credentials { - username = System.getenv("REPO_USER") - password = System.getenv("REPO_PASS") - } - - url = uri("https://repo.mattstudios.me/artifactory/public-snapshot/") - return@maven - } - - credentials { - username = System.getenv("SONATYPE_USER") - password = System.getenv("SONATYPE_PASSWORD") - } - - url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") - } - } - - } - - signing { - /*useGpgCmd() - val signingKey = System.getenv("GPG_KEY") - val signingPassword = System.getenv("GPG_PASS") - val secretKey = System.getenv("GPG_SECRET_KEY") - useInMemoryPgpKeys(signingKey, secretKey, signingPassword)*/ - sign(publishing.publications["maven"]) - } + api(libs.nova) + compileOnly(libs.guava) + compileOnly(libs.adventure.api) + compileOnly(libs.logger) } diff --git a/core/src/main/java/dev/triumphteam/gui/AbstractGuiView.java b/core/src/main/java/dev/triumphteam/gui/AbstractGuiView.java new file mode 100644 index 00000000..6184d69f --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/AbstractGuiView.java @@ -0,0 +1,194 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui; + +import dev.triumphteam.gui.actions.GuiCloseAction; +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.click.processor.ClickProcessor; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.RenderedComponent; +import dev.triumphteam.gui.component.StatefulGuiComponent; +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer; +import dev.triumphteam.gui.container.type.GuiContainerType; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.item.RenderedGuiItem; +import dev.triumphteam.gui.title.GuiTitle; +import dev.triumphteam.gui.title.StatefulGuiTitle; +import dev.triumphteam.gui.title.renderer.DefaultGuiTitleRenderer; +import dev.triumphteam.gui.title.renderer.GuiTitleRenderer; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class AbstractGuiView implements GuiView { + + private final P viewer; + private final GuiTitle title; + private final List> components; + private final List closeActions; + private final GuiComponentRenderer renderer; + private final ClickHandler

defaultClickHandler; + private final GuiContainerType containerType; + private final GuiTitleRenderer titleRenderer = new DefaultGuiTitleRenderer(); + // Click processor. + private final ClickProcessor clickProcessor; + // Cache of rendered components. + private final Map, RenderedComponent> renderedComponents = new ConcurrentHashMap<>(); + // All the gui items that have been rendered and are in the inventory. + private final Map> allRenderedItems = new ConcurrentHashMap<>(); + // Helper boolean for updating title. + private boolean updating = true; + private Component renderedTitle = null; + + public AbstractGuiView( + final @NotNull P viewer, + final @NotNull GuiTitle title, + final @NotNull List<@NotNull GuiComponent> components, + final @NotNull List closeActions, + final @NotNull GuiContainerType containerType, + final @NotNull GuiComponentRenderer renderer, + final @NotNull ClickHandler

defaultClickHandler, + final @NotNull ClickProcessor clickProcessor + ) { + this.title = title; + this.viewer = viewer; + this.components = components; + this.closeActions = closeActions; + this.containerType = containerType; + this.renderer = renderer; + this.defaultClickHandler = defaultClickHandler; + this.clickProcessor = clickProcessor; + } + + public @NotNull P viewer() { + return viewer; + } + + public abstract @NotNull String viewerName(); + + public abstract @NotNull UUID viewerUuid(); + + protected abstract void clearSlot(final int slot); + + protected abstract void populateInventory(final @NotNull Map> renderedItems); + + protected abstract void openInventory(final boolean updating); + + @Override + public void open() { + if (title instanceof StatefulGuiTitle statefulGuiTitle) { + // Add listener to used states + statefulGuiTitle.states().forEach(state -> { + state.addListener(this, () -> { + titleRenderer.renderTitle(title, (rendered) -> { + this.renderedTitle = rendered; + openInventory(true); + }); + }); + }); + } + + titleRenderer.renderTitle(title, (rendered) -> { + this.renderedTitle = rendered; + openInventory(false); + setup(); + }); + } + + protected void setup() { + components.forEach(component -> { + + if (component instanceof StatefulGuiComponent statefulComponent) { + // Add listener to used states + statefulComponent.states().forEach(state -> { + state.addListener(this, () -> renderer.renderComponent(viewer, component, this)); + }); + } + + // Then render component + renderer.renderComponent(viewer, component, this); + }); + } + + public void completeRendered(final @NotNull RenderedComponent renderedComponent) { + var ownerComponent = renderedComponent.component(); + // Check if component was already rendered before + var existing = renderedComponents.get(ownerComponent); + if (existing != null) { + // Clear its uses + existing.renderedItems().forEach((slot, ignored) -> { + clearSlot(slot); + allRenderedItems.remove(slot); + }); + } + + renderedComponents.put(ownerComponent, renderedComponent); + + final var renderedItems = renderedComponent.renderedItems(); + allRenderedItems.putAll(renderedItems); + + populateInventory(renderedItems); + } + + public void processClick(final @NotNull ClickContext context) { + clickProcessor.processClick(context, this); + } + + public @Nullable RenderedGuiItem getItem(final int slot) { + return allRenderedItems.get(slot); + } + + public @NotNull ClickHandler

getDefaultClickHandler() { + return defaultClickHandler; + } + + public @NotNull GuiContainerType getContainerType() { + return containerType; + } + + public @NotNull List getCloseActions() { + return closeActions; + } + + public Component getTitle() { + if (renderedTitle == null) { + throw new TriumphGuiException("Tried to get title before it was available."); + } + return renderedTitle; + } + + public boolean isUpdating() { + return updating; + } + + protected void setUpdating(final boolean newValue) { + this.updating = newValue; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/BaseGui.java b/core/src/main/java/dev/triumphteam/gui/BaseGui.java new file mode 100644 index 00000000..d7fe1326 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/BaseGui.java @@ -0,0 +1,43 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui; + +import org.jetbrains.annotations.NotNull; + +/** + * Representation of a simple GUI. + * The GUI itself does nothing other than be a blue-print for multiple {@link GuiView} views, + * for {@link P} players to interact with. + * + * @param

A player. + */ +public interface BaseGui

{ + + /** + * Opens a {@link GuiView} of this GUI for the {@link P} player. + * + * @param player The player to show the view to. + */ + void open(final @NotNull P player); +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java b/core/src/main/java/dev/triumphteam/gui/GuiView.java similarity index 83% rename from core/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java rename to core/src/main/java/dev/triumphteam/gui/GuiView.java index dbf38b93..353f0b87 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java +++ b/core/src/main/java/dev/triumphteam/gui/GuiView.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components.exception; +package dev.triumphteam.gui; -public final class GuiException extends RuntimeException { - public GuiException(String message) { - super(message); - } +public interface GuiView { + + void open(); + + void close(); } diff --git a/core/src/main/java/dev/triumphteam/gui/actions/GuiCloseAction.java b/core/src/main/java/dev/triumphteam/gui/actions/GuiCloseAction.java new file mode 100644 index 00000000..4d2ff46e --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/actions/GuiCloseAction.java @@ -0,0 +1,7 @@ +package dev.triumphteam.gui.actions; + +@FunctionalInterface +public interface GuiCloseAction { + + void onClose(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/BaseGuiBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/BaseGuiBuilder.java new file mode 100644 index 00000000..fdb04b97 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/builder/BaseGuiBuilder.java @@ -0,0 +1,270 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder; + +import dev.triumphteam.gui.BaseGui; +import dev.triumphteam.gui.actions.GuiCloseAction; +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.SimpleGuiComponent; +import dev.triumphteam.gui.component.functional.FunctionalGuiComponent; +import dev.triumphteam.gui.component.functional.FunctionalGuiComponentRender; +import dev.triumphteam.gui.component.functional.SimpleFunctionalGuiComponent; +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer; +import dev.triumphteam.gui.container.type.GuiContainerType; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.settings.GuiSettings; +import dev.triumphteam.gui.title.GuiTitle; +import dev.triumphteam.gui.title.SimpleGuiTitle; +import dev.triumphteam.gui.title.functional.FunctionalGuiTitle; +import dev.triumphteam.gui.title.functional.SimpleFunctionalGuiTitle; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Main builder for all GUIs. + * Container the main base methods that are shared between all implementations. + * + * @param The implementation of this {@link BaseGuiBuilder}. + * @param

The player type. + * @param The {@link BaseGui} type. + * @param The item type. + */ +@SuppressWarnings({"unchecked", "UnusedReturnValue"}) +public abstract class BaseGuiBuilder, P, G extends BaseGui

, I, C extends GuiContainerType> { + + private final GuiSettings guiSettings; + private final List> components = new ArrayList<>(); + private final List closeActions = new ArrayList<>(); + private C containerType; + private ClickHandler

clickHandler = null; + private GuiComponentRenderer componentRenderer = null; + private GuiTitle title = null; + private long spamPreventionDuration = -1; + + public BaseGuiBuilder( + final GuiSettings guiSettings, + final @NotNull C defaultContainerType + ) { + this.guiSettings = guiSettings; + this.containerType = defaultContainerType; + } + + /** + * Sets the title of the {@link BaseGui}. + * + * @param title The title {@link GuiTitle}. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B title(final @NotNull GuiTitle title) { + this.title = title; + return (B) this; + } + + /** + * Sets the title of the {@link BaseGui}. + * + * @param title The title {@link Component}. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B title(final @NotNull Component title) { + return title(new SimpleGuiTitle(() -> title, Collections.emptyList())); + } + + /** + * Sets the title of the {@link BaseGui}. + * This version is reactive and will update when the provided state changes. + * + * @param title The title builder function. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + public @NotNull B title(final @NotNull Consumer<@NotNull FunctionalGuiTitle> title) { + final var simpleTitle = new SimpleFunctionalGuiTitle(); + title.accept(simpleTitle); + return title(simpleTitle.asGuiTitle()); + } + + /** + * Sets the container type of the GUI. + * + * @param containerType The {@link GuiContainerType} to be used. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B containerType(final @NotNull C containerType) { + this.containerType = containerType; + return (B) this; + } + + /** + * Sets the {@link ClickHandler} to be used for all {@link GuiComponent} unless they provide their own. + * + * @param clickHandler The default {@link ClickHandler}. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B clickHandler(final @NotNull ClickHandler

clickHandler) { + this.clickHandler = clickHandler; + return (B) this; + } + + /** + * Sets the {@link GuiComponentRenderer} to be used by the {@link BaseGui}. + * + * @param componentRenderer The {@link GuiComponentRenderer} to use. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B componentRenderer(final @NotNull GuiComponentRenderer componentRenderer) { + this.componentRenderer = componentRenderer; + return (B) this; + } + + /** + * Sets how long the spam prevention should be, in {@link TimeUnit#MILLISECONDS}. + * Set it to {@code 0} to disable it altogether. + * + * @param spamPreventionDuration How long the spam prevention should last. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B spamPreventionDuration(final long spamPreventionDuration) { + if (spamPreventionDuration < 0) { + throw new TriumphGuiException("Spam prevention duration cannot be negative!"); + } + + this.spamPreventionDuration = spamPreventionDuration; + return (B) this; + } + + /** + * Adds a {@link GuiComponent} to the {@link BaseGui}. + *

+ * The {@link Consumer} will create a {@link FunctionalGuiComponent}, + * which by itself is NOT a {@link GuiComponent}.

+ * This will in turn build a real {@link GuiComponent} before being added to the {@link BaseGui}. + * + * @param component The functional component builder. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B component(final @NotNull Consumer<@NotNull FunctionalGuiComponent> component) { + final var simpleComponent = new SimpleFunctionalGuiComponent(); + component.accept(simpleComponent); + components.add(simpleComponent.asGuiComponent()); + return (B) this; + } + + /** + * For faster creation of components that don't require states. + * Use this for components you don't need to update, aka static items. + * + * @param render The component render. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B statelessComponent(final @NotNull FunctionalGuiComponentRender render) { + return component(new SimpleGuiComponent<>(render, Collections.emptyList())); + } + + /** + * Adds a {@link GuiComponent} to the {@link BaseGui}. + * + * @param component Any type of {@link GuiComponent}. + * @return The {@link B} instance of the {@link BaseGuiBuilder}. + */ + @Contract("_ -> this") + public @NotNull B component(final @NotNull GuiComponent component) { + components.add(component); + return (B) this; + } + + @Contract("_ -> this") + public @NotNull B onClose(final @NotNull GuiCloseAction closeAction) { + closeActions.add(closeAction); + return (B) this; + } + + /** + * Finalizes the builder and creates a {@link G} instance of {@link BaseGui} depending on the platform. + * + * @return {@link G} which is a {@link BaseGui}. + */ + public abstract G build(); + + // ---------------- Internal getters ---------------- // + + protected @NotNull C getContainerType() { + return containerType; + } + + protected @NotNull List> getComponents() { + return components; + } + + protected @NotNull List getCloseActions() { + return closeActions; + } + + protected @NotNull ClickHandler

getClickHandler() { + if (clickHandler == null) { + return guiSettings.getClickHandler(); + } + + return clickHandler; + } + + protected @NotNull GuiComponentRenderer getComponentRenderer() { + if (componentRenderer == null) { + return guiSettings.getComponentRenderer(); + } + + return componentRenderer; + } + + protected @NotNull GuiTitle getTitle() { + if (title == null) { + throw new TriumphGuiException("Cannot create GUI with empty title!"); + } + + return title; + } + + protected long getSpamPreventionDuration() { + if (spamPreventionDuration < 0) { + return guiSettings.getSpamPreventionDuration(); + } + + return spamPreventionDuration; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java deleted file mode 100644 index 2c3872d4..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java +++ /dev/null @@ -1,315 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.gui; - -import dev.triumphteam.gui.components.InteractionModifier; -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.guis.BaseGui; -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.EnumSet; -import java.util.Set; -import java.util.function.Consumer; - -/** - * The base for all the GUI builders this is due to some limitations - * where some builders will have unique features based on the GUI type - * - * @param The Type of {@link BaseGui} - */ -@SuppressWarnings("unchecked") -public abstract class BaseGuiBuilder> { - - private Component title = null; - private int rows = 1; - private final EnumSet interactionModifiers = EnumSet.noneOf(InteractionModifier.class); - - private Consumer consumer; - - /** - * Sets the rows for the GUI - * This will only work on CHEST {@link dev.triumphteam.gui.components.GuiType} - * - * @param rows The amount of rows - * @return The builder - */ - @NotNull - @Contract("_ -> this") - public B rows(final int rows) { - this.rows = rows; - return (B) this; - } - - /** - * Sets the title for the GUI - * This will be either a Component or a String - * - * @param title The GUI title - * @return The builder - */ - @NotNull - @Contract("_ -> this") - public B title(@NotNull final Component title) { - this.title = title; - return (B) this; - } - - /** - * Disable item placement inside the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B disableItemPlace() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_PLACE); - return (B) this; - } - - /** - * Disable item retrieval inside the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B disableItemTake() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_TAKE); - return (B) this; - } - - /** - * Disable item swap inside the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B disableItemSwap() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_SWAP); - return (B) this; - } - - /** - * Disable item drop inside the GUI - * - * @return The builder - * @since 3.0.3 - */ - @NotNull - @Contract(" -> this") - public B disableItemDrop() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_DROP); - return (B) this; - } - - /** - * Disable other GUI actions - * This option pretty much disables creating a clone stack of the item - * - * @return The builder - * @since 3.0.4 - */ - @NotNull - @Contract(" -> this") - public B disableOtherActions() { - interactionModifiers.add(InteractionModifier.PREVENT_OTHER_ACTIONS); - return (B) this; - } - - /** - * Disable all the modifications of the GUI, making it immutable by player interaction - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B disableAllInteractions() { - interactionModifiers.addAll(InteractionModifier.VALUES); - return (B) this; - } - - /** - * Allows item placement inside the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B enableItemPlace() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_PLACE); - return (B) this; - } - - /** - * Allow items to be taken from the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B enableItemTake() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_TAKE); - return (B) this; - } - - /** - * Allows item swap inside the GUI - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B enableItemSwap() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_SWAP); - return (B) this; - } - - /** - * Allows item drop inside the GUI - * - * @return The builder - * @since 3.0.3 - */ - @NotNull - @Contract(" -> this") - public B enableItemDrop() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_DROP); - return (B) this; - } - - /** - * Enable other GUI actions - * This option pretty much enables creating a clone stack of the item - * - * @return The builder - * @since 3.0.4 - */ - @NotNull - @Contract(" -> this") - public B enableOtherActions() { - interactionModifiers.remove(InteractionModifier.PREVENT_OTHER_ACTIONS); - return (B) this; - } - - /** - * Enable all modifications of the GUI, making it completely mutable by player interaction - * - * @return The builder - * @since 3.0.0 - * @author SecretX - */ - @NotNull - @Contract(" -> this") - public B enableAllInteractions() { - interactionModifiers.clear(); - return (B) this; - } - - /** - * Applies anything to the GUI once it's created - * Can be pretty useful for setting up small things like default actions - * - * @param consumer A {@link Consumer} that passes the built GUI - * @return The builder - */ - @NotNull - @Contract("_ -> this") - public B apply(@NotNull final Consumer consumer) { - this.consumer = consumer; - return (B) this; - } - - /** - * Creates the given GuiBase - * Has to be abstract because each GUI are different - * - * @return The new {@link BaseGui} - */ - @NotNull - @Contract(" -> new") - public abstract G create(); - - /** - * Getter for the title - * - * @return The current title - */ - @NotNull - protected Component getTitle() { - if (title == null) { - throw new GuiException("GUI title is missing!"); - } - - return title; - } - - /** - * Getter for the rows - * - * @return The amount of rows - */ - protected int getRows() { - return rows; - } - - /** - * Getter for the consumer - * - * @return The consumer - */ - @Nullable - protected Consumer getConsumer() { - return consumer; - } - - - /** - * Getter for the set of interaction modifiers - * @return The set of {@link InteractionModifier} - * @since 3.0.0 - * @author SecretX - */ - @NotNull - protected Set getModifiers() { - return interactionModifiers; - } -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java deleted file mode 100644 index deea0f6d..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.gui; - -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.guis.PaginatedGui; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Consumer; - -/** - * GUI builder for creating a {@link PaginatedGui} - */ -public class PaginatedBuilder extends BaseGuiBuilder { - - private int pageSize = 0; - - /** - * Sets the desirable page size, most of the time this isn't needed - * - * @param pageSize The amount of free slots that page items should occupy - * @return The current builder - */ - @NotNull - @Contract("_ -> this") - public PaginatedBuilder pageSize(final int pageSize) { - this.pageSize = pageSize; - return this; - } - - /** - * Creates a new {@link PaginatedGui} - * - * @return A new {@link PaginatedGui} - */ - @NotNull - @Override - @Contract(" -> new") - public PaginatedGui create() { - final PaginatedGui gui = new PaginatedGui(getRows(), pageSize, Legacy.SERIALIZER.serialize(getTitle()), getModifiers()); - - final Consumer consumer = getConsumer(); - if (consumer != null) consumer.accept(gui); - - return gui; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java deleted file mode 100644 index 4d5ea332..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.gui; - -import dev.triumphteam.gui.components.ScrollType; -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.guis.ScrollingGui; -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Consumer; - -/** - * The simple GUI builder is used for creating a {@link ScrollingGui} that uses {@link Component} for title - * TODO This class needs more work to remove the redundant pageSize since it's the same as the paginated builder - */ -public final class ScrollingBuilder extends BaseGuiBuilder { - - private ScrollType scrollType; - private int pageSize = 0; - - /** - * Main constructor - * - * @param scrollType The {@link ScrollType} to default to - */ - public ScrollingBuilder(@NotNull final ScrollType scrollType) { - this.scrollType = scrollType; - } - - /** - * Sets the {@link ScrollType} to be used - * - * @param scrollType Either horizontal or vertical scrolling - * @return The current builder - */ - @NotNull - @Contract("_ -> this") - public ScrollingBuilder scrollType(@NotNull final ScrollType scrollType) { - this.scrollType = scrollType; - return this; - } - - /** - * Sets the desirable page size, most of the times this isn't needed - * - * @param pageSize The amount of free slots that page items should occupy - * @return The current builder - */ - @NotNull - @Contract("_ -> this") - public ScrollingBuilder pageSize(final int pageSize) { - this.pageSize = pageSize; - return this; - } - - /** - * Creates a new {@link ScrollingGui} - * - * @return A new {@link ScrollingGui} - */ - @NotNull - @Override - @Contract(" -> new") - public ScrollingGui create() { - final ScrollingGui gui = new ScrollingGui(getRows(), pageSize, Legacy.SERIALIZER.serialize(getTitle()), scrollType, getModifiers()); - - final Consumer consumer = getConsumer(); - if (consumer != null) consumer.accept(gui); - - return gui; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java deleted file mode 100644 index 8dc98b67..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.gui; - -import dev.triumphteam.gui.components.GuiType; -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.guis.Gui; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Consumer; - -/** - * The simple GUI builder is used for creating a {@link Gui} - */ -public final class SimpleBuilder extends BaseGuiBuilder { - - private GuiType guiType; - - /** - * Main constructor - * - * @param guiType The {@link GuiType} to default to - */ - public SimpleBuilder(@NotNull final GuiType guiType) { - this.guiType = guiType; - } - - /** - * Sets the {@link GuiType} to use on the GUI - * This method is unique to the simple GUI - * - * @param guiType The {@link GuiType} - * @return The current builder - */ - @NotNull - @Contract("_ -> this") - public SimpleBuilder type(@NotNull final GuiType guiType) { - this.guiType = guiType; - return this; - } - - /** - * Creates a new {@link Gui} - * - * @return A new {@link Gui} - */ - @NotNull - @Override - @Contract(" -> new") - public Gui create() { - final Gui gui; - final String title = Legacy.SERIALIZER.serialize(getTitle()); - if (guiType == null || guiType == GuiType.CHEST) { - gui = new Gui(getRows(), title, getModifiers()); - } else { - gui = new Gui(guiType, title, getModifiers()); - } - - final Consumer consumer = getConsumer(); - if (consumer != null) consumer.accept(gui); - - return gui; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java deleted file mode 100644 index fc6045d6..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java +++ /dev/null @@ -1,197 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.components.util.VersionHelper; -import org.bukkit.DyeColor; -import org.bukkit.Material; -import org.bukkit.Tag; -import org.bukkit.block.banner.Pattern; -import org.bukkit.block.banner.PatternType; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BannerMeta; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.EnumSet; -import java.util.List; - -/** - * Item builder for banners only - * - * @author GabyTM https://github.com/iGabyTM - * @since 3.0.1 - */ -@SuppressWarnings("unused") -public final class BannerBuilder extends BaseItemBuilder { - - private static final Material DEFAULT_BANNER; - private static final EnumSet BANNERS; - - static { - if (VersionHelper.IS_ITEM_LEGACY) { - DEFAULT_BANNER = Material.valueOf("BANNER"); - BANNERS = EnumSet.of(Material.valueOf("BANNER")); - } else { - DEFAULT_BANNER = Material.WHITE_BANNER; - BANNERS = EnumSet.copyOf(Tag.BANNERS.getValues()); - } - } - - BannerBuilder() { - super(new ItemStack(DEFAULT_BANNER)); - } - - BannerBuilder(@NotNull ItemStack itemStack) { - super(itemStack); - if (!BANNERS.contains(itemStack.getType())) { - throw new GuiException("BannerBuilder requires the material to be a banner!"); - } - } - - /** - * Sets the base color for this banner - * - * @param color the base color - * @return {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BannerBuilder baseColor(@NotNull final DyeColor color) { - final BannerMeta bannerMeta = (BannerMeta) getMeta(); - - bannerMeta.setBaseColor(color); - setMeta(bannerMeta); - return this; - } - - /** - * Adds a new pattern on top of the existing patterns - * - * @param color the pattern color - * @param pattern the pattern type - * @return {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_, _ -> this") - public BannerBuilder pattern(@NotNull final DyeColor color, @NotNull final PatternType pattern) { - final BannerMeta bannerMeta = (BannerMeta) getMeta(); - - bannerMeta.addPattern(new Pattern(color, pattern)); - setMeta(bannerMeta); - return this; - } - - /** - * Adds new patterns on top of the existing patterns - * - * @param pattern the patterns - * @return {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BannerBuilder pattern(@NotNull final Pattern... pattern) { - return pattern(Arrays.asList(pattern)); - } - - /** - * Adds new patterns on top of the existing patterns - * - * @param patterns the patterns - * @return {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BannerBuilder pattern(@NotNull final List patterns) { - final BannerMeta bannerMeta = (BannerMeta) getMeta(); - - for (final Pattern it : patterns) { - bannerMeta.addPattern(it); - } - - setMeta(bannerMeta); - return this; - } - - /** - * Sets the pattern at the specified index - * - * @param index the index - * @param color the pattern color - * @param pattern the pattern type - * @return {@link BannerBuilder} - * @throws IndexOutOfBoundsException when index is not in [0, {@link BannerMeta#numberOfPatterns()}) range - * @since 3.0.1 - */ - @NotNull - @Contract("_, _, _ -> this") - public BannerBuilder pattern(final int index, @NotNull final DyeColor color, @NotNull final PatternType pattern) { - return pattern(index, new Pattern(color, pattern)); - } - - /** - * Sets the pattern at the specified index - * - * @param index the index - * @param pattern the new pattern - * @return {@link BannerBuilder} - * @throws IndexOutOfBoundsException when index is not in [0, {@link BannerMeta#numberOfPatterns()}) range - * @since 3.0.1 - */ - @NotNull - @Contract("_, _ -> this") - public BannerBuilder pattern(final int index, @NotNull final Pattern pattern) { - final BannerMeta bannerMeta = (BannerMeta) getMeta(); - - bannerMeta.setPattern(index, pattern); - setMeta(bannerMeta); - return this; - } - - /** - * Sets the patterns used on this banner - * - * @param patterns the new list of patterns - * @return {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BannerBuilder setPatterns(@NotNull List<@NotNull Pattern> patterns) { - final BannerMeta bannerMeta = (BannerMeta) getMeta(); - - bannerMeta.setPatterns(patterns); - setMeta(bannerMeta); - return this; - } - - // TODO add shield() - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java deleted file mode 100644 index 568c773f..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java +++ /dev/null @@ -1,709 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import com.google.common.base.Preconditions; -import dev.triumphteam.gui.components.GuiAction; -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.components.util.ItemNbt; -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.components.util.VersionHelper; -import dev.triumphteam.gui.guis.GuiItem; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.persistence.PersistentDataContainer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -/** - * Contains all the common methods for the future ItemBuilders - * - * @param The ItemBuilder type so the methods can cast to the subtype - */ -@SuppressWarnings("unchecked") -public abstract class BaseItemBuilder> { - - private static final EnumSet LEATHER_ARMOR = EnumSet.of( - Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS - ); - - private static final GsonComponentSerializer GSON = GsonComponentSerializer.gson(); - private static final Field DISPLAY_NAME_FIELD; - private static final Field LORE_FIELD; - - static { - try { - final Class metaClass = VersionHelper.craftClass("inventory.CraftMetaItem"); - - DISPLAY_NAME_FIELD = metaClass.getDeclaredField("displayName"); - DISPLAY_NAME_FIELD.setAccessible(true); - - LORE_FIELD = metaClass.getDeclaredField("lore"); - LORE_FIELD.setAccessible(true); - } catch (NoSuchFieldException | ClassNotFoundException exception) { - exception.printStackTrace(); - throw new GuiException("Could not retrieve displayName nor lore field for ItemBuilder."); - } - } - - private ItemStack itemStack; - private ItemMeta meta; - - protected BaseItemBuilder(@NotNull final ItemStack itemStack) { - Preconditions.checkNotNull(itemStack, "Item can't be null!"); - - this.itemStack = itemStack; - meta = itemStack.hasItemMeta() ? itemStack.getItemMeta() : Bukkit.getItemFactory().getItemMeta(itemStack.getType()); - } - - /** - * Sets the display name of the item using {@link Component} - * - * @param name The {@link Component} name - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B name(@NotNull final Component name) { - if (meta == null) return (B) this; - - if (VersionHelper.IS_COMPONENT_LEGACY) { - meta.setDisplayName(Legacy.SERIALIZER.serialize(name)); - return (B) this; - } - - try { - DISPLAY_NAME_FIELD.set(meta, GSON.serialize(name)); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - - return (B) this; - } - - /** - * Sets the amount of items - * - * @param amount the amount of items - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B amount(final int amount) { - itemStack.setAmount(amount); - return (B) this; - } - - /** - * Set the lore lines of an item - * - * @param lore Lore lines as varargs - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B lore(@Nullable final Component @NotNull ... lore) { - return lore(Arrays.asList(lore)); - } - - /** - * Set the lore lines of an item - * - * @param lore A {@link List} with the lore lines - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B lore(@NotNull final List<@Nullable Component> lore) { - if (meta == null) return (B) this; - - if (VersionHelper.IS_COMPONENT_LEGACY) { - meta.setLore(lore.stream().filter(Objects::nonNull).map(Legacy.SERIALIZER::serialize).collect(Collectors.toList())); - return (B) this; - } - - final List jsonLore = lore.stream().filter(Objects::nonNull).map(GSON::serialize).collect(Collectors.toList()); - - try { - LORE_FIELD.set(meta, jsonLore); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - - return (B) this; - } - - /** - * Consumer for freely adding to the lore - * - * @param lore A {@link Consumer} with the {@link List} of lore {@link Component} - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B lore(@NotNull final Consumer> lore) { - if (meta == null) return (B) this; - - List components; - if (VersionHelper.IS_COMPONENT_LEGACY) { - final List stringLore = meta.getLore(); - components = (stringLore == null) ? new ArrayList<>() : stringLore.stream().map(Legacy.SERIALIZER::deserialize).collect(Collectors.toList()); - } else { - try { - final List jsonLore = (List) LORE_FIELD.get(meta); - // The field is null by default ._. - components = (jsonLore == null) ? new ArrayList<>() : jsonLore.stream().map(GSON::deserialize).collect(Collectors.toList()); - } catch (IllegalAccessException exception) { - components = new ArrayList<>(); - exception.printStackTrace(); - } - } - - lore.accept(components); - return lore(components); - } - - /** - * Enchants the {@link ItemStack} - * - * @param enchantment The {@link Enchantment} to add - * @param level The level of the {@link Enchantment} - * @param ignoreLevelRestriction If should or not ignore it - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_, _, _ -> this") - public B enchant(@NotNull final Enchantment enchantment, final int level, final boolean ignoreLevelRestriction) { - meta.addEnchant(enchantment, level, ignoreLevelRestriction); - return (B) this; - } - - /** - * Enchants the {@link ItemStack} - * - * @param enchantment The {@link Enchantment} to add - * @param level The level of the {@link Enchantment} - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_, _ -> this") - public B enchant(@NotNull final Enchantment enchantment, final int level) { - return enchant(enchantment, level, true); - } - - /** - * Enchants the {@link ItemStack} - * - * @param enchantment The {@link Enchantment} to add - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B enchant(@NotNull final Enchantment enchantment) { - return enchant(enchantment, 1, true); - } - - /** - * Enchants the {@link ItemStack} with the specified map where the value - * is the level of the key's enchantment - * - * @param enchantments Enchantments to add - * @param ignoreLevelRestriction If level restriction should be ignored - * @return {@link ItemBuilder} - * @since 3.1.2 - */ - @NotNull - @Contract("_, _ -> this") - public B enchant(@NotNull final Map enchantments, final boolean ignoreLevelRestriction) { - enchantments.forEach((enchantment, level) -> this.enchant(enchantment, level, ignoreLevelRestriction)); - return (B) this; - } - - /** - * Enchants the {@link ItemStack} with the specified map where the value - * is the level of the key's enchantment - * - * @param enchantments Enchantments to add - * @return {@link ItemBuilder} - * @since 3.1.2 - */ - @NotNull - @Contract("_ -> this") - public B enchant(@NotNull final Map enchantments) { - return enchant(enchantments, true); - } - - /** - * Disenchants a certain {@link Enchantment} from the {@link ItemStack} - * - * @param enchantment The {@link Enchantment} to remove - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B disenchant(@NotNull final Enchantment enchantment) { - itemStack.removeEnchantment(enchantment); - return (B) this; - } - - /** - * Add an {@link ItemFlag} to the item - * - * @param flags The {@link ItemFlag} to add - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B flags(@NotNull final ItemFlag... flags) { - meta.addItemFlags(flags); - return (B) this; - } - - /** - * Makes the {@link ItemStack} unbreakable - * - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract(" -> this") - public B unbreakable() { - return unbreakable(true); - } - - /** - * Sets the item as unbreakable - * - * @param unbreakable If should or not be unbreakable - * @return {@link ItemBuilder} - */ - @NotNull - @Contract("_ -> this") - public B unbreakable(boolean unbreakable) { - if (VersionHelper.IS_UNBREAKABLE_LEGACY) { - return setNbt("Unbreakable", unbreakable); - } - - meta.setUnbreakable(unbreakable); - return (B) this; - } - - /** - * Makes the {@link ItemStack} glow - * - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract(" -> this") - public B glow() { - return glow(true); - } - - /** - * Adds or removes the {@link ItemStack} glow - * - * @param glow Should the item glow - * @return {@link ItemBuilder} - */ - @NotNull - @Contract("_ -> this") - public B glow(boolean glow) { - if (glow) { - meta.addEnchant(Enchantment.LURE, 1, false); - meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); - return (B) this; - } - - for (final Enchantment enchantment : meta.getEnchants().keySet()) { - meta.removeEnchant(enchantment); - } - - return (B) this; - } - - /** - * Consumer for applying {@link PersistentDataContainer} to the item - * This method will only work on versions above 1.14 - * - * @param consumer The {@link Consumer} with the PDC - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B pdc(@NotNull final Consumer consumer) { - consumer.accept(meta.getPersistentDataContainer()); - return (B) this; - } - - /** - * Sets the custom model data of the item - * Added in 1.13 - * - * @param modelData The custom model data from the resource pack - * @return {@link ItemBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> this") - public B model(final int modelData) { - if (VersionHelper.IS_CUSTOM_MODEL_DATA) { - meta.setCustomModelData(modelData); - } - - return (B) this; - } - - /** - * Color an {@link org.bukkit.inventory.ItemStack} - * - * @param color color - * @return {@link B} - * @see org.bukkit.inventory.meta.LeatherArmorMeta#setColor(Color) - * @see org.bukkit.inventory.meta.MapMeta#setColor(Color) - * @since 3.0.3 - */ - @NotNull - @Contract("_ -> this") - public B color(@NotNull final Color color) { - if (LEATHER_ARMOR.contains(itemStack.getType())) { - final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) getMeta(); - - leatherArmorMeta.setColor(color); - setMeta(leatherArmorMeta); - } - - return (B) this; - } - - /** - * Sets NBT tag to the {@link ItemStack} - * - * @param key The NBT key - * @param value The NBT value - * @return {@link ItemBuilder} - */ - @NotNull - @Contract("_, _ -> this") - public B setNbt(@NotNull final String key, @NotNull final String value) { - itemStack.setItemMeta(meta); - itemStack = ItemNbt.setString(itemStack, key, value); - meta = itemStack.getItemMeta(); - return (B) this; - } - - /** - * Sets NBT tag to the {@link ItemStack} - * - * @param key The NBT key - * @param value The NBT value - * @return {@link ItemBuilder} - */ - @NotNull - @Contract("_, _ -> this") - public B setNbt(@NotNull final String key, final boolean value) { - itemStack.setItemMeta(meta); - itemStack = ItemNbt.setBoolean(itemStack, key, value); - meta = itemStack.getItemMeta(); - return (B) this; - } - - /** - * Removes NBT tag from the {@link ItemStack} - * - * @param key The NBT key - * @return {@link ItemBuilder} - */ - @NotNull - @Contract("_ -> this") - public B removeNbt(@NotNull final String key) { - itemStack.setItemMeta(meta); - itemStack = ItemNbt.removeTag(itemStack, key); - meta = itemStack.getItemMeta(); - return (B) this; - } - - /** - * Builds the item into {@link ItemStack} - * - * @return The fully built {@link ItemStack} - */ - @NotNull - public ItemStack build() { - itemStack.setItemMeta(meta); - return itemStack; - } - - /** - * Creates a {@link GuiItem} instead of an {@link ItemStack} - * - * @return A {@link GuiItem} with no {@link GuiAction} - */ - @NotNull - @Contract(" -> new") - public GuiItem asGuiItem() { - return new GuiItem(build()); - } - - /** - * Creates a {@link GuiItem} instead of an {@link ItemStack} - * - * @param action The {@link GuiAction} to apply to the item - * @return A {@link GuiItem} with {@link GuiAction} - */ - @NotNull - @Contract("_ -> new") - public GuiItem asGuiItem(@NotNull final GuiAction action) { - return new GuiItem(build(), action); - } - - /** - * Package private getter for extended builders - * - * @return The ItemStack - */ - @NotNull - protected ItemStack getItemStack() { - return itemStack; - } - - /** - * Package private setter for the extended builders - * - * @param itemStack The ItemStack - */ - protected void setItemStack(@NotNull final ItemStack itemStack) { - this.itemStack = itemStack; - } - - /** - * Package private getter for extended builders - * - * @return The ItemMeta - */ - @NotNull - protected ItemMeta getMeta() { - return meta; - } - - /** - * Package private setter for the extended builders - * - * @param meta The ItemMeta - */ - protected void setMeta(@NotNull final ItemMeta meta) { - this.meta = meta; - } - - // DEPRECATED, TO BE REMOVED METHODS - // TODO Remove deprecated methods - - /** - * Set display name of the item - * - * @param name the display name of the item - * @return {@link ItemBuilder} - * @deprecated In favor of {@link BaseItemBuilder#name(Component)}, will be removed in 3.0.1 - */ - @Deprecated - public B setName(@NotNull final String name) { - getMeta().setDisplayName(name); - return (B) this; - } - - /** - * Sets the amount of items - * - * @param amount the amount of items - * @return {@link ItemBuilder} - * @deprecated In favor of {@link BaseItemBuilder#amount(int)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B setAmount(final int amount) { - getItemStack().setAmount(amount); - return (B) this; - } - - /** - * Add lore lines of an item - * - * @param lore the lore lines to add - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#lore(Consumer)}, will be removed in 3.0.1 - */ - @Deprecated - public B addLore(@NotNull final String... lore) { - return addLore(Arrays.asList(lore)); - } - - /** - * Set lore lines of an item - * - * @param lore A {@link List} with the lore lines to add - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#lore(Consumer)}, will be removed in 3.0.1 - */ - @Deprecated - public B addLore(@NotNull final List lore) { - final List newLore = getMeta().hasLore() ? getMeta().getLore() : new ArrayList<>(); - - newLore.addAll(lore); - return setLore(newLore); - } - - /** - * Set the lore lines of an item - * - * @param lore the lore lines to set - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#lore(Component...)}, will be removed in 3.0.1 - */ - @Deprecated - public B setLore(@NotNull final String... lore) { - return setLore(Arrays.asList(lore)); - } - - /** - * Set the lore lines of an item - * - * @param lore A {@link List} with the lore lines - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#lore(List)}, will be removed in 3.0.1 - */ - @Deprecated - public B setLore(@NotNull final List lore) { - getMeta().setLore(lore); - return (B) this; - } - - /** - * Add enchantment to an item - * - * @param enchantment the {@link Enchantment} to add - * @param level the level of the {@link Enchantment} - * @param ignoreLevelRestriction If should or not ignore it - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#enchant(Enchantment, int, boolean)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B addEnchantment(@NotNull final Enchantment enchantment, final int level, final boolean ignoreLevelRestriction) { - getMeta().addEnchant(enchantment, level, ignoreLevelRestriction); - return (B) this; - } - - /** - * Add enchantment to an item - * - * @param enchantment the {@link Enchantment} to add - * @param level the level of the {@link Enchantment} - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#enchant(Enchantment, int)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B addEnchantment(@NotNull final Enchantment enchantment, final int level) { - return addEnchantment(enchantment, level, true); - } - - /** - * Add enchantment to an item - * - * @param enchantment the {@link Enchantment} to add - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#enchant(Enchantment)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B addEnchantment(@NotNull final Enchantment enchantment) { - return addEnchantment(enchantment, 1, true); - } - - /** - * Removes a certain {@link Enchantment} from the item - * - * @param enchantment The {@link Enchantment} to remove - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#disenchant(Enchantment)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B removeEnchantment(@NotNull final Enchantment enchantment) { - getItemStack().removeEnchantment(enchantment); - return (B) this; - } - - /** - * Add a custom {@link ItemFlag} to the item - * - * @param flags the {@link ItemFlag} to add - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#flags(ItemFlag...)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B addItemFlags(@NotNull final ItemFlag... flags) { - getMeta().addItemFlags(flags); - return (B) this; - } - - /** - * Sets the item as unbreakable - * - * @param unbreakable If should or not be unbreakable - * @return {@link ItemBuilder} - * @deprecated In favor of {@link ItemBuilder#unbreakable()}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public B setUnbreakable(boolean unbreakable) { - return unbreakable(unbreakable); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java deleted file mode 100644 index 278f8087..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java +++ /dev/null @@ -1,181 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.components.util.Legacy; -import net.kyori.adventure.text.Component; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BookMeta; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.EnumSet; -import java.util.List; - -/** - * Item builder for {@link Material#WRITTEN_BOOK} and {@link Material#WRITTEN_BOOK} only - * - * @author GabyTM https://github.com/iGabyTM - * @since 3.0.1 - */ -public class BookBuilder extends BaseItemBuilder { - - private static final EnumSet BOOKS = EnumSet.of(Material.WRITABLE_BOOK, Material.WRITTEN_BOOK); - - BookBuilder(@NotNull ItemStack itemStack) { - super(itemStack); - if (!BOOKS.contains(itemStack.getType())) { - throw new GuiException("BookBuilder requires the material to be a WRITABLE_BOOK/WRITTEN_BOOK!"); - } - } - - /** - * Sets the author of the book. Removes author when given null. - * - * @param author the author to set - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BookBuilder author(@Nullable final Component author) { - final BookMeta bookMeta = (BookMeta) getMeta(); - - if (author == null) { - bookMeta.setAuthor(null); - setMeta(bookMeta); - return this; - } - - bookMeta.setAuthor(Legacy.SERIALIZER.serialize(author)); - setMeta(bookMeta); - return this; - } - - /** - * Sets the generation of the book. Removes generation when given null. - * - * @param generation the generation to set - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BookBuilder generation(@Nullable final BookMeta.Generation generation) { - final BookMeta bookMeta = (BookMeta) getMeta(); - - bookMeta.setGeneration(generation); - setMeta(bookMeta); - return this; - } - - /** - * Adds new pages to the end of the book. Up to a maximum of 50 pages with - * 256 characters per page. - * - * @param pages list of pages - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BookBuilder page(@NotNull final Component... pages) { - return page(Arrays.asList(pages)); - } - - /** - * Adds new pages to the end of the book. Up to a maximum of 50 pages with - * 256 characters per page. - * - * @param pages list of pages - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BookBuilder page(@NotNull final List pages) { - final BookMeta bookMeta = (BookMeta) getMeta(); - - for (final Component page : pages) { - bookMeta.addPage(Legacy.SERIALIZER.serialize(page)); - } - - setMeta(bookMeta); - return this; - } - - /** - * Sets the specified page in the book. Pages of the book must be - * contiguous. - *

- * The data can be up to 256 characters in length, additional characters - * are truncated. - *

- * Pages are 1-indexed. - * - * @param page the page number to set, in range [1, {@link BookMeta#getPageCount()}] - * @param data the data to set for that page - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_, _ -> this") - public BookBuilder page(final int page, @NotNull final Component data) { - final BookMeta bookMeta = (BookMeta) getMeta(); - - bookMeta.setPage(page, Legacy.SERIALIZER.serialize(data)); - setMeta(bookMeta); - return this; - } - - /** - * Sets the title of the book. - *

- * Limited to 32 characters. Removes title when given null. - * - * @param title the title to set - * @return {@link BookBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public BookBuilder title(@Nullable Component title) { - final BookMeta bookMeta = (BookMeta) getMeta(); - - if (title == null) { - bookMeta.setTitle(null); - setMeta(bookMeta); - return this; - } - - bookMeta.setTitle(Legacy.SERIALIZER.serialize(title)); - setMeta(bookMeta); - return this; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java deleted file mode 100644 index a99aa69e..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import dev.triumphteam.gui.components.exception.GuiException; -import org.bukkit.FireworkEffect; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.FireworkEffectMeta; -import org.bukkit.inventory.meta.FireworkMeta; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.List; - -/** - * Item builder for {@link Material#FIREWORK_ROCKET} and {@link Material#FIREWORK_ROCKET} only - * - * @author GabyTM https://github.com/iGabyTM - * @since 3.0.1 - */ -public class FireworkBuilder extends BaseItemBuilder { - - private static final Material STAR = Material.FIREWORK_STAR; - private static final Material ROCKET = Material.FIREWORK_ROCKET; - - FireworkBuilder(@NotNull final ItemStack itemStack) { - super(itemStack); - if (itemStack.getType() != STAR && itemStack.getType() != ROCKET) { - throw new GuiException("FireworkBuilder requires the material to be a FIREWORK_STAR/FIREWORK_ROCKET!"); - } - } - - /** - * Add several firework effects to this firework. - * - * @param effects effects to add - * @return {@link FireworkBuilder} - * @throws IllegalArgumentException If effects is null - * @throws IllegalArgumentException If any effect is null (may be thrown after changes have occurred) - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public FireworkBuilder effect(@NotNull final FireworkEffect... effects) { - return effect(Arrays.asList(effects)); - } - - /** - * Add several firework effects to this firework. - * - * @param effects effects to add - * @return {@link FireworkBuilder} - * @throws IllegalArgumentException If effects is null - * @throws IllegalArgumentException If any effect is null (may be thrown after changes have occurred) - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public FireworkBuilder effect(@NotNull final List effects) { - if (effects.isEmpty()) { - return this; - } - - if (getItemStack().getType() == STAR) { - final FireworkEffectMeta effectMeta = (FireworkEffectMeta) getMeta(); - - effectMeta.setEffect(effects.get(0)); - setMeta(effectMeta); - return this; - } - - final FireworkMeta fireworkMeta = (FireworkMeta) getMeta(); - - fireworkMeta.addEffects(effects); - setMeta(fireworkMeta); - return this; - } - - /** - * Sets the approximate power of the firework. Each level of power is half - * a second of flight time. - * - * @param power the power of the firework, from 0-128 - * @return {@link FireworkBuilder} - * @throws IllegalArgumentException if {@literal height<0 or height>128} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public FireworkBuilder power(final int power) { - if (getItemStack().getType() == ROCKET) { - final FireworkMeta fireworkMeta = (FireworkMeta) getMeta(); - - fireworkMeta.setPower(power); - setMeta(fireworkMeta); - } - - return this; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java deleted file mode 100644 index a0bd942b..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java +++ /dev/null @@ -1,268 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import dev.triumphteam.gui.components.util.SkullUtil; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.SkullMeta; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.Field; -import java.util.UUID; - -/** - * Main ItemBuilder - */ -public class ItemBuilder extends BaseItemBuilder { - - /** - * Constructor of the item builder - * - * @param itemStack The {@link ItemStack} of the item - */ - ItemBuilder(@NotNull final ItemStack itemStack) { - super(itemStack); - } - - /** - * Main method to create {@link ItemBuilder} - * - * @param itemStack The {@link ItemStack} you want to edit - * @return A new {@link ItemBuilder} - */ - @NotNull - @Contract("_ -> new") - public static ItemBuilder from(@NotNull final ItemStack itemStack) { - return new ItemBuilder(itemStack); - } - - - /** - * Alternative method to create {@link ItemBuilder} - * - * @param material The {@link Material} you want to create an item from - * @return A new {@link ItemBuilder} - */ - @NotNull - @Contract("_ -> new") - public static ItemBuilder from(@NotNull final Material material) { - return new ItemBuilder(new ItemStack(material)); - } - - /** - * Method for creating a {@link BannerBuilder} which will have BANNER specific methods - * - * @return A new {@link BannerBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract(" -> new") - public static BannerBuilder banner() { - return new BannerBuilder(); - } - - /** - * Method for creating a {@link BannerBuilder} which will have BANNER specific methods - * - * @param itemStack An existing BANNER {@link ItemStack} - * @return A new {@link BannerBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item is not a BANNER - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> new") - public static BannerBuilder banner(@NotNull final ItemStack itemStack) { - return new BannerBuilder(itemStack); - } - - /** - * Method for creating a {@link BookBuilder} which will have {@link Material#WRITABLE_BOOK} / - * {@link Material#WRITTEN_BOOK} specific methods - * - * @param itemStack an existing {@link Material#WRITABLE_BOOK} / {@link Material#WRITTEN_BOOK} {@link ItemStack} - * @return A new {@link FireworkBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#WRITABLE_BOOK} - * or {@link Material#WRITTEN_BOOK} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> new") - public static BookBuilder book(@NotNull final ItemStack itemStack) { - return new BookBuilder(itemStack); - } - - /** - * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_ROCKET} specific methods - * - * @return A new {@link FireworkBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract(" -> new") - public static FireworkBuilder firework() { - return new FireworkBuilder(new ItemStack(Material.FIREWORK_ROCKET)); - } - - /** - * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_ROCKET} specific methods - * - * @param itemStack an existing {@link Material#FIREWORK_ROCKET} {@link ItemStack} - * @return A new {@link FireworkBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#FIREWORK_ROCKET} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> new") - public static FireworkBuilder firework(@NotNull final ItemStack itemStack) { - return new FireworkBuilder(itemStack); - } - - /** - * Method for creating a {@link MapBuilder} which will have {@link Material#MAP} specific methods - * - * @return A new {@link MapBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract(" -> new") - public static MapBuilder map() { - return new MapBuilder(); - } - - /** - * Method for creating a {@link MapBuilder} which will have @link Material#MAP} specific methods - * - * @param itemStack An existing {@link Material#MAP} {@link ItemStack} - * @return A new {@link MapBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#MAP} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> new") - public static MapBuilder map(@NotNull final ItemStack itemStack) { - return new MapBuilder(itemStack); - } - - /** - * Method for creating a {@link SkullBuilder} which will have PLAYER_HEAD specific methods - * - * @return A new {@link SkullBuilder} - */ - @NotNull - @Contract(" -> new") - public static SkullBuilder skull() { - return new SkullBuilder(); - } - - /** - * Method for creating a {@link SkullBuilder} which will have PLAYER_HEAD specific methods - * - * @param itemStack An existing PLAYER_HEAD {@link ItemStack} - * @return A new {@link SkullBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item is not a player head - */ - @NotNull - @Contract("_ -> new") - public static SkullBuilder skull(@NotNull final ItemStack itemStack) { - return new SkullBuilder(itemStack); - } - - /** - * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_STAR} specific methods - * - * @return A new {@link FireworkBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract(" -> new") - public static FireworkBuilder star() { - return new FireworkBuilder(new ItemStack(Material.FIREWORK_STAR)); - } - - /** - * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_STAR} specific methods - * - * @param itemStack an existing {@link Material#FIREWORK_STAR} {@link ItemStack} - * @return A new {@link FireworkBuilder} - * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#FIREWORK_STAR} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> new") - public static FireworkBuilder star(@NotNull final ItemStack itemStack) { - return new FireworkBuilder(itemStack); - } - - /** - * Sets the skull texture - * - * @param texture The base64 texture - * @return {@link ItemBuilder} - * @deprecated In favor of {@link SkullBuilder#texture(String)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public ItemBuilder setSkullTexture(@NotNull final String texture) { - if (!SkullUtil.isPlayerSkull(getItemStack())) return this; - - final SkullMeta skullMeta = (SkullMeta) getMeta(); - final GameProfile profile = new GameProfile(UUID.randomUUID(), null); - profile.getProperties().put("textures", new Property("textures", texture)); - final Field profileField; - - try { - profileField = skullMeta.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - profileField.set(skullMeta, profile); - } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) { - ex.printStackTrace(); - } - - setMeta(skullMeta); - return this; - } - - /** - * Sets skull owner via bukkit methods - * - * @param player {@link OfflinePlayer} to set skull of - * @return {@link ItemBuilder} - * @deprecated In favor of {@link SkullBuilder#owner(OfflinePlayer)}, nothing changed just the name, will be removed in 3.0.1 - */ - @Deprecated - public ItemBuilder setSkullOwner(@NotNull final OfflinePlayer player) { - if (!SkullUtil.isPlayerSkull(getItemStack())) return this; - - final SkullMeta skullMeta = (SkullMeta) getMeta(); - skullMeta.setOwningPlayer(player); - - setMeta(skullMeta); - return this; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/MapBuilder.java b/core/src/main/java/dev/triumphteam/gui/builder/item/MapBuilder.java deleted file mode 100644 index 082c56ac..00000000 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/MapBuilder.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; - -import dev.triumphteam.gui.components.exception.GuiException; -import org.bukkit.Color; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.MapMeta; -import org.bukkit.map.MapView; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Item builder for {@link Material#MAP} only - * - * @author GabyTM https://github.com/iGabyTM - * @since 3.0.1 - */ -public class MapBuilder extends BaseItemBuilder { - - private static final Material MAP = Material.MAP; - - MapBuilder() { - super(new ItemStack(MAP)); - } - - MapBuilder(@NotNull ItemStack itemStack) { - super(itemStack); - if (itemStack.getType() != MAP) { - throw new GuiException("MapBuilder requires the material to be a MAP!"); - } - } - - /** - * Sets the map color. A custom map color will alter the display of the map - * in an inventory slot. - * - * @param color the color to set - * @return {@link MapBuilder} - * @since 3.0.1 - */ - @NotNull - @Override - @Contract("_ -> this") - public MapBuilder color(@Nullable final Color color) { - final MapMeta mapMeta = (MapMeta) getMeta(); - - mapMeta.setColor(color); - setMeta(mapMeta); - return this; - } - - /** - * Sets the location name. A custom map color will alter the display of the - * map in an inventory slot. - * - * @param name the name to set - * @return {@link MapMeta} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public MapBuilder locationName(@Nullable final String name) { - final MapMeta mapMeta = (MapMeta) getMeta(); - - mapMeta.setLocationName(name); - setMeta(mapMeta); - return this; - } - - /** - * Sets if this map is scaling or not. - * - * @param scaling true to scale - * @return {@link MapMeta} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public MapBuilder scaling(final boolean scaling) { - final MapMeta mapMeta = (MapMeta) getMeta(); - - mapMeta.setScaling(scaling); - setMeta(mapMeta); - return this; - } - - /** - * Sets the associated map. This is used to determine what map is displayed. - * - *

- * The implementation may allow null to clear the associated map, but - * this is not required and is liable to generate a new (undefined) map when - * the item is first used. - * - * @param view the map to set - * @return {@link MapBuilder} - * @since 3.0.1 - */ - @NotNull - @Contract("_ -> this") - public MapBuilder view(@NotNull final MapView view) { - final MapMeta mapMeta = (MapMeta) getMeta(); - - mapMeta.setMapView(view); - setMeta(mapMeta); - return this; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/components/ScrollType.java b/core/src/main/java/dev/triumphteam/gui/click/ClickContext.java similarity index 82% rename from core/src/main/java/dev/triumphteam/gui/components/ScrollType.java rename to core/src/main/java/dev/triumphteam/gui/click/ClickContext.java index 6ffa597d..d0504d60 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/ScrollType.java +++ b/core/src/main/java/dev/triumphteam/gui/click/ClickContext.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,16 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.click; -import dev.triumphteam.gui.guis.ScrollingGui; - -/** - * Possible Scroll types for the {@link ScrollingGui} - */ -public enum ScrollType { - - HORIZONTAL, - VERTICAL +import org.jetbrains.annotations.NotNull; +public record ClickContext(@NotNull GuiClick guiClick, int slot) { } diff --git a/core/src/main/java/dev/triumphteam/gui/click/GuiClick.java b/core/src/main/java/dev/triumphteam/gui/click/GuiClick.java new file mode 100644 index 00000000..66db9fbc --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/GuiClick.java @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click; + +public enum GuiClick { + + LEFT, + SHIFT_LEFT, + RIGHT, + SHIFT_RIGHT, + WINDOW_BORDER_LEFT, + WINDOW_BORDER_RIGHT, + MIDDLE, + NUMBER_KEY, + DOUBLE_CLICK, + DROP, + CONTROL_DROP, + CREATIVE, + SWAP_OFFHAND, + UNKNOWN, +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/action/EmptyGuiClickAction.java b/core/src/main/java/dev/triumphteam/gui/click/action/EmptyGuiClickAction.java new file mode 100644 index 00000000..04b211ed --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/action/EmptyGuiClickAction.java @@ -0,0 +1,27 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.action; + +public final class EmptyGuiClickAction

implements GuiClickAction

{ +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java b/core/src/main/java/dev/triumphteam/gui/click/action/GuiClickAction.java similarity index 62% rename from core/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java rename to core/src/main/java/dev/triumphteam/gui/click/action/GuiClickAction.java index 2a1597cc..d071428a 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java +++ b/core/src/main/java/dev/triumphteam/gui/click/action/GuiClickAction.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,24 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.click.action; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.click.handler.CompletableFutureClickHandler; +import dev.triumphteam.gui.click.handler.SimpleClickHandler; /** - * Used to control what kind of interaction can happen inside a GUI + * Represents an action for when a player clicks on the GUI. + * This interface is empty to allow users to implement their own. + * The implementation of the click action is entirely dependent on the + * {@link ClickHandler} used. * - * @since 3.0.0 - * @author SecretX + * @param

The player type. + * @see RunnableGuiClickAction + * @see ClickHandler + * @see SimpleClickHandler + * @see CompletableFutureClickHandler */ -public enum InteractionModifier { - PREVENT_ITEM_PLACE, - PREVENT_ITEM_TAKE, - PREVENT_ITEM_SWAP, - PREVENT_ITEM_DROP, - PREVENT_OTHER_ACTIONS; - - public static final Set VALUES = Collections.unmodifiableSet(EnumSet.allOf(InteractionModifier.class)); -} +public interface GuiClickAction

{} diff --git a/core/src/main/java/dev/triumphteam/gui/click/action/RunnableGuiClickAction.java b/core/src/main/java/dev/triumphteam/gui/click/action/RunnableGuiClickAction.java new file mode 100644 index 00000000..4a2fb14f --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/action/RunnableGuiClickAction.java @@ -0,0 +1,50 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.action; + +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.handler.CompletableFutureClickHandler; +import dev.triumphteam.gui.click.handler.SimpleClickHandler; +import org.jetbrains.annotations.NotNull; + +/** + * The main/default click action type. + * A simple {@link Runnable} like functional interface. + * This is by default handled by {@link CompletableFutureClickHandler} and {@link SimpleClickHandler}. + * + * @param

The player type. + * @see SimpleClickHandler + * @see CompletableFutureClickHandler + */ +@FunctionalInterface +public interface RunnableGuiClickAction

extends GuiClickAction

{ + + /** + * Run the click action. + * + * @param player The instance of the player clicking on the GUI. + * @param context The click context. + */ + void run(final @NotNull P player, final @NotNull ClickContext context); +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/controller/ClickController.java b/core/src/main/java/dev/triumphteam/gui/click/controller/ClickController.java new file mode 100644 index 00000000..34468bf2 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/controller/ClickController.java @@ -0,0 +1,40 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.controller; + +import org.jetbrains.annotations.Nullable; + +/** + * TODO + */ +public interface ClickController { + + boolean isDone(); + + void complete(final @Nullable Throwable throwable); + + boolean completingLater(); + + void completingLater(final boolean value); +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/controller/DefaultClickController.java b/core/src/main/java/dev/triumphteam/gui/click/controller/DefaultClickController.java new file mode 100644 index 00000000..6b790eae --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/controller/DefaultClickController.java @@ -0,0 +1,70 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.controller; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; + +/** + * TODO + */ +public final class DefaultClickController implements ClickController { + + private final CompletableFuture deferred = new CompletableFuture<>(); + private boolean completingLater = false; + + public DefaultClickController(final @NotNull BiConsumer onComplete) { + deferred.whenComplete(onComplete); + } + + @Override + public boolean isDone() { + return deferred.isDone() || deferred.isCancelled(); + } + + @Override + public void complete(final @Nullable Throwable throwable) { + if (isDone()) return; + + if (throwable != null) { + deferred.completeExceptionally(throwable); + return; + } + + deferred.complete(true); + } + + @Override + public boolean completingLater() { + return completingLater; + } + + @Override + public void completingLater(final boolean value) { + this.completingLater = value; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/handler/ClickHandler.java b/core/src/main/java/dev/triumphteam/gui/click/handler/ClickHandler.java new file mode 100644 index 00000000..fe166527 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/handler/ClickHandler.java @@ -0,0 +1,57 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.handler; + +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.click.controller.ClickController; +import org.jetbrains.annotations.NotNull; + +/** + * The click handler is responsible for handling a {@link GuiClickAction}. + * This can have many different behaviors, like sync clicks, async clicks, Kotlin suspending clicks, etc. + * It is left to the user to implement other behaviors. + * + * @param

The player instance. + * @see CompletableFutureClickHandler + * @see SimpleClickHandler + * @see GuiClickAction + */ +public interface ClickHandler

{ + + /** + * Handle the click action given the {@link ClickController}. + * + * @param player The player clicking in the GUI. + * @param context The click context, containing information about the click. + * @param action The action to run. + * @param controller The controller which can be used to control how the click is processed. + */ + void handle( + final @NotNull P player, + final @NotNull ClickContext context, + final @NotNull GuiClickAction

action, + final @NotNull ClickController controller + ); +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/handler/CompletableFutureClickHandler.java b/core/src/main/java/dev/triumphteam/gui/click/handler/CompletableFutureClickHandler.java new file mode 100644 index 00000000..da227c96 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/handler/CompletableFutureClickHandler.java @@ -0,0 +1,75 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.handler; + +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.click.action.RunnableGuiClickAction; +import dev.triumphteam.gui.click.controller.ClickController; +import dev.triumphteam.gui.exception.TriumphGuiException; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * A {@link ClickHandler} implementation that runs the click action async using {@link CompletableFuture}. + * + * @param

The player type. + */ +public final class CompletableFutureClickHandler

implements ClickHandler

{ + + private final long timeout; + private final TimeUnit unit; + + public CompletableFutureClickHandler() { + this(6, TimeUnit.SECONDS); + } + + public CompletableFutureClickHandler(final long timeout, final @NotNull TimeUnit unit) { + this.timeout = timeout; + this.unit = unit; + } + + @Override + public void handle( + final @NotNull P player, + final @NotNull ClickContext context, + final @NotNull GuiClickAction

action, + final @NotNull ClickController controller + ) { + // Only accept runnable actions + if (!(action instanceof RunnableGuiClickAction

runnableAction)) { + throw new TriumphGuiException("The click action type '" + action.getClass().getName() + "' is supported by the 'CompletableFutureClickHandler'."); + } + + // Tell the controller that it'll be complete later as to block all new clicks + controller.completingLater(true); + + // Run the action async and complete click when finished + CompletableFuture.runAsync(() -> runnableAction.run(player, context)) + .orTimeout(timeout, unit) + .whenComplete((unused, throwable) -> controller.complete(throwable)); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/handler/SimpleClickHandler.java b/core/src/main/java/dev/triumphteam/gui/click/handler/SimpleClickHandler.java new file mode 100644 index 00000000..e37c959b --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/handler/SimpleClickHandler.java @@ -0,0 +1,58 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.handler; + +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.click.action.RunnableGuiClickAction; +import dev.triumphteam.gui.click.controller.ClickController; +import dev.triumphteam.gui.exception.TriumphGuiException; +import org.jetbrains.annotations.NotNull; + +/** + * The simplest click handler, all it does is run the action given. + * + * @param

The player type. + * @see ClickHandler + * @see GuiClickAction + * @see CompletableFutureClickHandler for async handler. + */ +public final class SimpleClickHandler

implements ClickHandler

{ + + @Override + public void handle( + final @NotNull P player, + final @NotNull ClickContext context, + final @NotNull GuiClickAction

action, + final @NotNull ClickController controller + ) { + // Only accept runnable actions + if (!(action instanceof RunnableGuiClickAction

runnableAction)) { + throw new TriumphGuiException("The click action type '" + action.getClass().getName() + "' is supported by the 'CompletableFutureClickHandler'."); + } + + // Run the action + runnableAction.run(player, context); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/click/processor/ClickProcessor.java b/core/src/main/java/dev/triumphteam/gui/click/processor/ClickProcessor.java new file mode 100644 index 00000000..dcd0620d --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/click/processor/ClickProcessor.java @@ -0,0 +1,152 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.click.processor; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import dev.triumphteam.gui.AbstractGuiView; +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.action.EmptyGuiClickAction; +import dev.triumphteam.gui.click.controller.DefaultClickController; +import dev.triumphteam.gui.click.handler.ClickHandler; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalTime; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * The job of the click processor is to handle everything related to a player clicking on a GUI. + * The processor will only allow one click to go through per time, if a click is taking too long, it'll block + * all other clicks from happening. + * This is to prevent unwanted interactions. + *

+ * The processor can also handle cases of players spam clicking. + * The prevention duration can be configured. + * + * @param

The player type. + * @param The item type. + */ +public final class ClickProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(ClickProcessor.class); + + private final long spamPreventionDuration; + private final Cache spamPrevention; + + private boolean isProcessing = false; + + public ClickProcessor(final long spamPreventionDuration) { + // Cache for spam prevention + this.spamPreventionDuration = spamPreventionDuration; + this.spamPrevention = CacheBuilder.newBuilder() + .expireAfterWrite(spamPreventionDuration, TimeUnit.MILLISECONDS) + .build(); + } + + /** + * Process the current click. + * Will mark the processor as busy once the click starts then may or may not free it once it's done processing. + * A click can be blocked for a much longer time depending on the {@link ClickHandler} being used. + * + * @param context The context of the click. + * @param view The current view. + */ + public void processClick(final @NotNull ClickContext context, final @NotNull AbstractGuiView view) { + + final var viewerUuid = view.viewerUuid(); + + // Check if the player can currently click + if (!canClick(viewerUuid)) { + return; + } + + final var renderedItem = view.getItem(context.slot()); + if (renderedItem == null) return; + + final var action = renderedItem.action(); + // Early exit if action is empty + if (action instanceof EmptyGuiClickAction

) { + return; + } + + // Start processing the click + + this.isProcessing = true; + + final var handler = renderedItem.clickHandler(); + // Prepare the controller with the whenComplete handler + final var clickController = new DefaultClickController((ignored, throwable) -> { + // If something went wrong with the click log, the error and stop processing + if (throwable != null) { + LOGGER.error( + "An exception occurred while processing click for '{}' on slot '{}'.", + view.viewerName(), + context.slot(), + throwable + ); + } + + this.isProcessing = false; + }); + + // The handler needs to catch so the click can finish + // The exception is passed to the controller and still logged + Exception handledException = null; + try { + handler.handle(view.viewer(), context, action, clickController); + } catch (final Exception exception) { + handledException = exception; + } + + // If not completing later makes sure to immediately complete it + if (!clickController.completingLater()) { + clickController.complete(handledException); + } + } + + /** + * Checks if the player can currently click on the GUI. + * + * @param clickerUuid The UUID of the clicker. + * @return True if the processor is not busy and not timed out from spamming. + */ + private boolean canClick(final @NotNull UUID clickerUuid) { + + // If spam prevention is disabled, ignore it + if (spamPreventionDuration != 0) { + final var spamming = spamPrevention.getIfPresent(clickerUuid); + if (spamming != null) { + return false; + } + + spamPrevention.put(clickerUuid, LocalTime.now()); + } + + // Check if the processor is not currently busy + return !isProcessing; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/GuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/GuiComponent.java new file mode 100644 index 00000000..fdbf53eb --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/GuiComponent.java @@ -0,0 +1,35 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component; + +import dev.triumphteam.gui.click.handler.ClickHandler; +import org.jetbrains.annotations.Nullable; + +public interface GuiComponent { + + @Nullable + default ClickHandler

clickHandler() { + return null; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/ReactiveGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/ReactiveGuiComponent.java new file mode 100644 index 00000000..ff30fb68 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/ReactiveGuiComponent.java @@ -0,0 +1,32 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component; + +import dev.triumphteam.gui.container.GuiContainer; +import org.jetbrains.annotations.NotNull; + +public interface ReactiveGuiComponent extends StatefulGuiComponent { + + void render(final @NotNull GuiContainer<@NotNull P, @NotNull I> container); +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/RenderedComponent.java b/core/src/main/java/dev/triumphteam/gui/component/RenderedComponent.java new file mode 100644 index 00000000..2c4c231d --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/RenderedComponent.java @@ -0,0 +1,35 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component; + +import dev.triumphteam.gui.item.RenderedGuiItem; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public record RenderedComponent( + @NotNull GuiComponent component, + @NotNull Map> renderedItems +) { +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/SimpleGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/SimpleGuiComponent.java new file mode 100644 index 00000000..ad4956a3 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/SimpleGuiComponent.java @@ -0,0 +1,52 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component; + +import dev.triumphteam.gui.component.functional.FunctionalGuiComponentRender; +import dev.triumphteam.gui.container.GuiContainer; +import dev.triumphteam.nova.State; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public final class SimpleGuiComponent implements ReactiveGuiComponent { + + private final FunctionalGuiComponentRender component; + private final List states; + + public SimpleGuiComponent(final @NotNull FunctionalGuiComponentRender component, final @NotNull List states) { + this.component = component; + this.states = states; + } + + @Override + public void render(final @NotNull GuiContainer<@NotNull P, @NotNull I> container) { + component.render(container); + } + + @Override + public @NotNull List states() { + return states; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/StatefulGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/StatefulGuiComponent.java new file mode 100644 index 00000000..84b9957d --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/StatefulGuiComponent.java @@ -0,0 +1,34 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component; + +import dev.triumphteam.nova.State; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface StatefulGuiComponent extends GuiComponent { + + @NotNull List states(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/functional/AbstractFunctionalGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/functional/AbstractFunctionalGuiComponent.java new file mode 100644 index 00000000..d1d5eb54 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/functional/AbstractFunctionalGuiComponent.java @@ -0,0 +1,88 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.functional; + +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.click.handler.CompletableFutureClickHandler; +import dev.triumphteam.gui.click.handler.SimpleClickHandler; +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.gui.state.pagination.PagerState; +import dev.triumphteam.gui.state.pagination.ScrollerState; +import dev.triumphteam.nova.holder.AbstractStateHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractFunctionalGuiComponent

extends AbstractStateHolder implements BaseFunctionalGuiComponent

{ + + private ClickHandler

clickHandler = null; + + @Override + public @NotNull PagerState rememberPager( + final int startPage, + final @NotNull List elements, + final @NotNull GuiLayout layout) { + return remember(PagerState.of(startPage, elements, layout)); + } + + @Override + public @NotNull PagerState rememberPager(final @NotNull List elements, final @NotNull GuiLayout layout) { + return remember(PagerState.of(elements, layout)); + } + + @Override + public @NotNull ScrollerState rememberScroller( + final int steps, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + return remember(ScrollerState.of(steps, elements, layout)); + } + + @Override + public void withClickHandler(final @Nullable ClickHandler

clickHandler) { + this.clickHandler = clickHandler; + } + + @Override + public void withSimpleClickHandler() { + this.clickHandler = new SimpleClickHandler<>(); + } + + @Override + public void withCompletableFutureClickHandler() { + this.clickHandler = new CompletableFutureClickHandler<>(); + } + + @Override + public void withCompletableFutureClickHandler(final long timeout, final @NotNull TimeUnit unit) { + this.clickHandler = new CompletableFutureClickHandler<>(timeout, unit); + } + + public @Nullable ClickHandler

getClickHandler() { + return clickHandler; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/functional/BaseFunctionalGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/functional/BaseFunctionalGuiComponent.java new file mode 100644 index 00000000..61051f9a --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/functional/BaseFunctionalGuiComponent.java @@ -0,0 +1,67 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.functional; + +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.gui.state.pagination.PagerState; +import dev.triumphteam.gui.state.pagination.ScrollerState; +import dev.triumphteam.nova.holder.StateHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +interface BaseFunctionalGuiComponent

extends StateHolder { + + @NotNull PagerState rememberPager( + final int startPage, + final @NotNull List elements, + final @NotNull GuiLayout layout + ); + + @NotNull PagerState rememberPager( + final @NotNull List elements, + final @NotNull GuiLayout layout + ); + + @NotNull ScrollerState rememberScroller( + final int steps, + final @NotNull List elements, + final @NotNull GuiLayout layout + ); + + /** + * TODO + * @param clickHandler + */ + void withClickHandler(final @Nullable ClickHandler

clickHandler); + + void withSimpleClickHandler(); + + void withCompletableFutureClickHandler(); + + void withCompletableFutureClickHandler(final long timeout, final @NotNull TimeUnit unit); +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponent.java new file mode 100644 index 00000000..01d2207e --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponent.java @@ -0,0 +1,48 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.functional; + +import dev.triumphteam.gui.builder.BaseGuiBuilder; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.ReactiveGuiComponent; +import dev.triumphteam.gui.container.GuiContainer; +import org.jetbrains.annotations.NotNull; + +/** + * Similar to a {@link GuiComponent} this component will take in states and render a component. + * Unlike {@link GuiComponent} it is not meant to be extended upon and is only used by the {@link BaseGuiBuilder}. + * + * @param

The player type. + * @param The item type. + */ +public interface FunctionalGuiComponent extends BaseFunctionalGuiComponent

{ + + /** + * A component render function. + * The function inside works the same as a normal {@link ReactiveGuiComponent#render(GuiContainer)} would. + * + * @param render The component render. + */ + void render(final @NotNull FunctionalGuiComponentRender<@NotNull P, @NotNull I> render); +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/GuiAction.java b/core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponentRender.java similarity index 78% rename from core/src/main/java/dev/triumphteam/gui/components/GuiAction.java rename to core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponentRender.java index 7ba5c7b2..ce68f392 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/GuiAction.java +++ b/core/src/main/java/dev/triumphteam/gui/component/functional/FunctionalGuiComponentRender.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,18 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.component.functional; -import org.bukkit.event.Event; +import dev.triumphteam.gui.container.GuiContainer; +import org.jetbrains.annotations.NotNull; @FunctionalInterface -public interface GuiAction { - - /** - * Executes the event passed to it - * - * @param event Inventory action - */ - void execute(final T event); +public interface FunctionalGuiComponentRender { + void render(final @NotNull GuiContainer<@NotNull P, @NotNull I> container); } diff --git a/core/src/main/java/dev/triumphteam/gui/component/functional/SimpleFunctionalGuiComponent.java b/core/src/main/java/dev/triumphteam/gui/component/functional/SimpleFunctionalGuiComponent.java new file mode 100644 index 00000000..84b3e907 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/functional/SimpleFunctionalGuiComponent.java @@ -0,0 +1,47 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.functional; + +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.SimpleGuiComponent; +import dev.triumphteam.gui.exception.TriumphGuiException; +import org.jetbrains.annotations.NotNull; + +public final class SimpleFunctionalGuiComponent extends AbstractFunctionalGuiComponent

implements FunctionalGuiComponent { + + private FunctionalGuiComponentRender component = null; + + @Override + public void render(final @NotNull FunctionalGuiComponentRender component) { + this.component = component; + } + + public @NotNull GuiComponent asGuiComponent() { + if (component == null) { + throw new TriumphGuiException("TODO"); + } + + return new SimpleGuiComponent<>(component, getStates()); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/renderer/DefaultGuiComponentRenderer.java b/core/src/main/java/dev/triumphteam/gui/component/renderer/DefaultGuiComponentRenderer.java new file mode 100644 index 00000000..a55f0e2b --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/renderer/DefaultGuiComponentRenderer.java @@ -0,0 +1,58 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.renderer; + +import dev.triumphteam.gui.AbstractGuiView; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.ReactiveGuiComponent; +import dev.triumphteam.gui.component.RenderedComponent; +import dev.triumphteam.gui.container.MapBackedContainer; +import org.jetbrains.annotations.NotNull; + +public final class DefaultGuiComponentRenderer implements GuiComponentRenderer { + + @Override + public void renderComponent( + final @NotNull P player, + final @NotNull GuiComponent component, + final @NotNull AbstractGuiView view + ) { + + final var componentClickHandler = component.clickHandler(); + final var container = new MapBackedContainer( + componentClickHandler == null ? view.getDefaultClickHandler() : componentClickHandler, + view.getContainerType() + ); + + if (component instanceof ReactiveGuiComponent reactiveComponent) { + reactiveComponent.render(container); + } + + final var renderedItems = container.complete(); + final var renderedComponent = new RenderedComponent<>(component, renderedItems); + + // Complete rendered back in the view + view.completeRendered(renderedComponent); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/component/renderer/GuiComponentRenderer.java b/core/src/main/java/dev/triumphteam/gui/component/renderer/GuiComponentRenderer.java new file mode 100644 index 00000000..013f4841 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/component/renderer/GuiComponentRenderer.java @@ -0,0 +1,37 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.component.renderer; + +import dev.triumphteam.gui.AbstractGuiView; +import dev.triumphteam.gui.component.GuiComponent; +import org.jetbrains.annotations.NotNull; + +public interface GuiComponentRenderer { + + void renderComponent( + final @NotNull P player, + final @NotNull GuiComponent component, + final @NotNull AbstractGuiView view + ); +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java b/core/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java deleted file mode 100644 index 31863fdd..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java +++ /dev/null @@ -1,313 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.nbt; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Objects; - -/** - * Class to set / get NBT tags from items. - * I hate this class. - */ -public final class LegacyNbt implements NbtWrapper { - - public static final String PACKAGE_NAME = Bukkit.getServer().getClass().getPackage().getName(); - public static final String NMS_VERSION = PACKAGE_NAME.substring(PACKAGE_NAME.lastIndexOf(46) + 1); - - private static Method getStringMethod; - private static Method setStringMethod; - private static Method setBooleanMethod; - private static Method hasTagMethod; - private static Method getTagMethod; - private static Method setTagMethod; - private static Method removeTagMethod; - private static Method asNMSCopyMethod; - private static Method asBukkitCopyMethod; - - private static Constructor nbtCompoundConstructor; - - static { - try { - getStringMethod = Objects.requireNonNull(getNMSClass("NBTTagCompound")).getMethod("getString", String.class); - removeTagMethod = Objects.requireNonNull(getNMSClass("NBTTagCompound")).getMethod("remove", String.class); - setStringMethod = Objects.requireNonNull(getNMSClass("NBTTagCompound")).getMethod("setString", String.class, String.class); - setBooleanMethod = Objects.requireNonNull(getNMSClass("NBTTagCompound")).getMethod("setBoolean", String.class, boolean.class); - hasTagMethod = Objects.requireNonNull(getNMSClass("ItemStack")).getMethod("hasTag"); - getTagMethod = Objects.requireNonNull(getNMSClass("ItemStack")).getMethod("getTag"); - setTagMethod = Objects.requireNonNull(getNMSClass("ItemStack")).getMethod("setTag", getNMSClass("NBTTagCompound")); - nbtCompoundConstructor = Objects.requireNonNull(getNMSClass("NBTTagCompound")).getDeclaredConstructor(); - asNMSCopyMethod = Objects.requireNonNull(getCraftItemStackClass()).getMethod("asNMSCopy", ItemStack.class); - asBukkitCopyMethod = Objects.requireNonNull(getCraftItemStackClass()).getMethod("asBukkitCopy", getNMSClass("ItemStack")); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } - - /** - * Sets an NBT tag to the an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be set. - * @param key The NBT key to use. - * @param value The tag value to set. - * @return An {@link ItemStack} that has NBT set. - */ - @Override - public ItemStack setString(@NotNull final ItemStack itemStack, final String key, final String value) { - if (itemStack.getType() == Material.AIR) return itemStack; - - Object nmsItemStack = asNMSCopy(itemStack); - Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); - - setString(itemCompound, key, value); - setTag(nmsItemStack, itemCompound); - - return asBukkitCopy(nmsItemStack); - } - - /** - * Removes a tag from an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be remove. - * @param key The NBT key to remove. - * @return An {@link ItemStack} that has the tag removed. - */ - @Override - public ItemStack removeTag(@NotNull final ItemStack itemStack, final String key) { - if (itemStack.getType() == Material.AIR) return itemStack; - - Object nmsItemStack = asNMSCopy(itemStack); - Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); - - remove(itemCompound, key); - setTag(nmsItemStack, itemCompound); - - return asBukkitCopy(nmsItemStack); - } - - /** - * Sets a boolean to the {@link ItemStack}. - * Mainly used for setting an item to be unbreakable on older versions. - * - * @param itemStack The {@link ItemStack} to set the boolean to. - * @param key The key to use. - * @param value The boolean value. - * @return An {@link ItemStack} with a boolean value set. - */ - @Override - public ItemStack setBoolean(@NotNull final ItemStack itemStack, final String key, final boolean value) { - if (itemStack.getType() == Material.AIR) return itemStack; - - Object nmsItemStack = asNMSCopy(itemStack); - Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); - - setBoolean(itemCompound, key, value); - setTag(nmsItemStack, itemCompound); - - return asBukkitCopy(nmsItemStack); - } - - /** - * Gets the NBT tag based on a given key. - * - * @param itemStack The {@link ItemStack} to get from. - * @param key The key to look for. - * @return The tag that was stored in the {@link ItemStack}. - */ - @Nullable - @Override - public String getString(@NotNull final ItemStack itemStack, final String key) { - if (itemStack.getType() == Material.AIR) return null; - - Object nmsItemStack = asNMSCopy(itemStack); - Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); - - return getString(itemCompound, key); - } - - /** - * Mimics the itemCompound#setString method. - * - * @param itemCompound The ItemCompound. - * @param key The key to add. - * @param value The value to add. - */ - private static void setString(final Object itemCompound, final String key, final String value) { - try { - setStringMethod.invoke(itemCompound, key, value); - } catch (IllegalAccessException | InvocationTargetException ignored) { - } - } - - private static void setBoolean(final Object itemCompound, final String key, final boolean value) { - try { - setBooleanMethod.invoke(itemCompound, key, value); - } catch (IllegalAccessException | InvocationTargetException ignored) { - } - } - - /** - * Mimics the itemCompound#remove method. - * - * @param itemCompound The ItemCompound. - * @param key The key to remove. - */ - private static void remove(final Object itemCompound, final String key) { - try { - removeTagMethod.invoke(itemCompound, key); - } catch (IllegalAccessException | InvocationTargetException ignored) { - } - } - - /** - * Mimics the itemCompound#getString method. - * - * @param itemCompound The ItemCompound. - * @param key The key to get from. - * @return A string with the value from the key. - */ - private static String getString(final Object itemCompound, final String key) { - try { - return (String) getStringMethod.invoke(itemCompound, key); - } catch (IllegalAccessException | InvocationTargetException e) { - return null; - } - } - - /** - * Mimics the nmsItemStack#hasTag method. - * - * @param nmsItemStack the NMS ItemStack to check from. - * @return True or false depending if it has tag or not. - */ - private static boolean hasTag(final Object nmsItemStack) { - try { - return (boolean) hasTagMethod.invoke(nmsItemStack); - } catch (IllegalAccessException | InvocationTargetException e) { - return false; - } - } - - /** - * Mimics the nmsItemStack#getTag method. - * - * @param nmsItemStack The NMS ItemStack to get from. - * @return The tag compound. - */ - public static Object getTag(final Object nmsItemStack) { - try { - return getTagMethod.invoke(nmsItemStack); - } catch (IllegalAccessException | InvocationTargetException e) { - return null; - } - } - - /** - * Mimics the nmsItemStack#setTag method. - * - * @param nmsItemStack the NMS ItemStack to set the tag to. - * @param itemCompound The item compound to set. - */ - private static void setTag(final Object nmsItemStack, final Object itemCompound) { - try { - setTagMethod.invoke(nmsItemStack, itemCompound); - } catch (IllegalAccessException | InvocationTargetException ignored) { - } - } - - /** - * Mimics the new NBTTagCompound instantiation. - * - * @return The new NBTTagCompound. - */ - private static Object newNBTTagCompound() { - try { - return nbtCompoundConstructor.newInstance(); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - return null; - } - } - - /** - * Mimics the CraftItemStack#asNMSCopy method. - * - * @param itemStack The ItemStack to make NMS copy. - * @return An NMS copy of the ItemStack. - */ - public static Object asNMSCopy(final ItemStack itemStack) { - try { - return asNMSCopyMethod.invoke(null, itemStack); - } catch (IllegalAccessException | InvocationTargetException e) { - return null; - } - } - - /** - * Mimics the CraftItemStack#asBukkitCopy method. - * - * @param nmsItemStack The NMS ItemStack to turn into {@link ItemStack}. - * @return The new {@link ItemStack}. - */ - public static ItemStack asBukkitCopy(final Object nmsItemStack) { - try { - return (ItemStack) asBukkitCopyMethod.invoke(null, nmsItemStack); - } catch (IllegalAccessException | InvocationTargetException e) { - return null; - } - } - - /** - * Gets the NMS class from class name. - * - * @return The NMS class. - */ - private static Class getNMSClass(final String className) { - try { - return Class.forName("net.minecraft.server." + NMS_VERSION + "." + className); - } catch (ClassNotFoundException e) { - return null; - } - } - - /** - * Gets the NMS craft {@link ItemStack} class from class name. - * - * @return The NMS craft {@link ItemStack} class. - */ - private static Class getCraftItemStackClass() { - try { - return Class.forName("org.bukkit.craftbukkit." + NMS_VERSION + ".inventory.CraftItemStack"); - } catch (ClassNotFoundException e) { - return null; - } - } - -} \ No newline at end of file diff --git a/core/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java b/core/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java deleted file mode 100644 index 6562ffaa..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.nbt; - -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface NbtWrapper { - - /** - * Sets an String NBT tag to the an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be set. - * @param key The NBT key to use. - * @param value The tag value to set. - * @return An {@link ItemStack} that has NBT set. - */ - ItemStack setString(@NotNull final ItemStack itemStack, final String key, final String value); - - /** - * Removes a tag from an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be remove. - * @param key The NBT key to remove. - * @return An {@link ItemStack} that has the tag removed. - */ - ItemStack removeTag(@NotNull final ItemStack itemStack, final String key); - - /** - * Sets a boolean to the {@link ItemStack}. - * Mainly used for setting an item to be unbreakable on older versions. - * - * @param itemStack The {@link ItemStack} to set the boolean to. - * @param key The key to use. - * @param value The boolean value. - * @return An {@link ItemStack} with a boolean value set. - */ - ItemStack setBoolean(@NotNull final ItemStack itemStack, final String key, final boolean value); - - /** - * Gets the NBT tag based on a given key. - * - * @param itemStack The {@link ItemStack} to get from. - * @param key The key to look for. - * @return The tag that was stored in the {@link ItemStack}. - */ - @Nullable - String getString(@NotNull final ItemStack itemStack, final String key); - -} diff --git a/core/src/main/java/dev/triumphteam/gui/components/nbt/Pdc.java b/core/src/main/java/dev/triumphteam/gui/components/nbt/Pdc.java deleted file mode 100644 index 8f75fb02..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/nbt/Pdc.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.nbt; - -import org.bukkit.NamespacedKey; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.persistence.PersistentDataType; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Wrapper for compatibility with {@link LegacyNbt}. - * This ideally wouldn't need exist, but legacy. - */ -public final class Pdc implements NbtWrapper { - - /** - * Plugin instance required for the {@link NamespacedKey}. - */ - private static final Plugin PLUGIN = JavaPlugin.getProvidingPlugin(Pdc.class); - - /** - * Sets an String NBT tag to the an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be set. - * @param key The NBT key to use. - * @param value The tag value to set. - * @return An {@link ItemStack} that has NBT set. - */ - @Override - public ItemStack setString(@NotNull final ItemStack itemStack, final String key, final String value) { - final ItemMeta meta = itemStack.getItemMeta(); - if (meta == null) return itemStack; - meta.getPersistentDataContainer().set(new NamespacedKey(PLUGIN, key), PersistentDataType.STRING, value); - itemStack.setItemMeta(meta); - return itemStack; - } - - /** - * Removes a tag from an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be remove. - * @param key The NBT key to remove. - * @return An {@link ItemStack} that has the tag removed. - */ - @Override - public ItemStack removeTag(@NotNull final ItemStack itemStack, final String key) { - final ItemMeta meta = itemStack.getItemMeta(); - if (meta == null) return itemStack; - meta.getPersistentDataContainer().remove(new NamespacedKey(PLUGIN, key)); - itemStack.setItemMeta(meta); - return itemStack; - } - - /** - * Sets a boolean to the {@link ItemStack}. - * Mainly used for setting an item to be unbreakable on older versions. - * - * @param itemStack The {@link ItemStack} to set the boolean to. - * @param key The key to use. - * @param value The boolean value. - * @return An {@link ItemStack} with a boolean value set. - */ - @Override - public ItemStack setBoolean(@NotNull final ItemStack itemStack, final String key, final boolean value) { - final ItemMeta meta = itemStack.getItemMeta(); - if (meta == null) return itemStack; - meta.getPersistentDataContainer().set(new NamespacedKey(PLUGIN, key), PersistentDataType.BYTE, value ? (byte) 1 : 0); - itemStack.setItemMeta(meta); - return itemStack; - } - - /** - * Gets the NBT tag based on a given key. - * - * @param itemStack The {@link ItemStack} to get from. - * @param key The key to look for. - * @return The tag that was stored in the {@link ItemStack}. - */ - @Nullable - @Override - public String getString(@NotNull final ItemStack itemStack, final String key) { - final ItemMeta meta = itemStack.getItemMeta(); - if (meta == null) return null; - return meta.getPersistentDataContainer().get(new NamespacedKey(PLUGIN, key), PersistentDataType.STRING); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java b/core/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java deleted file mode 100644 index b75c0090..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java +++ /dev/null @@ -1,247 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.util; - -import dev.triumphteam.gui.components.GuiType; -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.guis.BaseGui; -import dev.triumphteam.gui.guis.GuiItem; -import dev.triumphteam.gui.guis.PaginatedGui; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * TODO fix comments - */ -public final class GuiFiller { - - private final BaseGui gui; - - public GuiFiller(final BaseGui gui) { - this.gui = gui; - } - - /** - * Fills top portion of the GUI - * - * @param guiItem GuiItem - */ - public void fillTop(@NotNull final GuiItem guiItem) { - fillTop(Collections.singletonList(guiItem)); - } - - /** - * Fills top portion of the GUI with alternation - * - * @param guiItems List of GuiItems - */ - public void fillTop(@NotNull final List guiItems) { - final List items = repeatList(guiItems); - for (int i = 0; i < 9; i++) { - if (!gui.getGuiItems().containsKey(i)) gui.setItem(i, items.get(i)); - } - } - - /** - * Fills bottom portion of the GUI - * - * @param guiItem GuiItem - */ - public void fillBottom(@NotNull final GuiItem guiItem) { - fillBottom(Collections.singletonList(guiItem)); - } - - /** - * Fills bottom portion of the GUI with alternation - * - * @param guiItems GuiItem - */ - public void fillBottom(@NotNull final List guiItems) { - final int rows = gui.getRows(); - final List items = repeatList(guiItems); - for (int i = 9; i > 0; i--) { - if (gui.getGuiItems().get((rows * 9) - i) == null) { - gui.setItem((rows * 9) - i, items.get(i)); - } - } - } - - /** - * Fills the outside section of the GUI with a GuiItem - * - * @param guiItem GuiItem - */ - public void fillBorder(@NotNull final GuiItem guiItem) { - fillBorder(Collections.singletonList(guiItem)); - } - - /** - * Fill empty slots with Multiple GuiItems, goes through list and starts again - * - * @param guiItems GuiItem - */ - public void fillBorder(@NotNull final List guiItems) { - final int rows = gui.getRows(); - if (rows <= 2) return; - - final List items = repeatList(guiItems); - - for (int i = 0; i < rows * 9; i++) { - if ((i <= 8) - || (i >= (rows * 9) - 8) && (i <= (rows * 9) - 2) - || i % 9 == 0 - || i % 9 == 8) - gui.setItem(i, items.get(i)); - - } - } - - /** - * Fills rectangle from points within the GUI - * - * @param rowFrom Row point 1 - * @param colFrom Col point 1 - * @param rowTo Row point 2 - * @param colTo Col point 2 - * @param guiItem Item to fill with - * @author Harolds - */ - public void fillBetweenPoints(final int rowFrom, final int colFrom, final int rowTo, final int colTo, @NotNull final GuiItem guiItem) { - fillBetweenPoints(rowFrom, colFrom, rowTo, colTo, Collections.singletonList(guiItem)); - } - - /** - * Fills rectangle from points within the GUI - * - * @param rowFrom Row point 1 - * @param colFrom Col point 1 - * @param rowTo Row point 2 - * @param colTo Col point 2 - * @param guiItems Item to fill with - * @author Harolds - */ - public void fillBetweenPoints(final int rowFrom, final int colFrom, final int rowTo, final int colTo, @NotNull final List guiItems) { - final int minRow = Math.min(rowFrom, rowTo); - final int maxRow = Math.max(rowFrom, rowTo); - final int minCol = Math.min(colFrom, colTo); - final int maxCol = Math.max(colFrom, colTo); - - final int rows = gui.getRows(); - final List items = repeatList(guiItems); - - for (int row = 1; row <= rows; row++) { - for (int col = 1; col <= 9; col++) { - final int slot = getSlotFromRowCol(row, col); - if (!((row >= minRow && row <= maxRow) && (col >= minCol && col <= maxCol))) - continue; - - gui.setItem(slot, items.get(slot)); - } - } - } - - /** - * Sets an GuiItem to fill up the entire inventory where there is no other item - * - * @param guiItem The item to use as fill - */ - public void fill(@NotNull final GuiItem guiItem) { - fill(Collections.singletonList(guiItem)); - } - - /** - * Fill empty slots with Multiple GuiItems, goes through list and starts again - * - * @param guiItems GuiItem - */ - public void fill(@NotNull final List guiItems) { - if (gui instanceof PaginatedGui) { - throw new GuiException("Full filling a GUI is not supported in a Paginated GUI!"); - } - - final GuiType type = gui.guiType(); - - final int fill; - if (type == GuiType.CHEST) { - fill = gui.getRows() * type.getLimit(); - } else { - fill = type.getLimit(); - } - - final List items = repeatList(guiItems); - for (int i = 0; i < fill; i++) { - if (gui.getGuiItems().get(i) == null) gui.setItem(i, items.get(i)); - } - } - - /** - * Fills specified side of the GUI with a GuiItem - * - * @param guiItems GuiItem - */ - public void fillSide(@NotNull final Side side, @NotNull final List guiItems) { - switch (side) { - case LEFT: - this.fillBetweenPoints(1, 1, gui.getRows(), 1, guiItems); - case RIGHT: - this.fillBetweenPoints(1, 9, gui.getRows(), 9, guiItems); - case BOTH: - this.fillBetweenPoints(1, 1, gui.getRows(), 1, guiItems); - this.fillBetweenPoints(1, 9, gui.getRows(), 9, guiItems); - } - } - - /** - * Repeats a list of items. Allows for alternating items - * Stores references to existing objects -> Does not create new objects - * - * @param guiItems List of items to repeat - * @return New list - */ - private List repeatList(@NotNull final List guiItems) { - final List repeated = new ArrayList<>(); - Collections.nCopies(gui.getRows() * 9, guiItems).forEach(repeated::addAll); - return repeated; - } - - /** - * Gets the slot from the row and col passed - * - * @param row The row - * @param col The col - * @return The new slot - */ - private int getSlotFromRowCol(final int row, final int col) { - return (col + (row - 1) * 9) - 1; - } - - public enum Side { - LEFT, - RIGHT, - BOTH - } -} diff --git a/core/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java b/core/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java deleted file mode 100644 index 1c034d83..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.util; - -import dev.triumphteam.gui.components.nbt.LegacyNbt; -import dev.triumphteam.gui.components.nbt.NbtWrapper; -import dev.triumphteam.gui.components.nbt.Pdc; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -/** - * Ideally this wouldn't need to be an util, but because of the {@link LegacyNbt} it makes it easier. Legacy.. - * Will use the PDC wrapper if version is higher than 1.14 - */ -public final class ItemNbt { - - private static final NbtWrapper nbt = selectNbt(); - - /** - * Sets an NBT tag to the an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be set. - * @param key The NBT key to use. - * @param value The tag value to set. - * @return An {@link ItemStack} that has NBT set. - */ - public static ItemStack setString(@NotNull final ItemStack itemStack, @NotNull final String key, @NotNull final String value) { - return nbt.setString(itemStack, key, value); - } - - /** - * Gets the NBT tag based on a given key. - * - * @param itemStack The {@link ItemStack} to get from. - * @param key The key to look for. - * @return The tag that was stored in the {@link ItemStack}. - */ - public static String getString(@NotNull final ItemStack itemStack, @NotNull final String key) { - return nbt.getString(itemStack, key); - } - - /** - * Sets a boolean to the {@link ItemStack}. - * Mainly used for setting an item to be unbreakable on older versions. - * - * @param itemStack The {@link ItemStack} to set the boolean to. - * @param key The key to use. - * @param value The boolean value. - * @return An {@link ItemStack} with a boolean value set. - */ - public static ItemStack setBoolean(@NotNull final ItemStack itemStack, @NotNull final String key, final boolean value) { - return nbt.setBoolean(itemStack, key, value); - } - - /** - * Removes a tag from an {@link ItemStack}. - * - * @param itemStack The current {@link ItemStack} to be remove. - * @param key The NBT key to remove. - * @return An {@link ItemStack} that has the tag removed. - */ - public static ItemStack removeTag(@NotNull final ItemStack itemStack, @NotNull final String key) { - return nbt.removeTag(itemStack, key); - } - - /** - * Selects which {@link NbtWrapper} to use based on server version. - * - * @return A {@link NbtWrapper} implementation, {@link Pdc} if version is higher than 1.14 and {@link LegacyNbt} if not. - */ - private static NbtWrapper selectNbt() { - if (VersionHelper.IS_PDC_VERSION) return new Pdc(); - return new LegacyNbt(); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java b/core/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java deleted file mode 100644 index 27eb0607..00000000 --- a/core/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.util; - -import com.google.common.primitives.Ints; -import dev.triumphteam.gui.components.exception.GuiException; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.jetbrains.annotations.NotNull; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class for detecting server version for legacy support :( - */ -public final class VersionHelper { - - private static final String NMS_VERSION = getNmsVersion(); - - // Unbreakable change - private static final int V1_11 = 1110; - // Material and components on items change - private static final int V1_13 = 1130; - // PDC and customModelData - private static final int V1_14 = 1140; - // Paper adventure changes - private static final int V1_16_5 = 1165; - // SkullMeta#setOwningPlayer was added - private static final int V1_12_1 = 1121; - // PlayerProfile API - private static final int V1_20_1 = 1201; - - private static final int CURRENT_VERSION = getCurrentVersion(); - - private static final boolean IS_PAPER = checkPaper(); - - /** - * Checks if the version supports Components or not - * Spigot always false - */ - public static final boolean IS_COMPONENT_LEGACY = CURRENT_VERSION < V1_16_5; - - /** - * Checks if the version is lower than 1.13 due to the item changes - */ - public static final boolean IS_ITEM_LEGACY = CURRENT_VERSION < V1_13; - - /** - * Checks if the version supports the {@link org.bukkit.inventory.meta.ItemMeta#setUnbreakable(boolean)} method - */ - public static final boolean IS_UNBREAKABLE_LEGACY = CURRENT_VERSION < V1_11; - - /** - * Checks if the version supports {@link org.bukkit.persistence.PersistentDataContainer} - */ - public static final boolean IS_PDC_VERSION = CURRENT_VERSION >= V1_14; - - /** - * Checks if the version doesn't have {@link org.bukkit.inventory.meta.SkullMeta#setOwningPlayer(OfflinePlayer)} and - * {@link org.bukkit.inventory.meta.SkullMeta#setOwner(String)} should be used instead - */ - public static final boolean IS_SKULL_OWNER_LEGACY = CURRENT_VERSION < V1_12_1; - - /** - * Checks if the version has {@link org.bukkit.inventory.meta.ItemMeta#setCustomModelData(Integer)} - */ - public static final boolean IS_CUSTOM_MODEL_DATA = CURRENT_VERSION >= V1_14; - - /** - * Checks if the version has {@link org.bukkit.profile.PlayerProfile} - */ - public static final boolean IS_PLAYER_PROFILE_API = CURRENT_VERSION >= V1_20_1; - - /** - * Checks if the server is Folia - */ - public static final boolean IS_FOLIA = checkFolia(); - - /** - * Check if the server has access to the Paper API - * Taken from PaperLib - * - * @return True if on Paper server (or forks), false anything else - */ - private static boolean checkPaper() { - try { - Class.forName("com.destroystokyo.paper.PaperConfig"); - return true; - } catch (ClassNotFoundException ignored) { - return false; - } - } - - /** - * Check if the server has access to the Folia API - * Taken from Folia - * - * @return True if on Folia server (or forks), false anything else - */ - private static boolean checkFolia() { - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - return true; - } catch (ClassNotFoundException ignored) { - return false; - } - } - - /** - * Gets the current server version - * - * @return A protocol like number representing the version, for example 1.16.5 - 1165 - */ - private static int getCurrentVersion() { - // No need to cache since will only run once - final Matcher matcher = Pattern.compile("(?\\d+\\.\\d+)(?\\.\\d+)?").matcher(Bukkit.getBukkitVersion()); - - final StringBuilder stringBuilder = new StringBuilder(); - if (matcher.find()) { - stringBuilder.append(matcher.group("version").replace(".", "")); - final String patch = matcher.group("patch"); - if (patch == null) stringBuilder.append("0"); - else stringBuilder.append(patch.replace(".", "")); - } - - //noinspection UnstableApiUsage - final Integer version = Ints.tryParse(stringBuilder.toString()); - - // Should never fail - if (version == null) throw new GuiException("Could not retrieve server version!"); - - return version; - } - - private static String getNmsVersion() { - final String version = Bukkit.getServer().getClass().getPackage().getName(); - return version.substring(version.lastIndexOf('.') + 1); - } - - public static Class craftClass(@NotNull final String name) throws ClassNotFoundException { - return Class.forName("org.bukkit.craftbukkit." + NMS_VERSION + "." + name); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/container/GuiContainer.java b/core/src/main/java/dev/triumphteam/gui/container/GuiContainer.java new file mode 100644 index 00000000..1368d6fe --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/container/GuiContainer.java @@ -0,0 +1,44 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.container; + +import dev.triumphteam.gui.container.type.GuiContainerType; +import dev.triumphteam.gui.item.GuiItem; +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; + +public interface GuiContainer { + + @NotNull + GuiContainerType containerType(); + + void setItem(final int slot, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem); + + void setItem(final int row, final int column, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem); + + void setItem(final @NotNull Slot slot, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem); + + void fill(final @NotNull GuiLayout layout, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem); +} diff --git a/core/src/main/java/dev/triumphteam/gui/container/MapBackedContainer.java b/core/src/main/java/dev/triumphteam/gui/container/MapBackedContainer.java new file mode 100644 index 00000000..34867882 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/container/MapBackedContainer.java @@ -0,0 +1,84 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.container; + +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.container.type.GuiContainerType; +import dev.triumphteam.gui.item.GuiItem; +import dev.triumphteam.gui.item.RenderedGuiItem; +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class MapBackedContainer implements GuiContainer { + + private final Map> backing = new HashMap<>(100); + private final ClickHandler

clickHandler; + private final GuiContainerType containerType; + + public MapBackedContainer( + final @NotNull ClickHandler

clickHandler, + final @NotNull GuiContainerType containerType + ) { + this.clickHandler = clickHandler; + this.containerType = containerType; + } + + @Override + public @NotNull GuiContainerType containerType() { + return null; + } + + @Override + public void setItem(final int row, final int column, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem) { + setItem(containerType.mapSlot(Slot.of(row, column)), guiItem); + } + + @Override + public void setItem(final @NotNull Slot slot, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem) { + setItem(containerType.mapSlot(slot), guiItem); + } + + @Override + public void setItem(final int slot, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem) { + // TODO VALIDATE SLOT HERE TOO + // Render item + final var renderedItem = new RenderedGuiItem<>(guiItem.render(), clickHandler, guiItem.getClickAction()); + // Add rendered to backing + backing.put(slot, renderedItem); + } + + @Override + public void fill(final @NotNull GuiLayout layout, final @NotNull GuiItem<@NotNull P, @NotNull I> guiItem) { + layout.forEach(position -> setItem(position, guiItem)); + } + + public @NotNull Map> complete() { + return Collections.unmodifiableMap(backing); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/container/type/GuiContainerType.java b/core/src/main/java/dev/triumphteam/gui/container/type/GuiContainerType.java new file mode 100644 index 00000000..3ccd39a6 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/container/type/GuiContainerType.java @@ -0,0 +1,34 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.container.type; + +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; + +public interface GuiContainerType { + + int mapSlot(final @NotNull Slot slot); + + @NotNull Slot mapSlot(final int slot); +} diff --git a/core/src/main/java/dev/triumphteam/gui/exception/TriumphGuiException.java b/core/src/main/java/dev/triumphteam/gui/exception/TriumphGuiException.java new file mode 100644 index 00000000..f1343817 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/exception/TriumphGuiException.java @@ -0,0 +1,33 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.exception; + +import org.jetbrains.annotations.NotNull; + +public final class TriumphGuiException extends RuntimeException { + + public TriumphGuiException(final @NotNull String message) { + super(message); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java b/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java deleted file mode 100644 index 34ebc581..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java +++ /dev/null @@ -1,1032 +0,0 @@ -/** - * MIT License - *

- * Copyright (c) 2021 TriumphTeam - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.components.GuiAction; -import dev.triumphteam.gui.components.GuiType; -import dev.triumphteam.gui.components.InteractionModifier; -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.components.util.GuiFiller; -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.components.util.VersionHelper; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - - -/** - * Base class that every GUI extends. - * Contains all the basics for the GUI to work. - * Main and simplest implementation of this is {@link Gui}. - */ -@SuppressWarnings("unused") -public abstract class BaseGui implements InventoryHolder { - - // The plugin instance for registering the event and for the close delay. - private static final Plugin plugin = JavaPlugin.getProvidingPlugin(BaseGui.class); - - // Registering the listener class. - static { - Bukkit.getPluginManager().registerEvents(new GuiListener(), plugin); - // TODO might join these two - Bukkit.getPluginManager().registerEvents(new InteractionModifierListener(), plugin); - } - - // Gui filler. - private final GuiFiller filler = new GuiFiller(this); - // Contains all items the GUI will have. - private final Map guiItems; - // Actions for specific slots. - private final Map> slotActions; - // Interaction modifiers. - private final Set interactionModifiers; - // Main inventory. - private Inventory inventory; - // title - private String title; - private int rows = 1; - // Gui type, defaults to chest. - private GuiType guiType = GuiType.CHEST; - // Action to execute when clicking on any item. - private GuiAction defaultClickAction; - // Action to execute when clicking on the top part of the GUI only. - private GuiAction defaultTopClickAction; - // Action to execute when clicking on the player Inventory. - private GuiAction playerInventoryAction; - // Action to execute when dragging the item on the GUI. - private GuiAction dragAction; - // Action to execute when GUI closes. - private GuiAction closeGuiAction; - // Action to execute when GUI opens. - private GuiAction openGuiAction; - // Action to execute when clicked outside the GUI. - private GuiAction outsideClickAction; - - // Whether the GUI is updating. - private boolean updating; - - // Whether should run the actions from the close and open methods. - private boolean runCloseAction = true; - private boolean runOpenAction = true; - - /** - * The main constructor, using {@link String}. - * - * @param rows The amount of rows to use. - * @param title The GUI title using {@link String}. - * @param interactionModifiers Modifiers to select which interactions are allowed. - * @since 3.0.0. - */ - public BaseGui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { - int finalRows = rows; - if (!(rows >= 1 && rows <= 6)) finalRows = 1; - this.rows = finalRows; - this.interactionModifiers = safeCopyOf(interactionModifiers); - this.title = title; - int inventorySize = this.rows * 9; - this.inventory = Bukkit.createInventory(this, inventorySize, title); - this.slotActions = new LinkedHashMap<>(inventorySize); - this.guiItems = new LinkedHashMap<>(inventorySize); - } - - /** - * Alternative constructor that takes {@link GuiType} instead of rows number. - * - * @param guiType The {@link GuiType} to use. - * @param title The GUI title using {@link String}. - * @param interactionModifiers Modifiers to select which interactions are allowed. - * @since 3.0.0 - */ - public BaseGui(@NotNull final GuiType guiType, @NotNull final String title, @NotNull final Set interactionModifiers) { - this.guiType = guiType; - this.interactionModifiers = safeCopyOf(interactionModifiers); - this.title = title; - int inventorySize = guiType.getLimit(); - this.inventory = Bukkit.createInventory(this, guiType.getInventoryType(), title); - this.slotActions = new LinkedHashMap<>(inventorySize); - this.guiItems = new LinkedHashMap<>(inventorySize); - } - - /** - * Legacy constructor that takes rows and title. - * - * @param rows The amount of rows the GUI should have. - * @param title The GUI title. - * @deprecated In favor of {@link BaseGui#BaseGui(int, String, Set)}. - */ - @Deprecated - public BaseGui(final int rows, @NotNull final String title) { - int finalRows = rows; - if (!(rows >= 1 && rows <= 6)) finalRows = 1; - this.rows = finalRows; - this.interactionModifiers = EnumSet.noneOf(InteractionModifier.class); - this.title = title; - - inventory = Bukkit.createInventory(this, this.rows * 9, title); - slotActions = new LinkedHashMap<>(); - guiItems = new LinkedHashMap<>(); - } - - /** - * Alternative constructor that takes {@link GuiType} instead of rows number. - * - * @param guiType The {@link GuiType} to use. - * @param title The GUI title. - * @deprecated In favor of {@link BaseGui#BaseGui(GuiType, String, Set)}. - */ - @Deprecated - public BaseGui(@NotNull final GuiType guiType, @NotNull final String title) { - this.guiType = guiType; - this.interactionModifiers = EnumSet.noneOf(InteractionModifier.class); - this.title = title; - - inventory = Bukkit.createInventory(this, this.guiType.getInventoryType(), title); - slotActions = new LinkedHashMap<>(); - guiItems = new LinkedHashMap<>(); - } - - /** - * Copy a set into an EnumSet, required because {@link EnumSet#copyOf(EnumSet)} throws an exception if the collection passed as argument is empty. - * - * @param set The set to be copied. - * @return An EnumSet with the provided elements from the original set. - */ - @NotNull - private Set safeCopyOf(@NotNull final Set set) { - if (set.isEmpty()) return EnumSet.noneOf(InteractionModifier.class); - else return EnumSet.copyOf(set); - } - - /** - * Gets the GUI's title as string. - * - * @return The GUI's title. - */ - @NotNull - @Deprecated - public String getTitle() { - return title; - } - - /** - * Gets the GUI title as a {@link Component}. - * - * @return The GUI title {@link Component}. - */ - @NotNull - public Component title() { - return Legacy.SERIALIZER.deserialize(title); - } - - /** - * Sets the {@link GuiItem} to a specific slot on the GUI. - * - * @param slot The GUI slot. - * @param guiItem The {@link GuiItem} to add to the slot. - */ - public void setItem(final int slot, @NotNull final GuiItem guiItem) { - validateSlot(slot); - guiItems.put(slot, guiItem); - } - - /** - * Removes the given {@link GuiItem} from the GUI. - * - * @param item The item to remove. - */ - public void removeItem(@NotNull final GuiItem item) { - final Optional> entry = guiItems.entrySet() - .stream() - .filter(it -> it.getValue().equals(item)) - .findFirst(); - - entry.ifPresent(it -> { - guiItems.remove(it.getKey()); - inventory.remove(it.getValue().getItemStack()); - }); - } - - /** - * Removes the given {@link ItemStack} from the GUI. - * - * @param item The item to remove. - */ - public void removeItem(@NotNull final ItemStack item) { - final Optional> entry = guiItems.entrySet() - .stream() - .filter(it -> it.getValue().getItemStack().equals(item)) - .findFirst(); - - entry.ifPresent(it -> { - guiItems.remove(it.getKey()); - inventory.remove(item); - }); - } - - /** - * Removes the {@link GuiItem} in the specific slot. - * - * @param slot The GUI slot. - */ - public void removeItem(final int slot) { - validateSlot(slot); - guiItems.remove(slot); - inventory.setItem(slot, null); - } - - /** - * Alternative {@link #removeItem(int)} with cols and rows. - * - * @param row The row. - * @param col The column. - */ - public void removeItem(final int row, final int col) { - removeItem(getSlotFromRowCol(row, col)); - } - - /** - * Alternative {@link #setItem(int, GuiItem)} to set item that takes a {@link List} of slots instead. - * - * @param slots The slots in which the item should go. - * @param guiItem The {@link GuiItem} to add to the slots. - */ - public void setItem(@NotNull final List slots, @NotNull final GuiItem guiItem) { - for (final int slot : slots) { - setItem(slot, guiItem); - } - } - - /** - * Alternative {@link #setItem(int, GuiItem)} to set item that uses ROWS and COLUMNS instead of slots. - * - * @param row The GUI row number. - * @param col The GUI column number. - * @param guiItem The {@link GuiItem} to add to the slot. - */ - public void setItem(final int row, final int col, @NotNull final GuiItem guiItem) { - setItem(getSlotFromRowCol(row, col), guiItem); - } - - /** - * Adds {@link GuiItem}s to the GUI without specific slot. - * It'll set the item to the next empty slot available. - * - * @param items Varargs for specifying the {@link GuiItem}s. - */ - public void addItem(@NotNull final GuiItem... items) { - this.addItem(false, items); - } - - /** - * Adds {@link GuiItem}s to the GUI without specific slot. - * It'll set the item to the next empty slot available. - * - * @param items Varargs for specifying the {@link GuiItem}s. - * @param expandIfFull If true, expands the gui if it is full - * and there are more items to be added - */ - public void addItem(final boolean expandIfFull, @NotNull final GuiItem... items) { - final List notAddedItems = new ArrayList<>(); - - for (final GuiItem guiItem : items) { - for (int slot = 0; slot < rows * 9; slot++) { - if (guiItems.get(slot) != null) { - if (slot == rows * 9 - 1) { - notAddedItems.add(guiItem); - } - continue; - } - - guiItems.put(slot, guiItem); - break; - } - } - - if (!expandIfFull || this.rows >= 6 || - notAddedItems.isEmpty() || - (this.guiType != null && this.guiType != GuiType.CHEST)) { - return; - } - - this.rows++; - this.inventory = Bukkit.createInventory(this, this.rows * 9, this.title); - this.update(); - this.addItem(true, notAddedItems.toArray(new GuiItem[0])); - } - - /** - * Adds a {@link GuiAction} for when clicking on a specific slot. - * See {@link InventoryClickEvent}. - * - * @param slot The slot that will trigger the {@link GuiAction}. - * @param slotAction {@link GuiAction} to resolve when clicking on specific slots. - */ - public void addSlotAction(final int slot, @Nullable final GuiAction<@NotNull InventoryClickEvent> slotAction) { - validateSlot(slot); - slotActions.put(slot, slotAction); - } - - /** - * Alternative method for {@link #addSlotAction(int, GuiAction)} to add a {@link GuiAction} to a specific slot using ROWS and COLUMNS instead of slots. - * See {@link InventoryClickEvent}. - * - * @param row The row of the slot. - * @param col The column of the slot. - * @param slotAction {@link GuiAction} to resolve when clicking on the slot. - */ - public void addSlotAction(final int row, final int col, @Nullable final GuiAction<@NotNull InventoryClickEvent> slotAction) { - addSlotAction(getSlotFromRowCol(row, col), slotAction); - } - - /** - * Gets a specific {@link GuiItem} on the slot. - * - * @param slot The slot of the item. - * @return The {@link GuiItem} on the introduced slot or {@code null} if doesn't exist. - */ - @Nullable - public GuiItem getGuiItem(final int slot) { - return guiItems.get(slot); - } - - /** - * Checks whether or not the GUI is updating. - * - * @return Whether the GUI is updating or not. - */ - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isUpdating() { - return updating; - } - - /** - * Sets the updating status of the GUI. - * - * @param updating Sets the GUI to the updating status. - */ - public void setUpdating(final boolean updating) { - this.updating = updating; - } - - /** - * Opens the GUI for a {@link HumanEntity}. - * - * @param player The {@link HumanEntity} to open the GUI to. - */ - public void open(@NotNull final HumanEntity player) { - if (player.isSleeping()) return; - - inventory.clear(); - populateGui(); - player.openInventory(inventory); - } - - /** - * Closes the GUI with a {@code 2 tick} delay (to prevent items from being taken from the {@link Inventory}). - * - * @param player The {@link HumanEntity} to close the GUI to. - */ - public void close(@NotNull final HumanEntity player) { - close(player, true); - } - - /** - * Closes the GUI with a {@code 2 tick} delay (to prevent items from being taken from the {@link Inventory}). - * - * @param player The {@link HumanEntity} to close the GUI to. - * @param runCloseAction If should or not run the close action. - */ - public void close(@NotNull final HumanEntity player, final boolean runCloseAction) { - if (VersionHelper.IS_FOLIA) { - player.getScheduler().runDelayed(plugin, task -> { - this.runCloseAction = runCloseAction; - player.closeInventory(); - this.runCloseAction = true; - }, null, 2L); - return; - } - - Bukkit.getScheduler().runTaskLater(plugin, () -> { - this.runCloseAction = runCloseAction; - player.closeInventory(); - this.runCloseAction = true; - }, 2L); - } - - /** - * Updates the GUI for all the {@link Inventory} views. - */ - public void update() { - inventory.clear(); - populateGui(); - for (HumanEntity viewer : new ArrayList<>(inventory.getViewers())) ((Player) viewer).updateInventory(); - } - - /** - * Updates the title of the GUI. - * This method may cause LAG if used on a loop. - * - * @param title The title to set. - * @return The GUI for easier use when declaring, works like a builder. - */ - @Contract("_ -> this") - @NotNull - public BaseGui updateTitle(@NotNull final String title) { - updating = true; - - final List viewers = new ArrayList<>(inventory.getViewers()); - - inventory = Bukkit.createInventory(this, inventory.getSize(), title); - - for (final HumanEntity player : viewers) { - open(player); - } - - updating = false; - this.title = title; - return this; - } - - /** - * Updates the specified item in the GUI at runtime, without creating a new {@link GuiItem}. - * - * @param slot The slot of the item to update. - * @param itemStack The {@link ItemStack} to replace in the original one in the {@link GuiItem}. - */ - public void updateItem(final int slot, @NotNull final ItemStack itemStack) { - final GuiItem guiItem = guiItems.get(slot); - - if (guiItem == null) { - updateItem(slot, new GuiItem(itemStack)); - return; - } - - guiItem.setItemStack(itemStack); - updateItem(slot, guiItem); - } - - /** - * Alternative {@link #updateItem(int, ItemStack)} that takes ROWS and COLUMNS instead of slots. - * - * @param row The row of the slot. - * @param col The columns of the slot. - * @param itemStack The {@link ItemStack} to replace in the original one in the {@link GuiItem}. - */ - public void updateItem(final int row, final int col, @NotNull final ItemStack itemStack) { - updateItem(getSlotFromRowCol(row, col), itemStack); - } - - /** - * Alternative {@link #updateItem(int, ItemStack)} but creates a new {@link GuiItem}. - * - * @param slot The slot of the item to update. - * @param item The {@link GuiItem} to replace in the original. - */ - public void updateItem(final int slot, @NotNull final GuiItem item) { - guiItems.put(slot, item); - inventory.setItem(slot, item.getItemStack()); - } - - /** - * Alternative {@link #updateItem(int, GuiItem)} that takes ROWS and COLUMNS instead of slots. - * - * @param row The row of the slot. - * @param col The columns of the slot. - * @param item The {@link GuiItem} to replace in the original. - */ - public void updateItem(final int row, final int col, @NotNull final GuiItem item) { - updateItem(getSlotFromRowCol(row, col), item); - } - - /** - * Disable item placement inside the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui disableItemPlace() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_PLACE); - return this; - } - - /** - * Disable item retrieval inside the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui disableItemTake() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_TAKE); - return this; - } - - /** - * Disable item swap inside the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui disableItemSwap() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_SWAP); - return this; - } - - /** - * Disable item drop inside the GUI - * - * @return The BaseGui - * @since 3.0.3. - */ - @NotNull - @Contract(" -> this") - public BaseGui disableItemDrop() { - interactionModifiers.add(InteractionModifier.PREVENT_ITEM_DROP); - return this; - } - - /** - * Disable other GUI actions - * This option pretty much disables creating a clone stack of the item - * - * @return The BaseGui - * @since 3.0.4 - */ - @NotNull - @Contract(" -> this") - public BaseGui disableOtherActions() { - interactionModifiers.add(InteractionModifier.PREVENT_OTHER_ACTIONS); - return this; - } - - /** - * Disable all the modifications of the GUI, making it immutable by player interaction. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui disableAllInteractions() { - interactionModifiers.addAll(InteractionModifier.VALUES); - return this; - } - - /** - * Allows item placement inside the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui enableItemPlace() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_PLACE); - return this; - } - - /** - * Allow items to be taken from the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui enableItemTake() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_TAKE); - return this; - } - - /** - * Allows item swap inside the GUI. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui enableItemSwap() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_SWAP); - return this; - } - - /** - * Allows item drop inside the GUI - * - * @return The BaseGui - * @since 3.0.3 - */ - @NotNull - @Contract(" -> this") - public BaseGui enableItemDrop() { - interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_DROP); - return this; - } - - /** - * Enable other GUI actions - * This option pretty much enables creating a clone stack of the item - * - * @return The BaseGui - * @since 3.0.4 - */ - @NotNull - @Contract(" -> this") - public BaseGui enableOtherActions() { - interactionModifiers.remove(InteractionModifier.PREVENT_OTHER_ACTIONS); - return this; - } - - /** - * Enable all modifications of the GUI, making it completely mutable by player interaction. - * - * @return The BaseGui. - * @author SecretX. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> this") - public BaseGui enableAllInteractions() { - interactionModifiers.clear(); - return this; - } - - public boolean allInteractionsDisabled() { - return interactionModifiers.size() == InteractionModifier.VALUES.size(); - } - - /** - * Check if item placement is allowed inside this GUI. - * - * @return True if item placement is allowed for this GUI. - * @author SecretX. - * @since 3.0.0. - */ - public boolean canPlaceItems() { - return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_PLACE); - } - - /** - * Check if item retrieval is allowed inside this GUI. - * - * @return True if item retrieval is allowed inside this GUI. - * @author SecretX. - * @since 3.0.0. - */ - public boolean canTakeItems() { - return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_TAKE); - } - - /** - * Check if item swap is allowed inside this GUI. - * - * @return True if item swap is allowed for this GUI. - * @author SecretX. - * @since 3.0.0. - */ - public boolean canSwapItems() { - return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_SWAP); - } - - /** - * Check if item drop is allowed inside this GUI - * - * @return True if item drop is allowed for this GUI - * @since 3.0.3 - */ - public boolean canDropItems() { - return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_DROP); - } - - /** - * Check if any other actions are allowed in this GUI - * - * @return True if other actions are allowed - * @since 3.0.4 - */ - public boolean allowsOtherActions() { - return !interactionModifiers.contains(InteractionModifier.PREVENT_OTHER_ACTIONS); - } - - /** - * Gets the {@link GuiFiller} that it's used for filling up the GUI in specific ways. - * - * @return The {@link GuiFiller}. - */ - @NotNull - public GuiFiller getFiller() { - return filler; - } - - /** - * Gets an immutable {@link Map} with all the GUI items. - * - * @return The {@link Map} with all the {@link #guiItems}. - */ - @NotNull - public Map<@NotNull Integer, @NotNull GuiItem> getGuiItems() { - return guiItems; - } - - /** - * Gets the main {@link Inventory} of this GUI. - * - * @return Gets the {@link Inventory} from the holder. - */ - @NotNull - @Override - public Inventory getInventory() { - return inventory; - } - - /** - * Sets the new inventory of the GUI. - * - * @param inventory The new inventory. - */ - public void setInventory(@NotNull final Inventory inventory) { - this.inventory = inventory; - } - - /** - * Gets the amount of {@link #rows}. - * - * @return The {@link #rows} of the GUI. - */ - public int getRows() { - return rows; - } - - /** - * Gets the {@link GuiType} in use. - * - * @return The {@link GuiType}. - */ - @NotNull - public GuiType guiType() { - return guiType; - } - - /** - * Gets the default click resolver. - */ - @Nullable - GuiAction getDefaultClickAction() { - return defaultClickAction; - } - - /** - * Sets the {@link GuiAction} of a default click on any item. - * See {@link InventoryClickEvent}. - * - * @param defaultClickAction {@link GuiAction} to resolve when any item is clicked. - */ - public void setDefaultClickAction(@Nullable final GuiAction<@NotNull InventoryClickEvent> defaultClickAction) { - this.defaultClickAction = defaultClickAction; - } - - /** - * Gets the default top click resolver. - */ - @Nullable - GuiAction getDefaultTopClickAction() { - return defaultTopClickAction; - } - - /** - * Sets the {@link GuiAction} of a default click on any item on the top part of the GUI. - * Top inventory being for example chests etc, instead of the {@link Player} inventory. - * See {@link InventoryClickEvent}. - * - * @param defaultTopClickAction {@link GuiAction} to resolve when clicking on the top inventory. - */ - public void setDefaultTopClickAction(@Nullable final GuiAction<@NotNull InventoryClickEvent> defaultTopClickAction) { - this.defaultTopClickAction = defaultTopClickAction; - } - - /** - * Gets the player inventory action. - */ - @Nullable - GuiAction getPlayerInventoryAction() { - return playerInventoryAction; - } - - public void setPlayerInventoryAction(@Nullable final GuiAction<@NotNull InventoryClickEvent> playerInventoryAction) { - this.playerInventoryAction = playerInventoryAction; - } - - /** - * Gets the default drag resolver. - */ - @Nullable - GuiAction getDragAction() { - return dragAction; - } - - /** - * Sets the {@link GuiAction} of a default drag action. - * See {@link InventoryDragEvent}. - * - * @param dragAction {@link GuiAction} to resolve. - */ - public void setDragAction(@Nullable final GuiAction<@NotNull InventoryDragEvent> dragAction) { - this.dragAction = dragAction; - } - - /** - * Gets the close gui resolver. - */ - @Nullable - GuiAction getCloseGuiAction() { - return closeGuiAction; - } - - /** - * Sets the {@link GuiAction} to run once the inventory is closed. - * See {@link InventoryCloseEvent}. - * - * @param closeGuiAction {@link GuiAction} to resolve when the inventory is closed. - */ - public void setCloseGuiAction(@Nullable final GuiAction<@NotNull InventoryCloseEvent> closeGuiAction) { - this.closeGuiAction = closeGuiAction; - } - - /** - * Gets the open gui resolver. - */ - @Nullable - GuiAction getOpenGuiAction() { - return openGuiAction; - } - - /** - * Sets the {@link GuiAction} to run when the GUI opens. - * See {@link InventoryOpenEvent}. - * - * @param openGuiAction {@link GuiAction} to resolve when opening the inventory. - */ - public void setOpenGuiAction(@Nullable final GuiAction<@NotNull InventoryOpenEvent> openGuiAction) { - this.openGuiAction = openGuiAction; - } - - /** - * Gets the resolver for the outside click. - */ - @Nullable - GuiAction getOutsideClickAction() { - return outsideClickAction; - } - - /** - * Sets the {@link GuiAction} to run when clicking on the outside of the inventory. - * See {@link InventoryClickEvent}. - * - * @param outsideClickAction {@link GuiAction} to resolve when clicking outside of the inventory. - */ - public void setOutsideClickAction(@Nullable final GuiAction<@NotNull InventoryClickEvent> outsideClickAction) { - this.outsideClickAction = outsideClickAction; - } - - /** - * Gets the action for the specified slot. - * - * @param slot The slot clicked. - */ - @Nullable - GuiAction getSlotAction(final int slot) { - return slotActions.get(slot); - } - - /** - * Populates the GUI with it's items. - */ - void populateGui() { - for (final Map.Entry entry : guiItems.entrySet()) { - inventory.setItem(entry.getKey(), entry.getValue().getItemStack()); - } - } - - boolean shouldRunCloseAction() { - return runCloseAction; - } - - boolean shouldRunOpenAction() { - return runOpenAction; - } - - /** - * Gets the slot from the row and column passed. - * - * @param row The row. - * @param col The column. - * @return The slot needed. - */ - int getSlotFromRowCol(final int row, final int col) { - return (col + (row - 1) * 9) - 1; - } - - /* - TODO fix this part, find a better solution for using Paper - protected Inventory createRowedInventory(@NotNull final Component title) { - if (VersionHelper.IS_COMPONENT_LEGACY) { - return Bukkit.createInventory(this, this.rows * 9, Legacy.SERIALIZER.serialize(title)); - } - - return inventory = Bukkit.createInventory(this, this.rows * 9, title); - } - - private Inventory createTypedInventory(@NotNull final Component title) { - final InventoryType inventoryType = guiType.getInventoryType(); - if (VersionHelper.IS_COMPONENT_LEGACY) { - return Bukkit.createInventory(this, inventoryType, Legacy.SERIALIZER.serialize(title)); - } - - return Bukkit.createInventory(this, inventoryType, title); - }*/ - - /** - * Checks if the slot introduces is a valid slot. - * - * @param slot The slot to check. - */ - private void validateSlot(final int slot) { - final int limit = guiType.getLimit(); - - if (guiType == GuiType.CHEST) { - if (slot < 0 || slot >= rows * limit) throwInvalidSlot(slot); - return; - } - - if (slot < 0 || slot > limit) throwInvalidSlot(slot); - } - - /** - * Throws an exception if the slot is invalid. - * - * @param slot The specific slot to display in the error message. - */ - private void throwInvalidSlot(final int slot) { - if (guiType == GuiType.CHEST) { - throw new GuiException("Slot " + slot + " is not valid for the gui type - " + guiType.name() + " and rows - " + rows + "!"); - } - - throw new GuiException("Slot " + slot + " is not valid for the gui type - " + guiType.name() + "!"); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/Gui.java b/core/src/main/java/dev/triumphteam/gui/guis/Gui.java deleted file mode 100644 index 68135533..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/Gui.java +++ /dev/null @@ -1,178 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.builder.gui.PaginatedBuilder; -import dev.triumphteam.gui.builder.gui.ScrollingBuilder; -import dev.triumphteam.gui.builder.gui.SimpleBuilder; -import dev.triumphteam.gui.builder.gui.StorageBuilder; -import dev.triumphteam.gui.components.GuiType; -import dev.triumphteam.gui.components.InteractionModifier; -import dev.triumphteam.gui.components.ScrollType; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Set; - -/** - * Standard GUI implementation of {@link BaseGui} - */ -public class Gui extends BaseGui { - - /** - * Main constructor for the GUI - * - * @param rows The amount of rows the GUI should have - * @param title The GUI's title using {@link String} - * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use - * @author SecretX - * @since 3.0.3 - */ - public Gui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { - super(rows, title, interactionModifiers); - } - - /** - * Alternative constructor that takes both a {@link GuiType} and a set of {@link InteractionModifier} - * - * @param guiType The {@link GuiType} to be used - * @param title The GUI's title using {@link String} - * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use - * @author SecretX - * @since 3.0.3 - */ - public Gui(@NotNull final GuiType guiType, @NotNull final String title, @NotNull final Set interactionModifiers) { - super(guiType, title, interactionModifiers); - } - - /** - * Old main constructor for the GUI - * - * @param rows The amount of rows the GUI should have - * @param title The GUI's title - * @deprecated In favor of {@link Gui#Gui(int, String, Set)} - */ - @Deprecated - public Gui(final int rows, @NotNull final String title) { - super(rows, title); - } - - /** - * Alternative constructor that defaults to 1 row - * - * @param title The GUI's title - * @deprecated In favor of {@link Gui#Gui(int, String, Set)} - */ - @Deprecated - public Gui(@NotNull final String title) { - super(1, title); - } - - /** - * Main constructor that takes a {@link GuiType} instead of rows - * - * @param guiType The {@link GuiType} to be used - * @param title The GUI's title - * @deprecated In favor of {@link Gui#Gui(GuiType, String, Set)} - */ - @Deprecated - public Gui(@NotNull final GuiType guiType, @NotNull final String title) { - super(guiType, title); - } - - /** - * Creates a {@link SimpleBuilder} to build a {@link dev.triumphteam.gui.guis.Gui} - * - * @param type The {@link GuiType} to be used - * @return A {@link SimpleBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> new") - public static SimpleBuilder gui(@NotNull final GuiType type) { - return new SimpleBuilder(type); - } - - /** - * Creates a {@link SimpleBuilder} with CHEST as the {@link GuiType} - * - * @return A CHEST {@link SimpleBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract(" -> new") - public static SimpleBuilder gui() { - return gui(GuiType.CHEST); - } - - /** - * Creates a {@link StorageBuilder}. - * - * @return A CHEST {@link StorageBuilder}. - * @since 3.0.0. - */ - @NotNull - @Contract(" -> new") - public static StorageBuilder storage() { - return new StorageBuilder(); - } - - /** - * Creates a {@link PaginatedBuilder} to build a {@link dev.triumphteam.gui.guis.PaginatedGui} - * - * @return A {@link PaginatedBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract(" -> new") - public static PaginatedBuilder paginated() { - return new PaginatedBuilder(); - } - - /** - * Creates a {@link ScrollingBuilder} to build a {@link dev.triumphteam.gui.guis.ScrollingGui} - * - * @param scrollType The {@link ScrollType} to be used by the GUI - * @return A {@link ScrollingBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract("_ -> new") - public static ScrollingBuilder scrolling(@NotNull final ScrollType scrollType) { - return new ScrollingBuilder(scrollType); - } - - /** - * Creates a {@link ScrollingBuilder} with VERTICAL as the {@link ScrollType} - * - * @return A vertical {@link SimpleBuilder} - * @since 3.0.0 - */ - @NotNull - @Contract(" -> new") - public static ScrollingBuilder scrolling() { - return scrolling(ScrollType.VERTICAL); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/GuiItem.java b/core/src/main/java/dev/triumphteam/gui/guis/GuiItem.java deleted file mode 100644 index cf700384..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/GuiItem.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import com.google.common.base.Preconditions; -import dev.triumphteam.gui.components.GuiAction; -import dev.triumphteam.gui.components.util.ItemNbt; -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.UUID; - -/** - * GuiItem represents the {@link ItemStack} on the {@link Inventory} - */ -@SuppressWarnings("unused") -public class GuiItem { - - // Action to do when clicking on the item - private GuiAction action; - - // The ItemStack of the GuiItem - private ItemStack itemStack; - - // Random UUID to identify the item when clicking - private final UUID uuid = UUID.randomUUID(); - - /** - * Main constructor of the GuiItem - * - * @param itemStack The {@link ItemStack} to be used - * @param action The {@link GuiAction} to run when clicking on the Item - */ - public GuiItem(@NotNull final ItemStack itemStack, @Nullable final GuiAction<@NotNull InventoryClickEvent> action) { - Preconditions.checkNotNull(itemStack, "The ItemStack for the GUI Item cannot be null!"); - - this.action = action; - - // Sets the UUID to an NBT tag to be identifiable later - this.itemStack = ItemNbt.setString(itemStack.clone(), "mf-gui", uuid.toString()); - } - - /** - * Secondary constructor with no action - * - * @param itemStack The ItemStack to be used - */ - public GuiItem(@NotNull final ItemStack itemStack) { - this(itemStack, null); - } - - /** - * Alternate constructor that takes {@link Material} instead of an {@link ItemStack} but without a {@link GuiAction} - * - * @param material The {@link Material} to be used when invoking class - */ - public GuiItem(@NotNull final Material material) { - this(new ItemStack(material), null); - } - - /** - * Alternate constructor that takes {@link Material} instead of an {@link ItemStack} - * - * @param material The {@code Material} to be used when invoking class - * @param action The {@link GuiAction} should be passed on {@link InventoryClickEvent} - */ - public GuiItem(@NotNull final Material material, @Nullable final GuiAction<@NotNull InventoryClickEvent> action) { - this(new ItemStack(material), action); - } - - /** - * Replaces the {@link ItemStack} of the GUI Item - * - * @param itemStack The new {@link ItemStack} - */ - public void setItemStack(@NotNull final ItemStack itemStack) { - Preconditions.checkNotNull(itemStack, "The ItemStack for the GUI Item cannot be null!"); - this.itemStack = ItemNbt.setString(itemStack.clone(), "mf-gui", uuid.toString()); - } - - /** - * Replaces the {@link GuiAction} of the current GUI Item - * - * @param action The new {@link GuiAction} to set - */ - public void setAction(@Nullable final GuiAction<@NotNull InventoryClickEvent> action) { - this.action = action; - } - - /** - * Gets the GuiItem's {@link ItemStack} - * - * @return The {@link ItemStack} - */ - @NotNull - public ItemStack getItemStack() { - return itemStack; - } - - /** - * Gets the random {@link UUID} that was generated when the GuiItem was made - */ - @NotNull - UUID getUuid() { - return uuid; - } - - /** - * Gets the {@link GuiAction} to do when the player clicks on it - */ - @Nullable - GuiAction getAction() { - return action; - } -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java b/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java deleted file mode 100644 index ce4d7428..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.components.GuiAction; -import dev.triumphteam.gui.components.util.ItemNbt; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -public final class GuiListener implements Listener { - - /** - * Handles what happens when a player clicks on the GUI - * - * @param event The InventoryClickEvent - */ - @EventHandler - public void onGuiClick(final InventoryClickEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // Gui - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - // Executes the outside click action - final GuiAction outsideClickAction = gui.getOutsideClickAction(); - if (outsideClickAction != null && event.getClickedInventory() == null) { - outsideClickAction.execute(event); - return; - } - - if (event.getClickedInventory() == null) return; - - // Default click action and checks weather or not there is a default action and executes it - final GuiAction defaultTopClick = gui.getDefaultTopClickAction(); - if (defaultTopClick != null && event.getClickedInventory().getType() != InventoryType.PLAYER) { - defaultTopClick.execute(event); - } - - // Default click action and checks weather or not there is a default action and executes it - final GuiAction playerInventoryClick = gui.getPlayerInventoryAction(); - if (playerInventoryClick != null && event.getClickedInventory().getType() == InventoryType.PLAYER) { - playerInventoryClick.execute(event); - } - - // Default click action and checks weather or not there is a default action and executes it - final GuiAction defaultClick = gui.getDefaultClickAction(); - if (defaultClick != null) defaultClick.execute(event); - - // Slot action and checks weather or not there is a slot action and executes it - final GuiAction slotAction = gui.getSlotAction(event.getSlot()); - if (slotAction != null && event.getClickedInventory().getType() != InventoryType.PLAYER) { - slotAction.execute(event); - } - - GuiItem guiItem; - - // Checks whether it's a paginated gui or not - if (gui instanceof PaginatedGui) { - final PaginatedGui paginatedGui = (PaginatedGui) gui; - - // Gets the gui item from the added items or the page items - guiItem = paginatedGui.getGuiItem(event.getSlot()); - if (guiItem == null) guiItem = paginatedGui.getPageItem(event.getSlot()); - - } else { - // The clicked GUI Item - guiItem = gui.getGuiItem(event.getSlot()); - } - - if (!isGuiItem(event.getCurrentItem(), guiItem)) return; - - // Executes the action of the item - final GuiAction itemAction = guiItem.getAction(); - if (itemAction != null) itemAction.execute(event); - } - - /** - * Handles what happens when a player clicks on the GUI - * - * @param event The InventoryClickEvent - */ - @EventHandler - public void onGuiDrag(final InventoryDragEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // Gui - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - // Default click action and checks weather or not there is a default action and executes it - final GuiAction dragAction = gui.getDragAction(); - if (dragAction != null) dragAction.execute(event); - } - - /** - * Handles what happens when the GUI is closed - * - * @param event The InventoryCloseEvent - */ - @EventHandler - public void onGuiClose(final InventoryCloseEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // GUI - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - // The GUI action for closing - final GuiAction closeAction = gui.getCloseGuiAction(); - - // Checks if there is or not an action set and executes it - if (closeAction != null && !gui.isUpdating() && gui.shouldRunCloseAction()) closeAction.execute(event); - } - - /** - * Handles what happens when the GUI is opened - * - * @param event The InventoryOpenEvent - */ - @EventHandler - public void onGuiOpen(final InventoryOpenEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // GUI - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - // The GUI action for opening - final GuiAction openAction = gui.getOpenGuiAction(); - - // Checks if there is or not an action set and executes it - if (openAction != null && !gui.isUpdating()) openAction.execute(event); - } - - /** - * Checks if the item is or not a GUI item - * - * @param currentItem The current item clicked - * @param guiItem The GUI item in the slot - * @return Whether it is or not a GUI item - */ - private boolean isGuiItem(@Nullable final ItemStack currentItem, @Nullable final GuiItem guiItem) { - if (currentItem == null || guiItem == null) return false; - // Checks whether the Item is truly a GUI Item - final String nbt = ItemNbt.getString(currentItem, "mf-gui"); - if (nbt == null) return false; - return nbt.equals(guiItem.getUuid().toString()); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java b/core/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java deleted file mode 100644 index e40d8a5a..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java +++ /dev/null @@ -1,255 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import com.google.common.base.Preconditions; -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; - -/** - * Listener that apply default GUI {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier}s to all GUIs - * - * @author SecretX - * @since 3.0.0 - */ -public final class InteractionModifierListener implements Listener { - - /** - * Handles any click on GUIs, applying all {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier} as required - * - * @param event The InventoryClickEvent - * @author SecretX - * @since 3.0.0 - */ - @EventHandler - public void onGuiClick(final InventoryClickEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // Gui - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - if (gui.allInteractionsDisabled()) { - event.setCancelled(true); - event.setResult(Event.Result.DENY); - return; - } - - // if player is trying to do a disabled action, cancel it - if ((!gui.canPlaceItems() && isPlaceItemEvent(event)) || (!gui.canTakeItems() && isTakeItemEvent(event)) || (!gui.canSwapItems() && isSwapItemEvent(event)) || (!gui.canDropItems() && isDropItemEvent(event)) || (!gui.allowsOtherActions() && isOtherEvent(event))) { - event.setCancelled(true); - event.setResult(Event.Result.DENY); - } - } - - /** - * Handles any item drag on GUIs, applying all {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier} as required - * - * @param event The InventoryDragEvent - * @author SecretX - * @since 3.0.0 - */ - @EventHandler - public void onGuiDrag(final InventoryDragEvent event) { - if (!(event.getInventory().getHolder() instanceof BaseGui)) return; - - // Gui - final BaseGui gui = (BaseGui) event.getInventory().getHolder(); - - if (gui.allInteractionsDisabled()) { - event.setCancelled(true); - event.setResult(Event.Result.DENY); - return; - } - - // if players are allowed to place items on the GUI, or player is not dragging on GUI, return - if (gui.canPlaceItems() || !isDraggingOnGui(event)) return; - - // cancel the interaction - event.setCancelled(true); - event.setResult(Event.Result.DENY); - } - - /** - * Checks if what is happening on the {@link InventoryClickEvent} is take an item from the GUI - * - * @param event The InventoryClickEvent - * @return True if the {@link InventoryClickEvent} is for taking an item from the GUI - * @author SecretX - * @since 3.0.0 - */ - private boolean isTakeItemEvent(final InventoryClickEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - - final Inventory inventory = event.getInventory(); - final Inventory clickedInventory = event.getClickedInventory(); - final InventoryAction action = event.getAction(); - - // magic logic, simplified version of https://paste.helpch.at/tizivomeco.cpp - if (clickedInventory != null && clickedInventory.getType() == InventoryType.PLAYER || inventory.getType() == InventoryType.PLAYER) { - return false; - } - - return action == InventoryAction.MOVE_TO_OTHER_INVENTORY || isTakeAction(action); - } - - /** - * Checks if what is happening on the {@link InventoryClickEvent} is place an item on the GUI - * - * @param event The InventoryClickEvent - * @return True if the {@link InventoryClickEvent} is for placing an item from the GUI - * @author SecretX - * @since 3.0.0 - */ - private boolean isPlaceItemEvent(final InventoryClickEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - - final Inventory inventory = event.getInventory(); - final Inventory clickedInventory = event.getClickedInventory(); - final InventoryAction action = event.getAction(); - - // shift click on item in player inventory - if (action == InventoryAction.MOVE_TO_OTHER_INVENTORY - && clickedInventory != null && clickedInventory.getType() == InventoryType.PLAYER - && inventory.getType() != clickedInventory.getType()) { - return true; - } - - // normal click on gui empty slot with item on cursor - return isPlaceAction(action) - && (clickedInventory == null || clickedInventory.getType() != InventoryType.PLAYER) - && inventory.getType() != InventoryType.PLAYER; - } - - /** - * Checks if what is happening on the {@link InventoryClickEvent} is swap any item with an item from the GUI - * - * @param event The InventoryClickEvent - * @return True if the {@link InventoryClickEvent} is for swapping any item with an item from the GUI - * @author SecretX - * @since 3.0.0 - */ - private boolean isSwapItemEvent(final InventoryClickEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - - final Inventory inventory = event.getInventory(); - final Inventory clickedInventory = event.getClickedInventory(); - final InventoryAction action = event.getAction(); - - return isSwapAction(action) - && (clickedInventory == null || clickedInventory.getType() != InventoryType.PLAYER) - && inventory.getType() != InventoryType.PLAYER; - } - - private boolean isDropItemEvent(final InventoryClickEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - - final Inventory inventory = event.getInventory(); - final Inventory clickedInventory = event.getClickedInventory(); - final InventoryAction action = event.getAction(); - - return isDropAction(action) - && (clickedInventory != null || inventory.getType() != InventoryType.PLAYER); - } - - private boolean isOtherEvent(final InventoryClickEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - - final Inventory inventory = event.getInventory(); - final Inventory clickedInventory = event.getClickedInventory(); - final InventoryAction action = event.getAction(); - - return isOtherAction(action) - && (clickedInventory != null || inventory.getType() != InventoryType.PLAYER); - } - - /** - * Checks if any item is being dragged on the GUI - * - * @param event The InventoryDragEvent - * @return True if the {@link InventoryDragEvent} is for dragging an item inside the GUI - * @author SecretX - * @since 3.0.0 - */ - private boolean isDraggingOnGui(final InventoryDragEvent event) { - Preconditions.checkNotNull(event, "event cannot be null"); - final int topSlots = event.getView().getTopInventory().getSize(); - // is dragging on any top inventory slot - return event.getRawSlots().stream().anyMatch(slot -> slot < topSlots); - } - - private boolean isTakeAction(final InventoryAction action) { - Preconditions.checkNotNull(action, "action cannot be null"); - return ITEM_TAKE_ACTIONS.contains(action); - } - - private boolean isPlaceAction(final InventoryAction action) { - Preconditions.checkNotNull(action, "action cannot be null"); - return ITEM_PLACE_ACTIONS.contains(action); - } - - private boolean isSwapAction(final InventoryAction action) { - Preconditions.checkNotNull(action, "action cannot be null"); - return ITEM_SWAP_ACTIONS.contains(action); - } - - private boolean isDropAction(final InventoryAction action) { - Preconditions.checkNotNull(action, "action cannot be null"); - return ITEM_DROP_ACTIONS.contains(action); - } - - private boolean isOtherAction(final InventoryAction action) { - Preconditions.checkNotNull(action, "action cannot be null"); - return action == InventoryAction.CLONE_STACK || action == InventoryAction.UNKNOWN; - } - - /** - * Holds all the actions that should be considered "take" actions - */ - private static final Set ITEM_TAKE_ACTIONS = Collections.unmodifiableSet(EnumSet.of(InventoryAction.PICKUP_ONE, InventoryAction.PICKUP_SOME, InventoryAction.PICKUP_HALF, InventoryAction.PICKUP_ALL, InventoryAction.COLLECT_TO_CURSOR, InventoryAction.HOTBAR_SWAP, InventoryAction.MOVE_TO_OTHER_INVENTORY)); - - /** - * Holds all the actions that should be considered "place" actions - */ - private static final Set ITEM_PLACE_ACTIONS = Collections.unmodifiableSet(EnumSet.of(InventoryAction.PLACE_ONE, InventoryAction.PLACE_SOME, InventoryAction.PLACE_ALL)); - - /** - * Holds all actions relating to swapping items - */ - private static final Set ITEM_SWAP_ACTIONS = Collections.unmodifiableSet(EnumSet.of(InventoryAction.HOTBAR_SWAP, InventoryAction.SWAP_WITH_CURSOR, InventoryAction.HOTBAR_MOVE_AND_READD)); - - /** - * Holds all actions relating to dropping items - */ - private static final Set ITEM_DROP_ACTIONS = Collections.unmodifiableSet(EnumSet.of(InventoryAction.DROP_ONE_SLOT, InventoryAction.DROP_ALL_SLOT, InventoryAction.DROP_ONE_CURSOR, InventoryAction.DROP_ALL_CURSOR)); -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java b/core/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java deleted file mode 100644 index 6ea2c0a9..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java +++ /dev/null @@ -1,498 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.components.InteractionModifier; -import org.bukkit.Bukkit; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -/** - * GUI that allows you to have multiple pages - */ -@SuppressWarnings("unused") -public class PaginatedGui extends BaseGui { - - // List with all the page items - private final List pageItems = new ArrayList<>(); - // Saves the current page items and it's slot - private final Map currentPage; - - private int pageSize; - private int pageNum = 1; - - /** - * Main constructor to provide a way to create PaginatedGui - * - * @param rows The amount of rows the GUI should have - * @param pageSize The page size. - * @param title The GUI's title using {@link String} - * @param interactionModifiers A set containing what {@link InteractionModifier} this GUI should have - * @author SecretX - * @since 3.0.3 - */ - public PaginatedGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final Set interactionModifiers) { - super(rows, title, interactionModifiers); - this.pageSize = pageSize; - int inventorySize = rows * 9; - this.currentPage = new LinkedHashMap<>(inventorySize); - } - - /** - * Old main constructor of the PaginatedGui - * - * @param rows The rows the GUI should have - * @param pageSize The pageSize - * @param title The GUI's title - * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} - */ - @Deprecated - public PaginatedGui(final int rows, final int pageSize, @NotNull final String title) { - super(rows, title); - this.pageSize = pageSize; - int inventorySize = rows * 9; - this.currentPage = new LinkedHashMap<>(inventorySize); - } - - /** - * Alternative constructor that doesn't require the {@link #pageSize} to be defined - * - * @param rows The rows the GUI should have - * @param title The GUI's title - * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} - */ - @Deprecated - public PaginatedGui(final int rows, @NotNull final String title) { - this(rows, 0, title); - } - - /** - * Alternative constructor that only requires title - * - * @param title The GUI's title - * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} - */ - @Deprecated - public PaginatedGui(@NotNull final String title) { - this(2, title); - } - - /** - * Sets the page size - * - * @param pageSize The new page size - * @return The GUI for easier use when declaring, works like a builder - */ - public BaseGui setPageSize(final int pageSize) { - this.pageSize = pageSize; - return this; - } - - /** - * Adds an {@link GuiItem} to the next available slot in the page area - * - * @param item The {@link GuiItem} to add to the page - */ - public void addItem(@NotNull final GuiItem item) { - pageItems.add(item); - } - - /** - * Overridden {@link BaseGui#addItem(GuiItem...)} to add the items to the page instead - * - * @param items Varargs for specifying the {@link GuiItem}s - */ - @Override - public void addItem(@NotNull final GuiItem... items) { - pageItems.addAll(Arrays.asList(items)); - } - - /** - * Overridden {@link BaseGui#update()} to use the paginated open - */ - @Override - public void update() { - getInventory().clear(); - populateGui(); - - updatePage(); - } - - /** - * Updates the page {@link GuiItem} on the slot in the page - * Can get the slot from {@link InventoryClickEvent#getSlot()} - * - * @param slot The slot of the item to update - * @param itemStack The new {@link ItemStack} - */ - public void updatePageItem(final int slot, @NotNull final ItemStack itemStack) { - if (!currentPage.containsKey(slot)) return; - final GuiItem guiItem = currentPage.get(slot); - guiItem.setItemStack(itemStack); - getInventory().setItem(slot, guiItem.getItemStack()); - } - - /** - * Alternative {@link #updatePageItem(int, ItemStack)} that uses ROWS and COLUMNS instead - * - * @param row The row of the slot - * @param col The columns of the slot - * @param itemStack The new {@link ItemStack} - */ - public void updatePageItem(final int row, final int col, @NotNull final ItemStack itemStack) { - updateItem(getSlotFromRowCol(row, col), itemStack); - } - - /** - * Alternative {@link #updatePageItem(int, ItemStack)} that uses {@link GuiItem} instead - * - * @param slot The slot of the item to update - * @param item The new ItemStack - */ - public void updatePageItem(final int slot, @NotNull final GuiItem item) { - if (!currentPage.containsKey(slot)) return; - // Gets the old item and its index on the main items list - final GuiItem oldItem = currentPage.get(slot); - final int index = pageItems.indexOf(currentPage.get(slot)); - - // Updates both lists and inventory - currentPage.put(slot, item); - pageItems.set(index, item); - getInventory().setItem(slot, item.getItemStack()); - } - - /** - * Alternative {@link #updatePageItem(int, GuiItem)} that uses ROWS and COLUMNS instead - * - * @param row The row of the slot - * @param col The columns of the slot - * @param item The new {@link GuiItem} - */ - public void updatePageItem(final int row, final int col, @NotNull final GuiItem item) { - updateItem(getSlotFromRowCol(row, col), item); - } - - /** - * Removes a given {@link GuiItem} from the page. - * - * @param item The {@link GuiItem} to remove. - */ - public void removePageItem(@NotNull final GuiItem item) { - pageItems.remove(item); - updatePage(); - } - - /** - * Removes a given {@link ItemStack} from the page. - * - * @param item The {@link ItemStack} to remove. - */ - public void removePageItem(@NotNull final ItemStack item) { - final Optional guiItem = pageItems.stream().filter(it -> it.getItemStack().equals(item)).findFirst(); - guiItem.ifPresent(this::removePageItem); - } - - /** - * Overrides {@link BaseGui#open(HumanEntity)} to use the paginated populator instead - * - * @param player The {@link HumanEntity} to open the GUI to - */ - @Override - public void open(@NotNull final HumanEntity player) { - open(player, 1); - } - - /** - * Specific open method for the Paginated GUI - * Uses {@link #populatePage()} - * - * @param player The {@link HumanEntity} to open it to - * @param openPage The specific page to open at - */ - public void open(@NotNull final HumanEntity player, final int openPage) { - if (player.isSleeping()) return; - if (openPage <= getPagesNum() || openPage > 0) pageNum = openPage; - - getInventory().clear(); - currentPage.clear(); - - populateGui(); - - if (pageSize == 0) pageSize = calculatePageSize(); - - populatePage(); - - player.openInventory(getInventory()); - } - - /** - * Overrides {@link BaseGui#updateTitle(String)} to use the paginated populator instead - * Updates the title of the GUI - * This method may cause LAG if used on a loop - * - * @param title The title to set - * @return The GUI for easier use when declaring, works like a builder - */ - @Override - @NotNull - public BaseGui updateTitle(@NotNull final String title) { - setUpdating(true); - - final List viewers = new ArrayList<>(getInventory().getViewers()); - - setInventory(Bukkit.createInventory(this, getInventory().getSize(), title)); - - for (final HumanEntity player : viewers) { - open(player, getPageNum()); - } - - setUpdating(false); - - return this; - } - - /** - * Gets an immutable {@link Map} with all the current pages items - * - * @return The {@link Map} with all the {@link #currentPage} - */ - @NotNull - public Map<@NotNull Integer, @NotNull GuiItem> getCurrentPageItems() { - return Collections.unmodifiableMap(currentPage); - } - - /** - * Gets an immutable {@link List} with all the page items added to the GUI - * - * @return The {@link List} with all the {@link #pageItems} - */ - @NotNull - public List<@NotNull GuiItem> getPageItems() { - return Collections.unmodifiableList(pageItems); - } - - - /** - * Gets the current page number - * - * @return The current page number - */ - public int getCurrentPageNum() { - return pageNum; - } - - /** - * Gets the next page number - * - * @return The next page number or {@link #pageNum} if no next is present - */ - public int getNextPageNum() { - if (pageNum + 1 > getPagesNum()) return pageNum; - return pageNum + 1; - } - - /** - * Gets the previous page number - * - * @return The previous page number or {@link #pageNum} if no previous is present - */ - public int getPrevPageNum() { - if (pageNum - 1 == 0) return pageNum; - return pageNum - 1; - } - - /** - * Goes to the next page - * - * @return False if there is no next page. - */ - public boolean next() { - if (pageNum + 1 > getPagesNum()) return false; - - pageNum++; - updatePage(); - return true; - } - - /** - * Goes to the previous page if possible - * - * @return False if there is no previous page. - */ - public boolean previous() { - if (pageNum - 1 == 0) return false; - - pageNum--; - updatePage(); - return true; - } - - /** - * Gets the page item for the GUI listener - * - * @param slot The slot to get - * @return The GuiItem on that slot - */ - GuiItem getPageItem(final int slot) { - return currentPage.get(slot); - } - - /** - * Gets the items in the page - * - * @param givenPage The page to get - * @return A list with all the page items - */ - private List getPageNum(final int givenPage) { - final int page = givenPage - 1; - - final List guiPage = new ArrayList<>(); - - int max = ((page * pageSize) + pageSize); - if (max > pageItems.size()) max = pageItems.size(); - - for (int i = page * pageSize; i < max; i++) { - guiPage.add(pageItems.get(i)); - } - - return guiPage; - } - - /** - * Gets the number of pages the GUI has - * - * @return The pages number - */ - public int getPagesNum() { - return (int) Math.ceil((double) pageItems.size() / pageSize); - } - - /** - * Populates the inventory with the page items - */ - private void populatePage() { - // Adds the paginated items to the page - for (final GuiItem guiItem : getPageNum(pageNum)) { - for (int slot = 0; slot < getRows() * 9; slot++) { - if (getGuiItem(slot) != null || getInventory().getItem(slot) != null) continue; - currentPage.put(slot, guiItem); - getInventory().setItem(slot, guiItem.getItemStack()); - break; - } - } - } - - /** - * Gets the current page items to be used on other gui types - * - * @return The {@link Map} with all the {@link #currentPage} - */ - Map getMutableCurrentPageItems() { - return currentPage; - } - - /** - * Clears the page content - */ - void clearPage() { - for (Map.Entry entry : currentPage.entrySet()) { - getInventory().setItem(entry.getKey(), null); - } - } - - /** - * Clears all previously added page items - */ - public void clearPageItems(final boolean update) { - pageItems.clear(); - if (update) update(); - } - - public void clearPageItems() { - clearPageItems(false); - } - - - /** - * Gets the page size - * - * @return The page size - */ - int getPageSize() { - return pageSize; - } - - /** - * Gets the page number - * - * @return The current page number - */ - int getPageNum() { - return pageNum; - } - - /** - * Sets the page number - * - * @param pageNum Sets the current page to be the specified number - */ - void setPageNum(final int pageNum) { - this.pageNum = pageNum; - } - - /** - * Updates the page content - */ - void updatePage() { - clearPage(); - populatePage(); - } - - /** - * Calculates the size of the give page - * - * @return The page size - */ - int calculatePageSize() { - int counter = 0; - - for (int slot = 0; slot < getRows() * 9; slot++) { - if (getInventory().getItem(slot) == null) counter++; - } - - return counter; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java b/core/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java deleted file mode 100644 index 38fbae31..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java +++ /dev/null @@ -1,316 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.components.InteractionModifier; -import dev.triumphteam.gui.components.ScrollType; -import org.bukkit.entity.HumanEntity; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * GUI that allows you to scroll through items - */ -@SuppressWarnings("unused") -public class ScrollingGui extends PaginatedGui { - - private final ScrollType scrollType; - private int scrollSize = 0; - - /** - * Main constructor of the Scrolling GUI - * - * @param rows The rows the GUI should have - * @param pageSize The Page size - * @param title The title using {@link String} - * @param scrollType The {@link ScrollType} - * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use - * @since 3.0.3 - * @author SecretX - */ - public ScrollingGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final ScrollType scrollType, @NotNull final Set interactionModifiers) { - super(rows, pageSize, title, interactionModifiers); - - this.scrollType = scrollType; - } - - /** - * Main constructor of the Scrolling GUI - * - * @param rows The rows the GUI should have - * @param pageSize The Page size - * @param title The GUI's title - * @param scrollType The {@link ScrollType} - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final ScrollType scrollType) { - super(rows, pageSize, title); - this.scrollType = scrollType; - } - - /** - * Alternative constructor that doesn't require the {@link ScrollType} - * - * @param rows The rows the GUI should have - * @param pageSize The Page size - * @param title The GUI's title - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(final int rows, final int pageSize, @NotNull final String title) { - this(rows, pageSize, title, ScrollType.VERTICAL); - } - - /** - * Alternative constructor that doesn't require the {@link ScrollType} or page size - * - * @param rows The rows the GUI should have - * @param title The GUI's title - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(final int rows, @NotNull final String title) { - this(rows, 0, title, ScrollType.VERTICAL); - } - - /** - * Alternative constructor that doesn't require the page size - * - * @param rows The rows the GUI should have - * @param title The GUI's title - * @param scrollType The {@link ScrollType} - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(final int rows, @NotNull final String title, @NotNull final ScrollType scrollType) { - this(rows, 0, title, scrollType); - } - - /** - * Alternative constructor that only requires title - * - * @param title The GUI's title - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(@NotNull final String title) { - this(2, title); - } - - /** - * Alternative constructor that doesn't require the rows or page size - * - * @param title The GUI's title - * @param scrollType The {@link ScrollType} - * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} - */ - @Deprecated - public ScrollingGui(@NotNull final String title, @NotNull final ScrollType scrollType) { - this(2, title, scrollType); - } - - /** - * Overrides {@link PaginatedGui#next()} to make it work with the specific scrolls - */ - @Override - public boolean next() { - if (getPageNum() * scrollSize + getPageSize() > getPageItems().size() + scrollSize) return false; - - setPageNum(getPageNum() + 1); - updatePage(); - return true; - } - - /** - * Overrides {@link PaginatedGui#previous()} to make it work with the specific scrolls - */ - @Override - public boolean previous() { - if (getPageNum() - 1 == 0) return false; - - setPageNum(getPageNum() - 1); - updatePage(); - return true; - } - - /** - * Overrides {@link PaginatedGui#open(HumanEntity)} to make it work with the specific scrolls - * - * @param player The {@link HumanEntity} to open the GUI to - */ - @Override - public void open(@NotNull final HumanEntity player) { - open(player, 1); - } - - /** - * Overrides {@link PaginatedGui#open(HumanEntity, int)} to make it work with the specific scrolls - * - * @param player The {@link HumanEntity} to open the GUI to - * @param openPage The page to open on - */ - @Override - public void open(@NotNull final HumanEntity player, final int openPage) { - if (player.isSleeping()) return; - getInventory().clear(); - getMutableCurrentPageItems().clear(); - - populateGui(); - - if (getPageSize() == 0) setPageSize(calculatePageSize()); - if (scrollSize == 0) scrollSize = calculateScrollSize(); - if (openPage > 0 && (openPage * scrollSize + getPageSize() <= getPageItems().size() + scrollSize)) { - setPageNum(openPage); - } - - populatePage(); - - player.openInventory(getInventory()); - } - - /** - * Overrides {@link PaginatedGui#updatePage()} to make it work with the specific scrolls - */ - @Override - void updatePage() { - clearPage(); - populatePage(); - } - - /** - * Fills the page with the items - */ - private void populatePage() { - // Adds the paginated items to the page - for (final GuiItem guiItem : getPage(getPageNum())) { - if (scrollType == ScrollType.HORIZONTAL) { - putItemHorizontally(guiItem); - continue; - } - - putItemVertically(guiItem); - } - } - - /** - * Calculates the size of each scroll - * - * @return The size of he scroll - */ - private int calculateScrollSize() { - int counter = 0; - - if (scrollType == ScrollType.VERTICAL) { - boolean foundCol = false; - - for (int row = 1; row <= getRows(); row++) { - for (int col = 1; col <= 9; col++) { - final int slot = getSlotFromRowCol(row, col); - if (getInventory().getItem(slot) == null) { - if (!foundCol) foundCol = true; - counter++; - } - } - - if (foundCol) return counter; - } - - return counter; - } - - boolean foundRow = false; - - for (int col = 1; col <= 9; col++) { - for (int row = 1; row <= getRows(); row++) { - final int slot = getSlotFromRowCol(row, col); - if (getInventory().getItem(slot) == null) { - if (!foundRow) foundRow = true; - counter++; - } - } - - if (foundRow) return counter; - } - - return counter; - } - - /** - * Puts the item in the GUI for horizontal scrolling - * - * @param guiItem The gui item - */ - private void putItemVertically(final GuiItem guiItem) { - for (int slot = 0; slot < getRows() * 9; slot++) { - if (getGuiItem(slot) != null || getInventory().getItem(slot) != null) continue; - getMutableCurrentPageItems().put(slot, guiItem); - getInventory().setItem(slot, guiItem.getItemStack()); - break; - } - } - - /** - * Puts item into the GUI for vertical scrolling - * - * @param guiItem The gui item - */ - private void putItemHorizontally(final GuiItem guiItem) { - for (int col = 1; col < 10; col++) { - for (int row = 1; row <= getRows(); row++) { - final int slot = getSlotFromRowCol(row, col); - if (getGuiItem(slot) != null || getInventory().getItem(slot) != null) continue; - getMutableCurrentPageItems().put(slot, guiItem); - getInventory().setItem(slot, guiItem.getItemStack()); - return; - } - } - } - - /** - * Gets the items from the current page - * - * @param givenPage The page number - * @return A list with all the items - */ - private List getPage(final int givenPage) { - final int page = givenPage - 1; - final int pageItemsSize = getPageItems().size(); - - final List guiPage = new ArrayList<>(); - - int max = page * scrollSize + getPageSize(); - if (max > pageItemsSize) max = pageItemsSize; - - for (int i = page * scrollSize; i < max; i++) { - guiPage.add(getPageItems().get(i)); - } - - return guiPage; - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/StorageGui.java b/core/src/main/java/dev/triumphteam/gui/guis/StorageGui.java deleted file mode 100644 index 9a4e8f82..00000000 --- a/core/src/main/java/dev/triumphteam/gui/guis/StorageGui.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.guis; - -import dev.triumphteam.gui.components.InteractionModifier; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * GUI that does not clear its items once it's closed - */ -@SuppressWarnings("unused") -public class StorageGui extends BaseGui { - - /** - * Main constructor for the StorageGui - * - * @param rows The amount of rows the GUI should have - * @param title The GUI's title using {@link String} - * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use - * @since 3.0.3 - */ - public StorageGui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { - super(rows, title, interactionModifiers); - } - - /** - * Main constructor of the Persistent GUI - * - * @param rows The rows the GUI should have - * @param title The GUI's title - */ - @Deprecated - public StorageGui(final int rows, @NotNull final String title) { - super(rows, title); - } - - /** - * Alternative constructor that does not require rows - * - * @param title The GUI's title - */ - @Deprecated - public StorageGui(@NotNull final String title) { - super(1, title); - } - - /** - * Adds {@link ItemStack} to the inventory straight, not the GUI - * - * @param items Varargs with {@link ItemStack}s - * @return An immutable {@link Map} with the left overs - */ - @NotNull - public Map<@NotNull Integer, @NotNull ItemStack> addItem(@NotNull final ItemStack... items) { - return Collections.unmodifiableMap(getInventory().addItem(items)); - } - - /** - * Adds {@link ItemStack} to the inventory straight, not the GUI - * - * @param items Varargs with {@link ItemStack}s - * @return An immutable {@link Map} with the left overs - */ - public Map<@NotNull Integer, @NotNull ItemStack> addItem(@NotNull final List items) { - return addItem(items.toArray(new ItemStack[0])); - } - - /** - * Overridden {@link BaseGui#open(HumanEntity)} to prevent - * - * @param player The {@link HumanEntity} to open the GUI to - */ - @Override - public void open(@NotNull final HumanEntity player) { - if (player.isSleeping()) return; - populateGui(); - player.openInventory(getInventory()); - } - -} diff --git a/core/src/main/java/dev/triumphteam/gui/item/GuiItem.java b/core/src/main/java/dev/triumphteam/gui/item/GuiItem.java new file mode 100644 index 00000000..b7dd6cb9 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/item/GuiItem.java @@ -0,0 +1,36 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.item; + +import dev.triumphteam.gui.click.action.GuiClickAction; +import org.jetbrains.annotations.NotNull; + +public interface GuiItem { + + @NotNull + I render(); + + @NotNull + GuiClickAction

getClickAction(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/item/RenderedGuiItem.java b/core/src/main/java/dev/triumphteam/gui/item/RenderedGuiItem.java new file mode 100644 index 00000000..0f8735db --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/item/RenderedGuiItem.java @@ -0,0 +1,34 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.item; + +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.click.handler.ClickHandler; +import org.jetbrains.annotations.NotNull; + +public record RenderedGuiItem( + @NotNull I item, + @NotNull ClickHandler

clickHandler, + @NotNull GuiClickAction

action +) {} diff --git a/core/src/main/java/dev/triumphteam/gui/item/items/SimpleGuiItem.java b/core/src/main/java/dev/triumphteam/gui/item/items/SimpleGuiItem.java new file mode 100644 index 00000000..f9c9d0eb --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/item/items/SimpleGuiItem.java @@ -0,0 +1,49 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.item.items; + +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.item.GuiItem; +import org.jetbrains.annotations.NotNull; + +public final class SimpleGuiItem implements GuiItem { + + private final I item; + private final GuiClickAction

clickAction; + + public SimpleGuiItem(final @NotNull I item, final @NotNull GuiClickAction

clickAction) { + this.item = item; + this.clickAction = clickAction; + } + + @Override + public @NotNull I render() { + return item; + } + + @Override + public @NotNull GuiClickAction

getClickAction() { + return clickAction; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/layout/BorderGuiLayout.java b/core/src/main/java/dev/triumphteam/gui/layout/BorderGuiLayout.java new file mode 100644 index 00000000..5794bb91 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/layout/BorderGuiLayout.java @@ -0,0 +1,72 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.layout; + +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public final class BorderGuiLayout implements GuiLayout { + + private final List slots = new ArrayList<>(); + + public BorderGuiLayout(final int rows) { + this(Slot.origin(), Slot.max(rows)); + } + + public BorderGuiLayout(final @NotNull Slot min, final @NotNull Slot max) { + // Top border + for (int col = min.column(); col <= max.column(); col++) { + slots.add(Slot.of(min.row(), col)); + } + + // Bottom border + for (int col = min.column(); col <= max.column(); col++) { + slots.add(Slot.of(max.row(), col)); + } + + // Left border + for (int row = min.row() + 1; row < max.row(); row++) { + slots.add(Slot.of(row, min.column())); + } + + // Right border + for (int row = min.row() + 1; row < max.row(); row++) { + slots.add(Slot.of(row, max.column())); + } + } + + @Override + public @NotNull Iterator iterator() { + return slots.iterator(); + } + + @Override + public int size() { + return slots.size(); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/layout/BoxGuiLayout.java b/core/src/main/java/dev/triumphteam/gui/layout/BoxGuiLayout.java new file mode 100644 index 00000000..e8709d20 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/layout/BoxGuiLayout.java @@ -0,0 +1,74 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.layout; + +import dev.triumphteam.gui.slot.Slot; +import org.checkerframework.checker.units.qual.min; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public final class BoxGuiLayout implements GuiLayout { + + private final List slots = new ArrayList<>(); + + public BoxGuiLayout(final @NotNull Slot min, final @NotNull Slot max) { + this(min, max, Direction.VERTICAL); + } + + public BoxGuiLayout(final @NotNull Slot min, final @NotNull Slot max, final @NotNull Direction direction) { + switch (direction) { + case HORIZONTAL -> { + for (int col = min.column(); col <= max.column(); col++) { + for (int row = min.row(); row <= max.row(); row++) { + slots.add(Slot.of(row, col)); + } + } + } + case VERTICAL -> { + for (int row = min.row(); row <= max.row(); row++) { + for (int col = min.column(); col <= max.column(); col++) { + slots.add(Slot.of(row, col)); + } + } + } + } + } + + @Override + public @NotNull Iterator iterator() { + return slots.iterator(); + } + + @Override + public int size() { + return slots.size(); + } + + public enum Direction { + HORIZONTAL, VERTICAL + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/util/Legacy.java b/core/src/main/java/dev/triumphteam/gui/layout/GuiLayout.java similarity index 62% rename from core/src/main/java/dev/triumphteam/gui/components/util/Legacy.java rename to core/src/main/java/dev/triumphteam/gui/layout/GuiLayout.java index e955eaa5..c73e3e42 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/util/Legacy.java +++ b/core/src/main/java/dev/triumphteam/gui/layout/GuiLayout.java @@ -1,18 +1,18 @@ /** * MIT License - * - * Copyright (c) 2021 TriumphTeam - * + *

+ * Copyright (c) 2024 TriumphTeam + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,23 +21,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components.util; +package dev.triumphteam.gui.layout; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; -public final class Legacy { +import javax.swing.*; - /** - * Since old versions and fucking spigot don't use the components, I use the serializer - * with the stupid format to guarantee compatibility - */ - public static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder() - .hexColors() - .useUnusualXRepeatedCharacterHexFormat() - .build(); +public interface GuiLayout extends Iterable { - private Legacy() { - throw new UnsupportedOperationException("Class should not be instantiated!"); + static @NotNull GuiLayout box( + final @NotNull Slot min, + final @NotNull Slot max, + final @NotNull BoxGuiLayout.Direction direction + ) { + return new BoxGuiLayout(min, max, direction); } + static @NotNull GuiLayout box( + final @NotNull Slot min, + final @NotNull Slot max + ) { + return new BoxGuiLayout(min, max); + } + + int size(); } diff --git a/core/src/main/java/dev/triumphteam/gui/settings/GuiSettings.java b/core/src/main/java/dev/triumphteam/gui/settings/GuiSettings.java new file mode 100644 index 00000000..d736bf9a --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/settings/GuiSettings.java @@ -0,0 +1,69 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.settings; + +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.click.handler.SimpleClickHandler; +import dev.triumphteam.gui.component.renderer.DefaultGuiComponentRenderer; +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unchecked") +public abstract class GuiSettings> { + + private ClickHandler

clickHandler = new SimpleClickHandler<>(); + private GuiComponentRenderer componentRenderer = new DefaultGuiComponentRenderer<>(); + private long spamPreventionDuration = 200L; + + public S clickHandler(final @NotNull ClickHandler

clickHandler) { + this.clickHandler = clickHandler; + return (S) this; + } + + public S componentRenderer(final @NotNull GuiComponentRenderer componentRenderer) { + this.componentRenderer = componentRenderer; + return (S) this; + } + + public S spamPreventionDuration(final long spamPreventionDuration) { + if (spamPreventionDuration < 0L) { + throw new IllegalArgumentException("Spam prevention duration cannot be negative!"); + } + + this.spamPreventionDuration = spamPreventionDuration; + return (S) this; + } + + public @NotNull ClickHandler

getClickHandler() { + return clickHandler; + } + + public @NotNull GuiComponentRenderer getComponentRenderer() { + return componentRenderer; + } + + public long getSpamPreventionDuration() { + return spamPreventionDuration; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/slot/Slot.java b/core/src/main/java/dev/triumphteam/gui/slot/Slot.java new file mode 100644 index 00000000..7aa0f6a1 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/slot/Slot.java @@ -0,0 +1,35 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.slot; + +public record Slot(int row, int column) { + + public static Slot of(final int row, final int column) { + return new Slot(row, column); + } + + public static Slot origin() {return new Slot(1, 1);} + + public static Slot max(final int rows) {return new Slot(rows, 9);} +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/AbstractScrollerState.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/AbstractScrollerState.java new file mode 100644 index 00000000..d9ee1bda --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/AbstractScrollerState.java @@ -0,0 +1,93 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.nova.AbstractState; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static java.lang.Math.ceil; +import static java.lang.Math.min; + +public abstract class AbstractScrollerState extends AbstractState implements ScrollerState { + + private final int steps; + private final int elementsPerView; + private final List elements; + private final GuiLayout layout; + private final int limit; + + private int current; + + public AbstractScrollerState( + final int steps, + final int startPosition, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + this.elementsPerView = layout.size(); + + // If no steps we use all elements per view + if (steps < 1) this.steps = this.elementsPerView; + else this.steps = steps; + + this.current = startPosition; + this.elements = elements; + this.layout = layout; + this.limit = (int) ceil((double) elements.size() / steps); + } + + @Override + public void next() { + if (current >= limit) return; + current += steps; + trigger(); + } + + @Override + public void prev() { + if (current <= 1) return; + current -= steps; + trigger(); + } + + @Override + public @NotNull Iterator> iterator() { + // TODO: this should probably be on page change not on iterator call to avoid calculation when multiple calls are made + final var from = current - 1; + final var to = min(from + elementsPerView, elements.size()); + final var subList = new ArrayList>(); + final var layoutIterator = layout.iterator(); + + for (int i = from; i < to; i++) { + if (!layoutIterator.hasNext()) break; + final var slot = layoutIterator.next(); + + subList.add(new PageEntry<>(slot, elements.get(i))); + } + + return subList.iterator(); + } + + protected int getSteps() { + return steps; + } + + protected int getElementsPerView() { + return elementsPerView; + } + + protected List getElements() { + return elements; + } + + protected int getLimit() { + return limit; + } + + protected int getCurrent() { + return current; + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/PageEntry.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/PageEntry.java new file mode 100644 index 00000000..13f3a2fd --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/PageEntry.java @@ -0,0 +1,23 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public record PageEntry(@NotNull Slot slot, @NotNull T element) implements Map.Entry { + @Override + public Slot getKey() { + return slot; + } + + @Override + public T getValue() { + return element; + } + + @Override + public T setValue(T value) { + throw new UnsupportedOperationException("PageEntry is immutable."); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/PagerState.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/PagerState.java new file mode 100644 index 00000000..1dab0e2c --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/PagerState.java @@ -0,0 +1,28 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.layout.GuiLayout; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface PagerState extends ScrollerState { + + static @NotNull PagerState of( + final int startPage, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + return new SimplePagerState<>(startPage, elements, layout); + } + + static @NotNull PagerState of( + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + return of(1, elements, layout); + } + + int getCurrentPage(); + + int getPageCount(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/ScrollerState.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/ScrollerState.java new file mode 100644 index 00000000..de69e77d --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/ScrollerState.java @@ -0,0 +1,22 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.layout.GuiLayout; +import dev.triumphteam.nova.State; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface ScrollerState extends State, Iterable> { + + static @NotNull ScrollerState of( + final int steps, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + return new SimpleScrollerState<>(steps, elements, layout); + } + + void prev(); + + void next(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/SimplePagerState.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/SimplePagerState.java new file mode 100644 index 00000000..5f93581a --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/SimplePagerState.java @@ -0,0 +1,27 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.layout.GuiLayout; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public final class SimplePagerState extends AbstractScrollerState implements PagerState { + + public SimplePagerState( + final int startPage, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + super(0, startPage, elements, layout); + } + + @Override + public int getCurrentPage() { + return getCurrent(); + } + + @Override + public int getPageCount() { + return getLimit(); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/state/pagination/SimpleScrollerState.java b/core/src/main/java/dev/triumphteam/gui/state/pagination/SimpleScrollerState.java new file mode 100644 index 00000000..4eed1ebe --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/state/pagination/SimpleScrollerState.java @@ -0,0 +1,17 @@ +package dev.triumphteam.gui.state.pagination; + +import dev.triumphteam.gui.layout.GuiLayout; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class SimpleScrollerState extends AbstractScrollerState { + + public SimpleScrollerState( + final int steps, + final @NotNull List elements, + final @NotNull GuiLayout layout + ) { + super(steps, 1, elements, layout); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/GuiPage.java b/core/src/main/java/dev/triumphteam/gui/title/GuiTitle.java similarity index 91% rename from core/src/main/java/dev/triumphteam/gui/components/GuiPage.java rename to core/src/main/java/dev/triumphteam/gui/title/GuiTitle.java index 05b54a41..718db21a 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/GuiPage.java +++ b/core/src/main/java/dev/triumphteam/gui/title/GuiTitle.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.title; -public final class GuiPage { -} +public interface GuiTitle {} diff --git a/core/src/main/java/dev/triumphteam/gui/title/ReactiveGuiTitle.java b/core/src/main/java/dev/triumphteam/gui/title/ReactiveGuiTitle.java new file mode 100644 index 00000000..b8aa93ca --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/title/ReactiveGuiTitle.java @@ -0,0 +1,32 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.title; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +public interface ReactiveGuiTitle extends StatefulGuiTitle { + + @NotNull Component render(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/GuiType.java b/core/src/main/java/dev/triumphteam/gui/title/SimpleGuiTitle.java similarity index 61% rename from core/src/main/java/dev/triumphteam/gui/components/GuiType.java rename to core/src/main/java/dev/triumphteam/gui/title/SimpleGuiTitle.java index a2443060..95e0cc87 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/GuiType.java +++ b/core/src/main/java/dev/triumphteam/gui/title/SimpleGuiTitle.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,36 +21,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.title; -import org.bukkit.event.inventory.InventoryType; +import dev.triumphteam.gui.title.functional.FunctionalGuiTitleRender; +import dev.triumphteam.nova.State; +import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; -// TODO COMMENTS -public enum GuiType { +import java.util.List; - CHEST(InventoryType.CHEST, 9), - WORKBENCH(InventoryType.WORKBENCH, 9), - HOPPER(InventoryType.HOPPER, 5), - DISPENSER(InventoryType.DISPENSER, 8), - BREWING(InventoryType.BREWING, 4); +public final class SimpleGuiTitle implements ReactiveGuiTitle { - @NotNull - private final InventoryType inventoryType; - private final int limit; + private final List states; + private final FunctionalGuiTitleRender render; - GuiType(@NotNull final InventoryType inventoryType, final int limit) { - this.inventoryType = inventoryType; - this.limit = limit; + public SimpleGuiTitle(final @NotNull FunctionalGuiTitleRender render, final @NotNull List states) { + this.render = render; + this.states = states; } - @NotNull - public InventoryType getInventoryType() { - return inventoryType; + @Override + public @NotNull Component render() { + return render.render(); } - public int getLimit() { - return limit; + @Override + public @NotNull List states() { + return states; } - } diff --git a/core/src/main/java/dev/triumphteam/gui/components/Serializable.java b/core/src/main/java/dev/triumphteam/gui/title/StatefulGuiTitle.java similarity index 85% rename from core/src/main/java/dev/triumphteam/gui/components/Serializable.java rename to core/src/main/java/dev/triumphteam/gui/title/StatefulGuiTitle.java index dc3516b0..a7545572 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/Serializable.java +++ b/core/src/main/java/dev/triumphteam/gui/title/StatefulGuiTitle.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,16 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.components; +package dev.triumphteam.gui.title; +import dev.triumphteam.nova.State; import org.jetbrains.annotations.NotNull; import java.util.List; -public interface Serializable { - - List encodeGui(); - - void decodeGui(@NotNull final List gui); +public interface StatefulGuiTitle extends GuiTitle { + @NotNull List states(); } diff --git a/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitle.java b/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitle.java new file mode 100644 index 00000000..8aabdc8e --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitle.java @@ -0,0 +1,32 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.title.functional; + +import dev.triumphteam.nova.holder.StateHolder; +import org.jetbrains.annotations.NotNull; + +public interface FunctionalGuiTitle extends StateHolder { + + void render(final @NotNull FunctionalGuiTitleRender render); +} diff --git a/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitleRender.java b/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitleRender.java new file mode 100644 index 00000000..437146cd --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/title/functional/FunctionalGuiTitleRender.java @@ -0,0 +1,33 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.title.functional; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface FunctionalGuiTitleRender { + + @NotNull Component render(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/title/functional/SimpleFunctionalGuiTitle.java b/core/src/main/java/dev/triumphteam/gui/title/functional/SimpleFunctionalGuiTitle.java new file mode 100644 index 00000000..24ced769 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/title/functional/SimpleFunctionalGuiTitle.java @@ -0,0 +1,47 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.title.functional; + +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.title.GuiTitle; +import dev.triumphteam.gui.title.SimpleGuiTitle; +import dev.triumphteam.nova.holder.AbstractStateHolder; +import org.jetbrains.annotations.NotNull; + +public final class SimpleFunctionalGuiTitle extends AbstractStateHolder implements FunctionalGuiTitle { + + private FunctionalGuiTitleRender guiTitleRender = null; + + @Override + public void render(final @NotNull FunctionalGuiTitleRender render) { + this.guiTitleRender = render; + } + + public @NotNull GuiTitle asGuiTitle() { + if (guiTitleRender == null) { + throw new TriumphGuiException("TODO TITLE"); + } + return new SimpleGuiTitle(guiTitleRender, getStates()); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java b/core/src/main/java/dev/triumphteam/gui/title/renderer/DefaultGuiTitleRenderer.java similarity index 60% rename from core/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java rename to core/src/main/java/dev/triumphteam/gui/title/renderer/DefaultGuiTitleRenderer.java index f160514a..38d824df 100644 --- a/core/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java +++ b/core/src/main/java/dev/triumphteam/gui/title/renderer/DefaultGuiTitleRenderer.java @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2021 TriumphTeam + * Copyright (c) 2024 TriumphTeam * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,35 +21,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package dev.triumphteam.gui.builder.gui; +package dev.triumphteam.gui.title.renderer; -import dev.triumphteam.gui.components.util.Legacy; -import dev.triumphteam.gui.guis.StorageGui; -import org.jetbrains.annotations.Contract; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.title.GuiTitle; +import dev.triumphteam.gui.title.ReactiveGuiTitle; +import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; -/** - * The simple GUI builder is used for creating a {@link StorageGui} - */ -public final class StorageBuilder extends BaseGuiBuilder { +public final class DefaultGuiTitleRenderer implements GuiTitleRenderer { - /** - * Creates a new {@link StorageGui} - * - * @return A new {@link StorageGui} - */ - @NotNull @Override - @Contract(" -> new") - public StorageGui create() { - final StorageGui gui = new StorageGui(getRows(), Legacy.SERIALIZER.serialize(getTitle()), getModifiers()); + public void renderTitle( + final @NotNull GuiTitle title, + final @NotNull Consumer thenRun + ) { - final Consumer consumer = getConsumer(); - if (consumer != null) consumer.accept(gui); + if (!(title instanceof ReactiveGuiTitle reactiveGuiTitle)) { + throw new TriumphGuiException("Could not render title as it is not supported by the current renderer."); + } - return gui; + final var component = reactiveGuiTitle.render(); + thenRun.accept(component); } - } diff --git a/core/src/main/java/dev/triumphteam/gui/title/renderer/GuiTitleRenderer.java b/core/src/main/java/dev/triumphteam/gui/title/renderer/GuiTitleRenderer.java new file mode 100644 index 00000000..9e6e9db7 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/title/renderer/GuiTitleRenderer.java @@ -0,0 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.title.renderer; + +import dev.triumphteam.gui.title.GuiTitle; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public interface GuiTitleRenderer { + + void renderTitle( + final @NotNull GuiTitle title, + final @NotNull Consumer thenRun + ); +} diff --git a/examples/paper/java/build.gradle.kts b/examples/paper/java/build.gradle.kts new file mode 100644 index 00000000..777bad78 --- /dev/null +++ b/examples/paper/java/build.gradle.kts @@ -0,0 +1,31 @@ +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml + +plugins { + id("gui.base") + id("gui.paper-example") +} + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(projects.triumphGuiPaper) +} + +tasks { + runServer { + minecraftVersion("1.21.1") + } + + bukkitPluginYaml { + main.set("dev.triumphteam.gui.example.GuiPlugin") + load = BukkitPluginYaml.PluginLoadOrder.STARTUP + authors.add("Matt") + apiVersion = "1.21" + commands.add(commands.create("gui-static")) + commands.add(commands.create("gui-clicker")) + commands.add(commands.create("gui-title")) + foliaSupported.set(true) + } +} diff --git a/examples/paper/java/src/main/java/dev/triumphteam/gui/example/GuiPlugin.java b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/GuiPlugin.java new file mode 100644 index 00000000..acc80e9e --- /dev/null +++ b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/GuiPlugin.java @@ -0,0 +1,39 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example; + +import dev.triumphteam.gui.example.examples.CookieClicker; +import dev.triumphteam.gui.example.examples.Static; +import dev.triumphteam.gui.example.examples.UpdatingTitle; +import org.bukkit.plugin.java.JavaPlugin; + +public final class GuiPlugin extends JavaPlugin { + + @Override + public void onEnable() { + getCommand("gui-static").setExecutor(new Static()); + getCommand("gui-clicker").setExecutor(new CookieClicker()); + getCommand("gui-title").setExecutor(new UpdatingTitle()); + } +} diff --git a/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/CookieClicker.java b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/CookieClicker.java new file mode 100644 index 00000000..a1f01454 --- /dev/null +++ b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/CookieClicker.java @@ -0,0 +1,67 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples; + +import dev.triumphteam.gui.paper.Gui; +import dev.triumphteam.gui.paper.builder.item.ItemBuilder; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public final class CookieClicker implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, final @NotNull String[] args) { + if (!(sender instanceof Player senderPlayer)) return false; + + final var gui = Gui + .of(1) + .spamPreventionDuration(0) // We want fast clicks so let's not have any spam prevention + .title(Component.text("Cookie Clicker Gui")) // Simple title for the GUI + .component(component -> { // A reactive component + + // Remember how many times the item was clicked + final var clicks = component.remember(0); + + // Rendering the component + component.render(container -> { + container.setItem(1, 1, ItemBuilder.from(Material.COOKIE) + .name(Component.text("Clicked " + clicks.get() + " times!")) + .asGuiItem((player, context) -> { + // Increase clicks which, in turn, updates the state of the component + clicks.update((value) -> value + 1); + }) + ); + }); + }) + .build(); + + gui.open(senderPlayer); + return true; + } +} diff --git a/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/Static.java b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/Static.java new file mode 100644 index 00000000..c2ae519d --- /dev/null +++ b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/Static.java @@ -0,0 +1,58 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples; + +import dev.triumphteam.gui.paper.Gui; +import dev.triumphteam.gui.paper.builder.item.ItemBuilder; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public final class Static implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, final @NotNull String[] args) { + if (!(sender instanceof Player senderPlayer)) return false; + + final var gui = Gui + .of(6) + .title(Component.text("Static Gui")) // Simple title for the GUI + .statelessComponent(container -> { // A stateless component, we don't care about the item updating, just want the click action + container.setItem(1, 1, ItemBuilder.from(Material.PAPER) + .name(Component.text("My Paper")) + .asGuiItem((player, context) -> { + player.sendMessage("You have clicked on the paper item!"); + }) + ); + }) + .build(); + + gui.open(senderPlayer); + return true; + } +} diff --git a/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/UpdatingTitle.java b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/UpdatingTitle.java new file mode 100644 index 00000000..ca160f92 --- /dev/null +++ b/examples/paper/java/src/main/java/dev/triumphteam/gui/example/examples/UpdatingTitle.java @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples; + +import dev.triumphteam.gui.paper.Gui; +import dev.triumphteam.gui.paper.builder.item.ItemBuilder; +import dev.triumphteam.nova.MutableState; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class UpdatingTitle implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, final @NotNull String[] args) { + if (!(sender instanceof Player senderPlayer)) return false; + + // Firstly, we create a state that'll be used by the title + // It's created here and not directly in the "remember" because it needs to be accessible in the component too + final var titleState = MutableState.of(Component.text("Original title")); + + final var gui = Gui + .of(1) + .title((title) -> { + // Tell the title to remember the state + title.remember(titleState); + // Render the title's value + title.render(titleState::get); + }) + .statelessComponent(container -> { // Since we don't care about updating the items, we can just use a stateless component + + // First item to change the title to PAPER + container.setItem(1, 1, ItemBuilder.from(Material.PAPER) + .name(Component.text("Change title to 'PAPER TITLE'!")) + .asGuiItem((player, context) -> { + titleState.set(Component.text("PAPER TITLE")); // Triggers an update + }) + ); + + // Second item to change title to COOKIE + container.setItem(1, 2, ItemBuilder.from(Material.COOKIE) + .name(Component.text("Change title to 'COOKIE TITLE'!")) + .asGuiItem((player, context) -> { + titleState.set(Component.text("COOKIE TITLE")); // Triggers an update + }) + ); + + // Important to remember; due to by default states using "StateMutabilityPolicy.StructuralEquality" + // it means that if the title is the same as the new value it'll **NOT** trigger an update + }) + .build(); + + gui.open(senderPlayer); + return true; + } +} diff --git a/examples/paper/kotlin/build.gradle.kts b/examples/paper/kotlin/build.gradle.kts new file mode 100644 index 00000000..abc55ecd --- /dev/null +++ b/examples/paper/kotlin/build.gradle.kts @@ -0,0 +1,36 @@ +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml + +plugins { + id("gui.base") + id("gui.paper-example") +} + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(projects.triumphGuiPaperKotlin) +} + +tasks { + runServer { + minecraftVersion("1.21.1") + } + + bukkitPluginYaml { + main.set("dev.triumphteam.gui.example.GuiPlugin") + load = BukkitPluginYaml.PluginLoadOrder.STARTUP + authors.add("Matt") + apiVersion = "1.21" + commands.add(commands.create("gui-static")) + commands.add(commands.create("gui-clicker")) + commands.add(commands.create("gui-title")) + commands.add(commands.create("gui-page")) + commands.add(commands.create("gui-scroll")) + foliaSupported.set(true) + + // really neat idea, world found a cave that takes you to a place kinda like annihilation movie + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/GuiPlugin.kt b/examples/paper/kotlin/src/main/kotlin/GuiPlugin.kt new file mode 100644 index 00000000..0b16581e --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/GuiPlugin.kt @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example + +import dev.triumphteam.gui.example.examples.CookieClicker +import dev.triumphteam.gui.example.examples.Paginated +import dev.triumphteam.gui.example.examples.Scrolling +import dev.triumphteam.gui.example.examples.Static +import dev.triumphteam.gui.example.examples.UpdatingTitle +import org.bukkit.plugin.java.JavaPlugin + +public class GuiPlugin : JavaPlugin() { + + override fun onEnable() { + getCommand("gui-static")?.setExecutor(Static()) + getCommand("gui-clicker")?.setExecutor(CookieClicker()) + getCommand("gui-title")?.setExecutor(UpdatingTitle()) + getCommand("gui-page")?.setExecutor(Paginated()) + getCommand("gui-scroll")?.setExecutor(Scrolling()) + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/examples/CookieClicker.kt b/examples/paper/kotlin/src/main/kotlin/examples/CookieClicker.kt new file mode 100644 index 00000000..ebe1393a --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/examples/CookieClicker.kt @@ -0,0 +1,79 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples + +import dev.triumphteam.gui.kotlin.set +import dev.triumphteam.gui.paper.builder.item.ItemBuilder +import dev.triumphteam.gui.paper.kotlin.builder.buildGui +import dev.triumphteam.gui.paper.kotlin.builder.chestContainer +import dev.triumphteam.nova.getValue +import dev.triumphteam.nova.setValue +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import kotlin.time.Duration.Companion.milliseconds + +public class CookieClicker : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + if (sender !is Player) return false + + val gui = buildGui { + + // Not needed in this case since it's just using the default value of chest - 1 row + containerType = chestContainer { + rows = 1 + } + + // We want fast clicks so let's not have any spam prevention + spamPreventionDuration = 0.milliseconds + + // Simple title for the GUI + title(Component.text("Cookie Clicker Gui")) + + // A reactive component + component { + + // Remember how many times the item was clicked + var clicks by remember(0) + + // Rendering the component + render { container -> + container[1, 1] = ItemBuilder.from(Material.COOKIE) + .name(Component.text("Clicked $clicks times!")) + .asGuiItem { _, _ -> + // Increase clicks which, in turn, updates the state of the component + clicks++ + } + } + } + } + + gui.open(sender) + return true + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/examples/Paginated.kt b/examples/paper/kotlin/src/main/kotlin/examples/Paginated.kt new file mode 100644 index 00000000..063a4b92 --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/examples/Paginated.kt @@ -0,0 +1,96 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples + +import dev.triumphteam.gui.kotlin.set +import dev.triumphteam.gui.kotlin.slot +import dev.triumphteam.gui.layout.GuiLayout +import dev.triumphteam.gui.paper.builder.item.ItemBuilder +import dev.triumphteam.gui.paper.kotlin.builder.buildGui +import dev.triumphteam.gui.paper.kotlin.builder.chestContainer +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +public class Paginated : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array?): Boolean { + if (sender !is Player) return false + + // Simple example to fill in the pages + val materials = Material.entries.filter { it.isItem && !it.isAir }.shuffled() + + val gui = buildGui { + // If not set, defaults to chest - 1 row + containerType = chestContainer { + rows = 6 + } + + // Simple title for the gui + title(Component.text("Paginated Gui")) + + component { + + // Tell the component to remember the pager state with the given elements + // Elements can be anything, in this case it's the material of the page items + // If the element is already a fully built ItemStack it'll always be static as it is rendered outside the component + val pagerState = rememberPager( + materials, + // The layout is very important as it dictates how the items will be distributed in a page and also + // how many items there will be per page + GuiLayout.box(slot(2, 3), slot(4, 7)) + ) + + render { container -> + + // Loop through the page and set items to the container + pagerState.forEach { (slot, element) -> + // Create a new item stack with the material provided by the pager + container[slot] = ItemBuilder.from(element).asGuiItem() + } + + // The previous page button + container[6, 2] = ItemBuilder.from(Material.PAPER) + .name(Component.text("Previous page")) + .asGuiItem { _, _ -> + pagerState.prev() + } + + // The next page button + container[6, 8] = ItemBuilder.from(Material.PAPER) + .name(Component.text("Next page")) + .asGuiItem { _, _ -> + pagerState.next() + } + } + } + } + + gui.open(sender) + return true + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/examples/Scrolling.kt b/examples/paper/kotlin/src/main/kotlin/examples/Scrolling.kt new file mode 100644 index 00000000..67aa3e1b --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/examples/Scrolling.kt @@ -0,0 +1,104 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples + +import dev.triumphteam.gui.kotlin.set +import dev.triumphteam.gui.kotlin.slot +import dev.triumphteam.gui.layout.BoxGuiLayout +import dev.triumphteam.gui.layout.GuiLayout +import dev.triumphteam.gui.paper.builder.item.ItemBuilder +import dev.triumphteam.gui.paper.kotlin.builder.buildGui +import dev.triumphteam.gui.paper.kotlin.builder.chestContainer +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +public class Scrolling : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array?): Boolean { + if (sender !is Player) return false + + // Simple example to fill in the pages + val materials = Material.entries.filter { it.isItem && !it.isAir }.shuffled() + + val gui = buildGui { + // If not set, defaults to chest - 1 row + containerType = chestContainer { + rows = 6 + } + + // Simple title for the gui + title(Component.text("Scrolling Gui")) + + // The system is very similar to how you would do a paginated gui + component { + + // Tell the component to remember the scroller state with the given elements + // Elements can be anything, in this case it's the material of the scrolling items + // If the element is already a fully built ItemStack it'll always be static as it is rendered outside the component + val pagerState = rememberPager( + materials, + // The layout is very important as it dictates how the items will be distributed in a "page" and also + // how many items there will be per "page" + // The direction is part of the layout and will control how the scrolling will happen + // This also means that other layouts will behave very differently + GuiLayout.box( + slot(2, 3), + slot(4, 7), + BoxGuiLayout.Direction.VERTICAL, + ) + ) + + render { container -> + + // Loop through the page and set items to the container + pagerState.forEach { (slot, element) -> + // Create a new item stack with the material provided by the pager + container[slot] = ItemBuilder.from(element).asGuiItem() + } + + // The previous page button + container[6, 2] = ItemBuilder.from(Material.PAPER) + .name(Component.text("Previous page")) + .asGuiItem { _, _ -> + pagerState.prev() + } + + // The next page button + container[6, 8] = ItemBuilder.from(Material.PAPER) + .name(Component.text("Next page")) + .asGuiItem { _, _ -> + pagerState.next() + } + } + } + } + + gui.open(sender) + return true + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/examples/Static.kt b/examples/paper/kotlin/src/main/kotlin/examples/Static.kt new file mode 100644 index 00000000..5a129bf0 --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/examples/Static.kt @@ -0,0 +1,64 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples + +import dev.triumphteam.gui.kotlin.set +import dev.triumphteam.gui.paper.builder.item.ItemBuilder +import dev.triumphteam.gui.paper.kotlin.builder.buildGui +import dev.triumphteam.gui.paper.kotlin.builder.chestContainer +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +public class Static : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array?): Boolean { + if (sender !is Player) return false + + val gui = buildGui { + // If not set, defaults to chest - 1 row + containerType = chestContainer { + rows = 6 + } + + // Simple title for the gui + title(Component.text("Static Gui")) + + // A stateless component, we don't care about the item updating, just want the click action + statelessComponent { container -> + container[1, 1] = ItemBuilder.from(Material.PAPER) + .name(Component.text("My Paper")) + .asGuiItem { player, _ -> + player.sendMessage("You have clicked on the paper item!") + } + } + } + + gui.open(sender) + return true + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/examples/UpdatingTitle.kt b/examples/paper/kotlin/src/main/kotlin/examples/UpdatingTitle.kt new file mode 100644 index 00000000..e49c2c50 --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/examples/UpdatingTitle.kt @@ -0,0 +1,85 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example.examples + +import dev.triumphteam.gui.kotlin.set +import dev.triumphteam.gui.paper.builder.item.ItemBuilder +import dev.triumphteam.gui.paper.kotlin.builder.buildGui +import dev.triumphteam.nova.getValue +import dev.triumphteam.nova.mutableStateOf +import dev.triumphteam.nova.setValue +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +public class UpdatingTitle : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + if (sender !is Player) return false + + val gui = buildGui { + + // Firstly, we create a state that'll be used by the title + // It's created here and not directly in the "remember" because it needs to be accessible in the component too + val titleState = mutableStateOf(Component.text("Original title")) + + // Delegate the value, so it's nicer to use it + // This isn't really necessary + var title by titleState + + title { + // Tell the title to remember the state + remember(titleState) + // Render the title's value + render { title } + } + + // Since we don't care about updating the items, we can just use a stateless component + statelessComponent { container -> + // First item to change the title to PAPER + container[1, 1] = ItemBuilder.from(Material.PAPER) + .name(Component.text("Change title to 'PAPER TITLE'!")) + .asGuiItem { _, _ -> + title = Component.text("PAPER TITLE") // Triggers an update + } + + // Second item to change title to COOKIE + container[1, 2] = ItemBuilder.from(Material.COOKIE) + .name(Component.text("Change title to 'COOKIE TITLE'!")) + .asGuiItem { _, _ -> + title = Component.text("COOKIE TITLE") // Triggers an update + } + } + + // Important to remember; due to by default states using "StateMutabilityPolicy.StructuralEquality" + // it means that if the title is the same as the new value it'll **NOT** trigger an update + } + + gui.open(sender) + return true + } +} diff --git a/examples/paper/kotlin/src/main/kotlin/package-info.kt b/examples/paper/kotlin/src/main/kotlin/package-info.kt new file mode 100644 index 00000000..114b949f --- /dev/null +++ b/examples/paper/kotlin/src/main/kotlin/package-info.kt @@ -0,0 +1,24 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.example diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts deleted file mode 100644 index 1ef76787..00000000 --- a/fabric/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - kotlin("jvm") version "1.4.32" -} - -dependencies { - implementation(project(":triumph-gui")) - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") -} - -tasks { - withType { - kotlinOptions { - jvmTarget = "1.8" - javaParameters = true - } - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..107d1282 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +kotlin.stdlib.default.dependency=false +version=4.0.0-SNAPSHOT +group=dev.triumphteam diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..2fff4920 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,56 @@ +[versions] +# kotlin +kotlin = "2.0.21" +coroutines = "1.6.4" + +license = "0.16.1" + +# Core +annotations = "24.1.0" +guava = "33.2.0-jre" +logger = "2.0.13" +nova = "1.0.0-SNAPSHOT" + +# Minecraft +adventure = "4.16.0" +paper = "1.21.1-R0.1-SNAPSHOT" +mojang-auth = "6.0.57" + +# Build +run-paper = "2.3.1" +resource-factory = "1.2.0" +shadow = "8.3.3" + +[libraries] +# Core +annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +logger = { module = "org.slf4j:slf4j-api", version.ref = "logger" } +nova = { module = "dev.triumphteam:nova", version.ref = "nova" } +nova-kotlin = { module = "dev.triumphteam:nova-kotlin", version.ref = "nova" } + +# Minecraft +paper = { module = "io.papermc.paper:paper-api", version.ref = "paper" } +adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" } +mojang-auth = { module = "com.mojang:authlib", version.ref = "mojang-auth" } + +# Kotlin +coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } + +# Build +build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +build-license = { module = "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin", version.ref = "license" } +build-run-paper = { module = "xyz.jpenilla:run-task", version.ref = "run-paper" } +build-resource-factory = { module = "xyz.jpenilla:resource-factory", version.ref = "resource-factory" } +build-shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" } + +[bundles] +build = [ + "build-kotlin", + "build-license", +] +paper-examples = [ + "build-run-paper", + "build-resource-factory", + "build-shadow", +] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e411586a..48c0a02c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kotlin/build.gradle.kts b/kotlin/build.gradle.kts index 692f0a39..7419696e 100644 --- a/kotlin/build.gradle.kts +++ b/kotlin/build.gradle.kts @@ -1,19 +1,12 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { - kotlin("jvm") version "1.9.10" + id("gui.base") + id("gui.library") } dependencies { - implementation(project(":triumph-gui")) - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") -} + api(libs.nova.kotlin) + api(projects.triumphGuiCore) -tasks { - withType { - kotlinOptions { - jvmTarget = "1.8" - javaParameters = true - } - } + compileOnly(kotlin("stdlib")) + compileOnly(libs.adventure.api) } diff --git a/kotlin/src/main/kotlin/ContainerExt.kt b/kotlin/src/main/kotlin/ContainerExt.kt new file mode 100644 index 00000000..776e54c6 --- /dev/null +++ b/kotlin/src/main/kotlin/ContainerExt.kt @@ -0,0 +1,41 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.kotlin + +import dev.triumphteam.gui.container.GuiContainer +import dev.triumphteam.gui.item.GuiItem +import dev.triumphteam.gui.layout.GuiLayout +import dev.triumphteam.gui.slot.Slot + +public operator fun

GuiContainer.set(row: Int, column: Int, item: GuiItem): Unit = + setItem(row, column, item) + +public operator fun

GuiContainer.set(slot: Slot, item: GuiItem): Unit = + setItem(slot, item) + +public operator fun

GuiContainer.set(slot: Int, item: GuiItem): Unit = + setItem(slot, item) + +public operator fun

GuiContainer.set(layout: GuiLayout, item: GuiItem): Unit = + fill(layout, item) diff --git a/kotlin/src/main/kotlin/ScrollerStateExt.kt b/kotlin/src/main/kotlin/ScrollerStateExt.kt new file mode 100644 index 00000000..e10e4dd0 --- /dev/null +++ b/kotlin/src/main/kotlin/ScrollerStateExt.kt @@ -0,0 +1,21 @@ +package dev.triumphteam.gui.kotlin + +import dev.triumphteam.gui.layout.GuiLayout +import dev.triumphteam.gui.state.pagination.PagerState +import dev.triumphteam.gui.state.pagination.ScrollerState +import dev.triumphteam.gui.state.pagination.SimplePagerState +import dev.triumphteam.gui.state.pagination.SimpleScrollerState + +public fun pagerState( + startPage: Int = 1, + elements: List, + layout: GuiLayout, +): PagerState = + SimplePagerState(startPage, elements, layout) + +public fun scrollerState( + steps: Int, + elements: List, + layout: GuiLayout, +): ScrollerState = + SimpleScrollerState(steps, elements, layout) diff --git a/kotlin/src/main/kotlin/SlotExt.kt b/kotlin/src/main/kotlin/SlotExt.kt new file mode 100644 index 00000000..3a8aa187 --- /dev/null +++ b/kotlin/src/main/kotlin/SlotExt.kt @@ -0,0 +1,31 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.kotlin + +import dev.triumphteam.gui.slot.Slot + +public operator fun Slot.component1(): Int = row +public operator fun Slot.component2(): Int = column + +public fun slot(row: Int, column: Int): Slot = Slot(row, column) diff --git a/kotlin/src/main/kotlin/builder/AbstractKotlinGuiBuilder.kt b/kotlin/src/main/kotlin/builder/AbstractKotlinGuiBuilder.kt new file mode 100644 index 00000000..a175b6b4 --- /dev/null +++ b/kotlin/src/main/kotlin/builder/AbstractKotlinGuiBuilder.kt @@ -0,0 +1,89 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.kotlin.builder + +import dev.triumphteam.gui.BaseGui +import dev.triumphteam.gui.actions.GuiCloseAction +import dev.triumphteam.gui.builder.BaseGuiBuilder +import dev.triumphteam.gui.click.handler.ClickHandler +import dev.triumphteam.gui.component.functional.FunctionalGuiComponent +import dev.triumphteam.gui.component.functional.FunctionalGuiComponentRender +import dev.triumphteam.gui.component.functional.SimpleFunctionalGuiComponent +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer +import dev.triumphteam.gui.container.type.GuiContainerType +import dev.triumphteam.gui.title.functional.FunctionalGuiTitle +import dev.triumphteam.gui.title.functional.SimpleFunctionalGuiTitle +import net.kyori.adventure.text.Component +import kotlin.time.Duration + +public abstract class AbstractKotlinGuiBuilder, P, G : BaseGui

, I, C : GuiContainerType>( + @PublishedApi internal val backing: B, +) { + + public var containerType: C? = null + set(value) { + field = value + value?.let(backing::containerType) + } + + public var spamPreventionDuration: Duration? = null + set(value) { + field = value + backing.spamPreventionDuration(value?.inWholeMilliseconds ?: -1) + } + + public var clickHandler: ClickHandler

? = null + set(value) { + field = value + value?.let(backing::clickHandler) + } + + public var componentRenderer: GuiComponentRenderer? = null + set(value) { + field = value + value?.let(backing::componentRenderer) + } + + public fun title(title: Component) { + backing.title(title) + } + + public inline fun title(block: FunctionalGuiTitle.() -> Unit) { + backing.title(SimpleFunctionalGuiTitle().apply(block).asGuiTitle()) + } + + public fun statelessComponent(render: FunctionalGuiComponentRender) { + backing.statelessComponent(render) + } + + public inline fun component(block: FunctionalGuiComponent.() -> Unit) { + backing.component(SimpleFunctionalGuiComponent().apply(block).asGuiComponent()) + } + + public fun onClose(block: GuiCloseAction) { + backing.onClose(block) + } + + protected fun backing(): B = backing +} diff --git a/kotlin/src/main/kotlin/package-info.kt b/kotlin/src/main/kotlin/package-info.kt new file mode 100644 index 00000000..dcf00b02 --- /dev/null +++ b/kotlin/src/main/kotlin/package-info.kt @@ -0,0 +1,24 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.kotlin diff --git a/paper/kotlin/build.gradle.kts b/paper/kotlin/build.gradle.kts new file mode 100644 index 00000000..b90d73ef --- /dev/null +++ b/paper/kotlin/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("gui.base") + id("gui.library") +} + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + compileOnly(libs.paper) + compileOnly(kotlin("stdlib")) + + api(projects.triumphGuiPaper) + api(projects.triumphGuiKotlin) +} diff --git a/paper/kotlin/src/main/kotlin/builder/Container.kt b/paper/kotlin/src/main/kotlin/builder/Container.kt new file mode 100644 index 00000000..cdd1c377 --- /dev/null +++ b/paper/kotlin/src/main/kotlin/builder/Container.kt @@ -0,0 +1,35 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.kotlin.builder + +import dev.triumphteam.gui.paper.container.type.ChestContainerType +import dev.triumphteam.gui.paper.container.type.PaperContainerType + +public class ContainerBuilder @PublishedApi internal constructor() { + public var rows: Int = 1 +} + +public inline fun chestContainer(block: ContainerBuilder.() -> Unit): PaperContainerType { + return ChestContainerType(ContainerBuilder().apply(block).rows) +} diff --git a/paper/kotlin/src/main/kotlin/builder/PaperKotlinGuiBuilder.kt b/paper/kotlin/src/main/kotlin/builder/PaperKotlinGuiBuilder.kt new file mode 100644 index 00000000..b1d4d626 --- /dev/null +++ b/paper/kotlin/src/main/kotlin/builder/PaperKotlinGuiBuilder.kt @@ -0,0 +1,45 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.kotlin.builder + +import dev.triumphteam.gui.kotlin.builder.AbstractKotlinGuiBuilder +import dev.triumphteam.gui.paper.Gui +import dev.triumphteam.gui.paper.builder.gui.PaperGuiBuilder +import dev.triumphteam.gui.paper.container.type.ChestContainerType +import dev.triumphteam.gui.paper.container.type.PaperContainerType +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack + +public class PaperKotlinGuiBuilder @PublishedApi internal constructor() : + AbstractKotlinGuiBuilder( + PaperGuiBuilder(ChestContainerType(1)) + ) { + + @PublishedApi + internal fun build(): Gui = backing().build() +} + +public inline fun buildGui(builder: PaperKotlinGuiBuilder.() -> Unit): Gui { + return PaperKotlinGuiBuilder().apply(builder).build() +} diff --git a/paper/kotlin/src/main/kotlin/package-info.kt b/paper/kotlin/src/main/kotlin/package-info.kt new file mode 100644 index 00000000..01b121ae --- /dev/null +++ b/paper/kotlin/src/main/kotlin/package-info.kt @@ -0,0 +1,24 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.kotlin diff --git a/paper/paper/build.gradle.kts b/paper/paper/build.gradle.kts new file mode 100644 index 00000000..3d9f853e --- /dev/null +++ b/paper/paper/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("gui.base") + id("gui.library") +} + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + api(projects.triumphGuiCore) + compileOnly(libs.paper) + compileOnly(libs.mojang.auth) +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/Gui.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/Gui.java new file mode 100644 index 00000000..7c6f3cba --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/Gui.java @@ -0,0 +1,115 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper; + +import dev.triumphteam.gui.BaseGui; +import dev.triumphteam.gui.actions.GuiCloseAction; +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer; +import dev.triumphteam.gui.paper.builder.gui.PaperGuiBuilder; +import dev.triumphteam.gui.paper.container.type.ChestContainerType; +import dev.triumphteam.gui.paper.container.type.PaperContainerType; +import dev.triumphteam.gui.title.GuiTitle; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * The GUI implementation for Paper servers. + */ +public final class Gui implements BaseGui { + + static { + PaperGuiListener.register(); + } + + private final GuiTitle title; + private final List> components; + private final List closeActions; + private final PaperContainerType containerType; + private final GuiComponentRenderer componentRenderer; + private final ClickHandler clickHandler; + private final long spamPreventionDuration; + + public Gui( + final @NotNull GuiTitle title, + final @NotNull List> components, + final @NotNull List closeActions, + final @NotNull PaperContainerType containerType, + final @NotNull GuiComponentRenderer componentRenderer, + final @NotNull ClickHandler clickHandler, + final long spamPreventionDuration + ) { + this.title = title; + this.components = components; + this.closeActions = closeActions; + this.containerType = containerType; + this.componentRenderer = componentRenderer; + this.clickHandler = clickHandler; + this.spamPreventionDuration = spamPreventionDuration; + } + + /** + * Create a new {@link PaperGuiBuilder} to create a new {@link Gui}. + * + * @param type The {@link PaperContainerType} to be used. + * @return A new {@link PaperGuiBuilder}. + */ + @Contract("_ -> new") + public static PaperGuiBuilder of(final @NotNull PaperContainerType type) { + return new PaperGuiBuilder(type); + } + + /** + * Create a new {@link PaperGuiBuilder} to create a new {@link Gui}. + * This factory will default to using a {@link ChestContainerType}. + * + * @param rows The rows of the {@link ChestContainerType}. + * @return A new {@link PaperGuiBuilder}. + */ + @Contract("_ -> new") + public static PaperGuiBuilder of(final int rows) { + return new PaperGuiBuilder(new ChestContainerType(rows)); + } + + @Override + public void open(final @NotNull Player player) { + final var view = new PaperGuiView( + player, + title, + containerType, + components, + closeActions, + componentRenderer, + clickHandler, + spamPreventionDuration + ); + + view.open(); + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiListener.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiListener.java new file mode 100644 index 00000000..4b5a8f17 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiListener.java @@ -0,0 +1,107 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper; + +import dev.triumphteam.gui.actions.GuiCloseAction; +import dev.triumphteam.gui.click.ClickContext; +import dev.triumphteam.gui.click.GuiClick; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class PaperGuiListener implements Listener { + + public static void register() { + // Auto-register listener if none was registered yet + PaperGuiSettings.get().register(JavaPlugin.getProvidingPlugin(PaperGuiListener.class)); + } + + @EventHandler + public void onGuiClick(final @NotNull InventoryClickEvent event) { + final var view = convertHolder(event.getInventory().getHolder()); + if (view == null) return; + + event.setCancelled(true); + view.processClick(new ClickContext(mapClick(event.getClick()), event.getSlot())); + } + + @EventHandler + public void onGuiDrag(final @NotNull InventoryDragEvent event) { + final var view = convertHolder(event.getInventory().getHolder()); + if (view == null) return; + + event.setCancelled(true); + } + + @EventHandler + public void onGuiOpen(final @NotNull InventoryOpenEvent event) { + final var view = convertHolder(event.getInventory().getHolder()); + if (view == null) return; + + if (!view.isUpdating()) return; + event.titleOverride(view.getTitle()); + } + + @EventHandler + public void onGuiClose(final @NotNull InventoryCloseEvent event) { + final var view = convertHolder(event.getInventory().getHolder()); + if (view == null) return; + + if (view.isUpdating()) return; + view.getCloseActions().forEach(GuiCloseAction::onClose); + } + + private @Nullable PaperGuiView convertHolder(final @Nullable InventoryHolder holder) { + if (holder == null) return null; + if (holder instanceof PaperGuiView paperGuiView) return paperGuiView; + return null; + } + + private @NotNull GuiClick mapClick(final @NotNull ClickType clickType) { + return switch (clickType) { + case ClickType.LEFT -> GuiClick.LEFT; + case ClickType.SHIFT_LEFT -> GuiClick.SHIFT_LEFT; + case ClickType.RIGHT -> GuiClick.RIGHT; + case ClickType.SHIFT_RIGHT -> GuiClick.SHIFT_RIGHT; + case ClickType.WINDOW_BORDER_LEFT -> GuiClick.WINDOW_BORDER_LEFT; + case ClickType.WINDOW_BORDER_RIGHT -> GuiClick.WINDOW_BORDER_RIGHT; + case ClickType.MIDDLE -> GuiClick.MIDDLE; + case ClickType.NUMBER_KEY -> GuiClick.NUMBER_KEY; + case ClickType.DOUBLE_CLICK -> GuiClick.DOUBLE_CLICK; + case ClickType.DROP -> GuiClick.DROP; + case ClickType.CONTROL_DROP -> GuiClick.CONTROL_DROP; + case ClickType.CREATIVE -> GuiClick.CREATIVE; + case ClickType.SWAP_OFFHAND -> GuiClick.SWAP_OFFHAND; + case ClickType.UNKNOWN -> GuiClick.UNKNOWN; + }; + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiSettings.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiSettings.java new file mode 100644 index 00000000..29472cb1 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiSettings.java @@ -0,0 +1,64 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper; + +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.settings.GuiSettings; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public final class PaperGuiSettings extends GuiSettings { + + private static final PaperGuiSettings INSTANCE = new PaperGuiSettings(); + + private boolean listenerRegistered = false; + private Plugin plugin = null; + + private PaperGuiSettings() {} + + public static PaperGuiSettings get() { + return INSTANCE; + } + + public PaperGuiSettings register(final @NotNull Plugin plugin) { + // Only register listener once + if (this.listenerRegistered) return this; + + this.plugin = plugin; + Bukkit.getServer().getPluginManager().registerEvents(new PaperGuiListener(), plugin); + this.listenerRegistered = true; + return this; + } + + public @NotNull Plugin getPlugin() { + if (plugin == null) { + throw new TriumphGuiException("An error occurred while attempting to get the plugin instance."); + } + + return plugin; + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiView.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiView.java new file mode 100644 index 00000000..29e54461 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/PaperGuiView.java @@ -0,0 +1,117 @@ +/** + * MIT License + *

+ * Copyright (c) 2024 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper; + +import dev.triumphteam.gui.AbstractGuiView; +import dev.triumphteam.gui.actions.GuiCloseAction; +import dev.triumphteam.gui.click.handler.ClickHandler; +import dev.triumphteam.gui.click.processor.ClickProcessor; +import dev.triumphteam.gui.component.GuiComponent; +import dev.triumphteam.gui.component.renderer.GuiComponentRenderer; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.item.RenderedGuiItem; +import dev.triumphteam.gui.paper.container.type.PaperContainerType; +import dev.triumphteam.gui.title.GuiTitle; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public final class PaperGuiView extends AbstractGuiView implements InventoryHolder { + + private final PaperContainerType containerType; + private Inventory inventory = null; + + public PaperGuiView( + final @NotNull Player player, + final @NotNull GuiTitle title, + final @NotNull PaperContainerType containerType, + final @NotNull List> components, + final @NotNull List closeActions, + final @NotNull GuiComponentRenderer componentRenderer, + final @NotNull ClickHandler clickHandler, + final long spamPreventionDuration + ) { + super(player, title, components, closeActions, containerType, componentRenderer, clickHandler, new ClickProcessor<>(spamPreventionDuration)); + this.containerType = containerType; + } + + @Override + public void openInventory(final boolean updating) { + if (inventory == null) { + this.inventory = containerType.createInventory(this, getTitle()); + } + + final var viewer = viewer(); + viewer.getScheduler().run(PaperGuiSettings.get().getPlugin(), (task) -> { + setUpdating(true); + viewer.openInventory(inventory); + setUpdating(false); + }, null); + } + + @Override + public void close() { + final var viewer = viewer(); + viewer.getScheduler().runDelayed(PaperGuiSettings.get().getPlugin(), (task) -> viewer.closeInventory(), null, 2L); + } + + @Override + public @NotNull Inventory getInventory() { + checkInventory(); + return inventory; + } + + @Override + protected void clearSlot(final int slot) { + checkInventory(); + inventory.clear(slot); + } + + @Override + public @NotNull String viewerName() { + return viewer().getName(); + } + + @Override + public @NotNull UUID viewerUuid() { + return viewer().getUniqueId(); + } + + @Override + protected void populateInventory(final @NotNull Map> renderedItems) { + renderedItems.forEach((slot, item) -> inventory.setItem(slot, item.item())); + } + + private void checkInventory() throws TriumphGuiException { + if (inventory == null) { + throw new TriumphGuiException("Tried to get inventory before it was available."); + } + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/gui/PaperGuiBuilder.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/gui/PaperGuiBuilder.java new file mode 100644 index 00000000..c81a63f7 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/gui/PaperGuiBuilder.java @@ -0,0 +1,52 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.builder.gui; + +import dev.triumphteam.gui.builder.BaseGuiBuilder; +import dev.triumphteam.gui.paper.Gui; +import dev.triumphteam.gui.paper.PaperGuiSettings; +import dev.triumphteam.gui.paper.container.type.PaperContainerType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class PaperGuiBuilder extends BaseGuiBuilder { + + public PaperGuiBuilder(final @NotNull PaperContainerType containerType) { + super(PaperGuiSettings.get(), containerType); + } + + @Override + public Gui build() { + return new Gui( + getTitle(), + getComponents(), + getCloseActions(), + getContainerType(), + getComponentRenderer(), + getClickHandler(), + getSpamPreventionDuration() + ); + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/AbstractItemBuilder.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/AbstractItemBuilder.java new file mode 100644 index 00000000..5941fd90 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/AbstractItemBuilder.java @@ -0,0 +1,366 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.builder.item; + +import com.google.common.base.Preconditions; +import dev.triumphteam.gui.click.action.EmptyGuiClickAction; +import dev.triumphteam.gui.click.action.GuiClickAction; +import dev.triumphteam.gui.click.action.RunnableGuiClickAction; +import dev.triumphteam.gui.item.GuiItem; +import dev.triumphteam.gui.item.items.SimpleGuiItem; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +@SuppressWarnings("unchecked") +public abstract class AbstractItemBuilder> { + + private ItemStack itemStack; + private ItemMeta meta; + + protected AbstractItemBuilder(final @NotNull ItemStack itemStack) { + Preconditions.checkNotNull(itemStack, "Item can't be null!"); + + this.itemStack = itemStack; + meta = itemStack.hasItemMeta() ? itemStack.getItemMeta() : Bukkit.getItemFactory().getItemMeta(itemStack.getType()); + } + + /** + * Sets the display name of the item using {@link Component} + * + * @param name The {@link Component} name + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B name(final @NotNull Component name) { + if (meta == null) return (B) this; + + meta.displayName(name); + return (B) this; + } + + /** + * Sets the amount of items + * + * @param amount the amount of items + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B amount(final int amount) { + itemStack.setAmount(amount); + return (B) this; + } + + /** + * Set the lore lines of an item + * + * @param lore Lore lines as varargs + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B lore(final @Nullable Component @NotNull ... lore) { + return lore(Arrays.asList(lore)); + } + + /** + * Set the lore lines of an item + * + * @param lore A {@link List} with the lore lines + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B lore(final @NotNull List<@Nullable Component> lore) { + if (meta == null) return (B) this; + meta.lore(lore); + return (B) this; + } + + /** + * Set the custom data model of an item + * + * @param modelData the data of the model to be set + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B model(final int modelData) { + if (meta == null) return (B) this; + meta.setCustomModelData(modelData); + return (B) this; + } + + /** + * Makes the {@link ItemStack} glow + * + * @return {@link B} + */ + @NotNull + @Contract(" -> this") + public B glow() { + return glow(true); + } + + /** + * Adds or removes the {@link ItemStack} glow + * + * @param glow Should the item glow + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B glow(boolean glow) { + if (glow) { + meta.addEnchant(Enchantment.LURE, 1, false); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + return (B) this; + } + + for (final Enchantment enchantment : meta.getEnchants().keySet()) { + meta.removeEnchant(enchantment); + } + + return (B) this; + } + + /** + * Consumer for applying {@link PersistentDataContainer} to the item + * + * @param consumer The {@link Consumer} with the PDC + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B pdc(@NotNull final Consumer consumer) { + consumer.accept(meta.getPersistentDataContainer()); + return (B) this; + } + + /** + * Enchants the {@link ItemStack} with the specified {@link Enchantment} and level + * + * @param enchantment The {@link Enchantment} to add + * @param level The level of the {@link Enchantment} + * @param ignoreLevelRestriction If should or not ignore it + * @return {@link B} + */ + @NotNull + @Contract("_, _, _ -> this") + public B enchant(@NotNull final Enchantment enchantment, final int level, final boolean ignoreLevelRestriction) { + meta.addEnchant(enchantment, level, ignoreLevelRestriction); + return (B) this; + } + + /** + * Enchants the {@link ItemStack} with the specified {@link Enchantment} and level + * + * @param enchantment The {@link Enchantment} to add + * @param level The level of the {@link Enchantment} + * @return {@link B} + */ + @NotNull + @Contract("_, _ -> this") + public B enchant(@NotNull final Enchantment enchantment, final int level) { + return enchant(enchantment, level, true); + } + + /** + * Enchants the {@link ItemStack} with the specified {@link Enchantment} + * + * @param enchantment The {@link Enchantment} to add + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B enchant(@NotNull final Enchantment enchantment) { + return enchant(enchantment, 1, true); + } + + /** + * Enchants the {@link ItemStack} with the specified map where the value + * is the level of the key's enchantment + * + * @param enchantments Enchantments to add + * @param ignoreLevelRestriction If level restriction should be ignored + * @return {@link B} + * @since 3.1.2 + */ + @NotNull + @Contract("_, _ -> this") + public B enchant(@NotNull final Map enchantments, final boolean ignoreLevelRestriction) { + enchantments.forEach((enchantment, level) -> this.enchant(enchantment, level, ignoreLevelRestriction)); + return (B) this; + } + + /** + * Enchants the {@link ItemStack} with the specified map where the value + * is the level of the key's enchantment + * + * @param enchantments Enchantments to add + * @return {@link B} + * @since 3.1.2 + */ + @NotNull + @Contract("_ -> this") + public B enchant(@NotNull final Map enchantments) { + return enchant(enchantments, true); + } + + /** + * Disenchants a certain {@link Enchantment} from the {@link ItemStack} + * + * @param enchantment The {@link Enchantment} to remove + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B disenchant(@NotNull final Enchantment enchantment) { + itemStack.removeEnchantment(enchantment); + return (B) this; + } + + /** + * Add an {@link ItemFlag} to the item + * + * @param flags The {@link ItemFlag} to add + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B flags(@NotNull final ItemFlag... flags) { + meta.addItemFlags(flags); + return (B) this; + } + + /** + * Makes the {@link ItemStack} unbreakable + * + * @return {@link ItemBuilder} + */ + @NotNull + @Contract(" -> this") + public B unbreakable() { + return unbreakable(true); + } + + /** + * Sets the item as unbreakable + * + * @param unbreakable If should or not be unbreakable + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> this") + public B unbreakable(boolean unbreakable) { + meta.setUnbreakable(unbreakable); + return (B) this; + } + + /** + * Sets the item meta of the item + * + * @param meta The {@link ItemMeta} to set + * @return {@link B} + */ + @NotNull + @Contract("_ -> this") + public B meta(ItemMeta meta) { + this.meta = meta; + return (B) this; + } + + /** + * Returns the {@link ItemMeta} of the item + * + * @return The {@link ItemMeta} of the item + */ + @NotNull + @Contract(" -> new") + public ItemMeta meta() { + return meta; + } + + /** + * Returns the {@link ItemStack} of the item + * + * @return The {@link ItemStack} of the item + */ + @NotNull + @Contract(" -> new") + public ItemStack asItemStack() { + itemStack.setItemMeta(meta); + return itemStack; + } + + /** + * Returns the {@link GuiItem} of the item + * + * @param action The {@link GuiClickAction} to set + * @return The {@link GuiItem} of the item + */ + @NotNull + @Contract("_ -> new") + public GuiItem asGuiItem(final @NotNull RunnableGuiClickAction action) { + return new SimpleGuiItem<>(asItemStack(), action); + } + + /** + * Returns the {@link GuiItem} of the item + * + * @param action The {@link GuiClickAction} to set + * @return The {@link GuiItem} of the item + */ + @NotNull + @Contract("_ -> new") + public GuiItem asGuiItem(final @NotNull GuiClickAction action) { + return new SimpleGuiItem<>(asItemStack(), action); + } + + /** + * Returns the {@link GuiItem} of the item + * + * @return The {@link GuiItem} of the item + */ + @NotNull + @Contract(" -> new") + public GuiItem asGuiItem() { + return new SimpleGuiItem<>(asItemStack(), new EmptyGuiClickAction<>()); + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/ItemBuilder.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/ItemBuilder.java new file mode 100644 index 00000000..004c37d8 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/ItemBuilder.java @@ -0,0 +1,43 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.builder.item; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class ItemBuilder extends AbstractItemBuilder { + + public ItemBuilder(final @NotNull ItemStack itemStack) { + super(itemStack); + } + + public static ItemBuilder from(final @NotNull ItemStack itemStack) { + return new ItemBuilder(itemStack); + } + + public static ItemBuilder from(final @NotNull Material material) { + return new ItemBuilder(new ItemStack(material)); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/SkullBuilder.java similarity index 58% rename from core/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java rename to paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/SkullBuilder.java index f6cec8cb..9d111475 100644 --- a/core/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/builder/item/SkullBuilder.java @@ -1,33 +1,9 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.builder.item; +package dev.triumphteam.gui.paper.builder.item; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; -import dev.triumphteam.gui.components.exception.GuiException; -import dev.triumphteam.gui.components.util.SkullUtil; -import dev.triumphteam.gui.components.util.VersionHelper; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.paper.util.SkullUtil; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; @@ -42,11 +18,7 @@ import java.net.URL; import java.util.UUID; -/** - * New builder for skull only, created to separate the specific features for skulls - * Soon I'll add more useful features to this builder - */ -public final class SkullBuilder extends BaseItemBuilder { +public final class SkullBuilder extends AbstractItemBuilder { private static final Field PROFILE_FIELD; @@ -72,7 +44,7 @@ public final class SkullBuilder extends BaseItemBuilder { SkullBuilder(final @NotNull ItemStack itemStack) { super(itemStack); if (!SkullUtil.isPlayerSkull(itemStack)) { - throw new GuiException("SkullBuilder requires the material to be a PLAYER_HEAD/SKULL_ITEM!"); + throw new TriumphGuiException("SkullBuilder requires the material to be a PLAYER_HEAD/SKULL_ITEM!"); } } @@ -86,16 +58,17 @@ public final class SkullBuilder extends BaseItemBuilder { @NotNull @Contract("_, _ -> this") public SkullBuilder texture(@NotNull final String texture, @NotNull final UUID profileId) { - if (!SkullUtil.isPlayerSkull(getItemStack())) return this; + if (!SkullUtil.isPlayerSkull(asItemStack())) return this; - if (VersionHelper.IS_PLAYER_PROFILE_API) { + int version = Integer.parseInt(Bukkit.getServer().getMinecraftVersion().replace(".", "")); + if (version >= 1201) { final String textureUrl = SkullUtil.getSkinUrl(texture); if (textureUrl == null) { return this; } - final SkullMeta skullMeta = (SkullMeta) getMeta(); + final SkullMeta skullMeta = (SkullMeta) asItemStack().getItemMeta(); final PlayerProfile profile = Bukkit.createPlayerProfile(profileId, ""); final PlayerTextures textures = profile.getTextures(); @@ -108,7 +81,7 @@ public SkullBuilder texture(@NotNull final String texture, @NotNull final UUID p profile.setTextures(textures); skullMeta.setOwnerProfile(profile); - setMeta(skullMeta); + meta(skullMeta); return this; } @@ -116,7 +89,7 @@ public SkullBuilder texture(@NotNull final String texture, @NotNull final UUID p return this; } - final SkullMeta skullMeta = (SkullMeta) getMeta(); + final SkullMeta skullMeta = (SkullMeta) meta(); final GameProfile profile = new GameProfile(profileId, ""); profile.getProperties().put("textures", new Property("textures", texture)); @@ -126,7 +99,7 @@ public SkullBuilder texture(@NotNull final String texture, @NotNull final UUID p ex.printStackTrace(); } - setMeta(skullMeta); + meta(skullMeta); return this; } @@ -151,17 +124,13 @@ public SkullBuilder texture(@NotNull final String texture) { @NotNull @Contract("_ -> this") public SkullBuilder owner(@NotNull final OfflinePlayer player) { - if (!SkullUtil.isPlayerSkull(getItemStack())) return this; + if (!SkullUtil.isPlayerSkull(asItemStack())) return this; - final SkullMeta skullMeta = (SkullMeta) getMeta(); + final SkullMeta skullMeta = (SkullMeta) meta(); - if (VersionHelper.IS_SKULL_OWNER_LEGACY) { - skullMeta.setOwner(player.getName()); - } else { - skullMeta.setOwningPlayer(player); - } + skullMeta.setOwningPlayer(player); - setMeta(skullMeta); + meta(skullMeta); return this; } diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/ChestContainerType.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/ChestContainerType.java new file mode 100644 index 00000000..1217ca6d --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/ChestContainerType.java @@ -0,0 +1,75 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.container.type; + +import dev.triumphteam.gui.container.type.GuiContainerType; +import dev.triumphteam.gui.exception.TriumphGuiException; +import dev.triumphteam.gui.slot.Slot; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public final class ChestContainerType implements PaperContainerType { + + private static final int LOWER_LIMIT = 0; + private final int rows; + private final int upperLimit; + + public ChestContainerType(final int rows) { + this.rows = rows; + this.upperLimit = rows * 9; + } + + public static @NotNull GuiContainerType of(final int rows) { + return new ChestContainerType(rows); + } + + @Override + public int mapSlot(final @NotNull Slot slot) { + final var realSlot = (slot.column() + (slot.row() - 1) * 9) - 1; + + if (realSlot < LOWER_LIMIT || realSlot > upperLimit) { + throw new TriumphGuiException( + "Invalid slot (" + slot.row() + ", " + slot.column() + "). Valid range is (1, 1) to (" + rows + ", 9)." + ); + } + + return realSlot; + } + + @Override + public @NotNull Slot mapSlot(final int slot) { + return Slot.of(slot / 9 + 1, slot % 9 + 1); + } + + @Override + public @NotNull Inventory createInventory( + final @NotNull InventoryHolder holder, + final @NotNull Component title + ) { + return Bukkit.createInventory(holder, upperLimit, title); + } +} diff --git a/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/PaperContainerType.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/PaperContainerType.java new file mode 100644 index 00000000..d62f67b6 --- /dev/null +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/container/type/PaperContainerType.java @@ -0,0 +1,39 @@ +/** + * MIT License + * + * Copyright (c) 2024 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.paper.container.type; + +import dev.triumphteam.gui.container.type.GuiContainerType; +import net.kyori.adventure.text.Component; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public interface PaperContainerType extends GuiContainerType { + + @NotNull + Inventory createInventory( + final @NotNull InventoryHolder holder, + final @NotNull Component title + ); +} diff --git a/core/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java b/paper/paper/src/main/java/dev/triumphteam/gui/paper/util/SkullUtil.java similarity index 62% rename from core/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java rename to paper/paper/src/main/java/dev/triumphteam/gui/paper/util/SkullUtil.java index 36a10f7b..3628393e 100644 --- a/core/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java +++ b/paper/paper/src/main/java/dev/triumphteam/gui/paper/util/SkullUtil.java @@ -1,27 +1,4 @@ -/** - * MIT License - * - * Copyright (c) 2021 TriumphTeam - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package dev.triumphteam.gui.components.util; +package dev.triumphteam.gui.paper.util; import com.google.gson.Gson; import com.google.gson.JsonElement; @@ -46,10 +23,6 @@ public final class SkullUtil { * @return The correct SKULL {@link Material} */ private static Material getSkullMaterial() { - if (VersionHelper.IS_ITEM_LEGACY) { - return Material.valueOf("SKULL_ITEM"); - } - return Material.PLAYER_HEAD; } @@ -60,7 +33,7 @@ private static Material getSkullMaterial() { */ @SuppressWarnings("deprecation") public static ItemStack skull() { - return VersionHelper.IS_ITEM_LEGACY ? new ItemStack(SKULL, 1, (short) 3) : new ItemStack(SKULL); + return new ItemStack(SKULL); } /** @@ -71,10 +44,6 @@ public static ItemStack skull() { */ @SuppressWarnings("deprecation") public static boolean isPlayerSkull(@NotNull final ItemStack item) { - if (VersionHelper.IS_ITEM_LEGACY) { - return item.getType() == SKULL && item.getDurability() == (short) 3; - } - return item.getType() == SKULL; } @@ -109,4 +78,4 @@ public static String getSkinUrl(String base64Texture) { return url == null ? null : url.getAsString(); } -} +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index f4b738d8..db4ed912 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,34 @@ +import dev.triumphteam.root.includeProject + +dependencyResolutionManagement { + includeBuild("build-logic") + repositories.gradlePluginPortal() +} + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://repo.triumphteam.dev/releases") + } +} + rootProject.name = "triumph-gui" -include("core") -findProject(":core")?.name = "triumph-gui" +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +plugins { + id("dev.triumphteam.root.settings") version "0.0.14" +} + +listOf( + "core" to "core", + "kotlin" to "kotlin", + + // Platforms and their Kotlin versions + "paper/paper" to "paper", + "paper/kotlin" to "paper-kotlin", -listOf("kotlin").forEach { - include(it) - findProject(":$it")?.name = "triumph-gui-$it" -} \ No newline at end of file + // Example projects + "examples/paper/java" to "example-paper-java", + "examples/paper/kotlin" to "example-paper-kotlin", +).forEach(::includeProject)