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

checkpoint - 6

This commit is contained in:
XiaoMoMi
2024-05-20 21:23:40 +08:00
parent c23bbc75d0
commit 9b8e1016b2
90 changed files with 1236 additions and 5800 deletions

View File

@@ -1,291 +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;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.CustomFishingReloadEvent;
import net.momirealms.customfishing.api.mechanic.misc.cooldown.CoolDownManager;
import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.bukkit.bag.BukkitBagManager;
import net.momirealms.customfishing.bukkit.block.BukkitBlockManager;
import net.momirealms.customfishing.bukkit.compatibility.BukkitIntegrationManager;
import net.momirealms.customfishing.bukkit.competition.BukkitCompetitionManager;
import net.momirealms.customfishing.bukkit.effect.BukkitEffectManager;
import net.momirealms.customfishing.bukkit.entity.BukkitEntityManager;
import net.momirealms.customfishing.bukkit.fishing.BukkitFishingManager;
import net.momirealms.customfishing.bukkit.game.BukkitGameManager;
import net.momirealms.customfishing.bukkit.hook.BukkitHookManager;
import net.momirealms.customfishing.bukkit.item.ItemManagerImpl;
import net.momirealms.customfishing.bukkit.loot.LootManagerImpl;
import net.momirealms.customfishing.bukkit.market.BukkitMarketManager;
import net.momirealms.customfishing.bukkit.misc.ChatCatcherManager;
import net.momirealms.customfishing.bukkit.statistic.BukkitStatisticsManager;
import net.momirealms.customfishing.bukkit.storage.BukkitStorageManager;
import net.momirealms.customfishing.bukkit.totem.BukkitTotemManager;
import net.momirealms.customfishing.common.helper.VersionHelper;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BukkitCustomFishingPluginImpl extends BukkitCustomFishingPlugin {
private static ProtocolManager protocolManager;
private CoolDownManager coolDownManager;
private ChatCatcherManager chatCatcherManager;
private DependencyManager dependencyManager;
public BukkitCustomFishingPluginImpl() {
super();
}
@Override
public void onLoad() {
this.versionManager = new VersionHelper(this);
this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader()));
this.dependencyManager.loadDependencies(new ArrayList<>(
List.of(
Dependency.GSON,
Dependency.SLF4J_API,
Dependency.SLF4J_SIMPLE,
Dependency.BOOSTED_YAML,
Dependency.EXP4J,
Dependency.MYSQL_DRIVER,
Dependency.MARIADB_DRIVER,
Dependency.MONGODB_DRIVER_SYNC,
Dependency.MONGODB_DRIVER_CORE,
Dependency.MONGODB_DRIVER_BSON,
Dependency.JEDIS,
Dependency.COMMONS_POOL_2,
Dependency.COMMONS_LANG_3,
Dependency.H2_DRIVER,
Dependency.SQLITE_DRIVER,
Dependency.BSTATS_BASE,
Dependency.HIKARI,
Dependency.BSTATS_BUKKIT,
versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API
)
));
}
@Override
public void onEnable() {
protocolManager = ProtocolLibrary.getProtocolManager();
NBTUtils.disableNBTAPILogs();
ReflectionUtils.load();
this.actionManager = new ActionManagerImpl(this);
this.adventure = new AdventureHelper(this);
this.bagManager = new BukkitBagManager(this);
this.blockManager = new BukkitBlockManager(this);
this.commandManager = new CommandManagerImpl(this);
this.effectManager = new BukkitEffectManager(this);
this.fishingManager = new BukkitFishingManager(this);
this.gameManager = new BukkitGameManager(this);
this.itemManager = new ItemManagerImpl(this);
this.lootManager = new LootManagerImpl(this);
this.marketManager = new BukkitMarketManager(this);
this.entityManager = new BukkitEntityManager(this);
this.placeholderManager = new BukkitPlaceholderManager(this);
this.requirementManager = new RequirementManagerImpl(this);
this.scheduler = new SchedulerImpl(this);
this.storageManager = new BukkitStorageManager(this);
this.competitionManager = new BukkitCompetitionManager(this);
this.integrationManager = new BukkitIntegrationManager(this);
this.statisticsManager = new BukkitStatisticsManager(this);
this.coolDownManager = new CoolDownManager(this);
this.totemManager = new BukkitTotemManager(this);
this.hookManager = new BukkitHookManager(this);
this.chatCatcherManager = new ChatCatcherManager(this);
this.reload();
super.initialized = true;
if (CFConfig.metrics) new Metrics(this, 16648);
if (CFConfig.updateChecker)
this.versionManager.checkUpdate().thenAccept(result -> {
if (!result) this.getAdventure().sendConsoleMessage("[CustomFishing] You are using the latest version.");
else this.getAdventure().sendConsoleMessage("[CustomFishing] Update is available: <u>https://polymart.org/resource/2723<!u>");
});
}
@Override
public void onDisable() {
if (this.adventure != null) ((AdventureHelper) this.adventure).close();
if (this.bagManager != null) ((BukkitBagManager) this.bagManager).disable();
if (this.blockManager != null) ((BukkitBlockManager) this.blockManager).disable();
if (this.effectManager != null) ((BukkitEffectManager) this.effectManager).disable();
if (this.fishingManager != null) ((BukkitFishingManager) this.fishingManager).disable();
if (this.gameManager != null) ((BukkitGameManager) this.gameManager).disable();
if (this.itemManager != null) ((ItemManagerImpl) this.itemManager).disable();
if (this.lootManager != null) ((LootManagerImpl) this.lootManager).disable();
if (this.marketManager != null) ((BukkitMarketManager) this.marketManager).disable();
if (this.entityManager != null) ((BukkitEntityManager) this.entityManager).disable();
if (this.requirementManager != null) ((RequirementManagerImpl) this.requirementManager).disable();
if (this.scheduler != null) ((SchedulerImpl) this.scheduler).shutdown();
if (this.integrationManager != null) ((BukkitIntegrationManager) this.integrationManager).disable();
if (this.competitionManager != null) ((BukkitCompetitionManager) this.competitionManager).disable();
if (this.storageManager != null) ((BukkitStorageManager) this.storageManager).disable();
if (this.placeholderManager != null) ((BukkitPlaceholderManager) this.placeholderManager).disable();
if (this.statisticsManager != null) ((BukkitStatisticsManager) this.statisticsManager).disable();
if (this.actionManager != null) ((ActionManagerImpl) this.actionManager).disable();
if (this.totemManager != null) ((BukkitTotemManager) this.totemManager).disable();
if (this.hookManager != null) ((BukkitHookManager) this.hookManager).disable();
if (this.coolDownManager != null) this.coolDownManager.disable();
if (this.chatCatcherManager != null) this.chatCatcherManager.disable();
if (this.commandManager != null) this.commandManager.unload();
HandlerList.unregisterAll(this);
}
/**
* Reload the plugin
*/
@Override
public void reload() {
CFConfig.load();
CFLocale.load();
((SchedulerImpl) this.scheduler).reload();
((RequirementManagerImpl) this.requirementManager).unload();
((RequirementManagerImpl) this.requirementManager).load();
((ActionManagerImpl) this.actionManager).unload();
((ActionManagerImpl) this.actionManager).load();
((BukkitGameManager) this.gameManager).unload();
((BukkitGameManager) this.gameManager).load();
((ItemManagerImpl) this.itemManager).unload();
((ItemManagerImpl) this.itemManager).load();
((LootManagerImpl) this.lootManager).unload();
((LootManagerImpl) this.lootManager).load();
((BukkitFishingManager) this.fishingManager).unload();
((BukkitFishingManager) this.fishingManager).load();
((BukkitTotemManager) this.totemManager).unload();
((BukkitTotemManager) this.totemManager).load();
((BukkitEffectManager) this.effectManager).unload();
((BukkitEffectManager) this.effectManager).load();
((BukkitMarketManager) this.marketManager).unload();
((BukkitMarketManager) this.marketManager).load();
((BukkitBagManager) this.bagManager).unload();
((BukkitBagManager) this.bagManager).load();
((BukkitBlockManager) this.blockManager).unload();
((BukkitBlockManager) this.blockManager).load();
((BukkitEntityManager) this.entityManager).unload();
((BukkitEntityManager) this.entityManager).load();
((BukkitCompetitionManager) this.competitionManager).unload();
((BukkitCompetitionManager) this.competitionManager).load();
((BukkitStorageManager) this.storageManager).reload();
((BukkitStatisticsManager) this.statisticsManager).unload();
((BukkitStatisticsManager) this.statisticsManager).load();
((BukkitPlaceholderManager) this.placeholderManager).unload();
((BukkitPlaceholderManager) this.placeholderManager).load();
((BukkitHookManager) this.hookManager).unload();
((BukkitHookManager) this.hookManager).load();
this.commandManager.unload();
this.commandManager.load();
this.coolDownManager.unload();
this.coolDownManager.load();
this.chatCatcherManager.unload();
this.chatCatcherManager.load();
CustomFishingReloadEvent event = new CustomFishingReloadEvent(this);
Bukkit.getPluginManager().callEvent(event);
}
/**
* Retrieves a YAML configuration from a file within the plugin's data folder.
*
* @param file The name of the configuration file.
* @return A YamlConfiguration object representing the configuration.
*/
@Override
public YamlConfiguration getConfig(String file) {
File config = new File(this.getDataFolder(), file);
if (!config.exists()) this.saveResource(file, false);
return YamlConfiguration.loadConfiguration(config);
}
/**
* Checks if a specified plugin is enabled on the Bukkit server.
*
* @param plugin The name of the plugin to check.
* @return True if the plugin is enabled, false otherwise.
*/
@Override
public boolean isHookedPluginEnabled(String plugin) {
return Bukkit.getPluginManager().isPluginEnabled(plugin);
}
/**
* Outputs a debugging message if the debug mode is enabled.
*
* @param message The debugging message to be logged.
*/
@Override
public void debug(String message) {
if (!CFConfig.debug) return;
LogUtils.info(message);
}
/**
* Gets the CoolDownManager instance associated with the plugin.
*
* @return The CoolDownManager instance.
*/
public CoolDownManager getCoolDownManager() {
return coolDownManager;
}
/**
* Gets the ChatCatcherManager instance associated with the plugin.
*
* @return The ChatCatcherManager instance.
*/
public ChatCatcherManager getChatCatcherManager() {
return chatCatcherManager;
}
public DependencyManager getDependencyManager() {
return dependencyManager;
}
/**
* Retrieves the ProtocolManager instance used for managing packets.
*
* @return The ProtocolManager instance.
*/
@NotNull
public static ProtocolManager getProtocolManager() {
return protocolManager;
}
public static void sendPacket(Player player, PacketContainer packet) {
protocolManager.sendServerPacket(player, packet);
}
public static void sendPackets(Player player, PacketContainer... packets) {
List<PacketContainer> bundle = new ArrayList<>(Arrays.asList(packets));
PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE);
bundlePacket.getPacketBundles().write(0, bundle);
sendPacket(player, bundlePacket);
}
}

View File

@@ -12,7 +12,9 @@ import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlacehol
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.api.mechanic.misc.value.TextValue;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.bukkit.compatibility.VaultHook;
import net.momirealms.customfishing.bukkit.integration.VaultHook;
import net.momirealms.customfishing.bukkit.util.ItemUtils;
import net.momirealms.customfishing.bukkit.util.LocationUtils;
import net.momirealms.customfishing.bukkit.util.PlayerUtils;
import net.momirealms.customfishing.common.helper.AdventureHelper;
import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask;
@@ -20,8 +22,6 @@ 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.common.util.RandomUtils;
import net.momirealms.customfishing.bukkit.util.ItemUtils;
import net.momirealms.customfishing.bukkit.util.LocationUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;

View File

