Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.extendedclip.deluxemenus.action;

import com.extendedclip.deluxemenus.menu.MenuHolder;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Longs;
import java.util.concurrent.ThreadLocalRandom;
Expand Down Expand Up @@ -99,33 +100,35 @@ public void setChance(@Nullable final String chance) {
/**
* Get the parsed delay of this action. If the delay is null or can't be parsed to a {@link Long}, the delay will be 0.
*
* @param holder the holder to parse placeholders in the delay for.
* @param holder the holder to parse placeholders in the delay for.
* @param context the context to resolve internal placeholders in the delay against.
* @return the parsed delay
*/
@SuppressWarnings("UnstableApiUsage")
public long getDelay(@NotNull final MenuHolder holder) {
public long getDelay(@NotNull final MenuHolder holder, @NotNull final PlaceholderContext context) {
if (delay == null || delay.isEmpty()) {
return 0;
}

final var parsed = Longs.tryParse(holder.setPlaceholdersAndArguments(delay));
final var parsed = Longs.tryParse(holder.setPlaceholdersAndArguments(delay, context));
return parsed == null ? 0 : parsed;
}

/**
* Parses the chance of this action and tries it. If {@link #getChance()} is null this will return true but if it
* can't be parsed to a {@link Double}, this will return false.
*
* @param holder the holder to parse placeholders in the chance for.
* @param holder the holder to parse placeholders in the chance for.
* @param context the context to resolve internal placeholders in the chance against.
* @return true if the chance has passed, false otherwise
*/
@SuppressWarnings("UnstableApiUsage")
public boolean checkChance(@NotNull final MenuHolder holder) {
public boolean checkChance(@NotNull final MenuHolder holder, @NotNull final PlaceholderContext context) {
if (chance == null) {
return true;
}

final Double parsedChance = Doubles.tryParse(holder.setPlaceholdersAndArguments(this.chance));
final Double parsedChance = Doubles.tryParse(holder.setPlaceholdersAndArguments(this.chance, context));
if (parsedChance == null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.extendedclip.deluxemenus.menu.Menu;
import com.extendedclip.deluxemenus.menu.MenuHolder;
import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.extendedclip.deluxemenus.utils.*;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.md_5.bungee.api.ChatMessageType;
Expand All @@ -25,6 +26,9 @@ public class ClickActionTask extends BukkitRunnable {
private final String exec;
// Ugly hack to get around the fact that arguments are not available at task execution time
private final Map<String, String> arguments;
// Same idea, for the same reason: the menu, item and click this action came from no longer
// exist by the time a delayed action runs, so they are snapshotted at click time.
private final PlaceholderContext context;
private final boolean parsePlaceholdersInArguments;
private final boolean parsePlaceholdersAfterArguments;

Expand All @@ -36,6 +40,20 @@ public ClickActionTask(
@NotNull final Map<String, String> arguments,
final boolean parsePlaceholdersInArguments,
final boolean parsePlaceholdersAfterArguments
) {
this(plugin, uuid, actionType, exec, arguments, parsePlaceholdersInArguments,
parsePlaceholdersAfterArguments, PlaceholderContext.EMPTY);
}

public ClickActionTask(
@NotNull final DeluxeMenus plugin,
@NotNull final UUID uuid,
@NotNull final ActionType actionType,
@NotNull final String exec,
@NotNull final Map<String, String> arguments,
final boolean parsePlaceholdersInArguments,
final boolean parsePlaceholdersAfterArguments,
@NotNull final PlaceholderContext context
) {
this.plugin = plugin;
this.uuid = uuid;
Expand All @@ -44,6 +62,7 @@ public ClickActionTask(
this.arguments = arguments;
this.parsePlaceholdersInArguments = parsePlaceholdersInArguments;
this.parsePlaceholdersAfterArguments = parsePlaceholdersAfterArguments;
this.context = context;
}

@Override
Expand All @@ -64,7 +83,8 @@ public void run() {
this.arguments,
target,
this.parsePlaceholdersInArguments,
this.parsePlaceholdersAfterArguments);
this.parsePlaceholdersAfterArguments,
this.context);

switch (actionType) {
case META:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package com.extendedclip.deluxemenus.action;

import com.extendedclip.deluxemenus.menu.MenuHolder;
import org.jetbrains.annotations.NotNull;

public interface ClickHandler {

void onClick(@NotNull final MenuHolder menuHolder);
}
package com.extendedclip.deluxemenus.action;

import com.extendedclip.deluxemenus.menu.MenuHolder;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import org.jetbrains.annotations.NotNull;

public interface ClickHandler {

void onClick(@NotNull final MenuHolder menuHolder, @NotNull final PlaceholderContext context);

/**
* Runs the handler with menu context only. Used by the open and close handlers, where there is no
* item and no click to report.
*/
default void onClick(@NotNull final MenuHolder menuHolder) {
onClick(menuHolder, PlaceholderContext.of(menuHolder));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.extendedclip.deluxemenus.config.DeluxeMenusConfig;
import com.extendedclip.deluxemenus.menu.Menu;
import com.extendedclip.deluxemenus.menu.MenuHolder;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.extendedclip.deluxemenus.utils.Messages;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
Expand Down Expand Up @@ -79,17 +80,22 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List<Str

MenuHolder holder = Menu.getMenuHolder(target).orElse(new MenuHolder(plugin, target));

if (!action.checkChance(holder)) {
// The action runs against `target`, not against the sender (which is usually console), so
// the context comes from the target's holder. There is no item and no click here, and when
// the target is not in a menu the holder above is a bare one, leaving %menu.*% literal too.
final PlaceholderContext context = PlaceholderContext.of(holder);

if (!action.checkChance(holder, context)) {
plugin.sms(sender, Messages.CHANCE_FAIL);
return;
}

final ClickActionTask actionTask = new ClickActionTask(plugin, target.getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), true, true);
final ClickActionTask actionTask = new ClickActionTask(plugin, target.getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), true, true, context);

if (action.hasDelay()) {
actionTask.runTaskLater(plugin, action.getDelay(holder));
actionTask.runTaskLater(plugin, action.getDelay(holder, context));

plugin.sms(sender, Messages.ACTION_TO_BE_EXECUTED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(action.getDelay(holder))).build()));
plugin.sms(sender, Messages.ACTION_TO_BE_EXECUTED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(action.getDelay(holder, context))).build()));
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import com.extendedclip.deluxemenus.menu.options.LoreAppendMode;
import com.extendedclip.deluxemenus.menu.options.MenuItemOptions;
import com.extendedclip.deluxemenus.menu.options.MenuOptions;
import com.extendedclip.deluxemenus.placeholder.internal.InternalPlaceholderResolver;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.extendedclip.deluxemenus.requirement.HasExpRequirement;
import com.extendedclip.deluxemenus.requirement.HasItemRequirement;
import com.extendedclip.deluxemenus.requirement.HasMetaRequirement;
Expand Down Expand Up @@ -169,8 +171,15 @@ public boolean loadDefConfig() {
c.addDefault("check_updates", true);
c.addDefault("use_admin_commands_in_menus_list", false);
c.addDefault("menus_list_page_size", 10);
c.addDefault("internal_placeholders.true_value", "true");
c.addDefault("internal_placeholders.false_value", "false");
c.options().copyDefaults(true);

InternalPlaceholderResolver.setBooleanValues(
c.getString("internal_placeholders.true_value", "true"),
c.getString("internal_placeholders.false_value", "false")
);

if (!c.contains("gui_menus")) {
createMenuExamples(c);
} else {
Expand Down Expand Up @@ -1223,18 +1232,18 @@ private ClickHandler getClickHandler(FileConfiguration c, String configPath) {
handler = new ClickHandler() {

@Override
public void onClick(@NotNull final MenuHolder holder) {
public void onClick(@NotNull final MenuHolder holder, @NotNull final PlaceholderContext context) {

for (ClickAction action : actions) {

if (!action.checkChance(holder)) {
if (!action.checkChance(holder, context)) {
continue;
}

final ClickActionTask actionTask = new ClickActionTask(plugin, holder.getViewer().getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), holder.parsePlaceholdersInArguments(), holder.parsePlaceholdersAfterArguments());
final ClickActionTask actionTask = new ClickActionTask(plugin, holder.getViewer().getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), holder.parsePlaceholdersInArguments(), holder.parsePlaceholdersAfterArguments(), context);

if (action.hasDelay()) {
actionTask.runTaskLater(plugin, action.getDelay(holder));
actionTask.runTaskLater(plugin, action.getDelay(holder, context));
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.extendedclip.deluxemenus.menu.Menu;
import com.extendedclip.deluxemenus.menu.MenuHolder;
import com.extendedclip.deluxemenus.menu.MenuItem;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.extendedclip.deluxemenus.requirement.RequirementList;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
Expand Down Expand Up @@ -152,36 +153,43 @@ public void onClick(InventoryClickEvent event) {
this.shiftCache.put(player.getUniqueId(), System.currentTimeMillis());
}

if (handleClick(player, holder, item.options().clickHandler(), item.options().clickRequirements())) {
// Snapshotted once here: everything a click action or requirement can report about the
// click and the clicked item, in a form that stays valid on later ticks.
final PlaceholderContext context = PlaceholderContext.of(holder)
.withItem(item)
.withItemStack(event.getCurrentItem(), event.getCurrentItem() == null ? null : event.getCurrentItem().getItemMeta())
.withClick(event);

if (handleClick(player, holder, context, item.options().clickHandler(), item.options().clickRequirements())) {
return;
}

if (event.isShiftClick() && event.isLeftClick()) {
if (handleClick(player, holder, item.options().shiftLeftClickHandler(), item.options().shiftLeftClickRequirements())) {
if (handleClick(player, holder, context, item.options().shiftLeftClickHandler(), item.options().shiftLeftClickRequirements())) {
return;
}
}

if (event.isShiftClick() && event.isRightClick()) {
if (handleClick(player, holder, item.options().shiftRightClickHandler(), item.options().shiftRightClickRequirements())) {
if (handleClick(player, holder, context, item.options().shiftRightClickHandler(), item.options().shiftRightClickRequirements())) {
return;
}
}

if (event.getClick() == ClickType.LEFT) {
if (handleClick(player, holder, item.options().leftClickHandler(), item.options().leftClickRequirements())) {
if (handleClick(player, holder, context, item.options().leftClickHandler(), item.options().leftClickRequirements())) {
return;
}
}

if (event.getClick() == ClickType.RIGHT) {
if (handleClick(player, holder, item.options().rightClickHandler(), item.options().rightClickRequirements())) {
if (handleClick(player, holder, context, item.options().rightClickHandler(), item.options().rightClickRequirements())) {
return;
}
}

if (event.getClick() == ClickType.MIDDLE) {
if (handleClick(player, holder, item.options().middleClickHandler(), item.options().middleClickRequirements())) {
if (handleClick(player, holder, context, item.options().middleClickHandler(), item.options().middleClickRequirements())) {
}
}
}
Expand All @@ -191,30 +199,31 @@ public void onClick(InventoryClickEvent event) {
*
* @param player player who clicked
* @param holder menu holder
* @param context snapshot of the menu, the clicked item and the click itself
* @param handler click handler
* @param requirements click requirements
* @return true if click was handled successfully. will ever return false if no click handler was found
*/
private boolean handleClick(final @NotNull Player player, final @NotNull MenuHolder holder, final @NotNull Optional<ClickHandler> handler, final @NotNull Optional<RequirementList> requirements) {
private boolean handleClick(final @NotNull Player player, final @NotNull MenuHolder holder, final @NotNull PlaceholderContext context, final @NotNull Optional<ClickHandler> handler, final @NotNull Optional<RequirementList> requirements) {
if (handler.isEmpty()) {
return false;
}

if (requirements.isPresent()) {
final ClickHandler denyHandler = requirements.get().getDenyHandler();

if (!requirements.get().evaluate(holder)) {
if (!requirements.get().evaluate(holder, context)) {
if (denyHandler == null) {
return true;
}

denyHandler.onClick(holder);
denyHandler.onClick(holder, context);
return true;
}
}

this.cache.put(player.getUniqueId(), System.currentTimeMillis());
handler.get().onClick(holder);
handler.get().onClick(holder, context);

return true;
}
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/com/extendedclip/deluxemenus/menu/Menu.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.extendedclip.deluxemenus.events.DeluxeMenusPreOpenMenuEvent;
import com.extendedclip.deluxemenus.menu.command.RegistrableMenuCommand;
import com.extendedclip.deluxemenus.menu.options.MenuOptions;
import com.extendedclip.deluxemenus.placeholder.internal.PlaceholderContext;
import com.extendedclip.deluxemenus.requirement.RequirementList;
import com.extendedclip.deluxemenus.utils.DebugLevel;
import com.extendedclip.deluxemenus.utils.StringUtils;
Expand Down Expand Up @@ -285,6 +286,9 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map<String, S
holder.setTypedArgs(args);
holder.parsePlaceholdersInArguments(this.options.parsePlaceholdersInArguments());
holder.parsePlaceholdersAfterArguments(this.options.parsePlaceholdersAfterArguments());
// Set before the requirement checks below, so %menu.*% resolves inside open_requirement
// and the args requirements instead of being left literal.
holder.setMenuName(this.options.name());

if (!this.handleArgRequirements(holder)) {
return;
Expand Down Expand Up @@ -316,7 +320,7 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map<String, S

if (item.options().viewRequirements().isPresent()) {

if (item.options().viewRequirements().get().evaluate(holder)) {
if (item.options().viewRequirements().get().evaluate(holder, PlaceholderContext.of(holder).withItem(item))) {

activeItems.add(item);
break;
Expand All @@ -333,7 +337,6 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map<String, S
return;
}

holder.setMenuName(this.options.name());
holder.setActiveItems(activeItems);

this.options.openHandler().ifPresent(h -> h.onClick(holder));
Expand Down
Loading