9
0
mirror of https://github.com/Xiao-MoMi/Custom-Fishing.git synced 2025-12-30 12:29:19 +00:00

checkpoint - 3

This commit is contained in:
XiaoMoMi
2024-05-19 04:13:40 +08:00
parent ddc1f4245f
commit 326c9580eb
102 changed files with 1758 additions and 970 deletions

View File

@@ -17,16 +17,33 @@
package net.momirealms.customfishing.api;
import net.momirealms.customfishing.api.mechanic.action.ActionManager;
import net.momirealms.customfishing.api.mechanic.config.ConfigManager;
import net.momirealms.customfishing.api.mechanic.event.EventManager;
import net.momirealms.customfishing.api.mechanic.misc.placeholder.PlaceholderManager;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.common.plugin.CustomFishingPlugin;
import net.momirealms.customfishing.common.sender.SenderFactory;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.io.File;
import static java.util.Objects.requireNonNull;
public abstract class BukkitCustomFishingPlugin implements CustomFishingPlugin {
private static BukkitCustomFishingPlugin instance;
private final Plugin boostrap = requireNonNull(Bukkit.getPluginManager().getPlugin("CustomFishing"));
protected EventManager eventManager;
protected ConfigManager configManager;
protected RequirementManager<Player> requirementManager;
protected ActionManager<Player> actionManager;
protected SenderFactory<BukkitCustomFishingPlugin, CommandSender> senderFactory;
protected PlaceholderManager placeholderManager;
public BukkitCustomFishingPlugin() {
instance = this;
@@ -47,4 +64,28 @@ public abstract class BukkitCustomFishingPlugin implements CustomFishingPlugin {
public ConfigManager getConfigManager() {
return configManager;
}
public RequirementManager<Player> getRequirementManager() {
return requirementManager;
}
public ActionManager<Player> getActionManager() {
return actionManager;
}
public SenderFactory<BukkitCustomFishingPlugin, CommandSender> getSenderFactory() {
return senderFactory;
}
public File getDataFolder() {
return boostrap.getDataFolder();
}
public PlaceholderManager getPlaceholderManager() {
return placeholderManager;
}
public Plugin getBoostrap() {
return boostrap;
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.manager;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public interface PlaceholderManager {
/**
* Register a custom placeholder
*
* @param placeholder for instance {level}
* @param original for instance %player_level%
* @return success or not, it would fail if the placeholder has been registered
*/
boolean registerCustomPlaceholder(String placeholder, String original);
/**
* Set placeholders in a text string for a player.
*
* @param player The player for whom the placeholders should be set.
* @param text The text string containing placeholders.
* @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text.
*/
String setPlaceholders(Player player, String text);
/**
* Set placeholders in a text string for an offline player.
*
* @param player The offline player for whom the placeholders should be set.
* @param text The text string containing placeholders.
* @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text.
*/
String setPlaceholders(OfflinePlayer player, String text);
/**
* Detect and extract placeholders from a text string.
*
* @param text The text string to search for placeholders.
* @return A list of detected placeholders in the text.
*/
List<String> detectPlaceholders(String text);
/**
* Get the value associated with a single placeholder.
*
* @param player The player for whom the placeholders are being resolved (nullable).
* @param placeholder The placeholder to look up.
* @param placeholders A map of placeholders to their corresponding values.
* @return The value associated with the placeholder, or the original placeholder if not found.
*/
String getSingleValue(@Nullable Player player, String placeholder, Map<String, String> placeholders);
/**
* Parse a text string by replacing placeholders with their corresponding values.
*
* @param player The offline player for whom the placeholders are being resolved (nullable).
* @param text The text string containing placeholders.
* @param placeholders A map of placeholders to their corresponding values.
* @return The text string with placeholders replaced by their values.
*/
String parse(@Nullable OfflinePlayer player, String text, Map<String, String> placeholders);
/**
* Parse a list of text strings by replacing placeholders with their corresponding values.
*
* @param player The player for whom the placeholders are being resolved (can be null for offline players).
* @param list The list of text strings containing placeholders.
* @param replacements A map of custom replacements for placeholders.
* @return The list of text strings with placeholders replaced by their values.
*/
List<String> parse(@Nullable OfflinePlayer player, List<String> list, Map<String, String> replacements);
/**
* Get an expression's value
* @param player player
* @param formula formula
* @param vars vars
* @return result
*/
double getExpressionValue(Player player, String formula, Map<String, String> vars);
/**
* Get an expression's value
* @param formula formula
* @return result
*/
double getExpressionValue(String formula);
}

View File

@@ -30,5 +30,5 @@ public interface ActionFactory<T> {
* @param args the args containing the arguments needed to build the action
* @return the constructed action
*/
Action<T> process(Object args);
Action<T> process(Object args, double chance);
}

View File

@@ -22,106 +22,81 @@ import net.momirealms.customfishing.api.mechanic.context.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
/**
* The ActionManager interface manages custom action types and provides methods for handling actions.
*
* @param <T> the type of the context in which the actions are triggered.
*/
public interface ActionManager<T> {
/**
* Registers an ActionFactory for a specific action type.
* This method allows you to associate an ActionFactory with a custom action type.
* Registers a custom action type with its corresponding factory.
*
* @param type The custom action type to register.
* @param actionFactory The ActionFactory responsible for creating actions of the specified type.
* @return True if the registration was successful (the action type was not already registered), false otherwise.
* @param type The type identifier of the action.
* @param actionFactory The factory responsible for creating instances of the action.
* @return True if registration was successful, false if the type is already registered.
*/
boolean registerAction(String type, ActionFactory<T> actionFactory);
/**
* Unregisters an ActionFactory for a specific action type.
* This method allows you to remove the association between an action type and its ActionFactory.
* Unregisters a custom action type.
*
* @param type The custom action type to unregister.
* @return True if the action type was successfully unregistered, false if it was not found.
* @param type The type identifier of the action to unregister.
* @return True if unregistration was successful, false if the type is not registered.
*/
boolean unregisterAction(String type);
/**
* Retrieves an Action object based on the configuration provided in a ConfigurationSection.
* This method reads the type of action from the section, obtains the corresponding ActionFactory,
* and builds an Action object using the specified values and chance.
* <p>
* events:
* success:
* action_1: <- section
* ...
* Checks if an action type is registered.
*
* @param section The ConfigurationSection containing the action configuration.
* @return An Action object created based on the configuration, or an EmptyAction instance if the action type is invalid.
* @param type The type identifier of the action.
* @return True if the action type is registered, otherwise false.
*/
Action<T> getAction(Section section);
boolean hasAction(@NotNull String type);
/**
* Retrieves a mapping of ActionTriggers to arrays of Actions from a ConfigurationSection.
* This method iterates through the provided ConfigurationSection to extract action triggers
* and their associated arrays of Actions.
* <p>
* events: <- section
* success:
* action_1:
* ...
* Retrieves the action factory for the specified action type.
*
* @param section The ConfigurationSection containing action mappings.
* @return A HashMap where keys are ActionTriggers and values are arrays of Action objects.
*/
HashMap<ActionTrigger, Action<T>[]> getActionMap(Section section);
/**
* Retrieves an array of Action objects from a ConfigurationSection.
* This method iterates through the provided ConfigurationSection to extract Action configurations
* and build an array of Action objects.
* <p>
* events:
* success: <- section
* action_1:
* ...
*
* @param section The ConfigurationSection containing action configurations.
* @return An array of Action objects created based on the configurations in the section.
*/
@NotNull
Action<T>[] getActions(@NotNull Section section);
/**
* Retrieves an ActionFactory associated with a specific action type.
*
* @param type The action type for which to retrieve the ActionFactory.
* @return The ActionFactory associated with the specified action type, or null if not found.
* @param type The type identifier of the action.
* @return The action factory for the specified type, or null if no factory is found.
*/
@Nullable
ActionFactory<T> getActionFactory(@NotNull String type);
/**
* Retrieves a mapping of success times to corresponding arrays of actions from a ConfigurationSection.
* <p>
* events:
* success-times: <- section
* 1:
* action_1:
* ...
* Parses an action from a configuration section.
*
* @param section The ConfigurationSection containing success times actions.
* @return A HashMap where success times associated with actions.
* @param section The configuration section containing the action definition.
* @return The parsed action.
*/
@NotNull
HashMap<Integer, Action<T>[]> getTimesActionMap(@NotNull Section section);
Action<T> parseAction(Section section);
/**
* Triggers a list of actions with the given condition.
* Parses an array of actions from a configuration section.
*
* @param section The configuration section containing the action definitions.
* @return An array of parsed actions.
*/
@NotNull
Action<T>[] parseActions(@NotNull Section section);
/**
* Parses an action from the given type and arguments.
*
* @param type The type identifier of the action.
* @param args The arguments for the action.
* @return The parsed action.
*/
Action<T> parseAction(@NotNull String type, @NotNull Object args);
/**
* Triggers a list of actions with the given context.
* If the list of actions is not null, each action in the list is triggered.
*
* @param actions The list of actions to trigger.
* @param context The context associated with the actions.
* @param actions The list of actions to trigger.
*/
static <T> void trigger(@NotNull Context<T> context, @Nullable List<Action<T>> actions) {
if (actions != null)
@@ -129,6 +104,13 @@ public interface ActionManager<T> {
action.trigger(context);
}
/**
* Triggers an array of actions with the given context.
* If the array of actions is not null, each action in the array is triggered.
*
* @param context The context associated with the actions.
* @param actions The array of actions to trigger.
*/
static <T> void trigger(@NotNull Context<T> context, @Nullable Action<T>[] actions) {
if (actions != null)
for (Action<T> action : actions)

View File

@@ -0,0 +1,17 @@
package net.momirealms.customfishing.api.mechanic.action;
import net.momirealms.customfishing.api.mechanic.context.Context;
import org.bukkit.entity.Player;
/**
* An implementation of the Action interface that represents an empty action with no behavior.
* This class serves as a default action to prevent NPE.
*/
public class EmptyAction implements Action<Player> {
public static final EmptyAction INSTANCE = new EmptyAction();
@Override
public void trigger(Context<Player> context) {
}
}

View File

@@ -1,197 +1,227 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.mechanic.competition;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfigImpl;
import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfigImpl;
import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig;
import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.bukkit.entity.Player;
import java.util.HashMap;
public class CompetitionConfig {
/**
* Interface representing the configuration for a fishing competition.
*/
public interface CompetitionConfig {
private final String key;
private int duration;
private int minPlayers;
private BossBarConfigImpl bossBarConfigImpl;
private ActionBarConfigImpl actionBarConfigImpl;
private Action[] skipActions;
private Action[] startActions;
private Action[] endActions;
private Action[] joinActions;
private Requirement[] requirements;
private CompetitionGoal goal;
private HashMap<String, Action[]> rewards;
CompetitionGoal DEFAULT_GOAL = CompetitionGoal.CATCH_AMOUNT;
int DEFAULT_DURATION = 300;
int DEFAULT_MIN_PLAYERS = 0;
Requirement<Player>[] DEFAULT_REQUIREMENTS = null;
Action<Player>[] DEFAULT_SKIP_ACTIONS = null;
Action<Player>[] DEFAULT_START_ACTIONS = null;
Action<Player>[] DEFAULT_END_ACTIONS = null;
Action<Player>[] DEFAULT_JOIN_ACTIONS = null;
HashMap<String, Action<Player>[]> DEFAULT_REWARDS = new HashMap<>();
public CompetitionConfig(String key) {
this.key = key;
}
/**
* Gets the unique key for the competition.
*
* @return the key for the competition.
*/
String key();
public String getKey() {
return key;
}
/**
* Gets the duration of the competition in seconds.
*
* @return the duration in seconds.
*/
int durationInSeconds();
public int getDurationInSeconds() {
return duration;
}
/**
* Gets the minimum number of players required to start the competition.
*
* @return the minimum number of players.
*/
int minPlayersToStart();
public int getMinPlayersToStart() {
return minPlayers;
}
/**
* Gets the actions to be performed when the competition starts.
*
* @return an array of start actions.
*/
Action<Player>[] startActions();
@Nullable
public Action[] getStartActions() {
return startActions;
}
/**
* Gets the actions to be performed when the competition ends.
*
* @return an array of end actions.
*/
Action<Player>[] endActions();
@Nullable
public Action[] getEndActions() {
return endActions;
/**
* Gets the actions to be performed when a player joins the competition.
*
* @return an array of join actions.
*/
Action<Player>[] joinActions();
/**
* Gets the actions to be performed when a player skips the competition.
*
* @return an array of skip actions.
*/
Action<Player>[] skipActions();
/**
* Gets the requirements that players must meet to join the competition.
*
* @return an array of join requirements.
*/
Requirement<Player>[] joinRequirements();
/**
* Gets the goal of the competition.
*
* @return the competition goal.
*/
CompetitionGoal goal();
/**
* Gets the rewards for the competition.
*
* @return a hashmap where the key is a string identifier and the value is an array of actions.
*/
HashMap<String, Action<Player>[]> rewards();
/**
* Gets the configuration for the boss bar during the competition.
*
* @return the boss bar configuration.
*/
BossBarConfig bossBarConfig();
/**
* Gets the configuration for the action bar during the competition.
*
* @return the action bar configuration.
*/
ActionBarConfig actionBarConfig();
/**
* Creates a new builder for the competition configuration.
*
* @return a new builder instance.
*/
static Builder builder() {
return new CompetitionConfigImpl.BuilderImpl();
}
/**
* Get the actions to perform if player joined the competition
*
* @return actions
* Builder interface for constructing a CompetitionConfig instance.
*/
@Nullable
public Action[] getJoinActions() {
return joinActions;
}
interface Builder {
/**
* Get the actions to perform if the amount of players doesn't meet the requirement
*
* @return actions
*/
@Nullable
public Action[] getSkipActions() {
return skipActions;
}
/**
* Sets the unique key for the competition.
*
* @param key the key for the competition.
* @return the builder instance.
*/
Builder key(String key);
/**
* Get the requirements for participating the competition
*
* @return requirements
*/
@Nullable
public Requirement[] getRequirements() {
return requirements;
}
/**
* Sets the goal of the competition.
*
* @param goal the competition goal.
* @return the builder instance.
*/
Builder goal(CompetitionGoal goal);
@NotNull
public CompetitionGoal getGoal() {
return goal;
}
/**
* Sets the duration of the competition.
*
* @param duration the duration in seconds.
* @return the builder instance.
*/
Builder duration(int duration);
/**
* Get the reward map
*
* @return reward map
*/
public HashMap<String, Action[]> getRewards() {
return rewards;
}
/**
* Sets the minimum number of players required to start the competition.
*
* @param minPlayers the minimum number of players.
* @return the builder instance.
*/
Builder minPlayers(int minPlayers);
@Nullable
public BossBarConfigImpl getBossBarConfig() {
return bossBarConfigImpl;
}
/**
* Sets the requirements that players must meet to join the competition.
*
* @param joinRequirements an array of join requirements.
* @return the builder instance.
*/
Builder joinRequirements(Requirement<Player>[] joinRequirements);
@Nullable
public ActionBarConfigImpl getActionBarConfig() {
return actionBarConfigImpl;
}
/**
* Sets the actions to be performed when a player skips the competition.
*
* @param skipActions an array of skip actions.
* @return the builder instance.
*/
Builder skipActions(Action<Player>[] skipActions);
public static Builder builder(String key) {
return new Builder(key);
}
/**
* Sets the actions to be performed when the competition starts.
*
* @param startActions an array of start actions.
* @return the builder instance.
*/
Builder startActions(Action<Player>[] startActions);
public static class Builder {
/**
* Sets the actions to be performed when the competition ends.
*
* @param endActions an array of end actions.
* @return the builder instance.
*/
Builder endActions(Action<Player>[] endActions);
private final CompetitionConfig config;
/**
* Sets the actions to be performed when a player joins the competition.
*
* @param joinActions an array of join actions.
* @return the builder instance.
*/
Builder joinActions(Action<Player>[] joinActions);
public Builder(String key) {
this.config = new CompetitionConfig(key);
}
/**
* Sets the rewards for the competition.
*
* @param rewards a hashmap where the key is a string identifier and the value is an array of actions.
* @return the builder instance.
*/
Builder rewards(HashMap<String, Action<Player>[]> rewards);
public Builder duration(int duration) {
config.duration = duration;
return this;
}
/**
* Sets the configuration for the boss bar during the competition.
*
* @param bossBarConfig the boss bar configuration.
* @return the builder instance.
*/
Builder bossBarConfig(BossBarConfig bossBarConfig);
public Builder minPlayers(int min) {
config.minPlayers = min;
return this;
}
/**
* Sets the configuration for the action bar during the competition.
*
* @param actionBarConfig the action bar configuration.
* @return the builder instance.
*/
Builder actionBarConfig(ActionBarConfig actionBarConfig);
public Builder startActions(Action[] startActions) {
config.startActions = startActions;
return this;
}
public Builder endActions(Action[] endActions) {
config.endActions = endActions;
return this;
}
public Builder skipActions(Action[] skipActions) {
config.skipActions = skipActions;
return this;
}
public Builder joinActions(Action[] joinActions) {
config.joinActions = joinActions;
return this;
}
@SuppressWarnings("UnusedReturnValue")
public Builder actionbar(ActionBarConfigImpl actionBarConfigImpl) {
config.actionBarConfigImpl = actionBarConfigImpl;
return this;
}
@SuppressWarnings("UnusedReturnValue")
public Builder bossbar(BossBarConfigImpl bossBarConfigImpl) {
config.bossBarConfigImpl = bossBarConfigImpl;
return this;
}
public Builder requirements(Requirement[] requirements) {
config.requirements = requirements;
return this;
}
public Builder goal(CompetitionGoal goal) {
config.goal = goal;
return this;
}
public Builder rewards(HashMap<String, Action[]> rewards) {
config.rewards = rewards;
return this;
}
public CompetitionConfig build() {
return config;
}
/**
* Builds and returns the CompetitionConfig instance.
*
* @return the constructed CompetitionConfig instance.
*/
CompetitionConfig build();
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.mechanic.competition;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig;
import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import org.bukkit.entity.Player;
import java.util.HashMap;
public class CompetitionConfigImpl implements CompetitionConfig {
private final String key;
private final CompetitionGoal goal;
private final int duration;
private final int minPlayers;
private final Requirement<Player>[] joinRequirements;
private final Action<Player>[] skipActions;
private final Action<Player>[] startActions;
private final Action<Player>[] endActions;
private final Action<Player>[] joinActions;
private final HashMap<String, Action<Player>[]> rewards;
private final BossBarConfig bossBarConfig;
private final ActionBarConfig actionBarConfig;
public CompetitionConfigImpl(String key, CompetitionGoal goal, int duration, int minPlayers, Requirement<Player>[] joinRequirements, Action<Player>[] skipActions, Action<Player>[] startActions, Action<Player>[] endActions, Action<Player>[] joinActions, HashMap<String, Action<Player>[]> rewards, BossBarConfig bossBarConfig, ActionBarConfig actionBarConfig) {
this.key = key;
this.goal = goal;
this.duration = duration;
this.minPlayers = minPlayers;
this.joinRequirements = joinRequirements;
this.skipActions = skipActions;
this.startActions = startActions;
this.endActions = endActions;
this.joinActions = joinActions;
this.rewards = rewards;
this.bossBarConfig = bossBarConfig;
this.actionBarConfig = actionBarConfig;
}
@Override
public String key() {
return key;
}
@Override
public int durationInSeconds() {
return duration;
}
@Override
public int minPlayersToStart() {
return minPlayers;
}
@Override
public Action<Player>[] startActions() {
return startActions;
}
@Override
public Action<Player>[] endActions() {
return endActions;
}
@Override
public Action<Player>[] joinActions() {
return joinActions;
}
@Override
public Action<Player>[] skipActions() {
return skipActions;
}
@Override
public Requirement<Player>[] joinRequirements() {
return joinRequirements;
}
@Override
public CompetitionGoal goal() {
return goal;
}
@Override
public HashMap<String, Action<Player>[]> rewards() {
return rewards;
}
@Override
public BossBarConfig bossBarConfig() {
return bossBarConfig;
}
@Override
public ActionBarConfig actionBarConfig() {
return actionBarConfig;
}
public static class BuilderImpl implements Builder {
private String key;
private CompetitionGoal goal = DEFAULT_GOAL;
private int duration = DEFAULT_DURATION;
private int minPlayers = DEFAULT_MIN_PLAYERS;
private Requirement<Player>[] joinRequirements = DEFAULT_REQUIREMENTS;
private Action<Player>[] skipActions = DEFAULT_SKIP_ACTIONS;
private Action<Player>[] startActions = DEFAULT_START_ACTIONS;
private Action<Player>[] endActions = DEFAULT_END_ACTIONS;
private Action<Player>[] joinActions = DEFAULT_JOIN_ACTIONS;
private HashMap<String, Action<Player>[]> rewards = DEFAULT_REWARDS;
private BossBarConfig bossBarConfig;
private ActionBarConfig actionBarConfig;
@Override
public Builder key(String key) {
this.key = key;
return this;
}
@Override
public Builder goal(CompetitionGoal goal) {
this.goal = goal;
return this;
}
@Override
public Builder duration(int duration) {
this.duration = duration;
return this;
}
@Override
public Builder minPlayers(int minPlayers) {
this.minPlayers = minPlayers;
return this;
}
@Override
public Builder joinRequirements(Requirement<Player>[] joinRequirements) {
this.joinRequirements = joinRequirements;
return this;
}
@Override
public Builder skipActions(Action<Player>[] skipActions) {
this.skipActions = skipActions;
return this;
}
@Override
public Builder startActions(Action<Player>[] startActions) {
this.startActions = startActions;
return this;
}
@Override
public Builder endActions(Action<Player>[] endActions) {
this.endActions = endActions;
return this;
}
@Override
public Builder joinActions(Action<Player>[] joinActions) {
this.joinActions = joinActions;
return this;
}
@Override
public Builder rewards(HashMap<String, Action<Player>[]> rewards) {
this.rewards = rewards;
return this;
}
@Override
public Builder bossBarConfig(BossBarConfig bossBarConfig) {
this.bossBarConfig = bossBarConfig;
return this;
}
@Override
public Builder actionBarConfig(ActionBarConfig actionBarConfig) {
this.actionBarConfig = actionBarConfig;
return this;
}
@Override
public CompetitionConfig build() {
return new CompetitionConfigImpl(key, goal, duration, minPlayers, joinRequirements, skipActions, startActions, endActions, joinActions, rewards, bossBarConfig, actionBarConfig);
}
}
}

View File

@@ -19,6 +19,7 @@ package net.momirealms.customfishing.api.mechanic.competition;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.util.Index;
import net.momirealms.customfishing.common.util.RandomUtils;
import java.util.concurrent.ThreadLocalRandom;
@@ -60,7 +61,7 @@ public final class CompetitionGoal {
* @return A randomly selected competition goal.
*/
public static CompetitionGoal getRandom() {
return CompetitionGoal.values()[ThreadLocalRandom.current().nextInt(CompetitionGoal.values().length - 1)];
return CompetitionGoal.values()[RandomUtils.generateRandomInt(0, values.length - 1)];
}
private final Key key;

View File

@@ -64,7 +64,7 @@ public interface CompetitionManager {
* @param serverGroup Whether the competition should start across all servers that connected to Redis
* @return True if the competition was started successfully, false otherwise.
*/
boolean startCompetition(CompetitionConfig config, boolean force, @Nullable String serverGroup);
boolean startCompetition(CompetitionConfigImpl config, boolean force, @Nullable String serverGroup);
/**
* Gets the number of seconds until the next competition.
@@ -77,7 +77,8 @@ public interface CompetitionManager {
* Retrieves the configuration for a competition based on its key.
*
* @param key The key of the competition configuration to retrieve.
* @return The {@link CompetitionConfig} for the specified key, or {@code null} if no configuration exists with that key.
* @return The {@link CompetitionConfigImpl} for the specified key, or {@code null} if no configuration exists with that key.
*/
@Nullable CompetitionConfig getConfig(String key);
@Nullable
CompetitionConfigImpl getConfig(String key);
}

View File

@@ -19,53 +19,98 @@ package net.momirealms.customfishing.api.mechanic.competition;
import org.jetbrains.annotations.NotNull;
public class CompetitionPlayer implements Comparable<CompetitionPlayer>{
/**
* Represents a player participating in a fishing competition.
*/
public class CompetitionPlayer implements Comparable<CompetitionPlayer> {
private static CompetitionPlayer empty = new CompetitionPlayer("", 0);
private long time;
private final String player;
private long time;
private double score;
/**
* Constructs a new CompetitionPlayer with the specified player name and initial score.
*
* @param player the name of the player.
* @param score the initial score of the player.
*/
public CompetitionPlayer(String player, double score) {
this.player = player;
this.score = score;
this.time = System.currentTimeMillis();
}
/**
* Adds the specified score to the player's current score.
* If the added score is positive, updates the player's time to the current time.
*
* @param score the score to add.
*/
public void addScore(double score) {
this.score += score;
if (score <= 0) return;
this.time = System.currentTimeMillis();
}
/**
* Sets the player's score to the specified value and updates the player's time to the current time.
*
* @param score the new score for the player.
*/
public void setScore(double score) {
this.score = score;
this.time = System.currentTimeMillis();
}
/**
* Gets the time when the player's score was last updated.
*
* @return the last update time in milliseconds.
*/
public long getTime() {
return time;
}
/**
* Gets the player's current score.
*
* @return the current score.
*/
public double getScore() {
return this.score;
}
public String getPlayer(){
/**
* Gets the name of the player.
*
* @return the player's name.
*/
public String getPlayer() {
return this.player;
}
/**
* Compares this player to another CompetitionPlayer for ordering.
* Players are compared first by score, then by time if scores are equal.
*
* @param another the other player to compare to.
*/
@Override
public int compareTo(@NotNull CompetitionPlayer competitionPlayer) {
if (competitionPlayer.getScore() != this.score) {
return (competitionPlayer.getScore() > this.score) ? 1 : -1;
} else if (competitionPlayer.getTime() != this.time) {
return (competitionPlayer.getTime() > this.time) ? 1 : -1;
public int compareTo(@NotNull CompetitionPlayer another) {
if (another.getScore() != this.score) {
return (another.getScore() > this.score) ? 1 : -1;
} else if (another.getTime() != this.time) {
return (another.getTime() > this.time) ? 1 : -1;
} else {
return 0;
}
}
/**
* Returns a string representation of the CompetitionPlayer.
*
* @return a string containing the player's name, score, and last update time.
*/
@Override
public String toString() {
return "CompetitionPlayer[" +

View File

@@ -91,7 +91,8 @@ public interface FishingCompetition {
*
* @return The configuration of the fishing competition.
*/
@NotNull CompetitionConfig getConfig();
@NotNull
CompetitionConfigImpl getConfig();
/**
* Gets the goal of the fishing competition.
@@ -105,7 +106,8 @@ public interface FishingCompetition {
*
* @return The ranking data for the fishing competition.
*/
@NotNull Ranking getRanking();
@NotNull
RankingProvider getRanking();
/**
* Gets the cached placeholders for the fishing competition.

View File

@@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
public interface Ranking {
public interface RankingProvider {
/**
* Clears the list of competition players.

View File

@@ -27,12 +27,14 @@ public abstract class AbstractCompetitionInfo {
protected int switchInterval;
protected boolean showToAll;
protected String[] texts;
protected boolean enabled;
protected AbstractCompetitionInfo(int refreshRate, int switchInterval, boolean showToAll, String[] texts) {
protected AbstractCompetitionInfo(boolean enabled, int refreshRate, int switchInterval, boolean showToAll, String[] texts) {
this.refreshRate = refreshRate;
this.switchInterval = switchInterval;
this.showToAll = showToAll;
this.texts = texts;
this.enabled = enabled;
}
/**
@@ -70,4 +72,13 @@ public abstract class AbstractCompetitionInfo {
public String[] texts() {
return texts;
}
/**
* If the feature is enabled.
*
* @return enabled or not.
*/
public boolean enabled() {
return enabled;
}
}

View File

@@ -35,6 +35,13 @@ public interface ActionBarConfig {
*/
String[] texts();
/**
* Is action bar enabled
*
* @return enabled or not
*/
boolean enabled();
/**
* Creates a new builder instance for constructing {@code ActionBarConfig} objects.
*
@@ -81,6 +88,8 @@ public interface ActionBarConfig {
*/
Builder text(String[] texts);
Builder enable(boolean enable);
/**
* Builds the {@code ActionBarConfig} object with the configured settings.
*

View File

@@ -19,8 +19,8 @@ package net.momirealms.customfishing.api.mechanic.competition.info;
public class ActionBarConfigImpl extends AbstractCompetitionInfo implements ActionBarConfig {
public ActionBarConfigImpl(int refreshRate, int switchInterval, boolean showToAll, String[] texts) {
super(refreshRate, switchInterval, showToAll, texts);
public ActionBarConfigImpl(boolean enable, int refreshRate, int switchInterval, boolean showToAll, String[] texts) {
super(enable, refreshRate, switchInterval, showToAll, texts);
}
public static class BuilderImpl implements Builder {
@@ -28,29 +28,35 @@ public class ActionBarConfigImpl extends AbstractCompetitionInfo implements Acti
private int switchInterval = DEFAULT_SWITCH_INTERVAL;
private boolean showToAll = DEFAULT_VISIBILITY;
private String[] texts = DEFAULT_TEXTS;
private boolean enable = true;
@Override
public BuilderImpl showToAll(boolean showToAll) {
public Builder showToAll(boolean showToAll) {
this.showToAll = showToAll;
return this;
}
@Override
public BuilderImpl refreshRate(int rate) {
public Builder refreshRate(int rate) {
this.refreshRate = rate;
return this;
}
@Override
public BuilderImpl switchInterval(int interval) {
public Builder switchInterval(int interval) {
this.switchInterval = interval;
return this;
}
@Override
public BuilderImpl text(String[] texts) {
public Builder text(String[] texts) {
this.texts = texts;
return this;
}
@Override
public ActionBarConfigImpl build() {
return new ActionBarConfigImpl(refreshRate, switchInterval, showToAll, texts);
public Builder enable(boolean enable) {
this.enable = enable;
return this;
}
@Override
public ActionBarConfig build() {
return new ActionBarConfigImpl(enable, refreshRate, switchInterval, showToAll, texts);
}
}
}

View File

@@ -53,6 +53,13 @@ public interface BossBarConfig {
*/
BossBar.Overlay overlay();
/**
* Is boss bar enabled
*
* @return enabled or not
*/
boolean enabled();
/**
* Creates a new builder instance for constructing {@code BossBarConfig} objects.
*
@@ -115,6 +122,8 @@ public interface BossBarConfig {
*/
Builder overlay(BossBar.Overlay overlay);
Builder enable(boolean enable);
/**
* Builds the {@code BossBarConfig} object with the configured settings.
*

View File

@@ -24,8 +24,8 @@ public class BossBarConfigImpl extends AbstractCompetitionInfo implements BossBa
private final BossBar.Color color;
private final BossBar.Overlay overlay;
public BossBarConfigImpl(int refreshRate, int switchInterval, boolean showToAll, String[] texts, BossBar.Color color, BossBar.Overlay overlay) {
super(refreshRate, switchInterval, showToAll, texts);
public BossBarConfigImpl(boolean enable, int refreshRate, int switchInterval, boolean showToAll, String[] texts, BossBar.Color color, BossBar.Overlay overlay) {
super(enable, refreshRate, switchInterval, showToAll, texts);
this.color = color;
this.overlay = overlay;
}
@@ -46,40 +46,46 @@ public class BossBarConfigImpl extends AbstractCompetitionInfo implements BossBa
private boolean showToAll = DEFAULT_VISIBILITY;
private String[] texts = DEFAULT_TEXTS;
private BossBar.Overlay overlay = DEFAULT_OVERLAY;
public BossBar.Color color = DEFAULT_COLOR;
private BossBar.Color color = DEFAULT_COLOR;
private boolean enable = true;
@Override
public BuilderImpl showToAll(boolean showToAll) {
public Builder showToAll(boolean showToAll) {
this.showToAll = showToAll;
return this;
}
@Override
public BuilderImpl refreshRate(int rate) {
public Builder refreshRate(int rate) {
this.refreshRate = rate;
return this;
}
@Override
public BuilderImpl switchInterval(int interval) {
public Builder switchInterval(int interval) {
this.switchInterval = interval;
return this;
}
@Override
public BuilderImpl text(String[] texts) {
public Builder text(String[] texts) {
this.texts = texts;
return this;
}
@Override
public BuilderImpl color(BossBar.Color color) {
public Builder color(BossBar.Color color) {
this.color = color;
return this;
}
@Override
public BuilderImpl overlay(BossBar.Overlay overlay) {
public Builder overlay(BossBar.Overlay overlay) {
this.overlay = overlay;
return this;
}
@Override
public BossBarConfigImpl build() {
return new BossBarConfigImpl(refreshRate, switchInterval, showToAll, texts, color, overlay);
public Builder enable(boolean enable) {
this.enable = enable;
return this;
}
@Override
public BossBarConfig build() {
return new BossBarConfigImpl(enable, refreshRate, switchInterval, showToAll, texts, color, overlay);
}
}
}

View File

@@ -18,7 +18,6 @@
package net.momirealms.customfishing.api.mechanic.context;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -40,6 +39,13 @@ public interface Context<T> {
*/
Map<ContextKeys<?>, Object> args();
/**
* Converts the context to a map of placeholders
*
* @return a map of placeholders
*/
Map<String, String> toPlaceholderMap();
/**
* Adds or updates an argument in the context.
* This method allows adding a new argument or updating the value of an existing argument.
@@ -75,7 +81,7 @@ public interface Context<T> {
* @param player the player to be used as the holder of the context.
* @return a new Context instance with the specified player as the holder.
*/
static Context<Player> player(@NotNull Player player) {
static Context<Player> player(@Nullable Player player) {
return new PlayerContextImpl(player);
}
}

View File

@@ -32,6 +32,7 @@ public class ContextKeys<T> {
public static final ContextKeys<String> TYPE = of("type", String.class);
public static final ContextKeys<Float> SIZE = of("size", Float.class);
public static final ContextKeys<Double> PRICE = of("price", Double.class);
public static final ContextKeys<String> SURROUNDING = of("surrounding", String.class);
private final String key;
private final Class<T> type;

View File

@@ -19,7 +19,7 @@ package net.momirealms.customfishing.api.mechanic.context;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -39,8 +39,9 @@ public final class PlayerContextImpl implements Context<Player> {
*
* @param player the player to be associated with this context.
*/
public PlayerContextImpl(@NotNull Player player) {
public PlayerContextImpl(@Nullable Player player) {
this.player = player;
if (player == null) return;
final Location location = player.getLocation();
arg(ContextKeys.LOCATION, location)
.arg(ContextKeys.X, location.getBlockX())
@@ -49,47 +50,32 @@ public final class PlayerContextImpl implements Context<Player> {
.arg(ContextKeys.WORLD, location.getWorld().getName());
}
/**
* Retrieves the map of arguments associated with this context.
*
* @return a map where the keys are argument names and the values are argument values.
*/
@Override
public Map<ContextKeys<?>, Object> args() {
return args;
}
/**
* Adds an argument to the context and returns the context itself
* to allow for method chaining.
*
* @param key the name of the argument to add.
* @param value the value of the argument to add.
* @return the PlayerContextImpl instance to allow for method chaining.
*/
@Override
public Map<String, String> toPlaceholderMap() {
HashMap<String, String> placeholders = new HashMap<>();
for (Map.Entry<ContextKeys<?>, Object> entry : args.entrySet()) {
placeholders.put(entry.getKey().toString(), entry.getValue().toString());
}
return placeholders;
}
@Override
public <C> PlayerContextImpl arg(ContextKeys<C> key, C value) {
args.put(key, value);
return this;
}
/**
* Retrieves the value of a specific argument from the context.
*
* @param key the name of the argument to retrieve.
* @return the value of the argument, or null if no argument with the given key exists.
*/
@Override
@SuppressWarnings("unchecked")
public <C> C arg(ContextKeys<C> key) {
return (C) args.get(key);
}
/**
* Gets the player associated with this context.
*
* @return the player object associated with this context.
*/
@Override
public Player getHolder() {
return player;

View File

@@ -23,6 +23,8 @@ public class EffectProperties<T> {
public static final EffectProperties<Boolean> LAVA_FISHING = of("lava", Boolean.class);
public static final EffectProperties<Boolean> VOID_FISHING = of("void", Boolean.class);
// It's not actually used because it's a vanilla mechanic
public static final EffectProperties<Boolean> WATER_FISHING = of("water", Boolean.class);
private final String key;
private final Class<T> type;

View File

@@ -17,95 +17,7 @@
package net.momirealms.customfishing.api.mechanic.fishing;
import net.momirealms.customfishing.api.mechanic.effect.Effect;
import net.momirealms.customfishing.api.mechanic.game.GameInstance;
import net.momirealms.customfishing.api.mechanic.game.GameSettings;
import net.momirealms.customfishing.api.mechanic.game.GamingPlayer;
import org.bukkit.entity.FishHook;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface FishingManager {
/**
* Removes a fishing hook entity associated with a given player's UUID.
*
* @param uuid The UUID of the player
* @return {@code true} if the fishing hook was successfully removed, {@code false} otherwise.
*/
boolean removeHook(UUID uuid);
/**
* Retrieves a FishHook object associated with the provided player's UUID
*
* @param uuid The UUID of the player
* @return fishhook entity, null if not exists
*/
@Nullable FishHook getHook(UUID uuid);
/**
* Sets the temporary fishing state for a player.
*
* @param player The player for whom to set the temporary fishing state.
* @param tempFishingState The temporary fishing state to set for the player.
*/
void setTempFishingState(Player player, TempFishingState tempFishingState);
/**
* Gets the {@link TempFishingState} object associated with the given UUID.
*
* @param uuid The UUID of the player.
* @return The {@link TempFishingState} object if found, or {@code null} if not found.
*/
@Nullable TempFishingState getTempFishingState(UUID uuid);
/**
* Removes the temporary fishing state associated with a player.
*
* @param player The player whose temporary fishing state should be removed.
*/
@Nullable TempFishingState removeTempFishingState(Player player);
/**
* Processes the game result for a gaming player
*
* @param gamingPlayer The gaming player whose game result should be processed.
*/
void processGameResult(GamingPlayer gamingPlayer);
/**
* Starts a fishing game for the specified player with the given condition and effect.
*
* @param player The player starting the fishing game.
* @param playerContext The condition used to determine the game.
* @param effect The effect applied to the game.
*/
boolean startFishingGame(Player player, PlayerContext playerContext, Effect effect);
/**
* Starts a fishing game for the specified player with the given settings and game instance.
*
* @param player The player starting the fishing game.
* @param settings The game settings for the fishing game.
* @param gameInstance The instance of the fishing game to start.
*/
boolean startFishingGame(Player player, GameSettings settings, GameInstance gameInstance);
/**
* Checks if a player with the given UUID has cast their fishing hook.
*
* @param uuid The UUID of the player to check.
* @return {@code true} if the player has cast their fishing hook, {@code false} otherwise.
*/
boolean hasPlayerCastHook(UUID uuid);
/**
* Gets the {@link GamingPlayer} object associated with the given UUID.
*
* @param uuid The UUID of the player.
* @return The {@link GamingPlayer} object if found, or {@code null} if not found.
*/
@Nullable GamingPlayer getGamingPlayer(UUID uuid);
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.mechanic.misc.placeholder;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public interface PlaceholderManager {
Pattern PATTERN = Pattern.compile("\\{[^{}]+}");
/**
* Registers a custom placeholder with its corresponding original string.
*
* @param placeholder the placeholder to register.
* @param original the original string corresponding to the placeholder.
* @return true if the placeholder was successfully registered, false if it already exists.
*/
boolean registerCustomPlaceholder(String placeholder, String original);
/**
* Resolves all placeholders within a given text.
*
* @param text the text to resolve placeholders in.
* @return a list of found placeholders.
*/
List<String> resolvePlaceholders(String text);
/**
* Parses a single placeholder for a specified player, optionally using a map of replacements.
*
* @param player the player for whom the placeholder should be parsed.
* @param placeholder the placeholder to parse.
* @param replacements a map of replacement strings for placeholders.
* @return the parsed placeholder string.
*/
String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map<String, String> replacements);
/**
* Parses all placeholders in the given text for a specified player, optionally using a map of replacements.
*
* @param player the player for whom the placeholders should be parsed.
* @param text the text containing placeholders.
* @param replacements a map of replacement strings for placeholders.
* @return the text with parsed placeholders.
*/
String parse(@Nullable OfflinePlayer player, String text, Map<String, String> replacements);
/**
* Parses all placeholders in a list of strings for a specified player, optionally using a map of replacements.
*
* @param player the player for whom the placeholders should be parsed.
* @param list the list of strings containing placeholders.
* @param replacements a map of replacement strings for placeholders.
* @return the list of strings with parsed placeholders.
*/
List<String> parse(@Nullable OfflinePlayer player, List<String> list, Map<String, String> replacements);
}

View File

@@ -15,19 +15,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.requirement;
package net.momirealms.customfishing.api.mechanic.requirement;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.mechanic.context.Context;
import org.bukkit.entity.Player;
/**
* Represents an empty requirement that always returns true when checking conditions.
*/
public class EmptyRequirement implements Requirement {
public class EmptyRequirement implements Requirement<Player> {
public static EmptyRequirement instance = new EmptyRequirement();
public static final EmptyRequirement INSTANCE = new EmptyRequirement();
@Override
public boolean isConditionMet(PlayerContext playerContext) {
public boolean isSatisfied(Context<Player> context) {
return true;
}
}

View File

@@ -22,6 +22,13 @@ import net.momirealms.customfishing.api.mechanic.context.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* The RequirementManager interface manages custom requirement types and provides methods for handling requirements.
*
* @param <T> the type of the context in which the requirements are evaluated.
*/
public interface RequirementManager<T> {
/**
@@ -42,52 +49,13 @@ public interface RequirementManager<T> {
boolean unregisterRequirement(@NotNull String type);
/**
* Retrieves an array of requirements based on a configuration section.
* Checks if a requirement type is registered.
*
* @param section The configuration section containing requirement definitions.
* @param runActions A flag indicating whether to use advanced requirements.
* @return An array of Requirement objects based on the configuration section
*/
@Nullable
Requirement<T>[] getRequirements(@NotNull Section section, boolean runActions);
/**
* If a requirement type exists
*
* @param type type
* @return exists or not
* @param type The type identifier of the requirement.
* @return True if the requirement type is registered, otherwise false.
*/
boolean hasRequirement(@NotNull String type);
/**
* Retrieves a Requirement object based on a configuration section and advanced flag.
* <p>
* requirement_1: <- section
* type: xxx
* value: xxx
*
* @param section The configuration section containing requirement definitions.
* @param runActions A flag indicating whether to use advanced requirements.
* @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid.
*/
@NotNull
Requirement<T> getRequirement(@NotNull Section section, boolean runActions);
/**
* Gets a requirement based on the provided type and value.
* If a valid RequirementFactory is found for the type, it is used to create the requirement.
* If no factory is found, a warning is logged, and an empty requirement instance is returned.
* <p>
* world: <- type
* - world <- value
*
* @param type The type representing the requirement type.
* @param value The value associated with the requirement.
* @return A Requirement instance based on the type and value, or an EmptyRequirement if the type is invalid.
*/
@NotNull
Requirement<T> getRequirement(@NotNull String type, @NotNull Object value);
/**
* Retrieves a RequirementFactory based on the specified requirement type.
*
@@ -97,6 +65,44 @@ public interface RequirementManager<T> {
@Nullable
RequirementFactory<T> getRequirementFactory(@NotNull String type);
/**
* Retrieves an array of requirements based on a configuration section.
*
* @param section The configuration section containing requirement definitions.
* @param runActions A flag indicating whether to use advanced requirements.
* @return An array of Requirement objects based on the configuration section.
*/
@NotNull
Requirement<T>[] parseRequirements(@NotNull Section section, boolean runActions);
/**
* Retrieves a Requirement object based on a configuration section and advanced flag.
*
* @param section The configuration section containing requirement definitions.
* @param runActions A flag indicating whether to use advanced requirements.
* @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid.
*/
@NotNull
Requirement<T> parseRequirement(@NotNull Section section, boolean runActions);
/**
* Gets a requirement based on the provided type and value.
* If a valid RequirementFactory is found for the type, it is used to create the requirement.
*
* @param type The type representing the requirement type.
* @param value The value associated with the requirement.
* @return A Requirement instance based on the type and value, or an EmptyRequirement if the type is invalid.
*/
@NotNull
Requirement<T> parseRequirement(@NotNull String type, @NotNull Object value);
/**
* Checks if all requirements in the provided array are satisfied within the given context.
*
* @param context The context in which the requirements are evaluated.
* @param requirements An array of requirements to check.
* @return True if all requirements are satisfied, otherwise false.
*/
static <T> boolean isSatisfied(Context<T> context, @Nullable Requirement<T>[] requirements) {
if (requirements == null) return true;
for (Requirement<T> requirement : requirements) {
@@ -106,4 +112,14 @@ public interface RequirementManager<T> {
}
return true;
}
static <T> boolean isSatisfied(Context<T> context, @Nullable List<Requirement<T>> requirements) {
if (requirements == null) return true;
for (Requirement<T> requirement : requirements) {
if (!requirement.isSatisfied(context)) {
return false;
}
}
return true;
}
}

View File

@@ -9,11 +9,11 @@ import org.incendo.cloud.context.CommandContext;
public abstract class AbstractCommandFeature<C> implements CommandFeature<C> {
protected final CustomFishingCommandManager<C> customFishingCommandManager;
protected final CustomFishingCommandManager<C> commandManager;
protected CommandConfig<C> commandConfig;
public AbstractCommandFeature(CustomFishingCommandManager<C> customFishingCommandManager) {
this.customFishingCommandManager = customFishingCommandManager;
public AbstractCommandFeature(CustomFishingCommandManager<C> commandManager) {
this.commandManager = commandManager;
}
protected abstract SenderFactory<?, C> getSenderFactory();
@@ -44,17 +44,17 @@ public abstract class AbstractCommandFeature<C> implements CommandFeature<C> {
if (context.flags().hasFlag("silent")) {
return;
}
customFishingCommandManager.handleCommandFeedback((C) context.sender(), key, args);
commandManager.handleCommandFeedback((C) context.sender(), key, args);
}
@Override
public void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args) {
customFishingCommandManager.handleCommandFeedback(sender, key, args);
commandManager.handleCommandFeedback(sender, key, args);
}
@Override
public CustomFishingCommandManager<C> getCustomFishingCommandManager() {
return customFishingCommandManager;
return commandManager;
}
@Override

View File

@@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public abstract class AbstractCustomFishingCommandManager<C> implements CustomFishingCommandManager<C> {
public abstract class AbstractCommandManager<C> implements CustomFishingCommandManager<C> {
protected final HashSet<CommandComponent<C>> registeredRootCommandComponents = new HashSet<>();
protected final HashSet<CommandFeature<C>> registeredFeatures = new HashSet<>();
@@ -38,7 +38,7 @@ public abstract class AbstractCustomFishingCommandManager<C> implements CustomFi
private TriConsumer<C, String, Component> feedbackConsumer;
public AbstractCustomFishingCommandManager(CustomFishingPlugin plugin, CommandManager<C> commandManager) {
public AbstractCommandManager(CustomFishingPlugin plugin, CommandManager<C> commandManager) {
this.commandManager = commandManager;
this.plugin = plugin;
this.inject();

View File

@@ -1,6 +1,19 @@
dependencies {
compileOnly(project(":common"))
compileOnly(project(":api"))
// papi
compileOnly("me.clip:placeholderapi:2.11.5")
// server
compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT")
// local jars
compileOnly(files("libs/AdvancedEnchantments-api.jar"))
compileOnly(files("libs/BattlePass-4.0.6-api.jar"))
compileOnly(files("libs/RealisticSeasons-api.jar"))
compileOnly(files("libs/mcMMO-api.jar"))
compileOnly(files("libs/ClueScrolls-4.8.7-api.jar"))
compileOnly(files("libs/notquests-5.17.1.jar"))
compileOnly(files("libs/zaphkiel-2.0.24.jar"))
}
java {

View File

@@ -15,19 +15,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.bukkit.compatibility.papi;
package net.momirealms.customfishing.bukkit.compatibility;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
public class ParseUtils {
public class PlaceholderAPIUtils {
public static String setPlaceholders(Player player, String text) {
public static String parse(Player player, String text) {
return PlaceholderAPI.setPlaceholders(player, text);
}
public static String setPlaceholders(OfflinePlayer player, String text) {
public static String parse(OfflinePlayer player, String text) {
return PlaceholderAPI.setPlaceholders(player, text);
}
}

View File

@@ -25,11 +25,11 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CFPapi extends PlaceholderExpansion {
public class CustomFishingPapi extends PlaceholderExpansion {
private final BukkitCustomFishingPlugin plugin;
public CFPapi(BukkitCustomFishingPlugin plugin) {
public CustomFishingPapi(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
}

View File

@@ -28,12 +28,12 @@ public class RealisticSeasonsImpl implements SeasonProvider {
@Override
public String getSeason(World world) {
return switch (SeasonsAPI.getInstance().getSeason(world)) {
case WINTER -> "winter";
case SPRING -> "spring";
case SUMMER -> "summer";
case FALL -> "autumn";
case DISABLED -> "disabled";
case RESTORE -> "restore";
case Season.WINTER -> "winter";
case Season.SPRING -> "spring";
case Season.SUMMER -> "summer";
case Season.FALL -> "autumn";
case Season.DISABLED -> "disabled";
case Season.RESTORE -> "restore";
};
}
}

View File

@@ -5,6 +5,7 @@ plugins {
dependencies {
implementation(project(":common"))
implementation(project(":api"))
implementation(project(":compatibility"))
// adventure
implementation("net.kyori:adventure-api:4.17.0")
implementation("net.kyori:adventure-text-minimessage:4.17.0")
@@ -25,8 +26,7 @@ dependencies {
compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT")
// bStats
compileOnly("org.bstats:bstats-bukkit:3.0.2")
// papi
compileOnly("me.clip:placeholderapi:2.11.5")
// config
compileOnly("dev.dejvokep:boosted-yaml:1.3.4")
// Gson
@@ -37,14 +37,10 @@ dependencies {
compileOnly("org.mongodb:mongodb-driver-sync:5.0.1")
compileOnly("com.zaxxer:HikariCP:5.0.1")
compileOnly("redis.clients:jedis:5.1.2")
// local jars
compileOnly(files("libs/AdvancedEnchantments-api.jar"))
compileOnly(files("libs/BattlePass-4.0.6-api.jar"))
compileOnly(files("libs/RealisticSeasons-api.jar"))
compileOnly(files("libs/mcMMO-api.jar"))
compileOnly(files("libs/ClueScrolls-4.8.7-api.jar"))
compileOnly(files("libs/notquests-5.17.1.jar"))
compileOnly(files("libs/zaphkiel-2.0.24.jar"))
compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}")
compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}")
compileOnly("org.incendo:cloud-paper:${rootProject.properties["cloud_paper_version"]}")
}
tasks {

View File

@@ -20,11 +20,11 @@ package net.momirealms.customfishing;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.CustomFishingReloadEvent;
import net.momirealms.customfishing.bukkit.compatibility.IntegrationManagerImpl;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.mechanic.action.ActionManagerImpl;
import net.momirealms.customfishing.mechanic.bag.BagManagerImpl;
import net.momirealms.customfishing.mechanic.block.BlockManagerImpl;
import net.momirealms.customfishing.mechanic.competition.CompetitionManagerImpl;
import net.momirealms.customfishing.bukkit.competition.CompetitionManagerImpl;
import net.momirealms.customfishing.mechanic.effect.EffectManagerImpl;
import net.momirealms.customfishing.mechanic.entity.EntityManagerImpl;
import net.momirealms.customfishing.mechanic.fishing.FishingManagerImpl;
@@ -111,7 +111,7 @@ public class BukkitCustomFishingPluginImpl extends BukkitCustomFishingPlugin {
this.lootManager = new LootManagerImpl(this);
this.marketManager = new MarketManagerImpl(this);
this.entityManager = new EntityManagerImpl(this);
this.placeholderManager = new PlaceholderManagerImpl(this);
this.placeholderManager = new BukkitPlaceholderManager(this);
this.requirementManager = new RequirementManagerImpl(this);
this.scheduler = new SchedulerImpl(this);
this.storageManager = new StorageManagerImpl(this);
@@ -150,7 +150,7 @@ public class BukkitCustomFishingPluginImpl extends BukkitCustomFishingPlugin {
if (this.integrationManager != null) ((IntegrationManagerImpl) this.integrationManager).disable();
if (this.competitionManager != null) ((CompetitionManagerImpl) this.competitionManager).disable();
if (this.storageManager != null) ((StorageManagerImpl) this.storageManager).disable();
if (this.placeholderManager != null) ((PlaceholderManagerImpl) this.placeholderManager).disable();
if (this.placeholderManager != null) ((BukkitPlaceholderManager) this.placeholderManager).disable();
if (this.statisticsManager != null) ((StatisticsManagerImpl) this.statisticsManager).disable();
if (this.actionManager != null) ((ActionManagerImpl) this.actionManager).disable();
if (this.totemManager != null) ((TotemManagerImpl) this.totemManager).disable();
@@ -198,8 +198,8 @@ public class BukkitCustomFishingPluginImpl extends BukkitCustomFishingPlugin {
((StorageManagerImpl) this.storageManager).reload();
((StatisticsManagerImpl) this.statisticsManager).unload();
((StatisticsManagerImpl) this.statisticsManager).load();
((PlaceholderManagerImpl) this.placeholderManager).unload();
((PlaceholderManagerImpl) this.placeholderManager).load();
((BukkitPlaceholderManager) this.placeholderManager).unload();
((BukkitPlaceholderManager) this.placeholderManager).load();
((HookManagerImpl) this.hookManager).unload();
((HookManagerImpl) this.hookManager).load();
this.commandManager.unload();

View File

@@ -0,0 +1,144 @@
package net.momirealms.customfishing.bukkit.action;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.kyori.adventure.audience.Audience;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.*;
import net.momirealms.customfishing.common.helper.AdventureHelper;
import net.momirealms.customfishing.common.util.ClassUtils;
import net.momirealms.customfishing.common.util.ListUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BukkitActionManager implements ActionManager<Player> {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, ActionFactory<Player>> actionFactoryMap = new HashMap<>();
private static final String EXPANSION_FOLDER = "expansions/action";
public BukkitActionManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.registerBuiltInActions();
}
@Override
public boolean registerAction(String type, ActionFactory<Player> actionFactory) {
if (this.actionFactoryMap.containsKey(type)) return false;
this.actionFactoryMap.put(type, actionFactory);
return true;
}
@Override
public boolean unregisterAction(String type) {
return this.actionFactoryMap.remove(type) != null;
}
@Nullable
@Override
public ActionFactory<Player> getActionFactory(@NotNull String type) {
return actionFactoryMap.get(type);
}
@Override
public boolean hasAction(@NotNull String type) {
return actionFactoryMap.containsKey(type);
}
@Override
public Action<Player> parseAction(Section section) {
ActionFactory<Player> factory = getActionFactory(section.getString("type"));
if (factory == null) {
plugin.getPluginLogger().warn("Action type: " + section.getString("type") + " doesn't exist.");
return EmptyAction.INSTANCE;
}
return factory.process(section.get("value"), section.getDouble("chance", 1d));
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public Action<Player>[] parseActions(@NotNull Section section) {
ArrayList<Action<Player>> actionList = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section innerSection) {
Action<Player> action = parseAction(innerSection);
if (action != null)
actionList.add(action);
}
}
return actionList.toArray(new Action[0]);
}
@Override
public Action<Player> parseAction(@NotNull String type, @NotNull Object args) {
ActionFactory<Player> factory = getActionFactory(type);
if (factory == null) {
plugin.getPluginLogger().warn("Action type: " + type + " doesn't exist.");
return EmptyAction.INSTANCE;
}
return factory.process(args, 1);
}
private void registerBuiltInActions() {
this.registerMessageAction();
}
private void registerMessageAction() {
registerAction("message", (args, chance) -> {
List<String> messages = ListUtils.toList(args);
return context -> {
if (Math.random() > chance) return;
List<String> replaced = plugin.getPlaceholderManager().parse(context.getHolder(), messages, context.toPlaceholderMap());
Audience audience = plugin.getSenderFactory().getAudience(context.getHolder());
for (String text : replaced) {
audience.sendMessage(AdventureHelper.getMiniMessage().deserialize(text));
}
};
});
}
/**
* Loads custom ActionExpansions from JAR files located in the expansion directory.
* This method scans the expansion folder for JAR files, loads classes that extend ActionExpansion,
* and registers them with the appropriate action type and ActionFactory.
*/
@SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"})
private void loadExpansions() {
File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER);
if (!expansionFolder.exists())
expansionFolder.mkdirs();
List<Class<? extends ActionExpansion<Player>>> classes = new ArrayList<>();
File[] expansionJars = expansionFolder.listFiles();
if (expansionJars == null) return;
for (File expansionJar : expansionJars) {
if (expansionJar.getName().endsWith(".jar")) {
try {
Class<? extends ActionExpansion<Player>> expansionClass = (Class<? extends ActionExpansion<Player>>) ClassUtils.findClass(expansionJar, ActionExpansion.class);
classes.add(expansionClass);
} catch (IOException | ClassNotFoundException e) {
plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e);
}
}
}
try {
for (Class<? extends ActionExpansion<Player>> expansionClass : classes) {
ActionExpansion<Player> expansion = expansionClass.getDeclaredConstructor().newInstance();
unregisterAction(expansion.getActionType());
registerAction(expansion.getActionType(), expansion.getActionFactory());
plugin.getPluginLogger().info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() );
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e);
}
}
}

View File

@@ -0,0 +1,44 @@
package net.momirealms.customfishing.bukkit.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.common.command.AbstractCommandFeature;
import net.momirealms.customfishing.common.command.CustomFishingCommandManager;
import net.momirealms.customfishing.common.sender.SenderFactory;
import net.momirealms.customfishing.common.util.Pair;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.incendo.cloud.bukkit.data.Selector;
import java.util.Collection;
public abstract class BukkitCommandFeature<C extends CommandSender> extends AbstractCommandFeature<C> {
public BukkitCommandFeature(CustomFishingCommandManager<C> commandManager) {
super(commandManager);
}
@Override
@SuppressWarnings("unchecked")
protected SenderFactory<?, C> getSenderFactory() {
return (SenderFactory<?, C>) BukkitCustomFishingPlugin.getInstance().getSenderFactory();
}
public Pair<TranslatableComponent.Builder, Component> resolveSelector(Selector<? extends Entity> selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) {
Collection<? extends Entity> entities = selector.values();
if (entities.size() == 1) {
return Pair.of(single, Component.text(entities.iterator().next().getName()));
} else {
return Pair.of(multiple, Component.text(entities.size()));
}
}
public Pair<TranslatableComponent.Builder, Component> resolveSelector(Collection<? extends Entity> selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) {
if (selector.size() == 1) {
return Pair.of(single, Component.text(selector.iterator().next().getName()));
} else {
return Pair.of(multiple, Component.text(selector.size()));
}
}
}

View File

@@ -0,0 +1,41 @@
package net.momirealms.customfishing.bukkit.command;
import net.kyori.adventure.util.Index;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.bukkit.command.feature.ReloadCommand;
import net.momirealms.customfishing.common.command.AbstractCommandManager;
import net.momirealms.customfishing.common.command.CommandFeature;
import net.momirealms.customfishing.common.sender.Sender;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.PaperCommandManager;
import java.util.List;
public class BukkitCommandManager extends AbstractCommandManager<CommandSender> {
private final List<CommandFeature<CommandSender>> FEATURES = List.of(
new ReloadCommand(this)
);
private final Index<String, CommandFeature<CommandSender>> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES);
public BukkitCommandManager(BukkitCustomFishingPlugin plugin) {
super(plugin, new PaperCommandManager<>(
plugin.getBoostrap(),
ExecutionCoordinator.simpleCoordinator(),
SenderMapper.identity()
));
}
@Override
protected Sender wrapSender(CommandSender sender) {
return ((BukkitCustomFishingPlugin) plugin).getSenderFactory().wrap(sender);
}
@Override
public Index<String, CommandFeature<CommandSender>> getFeatures() {
return INDEX;
}
}

View File

@@ -0,0 +1,24 @@
package net.momirealms.customfishing.bukkit.command.feature;
import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature;
import net.momirealms.customfishing.common.command.CustomFishingCommandManager;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.Command;
import org.incendo.cloud.CommandManager;
public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
public ReloadCommand(CustomFishingCommandManager<CommandSender> commandManager) {
super(commandManager);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return null;
}
@Override
public String getFeatureID() {
return "reload";
}
}

View File

@@ -1,220 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.bukkit.compatibility.papi;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.manager.PlaceholderManager;
import net.momirealms.customfishing.util.ConfigUtils;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class PlaceholderManagerImpl implements PlaceholderManager {
private static PlaceholderManagerImpl instance;
private final BukkitCustomFishingPlugin plugin;
private final boolean hasPapi;
private final Pattern pattern;
private final HashMap<String, String> customPlaceholderMap;
private CompetitionPapi competitionPapi;
private StatisticsPapi statisticsPapi;
private CFPapi cfPapi;
public PlaceholderManagerImpl(BukkitCustomFishingPlugin plugin) {
instance = this;
this.plugin = plugin;
this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
this.pattern = Pattern.compile("\\{[^{}]+}");
this.customPlaceholderMap = new HashMap<>();
if (this.hasPapi) {
competitionPapi = new CompetitionPapi(plugin);
statisticsPapi = new StatisticsPapi(plugin);
cfPapi = new CFPapi(plugin);
}
}
public void load() {
if (competitionPapi != null) competitionPapi.load();
if (statisticsPapi != null) statisticsPapi.load();
if (cfPapi != null) cfPapi.load();
loadCustomPlaceholders();
}
public void unload() {
if (competitionPapi != null) competitionPapi.unload();
if (statisticsPapi != null) statisticsPapi.unload();
if (cfPapi != null) cfPapi.unload();
}
public void disable() {
this.customPlaceholderMap.clear();
}
public void loadCustomPlaceholders() {
YamlConfiguration config = plugin.getConfig("config.yml");
ConfigurationSection section = config.getConfigurationSection("other-settings.placeholder-register");
if (section != null) {
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
registerCustomPlaceholder(entry.getKey(), (String) entry.getValue());
}
}
}
@Override
public boolean registerCustomPlaceholder(String placeholder, String original) {
if (this.customPlaceholderMap.containsKey(placeholder)) {
return false;
}
this.customPlaceholderMap.put(placeholder, original);
return true;
}
/**
* Set placeholders in a text string for a player.
*
* @param player The player for whom the placeholders should be set.
* @param text The text string containing placeholders.
* @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text.
*/
@Override
public String setPlaceholders(Player player, String text) {
return hasPapi ? ParseUtils.setPlaceholders(player, text) : text;
}
/**
* Set placeholders in a text string for an offline player.
*
* @param player The offline player for whom the placeholders should be set.
* @param text The text string containing placeholders.
* @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text.
*/
@Override
public String setPlaceholders(OfflinePlayer player, String text) {
return hasPapi ? ParseUtils.setPlaceholders(player, text) : text;
}
/**
* Detect and extract placeholders from a text string.
*
* @param text The text string to search for placeholders.
* @return A list of detected placeholders in the text.
*/
@Override
public List<String> detectPlaceholders(String text) {
List<String> placeholders = new ArrayList<>();
Matcher matcher = pattern.matcher(text);
while (matcher.find()) placeholders.add(matcher.group());
return placeholders;
}
/**
* Get the value associated with a single placeholder.
*
* @param player The player for whom the placeholders are being resolved (nullable).
* @param placeholder The placeholder to look up.
* @param placeholders A map of placeholders to their corresponding values.
* @return The value associated with the placeholder, or the original placeholder if not found.
*/
@Override
public String getSingleValue(@Nullable Player player, String placeholder, Map<String, String> placeholders) {
String result = null;
if (placeholders != null)
result = placeholders.get(placeholder);
if (result != null)
return result;
String custom = customPlaceholderMap.get(placeholder);
if (custom == null)
return placeholder;
return setPlaceholders(player, custom);
}
/**
* Parse a text string by replacing placeholders with their corresponding values.
*
* @param player The offline player for whom the placeholders are being resolved (nullable).
* @param text The text string containing placeholders.
* @param placeholders A map of placeholders to their corresponding values.
* @return The text string with placeholders replaced by their values.
*/
@Override
public String parse(@Nullable OfflinePlayer player, String text, Map<String, String> placeholders) {
var list = detectPlaceholders(text);
for (String papi : list) {
String replacer = null;
if (placeholders != null) {
replacer = placeholders.get(papi);
}
if (replacer == null) {
String custom = customPlaceholderMap.get(papi);
if (custom != null) {
replacer = setPlaceholders(player, parse(player, custom, placeholders));
}
}
if (replacer != null) {
text = text.replace(papi, replacer);
}
}
return text;
}
/**
* Parse a list of text strings by replacing placeholders with their corresponding values.
*
* @param player The player for whom the placeholders are being resolved (can be null for offline players).
* @param list The list of text strings containing placeholders.
* @param replacements A map of custom replacements for placeholders.
* @return The list of text strings with placeholders replaced by their values.
*/
@Override
public List<String> parse(@Nullable OfflinePlayer player, List<String> list, Map<String, String> replacements) {
return list.stream()
.map(s -> parse(player, s, replacements))
.collect(Collectors.toList());
}
public static PlaceholderManagerImpl getInstance() {
return instance;
}
public boolean hasPapi() {
return hasPapi;
}
@Override
public double getExpressionValue(Player player, String formula, Map<String, String> vars) {
return ConfigUtils.getExpressionValue(player, formula, vars);
}
@Override
public double getExpressionValue(String formula) {
return new ExpressionBuilder(formula).build().evaluate();
}
}

View File

@@ -15,21 +15,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition;
package net.momirealms.customfishing.bukkit.competition;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.event.CompetitionEvent;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfig;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfigImpl;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal;
import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition;
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
import net.momirealms.customfishing.api.mechanic.competition.RankingProvider;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.mechanic.competition.actionbar.ActionBarManager;
import net.momirealms.customfishing.mechanic.competition.bossbar.BossBarManager;
import net.momirealms.customfishing.mechanic.competition.ranking.LocalRankingImpl;
import net.momirealms.customfishing.mechanic.competition.ranking.RedisRankingImpl;
import net.momirealms.customfishing.bukkit.competition.bossbar.BossBarManager;
import net.momirealms.customfishing.bukkit.competition.ranking.LocalRankingProvider;
import net.momirealms.customfishing.bukkit.competition.actionbar.ActionBarManager;
import net.momirealms.customfishing.bukkit.competition.ranking.RedisRankingProvider;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
@@ -45,22 +45,22 @@ import java.util.concurrent.TimeUnit;
public class Competition implements FishingCompetition {
private final CompetitionConfig config;
private final CompetitionConfigImpl config;
private CancellableTask competitionTimerTask;
private final CompetitionGoal goal;
private final ConcurrentHashMap<String, String> publicPlaceholders;
private final Ranking ranking;
private final RankingProvider rankingProvider;
private float progress;
private long remainingTime;
private long startTime;
private BossBarManager bossBarManager;
private ActionBarManager actionBarManager;
public Competition(CompetitionConfig config) {
public Competition(CompetitionConfigImpl config) {
this.config = config;
this.goal = config.getGoal() == CompetitionGoal.RANDOM ? CompetitionGoal.getRandom() : config.getGoal();
if (CFConfig.redisRanking) this.ranking = new RedisRankingImpl();
else this.ranking = new LocalRankingImpl();
if (CFConfig.redisRanking) this.rankingProvider = new RedisRankingProvider();
else this.rankingProvider = new LocalRankingProvider();
this.publicPlaceholders = new ConcurrentHashMap<>();
this.publicPlaceholders.put("{goal}", BukkitCustomFishingPlugin.get().getCompetitionManager().getCompetitionGoalLocale(goal));
}
@@ -87,7 +87,7 @@ public class Competition implements FishingCompetition {
this.actionBarManager.load();
}
Action[] actions = config.getStartActions();
Action[] actions = config.startActions();
if (actions != null) {
PlayerContext playerContext = new PlayerContext(null, null, this.publicPlaceholders);
for (Action action : actions) {
@@ -95,7 +95,7 @@ public class Competition implements FishingCompetition {
}
}
this.ranking.clear();
this.rankingProvider.clear();
this.updatePublicPlaceholders();
CompetitionEvent competitionStartEvent = new CompetitionEvent(CompetitionEvent.State.START, this);
@@ -126,9 +126,9 @@ public class Competition implements FishingCompetition {
private void updatePublicPlaceholders() {
for (int i = 1; i < CFConfig.placeholderLimit + 1; i++) {
int finalI = i;
Optional.ofNullable(ranking.getPlayerAt(i)).ifPresentOrElse(player -> {
Optional.ofNullable(rankingProvider.getPlayerAt(i)).ifPresentOrElse(player -> {
publicPlaceholders.put("{" + finalI + "_player}", player);
publicPlaceholders.put("{" + finalI + "_score}", String.format("%.2f", ranking.getScoreAt(finalI)));
publicPlaceholders.put("{" + finalI + "_score}", String.format("%.2f", rankingProvider.getScoreAt(finalI)));
}, () -> {
publicPlaceholders.put("{" + finalI + "_player}", CFLocale.MSG_No_Player);
publicPlaceholders.put("{" + finalI + "_score}", CFLocale.MSG_No_Score);
@@ -150,7 +150,7 @@ public class Competition implements FishingCompetition {
if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel();
if (this.bossBarManager != null) this.bossBarManager.unload();
if (this.actionBarManager != null) this.actionBarManager.unload();
this.ranking.clear();
this.rankingProvider.clear();
this.remainingTime = 0;
if (triggerEvent) {
@@ -176,8 +176,8 @@ public class Competition implements FishingCompetition {
// give prizes
HashMap<String, Action[]> rewardsMap = config.getRewards();
if (ranking.getSize() != 0 && rewardsMap != null) {
Iterator<Pair<String, Double>> iterator = ranking.getIterator();
if (rankingProvider.getSize() != 0 && rewardsMap != null) {
Iterator<Pair<String, Double>> iterator = rankingProvider.getIterator();
int i = 1;
while (iterator.hasNext()) {
Pair<String, Double> competitionPlayer = iterator.next();
@@ -218,7 +218,7 @@ public class Competition implements FishingCompetition {
}
// 1 seconds delay for other servers to read the redis data
BukkitCustomFishingPlugin.get().getScheduler().runTaskAsyncLater(this.ranking::clear, 1, TimeUnit.SECONDS);
BukkitCustomFishingPlugin.get().getScheduler().runTaskAsyncLater(this.rankingProvider::clear, 1, TimeUnit.SECONDS);
}
/**
@@ -270,11 +270,11 @@ public class Competition implements FishingCompetition {
// refresh data
switch (this.goal) {
case CATCH_AMOUNT -> ranking.refreshData(player.getName(), 1);
case TOTAL_SIZE, TOTAL_SCORE -> ranking.refreshData(player.getName(), score);
case CATCH_AMOUNT -> rankingProvider.refreshData(player.getName(), 1);
case TOTAL_SIZE, TOTAL_SCORE -> rankingProvider.refreshData(player.getName(), score);
case MAX_SIZE -> {
if (score > ranking.getPlayerScore(player.getName())) {
ranking.setData(player.getName(), score);
if (score > rankingProvider.getPlayerScore(player.getName())) {
rankingProvider.setData(player.getName(), score);
}
}
}
@@ -288,7 +288,7 @@ public class Competition implements FishingCompetition {
*/
@Override
public boolean hasPlayerJoined(OfflinePlayer player) {
return ranking.getPlayerRank(player.getName()) != -1;
return rankingProvider.getPlayerRank(player.getName()) != -1;
}
/**
@@ -328,7 +328,7 @@ public class Competition implements FishingCompetition {
*/
@NotNull
@Override
public CompetitionConfig getConfig() {
public CompetitionConfigImpl getConfig() {
return config;
}
@@ -350,8 +350,8 @@ public class Competition implements FishingCompetition {
*/
@NotNull
@Override
public Ranking getRanking() {
return ranking;
public RankingProvider getRanking() {
return rankingProvider;
}
/**

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition;
package net.momirealms.customfishing.bukkit.competition;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.common.Pair;
@@ -42,8 +42,8 @@ import java.util.concurrent.TimeUnit;
public class CompetitionManagerImpl implements CompetitionManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<CompetitionSchedule, CompetitionConfig> timeConfigMap;
private final HashMap<String, CompetitionConfig> commandConfigMap;
private final HashMap<CompetitionSchedule, CompetitionConfigImpl> timeConfigMap;
private final HashMap<String, CompetitionConfigImpl> commandConfigMap;
private Competition currentCompetition;
private CancellableTask timerCheckTask;
private int nextCompetitionSeconds;
@@ -124,12 +124,12 @@ public class CompetitionManagerImpl implements CompetitionManager {
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection section) {
CompetitionConfig.Builder builder = new CompetitionConfig.Builder(entry.getKey())
CompetitionConfigImpl.Builder builder = new CompetitionConfigImpl.Builder(entry.getKey())
.goal(CompetitionGoal.valueOf(section.getString("goal", "TOTAL_SCORE").toUpperCase(Locale.ENGLISH)))
.minPlayers(section.getInt("min-players", 0))
.duration(section.getInt("duration", 300))
.rewards(getPrizeActions(section.getConfigurationSection("rewards")))
.requirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("participate-requirements"), false))
.requirements(plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("participate-requirements"), false))
.joinActions(plugin.getActionManager().getActions(section.getConfigurationSection("participate-actions")))
.startActions(plugin.getActionManager().getActions(section.getConfigurationSection("start-actions")))
.endActions(plugin.getActionManager().getActions(section.getConfigurationSection("end-actions")))
@@ -155,7 +155,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
.build());
}
CompetitionConfig competitionConfig = builder.build();
CompetitionConfigImpl competitionConfigImpl = builder.build();
List<Pair<Integer, Integer>> timePairs = section.getStringList("start-time")
.stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, ":")).toList();
List<Integer> weekdays = section.getIntegerList("start-weekday");
@@ -165,10 +165,10 @@ public class CompetitionManagerImpl implements CompetitionManager {
for (Integer weekday : weekdays) {
for (Pair<Integer, Integer> timePair : timePairs) {
CompetitionSchedule schedule = new CompetitionSchedule(weekday, timePair.left(), timePair.right(), 0);
timeConfigMap.put(schedule, competitionConfig);
timeConfigMap.put(schedule, competitionConfigImpl);
}
}
commandConfigMap.put(entry.getKey(), competitionConfig);
commandConfigMap.put(entry.getKey(), competitionConfigImpl);
}
}
}
@@ -207,7 +207,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
nextCompetitionTime = Math.min(nextCompetitionTime, schedule.getTimeDelta(seconds));
}
this.nextCompetitionSeconds = nextCompetitionTime;
CompetitionConfig config = timeConfigMap.get(competitionSchedule);
CompetitionConfigImpl config = timeConfigMap.get(competitionSchedule);
if (config != null) {
startCompetition(config, false, null);
}
@@ -241,7 +241,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
@Override
public boolean startCompetition(String competition, boolean force, String serverGroup) {
CompetitionConfig config = commandConfigMap.get(competition);
CompetitionConfigImpl config = commandConfigMap.get(competition);
if (config == null) {
LogUtils.warn("Competition " + competition + " doesn't exist.");
return false;
@@ -262,7 +262,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
}
@Override
public boolean startCompetition(CompetitionConfig config, boolean force, @Nullable String serverGroup) {
public boolean startCompetition(CompetitionConfigImpl config, boolean force, @Nullable String serverGroup) {
if (!force) {
int players = Bukkit.getOnlinePlayers().size();
if (players < config.getMinPlayersToStart()) {
@@ -286,7 +286,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
}
}
private void start(CompetitionConfig config) {
private void start(CompetitionConfigImpl config) {
if (getOnGoingCompetition() != null) {
// END
currentCompetition.end(true);
@@ -318,11 +318,11 @@ public class CompetitionManagerImpl implements CompetitionManager {
* Retrieves the configuration for a competition based on its key.
*
* @param key The key of the competition configuration to retrieve.
* @return The {@link CompetitionConfig} for the specified key, or {@code null} if no configuration exists with that key.
* @return The {@link CompetitionConfigImpl} for the specified key, or {@code null} if no configuration exists with that key.
*/
@Nullable
@Override
public CompetitionConfig getConfig(String key) {
public CompetitionConfigImpl getConfig(String key) {
return commandConfigMap.get(key);
}
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition;
package net.momirealms.customfishing.bukkit.competition;
public class CompetitionSchedule {

View File

@@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.actionbar;
package net.momirealms.customfishing.bukkit.competition.actionbar;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfigImpl;
import net.momirealms.customfishing.mechanic.competition.Competition;
import net.momirealms.customfishing.bukkit.competition.Competition;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;

View File

@@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.actionbar;
package net.momirealms.customfishing.bukkit.competition.actionbar;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfigImpl;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.mechanic.competition.Competition;
import net.momirealms.customfishing.bukkit.competition.Competition;
import net.momirealms.customfishing.mechanic.misc.DynamicText;
import org.bukkit.entity.Player;

View File

@@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.bossbar;
package net.momirealms.customfishing.bukkit.competition.bossbar;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfigImpl;
import net.momirealms.customfishing.mechanic.competition.Competition;
import net.momirealms.customfishing.bukkit.competition.Competition;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.bossbar;
package net.momirealms.customfishing.bukkit.competition.bossbar;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.InternalStructure;
@@ -28,7 +28,7 @@ import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfigImpl;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.api.util.ReflectionUtils;
import net.momirealms.customfishing.mechanic.competition.Competition;
import net.momirealms.customfishing.bukkit.competition.Competition;
import net.momirealms.customfishing.mechanic.misc.DynamicText;
import org.bukkit.boss.BarColor;
import org.bukkit.entity.Player;

View File

@@ -15,22 +15,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.ranking;
package net.momirealms.customfishing.bukkit.competition.ranking;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer;
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
import net.momirealms.customfishing.api.mechanic.competition.RankingProvider;
import java.util.*;
/**
* Implementation of the Ranking interface that manages the ranking of competition players locally.
*/
public class LocalRankingImpl implements Ranking {
public class LocalRankingProvider implements RankingProvider {
private final Set<CompetitionPlayer> competitionPlayers;
public LocalRankingImpl() {
public LocalRankingProvider() {
competitionPlayers = Collections.synchronizedSet(new TreeSet<>());
}

View File

@@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.mechanic.competition.ranking;
package net.momirealms.customfishing.bukkit.competition.ranking;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer;
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
import net.momirealms.customfishing.api.mechanic.competition.RankingProvider;
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.resps.Tuple;
@@ -27,7 +27,7 @@ import redis.clients.jedis.resps.Tuple;
import java.util.Iterator;
import java.util.List;
public class RedisRankingImpl implements Ranking {
public class RedisRankingProvider implements RankingProvider {
/**
* Clears the ranking data by removing all players and scores.

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.bukkit.misc.placeholder;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.misc.placeholder.PlaceholderManager;
import net.momirealms.customfishing.bukkit.compatibility.PlaceholderAPIUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
public class BukkitPlaceholderManager implements PlaceholderManager {
private final BukkitCustomFishingPlugin plugin;
private final boolean hasPapi;
private final HashMap<String, String> customPlaceholderMap;
public BukkitPlaceholderManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
this.customPlaceholderMap = new HashMap<>();
}
@Override
public boolean registerCustomPlaceholder(String placeholder, String original) {
if (this.customPlaceholderMap.containsKey(placeholder)) return false;
this.customPlaceholderMap.put(placeholder, original);
return true;
}
@Override
public List<String> resolvePlaceholders(String text) {
List<String> placeholders = new ArrayList<>();
Matcher matcher = PATTERN.matcher(text);
while (matcher.find()) placeholders.add(matcher.group());
return placeholders;
}
private String setPlaceholders(OfflinePlayer player, String text) {
return hasPapi ? PlaceholderAPIUtils.parse(player, text) : text;
}
@Override
public String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map<String, String> replacements) {
String result = null;
if (replacements != null)
result = replacements.get(placeholder);
if (result != null)
return result;
String custom = customPlaceholderMap.get(placeholder);
if (custom == null)
return placeholder;
return hasPapi ? PlaceholderAPIUtils.parse(player, custom) : custom;
}
@Override
public String parse(@Nullable OfflinePlayer player, String text, Map<String, String> replacements) {
var list = resolvePlaceholders(text);
for (String papi : list) {
String replacer = null;
if (replacements != null) {
replacer = replacements.get(papi);
}
if (replacer == null) {
String custom = customPlaceholderMap.get(papi);
if (custom != null) {
replacer = setPlaceholders(player, parse(player, custom, replacements));
}
}
if (replacer != null) {
text = text.replace(papi, replacer);
}
}
return text;
}
@Override
public List<String> parse(@Nullable OfflinePlayer player, List<String> list, Map<String, String> replacements) {
return list.stream()
.map(s -> parse(player, s, replacements))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,280 @@
package net.momirealms.customfishing.bukkit.requirement;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.action.ActionManager;
import net.momirealms.customfishing.api.mechanic.context.ContextKeys;
import net.momirealms.customfishing.api.mechanic.effect.EffectProperties;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementExpansion;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.common.util.ClassUtils;
import net.momirealms.customfishing.common.util.ListUtils;
import net.momirealms.customfishing.common.util.Pair;
import net.momirealms.customfishing.api.mechanic.requirement.EmptyRequirement;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class BukkitRequirementManager implements RequirementManager<Player> {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, RequirementFactory<Player>> requirementFactoryMap = new HashMap<>();
private static final String EXPANSION_FOLDER = "expansions/requirement";
public BukkitRequirementManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.registerBuiltInRequirements();
this.loadExpansions();
}
@Override
public boolean registerRequirement(@NotNull String type, @NotNull RequirementFactory<Player> requirementFactory) {
if (this.requirementFactoryMap.containsKey(type)) return false;
this.requirementFactoryMap.put(type, requirementFactory);
return true;
}
@Override
public boolean unregisterRequirement(@NotNull String type) {
return this.requirementFactoryMap.remove(type) != null;
}
@Nullable
@Override
public RequirementFactory<Player> getRequirementFactory(@NotNull String type) {
return requirementFactoryMap.get(type);
}
@Override
public boolean hasRequirement(@NotNull String type) {
return requirementFactoryMap.containsKey(type);
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public Requirement<Player>[] parseRequirements(@NotNull Section section, boolean runActions) {
List<Requirement<Player>> requirements = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
String typeOrName = entry.getKey();
if (hasRequirement(typeOrName)) {
requirements.add(parseRequirement(typeOrName, entry.getValue()));
} else {
requirements.add(parseRequirement(section.getSection(typeOrName), runActions));
}
}
return requirements.toArray(new Requirement[0]);
}
@NotNull
@Override
public Requirement<Player> parseRequirement(@NotNull Section section, boolean runActions) {
List<Action<Player>> actionList = new ArrayList<>();
if (runActions && section.contains("not-met-actions")) {
actionList.addAll(List.of(plugin.getActionManager().parseActions(requireNonNull(section.getSection("not-met-actions")))));
}
String type = section.getString("type");
if (type == null) {
plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString());
return EmptyRequirement.INSTANCE;
}
var factory = getRequirementFactory(type);
if (factory == null) {
plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString());
return EmptyRequirement.INSTANCE;
}
return factory.process(section.get("value"), actionList, runActions);
}
@NotNull
@Override
public Requirement<Player> parseRequirement(@NotNull String type, @NotNull Object value) {
RequirementFactory<Player> factory = getRequirementFactory(type);
if (factory == null) {
plugin.getPluginLogger().warn("Requirement type: " + type + " doesn't exist.");
return EmptyRequirement.INSTANCE;
}
return factory.process(value);
}
private void registerBuiltInRequirements() {
this.registerTimeRequirement();
this.registerYRequirement();
this.registerInWaterRequirement();
this.registerInVoidRequirement();
this.registerInLavaRequirement();
this.registerAndRequirement();
this.registerOrRequirement();
}
private void registerTimeRequirement() {
registerRequirement("time", (args, actions, advanced) -> {
List<String> list = ListUtils.toList(args);
List<Pair<Integer, Integer>> timePairs = list.stream().map(line -> {
String[] split = line.split("~");
return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
}).toList();
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
long time = location.getWorld().getTime();
for (Pair<Integer, Integer> pair : timePairs)
if (time >= pair.left() && time <= pair.right())
return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
}
private void registerYRequirement() {
registerRequirement("ypos", (args, actions, advanced) -> {
List<String> list = ListUtils.toList(args);
List<Pair<Double, Double>> posPairs = list.stream().map(line -> {
String[] split = line.split("~");
return new Pair<>(Double.parseDouble(split[0]), Double.parseDouble(split[1]));
}).toList();
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
double y = location.getY();
for (Pair<Double, Double> pair : posPairs)
if (y >= pair.left() && y <= pair.right())
return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
}
private void registerOrRequirement() {
registerRequirement("||", (args, actions, advanced) -> {
if (args instanceof Section section) {
Requirement<Player>[] requirements = parseRequirements(section, advanced);
return context -> {
for (Requirement<Player> requirement : requirements)
if (requirement.isSatisfied(context))
return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at || requirement which should be Section");
return EmptyRequirement.INSTANCE;
}
});
}
private void registerAndRequirement() {
registerRequirement("&&", (args, actions, advanced) -> {
if (args instanceof Section section) {
Requirement<Player>[] requirements = parseRequirements(section, advanced);
return context -> {
outer: {
for (Requirement<Player> requirement : requirements)
if (!requirement.isSatisfied(context))
break outer;
return true;
}
if (advanced) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at && requirement which should be Section");
return EmptyRequirement.INSTANCE;
}
});
}
private void registerInWaterRequirement() {
registerRequirement("in-water", (args, actions, advanced) -> {
boolean inWater = (boolean) args;
return context -> {
boolean in_water = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.WATER_FISHING.key());
if (in_water == inWater) return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
}
private void registerInVoidRequirement() {
registerRequirement("in-void", (args, actions, advanced) -> {
boolean inWater = (boolean) args;
return context -> {
boolean in_water = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.VOID_FISHING.key());
if (in_water == inWater) return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
}
private void registerInLavaRequirement() {
registerRequirement("lava-fishing", (args, actions, advanced) -> {
boolean inLava = (boolean) args;
if (!inLava) {
throw new IllegalArgumentException("");
}
return context -> {
boolean in_lava = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.LAVA_FISHING.key());
if (in_lava) return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
registerRequirement("in-lava", (args, actions, advanced) -> {
boolean inLava = (boolean) args;
return context -> {
boolean in_lava = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.LAVA_FISHING.key());
if (in_lava == inLava) return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
});
}
/**
* Loads requirement expansions from external JAR files located in the expansion folder.
* Each expansion JAR should contain classes that extends the RequirementExpansion class.
* Expansions are registered and used to create custom requirements.
*/
@SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"})
private void loadExpansions() {
File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER);
if (!expansionFolder.exists())
expansionFolder.mkdirs();
List<Class<? extends RequirementExpansion<Player>>> classes = new ArrayList<>();
File[] expansionJars = expansionFolder.listFiles();
if (expansionJars == null) return;
for (File expansionJar : expansionJars) {
if (expansionJar.getName().endsWith(".jar")) {
try {
Class<? extends RequirementExpansion<Player>> expansionClass = (Class<? extends RequirementExpansion<Player>>) ClassUtils.findClass(expansionJar, RequirementExpansion.class);
classes.add(expansionClass);
} catch (IOException | ClassNotFoundException e) {
plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e);
}
}
}
try {
for (Class<? extends RequirementExpansion<Player>> expansionClass : classes) {
RequirementExpansion<Player> expansion = expansionClass.getDeclaredConstructor().newInstance();
unregisterRequirement(expansion.getRequirementType());
registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory());
plugin.getPluginLogger().info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor());
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e);
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 net.momirealms.customfishing.bukkit.sender;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.common.sender.Sender;
import net.momirealms.customfishing.common.sender.SenderFactory;
import net.momirealms.customfishing.common.util.Tristate;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<BukkitCustomFishingPlugin, CommandSender> {
private final BukkitAudiences audiences;
public BukkitSenderFactory(BukkitCustomFishingPlugin plugin) {
super(plugin);
this.audiences = BukkitAudiences.create(plugin.getBoostrap());
}
@Override
protected String getName(CommandSender sender) {
if (sender instanceof Player) {
return sender.getName();
}
return Sender.CONSOLE_NAME;
}
@Override
protected UUID getUniqueId(CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getUniqueId();
}
return Sender.CONSOLE_UUID;
}
@Override
public Audience getAudience(CommandSender sender) {
return this.audiences.sender(sender);
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
getAudience(sender).sendMessage(message);
} else {
getPlugin().getScheduler().executeSync(() -> getAudience(sender).sendMessage(message));
}
}
@Override
protected Tristate getPermissionValue(CommandSender sender, String node) {
if (sender.hasPermission(node)) {
return Tristate.TRUE;
} else if (sender.isPermissionSet(node)) {
return Tristate.FALSE;
} else {
return Tristate.UNDEFINED;
}
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
@Override
protected void performCommand(CommandSender sender, String command) {
getPlugin().getBoostrap().getServer().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
public void close() {
super.close();
this.audiences.close();
}
}

View File

@@ -30,8 +30,9 @@ import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.api.mechanic.action.EmptyAction;
import net.momirealms.customfishing.bukkit.compatibility.VaultHook;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.common.util.ClassUtils;
import net.momirealms.customfishing.util.*;
import org.bukkit.Bukkit;
@@ -157,9 +158,9 @@ public class ActionManagerImpl implements ActionManager {
if (factory == null) {
LogUtils.warn("Action type: " + section.getString("type") + " doesn't exist.");
// to prevent NPE
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
return factory.build(
return factory.process(
section.get("value"),
section.getDouble("chance", 1d)
);
@@ -297,7 +298,7 @@ public class ActionManagerImpl implements ActionManager {
ArrayList<String> msg = ConfigUtils.stringListArgs(args);
return condition -> {
if (Math.random() > chance) return;
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(
condition.getPlayer(),
msg,
condition.getArgs()
@@ -311,7 +312,7 @@ public class ActionManagerImpl implements ActionManager {
ArrayList<String> msg = ConfigUtils.stringListArgs(args);
return condition -> {
if (Math.random() > chance) return;
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(
condition.getPlayer(),
msg,
condition.getArgs()
@@ -335,7 +336,7 @@ public class ActionManagerImpl implements ActionManager {
double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation());
if (distance <= range) {
condition.insertArg("{near}", player.getName());
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(
owner,
msg,
condition.getArgs()
@@ -350,7 +351,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: message-nearby");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
registerAction("random-message", (args, chance) -> {
@@ -358,7 +359,7 @@ public class ActionManagerImpl implements ActionManager {
return condition -> {
if (Math.random() > chance) return;
String random = msg.get(ThreadLocalRandom.current().nextInt(msg.size()));
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
random = BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
AdventureHelper.getInstance().sendPlayerMessage(condition.getPlayer(), random);
};
});
@@ -369,7 +370,7 @@ public class ActionManagerImpl implements ActionManager {
ArrayList<String> cmd = ConfigUtils.stringListArgs(args);
return condition -> {
if (Math.random() > chance) return;
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(
condition.getPlayer(),
cmd,
condition.getArgs()
@@ -386,7 +387,7 @@ public class ActionManagerImpl implements ActionManager {
return condition -> {
if (Math.random() > chance) return;
String random = cmd.get(ThreadLocalRandom.current().nextInt(cmd.size()));
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
random = BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
String finalRandom = random;
plugin.getScheduler().runTaskSync(() -> {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom);
@@ -405,7 +406,7 @@ public class ActionManagerImpl implements ActionManager {
double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation());
if (distance <= range) {
condition.insertArg("{near}", player.getName());
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(
owner,
cmd,
condition.getArgs()
@@ -420,7 +421,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: command-nearby");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -442,7 +443,7 @@ public class ActionManagerImpl implements ActionManager {
return condition -> {
if (Math.random() > chance) return;
String random = texts.get(ThreadLocalRandom.current().nextInt(texts.size()));
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
random = BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
AdventureHelper.getInstance().sendActionbar(condition.getPlayer(), random);
};
});
@@ -458,7 +459,7 @@ public class ActionManagerImpl implements ActionManager {
double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation());
if (distance <= range) {
condition.insertArg("{near}", player.getName());
String replaced = PlaceholderManagerImpl.getInstance().parse(
String replaced = BukkitPlaceholderManager.getInstance().parse(
owner,
actionbar,
condition.getArgs()
@@ -472,7 +473,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: command-nearby");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -545,7 +546,7 @@ public class ActionManagerImpl implements ActionManager {
(Player) player,
location.clone().add(x, y, z),
AdventureHelper.getInstance().getComponentFromMiniMessage(
PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs())
BukkitPlaceholderManager.getInstance().parse(owner, text, condition.getArgs())
),
duration
);
@@ -558,7 +559,7 @@ public class ActionManagerImpl implements ActionManager {
owner,
location.clone().add(x, y, z),
AdventureHelper.getInstance().getComponentFromMiniMessage(
PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs())
BukkitPlaceholderManager.getInstance().parse(owner, text, condition.getArgs())
),
duration
);
@@ -566,7 +567,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: hologram");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -584,7 +585,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: item-amount");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -606,7 +607,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: durability");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -623,7 +624,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: give-item");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -686,7 +687,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: fake-item");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -825,8 +826,8 @@ public class ActionManagerImpl implements ActionManager {
if (Math.random() > chance) return;
AdventureHelper.getInstance().sendTitle(
condition.getPlayer(),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), title, condition.getArgs()),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), title, condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()),
fadeIn,
stay,
fadeOut
@@ -834,7 +835,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: title");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
registerAction("title-nearby", (args, chance) -> {
@@ -854,8 +855,8 @@ public class ActionManagerImpl implements ActionManager {
condition.insertArg("{near}", player.getName());
AdventureHelper.getInstance().sendTitle(
condition.getPlayer(),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), title, condition.getArgs()),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), title, condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()),
fadeIn,
stay,
fadeOut
@@ -868,7 +869,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: title-nearby");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
registerAction("random-title", (args, chance) -> {
@@ -884,8 +885,8 @@ public class ActionManagerImpl implements ActionManager {
if (Math.random() > chance) return;
AdventureHelper.getInstance().sendTitle(
condition.getPlayer(),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), condition.getArgs()),
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), condition.getArgs()),
BukkitPlaceholderManager.getInstance().parse(condition.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), condition.getArgs()),
fadeIn,
stay,
fadeOut
@@ -893,7 +894,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: random-title");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -912,7 +913,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: potion-effect");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -944,7 +945,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: sound");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -953,7 +954,7 @@ public class ActionManagerImpl implements ActionManager {
registerAction("conditional", (args, chance) -> {
if (args instanceof ConfigurationSection section) {
Action[] actions = getActions(section.getConfigurationSection("actions"));
Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true);
Requirement[] requirements = plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("conditions"), true);
return condition -> {
if (Math.random() > chance) return;
if (requirements != null)
@@ -968,7 +969,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: conditional");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -980,7 +981,7 @@ public class ActionManagerImpl implements ActionManager {
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection inner) {
Action[] actions = getActions(inner.getConfigurationSection("actions"));
Requirement[] requirements = plugin.getRequirementManager().getRequirements(inner.getConfigurationSection("conditions"), false);
Requirement[] requirements = plugin.getRequirementManager().parseRequirements(inner.getConfigurationSection("conditions"), false);
conditionActionPairList.add(Pair.of(requirements, actions));
}
}
@@ -1003,7 +1004,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: priority");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}
@@ -1022,7 +1023,7 @@ public class ActionManagerImpl implements ActionManager {
};
} else {
LogUtils.warn("Illegal value format found at action: plugin-exp");
return EmptyAction.instance;
return EmptyAction.INSTANCE;
}
});
}

View File

@@ -1,16 +0,0 @@
package net.momirealms.customfishing.mechanic.action;
import net.momirealms.customfishing.api.mechanic.action.Action;
/**
* An implementation of the Action interface that represents an empty action with no behavior.
* This class serves as a default action to prevent NPE.
*/
public class EmptyAction implements Action {
public static EmptyAction instance = new EmptyAction();
@Override
public void trigger(PlayerContext playerContext) {
}
}

View File

@@ -23,7 +23,7 @@ import net.momirealms.customfishing.api.storage.user.UserData;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.util.InventoryUtils;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
@@ -73,7 +73,7 @@ public class BagManagerImpl implements BagManager, Listener {
if (bagStoreLoots) {
collectLootActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("collect-actions"));
bagFullActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("full-actions"));
collectRequirements = plugin.getRequirementManager().getRequirements(bagSection.getConfigurationSection("collect-requirements"), false);
collectRequirements = plugin.getRequirementManager().parseRequirements(bagSection.getConfigurationSection("collect-requirements"), false);
}
}
}
@@ -106,7 +106,7 @@ public class BagManagerImpl implements BagManager, Listener {
if (bag.getSize() != rows * 9) {
Inventory newBag = InventoryUtils.createInventory(onlinePlayer.getHolder(), rows * 9,
AdventureHelper.getInstance().getComponentFromMiniMessage(
PlaceholderManagerImpl.getInstance().parse(
BukkitPlaceholderManager.getInstance().parse(
player, bagTitle, Map.of("{player}", player.getName())
)
));

View File

@@ -165,7 +165,7 @@ public class EffectManagerImpl implements EffectManager {
if (section == null) return null;
return new EffectCarrier.Builder()
.key(key)
.requirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), true))
.requirements(plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("requirements"), true))
.effect(getEffectModifiers(section.getConfigurationSection("effects")))
.actionMap(plugin.getActionManager().getActionMap(section.getConfigurationSection("events")))
.build();
@@ -368,7 +368,7 @@ public class EffectManagerImpl implements EffectManager {
return ((effect, condition) -> effect.setLavaFishing(true));
}
case "conditional" -> {
Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true);
Requirement[] requirements = plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("conditions"), true);
EffectModifier[] modifiers = getEffectModifiers(section.getConfigurationSection("effects"));
return ((effect, condition) -> {
for (Requirement requirement : requirements)

View File

@@ -708,7 +708,7 @@ public class FishingManagerImpl implements Listener, FishingManager {
*/
private void doSuccessActions(Loot loot, Effect effect, FishingPreparation fishingPreparation, Player player) {
FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition();
if (competition != null && RequirementManager.isRequirementMet(fishingPreparation, competition.getConfig().getRequirements())) {
if (competition != null && RequirementManager.isRequirementMet(fishingPreparation, competition.getConfig().getJoinRequirements())) {
String scoreStr = fishingPreparation.getArg("{CUSTOM_SCORE}");
if (scoreStr != null) {
competition.refreshData(player, Double.parseDouble(scoreStr));

View File

@@ -38,7 +38,7 @@ import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.bukkit.compatibility.item.CustomFishingItemImpl;
import net.momirealms.customfishing.bukkit.compatibility.item.VanillaItemImpl;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.util.ConfigUtils;
import net.momirealms.customfishing.util.ItemUtils;
import net.momirealms.customfishing.util.LocationUtils;
@@ -580,7 +580,7 @@ public class ItemManagerImpl implements ItemManager, Listener {
editors.put("name", (player, nbtItem, placeholders) -> {
nbtItem.set(AdventureHelper.getInstance().componentToJson(
AdventureHelper.getInstance().getComponentFromMiniMessage(
"<!i>" + PlaceholderManagerImpl.getInstance().parse(player, name, placeholders)
"<!i>" + BukkitPlaceholderManager.getInstance().parse(player, name, placeholders)
)
), "display", "Name");
});
@@ -635,7 +635,7 @@ public class ItemManagerImpl implements ItemManager, Listener {
editors.put("lore", (player, nbtItem, placeholders) -> {
List<String> list = new ArrayList<>(lore.stream().map(s -> AdventureHelper.getInstance().componentToJson(
AdventureHelper.getInstance().getComponentFromMiniMessage(
"<!i>" + PlaceholderManagerImpl.getInstance().parse(player, s, placeholders)
"<!i>" + BukkitPlaceholderManager.getInstance().parse(player, s, placeholders)
)
)).toList());
nbtItem.set(list, "display", "Lore");

View File

@@ -240,7 +240,7 @@ public class LootManagerImpl implements LootManager {
if (section.contains("requirements") && section.contains("weight")) {
plugin.getRequirementManager().putLegacyLootToMap(
loot.getID(),
plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), false),
plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("requirements"), false),
section.getDouble("weight", 0)
);
}

View File

@@ -24,7 +24,7 @@ import net.momirealms.customfishing.api.mechanic.market.MarketManager;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.util.ConfigUtils;
import net.momirealms.customfishing.util.NumberUtils;
import net.objecthunter.exp4j.ExpressionBuilder;
@@ -497,8 +497,8 @@ public class MarketManagerImpl implements MarketManager, Listener {
@Override
public double getFishPrice(Player player, Map<String, String> vars) {
String temp = PlaceholderManagerImpl.getInstance().parse(player, formula, vars);
var placeholders = PlaceholderManagerImpl.getInstance().detectPlaceholders(temp);
String temp = BukkitPlaceholderManager.getInstance().parse(player, formula, vars);
var placeholders = BukkitPlaceholderManager.getInstance().resolvePlaceholders(temp);
for (String placeholder : placeholders) {
temp = temp.replace(placeholder, "0");
}
@@ -533,7 +533,7 @@ public class MarketManagerImpl implements MarketManager, Listener {
@Override
public double getEarningLimit(Player player) {
return new ExpressionBuilder(
PlaceholderManagerImpl.getInstance().parse(
BukkitPlaceholderManager.getInstance().parse(
player,
earningLimitExpression,
new HashMap<>()

View File

@@ -17,7 +17,7 @@
package net.momirealms.customfishing.mechanic.misc;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import org.bukkit.entity.Player;
import java.util.ArrayList;
@@ -39,7 +39,7 @@ public class DynamicText {
private void analyze(String value) {
// Analyze the provided text to find and replace placeholders with '%s'.
// Store the original value, placeholders, and the initial latest value.
List<String> placeholdersOwner = new ArrayList<>(PlaceholderManagerImpl.getInstance().detectPlaceholders(value));
List<String> placeholdersOwner = new ArrayList<>(BukkitPlaceholderManager.getInstance().resolvePlaceholders(value));
String origin = value;
for (String placeholder : placeholdersOwner) {
origin = origin.replace(placeholder, "%s");
@@ -57,13 +57,13 @@ public class DynamicText {
// Update the dynamic text by replacing placeholders with actual values.
String string = originalValue;
if (this.placeholders.length != 0) {
PlaceholderManagerImpl placeholderManagerImpl = PlaceholderManagerImpl.getInstance();
BukkitPlaceholderManager bukkitPlaceholderManager = BukkitPlaceholderManager.getInstance();
if ("%s".equals(originalValue)) {
string = placeholderManagerImpl.getSingleValue(owner, this.placeholders[0], placeholders);
string = bukkitPlaceholderManager.getSingleValue(owner, this.placeholders[0], placeholders);
} else {
Object[] values = new String[this.placeholders.length];
for (int i = 0; i < this.placeholders.length; i++) {
values[i] = placeholderManagerImpl.getSingleValue(owner, this.placeholders[i], placeholders);
values[i] = bukkitPlaceholderManager.getSingleValue(owner, this.placeholders[i], placeholders);
}
string = String.format(originalValue, values);
}

View File

@@ -21,15 +21,12 @@ import net.momirealms.customfishing.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.integration.LevelerProvider;
import net.momirealms.customfishing.api.integration.SeasonProvider;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.api.mechanic.requirement.*;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementExpansion;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory;
import net.momirealms.customfishing.bukkit.compatibility.VaultHook;
import net.momirealms.customfishing.bukkit.compatibility.papi.ParseUtils;
import net.momirealms.customfishing.bukkit.misc.placeholder.papi.ParseUtils;
import net.momirealms.customfishing.common.util.ClassUtils;
import net.momirealms.customfishing.util.ClassUtils;
import net.momirealms.customfishing.util.ConfigUtils;
@@ -271,7 +268,7 @@ public class RequirementManagerImpl implements RequirementManager {
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
String typeOrName = entry.getKey();
if (hasRequirement(typeOrName)) {
requirements.add(getRequirement(typeOrName, entry.getValue()));
requirements.add(parseRequirement(typeOrName, entry.getValue()));
} else {
requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced));
}
@@ -294,7 +291,7 @@ public class RequirementManagerImpl implements RequirementManager {
@NotNull
@Override
public Requirement getRequirement(ConfigurationSection section, boolean advanced) {
if (section == null) return EmptyRequirement.instance;
if (section == null) return EmptyRequirement.INSTANCE;
List<Action> actionList = null;
if (advanced) {
actionList = new ArrayList<>();
@@ -311,11 +308,11 @@ public class RequirementManagerImpl implements RequirementManager {
String type = section.getString("type");
if (type == null) {
LogUtils.warn("No requirement type found at " + section.getCurrentPath());
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
var builder = getRequirementFactory(type);
if (builder == null) {
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
return builder.build(section.get("value"), actionList, advanced);
}
@@ -331,11 +328,11 @@ public class RequirementManagerImpl implements RequirementManager {
*/
@Override
@NotNull
public Requirement getRequirement(@NotNull String type, @NotNull Object value) {
public Requirement parseRequirement(@NotNull String type, @NotNull Object value) {
RequirementFactory factory = getRequirementFactory(type);
if (factory == null) {
LogUtils.warn("Requirement type: " + type + " doesn't exist.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
return factory.build(value);
}
@@ -457,7 +454,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at || requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -480,7 +477,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at && requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -675,7 +672,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at cooldown requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -747,7 +744,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at >= requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement(">", (args, actions, advanced) -> {
@@ -763,7 +760,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at > requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -780,7 +777,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at regex requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -799,7 +796,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!=", (args, actions, advanced) -> {
@@ -815,7 +812,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -835,7 +832,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at < requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("<=", (args, actions, advanced) -> {
@@ -851,7 +848,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at <= requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -870,7 +867,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at startsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!startsWith", (args, actions, advanced) -> {
@@ -886,7 +883,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -905,7 +902,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at endsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!endsWith", (args, actions, advanced) -> {
@@ -921,7 +918,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !endsWith requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -940,7 +937,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at contains requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!contains", (args, actions, advanced) -> {
@@ -956,7 +953,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !contains requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -974,7 +971,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at in-list requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!in-list", (args, actions, advanced) -> {
@@ -989,7 +986,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at in-list requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -1008,7 +1005,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at equals requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
registerRequirement("!equals", (args, actions, advanced) -> {
@@ -1024,7 +1021,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at !equals requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -1067,7 +1064,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at item-in-hand requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -1232,7 +1229,7 @@ public class RequirementManagerImpl implements RequirementManager {
for (String e : list) {
LogUtils.warn(" - " + e);
}
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
});
}
@@ -1265,7 +1262,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at competition requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -1289,7 +1286,7 @@ public class RequirementManagerImpl implements RequirementManager {
};
} else {
LogUtils.warn("Wrong value format found at plugin-level requirement.");
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
});
}
@@ -1302,7 +1299,7 @@ public class RequirementManagerImpl implements RequirementManager {
PotionEffectType type = PotionEffectType.getByName(split[0]);
if (type == null) {
LogUtils.warn("Potion effect doesn't exist: " + split[0]);
return EmptyRequirement.instance;
return EmptyRequirement.INSTANCE;
}
int required = Integer.parseInt(split[1]);
String operator = potions.substring(split[0].length(), potions.length() - split[1].length());

View File

@@ -220,7 +220,7 @@ public class TotemManagerImpl implements TotemManager, Listener {
TotemConfig totemConfig = new TotemConfig.Builder(entry.getKey())
.setTotemModels(getTotemModels(section.getConfigurationSection("pattern")))
.setParticleSettings(getParticleSettings(section.getConfigurationSection("particles")))
.setRequirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), true))
.setRequirements(plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("requirements"), true))
.setRadius(section.getDouble("radius", 8))
.setDuration(section.getInt("duration", 300))
.build();

View File

@@ -23,7 +23,7 @@ import net.momirealms.customfishing.api.storage.data.InventoryData;
import net.momirealms.customfishing.api.storage.data.PlayerData;
import net.momirealms.customfishing.api.storage.data.StatisticData;
import net.momirealms.customfishing.api.storage.user.UserData;
import net.momirealms.customfishing.bukkit.compatibility.papi.PlaceholderManagerImpl;
import net.momirealms.customfishing.bukkit.misc.placeholder.BukkitPlaceholderManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
@@ -59,7 +59,7 @@ public class OfflineUser implements UserData<OfflinePlayer> {
// Set up the inventory for the FishingBagHolder
this.holder.setInventory(InventoryUtils.createInventory(this.holder, playerData.getBagData().size,
AdventureHelper.getInstance().getComponentFromMiniMessage(
PlaceholderManagerImpl.getInstance().parse(
BukkitPlaceholderManager.getInstance().parse(
offlinePlayer,
BukkitCustomFishingPlugin.get().getBagManager().getBagTitle(),
Map.of("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(String.valueOf(uuid)))

Some files were not shown because too many files have changed in this diff Show More