@@ -17,7 +17,6 @@
package net.momirealms.customfishing.bukkit.bag;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager;
@@ -45,12 +44,12 @@ public class BukkitBagManager implements BagManager, Listener {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<UUID, UserData> tempEditMap;
private Action[] collectLootActions;
private Action[] bagFullActions;
private Action<Player>[] collectLootActions;
private Action<Player>[] bagFullActions;
private boolean bagStoreLoots;
private String bagTitle;
private List<Material> bagWhiteListItems;
private Requirement[] collectRequirements;
private Requirement<Player>[] collectRequirements;
public BukkitBagManager(BukkitCustomFishingPluginImpl plugin) {
this.plugin = plugin;

View File

@@ -17,27 +17,27 @@
package net.momirealms.customfishing.bukkit.block;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.common.Tuple;
import net.momirealms.customfishing.api.integration.BlockProvider;
import net.momirealms.customfishing.api.mechanic.block.*;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.bukkit.compatibility.block.VanillaBlockProvider;
import net.momirealms.customfishing.bukkit.util.ConfigUtils;
import net.momirealms.customfishing.api.mechanic.config.ConfigManager;
import net.momirealms.customfishing.api.mechanic.context.Context;
import net.momirealms.customfishing.api.mechanic.context.ContextKeys;
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.common.util.Pair;
import net.momirealms.customfishing.common.util.RandomUtils;
import net.momirealms.customfishing.common.util.Tuple;
import org.bukkit.*;
import org.bukkit.block.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Container;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
import org.bukkit.block.data.type.Campfire;
import org.bukkit.block.data.type.Farmland;
import org.bukkit.block.data.type.NoteBlock;
import org.bukkit.block.data.type.TurtleEgg;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -49,61 +49,77 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
public class BukkitBlockManager implements BlockManager, Listener {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, BlockProvider> blockLibraryMap;
private BlockProvider[] blockDetectionArray;
private final HashMap<String, BlockConfigImpl> blockConfigMap;
private final HashMap<String, BlockDataModifierFactory> dataBuilderMap;
private final HashMap<String, BlockStateModifierFactory> stateBuilderMap;
private final HashMap<String, BlockProvider> blockProviders = new HashMap<>();
private final HashMap<String, BlockConfig> blocks = new HashMap<>();
private final HashMap<String, BlockDataModifierFactory> dataFactories = new HashMap<>();
private final HashMap<String, BlockStateModifierFactory> stateFactories = new HashMap<>();
private BlockProvider[] blockDetectArray;
public BukkitBlockManager(BukkitCustomFishingPluginImpl plugin) {
this.plugin = plugin;
this.blockLibraryMap = new HashMap<>();
this.blockConfigMap = new HashMap<>();
this.dataBuilderMap = new HashMap<>();
this.stateBuilderMap = new HashMap<>();
this.registerInbuiltProperties();
this.registerBlockLibrary(new VanillaBlockProvider());
this.registerBlockProvider(new BlockProvider() {
@Override
public String identifier() {
return "vanilla";
}
@Override
public BlockData blockData(@NotNull Context<Player> context, @NotNull String id, List<BlockDataModifier> modifiers) {
BlockData blockData = Material.valueOf(id.toUpperCase(Locale.ENGLISH)).createBlockData();
for (BlockDataModifier modifier : modifiers)
modifier.apply(context, blockData);
return blockData;
}
@NotNull
@Override
public String blockID(@NotNull Block block) {
return block.getType().name();
}
});
}
@Override
public boolean registerBlock(@NotNull String id, @NotNull BlockConfig block) {
if (blocks.containsKey(id)) return false;
blocks.put(id, block);
return true;
}
@Override
public void load() {
this.loadConfig();
Bukkit.getPluginManager().registerEvents(this, plugin);
Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap());
this.resetBlockDetectionOrder();
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
HashMap<String, BlockConfigImpl> tempMap = new HashMap<>(this.blockConfigMap);
this.blockConfigMap.clear();
for (Map.Entry<String, BlockConfigImpl> entry : tempMap.entrySet()) {
if (entry.getValue().isPersist()) {
tempMap.put(entry.getKey(), entry.getValue());
}
}
this.blocks.clear();
}
@Override
public void disable() {
this.blockLibraryMap.clear();
this.blockProviders.clear();
}
private void resetBlockDetectionOrder() {
ArrayList<BlockProvider> list = new ArrayList<>();
for (String plugin : CFConfig.itemDetectOrder) {
BlockProvider library = blockLibraryMap.get(plugin);
if (library != null) {
for (String plugin : ConfigManager.blockDetectOrder()) {
BlockProvider library = blockProviders.get(plugin);
if (library != null)
list.add(library);
}
}
this.blockDetectionArray = list.toArray(new BlockProvider[0]);
this.blockDetectArray = list.toArray(new BlockProvider[0]);
}
/**
@@ -117,7 +133,7 @@ public class BukkitBlockManager implements BlockManager, Listener {
// Retrieve a custom string value stored in the entity's persistent data container.
String temp = event.getEntity().getPersistentDataContainer().get(
Objects.requireNonNull(NamespacedKey.fromString("block", BukkitCustomFishingPlugin.get())),
requireNonNull(NamespacedKey.fromString("block", plugin.getBoostrap())),
PersistentDataType.STRING
);
@@ -128,8 +144,8 @@ public class BukkitBlockManager implements BlockManager, Listener {
String[] split = temp.split(";");
// If no BlockConfig is found for the specified key, return without further action.
BlockConfigImpl blockConfigImpl = blockConfigMap.get(split[0]);
if (blockConfigImpl == null) return;
BlockConfig blockConfig= blocks.get(split[0]);
if (blockConfig == null) return;
// If the player is not online or not found, remove the entity and set the block to air
Player player = Bukkit.getPlayer(split[1]);
@@ -138,254 +154,107 @@ public class BukkitBlockManager implements BlockManager, Listener {
event.getBlock().setType(Material.AIR);
return;
}
Context<Player> context = Context.player(player);
Location location = event.getBlock().getLocation();
// Apply block state modifiers from the BlockConfig to the block 1 tick later.
plugin.getScheduler().runTaskSyncLater(() -> {
plugin.getScheduler().sync().runLater(() -> {
BlockState state = location.getBlock().getState();
for (BlockStateModifier modifier : blockConfigImpl.stateModifiers()) {
modifier.apply(player, state);
for (BlockStateModifier modifier : blockConfig.stateModifiers()) {
modifier.apply(context, state);
}
}, location, 50, TimeUnit.MILLISECONDS);
}, 1, location);
}
/**
* Registers a BlockLibrary instance.
* This method associates a BlockLibrary with its unique identification and adds it to the registry.
*
* @param blockProvider The BlockLibrary instance to register.
* @return True if the registration was successful (the identification is not already registered), false otherwise.
*/
@Override
public boolean registerBlockLibrary(BlockProvider blockProvider) {
if (this.blockLibraryMap.containsKey(blockProvider.identification())) return false;
this.blockLibraryMap.put(blockProvider.identification(), blockProvider);
public boolean registerBlockProvider(BlockProvider blockProvider) {
if (this.blockProviders.containsKey(blockProvider.identifier())) return false;
this.blockProviders.put(blockProvider.identifier(), blockProvider);
this.resetBlockDetectionOrder();
return true;
}
/**
* Unregisters a BlockLibrary instance by its identification.
* This method removes a BlockLibrary from the registry based on its unique identification.
*
* @param identification The unique identification of the BlockLibrary to unregister.
* @return True if the BlockLibrary was successfully unregistered, false if it was not found.
*/
@Override
public boolean unregisterBlockLibrary(String identification) {
boolean success = blockLibraryMap.remove(identification) != null;
public boolean unregisterBlockProvider(String identification) {
boolean success = blockProviders.remove(identification) != null;
if (success)
this.resetBlockDetectionOrder();
return success;
}
/**
* Registers a BlockDataModifierBuilder for a specific type.
* This method associates a BlockDataModifierBuilder with its type and adds it to the registry.
*
* @param type The type of the BlockDataModifierBuilder to register.
* @param builder The BlockDataModifierBuilder instance to register.
* @return True if the registration was successful (the type is not already registered), false otherwise.
*/
@Override
public boolean registerBlockDataModifierBuilder(String type, BlockDataModifierFactory builder) {
if (dataBuilderMap.containsKey(type)) return false;
dataBuilderMap.put(type, builder);
public boolean registerBlockDataModifierBuilder(String type, BlockDataModifierFactory factory) {
if (this.dataFactories.containsKey(type)) return false;
this.dataFactories.put(type, factory);
return true;
}
/**
* Registers a BlockStateModifierBuilder for a specific type.
* This method associates a BlockStateModifierBuilder with its type and adds it to the registry.
*
* @param type The type of the BlockStateModifierBuilder to register.
* @param builder The BlockStateModifierBuilder instance to register.
* @return True if the registration was successful (the type is not already registered), false otherwise.
*/
@Override
public boolean registerBlockStateModifierBuilder(String type, BlockStateModifierFactory builder) {
if (stateBuilderMap.containsKey(type)) return false;
stateBuilderMap.put(type, builder);
public boolean registerBlockStateModifierBuilder(String type, BlockStateModifierFactory factory) {
if (stateFactories.containsKey(type)) return false;
this.stateFactories.put(type, factory);
return true;
}
/**
* Unregisters a BlockDataModifierBuilder with the specified type.
*
* @param type The type of the BlockDataModifierBuilder to unregister.
* @return True if the BlockDataModifierBuilder was successfully unregistered, false otherwise.
*/
@Override
public boolean unregisterBlockDataModifierBuilder(String type) {
return dataBuilderMap.remove(type) != null;
return this.dataFactories.remove(type) != null;
}
/**
* Unregisters a BlockStateModifierBuilder with the specified type.
*
* @param type The type of the BlockStateModifierBuilder to unregister.
* @return True if the BlockStateModifierBuilder was successfully unregistered, false otherwise.
*/
@Override
public boolean unregisterBlockStateModifierBuilder(String type) {
return stateBuilderMap.remove(type) != null;
return this.stateFactories.remove(type) != null;
}
private void registerInbuiltProperties() {
this.registerDirectional();
this.registerStorage();
this.registerRotatable();
this.registerTurtleEggs();
this.registerMoisture();
this.registerNoteBlock();
this.registerCampfire();
this.registerAge();
}
/**
* Loads configuration files from the plugin's data folder and processes them.
* Configuration files are organized by type (e.g., "block").
*/
@SuppressWarnings("DuplicatedCode")
private void loadConfig() {
Deque<File> fileDeque = new ArrayDeque<>();
for (String type : List.of("block")) {
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type);
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
File file = fileDeque.pop();
File[] files = file.listFiles();
if (files == null) continue;
for (File subFile : files) {
if (subFile.isDirectory()) {
fileDeque.push(subFile);
} else if (subFile.isFile() && subFile.getName().endsWith(".yml")) {
this.loadSingleFile(subFile);
}
}
}
}
}
/**
* Loads configuration data from a single YAML file and processes it to create BlockConfig instances.
*
* @param file The YAML file to load and process.
*/
private void loadSingleFile(File file) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection section) {
// Check if the "block" is null and log a warning if so.
String blockID = section.getString("block");
if (blockID == null) {
LogUtils.warn("Block can't be null. File:" + file.getAbsolutePath() + "; Section:" + section.getCurrentPath());
continue;
}
List<BlockDataModifier> dataModifiers = new ArrayList<>();
List<BlockStateModifier> stateModifiers = new ArrayList<>();
// If a "properties" section exists, process its entries.
ConfigurationSection property = section.getConfigurationSection("properties");
if (property != null) {
for (Map.Entry<String, Object> innerEntry : property.getValues(false).entrySet()) {
BlockDataModifierFactory dataBuilder = dataBuilderMap.get(innerEntry.getKey());
if (dataBuilder != null) {
dataModifiers.add(dataBuilder.build(innerEntry.getValue()));
continue;
}
BlockStateModifierFactory stateBuilder = stateBuilderMap.get(innerEntry.getKey());
if (stateBuilder != null) {
stateModifiers.add(stateBuilder.build(innerEntry.getValue()));
}
}
}
// Create a BlockConfig instance with the processed data and add it to the blockConfigMap.
BlockConfigImpl blockConfigImpl = new BlockConfigImpl.Builder()
.blockID(blockID)
.persist(false)
.horizontalVector(section.getDouble("velocity.horizontal", 1.1))
.verticalVector(section.getDouble("velocity.vertical", 1.2))
.dataModifiers(dataModifiers)
.stateModifiers(stateModifiers)
.build();
blockConfigMap.put(entry.getKey(), blockConfigImpl);
}
}
}
/**
* Summons a falling block at a specified location based on the provided loot.
* This method spawns a falling block at the given hookLocation with specific properties determined by the loot.
*
* @param player The player who triggered the action.
* @param hookLocation The location where the hook is positioned.
* @param playerLocation The location of the player.
* @param loot The loot to be associated with the summoned block.
*/
@Override
public void summonBlock(Player player, Location hookLocation, Location playerLocation, Loot loot) {
BlockConfigImpl config = blockConfigMap.get(loot.getID());
if (config == null) {
LogUtils.warn("Block: " + loot.getID() + " doesn't exist.");
return;
}
@Nullable
public FallingBlock summonBlockLoot(@NotNull Context<Player> context) {
String id = context.arg(ContextKeys.ID);
BlockConfig config = requireNonNull(blocks.get(id), "Block " + id + " not found");
String blockID = config.blockID();
BlockData blockData;
if (blockID.contains(":")) {
String[] split = blockID.split(":", 2);
String lib = split[0];
String id = split[1];
blockData = blockLibraryMap.get(lib).blockData(player, id, config.dataModifier());
BlockProvider provider = requireNonNull(blockProviders.get(split[0]), "BlockProvider " + split[0] + " doesn't exist");
blockData = requireNonNull(provider.blockData(context, split[1], config.dataModifier()), "Block " + split[1] + " doesn't exist");
} else {
blockData = blockLibraryMap.get("vanilla").blockData(player, blockID, config.dataModifier());
blockData = blockProviders.get("vanilla").blockData(context, blockID, config.dataModifier());
}
FallingBlock fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData);
Location hookLocation = requireNonNull(context.arg(ContextKeys.HOOK_LOCATION));
Location playerLocation = requireNonNull(context.getHolder()).getLocation();
FallingBlock fallingBlock = hookLocation.getWorld().spawn(hookLocation, FallingBlock.class, (fb -> fb.setBlockData(blockData)));
fallingBlock.getPersistentDataContainer().set(
Objects.requireNonNull(NamespacedKey.fromString("block", BukkitCustomFishingPlugin.get())),
requireNonNull(NamespacedKey.fromString("block", plugin.getBoostrap())),
PersistentDataType.STRING,
loot.getID() + ";" + player.getName()
id + ";" + context.getHolder().getName()
);
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.horizontalVector()) - 1);
vector = vector.setY((vector.getY() + 0.2) * config.verticalVector());
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.horizontalVector().evaluate(context)) - 1);
vector = vector.setY((vector.getY() + 0.2) * config.verticalVector().evaluate(context));
fallingBlock.setVelocity(vector);
return fallingBlock;
}
/**
* Retrieves the block ID associated with a given Block instance using block detection order.
* This method iterates through the configured block detection order to find the block's ID
* by checking different BlockLibrary instances in the specified order.
*
* @param block The Block instance for which to retrieve the block ID.
* @return The block ID
*/
@Override
@NotNull
public String getAnyPluginBlockID(Block block) {
for (BlockProvider blockProvider : blockDetectionArray) {
public String getBlockID(@NotNull Block block) {
for (BlockProvider blockProvider : blockDetectArray) {
String id = blockProvider.blockID(block);
if (id != null) {
return id;
}
if (id != null) return id;
}
// Should not reach this because vanilla library would always work
return "AIR";
}
private void registerDirectional() {
this.registerBlockDataModifierBuilder("directional-4", (args) -> (player, blockData) -> {
this.registerBlockDataModifierBuilder("directional-4", (args) -> (context, blockData) -> {
boolean arg = (boolean) args;
if (arg && blockData instanceof Directional directional) {
directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 4)]);
}
});
this.registerBlockDataModifierBuilder("directional-6", (args) -> (player, blockData) -> {
this.registerBlockDataModifierBuilder("directional-6", (args) -> (context, blockData) -> {
boolean arg = (boolean) args;
if (arg && blockData instanceof Directional directional) {
directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 6)]);
@@ -393,32 +262,10 @@ public class BukkitBlockManager implements BlockManager, Listener {
});
}
private void registerMoisture() {
this.registerBlockDataModifierBuilder("moisture", (args) -> {
int arg = (int) args;
return (player, blockData) -> {
if (blockData instanceof Farmland farmland) {
farmland.setMoisture(arg);
}
};
});
}
private void registerCampfire() {
this.registerBlockDataModifierBuilder("campfire", (args) -> {
boolean arg = (boolean) args;
return (player, blockData) -> {
if (blockData instanceof Campfire campfire) {
campfire.setSignalFire(arg);
}
};
});
}
private void registerRotatable() {
this.registerBlockDataModifierBuilder("rotatable", (args) -> {
boolean arg = (boolean) args;
return (player, blockData) -> {
return (context, blockData) -> {
if (arg && blockData instanceof Rotatable rotatable) {
rotatable.setRotation(BlockFace.values()[ThreadLocalRandom.current().nextInt(BlockFace.values().length)]);
}
@@ -428,99 +275,64 @@ public class BukkitBlockManager implements BlockManager, Listener {
private void registerNoteBlock() {
this.registerBlockDataModifierBuilder("noteblock", (args) -> {
if (args instanceof ConfigurationSection section) {
if (args instanceof Section section) {
var instrument = Instrument.valueOf(section.getString("instrument"));
var note = new Note(section.getInt("note"));
return (player, blockData) -> {
return (context, blockData) -> {
if (blockData instanceof NoteBlock noteBlock) {
noteBlock.setNote(note);
noteBlock.setInstrument(instrument);
}
};
} else {
LogUtils.warn("Invalid property format found at block noteblock.");
return null;
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at noteblock property which should be Section");
return EmptyBlockDataModifier.INSTANCE;
}
});
}
private void registerAge() {
this.registerBlockDataModifierBuilder("age", (args) -> {
int arg = (int) args;
return (player, blockData) -> {
if (blockData instanceof Ageable ageable) {
ageable.setAge(arg);
}
};
});
}
private void registerTurtleEggs() {
this.registerBlockDataModifierBuilder("turtle-eggs", (args) -> {
int arg = (int) args;
return (player, blockData) -> {
if (blockData instanceof TurtleEgg egg) {
egg.setEggs(arg);
}
};
});
}
private void registerStorage() {
this.registerBlockStateModifierBuilder("storage", (args) -> {
if (args instanceof ConfigurationSection section) {
ArrayList<Tuple<Double, String, Pair<Integer, Integer>>> tempChanceList = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
if (args instanceof Section section) {
List<Tuple<MathValue<Player>, String, Pair<MathValue<Player>, MathValue<Player>>>> contents = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection inner) {
String item = inner.getString("item");
Pair<Integer, Integer> amountPair = ConfigUtils.splitStringIntegerArgs(inner.getString("amount","1~1"), "~");
double chance = inner.getDouble("chance", 1);
tempChanceList.add(Tuple.of(chance, item, amountPair));
String[] split = inner.getString("amount","1~1").split("~");
Pair<MathValue<Player>, MathValue<Player>> amountPair = Pair.of(MathValue.auto(split[0]), MathValue.auto(split[1]));
MathValue<Player> chance = MathValue.auto(inner.get("chance", 1d));
contents.add(Tuple.of(chance, item, amountPair));
}
}
return (player, blockState) -> {
if (blockState instanceof Chest chest) {
setInventoryItems(tempChanceList, player, chest.getInventory());
return;
}
if (blockState instanceof Barrel barrel) {
setInventoryItems(tempChanceList, player, barrel.getInventory());
return;
}
if (blockState instanceof ShulkerBox shulkerBox) {
setInventoryItems(tempChanceList, player, shulkerBox.getInventory());
return;
return (context, blockState) -> {
if (blockState instanceof Container container) {
setInventoryItems(contents, context, container.getInventory());
}
};
} else {
LogUtils.warn("Invalid property format found at block storage.");
return null;
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at storage property which should be Section");
return EmptyBlockStateModifier.INSTANCE;
}
});
}
/**
* Sets items in the BLOCK's inventory based on chance and configuration.
*
* @param tempChanceList A list of tuples containing chance, item ID, and quantity range for each item.
* @param player The inventory items are being set.
* @param inventory The inventory where the items will be placed.
*/
private void setInventoryItems(
ArrayList<Tuple<Double, String, Pair<Integer, Integer>>> tempChanceList,
Player player,
List<Tuple<MathValue<Player>, String, Pair<MathValue<Player>, MathValue<Player>>>> contents,
Context<Player> context,
Inventory inventory
) {
LinkedList<Integer> unused = new LinkedList<>();
for (int i = 0; i < 27; i++) {
for (int i = 0; i < inventory.getSize(); i++) {
unused.add(i);
}
Collections.shuffle(unused);
for (Tuple<Double, String, Pair<Integer, Integer>> tuple : tempChanceList) {
ItemStack itemStack = plugin.getItemManager().buildAnyPluginItemByID(player, tuple.getMid());
itemStack.setAmount(ThreadLocalRandom.current().nextInt(tuple.getRight().left(), tuple.getRight().right() + 1));
if (tuple.getLeft() > Math.random()) {
inventory.setItem(unused.pop(), itemStack);
for (Tuple<MathValue<Player>, String, Pair<MathValue<Player>, MathValue<Player>>> tuple : contents) {
if (tuple.getLeft().evaluate(context) > Math.random()) {
ItemStack itemStack = plugin.getItemManager().buildAny(context, tuple.getMid());
if (itemStack != null) {
itemStack.setAmount(RandomUtils.generateRandomInt((int) tuple.getRight().left().evaluate(context), (int) (tuple.getRight().right().evaluate(context) + 1)));
inventory.setItem(unused.pop(), itemStack);
}
}
}
}

View File

@@ -1,271 +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;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.integration.EnchantmentProvider;
import net.momirealms.customfishing.api.integration.IntegrationManager;
import net.momirealms.customfishing.api.integration.LevelerProvider;
import net.momirealms.customfishing.api.integration.SeasonProvider;
import net.momirealms.customfishing.bukkit.compatibility.block.ItemsAdderBlockProvider;
import net.momirealms.customfishing.bukkit.compatibility.enchant.AdvancedEnchantmentsProvider;
import net.momirealms.customfishing.bukkit.compatibility.enchant.VanillaEnchantmentsProvider;
import net.momirealms.customfishing.bukkit.compatibility.entity.ItemsAdderEntityProvider;
import net.momirealms.customfishing.bukkit.compatibility.entity.MythicEntityProvider;
import net.momirealms.customfishing.bukkit.compatibility.item.*;
import net.momirealms.customfishing.bukkit.compatibility.level.*;
import net.momirealms.customfishing.bukkit.compatibility.quest.BattlePassQuest;
import net.momirealms.customfishing.bukkit.compatibility.quest.BetonQuestQuest;
import net.momirealms.customfishing.bukkit.compatibility.quest.ClueScrollsQuest;
import net.momirealms.customfishing.bukkit.compatibility.season.CustomCropsSeasonProvider;
import net.momirealms.customfishing.bukkit.compatibility.season.RealisticSeasonsProvider;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class BukkitIntegrationManager implements IntegrationManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, LevelerProvider> levelPluginMap;
private final HashMap<String, EnchantmentProvider> enchantmentPluginMap;
private SeasonProvider seasonProvider;
public BukkitIntegrationManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.levelPluginMap = new HashMap<>();
this.enchantmentPluginMap = new HashMap<>();
this.load();
}
public void disable() {
this.enchantmentPluginMap.clear();
this.levelPluginMap.clear();
}
public void load() {
if (Bukkit.getPluginManager().getPlugin("ItemsAdder") != null) {
plugin.getItemManager().registerItemLibrary(new ItemsAdderItemProvider());
plugin.getBlockManager().registerBlockLibrary(new ItemsAdderBlockProvider());
plugin.getEntityManager().registerEntityProvider(new ItemsAdderEntityProvider());
hookMessage("ItemsAdder");
}
if (Bukkit.getPluginManager().getPlugin("MMOItems") != null) {
plugin.getItemManager().registerItemLibrary(new MMOItemsItemProvider());
hookMessage("MMOItems");
}
if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) {
plugin.getItemManager().registerItemLibrary(new OraxenItemProvider());
hookMessage("Oraxen");
}
if (plugin.isHookedPluginEnabled("Zaphkiel")) {
plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl());
hookMessage("Zaphkiel");
}
if (plugin.isHookedPluginEnabled("NeigeItems")) {
plugin.getItemManager().registerItemLibrary(new NeigeItemsItemProvider());
hookMessage("NeigeItems");
}
if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) {
plugin.getItemManager().registerItemLibrary(new MythicMobsItemProvider());
plugin.getEntityManager().registerEntityProvider(new MythicEntityProvider());
hookMessage("MythicMobs");
}
if (plugin.isHookedPluginEnabled("EcoJobs")) {
registerLevelerProvider("EcoJobs", new EcoJobsLevelerProvider());
hookMessage("EcoJobs");
}
if (plugin.isHookedPluginEnabled("EcoSkills")) {
registerLevelerProvider("EcoSkills", new EcoSkillsLevelerProvider());
hookMessage("EcoSkills");
}
if (Bukkit.getPluginManager().getPlugin("Jobs") != null) {
registerLevelerProvider("JobsReborn", new JobsRebornLevelerProvider());
hookMessage("JobsReborn");
}
if (plugin.isHookedPluginEnabled("MMOCore")) {
registerLevelerProvider("MMOCore", new MMOCoreLevelerProvider());
hookMessage("MMOCore");
}
if (plugin.isHookedPluginEnabled("mcMMO")) {
try {
plugin.getItemManager().registerItemLibrary(new McMMOTreasureProvider());
} catch (ClassNotFoundException | NoSuchMethodException e) {
LogUtils.warn("Failed to initialize mcMMO Treasure");
}
registerLevelerProvider("mcMMO", new McMMOLevelerProvider());
hookMessage("mcMMO");
}
if (plugin.isHookedPluginEnabled("AureliumSkills")) {
registerLevelerProvider("AureliumSkills", new AureliumSkillsProvider());
hookMessage("AureliumSkills");
}
if (plugin.isHookedPluginEnabled("AuraSkills")) {
registerLevelerProvider("AuraSkills", new AuraSkillsLevelerProvider());
hookMessage("AuraSkills");
}
if (plugin.isHookedPluginEnabled("EcoEnchants")) {
this.enchantmentPluginMap.put("EcoEnchants", new VanillaEnchantmentsProvider());
hookMessage("EcoEnchants");
} else {
this.enchantmentPluginMap.put("vanilla", new VanillaEnchantmentsProvider());
}
if (plugin.isHookedPluginEnabled("AdvancedEnchantments")) {
this.enchantmentPluginMap.put("AdvancedEnchantments", new AdvancedEnchantmentsProvider());
hookMessage("AdvancedEnchantments");
}
if (plugin.isHookedPluginEnabled("RealisticSeasons")) {
this.seasonProvider = new RealisticSeasonsProvider();
} else if (plugin.isHookedPluginEnabled("CustomCrops")) {
this.seasonProvider = new CustomCropsSeasonProvider();
}
if (plugin.isHookedPluginEnabled("Vault")) {
VaultHook.initialize();
}
if (plugin.isHookedPluginEnabled("BattlePass")){
BattlePassQuest battlePassQuest = new BattlePassQuest();
battlePassQuest.register();
hookMessage("BattlePass");
}
if (plugin.isHookedPluginEnabled("ClueScrolls")) {
ClueScrollsQuest clueScrollsQuest = new ClueScrollsQuest();
clueScrollsQuest.register();
hookMessage("ClueScrolls");
}
if (plugin.isHookedPluginEnabled("BetonQuest")) {
BetonQuestQuest.register();
hookMessage("BetonQuest");
}
}
/**
* Registers a level plugin with the specified name.
*
* @param plugin The name of the level plugin.
* @param level The implementation of the LevelInterface.
* @return true if the registration was successful, false if the plugin name is already registered.
*/
@Override
public boolean registerLevelerProvider(String plugin, LevelerProvider level) {
if (levelPluginMap.containsKey(plugin)) return false;
levelPluginMap.put(plugin, level);
return true;
}
/**
* Unregisters a level plugin with the specified name.
*
* @param plugin The name of the level plugin to unregister.
* @return true if the unregistration was successful, false if the plugin name is not found.
*/
@Override
public boolean unregisterLevelerProvider(String plugin) {
return levelPluginMap.remove(plugin) != null;
}
/**
* Registers an enchantment provided by a plugin.
*
* @param plugin The name of the plugin providing the enchantment.
* @param enchantment The enchantment to register.
* @return true if the registration was successful, false if the enchantment name is already in use.
*/
@Override
public boolean registerEnchantment(String plugin, EnchantmentProvider enchantment) {
if (enchantmentPluginMap.containsKey(plugin)) return false;
enchantmentPluginMap.put(plugin, enchantment);
return true;
}
/**
* Unregisters an enchantment provided by a plugin.
*
* @param plugin The name of the plugin providing the enchantment.
* @return true if the enchantment was successfully unregistered, false if the enchantment was not found.
*/
@Override
public boolean unregisterEnchantment(String plugin) {
return enchantmentPluginMap.remove(plugin) != null;
}
private void hookMessage(String plugin) {
LogUtils.info( plugin + " hooked!");
}
/**
* Get the LevelInterface provided by a plugin.
*
* @param plugin The name of the plugin providing the LevelInterface.
* @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered.
*/
@Override
@Nullable
public LevelerProvider getLevelPlugin(String plugin) {
return levelPluginMap.get(plugin);
}
/**
* Get an enchantment plugin by its plugin name.
*
* @param plugin The name of the enchantment plugin.
* @return The enchantment plugin interface, or null if not found.
*/
@Override
@Nullable
public EnchantmentProvider getEnchantmentPlugin(String plugin) {
return enchantmentPluginMap.get(plugin);
}
/**
* Get a list of enchantment keys with level applied to the given ItemStack.
*
* @param itemStack The ItemStack to check for enchantments.
* @return A list of enchantment names applied to the ItemStack.
*/
@Override
public List<String> getEnchantments(ItemStack itemStack) {
ArrayList<String> list = new ArrayList<>();
for (EnchantmentProvider enchantmentProvider : enchantmentPluginMap.values()) {
list.addAll(enchantmentProvider.getEnchants(itemStack));
}
return list;
}
/**
* Get the current season interface, if available.
*
* @return The current season interface, or null if not available.
*/
@Nullable
public SeasonProvider getSeasonInterface() {
return seasonProvider;
}
/**
* Set the current season interface.
*
* @param season The season interface to set.
*/
@Override
public void setSeasonInterface(SeasonProvider season) {
this.seasonProvider = season;
}
}

View File

@@ -116,7 +116,6 @@ public class BukkitCompetitionManager implements CompetitionManager {
YamlDocument document = plugin.getConfigManager().loadData(file);
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section section) {
CompetitionConfig.Builder builder = CompetitionConfig.builder()
.key(entry.getKey())
.goal(CompetitionGoal.index().value(section.getString("goal", "TOTAL_SCORE").toLowerCase(Locale.ENGLISH)))
@@ -128,7 +127,6 @@ public class BukkitCompetitionManager implements CompetitionManager {
.startActions(plugin.getActionManager().parseActions(section.getSection("start-actions")))
.endActions(plugin.getActionManager().parseActions(section.getSection("end-actions")))
.skipActions(plugin.getActionManager().parseActions(section.getSection("skip-actions")));;
if (section.getBoolean("bossbar.enable", false)) {
builder.bossBarConfig(
BossBarConfig.builder()
@@ -142,7 +140,6 @@ public class BukkitCompetitionManager implements CompetitionManager {
.build()
);
}
if (section.getBoolean("actionbar.enable", false)) {
builder.actionBarConfig(
ActionBarConfig.builder()
@@ -154,7 +151,6 @@ public class BukkitCompetitionManager implements CompetitionManager {
.build()
);
}
CompetitionConfig competitionConfig = builder.build();
List<Pair<Integer, Integer>> timePairs = section.getStringList("start-time")
.stream().map(it -> {

View File

@@ -84,7 +84,7 @@ public class ActionBarSender {
DynamicText text = texts[counter % (texts.length)];
updatePrivatePlaceholders();
text.update(this.privateContext.placeholderMap());
audience.sendActionBar(AdventureHelper.miniMessage().deserialize(text.getLatestValue()));
audience.sendActionBar(AdventureHelper.miniMessage(text.getLatestValue()));
}
}, 50, 50, TimeUnit.MILLISECONDS);
}

View File

@@ -63,7 +63,7 @@ public class BossBarSender {
texts[i].update(privateContext.placeholderMap());
}
bossBar = BossBar.bossBar(
AdventureHelper.miniMessage().deserialize(texts[0].getLatestValue()),
AdventureHelper.miniMessage(texts[0].getLatestValue()),
competition.getProgress(),
config.color(),
config.overlay(),
@@ -95,7 +95,7 @@ public class BossBarSender {
DynamicText text = texts[counter % (texts.length)];
updatePrivatePlaceholders();
if (text.update(privateContext.placeholderMap())) {
bossBar.name(AdventureHelper.miniMessage().deserialize(text.getLatestValue()));
bossBar.name(AdventureHelper.miniMessage(text.getLatestValue()));
}
bossBar.progress(competition.getProgress());
}

View File

@@ -88,27 +88,27 @@ public class BukkitConfigLoader extends ConfigManager {
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.difficultyAdder(mathValue);
}, "base-difficulty-adder");
}, "base-effects", "difficulty-adder");
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.difficultyMultiplier(mathValue);
}, "base-difficulty-multiplier");
}, "base-effects", "difficulty-multiplier");
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.gameTimeAdder(mathValue);
}, "base-game-time-adder");
}, "base-effects", "game-time-adder");
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.gameTimeMultiplier(mathValue);
}, "base-game-time-multiplier");
}, "base-effects", "game-time-multiplier");
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.waitTimeAdder(mathValue);
}, "base-wait-time-adder");
}, "base-effects", "wait-time-adder");
this.registerBaseEffectParser(object -> {
MathValue<Player> mathValue = MathValue.auto(object);
return builder -> builder.waitTimeMultiplier(mathValue);
}, "base-wait-time-multiplier");
}, "base-effects", "wait-time-multiplier");
}
private void registerBuiltInLootParser() {

View File

@@ -18,371 +18,35 @@
package net.momirealms.customfishing.bukkit.effect;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.common.Key;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffectImpl;
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.mechanic.misc.value.PlainMathValue;
import net.momirealms.customfishing.bukkit.util.ConfigUtils;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.momirealms.customfishing.api.mechanic.effect.EffectManager;
import net.momirealms.customfishing.api.mechanic.effect.EffectModifier;
import net.momirealms.customfishing.common.util.Key;
import java.io.File;
import java.util.*;
import java.util.HashMap;
import java.util.Optional;
public class BukkitEffectManager implements EffectManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<Key, EffectCarrier> effectMap;
private final HashMap<Key, EffectModifier> effectModifiers = new HashMap<>();
public BukkitEffectManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.effectMap = new HashMap<>();
}
public void disable() {
this.effectMap.clear();
this.effectModifiers.clear();
}
/**
* Registers an EffectCarrier with a unique Key.
*
* @param key The unique Key associated with the EffectCarrier.
* @param effect The EffectCarrier to be registered.
* @return True if the registration was successful, false if the Key already exists.
*/
@Override
public boolean registerEffectCarrier(Key key, EffectCarrier effect) {
if (effectMap.containsKey(key)) return false;
this.effectMap.put(key, effect);
public boolean registerEffectModifier(Key key, EffectModifier effect) {
if (effectModifiers.containsKey(key)) return false;
this.effectModifiers.put(key, effect);
return true;
}
/**
* Unregisters an EffectCarrier associated with the specified Key.
*
* @param key The unique Key of the EffectCarrier to unregister.
* @return True if the EffectCarrier was successfully unregistered, false if the Key does not exist.
*/
@Override
public boolean unregisterEffectCarrier(Key key) {
return this.effectMap.remove(key) != null;
}
/**
* Checks if an EffectCarrier with the specified namespace and id exists.
*
* @param namespace The namespace of the EffectCarrier.
* @param id The unique identifier of the EffectCarrier.
* @return True if an EffectCarrier with the given namespace and id exists, false otherwise.
*/
@Override
public boolean hasEffectCarrier(String namespace, String id) {
return effectMap.containsKey(Key.of(namespace, id));
}
/**
* Retrieves an EffectCarrier with the specified namespace and id.
*
* @param namespace The namespace of the EffectCarrier.
* @param id The unique identifier of the EffectCarrier.
* @return The EffectCarrier with the given namespace and id, or null if it doesn't exist.
*/
@Nullable
@Override
public EffectCarrier getEffectCarrier(String namespace, String id) {
return effectMap.get(Key.of(namespace, id));
}
public void load() {
this.loadFiles();
this.loadGlobalEffects();
}
/**
* Loads EffectCarrier configurations from YAML files in different content folders.
* EffectCarrier configurations are organized by type (rod, bait, enchant, util, totem, hook) in separate folders.
* Each YAML file within these folders is processed to populate the effectMap.
*/
@SuppressWarnings("DuplicatedCode")
private void loadFiles() {
Deque<File> fileDeque = new ArrayDeque<>();
for (String type : List.of("rod", "bait", "enchant", "util", "totem", "hook")) {
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type);
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
File file = fileDeque.pop();
File[] files = file.listFiles();
if (files == null) continue;
for (File subFile : files) {
if (subFile.isDirectory()) {
fileDeque.push(subFile);
} else if (subFile.isFile() && subFile.getName().endsWith(".yml")) {
this.loadSingleFile(subFile, type);
}
}
}
}
}
/**
* Loads EffectCarrier configurations from a YAML file and populates the effectMap.
*
* @param file The YAML file to load configurations from.
* @param namespace The namespace to use when creating keys for EffectCarriers.
*/
private void loadSingleFile(File file, String namespace) {
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
for (Map.Entry<String, Object> entry : yaml.getValues(false).entrySet()) {
String value = entry.getKey();
if (entry.getValue() instanceof ConfigurationSection section) {
Key key = Key.of(namespace, value);
EffectCarrier item = getEffectCarrierFromSection(key, section);
if (item != null)
effectMap.put(key, item);
}
}
}
/**
* Parses a ConfigurationSection to create an EffectCarrier based on the specified key and configuration.
*
* @param key The key that uniquely identifies the EffectCarrier.
* @param section The ConfigurationSection containing the EffectCarrier configuration.
* @return An EffectCarrier instance based on the key and configuration, or null if the section is null.
*/
@Override
@Nullable
public EffectCarrier getEffectCarrierFromSection(Key key, ConfigurationSection section) {
if (section == null) return null;
return new EffectCarrier.Builder()
.key(key)
.requirements(plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("requirements"), true))
.effect(getEffectModifiers(section.getConfigurationSection("effects")))
.actionMap(plugin.getActionManager().getActionMap(section.getConfigurationSection("events")))
.build();
}
public void unload() {
HashMap<Key, EffectCarrier> temp = new HashMap<>(effectMap);
effectMap.clear();
for (Map.Entry<Key, EffectCarrier> entry : temp.entrySet()) {
if (entry.getValue().isPersist()) {
effectMap.put(entry.getKey(), entry.getValue());
}
}
}
/**
* Retrieves the initial FishingEffect that represents no special effects.
*
* @return The initial FishingEffect.
*/
@NotNull
@Override
public FishingEffect getInitialEffect() {
return new FishingEffect();
}
/**
* Retrieves a list of modifiers based on specified loot groups.
*
* @param modList A list of strings containing group modifiers in the format "group:modifier".
* @return A list of pairs where each pair represents a loot item and its associated modifier.
*/
private List<Pair<String, WeightModifier>> getGroupModifiers(List<String> modList) {
List<Pair<String, WeightModifier>> result = new ArrayList<>();
for (String group : modList) {
String[] split = group.split(":",2);
String key = split[0];
List<String> members = plugin.getLootManager().getGroupMembers(key);
if (members == null) {
LogUtils.warn("Group " + key + " doesn't contain any loot. The effect would not take effect.");
return result;
}
for (String loot : members) {
result.add(Pair.of(loot, ConfigUtils.getModifier(split[1])));
}
}
return result;
}
/**
* Parses a ConfigurationSection to retrieve an array of EffectModifiers.
*
* @param section The ConfigurationSection to parse.
* @return An array of EffectModifiers based on the values found in the section.
*/
@NotNull
@Override
public EffectModifier[] getEffectModifiers(ConfigurationSection section) {
if (section == null) return new EffectModifier[0];
ArrayList<EffectModifier> modifiers = new ArrayList<>();
for (Map.Entry<String, Object> entry: section.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection inner) {
EffectModifier effectModifier = getEffectModifier(inner);
if (effectModifier != null)
modifiers.add(effectModifier);
}
}
return modifiers.toArray(new EffectModifier[0]);
}
@Override
public LootBaseEffectImpl getBaseEffect(ConfigurationSection section) {
if (section == null) return new LootBaseEffectImpl(
new PlainMathValue(0), new PlainMathValue(1d),
new PlainMathValue(0), new PlainMathValue(1d),
new PlainMathValue(0), new PlainMathValue(1d)
);
MathValue waitTime = section.contains("wait-time") ? ConfigUtils.getValue(section.get("wait-time")) : new PlainMathValue(0);
MathValue difficulty = section.contains("difficulty") ? ConfigUtils.getValue(section.get("difficulty")) : new PlainMathValue(0);
MathValue gameTime = section.contains("game-time") ? ConfigUtils.getValue(section.get("game-time")) : new PlainMathValue(0);
MathValue waitTimeMultiplier = section.contains("wait-time-multiplier") ? ConfigUtils.getValue(section.get("wait-time-multiplier")) : new PlainMathValue(1);
MathValue difficultyMultiplier = section.contains("difficulty-multiplier") ? ConfigUtils.getValue(section.get("difficulty-multiplier")) : new PlainMathValue(1);
MathValue gameTimeMultiplier = section.contains("game-time-multiplier") ? ConfigUtils.getValue(section.get("game-time-multiplier")) : new PlainMathValue(1);
return new LootBaseEffectImpl(
waitTime, waitTimeMultiplier,
difficulty, difficultyMultiplier,
gameTime, gameTimeMultiplier
);
}
private void loadGlobalEffects() {
YamlConfiguration config = plugin.getConfig("config.yml");
ConfigurationSection section = config.getConfigurationSection("mechanics.global-effects");
GlobalSettings.setEffects(getEffectModifiers(section));
}
/**
* Parses a ConfigurationSection to create an EffectModifier based on the specified type and configuration.
*
* @param section The ConfigurationSection containing the effect modifier configuration.
* @return An EffectModifier instance based on the type and configuration.
*/
@Override
@Nullable
public EffectModifier getEffectModifier(ConfigurationSection section) {
String type = section.getString("type");
if (type == null) return null;
switch (type) {
case "weight-mod" -> {
var modList = ConfigUtils.getModifiers(section.getStringList("value"));
return ((effect, condition) -> {
effect.addWeightModifier(modList);
});
}
case "weight-mod-ignore-conditions" -> {
var modList = ConfigUtils.getModifiers(section.getStringList("value"));
return ((effect, condition) -> {
effect.addWeightModifierIgnored(modList);
});
}
case "group-mod" -> {
var modList = getGroupModifiers(section.getStringList("value"));
return ((effect, condition) -> {
effect.addWeightModifier(modList);
});
}
case "group-mod-ignore-conditions" -> {
var modList = getGroupModifiers(section.getStringList("value"));
return ((effect, condition) -> {
effect.addWeightModifierIgnored(modList);
});
}
case "wait-time" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setWaitTime(effect.waitTimeAdder() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "hook-time", "wait-time-multiplier" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setWaitTimeMultiplier(effect.getWaitTimeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1);
});
}
case "difficulty" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setDifficulty(effect.difficultyAdder() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "difficulty-multiplier", "difficulty-bonus" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setDifficultyMultiplier(effect.difficultyMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1);
});
}
case "multiple-loot" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setMultipleLootChance(effect.multipleLootChance() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "score" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setScore(effect.scoreAdder() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "score-bonus", "score-multiplier" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setScoreMultiplier(effect.scoreMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1);
});
}
case "size" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setSize(effect.sizeAdder() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "size-bonus", "size-multiplier" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setSizeMultiplier(effect.sizeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1);
});
}
case "game-time" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setGameTime(effect.gameTimeAdder() + value.get(condition.getPlayer(), condition.getArgs()));
});
}
case "game-time-bonus", "game-time-multiplier" -> {
var value = ConfigUtils.getValue(section.get("value"));
return ((effect, condition) -> {
effect.setGameTimeMultiplier(effect.gameTimeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1);
});
}
case "lava-fishing" -> {
return ((effect, condition) -> effect.setLavaFishing(true));
}
case "conditional" -> {
Requirement[] requirements = plugin.getRequirementManager().parseRequirements(section.getConfigurationSection("conditions"), true);
EffectModifier[] modifiers = getEffectModifiers(section.getConfigurationSection("effects"));
return ((effect, condition) -> {
for (Requirement requirement : requirements)
if (!requirement.check(condition))
return;
for (EffectModifier modifier : modifiers) {
modifier.modify(effect, condition);
}
});
}
default -> {
LogUtils.warn("Effect " + type + " doesn't exist.");
return null;
}
}
public Optional<EffectModifier> getEffectModifier(Key key) {
return Optional.ofNullable(this.effectModifiers.get(key));
}
}

