Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions API/src/main/java/fr/maxlego08/menu/api/MenuPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import fr.maxlego08.menu.api.storage.StorageManager;
import fr.maxlego08.menu.api.utils.MetaUpdater;
import fr.maxlego08.menu.api.utils.toast.ToastHelper;
import fr.maxlego08.menu.api.utils.version.ClientVersionManager;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
Expand Down Expand Up @@ -261,6 +262,17 @@ public interface MenuPlugin extends Plugin {

DialogManager getDialogManager();

/**
* Returns the client version manager, used to know which Minecraft version a player's
* client is running. Never null, but may have no provider registered, in which case it
* reports the server version for every player.
*
* @return the client version manager
*/
default ClientVersionManager getClientVersionManager() {
return ClientVersionManager.SERVER_VERSION_ONLY;
}

ItemManager getItemManager();

AttributApplier getAttributApplier();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private void execute(@NotNull MenuPlugin plugin, @NotNull Player player, @NotNul
} catch (Exception exception) {
if (Configuration.enableDebug){
Logger.info("An error occurred while executing command: " + command);
exception.printStackTrace();
Logger.error(exception);
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fr.maxlego08.menu.api.engine.InventoryResult;
import fr.maxlego08.menu.api.enums.dialog.DialogType;
import fr.maxlego08.menu.api.pattern.Pattern;
import fr.maxlego08.menu.api.utils.DialogFallback;
import fr.maxlego08.menu.api.utils.OpenWithItem;
import fr.maxlego08.menu.api.utils.PaperMetaUpdater;
import fr.maxlego08.menu.api.utils.Placeholders;
Expand All @@ -18,6 +19,7 @@
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -68,6 +70,24 @@ public interface DialogInventory extends Inventory {

ZDialogInventoryBuild getBuild(Player player);

/**
* The inventory opened instead of this dialog when the player's client is below
* Minecraft 1.21.6 and therefore cannot render dialogs.
*
* @return the configured fallback, or null if none was configured
*/
@Nullable
default DialogFallback getFallbackInventory() {
return null;
}

/**
* @param fallbackInventory the fallback inventory, may be null
* @see #getFallbackInventory()
*/
default void setFallbackInventory(@Nullable DialogFallback fallbackInventory) {
}

/**
* @deprecated Use {@link ServerLinksDialogInventory#setExitActionButton(ActionButtonRecord)}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fr.maxlego08.menu.api.loader;

import fr.maxlego08.menu.zcore.logger.Logger;

import fr.maxlego08.menu.api.button.Button;
import fr.maxlego08.menu.api.button.DefaultButtonValue;
import org.bukkit.configuration.file.YamlConfiguration;
Expand Down Expand Up @@ -29,7 +31,7 @@ public Button load(@NonNull YamlConfiguration configuration, @NonNull String pat
}
return this.clazz.getDeclaredConstructor().newInstance();
} catch (Exception exception) {
exception.printStackTrace();
Logger.error(exception);
}
return null;
}
Expand Down
25 changes: 25 additions & 0 deletions API/src/main/java/fr/maxlego08/menu/api/utils/DialogFallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package fr.maxlego08.menu.api.utils;

/**
* The inventory opened in place of a dialog when the player's client is too old to
* render dialogs (below Minecraft 1.21.6).
*
* @param inventoryName the name of the inventory file, without its extension
* @param plugin the plugin owning the inventory, defaults to {@code zMenu}
* @param page the page to open, defaults to {@code 1}
*/
public record DialogFallback(String inventoryName, String plugin, int page) {

public DialogFallback(String inventoryName, String plugin, int page) {
this.inventoryName = inventoryName;
this.plugin = plugin == null || plugin.isEmpty() ? "zMenu" : plugin;
this.page = Math.max(1, page);
}

/**
* @return true if an inventory name was configured.
*/
public boolean isValid() {
return this.inventoryName != null && !this.inventoryName.isEmpty();
}
}
1 change: 1 addition & 0 deletions API/src/main/java/fr/maxlego08/menu/api/utils/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public enum Message implements IMessage {
DIALOG_OPEN_SUCCESS_OTHER("&aYou have just opened the dialog &f%name%&a to the &3%player%&a."),
DIALOG_OPEN_ERROR_NOT_FOUND("&cImpossible to find the dialog &f%name%&c."),
DIALOG_OPEN_ERROR_CONSOLE("&cOnly one player can open a dialog."),
DIALOG_NOT_SUPPORTED("&cYour Minecraft version does not support this menu, you need at least &f%version%&c."),

BEDROCK_OPEN_SUCCESS("&aYou have just opened the bedrock inventory &f%name%&a."),
BEDROCK_OPEN_SUCCESS_OTHER("&aYou have just opened the bedrock inventory &f%name%&a to the &3%player%&a."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public String parse(@NotNull String string, @NotNull String key, @NotNull String
}
}
} catch (Exception exception) {
exception.printStackTrace();
Logger.error(exception);
Logger.info("Error with placeholder key " + key + " !", Logger.LogType.ERROR);
}
return string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.maxlego08.menu.api.utils.resolvable;

import com.google.common.base.Preconditions;
import fr.maxlego08.menu.api.context.BuildContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -78,6 +79,20 @@ static <K, V> void applyResolvable(
return resolvable.resolve(context);
}

static <X> @NotNull X resolveOrDefault(
@NotNull BuildContext context,
@Nullable Resolvable<X> resolvable,
@NotNull X defaultValue
) {
Preconditions.checkNotNull(defaultValue, "Default value cannot be null");
if (resolvable == null) {
return defaultValue;
}

X value = resolvable.resolve(context);
return value != null ? value : defaultValue;
}

static <X> @NotNull List<X> resolveList(
@NotNull BuildContext context,
@Nullable List<? extends @Nullable Resolvable<X>> resolvables
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package fr.maxlego08.menu.api.utils.version;

import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;

/**
* Resolves the Minecraft version of a player's client through the registered
* {@link ClientVersionProvider}s, which are queried in registration order.
* <p>
* Providers are ordered from most to least trustworthy: protocol translation plugins first,
* then Paper's own handshake protocol number. When no provider is able to answer, the
* manager falls back to the server version, so a server with no usable source of client
* versions behaves as if every client matched it.
*/
public interface ClientVersionManager {

/**
* The first Minecraft version able to render the Paper Dialog API.
*/
MinecraftVersion DIALOG_MINIMUM_VERSION = MinecraftVersion.parse("1.21.6");

/**
* Registers a new provider. Providers are queried in registration order.
*
* @param provider the provider to register
*/
void registerProvider(@NotNull ClientVersionProvider provider);

/**
* @return the registered providers, in registration order.
*/
@NotNull
Collection<ClientVersionProvider> getProviders();

/**
* Returns the version of the player's client, or the server version when no provider
* could determine it.
*
* @param player the player to inspect
* @return the client version, never null
*/
@NotNull
MinecraftVersion getClientVersion(@NotNull Player player);

/**
* @param player the player to inspect
* @return true if the player's client can display dialogs (Minecraft 1.21.6 or above).
*/
default boolean supportsDialogs(@NotNull Player player) {
return this.getClientVersion(player).isAtLeast(DIALOG_MINIMUM_VERSION);
}

/**
* A manager with no provider, reporting the server version for every player. Used as the
* default for {@link fr.maxlego08.menu.api.MenuPlugin#getClientVersionManager()} so an
* implementation predating this interface keeps working.
*/
ClientVersionManager SERVER_VERSION_ONLY = new ClientVersionManager() {

@Override
public void registerProvider(@NotNull ClientVersionProvider provider) {
}

@Override
@NotNull
public Collection<ClientVersionProvider> getProviders() {
return List.of();
}

@Override
@NotNull
public MinecraftVersion getClientVersion(@NotNull Player player) {
return MinecraftVersion.getCurrentVersion();
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fr.maxlego08.menu.api.utils.version;

import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;

/**
* Provides the Minecraft version of a player's <b>client</b>, which can differ from the
* server version when a protocol translation plugin such as ViaVersion is installed.
* <p>
* Implementations live in the {@code Hooks/ClientVersion} module and are registered on
* {@link ClientVersionManager} when their backing plugin is present.
*/
public interface ClientVersionProvider {

/**
* @return the name of the plugin backing this provider, used for logging.
*/
@NotNull
String getName();

/**
* @return true if the backing plugin is present and its API can be used.
*/
boolean isAvailable();

/**
* Returns the version of the player's client.
*
* @param player the player to inspect
* @return the client version, or {@link Optional#empty()} if it could not be determined
*/
@NotNull
Optional<MinecraftVersion> getClientVersion(@NotNull Player player);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ public static MinecraftVersion parse(@Nullable String rawVersion) {
for (String part : parts) {
if (index >= 3) break;
if (!NUMERIC.matcher(part).matches()) {
continue;
break;
}
try {
numbers[index++] = Integer.parseInt(part);
numbers[index] = Integer.parseInt(part);
index++;
} catch (NumberFormatException e) {
Logger.info("Could not parse numeric segment '" + part + "' in Minecraft value '" + raw + "'. (" + e.getMessage() + ")", Logger.LogType.WARNING);
break;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package fr.maxlego08.menu.api.utils.version;

import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.TreeMap;

/**
* Maps a Minecraft protocol number to the Minecraft version that introduced it.
* <p>
* Protocol numbers are the values sent by the client during the handshake, as exposed by
* {@code Player#getProtocolVersion()} on Paper. Versions sharing a protocol are collapsed
* onto the lowest one of the range ({@code 772} is {@code 1.21.7-1.21.8}, so it maps to
* {@code 1.21.7}), which is the conservative choice for a minimum-version check.
*/
public final class ProtocolVersions {
private static final NavigableMap<Integer, MinecraftVersion> VERSIONS = new TreeMap<>();

static {
register(4, "1.7.2");
register(5, "1.7.6");
register(47, "1.8");
register(107, "1.9");
register(108, "1.9.1");
register(109, "1.9.2");
register(110, "1.9.3");
register(210, "1.10");
register(315, "1.11");
register(316, "1.11.1");
register(335, "1.12");
register(338, "1.12.1");
register(340, "1.12.2");
register(393, "1.13");
register(401, "1.13.1");
register(404, "1.13.2");
register(477, "1.14");
register(480, "1.14.1");
register(485, "1.14.2");
register(490, "1.14.3");
register(498, "1.14.4");
register(573, "1.15");
register(575, "1.15.1");
register(578, "1.15.2");
register(735, "1.16");
register(736, "1.16.1");
register(751, "1.16.2");
register(753, "1.16.3");
register(754, "1.16.4");
register(755, "1.17");
register(756, "1.17.1");
register(757, "1.18");
register(758, "1.18.2");
register(759, "1.19");
register(760, "1.19.1");
register(761, "1.19.3");
register(762, "1.19.4");
register(763, "1.20");
register(764, "1.20.2");
register(765, "1.20.3");
register(766, "1.20.5");
register(767, "1.21");
register(768, "1.21.2");
register(769, "1.21.4");
register(770, "1.21.5");
register(771, "1.21.6");
register(772, "1.21.7");
register(773, "1.21.9");
register(774, "1.21.11");
register(775, "26.1");
register(776, "26.2");
}

private ProtocolVersions() {
}

private static void register(int protocol, String version) {
VERSIONS.put(protocol, MinecraftVersion.parse(version));
}

public static Optional<MinecraftVersion> fromProtocol(int protocol) {
if (protocol < 0) return Optional.empty();

Map.Entry<Integer, MinecraftVersion> entry = VERSIONS.floorEntry(protocol);
if (entry == null) {
return Optional.of(VERSIONS.firstEntry().getValue());
}
return Optional.of(entry.getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static <T, A extends Annotation, P extends Plugin> int scanAndRegister(
} catch (Exception e) {
if (Configuration.enableDebug) {
Logger.error("Failed to load class " + clazz.getName() + " for plugin " + plugin.getName() + " with annotation " + annotation.getSimpleName() + " due to: " + e.getMessage() + ". Please check reporte this error to the plugin developer (" + plugin.getDescription().getAuthors() + ")");
e.printStackTrace();
Logger.error(e);
}
}
}
Expand Down
Loading
Loading