View File

@@ -19,162 +19,98 @@ package net.momirealms.customfishing.bukkit.entity;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.integration.EntityProvider;
import net.momirealms.customfishing.api.mechanic.entity.EntityConfigImpl;
import net.momirealms.customfishing.api.mechanic.context.Context;
import net.momirealms.customfishing.api.mechanic.context.ContextKeys;
import net.momirealms.customfishing.api.mechanic.entity.EntityConfig;
import net.momirealms.customfishing.api.mechanic.entity.EntityManager;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.bukkit.compatibility.entity.VanillaEntityProvider;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
public class BukkitEntityManager implements EntityManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, EntityProvider> entityLibraryMap;
private final HashMap<String, EntityConfigImpl> entityConfigMap;
private final HashMap<String, EntityProvider> entityProviders = new HashMap<>();
private final HashMap<String, EntityConfig> entities = new HashMap<>();
public BukkitEntityManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.entityLibraryMap = new HashMap<>();
this.entityConfigMap = new HashMap<>();
this.registerEntityProvider(new VanillaEntityProvider());
}
public void load() {
this.loadConfig();
}
public void unload() {
HashMap<String, EntityConfigImpl> tempMap = new HashMap<>(this.entityConfigMap);
this.entityConfigMap.clear();
for (Map.Entry<String, EntityConfigImpl> entry : tempMap.entrySet()) {
if (entry.getValue().isPersist()) {
tempMap.put(entry.getKey(), entry.getValue());
this.registerEntityProvider(new EntityProvider() {
@Override
public String identifier() {
return "vanilla";
}
}
@NotNull
@Override
public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map<String, Object> propertyMap) {
return location.getWorld().spawnEntity(location, EntityType.valueOf(id.toUpperCase(Locale.ENGLISH)));
}
});
}
/**
* Registers an entity library for use in the plugin.
*
* @param entityProvider The entity library to register.
* @return {@code true} if the entity library was successfully registered, {@code false} if it already exists.
*/
@Override
public boolean registerEntityProvider(EntityProvider entityProvider) {
if (entityLibraryMap.containsKey(entityProvider.identifier())) return false;
else entityLibraryMap.put(entityProvider.identifier(), entityProvider);
public void unload() {
this.entities.clear();
}
@Override
public void disable() {
unload();
this.entityProviders.clear();
}
@Override
public Optional<EntityConfig> getEntity(String id) {
return Optional.ofNullable(this.entities.get(id));
}
@Override
public boolean registerEntity(String id, EntityConfig entity) {
if (entities.containsKey(id)) return false;
this.entities.put(id, entity);
return true;
}
/**
* Unregisters an entity library by its identification key.
*
* @param identification The identification key of the entity library to unregister.
* @return {@code true} if the entity library was successfully unregistered, {@code false} if it does not exist.
*/
public boolean registerEntityProvider(EntityProvider entityProvider) {
if (entityProviders.containsKey(entityProvider.identifier())) return false;
else entityProviders.put(entityProvider.identifier(), entityProvider);
return true;
}
public boolean unregisterEntityProvider(String id) {
return entityProviders.remove(id) != null;
}
@Nullable
@Override
public boolean unregisterEntityProvider(String identification) {
return entityLibraryMap.remove(identification) != null;
}
/**
* Load configuration files for entity properties.
*/
@SuppressWarnings("DuplicatedCode")
private void loadConfig() {
Deque<File> fileDeque = new ArrayDeque<>();
for (String type : List.of("entity")) {
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type);
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
File file = fileDeque.pop();
File[] files = file.listFiles();
if (files == null) continue;
for (File subFile : files) {
if (subFile.isDirectory()) {
fileDeque.push(subFile);
} else if (subFile.isFile() && subFile.getName().endsWith(".yml")) {
this.loadSingleFile(subFile);
}
}
}
}
}
/**
* Load a single entity configuration file.
*
* @param file The YAML file to load.
*/
private void loadSingleFile(File file) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection section) {
String entityID = section.getString("entity");
if (entityID == null) {
LogUtils.warn("Entity can't be null. File:" + file.getAbsolutePath() + "; Section:" + section.getCurrentPath());
continue;
}
HashMap<String, Object> propertyMap = new HashMap<>();
ConfigurationSection property = section.getConfigurationSection("properties");
if (property != null) {
propertyMap.putAll(property.getValues(false));
}
EntityConfigImpl entityConfigImpl = new EntityConfigImpl.BuilderImpl()
.entityID(entityID)
.persist(false)
.horizontalVector(section.getDouble("velocity.horizontal", 1.1))
.verticalVector(section.getDouble("velocity.vertical", 1.2))
.propertyMap(propertyMap)
.build();
entityConfigMap.put(entry.getKey(), entityConfigImpl);
}
}
}
public void disable() {
unload();
this.entityConfigMap.clear();
this.entityLibraryMap.clear();
}
/**
* Summons an entity based on the given loot configuration to a specified location.
*
* @param hookLocation The location where the entity will be summoned, typically where the fishing hook is.
* @param playerLocation The location of the player who triggered the entity summoning.
* @param loot The loot configuration that defines the entity to be summoned.
*/
@Override
public void summonEntity(Location hookLocation, Location playerLocation, Loot loot) {
EntityConfigImpl config = entityConfigMap.get(loot.getID());
if (config == null) {
LogUtils.warn("Entity: " + loot.getID() + " doesn't exist.");
return;
}
public Entity summonEntityLoot(Context<Player> context) {
String id = context.arg(ContextKeys.ID);
EntityConfig config = requireNonNull(entities.get(id), "Entity " + id + " not found");
Location hookLocation = requireNonNull(context.arg(ContextKeys.HOOK_LOCATION));
Location playerLocation = requireNonNull(context.getHolder().getLocation());
String entityID = config.getEntityID();
Entity entity;
if (entityID.contains(":")) {
String[] split = entityID.split(":", 2);
String identification = split[0];
String id = split[1];
EntityProvider library = entityLibraryMap.get(identification);
entity = library.spawn(hookLocation, id, config.getPropertyMap());
EntityProvider provider = requireNonNull(entityProviders.get(split[0]), "EntityProvider " + split[0] + " doesn't exist");
entity = requireNonNull(provider.spawn(hookLocation, split[1], config.getPropertyMap()), "Entity " + entityID + " doesn't exist");
} else {
entity = entityLibraryMap.get("vanilla").spawn(hookLocation, entityID, config.getPropertyMap());
entity = entityProviders.get("vanilla").spawn(hookLocation, entityID, config.getPropertyMap());
}
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1);
vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector());
entity.setVelocity(vector);
return entity;
}
}

View File

@@ -1,32 +1,73 @@
package net.momirealms.customfishing.bukkit.event;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import net.momirealms.customfishing.api.mechanic.context.Context;
import net.momirealms.customfishing.api.mechanic.event.EventCarrier;
import net.momirealms.customfishing.api.mechanic.event.EventManager;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Optional;
public class BukkitEventManager implements EventManager {
public class BukkitEventManager implements EventManager, Listener {
private final HashMap<String, EventCarrier> carrierMap = new HashMap<>();
private BukkitCustomFishingPlugin plugin;
private final HashMap<String, EventCarrier> carriers = new HashMap<>();
private final BukkitCustomFishingPlugin plugin;
public BukkitEventManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.carriers.clear();
HandlerList.unregisterAll(this);
}
@Override
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap());
}
@Override
public Optional<EventCarrier> getEventCarrier(String id) {
return Optional.ofNullable(carrierMap.get(id));
return Optional.ofNullable(carriers.get(id));
}
@Override
public boolean registerEventCarrier(String id, EventCarrier carrier) {
if (carrierMap.containsKey(id)) {
return false;
}
carrierMap.put(id, carrier);
if (carriers.containsKey(id)) return false;
carriers.put(id, carrier);
return true;
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND)
return;
if (event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_AIR && event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK)
return;
ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand();
if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0)
return;
String id = plugin.getItemManager().getItemID(itemStack);
Optional.ofNullable(carriers.get(id)).ifPresent(carrier -> {
carrier.trigger(Context.player(event.getPlayer()), ActionTrigger.INTERACT);
});
}
@EventHandler (ignoreCancelled = true)
public void onConsumeItem(PlayerItemConsumeEvent event) {
Optional.ofNullable(carriers.get(plugin.getItemManager().getItemID(event.getItem())))
.ifPresent(carrier -> carrier.trigger(Context.player(event.getPlayer()), ActionTrigger.CONSUME));
}
}

View File

@@ -1,84 +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.fishing;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.bukkit.util.FakeItemUtils;
import org.bukkit.entity.FishHook;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* A task responsible for animating bait when it's attached to a fishing hook.
*/
public class BaitAnimationTask implements Runnable {
private final CancellableTask cancellableTask;
private final int entityID;
private final Player player;
private final FishHook fishHook;
/**
* Constructs a new BaitAnimationTask.
*
* @param plugin The CustomFishingPlugin instance.
* @param player The player who cast the fishing rod.
* @param fishHook The FishHook entity.
* @param baitItem The bait ItemStack.
*/
public BaitAnimationTask(BukkitCustomFishingPlugin plugin, Player player, FishHook fishHook, ItemStack baitItem) {
this.player = player;
this.fishHook = fishHook;
entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
if (plugin.getVersionManager().isVersionNewerThan1_19_4()) {
BukkitCustomFishingPluginImpl.sendPackets(player, FakeItemUtils.getSpawnPacket(entityID, fishHook.getLocation()), FakeItemUtils.getMetaPacket(entityID, baitItem));
} else {
BukkitCustomFishingPluginImpl.sendPacket(player, FakeItemUtils.getSpawnPacket(entityID, fishHook.getLocation()));
BukkitCustomFishingPluginImpl.sendPacket(player, FakeItemUtils.getMetaPacket(entityID, baitItem));
}
this.cancellableTask = plugin.getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS);
}
@Override
public void run() {
if ( fishHook == null
|| fishHook.isOnGround()
|| fishHook.isInLava()
|| fishHook.isInWater()
|| !fishHook.isValid()
) {
cancelAnimation();
} else {
BukkitCustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getVelocityPacket(entityID, fishHook.getVelocity()));
BukkitCustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getTpPacket(entityID, fishHook.getLocation()));
}
}
/**
* Cancels the bait animation and cleans up resources.
*/
private void cancelAnimation() {
cancellableTask.cancel();
BukkitCustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getDestroyPacket(entityID));
}
}

View File

@@ -1,872 +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.fishing;
import com.destroystokyo.paper.event.player.PlayerJumpEvent;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.common.Pair;
import net.momirealms.customfishing.api.event.FishingResultEvent;
import net.momirealms.customfishing.api.event.LavaFishingEvent;
import net.momirealms.customfishing.api.event.RodCastEvent;
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition;
import net.momirealms.customfishing.api.mechanic.effect.Effect;
import net.momirealms.customfishing.api.mechanic.fishing.FishingManager;
import net.momirealms.customfishing.api.mechanic.game.BasicGameConfig;
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 net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.api.mechanic.loot.LootType;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.api.util.WeightUtils;
import net.momirealms.customfishing.bukkit.util.ItemUtils;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.*;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitFishingManager implements Listener, FishingManager {
private final BukkitCustomFishingPluginImpl plugin;
private final ConcurrentHashMap<UUID, FishHook> hookCacheMap;
private final ConcurrentHashMap<UUID, HookCheckTimerTask> hookCheckMap;
private final ConcurrentHashMap<UUID, TempFishingState> tempFishingStateMap;
private final ConcurrentHashMap<UUID, GamingPlayer> gamingPlayerMap;
private final ConcurrentHashMap<UUID, Pair<ItemStack, Integer>> vanillaLootMap;
public BukkitFishingManager(BukkitCustomFishingPluginImpl plugin) {
this.plugin = plugin;
this.hookCacheMap = new ConcurrentHashMap<>();
this.tempFishingStateMap = new ConcurrentHashMap<>();
this.gamingPlayerMap = new ConcurrentHashMap<>();
this.hookCheckMap = new ConcurrentHashMap<>();
this.vanillaLootMap = new ConcurrentHashMap<>();
}
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
public void unload() {
HandlerList.unregisterAll(this);
for (FishHook hook : hookCacheMap.values()) {
hook.remove();
}
for (HookCheckTimerTask task : hookCheckMap.values()) {
task.destroy();
}
for (GamingPlayer gamingPlayer : gamingPlayerMap.values()) {
gamingPlayer.cancel();
}
this.hookCacheMap.clear();
this.tempFishingStateMap.clear();
this.gamingPlayerMap.clear();
this.hookCheckMap.clear();
}
public void disable() {
unload();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onFishMONITOR(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.MONITOR) return;
this.selectState(event);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onFishHIGHEST(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.HIGHEST) return;
this.selectState(event);
}
@EventHandler(priority = EventPriority.HIGH)
public void onFishHIGH(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.HIGH) return;
this.selectState(event);
}
@EventHandler(priority = EventPriority.NORMAL)
public void onFishNORMAL(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.NORMAL) return;
this.selectState(event);
}
@EventHandler(priority = EventPriority.LOW)
public void onFishLOW(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.LOW) return;
this.selectState(event);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onFishLOWEST(PlayerFishEvent event) {
if (CFConfig.eventPriority != EventPriority.LOWEST) return;
this.selectState(event);
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
this.removeHook(uuid);
this.removeTempFishingState(player);
this.removeHookCheckTask(player);
this.vanillaLootMap.remove(uuid);
GamingPlayer gamingPlayer = gamingPlayerMap.remove(player.getUniqueId());
if (gamingPlayer != null) {
gamingPlayer.cancel();
}
}
/**
* Known bug: This is a Minecraft packet limitation
* When you fish, both left click air and right click air
* are triggered. And you can't cancel the left click event.
*/
@EventHandler (ignoreCancelled = false)
public void onLeftClick(PlayerInteractEvent event) {
if (event.getAction() != Action.LEFT_CLICK_AIR)
return;
if (event.getMaterial() != Material.FISHING_ROD)
return;
if (event.getHand() != EquipmentSlot.HAND)
return;
GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId());
if (gamingPlayer != null) {
if (gamingPlayer.onLeftClick())
event.setCancelled(true);
}
}
@EventHandler (ignoreCancelled = true)
public void onSwapHand(PlayerSwapHandItemsEvent event) {
GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId());
if (gamingPlayer != null) {
if (gamingPlayer.onSwapHand())
event.setCancelled(true);
}
}
@EventHandler
public void onJump(PlayerJumpEvent event) {
if (event.isCancelled()) return;
GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId());
if (gamingPlayer != null) {
if (gamingPlayer.onJump())
event.setCancelled(true);
}
}
@EventHandler
public void onSneak(PlayerToggleSneakEvent event) {
if (event.isCancelled()) return;
if (!event.isSneaking()) return;
GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId());
if (gamingPlayer != null) {
if (gamingPlayer.onSneak())
event.setCancelled(true);
}
}
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
if (event.isCancelled()) return;
GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId());
if (gamingPlayer != null) {
if (gamingPlayer.onChat(event.getMessage()))
event.setCancelled(true);
}
}
/**
* Removes a fishing hook entity associated with a given UUID.
*
* @param uuid The UUID of the fishing hook entity to be removed.
* @return {@code true} if the fishing hook was successfully removed, {@code false} otherwise.
*/
@Override
public boolean removeHook(UUID uuid) {
FishHook hook = hookCacheMap.remove(uuid);
if (hook != null && hook.isValid()) {
plugin.getScheduler().runTaskSync(hook::remove, hook.getLocation());
return true;
} else {
return false;
}
}
/**
* 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
*/
@Override
@Nullable
public FishHook getHook(UUID uuid) {
FishHook fishHook = hookCacheMap.get(uuid);
if (fishHook != null) {
if (!fishHook.isValid()) {
hookCacheMap.remove(uuid);
return null;
} else {
return fishHook;
}
}
return null;
}
/**
* Selects the appropriate fishing state based on the provided PlayerFishEvent and triggers the corresponding action.
*
* @param event The PlayerFishEvent that represents the fishing action.
*/
public void selectState(PlayerFishEvent event) {
if (event.isCancelled()) return;
switch (event.getState()) {
case FISHING -> onCastRod(event);
case REEL_IN -> onReelIn(event);
case CAUGHT_ENTITY -> onCaughtEntity(event);
case CAUGHT_FISH -> onCaughtFish(event);
case BITE -> onBite(event);
case IN_GROUND -> onInGround(event);
}
}
/**
* Handle the event when the fishing hook lands on the ground.
*
* @param event The PlayerFishEvent that occurred.
*/
private void onInGround(PlayerFishEvent event) {
final Player player = event.getPlayer();
FishHook hook = event.getHook();
if (player.getGameMode() != GameMode.CREATIVE) {
ItemStack itemStack = player.getInventory().getItemInMainHand();
if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand();
if (itemStack.getType() == Material.FISHING_ROD) {
NBTItem nbtItem = new NBTItem(itemStack);
NBTCompound compound = nbtItem.getCompound("CustomFishing");
if (compound != null && compound.hasTag("max_dur")) {
event.setCancelled(true);
hook.remove();
ItemUtils.decreaseDurability(player, itemStack, 2, true);
}
}
}
}
/**
* Handle the event when a player casts a fishing rod.
*
* @param event The PlayerFishEvent that occurred.
*/
public void onCastRod(PlayerFishEvent event) {
var player = event.getPlayer();
var fishingPreparation = new FishingPreparationImpl(player, plugin);
if (!fishingPreparation.canFish()) {
event.setCancelled(true);
return;
}
// Check mechanic requirements
if (!RequirementManager.isRequirementMet(
fishingPreparation, RequirementManagerImpl.mechanicRequirements
)) {
this.removeTempFishingState(player);
return;
}
FishingEffect initialEffect = plugin.getEffectManager().getInitialEffect();
// Merge totem effects
EffectCarrier totemEffect = plugin.getTotemManager().getTotemEffect(player.getLocation());
if (totemEffect != null)
for (EffectModifier modifier : totemEffect.getEffectModifiers()) {
modifier.modify(initialEffect, fishingPreparation);
}
// Call custom event
RodCastEvent rodCastEvent = new RodCastEvent(event, fishingPreparation, initialEffect);
Bukkit.getPluginManager().callEvent(rodCastEvent);
if (rodCastEvent.isCancelled()) {
return;
}
// Store fishhook entity and apply the effects
final FishHook fishHook = event.getHook();
this.hookCacheMap.put(player.getUniqueId(), fishHook);
// Reduce amount & Send animation
var baitItem = fishingPreparation.getBaitItemStack();
if (baitItem != null) {
ItemStack cloned = baitItem.clone();
cloned.setAmount(1);
new BaitAnimationTask(plugin, player, fishHook, cloned);
baitItem.setAmount(baitItem.getAmount() - 1);
}
// Arrange hook check task
this.hookCheckMap.put(player.getUniqueId(), new HookCheckTimerTask(this, fishHook, fishingPreparation, initialEffect));
// trigger actions
fishingPreparation.triggerActions(ActionTrigger.CAST);
}
/**
* Handle the event when a player catches an entity.
*
* @param event The PlayerFishEvent that occurred.
*/
private void onCaughtEntity(PlayerFishEvent event) {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
Entity entity = event.getCaught();
if ((entity instanceof ArmorStand armorStand)
&& armorStand.getPersistentDataContainer().get(
Objects.requireNonNull(NamespacedKey.fromString("lavafishing", plugin)),
PersistentDataType.STRING
) != null) {
// The hook is hooked into the temp entity
// This might be called both not in game and in game
LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(player, LavaFishingEvent.State.REEL_IN, event.getHook());
Bukkit.getPluginManager().callEvent(lavaFishingEvent);
if (lavaFishingEvent.isCancelled()) {
event.setCancelled(true);
return;
}
GamingPlayer gamingPlayer = gamingPlayerMap.get(uuid);
if (gamingPlayer != null) {
// in game
if (gamingPlayer.onRightClick())
event.setCancelled(true);
} else {
// not in game
HookCheckTimerTask task = hookCheckMap.get(uuid);
if (task != null)
task.destroy();
else
// should not reach this but in case
entity.remove();
}
return;
}
if (player.getGameMode() != GameMode.CREATIVE) {
ItemStack itemStack = player.getInventory().getItemInMainHand();
if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand();
NBTItem nbtItem = new NBTItem(itemStack);
NBTCompound nbtCompound = nbtItem.getCompound("CustomFishing");
if (nbtCompound != null && nbtCompound.hasTag("max_dur")) {
event.getHook().remove();
event.setCancelled(true);
ItemUtils.decreaseDurability(player, itemStack, 5, true);
}
}
}
/**
* Handle the event when a player catches a fish.
*
* @param event The PlayerFishEvent that occurred.
*/
private void onCaughtFish(PlayerFishEvent event) {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
if (!(event.getCaught() instanceof Item item))
return;
// If player is playing the game
GamingPlayer gamingPlayer = gamingPlayerMap.get(uuid);
if (gamingPlayer != null) {
if (gamingPlayer.onRightClick())
event.setCancelled(true);
return;
}
// If player is not playing the game
var temp = this.getTempFishingState(uuid);
if (temp != null) {
var loot = temp.getLoot();
if (loot.getID().equals("vanilla")) {
// put vanilla loot in map
this.vanillaLootMap.put(uuid, Pair.of(item.getItemStack(), event.getExpToDrop()));
}
var fishingPreparation = temp.getPreparation();
loot.triggerActions(ActionTrigger.HOOK, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.HOOK);
if (!loot.disableGame()) {
// start the game if the loot has a game
if (startFishingGame(player, fishingPreparation, temp.getEffect())) {
event.setCancelled(true);
}
} else {
// remove temp state if fishing game not exists
this.removeTempFishingState(player);
var hook = event.getHook();
// If the game is disabled, then do success actions
success(temp, hook);
// Cancel the event because loots can be multiple and unique
event.setCancelled(true);
hook.remove();
}
return;
}
}
/**
* Handle the event when a player receives a bite on their fishing hook.
*
* @param event The PlayerFishEvent that occurred.
*/
private void onBite(PlayerFishEvent event) {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
// If player is already in game
// then ignore the event
GamingPlayer gamingPlayer = getGamingPlayer(uuid);
if (gamingPlayer != null) {
return;
}
// If the loot's game is instant
TempFishingState temp = getTempFishingState(uuid);
if (temp != null) {
var loot = temp.getLoot();
var fishingPreparation = temp.getPreparation();
fishingPreparation.setLocation(event.getHook().getLocation());
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.BITE, fishingPreparation);
loot.triggerActions(ActionTrigger.BITE, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.BITE);
if (loot.instantGame() && !loot.disableGame()) {
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.HOOK, fishingPreparation);
loot.triggerActions(ActionTrigger.HOOK, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.HOOK);
startFishingGame(player, fishingPreparation, temp.getEffect());
}
}
}
/**
* Handle the event when a player reels in their fishing line.
*
* @param event The PlayerFishEvent that occurred.
*/
private void onReelIn(PlayerFishEvent event) {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
// If player is in game
GamingPlayer gamingPlayer = getGamingPlayer(uuid);
if (gamingPlayer != null) {
if (gamingPlayer.onRightClick())
event.setCancelled(true);
return;
}
// If player is lava fishing
HookCheckTimerTask hookTask = hookCheckMap.get(uuid);
if (hookTask != null && hookTask.isFishHooked()) {
LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(player, LavaFishingEvent.State.CAUGHT_FISH, event.getHook());
Bukkit.getPluginManager().callEvent(lavaFishingEvent);
if (lavaFishingEvent.isCancelled()) {
event.setCancelled(true);
return;
}
var temp = getTempFishingState(uuid);
if (temp != null ) {
Loot loot = temp.getLoot();
var fishingPreparation = temp.getPreparation();
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.HOOK, fishingPreparation);
loot.triggerActions(ActionTrigger.HOOK, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.HOOK);
if (!loot.disableGame()) {
event.setCancelled(true);
startFishingGame(player, fishingPreparation, temp.getEffect());
} else {
success(temp, event.getHook());
}
}
return;
}
}
/**
* Removes the temporary fishing state associated with a player.
*
* @param player The player whose temporary fishing state should be removed.
*/
@Override
public TempFishingState removeTempFishingState(Player player) {
return this.tempFishingStateMap.remove(player.getUniqueId());
}
/**
* Processes the game result for a gaming player
*
* @param gamingPlayer The gaming player whose game result should be processed.
*/
@Override
public void processGameResult(GamingPlayer gamingPlayer) {
final Player player = gamingPlayer.getPlayer();
final UUID uuid = player.getUniqueId();
FishHook fishHook = hookCacheMap.remove(uuid);
if (fishHook == null) {
LogUtils.warn("Unexpected situation: Can't get player's fish hook when processing game results.");
return;
}
TempFishingState tempFishingState = removeTempFishingState(player);
if (tempFishingState == null) {
LogUtils.warn("Unexpected situation: Can't get player's fishing state when processing game results.");
return;
}
Effect bonus = gamingPlayer.getEffectReward();
if (bonus != null)
tempFishingState.getEffect().merge(bonus);
gamingPlayer.cancel();
gamingPlayerMap.remove(uuid);
plugin.getScheduler().runTaskSync(() -> {
if (gamingPlayer.isSuccessful()) {
success(tempFishingState, fishHook);
} else {
fail(tempFishingState, fishHook);
}
fishHook.remove();
}, fishHook.getLocation());
}
public void fail(TempFishingState state, FishHook hook) {
var loot = state.getLoot();
var fishingPreparation = state.getPreparation();
if (loot.getID().equals("vanilla")) {
Pair<ItemStack, Integer> pair = this.vanillaLootMap.remove(fishingPreparation.getPlayer().getUniqueId());
if (pair != null) {
fishingPreparation.insertArg("{nick}", "<lang:item.minecraft." + pair.left().getType().toString().toLowerCase() + ">");
fishingPreparation.insertArg("{loot}", pair.left().getType().toString());
}
}
// call event
FishingResultEvent fishingResultEvent = new FishingResultEvent(
fishingPreparation.getPlayer(),
FishingResultEvent.Result.FAILURE,
hook,
loot,
fishingPreparation.getArgs()
);
Bukkit.getPluginManager().callEvent(fishingResultEvent);
if (fishingResultEvent.isCancelled()) {
return;
}
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.FAILURE, fishingPreparation);
loot.triggerActions(ActionTrigger.FAILURE, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.FAILURE);
if (state.getPreparation().getPlayer().getGameMode() != GameMode.CREATIVE) {
ItemUtils.decreaseHookDurability(fishingPreparation.getRodItemStack(), 1, true);
}
}
/**
* Handle the success of a fishing attempt, including spawning loot, calling events, and executing success actions.
*
* @param state The temporary fishing state containing information about the loot and effect.
* @param hook The FishHook entity associated with the fishing attempt.
*/
public void success(TempFishingState state, FishHook hook) {
var loot = state.getLoot();
var effect = state.getEffect();
var fishingPreparation = state.getPreparation();
var player = fishingPreparation.getPlayer();
fishingPreparation.insertArg("{size-multiplier}", String.valueOf(effect.sizeMultiplier()));
fishingPreparation.insertArg("{size-fixed}", String.valueOf(effect.sizeAdder()));
int amount;
if (loot.getType() == LootType.ITEM) {
amount = (int) effect.multipleLootChance();
amount += Math.random() < (effect.multipleLootChance() - amount) ? 2 : 1;
} else {
amount = 1;
}
fishingPreparation.insertArg("{amount}", String.valueOf(amount));
// call event
FishingResultEvent fishingResultEvent = new FishingResultEvent(
player,
FishingResultEvent.Result.SUCCESS,
hook,
loot,
fishingPreparation.getArgs()
);
Bukkit.getPluginManager().callEvent(fishingResultEvent);
if (fishingResultEvent.isCancelled()) {
return;
}
switch (loot.getType()) {
case ITEM -> {
// build the items for multiple times instead of using setAmount() to make sure that each item is unique
if (loot.getID().equals("vanilla")) {
Pair<ItemStack, Integer> pair = vanillaLootMap.remove(player.getUniqueId());
if (pair != null) {
fishingPreparation.insertArg("{nick}", "<lang:item.minecraft." + pair.left().getType().toString().toLowerCase() + ">");
for (int i = 0; i < amount; i++) {
plugin.getScheduler().runTaskSyncLater(() -> {
plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), pair.left().clone(), fishingPreparation);
doSuccessActions(loot, effect, fishingPreparation, player);
if (pair.right() > 0) {
player.giveExp(pair.right(), true);
AdventureHelper.getInstance().sendSound(player, Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1);
}
}, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i);
}
}
} else {
for (int i = 0; i < amount; i++) {
plugin.getScheduler().runTaskSyncLater(() -> {
ItemStack item = plugin.getItemManager().buildInternal(player, "item", loot.getID(), fishingPreparation.getArgs());
if (item == null) {
LogUtils.warn(String.format("Item %s not exists", loot.getID()));
return;
}
plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), item, fishingPreparation);
doSuccessActions(loot, effect, fishingPreparation, player);
}, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i);
}
}
}
case ENTITY -> {
plugin.getEntityManager().summonEntity(hook.getLocation(), player.getLocation(), loot);
doSuccessActions(loot, effect, fishingPreparation, player);
}
case BLOCK -> {
plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot);
doSuccessActions(loot, effect, fishingPreparation, player);
}
}
if (player.getGameMode() != GameMode.CREATIVE) {
ItemStack rod = state.getPreparation().getRodItemStack();
ItemUtils.decreaseHookDurability(rod, 1, false);
ItemUtils.decreaseDurability(player, rod, 1, true);
}
}
/**
* Execute success-related actions after a successful fishing attempt, including updating competition data, triggering events and actions, and updating player statistics.
*
* @param loot The loot that was successfully caught.
* @param effect The effect applied during fishing.
* @param fishingPreparation The fishing preparation containing preparation data.
* @param player The player who successfully caught the loot.
*/
private void doSuccessActions(Loot loot, Effect effect, FishingPreparation fishingPreparation, Player player) {
FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition();
if (competition != null && RequirementManager.isRequirementMet(fishingPreparation, competition.getConfig().getJoinRequirements())) {
String scoreStr = fishingPreparation.getArg("{CUSTOM_SCORE}");
if (scoreStr != null) {
competition.refreshData(player, Double.parseDouble(scoreStr));
} else {
double score = 0;
switch (competition.getGoal()) {
case CATCH_AMOUNT -> {
score = 1;
competition.refreshData(player, score);
}
case MAX_SIZE, TOTAL_SIZE -> {
String size = fishingPreparation.getArg("{SIZE}");
if (size != null) {
score = Double.parseDouble(size);
competition.refreshData(player, score);
} else {
score = 0;
}
}
case TOTAL_SCORE -> {
score = loot.getScore();
if (score > 0) {
score = score * effect.scoreMultiplier() + effect.scoreAdder();
competition.refreshData(player, score);
} else {
score = 0;
}
}
}
fishingPreparation.insertArg("{score}", String.format("%.2f", score));
fishingPreparation.insertArg("{SCORE}", String.valueOf(score));
}
}
// events and actions
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.SUCCESS, fishingPreparation);
loot.triggerActions(ActionTrigger.SUCCESS, fishingPreparation);
fishingPreparation.triggerActions(ActionTrigger.SUCCESS);
player.setStatistic(
Statistic.FISH_CAUGHT,
player.getStatistic(Statistic.FISH_CAUGHT) + 1
);
if (!loot.disableStats())
Optional.ofNullable(
plugin.getStatisticsManager()
.getStatistics(player.getUniqueId())
).ifPresent(it -> {
it.addLootAmount(loot, fishingPreparation, 1);
String size = fishingPreparation.getArg("{SIZE}");
if (size != null)
if (it.setSizeIfHigher(loot.getStatisticKey().sizeKey(), Float.parseFloat(size))) {
if (!loot.disableGlobalAction())
GlobalSettings.triggerLootActions(ActionTrigger.NEW_SIZE_RECORD, fishingPreparation);
loot.triggerActions(ActionTrigger.NEW_SIZE_RECORD, fishingPreparation);
}
});
}
/**
* 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.
*/
@Override
public boolean startFishingGame(Player player, PlayerContext playerContext, Effect effect) {
Map<String, Double> gameWithWeight = plugin.getGameManager().getGameWithWeight(playerContext);
String random = WeightUtils.getRandom(gameWithWeight);
Pair<BasicGameConfig, GameInstance> gamePair = plugin.getGameManager().getGameInstance(random);
if (random == null) {
LogUtils.warn("No game is available for player:" + player.getName() + " location:" + playerContext.getLocation());
return false;
}
if (gamePair == null) {
LogUtils.warn(String.format("Game %s doesn't exist.", random));
return false;
}
plugin.debug("Game: " + random);
return startFishingGame(player, Objects.requireNonNull(gamePair.left().getGameSetting(effect)), gamePair.right());
}
/**
* 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.
*/
@Override
public boolean startFishingGame(Player player, GameSettings settings, GameInstance gameInstance) {
plugin.debug("Difficulty:" + settings.difficulty());
plugin.debug("Time:" + settings.time());
FishHook hook = getHook(player.getUniqueId());
if (hook != null) {
this.gamingPlayerMap.put(player.getUniqueId(), gameInstance.start(player, hook, settings));
return true;
} else {
LogUtils.warn("It seems that player " + player.getName() + " is not fishing. Fishing game failed to start.");
return false;
}
}
/**
* 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.
*/
@Override
public boolean hasPlayerCastHook(UUID uuid) {
FishHook fishHook = hookCacheMap.get(uuid);
if (fishHook == null) return false;
if (!fishHook.isValid()) {
hookCacheMap.remove(uuid);
return false;
}
return true;
}
/**
* 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.
*/
@Override
public void setTempFishingState(Player player, TempFishingState tempFishingState) {
tempFishingStateMap.put(player.getUniqueId(), tempFishingState);
}
public void removeHookCheckTask(Player player) {
hookCheckMap.remove(player.getUniqueId());
}
/**
* 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.
*/
@Override
@Nullable
public GamingPlayer getGamingPlayer(UUID uuid) {
return gamingPlayerMap.get(uuid);
}
/**
* 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.
*/
@Override
@Nullable
public TempFishingState getTempFishingState(UUID uuid) {
return tempFishingStateMap.get(uuid);
}
}

View File

@@ -1,188 +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.fishing;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class FishingPreparationImpl extends FishingPreparation {
private boolean hasBait = false;
private boolean hasHook = false;
private @Nullable ItemStack baitItemStack;
private final @NotNull ItemStack rodItemStack;
private final List<EffectCarrier> effects;
private boolean canFish = true;
public FishingPreparationImpl(Player player, BukkitCustomFishingPlugin plugin) {
super(player);
PlayerInventory playerInventory = player.getInventory();
ItemStack mainHandItem = playerInventory.getItemInMainHand();
ItemStack offHandItem = playerInventory.getItemInOffHand();
this.effects = new ArrayList<>();
boolean rodOnMainHand = mainHandItem.getType() == Material.FISHING_ROD;
this.rodItemStack = rodOnMainHand ? mainHandItem : offHandItem;
String rodItemID = plugin.getItemManager().getAnyPluginItemID(this.rodItemStack);
EffectCarrier rodEffect = plugin.getEffectManager().getEffectCarrier("rod", rodItemID);
if (rodEffect != null) effects.add(rodEffect);
super.insertArg("{rod}", rodItemID);
NBTItem nbtItem = new NBTItem(rodItemStack);
NBTCompound cfCompound = nbtItem.getCompound("CustomFishing");
if (cfCompound != null && cfCompound.hasTag("hook_id")) {
String hookID = cfCompound.getString("hook_id");
super.insertArg("{hook}", hookID);
this.hasHook = true;
EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("hook", hookID);
if (carrier != null) {
this.effects.add(carrier);
}
}
String baitItemID = plugin.getItemManager().getAnyPluginItemID(rodOnMainHand ? offHandItem : mainHandItem);
EffectCarrier baitEffect = plugin.getEffectManager().getEffectCarrier("bait", baitItemID);
if (baitEffect != null) {
this.baitItemStack = rodOnMainHand ? offHandItem : mainHandItem;
this.effects.add(baitEffect);
this.hasBait = true;
super.insertArg("{bait}", baitItemID);
}
if (plugin.getBagManager().isEnabled()) {
Inventory fishingBag = plugin.getBagManager().getOnlineBagInventory(player.getUniqueId());
HashSet<String> uniqueUtils = new HashSet<>(4);
if (fishingBag != null) {
this.insertArg("{in-bag}", "true");
for (int i = 0; i < fishingBag.getSize(); i++) {
ItemStack itemInBag = fishingBag.getItem(i);
String bagItemID = plugin.getItemManager().getAnyPluginItemID(itemInBag);
if (!hasBait) {
EffectCarrier effect = plugin.getEffectManager().getEffectCarrier("bait", bagItemID);
if (effect != null) {
this.hasBait = true;
this.baitItemStack = itemInBag;
this.effects.add(effect);
super.insertArg("{bait}", bagItemID);
continue;
}
}
EffectCarrier utilEffect = plugin.getEffectManager().getEffectCarrier("util", bagItemID);
if (utilEffect != null && !uniqueUtils.contains(bagItemID)) {
effects.add(utilEffect);
uniqueUtils.add(bagItemID);
}
}
this.delArg("{in-bag}");
}
}
for (String enchant : plugin.getIntegrationManager().getEnchantments(rodItemStack)) {
EffectCarrier enchantEffect = plugin.getEffectManager().getEffectCarrier("enchant", enchant);
if (enchantEffect != null) {
this.effects.add(enchantEffect);
}
}
for (EffectCarrier effectCarrier : effects) {
if (!effectCarrier.isConditionMet(this)) {
this.canFish = false;
return;
}
}
}
/**
* Retrieves the ItemStack representing the fishing rod.
*
* @return The ItemStack representing the fishing rod.
*/
@NotNull
public ItemStack getRodItemStack() {
return rodItemStack;
}
/**
* Retrieves the ItemStack representing the bait (if available).
*
* @return The ItemStack representing the bait, or null if no bait is set.
*/
@Nullable
public ItemStack getBaitItemStack() {
return baitItemStack;
}
/**
* Checks if player meet the requirements for fishing gears
*
* @return True if can fish, false otherwise.
*/
public boolean canFish() {
return this.canFish;
}
/**
* Merges a FishingEffect into this fishing rod, applying effect modifiers.
*
* @param effect The FishingEffect to merge into this rod.
*/
public void mergeEffect(FishingEffect effect) {
for (EffectModifier modifier : GlobalSettings.getEffectModifiers()) {
modifier.modify(effect, this);
}
for (EffectCarrier effectCarrier : effects) {
for (EffectModifier modifier : effectCarrier.getEffectModifiers()) {
modifier.modify(effect, this);
}
}
}
/**
* Triggers actions associated with a specific action trigger.
*
* @param actionTrigger The action trigger that initiates the actions.
*/
public void triggerActions(ActionTrigger actionTrigger) {
GlobalSettings.triggerRodActions(actionTrigger, this);
if (hasBait) GlobalSettings.triggerBaitActions(actionTrigger, this);
if (hasHook) GlobalSettings.triggerHookActions(actionTrigger, this);
for (EffectCarrier effectCarrier : effects) {
Action[] actions = effectCarrier.getActions(actionTrigger);
if (actions != null)
for (Action action : actions) {
action.trigger(this);
}
}
}
}

View File

@@ -1,353 +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.fishing;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.FishHookLandEvent;
import net.momirealms.customfishing.api.event.LavaFishingEvent;
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import net.momirealms.customfishing.api.mechanic.effect.Effect;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FishHook;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.Vector;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* A task responsible for checking the state of a fishing hook and handling lava fishing mechanics.
*/
public class HookCheckTimerTask implements Runnable {
private final BukkitFishingManager manager;
private final CancellableTask hookMovementTask;
private LavaEffectTask lavaFishingTask;
private final FishHook fishHook;
private final FishingPreparation fishingPreparation;
private final FishingEffect initialEffect;
private Effect tempEffect;
private final int lureLevel;
private boolean firstTime;
private boolean fishHooked;
private boolean reserve;
private int jumpTimer;
private Entity hookedEntity;
private Loot loot;
private boolean inWater;
/**
* Constructs a new HookCheckTimerTask.
*
* @param manager The FishingManagerImpl instance.
* @param fishHook The FishHook entity being checked.
* @param fishingPreparation The FishingPreparation instance.
* @param initialEffect The initial fishing effect.
*/
public HookCheckTimerTask(
BukkitFishingManager manager,
FishHook fishHook,
FishingPreparation fishingPreparation,
FishingEffect initialEffect
) {
this.inWater = false;
this.manager = manager;
this.fishHook = fishHook;
this.initialEffect = initialEffect;
this.fishingPreparation = fishingPreparation;
this.hookMovementTask = BukkitCustomFishingPlugin.get().getScheduler().runTaskSyncTimer(this, fishHook.getLocation(), 1, 1);
this.lureLevel = fishingPreparation.getRodItemStack().getEnchantmentLevel(Enchantment.LURE);
this.firstTime = true;
this.tempEffect = new FishingEffect();
}
@Override
public void run() {
if (
!this.fishHook.isValid()
//|| (fishHook.getHookedEntity() != null && fishHook.getHookedEntity().getType() != EntityType.ARMOR_STAND)
) {
// This task would be cancelled when hook is removed
this.destroy();
return;
}
if (this.fishHook.isOnGround()) {
this.inWater = false;
return;
}
if (this.fishHook.getLocation().getBlock().getType() == Material.LAVA) {
this.inWater = false;
// if player can fish in lava
if (firstTime) {
this.firstTime = false;
this.fishingPreparation.setLocation(this.fishHook.getLocation());
this.fishingPreparation.mergeEffect(this.initialEffect);
if (!initialEffect.allowLavaFishing()) {
this.destroy();
return;
}
FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.LAVA, this.fishHook, true, this.initialEffect);
Bukkit.getPluginManager().callEvent(event);
this.fishingPreparation.insertArg("{lava}", "true");
this.fishingPreparation.triggerActions(ActionTrigger.LAND);
}
// simulate fishing mechanic
if (this.fishHooked) {
this.jumpTimer++;
if (this.jumpTimer < 4)
return;
this.jumpTimer = 0;
this.fishHook.setVelocity(new Vector(0,0.24,0));
return;
}
if (!this.reserve) {
// jump
if (this.jumpTimer < 5) {
this.jumpTimer++;
this.fishHook.setVelocity(new Vector(0,0.2 - this.jumpTimer * 0.02,0));
return;
}
this.reserve = true;
this.setNextLoot();
if (this.loot != null) {
this.tempEffect = this.loot.baseEffect().build(fishingPreparation.getPlayer(), fishingPreparation.getArgs());
this.tempEffect.merge(this.initialEffect);
this.setTempState();
this.startLavaFishingMechanic();
} else {
this.tempEffect = new FishingEffect();
this.tempEffect.merge(this.initialEffect);
this.manager.removeTempFishingState(fishingPreparation.getPlayer());
BukkitCustomFishingPlugin.get().debug("No loot available for " + fishingPreparation.getPlayer().getName() + " at " + fishingPreparation.getLocation());
}
this.makeHookStatic(this.fishHook.getLocation());
}
return;
}
if (!this.inWater && this.fishHook.isInWater()) {
this.inWater = true;
this.fishingPreparation.setLocation(this.fishHook.getLocation());
this.fishingPreparation.insertArg("{lava}", "false");
this.fishingPreparation.insertArg("{open-water}", String.valueOf(this.fishHook.isInOpenWater()));
if (this.firstTime) {
this.firstTime = false;
this.fishingPreparation.mergeEffect(this.initialEffect);
FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.WATER, this.fishHook, false, this.initialEffect);
Bukkit.getPluginManager().callEvent(event);
this.fishingPreparation.triggerActions(ActionTrigger.LAND);
} else {
FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.WATER, this.fishHook, true, this.initialEffect);
Bukkit.getPluginManager().callEvent(event);
}
this.setNextLoot();
if (this.loot == null) {
// prevent players from getting vanilla loots
this.fishHook.setWaitTime(Integer.MAX_VALUE);
this.tempEffect = new FishingEffect();
this.tempEffect.merge(this.initialEffect);
this.manager.removeTempFishingState(fishingPreparation.getPlayer());
BukkitCustomFishingPlugin.get().debug("No loot available for " + fishingPreparation.getPlayer().getName() + " at " + fishingPreparation.getLocation());
} else {
this.tempEffect = this.loot.baseEffect().build(fishingPreparation.getPlayer(), fishingPreparation.getArgs());
this.tempEffect.merge(this.initialEffect);
this.setWaitTime();
this.setTempState();
}
return;
}
}
/**
* Destroys the task and associated entities.
*/
public void destroy() {
this.cancelSubTask();
this.removeTempEntity();
this.hookMovementTask.cancel();
this.manager.removeHookCheckTask(fishingPreparation.getPlayer());
}
/**
* Cancels the lava fishing subtask if it's active.
*/
public void cancelSubTask() {
if (lavaFishingTask != null && !lavaFishingTask.isCancelled()) {
lavaFishingTask.cancel();
lavaFishingTask = null;
}
}
private void setNextLoot() {
Loot nextLoot = BukkitCustomFishingPlugin.get().getLootManager().getNextLoot(initialEffect, fishingPreparation);
if (nextLoot == null) {
this.loot = null;
return;
}
this.loot = nextLoot;
}
/**
* Sets temporary state and prepares for the next loot.
*/
private void setTempState() {
fishingPreparation.insertArg("{nick}", loot.getNick());
fishingPreparation.insertArg("{loot}", loot.getID());
if (!loot.disableStats()) {
fishingPreparation.insertArg("{statistics_size}", loot.getStatisticKey().sizeKey());
fishingPreparation.insertArg("{statistics_amount}", loot.getStatisticKey().amountKey());
}
manager.setTempFishingState(fishingPreparation.getPlayer(), new TempFishingState(
tempEffect,
fishingPreparation,
loot
));
}
/**
* Removes the temporary hooked entity.
*/
public void removeTempEntity() {
if (hookedEntity != null && !hookedEntity.isDead())
hookedEntity.remove();
}
/**
* Starts the lava fishing mechanic.
*/
private void startLavaFishingMechanic() {
// get random time
int random;
if (CFConfig.overrideVanilla) {
random = ThreadLocalRandom.current().nextInt(CFConfig.lavaMinTime, CFConfig.lavaMaxTime);
random *= tempEffect.waitTimeMultiplier();
random += tempEffect.waitTimeAdder();
random = Math.max(1, random);
} else {
random = ThreadLocalRandom.current().nextInt(CFConfig.lavaMinTime, CFConfig.lavaMaxTime);
random -= lureLevel * 100;
random = Math.max(CFConfig.lavaMinTime, random);
random *= tempEffect.waitTimeMultiplier();
random += tempEffect.waitTimeAdder();
random = Math.max(1, random);
}
// lava effect task (Three seconds in advance)
this.lavaFishingTask = new LavaEffectTask(
this,
fishHook.getLocation(),
random - 3 * 20
);
}
/**
* Handles the hook state of the fish hook.
*/
public void getHooked() {
LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(fishingPreparation.getPlayer(), LavaFishingEvent.State.BITE, fishHook);
Bukkit.getPluginManager().callEvent(lavaFishingEvent);
if (lavaFishingEvent.isCancelled()) {
this.startLavaFishingMechanic();
return;
}
this.loot.triggerActions(ActionTrigger.BITE, fishingPreparation);
this.fishingPreparation.triggerActions(ActionTrigger.BITE);
this.fishHooked = true;
this.removeTempEntity();
AdventureHelper.getInstance().sendSound(
fishingPreparation.getPlayer(),
Sound.Source.NEUTRAL,
Key.key("minecraft:block.pointed_dripstone.drip_lava_into_cauldron"),
1,
1
);
BukkitCustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> {
fishHooked = false;
reserve = false;
}, (2 * 20) * 50L, TimeUnit.MILLISECONDS);
}
private void makeHookStatic(Location armorLoc) {
armorLoc.setY(armorLoc.getBlockY() + 0.2);
if (hookedEntity != null && !hookedEntity.isDead())
hookedEntity.remove();
hookedEntity = armorLoc.getWorld().spawn(armorLoc, ArmorStand.class, a -> {
a.setInvisible(true);
a.setCollidable(false);
a.setInvulnerable(true);
a.setVisible(false);
a.setCustomNameVisible(false);
a.setSmall(true);
a.setGravity(false);
a.getPersistentDataContainer().set(
Objects.requireNonNull(NamespacedKey.fromString("lavafishing", BukkitCustomFishingPlugin.get())),
PersistentDataType.STRING,
"temp"
);
});
fishHook.setHookedEntity(hookedEntity);
}
/**
* Checks if the fish hook is currently hooked.
*
* @return True if the fish hook is hooked, false otherwise.
*/
public boolean isFishHooked() {
return fishHooked;
}
private void setWaitTime() {
if (CFConfig.overrideVanilla) {
double initialTime = ThreadLocalRandom.current().nextInt(CFConfig.waterMaxTime - CFConfig.waterMinTime + 1) + CFConfig.waterMinTime;
fishHook.setWaitTime(Math.max(1, (int) (initialTime * tempEffect.waitTimeMultiplier() + tempEffect.waitTimeAdder())));
} else {
fishHook.setMinWaitTime(Math.max(1, (int) (fishHook.getMinWaitTime() * tempEffect.waitTimeMultiplier() + tempEffect.waitTimeAdder())));
fishHook.setMaxWaitTime(Math.max(2, (int) (fishHook.getMaxWaitTime() * tempEffect.waitTimeMultiplier() + tempEffect.waitTimeAdder())));
}
}
}

View File

@@ -1,89 +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.fishing;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import org.bukkit.Location;
import org.bukkit.Particle;
import java.util.concurrent.TimeUnit;
/**
* A task responsible for creating a lava effect animation between two points.
*/
public class LavaEffectTask implements Runnable {
private final Location startLoc;
private final Location endLoc;
private final Location controlLoc;
private int timer;
private final CancellableTask lavaTask;
private final HookCheckTimerTask hookCheckTimerTask;
/**
* Constructs a new LavaEffectTask.
*
* @param hookCheckTimerTask The HookCheckTimerTask instance.
* @param location The starting location for the lava effect.
* @param delay The delay before starting the task.
*/
public LavaEffectTask(HookCheckTimerTask hookCheckTimerTask, Location location, int delay) {
this.hookCheckTimerTask = hookCheckTimerTask;
this.startLoc = location.clone().add(0,0.3,0);
this.endLoc = this.startLoc.clone().add((Math.random() * 16 - 8), startLoc.getY(), (Math.random() * 16 - 8));
this.controlLoc = new Location(
startLoc.getWorld(),
(startLoc.getX() + endLoc.getX())/2 + Math.random() * 12 - 6,
startLoc.getY(),
(startLoc.getZ() + endLoc.getZ())/2 + Math.random() * 12 - 6
);
this.lavaTask = BukkitCustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, delay * 50L, 50, TimeUnit.MILLISECONDS);
}
@Override
public void run() {
timer++;
if (timer > 60) {
lavaTask.cancel();
BukkitCustomFishingPlugin.get().getScheduler().runTaskSync(hookCheckTimerTask::getHooked, startLoc);
} else {
double t = (double) timer / 60;
Location particleLoc = endLoc.clone().multiply(Math.pow((1 - t), 2)).add(controlLoc.clone().multiply(2 * t * (1 - t))).add(startLoc.clone().multiply(Math.pow(t, 2)));
particleLoc.setY(startLoc.getY());
startLoc.getWorld().spawnParticle(Particle.FLAME, particleLoc,1,0,0,0,0);
}
}
/**
* Cancels the lava effect task.
*/
public void cancel() {
if (lavaTask != null && !lavaTask.isCancelled())
lavaTask.cancel();
}
/**
* Checks if the lava effect task is cancelled.
*
* @return True if the task is cancelled, false otherwise.
*/
public boolean isCancelled() {
return lavaTask.isCancelled();
}
}

View File

@@ -17,7 +17,6 @@
package net.momirealms.customfishing.bukkit.gui.icon.property.item;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.bukkit.adventure.ShadedAdventureComponentWrapper;
import net.momirealms.customfishing.bukkit.gui.SectionPage;

View File

@@ -21,7 +21,6 @@ import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.bukkit.adventure.ShadedAdventureComponentWrapper;
import net.momirealms.customfishing.bukkit.gui.SectionPage;
import net.momirealms.customfishing.bukkit.gui.icon.BackGroundItem;
import net.momirealms.customfishing.bukkit.item.ItemManagerImpl;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;

View File

@@ -1,331 +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.hook;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.hook.HookManager;
import net.momirealms.customfishing.api.mechanic.hook.HookSetting;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.bukkit.item.ItemManagerImpl;
import net.momirealms.customfishing.bukkit.util.ItemUtils;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
public class BukkitHookManager implements Listener, HookManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, HookSetting> hookSettingMap;
public BukkitHookManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.hookSettingMap = new HashMap<>();
}
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin);
loadConfig();
}
public void unload() {
HandlerList.unregisterAll(this);
hookSettingMap.clear();
}
public void disable() {
unload();
}
/**
* Loads configuration files for the specified types.
*/
@SuppressWarnings("DuplicatedCode")
private void loadConfig() {
Deque<File> fileDeque = new ArrayDeque<>();
for (String type : List.of("hook")) {
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type);
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
File file = fileDeque.pop();
File[] files = file.listFiles();
if (files == null) continue;
for (File subFile : files) {
if (subFile.isDirectory()) {
fileDeque.push(subFile);
} else if (subFile.isFile() && subFile.getName().endsWith(".yml")) {
this.loadSingleFile(subFile);
}
}
}
}
}
/**
* Loads data from a single configuration file.
*
* @param file The configuration file to load.
*/
private void loadSingleFile(File file) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection section) {
if (!section.contains("max-durability")) {
LogUtils.warn("Please set max-durability to hook: " + entry.getKey());
continue;
}
var setting = new HookSetting.Builder(entry.getKey())
.durability(section.getInt("max-durability", 16))
.lore(section.getStringList("lore-on-rod").stream().map(it -> "<!i>" + it).toList())
.build();
hookSettingMap.put(entry.getKey(), setting);
}
}
}
/**
* Get the hook setting by its ID.
*
* @param id The ID of the hook setting to retrieve.
* @return The hook setting with the given ID, or null if not found.
*/
@Nullable
@Override
public HookSetting getHookSetting(String id) {
return hookSettingMap.get(id);
}
/**
* Decreases the durability of a fishing hook by a specified amount and optionally updates its lore.
*
* @param rod The fishing rod ItemStack to modify.
* @param amount The amount by which to decrease the durability.
* @param updateLore Whether to update the lore of the fishing rod.
*/
@Override
public void decreaseHookDurability(ItemStack rod, int amount, boolean updateLore) {
ItemUtils.decreaseHookDurability(rod, amount, updateLore);
}
/**
* Increases the durability of a fishing hook by a specified amount and optionally updates its lore.
*
* @param rod The fishing rod ItemStack to modify.
* @param amount The amount by which to increase the durability.
* @param updateLore Whether to update the lore of the fishing rod.
*/
@Override
public void increaseHookDurability(ItemStack rod, int amount, boolean updateLore) {
ItemUtils.increaseHookDurability(rod, amount, updateLore);
}
/**
* Sets the durability of a fishing hook to a specific amount and optionally updates its lore.
*
* @param rod The fishing rod ItemStack to modify.
* @param amount The new durability value to set.
* @param updateLore Whether to update the lore of the fishing rod.
*/
@Override
public void setHookDurability(ItemStack rod, int amount, boolean updateLore) {
ItemUtils.setHookDurability(rod, amount, updateLore);
}
/**
* Equips a fishing hook on a fishing rod.
*
* @param rod The fishing rod ItemStack.
* @param hook The fishing hook ItemStack.
* @return True if the hook was successfully equipped, false otherwise.
*/
@Override
public boolean equipHookOnRod(ItemStack rod, ItemStack hook) {
if (rod == null || hook == null || hook.getType() == Material.AIR || hook.getAmount() != 1)
return false;
if (rod.getType() != Material.FISHING_ROD)
return false;
String hookID = plugin.getItemManager().getAnyPluginItemID(hook);
HookSetting setting = getHookSetting(hookID);
if (setting == null)
return false;
var curDurability = ItemUtils.getCustomDurability(hook);
if (curDurability.left() == 0)
return false;
NBTItem rodNBTItem = new NBTItem(rod);
NBTCompound cfCompound = rodNBTItem.getOrCreateCompound("CustomFishing");
cfCompound.setString("hook_id", hookID);
cfCompound.setItemStack("hook_item", hook);
cfCompound.setInteger("hook_dur", curDurability.right());
ItemUtils.updateNBTItemLore(rodNBTItem);
rod.setItemMeta(rodNBTItem.getItem().getItemMeta());
return true;
}
/**
* Removes the fishing hook from a fishing rod.
*
* @param rod The fishing rod ItemStack.
* @return The removed fishing hook ItemStack, or null if no hook was found.
*/
@Override
public ItemStack removeHookFromRod(ItemStack rod) {
if (rod == null || rod.getType() != Material.FISHING_ROD)
return null;
NBTItem rodNBTItem = new NBTItem(rod);
NBTCompound cfCompound = rodNBTItem.getCompound("CustomFishing");
if (cfCompound == null)
return null;
ItemStack hook = cfCompound.getItemStack("hook_item");
if (hook != null) {
cfCompound.removeKey("hook_item");
cfCompound.removeKey("hook_id");
cfCompound.removeKey("hook_dur");
ItemUtils.updateNBTItemLore(rodNBTItem);
rod.setItemMeta(rodNBTItem.getItem().getItemMeta());
}
return hook;
}
/**
* Handles the event when a player clicks on a fishing rod in their inventory.
*
* @param event The InventoryClickEvent to handle.
*/
@EventHandler
@SuppressWarnings("deprecation")
public void onDragDrop(InventoryClickEvent event) {
if (event.isCancelled())
return;
final Player player = (Player) event.getWhoClicked();
if (event.getClickedInventory() != player.getInventory())
return;
ItemStack clicked = event.getCurrentItem();
if (clicked == null || clicked.getType() != Material.FISHING_ROD)
return;
if (player.getGameMode() != GameMode.SURVIVAL)
return;
if (plugin.getFishingManager().hasPlayerCastHook(player.getUniqueId()))
return;
ItemStack cursor = event.getCursor();
if (cursor == null || cursor.getType() == Material.AIR) {
if (event.getClick() == ClickType.RIGHT) {
NBTItem nbtItem = new NBTItem(clicked);
NBTCompound cfCompound = nbtItem.getCompound("CustomFishing");
if (cfCompound == null)
return;
if (cfCompound.hasTag("hook_id")) {
event.setCancelled(true);
ItemStack hook = cfCompound.getItemStack("hook_item");
ItemUtils.setDurability(hook, cfCompound.getInteger("hook_dur"), true);
cfCompound.removeKey("hook_id");
cfCompound.removeKey("hook_item");
cfCompound.removeKey("hook_dur");
event.setCursor(hook);
ItemUtils.updateNBTItemLore(nbtItem);
clicked.setItemMeta(nbtItem.getItem().getItemMeta());
}
}
return;
}
String hookID = plugin.getItemManager().getAnyPluginItemID(cursor);
HookSetting setting = getHookSetting(hookID);
if (setting == null)
return;
var cursorDurability = ItemUtils.getCustomDurability(cursor);
if (cursorDurability.left() == 0) {
if (plugin.getItemManager().getBuildableItem("hook", hookID) instanceof ItemManagerImpl.CFBuilder cfBuilder) {
ItemStack itemStack = cfBuilder.build(player, new HashMap<>());
var pair = ItemUtils.getCustomDurability(itemStack);
cursorDurability = pair;
NBTItem nbtItem = new NBTItem(cursor);
NBTCompound compound = nbtItem.getOrCreateCompound("CustomFishing");
compound.setInteger("max_dur", pair.left());
compound.setInteger("cur_dur", pair.right());
compound.setString("type", "hook");
compound.setString("id", hookID);
cursor.setItemMeta(nbtItem.getItem().getItemMeta());
} else {
return;
}
}
PlayerContext playerContext = new PlayerContext(player, new HashMap<>());
playerContext.insertArg("{rod}", plugin.getItemManager().getAnyPluginItemID(clicked));
EffectCarrier effectCarrier = plugin.getEffectManager().getEffectCarrier("hook", hookID);
if (effectCarrier != null) {
if (!RequirementManager.isRequirementMet(playerContext, effectCarrier.getRequirements())) {
return;
}
}
event.setCancelled(true);
NBTItem rodNBTItem = new NBTItem(clicked);
NBTCompound cfCompound = rodNBTItem.getOrCreateCompound("CustomFishing");
String previousHookID = cfCompound.getString("hook_id");
ItemStack clonedHook = cursor.clone();
clonedHook.setAmount(1);
cursor.setAmount(cursor.getAmount() - 1);
if (previousHookID != null && !previousHookID.equals("")) {
int previousHookDurability = cfCompound.getInteger("hook_dur");
ItemStack previousItemStack = cfCompound.getItemStack("hook_item");
ItemUtils.setDurability(previousItemStack, previousHookDurability, true);
if (cursor.getAmount() == 0) {
event.setCursor(previousItemStack);
} else {
ItemUtils.giveItem(player, previousItemStack, 1);
}
}
cfCompound.setString("hook_id", hookID);
cfCompound.setItemStack("hook_item", clonedHook);
cfCompound.setInteger("hook_dur", cursorDurability.right());
ItemUtils.updateNBTItemLore(rodNBTItem);
clicked.setItemMeta(rodNBTItem.getItem().getItemMeta());
}
}

View File

@@ -0,0 +1,244 @@
/*
* 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.integration;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.integration.*;
import net.momirealms.customfishing.bukkit.block.BukkitBlockManager;
import net.momirealms.customfishing.bukkit.entity.BukkitEntityManager;
import net.momirealms.customfishing.bukkit.integration.block.ItemsAdderBlockProvider;
import net.momirealms.customfishing.bukkit.integration.block.OraxenBlockProvider;
import net.momirealms.customfishing.bukkit.integration.enchant.AdvancedEnchantmentsProvider;
import net.momirealms.customfishing.bukkit.integration.entity.ItemsAdderEntityProvider;
import net.momirealms.customfishing.bukkit.integration.entity.MythicEntityProvider;
import net.momirealms.customfishing.bukkit.integration.item.*;
import net.momirealms.customfishing.bukkit.integration.level.*;
import net.momirealms.customfishing.bukkit.integration.quest.BattlePassQuest;
import net.momirealms.customfishing.bukkit.integration.quest.BetonQuestQuest;
import net.momirealms.customfishing.bukkit.integration.quest.ClueScrollsQuest;
import net.momirealms.customfishing.bukkit.integration.season.AdvancedSeasonsProvider;
import net.momirealms.customfishing.bukkit.integration.season.CustomCropsSeasonProvider;
import net.momirealms.customfishing.bukkit.integration.season.RealisticSeasonsProvider;
import net.momirealms.customfishing.bukkit.item.BukkitItemManager;
import net.momirealms.customfishing.common.util.Pair;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class BukkitIntegrationManager implements IntegrationManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, LevelerProvider> levelerProviders;
private final HashMap<String, EnchantmentProvider> enchantmentProviders;
private SeasonProvider seasonProvider;
public BukkitIntegrationManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.levelerProviders = new HashMap<>();
this.enchantmentProviders = new HashMap<>();
this.load();
}
public void disable() {
this.enchantmentProviders.clear();
this.levelerProviders.clear();
}
public void load() {
if (isHooked("ItemsAdder")) {
registerItemProvider(new ItemsAdderItemProvider());
registerBlockProvider(new ItemsAdderBlockProvider());
registerEntityProvider(new ItemsAdderEntityProvider());
}
if (isHooked("MMOItems")) {
registerItemProvider(new MMOItemsItemProvider());
}
if (isHooked("Oraxen")) {
registerItemProvider(new OraxenItemProvider());
registerBlockProvider(new OraxenBlockProvider());
}
if (isHooked("Zaphkiel")) {
registerItemProvider(new ZaphkielItemProvider());
}
if (isHooked("NeigeItems")) {
registerItemProvider(new NeigeItemsItemProvider());
}
if (isHooked("MythicMobs")) {
registerItemProvider(new MythicMobsItemProvider());
registerEntityProvider(new MythicEntityProvider());
}
if (isHooked("EcoJobs")) {
registerLevelerProvider(new EcoJobsLevelerProvider());
}
if (isHooked("EcoSkills")) {
registerLevelerProvider(new EcoSkillsLevelerProvider());
}
if (isHooked("Jobs")) {
registerLevelerProvider(new JobsRebornLevelerProvider());
}
if (isHooked("MMOCore")) {
registerLevelerProvider(new MMOCoreLevelerProvider());
}
if (isHooked("mcMMO")) {
try {
registerItemProvider(new McMMOTreasureProvider());
} catch (ClassNotFoundException | NoSuchMethodException e) {
plugin.getPluginLogger().warn("Failed to initialize mcMMO Treasure");
}
registerLevelerProvider(new McMMOLevelerProvider());
}
if (isHooked("AureliumSkills")) {
registerLevelerProvider(new AureliumSkillsProvider());
}
if (isHooked("AuraSkills")) {
registerLevelerProvider(new AuraSkillsLevelerProvider());
}
if (isHooked("AdvancedEnchantments")) {
registerEnchantmentProvider(new AdvancedEnchantmentsProvider());
}
if (isHooked("RealisticSeasons")) {
registerSeasonProvider(new RealisticSeasonsProvider());
} else if (isHooked("AdvancedSeasons")) {
registerSeasonProvider(new AdvancedSeasonsProvider());
} else if (isHooked("CustomCrops")) {
registerSeasonProvider(new CustomCropsSeasonProvider());
}
if (isHooked("Vault")) {
VaultHook.initialize();
}
if (isHooked("BattlePass")){
BattlePassQuest battlePassQuest = new BattlePassQuest();
battlePassQuest.register();
}
if (isHooked("ClueScrolls")) {
ClueScrollsQuest clueScrollsQuest = new ClueScrollsQuest();
clueScrollsQuest.register();
}
if (isHooked("BetonQuest")) {
BetonQuestQuest.register();
}
}
private boolean isHooked(String hooked) {
if (Bukkit.getPluginManager().getPlugin(hooked) != null) {
plugin.getPluginLogger().info(hooked + " hooked!");
return true;
}
return false;
}
@Override
public boolean registerLevelerProvider(@NotNull LevelerProvider leveler) {
if (levelerProviders.containsKey(leveler.identifier())) return false;
levelerProviders.put(leveler.identifier(), leveler);
return true;
}
@Override
public boolean unregisterLevelerProvider(@NotNull String id) {
return levelerProviders.remove(id) != null;
}
@Override
public boolean registerEnchantmentProvider(@NotNull EnchantmentProvider enchantment) {
if (enchantmentProviders.containsKey(enchantment.identifier())) return false;
enchantmentProviders.put(enchantment.identifier(), enchantment);
return true;
}
@Override
public boolean unregisterEnchantmentProvider(@NotNull String id) {
return enchantmentProviders.remove(id) != null;
}
@Override
@Nullable
public LevelerProvider getLevelerProvider(String plugin) {
return levelerProviders.get(plugin);
}
@Override
@Nullable
public EnchantmentProvider getEnchantmentProvider(String id) {
return enchantmentProviders.get(id);
}
@Override
public List<Pair<String, Short>> getEnchantments(ItemStack itemStack) {
ArrayList<Pair<String, Short>> list = new ArrayList<>();
for (EnchantmentProvider enchantmentProvider : enchantmentProviders.values()) {
list.addAll(enchantmentProvider.getEnchants(itemStack));
}
return list;
}
@Nullable
@Override
public SeasonProvider getSeasonProvider() {
return seasonProvider;
}
@Override
public boolean registerSeasonProvider(@NotNull SeasonProvider season) {
if (this.seasonProvider != null) return false;
this.seasonProvider = season;
return true;
}
@Override
public boolean unregisterSeasonProvider() {
if (this.seasonProvider == null) return false;
this.seasonProvider = null;
return true;
}
@Override
public boolean registerEntityProvider(@NotNull EntityProvider entity) {
return ((BukkitEntityManager) plugin.getEntityManager()).registerEntityProvider(entity);
}
@Override
public boolean unregisterEntityProvider(@NotNull String id) {
return ((BukkitEntityManager) plugin.getEntityManager()).unregisterEntityProvider(id);
}
@Override
public boolean registerItemProvider(@NotNull ItemProvider item) {
return ((BukkitItemManager) plugin.getItemManager()).registerItemProvider(item);
}
@Override
public boolean unregisterItemProvider(@NotNull String id) {
return ((BukkitItemManager) plugin.getItemManager()).unregisterItemProvider(id);
}
@Override
public boolean registerBlockProvider(@NotNull BlockProvider block) {
return ((BukkitBlockManager) plugin.getBlockManager()).registerBlockProvider(block);
}
@Override
public boolean unregisterBlockProvider(@NotNull String id) {
return ((BukkitBlockManager) plugin.getBlockManager()).unregisterBlockProvider(id);
}
}

View File

@@ -48,6 +48,16 @@ public abstract class BukkitItemFactory extends ItemFactory<CustomFishingPlugin,
return Optional.ofNullable(item.get(path));
}
@Override
protected boolean hasTag(RtagItem item, Object... path) {
return item.hasTag(path);
}
@Override
protected boolean removeTag(RtagItem item, Object... path) {
return item.remove(path);
}
@Override
protected void update(RtagItem item) {
item.update();

View File

@@ -2,38 +2,59 @@ package net.momirealms.customfishing.bukkit.item;
import net.kyori.adventure.key.Key;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.FishingLootPreSpawnEvent;
import net.momirealms.customfishing.api.event.FishingLootSpawnEvent;
import net.momirealms.customfishing.api.integration.ItemProvider;
import net.momirealms.customfishing.api.mechanic.config.ConfigManager;
import net.momirealms.customfishing.api.mechanic.context.Context;
import net.momirealms.customfishing.api.mechanic.context.ContextKeys;
import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem;
import net.momirealms.customfishing.api.mechanic.item.ItemManager;
import net.momirealms.customfishing.bukkit.util.ItemUtils;
import net.momirealms.customfishing.bukkit.util.LocationUtils;
import net.momirealms.customfishing.common.item.Item;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.Skull;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.*;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.inventory.InventoryPickupItemEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import static java.util.Objects.requireNonNull;
public class BukkitItemManager implements ItemManager {
public class BukkitItemManager implements ItemManager, Listener {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, ItemProvider> itemProviders = new HashMap<>();
private final HashMap<Key, CustomFishingItem> itemMap = new HashMap<>();
private final HashMap<Key, CustomFishingItem> items = new HashMap<>();
private final BukkitItemFactory factory;
private ItemProvider[] itemDetectArray;
public BukkitItemManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.factory = BukkitItemFactory.create(plugin);
this.registerVanilla();
}
private void registerVanilla() {
this.itemProviders.put("vanilla", new ItemProvider() {
this.registerItemProvider(new ItemProvider() {
@NotNull
@Override
public ItemStack buildItem(@NotNull Player player, @NotNull String id) {
@@ -51,10 +72,27 @@ public class BukkitItemManager implements ItemManager {
});
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
}
@Override
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap());
}
@Override
public boolean registerItem(@NotNull Key key, @NotNull CustomFishingItem item) {
if (items.containsKey(key)) return false;
items.put(key, item);
return true;
}
@Nullable
@Override
public ItemStack buildInternal(Context<Player> context, Key key) {
CustomFishingItem item = requireNonNull(itemMap.get(key), () -> "No item found for " + key);
public ItemStack buildInternal(@NotNull Context<Player> context, @NotNull Key key) {
CustomFishingItem item = requireNonNull(items.get(key), () -> "No item found for " + key);
ItemStack itemStack = getOriginalStack(context.getHolder(), item.material());
Item<ItemStack> wrappedItemStack = factory.wrap(itemStack);
for (BiConsumer<Item<ItemStack>, Context<Player>> consumer : item.tagConsumers()) {
@@ -63,8 +101,70 @@ public class BukkitItemManager implements ItemManager {
return wrappedItemStack.getItem();
}
@Override
public ItemStack buildAny(@NotNull Context<Player> context, @NotNull String item) {
return getOriginalStack(context.getHolder(), item);
}
@NotNull
@Override
public String getItemID(@NotNull ItemStack itemStack) {
if (itemStack.getType() == Material.AIR)
return "AIR";
for (ItemProvider library : itemDetectArray) {
String id = library.itemID(itemStack);
if (id != null)
return id;
}
// should not reach this because vanilla library would always work
return "AIR";
}
@Override
public String getCustomFishingItemID(@NotNull ItemStack itemStack) {
return (String) factory.wrap(itemStack).getTag("CustomFishing", "id").orElse(null);
}
@Nullable
@Override
@SuppressWarnings("all")
public org.bukkit.entity.Item dropItemLoot(@NotNull Context<Player> context) {
String id = requireNonNull(context.arg(ContextKeys.ID));
ItemStack itemStack = requireNonNull(buildInternal(context, Key.key("item", id)));
Player player = context.getHolder();
Location playerLocation = player.getLocation();
Location hookLocation = requireNonNull(context.arg(ContextKeys.HOOK_LOCATION));
FishingLootPreSpawnEvent preSpawnEvent = new FishingLootPreSpawnEvent(player, hookLocation, itemStack);
Bukkit.getPluginManager().callEvent(preSpawnEvent);
if (preSpawnEvent.isCancelled()) {
return null;
}
org.bukkit.entity.Item itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack);
FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(player, hookLocation, itemEntity);
Bukkit.getPluginManager().callEvent(spawnEvent);
if (spawnEvent.isCancelled()) {
itemEntity.remove();
return null;
}
itemEntity.setInvulnerable(true);
// prevent from being killed by lava
plugin.getScheduler().asyncLater(() -> {
if (itemEntity.isValid())
itemEntity.setInvulnerable(false);
}, 1, TimeUnit.SECONDS);
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105);
vector = vector.setY((vector.getY() + 0.22) * 1.18);
itemEntity.setVelocity(vector);
return itemEntity;
}
private ItemStack getOriginalStack(Player player, String material) {
if (material.contains(":")) {
if (!material.contains(":")) {
try {
return new ItemStack(Material.valueOf(material.toUpperCase(Locale.ENGLISH)));
} catch (IllegalArgumentException e) {
@@ -73,8 +173,132 @@ public class BukkitItemManager implements ItemManager {
}
} else {
String[] split = material.split(":", 2);
ItemProvider provider = requireNonNull(itemProviders.get(split[0]), "item provider " + split[0] + " not found");
return requireNonNull(provider.buildItem(player, split[0]), "item " + split[0] + " not found");
ItemProvider provider = requireNonNull(itemProviders.get(split[0]), "Item provider: " + split[0] + " not found");
return requireNonNull(provider.buildItem(player, split[0]), "Item: " + split[0] + " not found");
}
}
private void resetItemDetectionOrder() {
ArrayList<ItemProvider> list = new ArrayList<>();
for (String plugin : ConfigManager.itemDetectOrder()) {
ItemProvider provider = itemProviders.get(plugin);
if (provider != null)
list.add(provider);
}
this.itemDetectArray = list.toArray(new ItemProvider[0]);
}
public boolean registerItemProvider(ItemProvider item) {
if (itemProviders.containsKey(item.identifier())) return false;
itemProviders.put(item.identifier(), item);
this.resetItemDetectionOrder();
return true;
}
public boolean unregisterItemProvider(String id) {
boolean success = itemProviders.remove(id) != null;
if (success)
this.resetItemDetectionOrder();
return success;
}
@EventHandler(ignoreCancelled = true)
public void onInvPickItem(InventoryPickupItemEvent event) {
ItemStack itemStack = event.getItem().getItemStack();
Item<ItemStack> wrapped = factory.wrap(itemStack);
if (wrapped.hasTag("owner")) {
wrapped.removeTag("owner");
itemStack.setItemMeta(wrapped.getItem().getItemMeta());
}
}
@EventHandler (ignoreCancelled = true)
public void onPlaceBlock(BlockPlaceEvent event) {
ItemStack itemStack = event.getItemInHand();
if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0 || !itemStack.hasItemMeta()) {
return;
}
Item<ItemStack> wrapped = factory.wrap(itemStack);
if (wrapped.hasTag("CustomFishing")) {
if (!wrapped.hasTag("CustomFishing", "placeable")) {
event.setCancelled(true);
return;
}
Block block = event.getBlock();
if (block.getState() instanceof Skull) {
PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer();
ItemStack cloned = itemStack.clone();
cloned.setAmount(1);
pdc.set(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING, ItemUtils.toBase64(cloned));
} else {
event.setCancelled(true);
}
}
}
@EventHandler (ignoreCancelled = true)
public void onBreakBlock(BlockBreakEvent event) {
final Block block = event.getBlock();
if (block.getState() instanceof Skull) {
PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer();
String base64 = pdc.get(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING);
if (base64 != null) {
ItemStack itemStack = ItemUtils.fromBase64(base64);
event.setDropItems(false);
block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack);
}
}
}
@EventHandler (ignoreCancelled = true)
public void onPiston(BlockPistonExtendEvent event) {
handlePiston(event, event.getBlocks());
}
@EventHandler (ignoreCancelled = true)
public void onPiston(BlockPistonRetractEvent event) {
handlePiston(event, event.getBlocks());
}
private void handlePiston(Cancellable event, List<Block> blockList) {
for (Block block : blockList) {
if (block.getState() instanceof Skull) {
PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer();
if (pdc.has(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING)) {
event.setCancelled(true);
return;
}
}
}
}
@EventHandler (ignoreCancelled = true)
public void onExplosion(BlockExplodeEvent event) {
handleExplosion(event.blockList());
}
@EventHandler (ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent event) {
handleExplosion(event.blockList());
}
private void handleExplosion(List<Block> blocks) {
ArrayList<Block> blockToRemove = new ArrayList<>();
for (Block block : blocks) {
if (block.getState() instanceof Skull) {
PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer();
var nk = new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation()));
String base64 = pdc.get(nk, PersistentDataType.STRING);
if (base64 != null) {
ItemStack itemStack = ItemUtils.fromBase64(base64);
block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack);
blockToRemove.add(block);
block.setType(Material.AIR);
pdc.remove(nk);
}
}
}
blocks.removeAll(blockToRemove);
}
}

View File

@@ -11,8 +11,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class BukkitLootManager implements LootManager {
private final BukkitCustomFishingPlugin plugin;
@@ -23,13 +21,8 @@ public class BukkitLootManager implements LootManager {
this.plugin = plugin;
}
private void loadConfig() {
}
@Override
public void registerLoot(@NotNull final Loot loot) {
requireNonNull(loot, "loot cannot be null");
public void registerLoot(@NotNull Loot loot) {
this.lootMap.put(loot.getID(), loot);
for (String group : loot.lootGroup()) {
addGroupMember(group, loot.getID());

View File

@@ -20,13 +20,17 @@ package net.momirealms.customfishing.bukkit.market;
import de.tr7zw.changeme.nbtapi.NBTItem;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.action.Action;
import net.momirealms.customfishing.api.mechanic.context.Context;
import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder;
import net.momirealms.customfishing.api.mechanic.market.MarketManager;
import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customfishing.api.mechanic.misc.value.TextValue;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.api.storage.data.EarningData;
import net.momirealms.customfishing.bukkit.util.ConfigUtils;
import net.momirealms.customfishing.bukkit.util.NumberUtils;
import net.momirealms.customfishing.common.helper.ExpressionHelper;
import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -76,7 +80,7 @@ public class BukkitMarketManager implements MarketManager, Listener {
private boolean sellFishingBag;
private final ConcurrentHashMap<UUID, MarketGUI> marketGUIMap;
private boolean enable;
private CancellableTask resetEarningsTask;
private SchedulerTask resetEarningsTask;
private int date;
public BukkitMarketManager(BukkitCustomFishingPlugin plugin) {
@@ -91,7 +95,7 @@ public class BukkitMarketManager implements MarketManager, Listener {
this.loadConfig();
Bukkit.getPluginManager().registerEvents(this, plugin);
if (!enable) return;
this.resetEarningsTask = plugin.getScheduler().runTaskAsyncTimer(() -> {
this.resetEarningsTask = plugin.getScheduler().asyncRepeating(() -> {
int now = getRealTimeDate();
if (this.date != now) {
this.date = now;
@@ -491,18 +495,19 @@ public class BukkitMarketManager implements MarketManager, Listener {
}
@Override
public String getFormula() {
public TextValue<Player> getFormula() {
return formula;
}
@Override
public double getFishPrice(Player player, Map<String, String> vars) {
String temp = BukkitPlaceholderManager.getInstance().parse(player, formula, vars);
public double getFishPrice(Context<Player> context) {
formula.
String temp = plugin.getPlaceholderManager().parse(player, formula, vars);
var placeholders = BukkitPlaceholderManager.getInstance().resolvePlaceholders(temp);
for (String placeholder : placeholders) {
temp = temp.replace(placeholder, "0");
}
return new ExpressionBuilder(temp).build().evaluate();
return ExpressionHelper.evaluate(temp);
}
@Override

View File

@@ -15,7 +15,7 @@ import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.api.mechanic.misc.value.TextValue;
import net.momirealms.customfishing.api.mechanic.requirement.*;
import net.momirealms.customfishing.api.util.MoonPhase;
import net.momirealms.customfishing.bukkit.compatibility.VaultHook;
import net.momirealms.customfishing.bukkit.integration.VaultHook;
import net.momirealms.customfishing.common.util.ClassUtils;
import net.momirealms.customfishing.common.util.ListUtils;
import net.momirealms.customfishing.common.util.Pair;
@@ -599,7 +599,7 @@ public class BukkitRequirementManager implements RequirementManager<Player> {
registerRequirement("ice-fishing", (args, actions, advanced) -> {
boolean iceFishing = (boolean) args;
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Location location = requireNonNull(context.arg(ContextKeys.HOOK_LOCATION));
int water = 0, ice = 0;
for (int i = -2; i <= 2; i++)
for (int j = -1; j <= 2; j++)
@@ -661,7 +661,7 @@ public class BukkitRequirementManager implements RequirementManager<Player> {
registerRequirement("biome", (args, actions, advanced) -> {
HashSet<String> biomes = new HashSet<>(ListUtils.toList(args));
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Location location = requireNonNull(Optional.ofNullable(context.arg(ContextKeys.HOOK_LOCATION)).orElse(context.arg(ContextKeys.LOCATION)));
String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location);
if (biomes.contains(currentBiome))
return true;
@@ -672,7 +672,7 @@ public class BukkitRequirementManager implements RequirementManager<Player> {
registerRequirement("!biome", (args, actions, advanced) -> {
HashSet<String> biomes = new HashSet<>(ListUtils.toList(args));
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Location location = requireNonNull(Optional.ofNullable(context.arg(ContextKeys.HOOK_LOCATION)).orElse(context.arg(ContextKeys.LOCATION)));
String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location);
if (!biomes.contains(currentBiome))
return true;
@@ -749,7 +749,19 @@ public class BukkitRequirementManager implements RequirementManager<Player> {
private void registerCoolDownRequirement() {
registerRequirement("cooldown", (args, actions, advanced) -> {
return null;
if (args instanceof Section section) {
String key = section.getString("key");
int time = section.getInt("time");
return context -> {
if (!plugin.getCoolDownManager().isCoolDown(context.getHolder().getUniqueId(), key, time))
return true;
if (advanced) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at cooldown requirement which should be Section");
return EmptyRequirement.INSTANCE;
}
});
}

View File

@@ -20,6 +20,7 @@ package net.momirealms.customfishing.bukkit.statistic;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.statistic.StatisticsManager;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
@@ -28,39 +29,22 @@ import java.util.*;
public class BukkitStatisticsManager implements StatisticsManager {
private final BukkitCustomFishingPlugin plugin;
private final HashMap<String, List<String>> categoryMap;
private final Map<String, List<String>> categoryMap = new HashMap<>();
public BukkitStatisticsManager(BukkitCustomFishingPlugin plugin) {
this.plugin = plugin;
this.categoryMap = new HashMap<>();
}
@Override
public void load() {
this.loadCategoriesFromPluginFolder();
}
@Override
public void unload() {
this.categoryMap.clear();
}
public void disable() {
unload();
}
/**
* Get the statistics for a player with the given UUID.
*
* @param uuid The UUID of the player for whom statistics are retrieved.
* @return The player's statistics or null if the player is not found.
*/
@Override
@Nullable
public Statistics getStatistics(UUID uuid) {
OnlineUserData onlineUser = plugin.getStorageManager().getOnlineUser(uuid);
if (onlineUser == null) return null;
return onlineUser.getStatistics();
}
@SuppressWarnings("DuplicatedCode")
public void loadCategoriesFromPluginFolder() {
Deque<File> fileDeque = new ArrayDeque<>();
@@ -68,7 +52,7 @@ public class BukkitStatisticsManager implements StatisticsManager {
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type);
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
plugin.getBoostrap().saveResource("contents" + File.separator + type + File.separator + "default.yml", false);
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
@@ -93,15 +77,9 @@ public class BukkitStatisticsManager implements StatisticsManager {
}
}
/**
* Get a list of strings associated with a specific key in a category map.
*
* @param key The key to look up in the category map.
* @return A list of strings associated with the key or null if the key is not found.
*/
@NotNull
@Override
@Nullable
public List<String> getCategory(String key) {
return categoryMap.get(key);
public List<String> getCategoryMembers(String key) {
return categoryMap.getOrDefault(key, List.of());
}
}

View File

@@ -20,7 +20,6 @@ package net.momirealms.customfishing.bukkit.storage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.api.storage.DataStorageProvider;

View File

@@ -17,7 +17,6 @@
package net.momirealms.customfishing.bukkit.storage.method.database.sql;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.storage.StorageType;
import net.momirealms.customfishing.libraries.dependencies.Dependency;

View File

@@ -17,7 +17,6 @@
package net.momirealms.customfishing.bukkit.storage.method.database.sql;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.storage.StorageType;
import net.momirealms.customfishing.api.storage.data.PlayerData;

View File

@@ -23,7 +23,6 @@ import com.comphenix.protocol.wrappers.*;
import com.google.common.collect.Lists;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.momirealms.customfishing.bukkit.BukkitCustomFishingPluginImpl;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;