mirror of
https://github.com/Xiao-MoMi/Custom-Fishing.git
synced 2025-12-25 09:59:19 +00:00
2.0-backup-1
This commit is contained in:
42
plugin/.gitignore
vendored
Normal file
42
plugin/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
85
plugin/build.gradle.kts
Normal file
85
plugin/build.gradle.kts
Normal file
@@ -0,0 +1,85 @@
|
||||
dependencies {
|
||||
// server
|
||||
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
|
||||
|
||||
// command
|
||||
compileOnly("dev.jorel:commandapi-bukkit-core:9.1.0")
|
||||
|
||||
// packet
|
||||
compileOnly("com.comphenix.protocol:ProtocolLib:5.0.0")
|
||||
|
||||
// papi
|
||||
compileOnly("me.clip:placeholderapi:2.11.3")
|
||||
compileOnly("io.github.miniplaceholders:miniplaceholders-api:2.1.0")
|
||||
|
||||
// config
|
||||
compileOnly("dev.dejvokep:boosted-yaml:1.3.1")
|
||||
|
||||
// mythic
|
||||
compileOnly("io.lumine:Mythic-Dist:5.3.5")
|
||||
compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT")
|
||||
compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT")
|
||||
compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT")
|
||||
|
||||
// Gson
|
||||
compileOnly("com.google.code.gson:gson:2.10.1")
|
||||
|
||||
// eco
|
||||
compileOnly("com.willfp:eco:6.65.4")
|
||||
compileOnly("com.willfp:EcoJobs:3.29.1")
|
||||
compileOnly("com.willfp:EcoSkills:3.17.1")
|
||||
compileOnly("com.willfp:libreforge:4.29.1")
|
||||
|
||||
// database
|
||||
compileOnly("org.xerial:sqlite-jdbc:3.42.0.0")
|
||||
compileOnly("com.h2database:h2:2.2.220")
|
||||
compileOnly("org.mongodb:mongodb-driver-sync:4.10.2")
|
||||
compileOnly("com.zaxxer:HikariCP:5.0.1")
|
||||
compileOnly("redis.clients:jedis:4.4.3")
|
||||
|
||||
// others
|
||||
compileOnly("com.github.LoneDev6:api-itemsadder:3.5.0b")
|
||||
compileOnly("com.github.oraxen:oraxen:1.159.0")
|
||||
compileOnly("pers.neige.neigeitems:NeigeItems:1.15.9")
|
||||
compileOnly("com.github.Zrips:Jobs:4.17.2")
|
||||
compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21")
|
||||
|
||||
// local jars
|
||||
compileOnly(files("libs/AdvancedEnchantments-api.jar"))
|
||||
compileOnly(files("libs/RealisticSeasons-api.jar"))
|
||||
compileOnly(files("libs/CustomCrops-api.jar"))
|
||||
compileOnly(files("libs/mcMMO-api.jar"))
|
||||
|
||||
// api module
|
||||
implementation(project(":api"))
|
||||
|
||||
// adventure
|
||||
implementation("net.kyori:adventure-api:4.14.0")
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.0")
|
||||
implementation("net.kyori:adventure-text-minimessage:4.14.0")
|
||||
implementation("net.kyori:adventure-text-serializer-legacy:4.14.0")
|
||||
|
||||
// nbt
|
||||
implementation("de.tr7zw:item-nbt-api:2.11.3")
|
||||
|
||||
// bStats
|
||||
implementation("org.bstats:bstats-bukkit:3.0.1")
|
||||
|
||||
// local lib
|
||||
implementation(files("libs/BiomeAPI.jar"))
|
||||
implementation(files("libs/ProtectionLib.jar"))
|
||||
|
||||
// anvil
|
||||
implementation("net.wesjd:anvilgui:1.7.0-SNAPSHOT")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate ("de.tr7zw", "net.momirealms.customfishing.libraries")
|
||||
relocate ("net.kyori", "net.momirealms.customfishing.libraries")
|
||||
relocate ("net.wesjd", "net.momirealms.customfishing.libraries")
|
||||
relocate ("org.bstats", "net.momirealms.customfishing.libraries.bstats")
|
||||
relocate ("net.momirealms.biomeapi", "net.momirealms.customfishing.libraries.biomeapi")
|
||||
relocate ("net.momirealms.protectionlib", "net.momirealms.customfishing.libraries.protectionlib")
|
||||
}
|
||||
}
|
||||
BIN
plugin/libs/AdvancedEnchantments-api.jar
Normal file
BIN
plugin/libs/AdvancedEnchantments-api.jar
Normal file
Binary file not shown.
BIN
plugin/libs/BiomeAPI.jar
Normal file
BIN
plugin/libs/BiomeAPI.jar
Normal file
Binary file not shown.
BIN
plugin/libs/CustomCrops-api.jar
Normal file
BIN
plugin/libs/CustomCrops-api.jar
Normal file
Binary file not shown.
BIN
plugin/libs/RealisticSeasons-api.jar
Normal file
BIN
plugin/libs/RealisticSeasons-api.jar
Normal file
Binary file not shown.
BIN
plugin/libs/mcMMO-api.jar
Normal file
BIN
plugin/libs/mcMMO-api.jar
Normal file
Binary file not shown.
@@ -0,0 +1,211 @@
|
||||
package net.momirealms.customfishing;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||
import de.tr7zw.changeme.nbtapi.utils.VersionChecker;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.api.util.ReflectionUtils;
|
||||
import net.momirealms.customfishing.command.CommandManagerImpl;
|
||||
import net.momirealms.customfishing.compatibility.IntegrationManagerImpl;
|
||||
import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl;
|
||||
import net.momirealms.customfishing.libraries.libraryloader.LibraryLoader;
|
||||
import net.momirealms.customfishing.mechanic.action.ActionManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.bag.BagManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.block.BlockManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.competition.CompetitionManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.effect.EffectManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.fishing.FishingManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.game.GameManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.item.ItemManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.loot.LootManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.market.MarketManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.mob.MobManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl;
|
||||
import net.momirealms.customfishing.scheduler.SchedulerImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import net.momirealms.customfishing.storage.StorageManagerImpl;
|
||||
import net.momirealms.customfishing.version.VersionManagerImpl;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class CustomFishingPluginImpl extends CustomFishingPlugin {
|
||||
|
||||
private static ProtocolManager protocolManager;
|
||||
|
||||
public CustomFishingPluginImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
this.versionManager = new VersionManagerImpl(this);
|
||||
this.disableNBTAPILogs();
|
||||
ReflectionUtils.load();
|
||||
|
||||
this.actionManager = new ActionManagerImpl(this);
|
||||
this.adventure = new AdventureManagerImpl(this);
|
||||
this.bagManager = new BagManagerImpl(this);
|
||||
this.blockManager = new BlockManagerImpl(this);
|
||||
this.commandManager = new CommandManagerImpl(this);
|
||||
this.effectManager = new EffectManagerImpl(this);
|
||||
this.fishingManager = new FishingManagerImpl(this);
|
||||
this.gameManager = new GameManagerImpl(this);
|
||||
this.integrationManager = new IntegrationManagerImpl(this);
|
||||
this.itemManager = new ItemManagerImpl(this);
|
||||
this.lootManager = new LootManagerImpl(this);
|
||||
this.marketManager = new MarketManagerImpl(this);
|
||||
this.mobManager = new MobManagerImpl(this);
|
||||
this.placeholderManager = new PlaceholderManagerImpl(this);
|
||||
this.requirementManager = new RequirementManagerImpl(this);
|
||||
this.scheduler = new SchedulerImpl(this);
|
||||
this.storageManager = new StorageManagerImpl(this);
|
||||
this.competitionManager = new CompetitionManagerImpl(this);
|
||||
this.reload();
|
||||
if (Config.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/customfishing.2723<!u>");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
((AdventureManagerImpl) this.adventure).close();
|
||||
((BagManagerImpl) this.bagManager).disable();
|
||||
((BlockManagerImpl) this.blockManager).disable();
|
||||
((EffectManagerImpl) this.effectManager).disable();
|
||||
((FishingManagerImpl) this.fishingManager).disable();
|
||||
((GameManagerImpl) this.gameManager).disable();
|
||||
((ItemManagerImpl) this.itemManager).disable();
|
||||
((LootManagerImpl) this.lootManager).disable();
|
||||
((MarketManagerImpl) this.marketManager).disable();
|
||||
((MobManagerImpl) this.mobManager).disable();
|
||||
((RequirementManagerImpl) this.requirementManager).disable();
|
||||
((SchedulerImpl) this.scheduler).shutdown();
|
||||
((IntegrationManagerImpl) this.integrationManager).disable();
|
||||
((StorageManagerImpl) this.storageManager).disable();
|
||||
((CompetitionManagerImpl) this.competitionManager).disable();
|
||||
((PlaceholderManagerImpl) this.placeholderManager).disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
Config.load();
|
||||
Locale.load();
|
||||
((SchedulerImpl) this.scheduler).reload();
|
||||
((RequirementManagerImpl) this.requirementManager).unload();
|
||||
((RequirementManagerImpl) this.requirementManager).load();
|
||||
((ItemManagerImpl) this.itemManager).unload();
|
||||
((ItemManagerImpl) this.itemManager).load();
|
||||
((LootManagerImpl) this.lootManager).unload();
|
||||
((LootManagerImpl) this.lootManager).load();
|
||||
((FishingManagerImpl) this.fishingManager).unload();
|
||||
((FishingManagerImpl) this.fishingManager).load();
|
||||
((EffectManagerImpl) this.effectManager).unload();
|
||||
((EffectManagerImpl) this.effectManager).load();
|
||||
((MarketManagerImpl) this.marketManager).unload();
|
||||
((MarketManagerImpl) this.marketManager).load();
|
||||
((BagManagerImpl) this.bagManager).unload();
|
||||
((BagManagerImpl) this.bagManager).load();
|
||||
((BlockManagerImpl) this.blockManager).unload();
|
||||
((BlockManagerImpl) this.blockManager).load();
|
||||
((GameManagerImpl) this.gameManager).unload();
|
||||
((GameManagerImpl) this.gameManager).load();
|
||||
((MobManagerImpl) this.mobManager).unload();
|
||||
((MobManagerImpl) this.mobManager).load();
|
||||
((CompetitionManagerImpl) this.competitionManager).unload();
|
||||
((CompetitionManagerImpl) this.competitionManager).load();
|
||||
((StorageManagerImpl) this.storageManager).reload();
|
||||
this.commandManager.loadCommands();
|
||||
}
|
||||
|
||||
private void loadDependencies() {
|
||||
String libRepo = TimeZone.getDefault().getID().startsWith("Asia") ?
|
||||
"https://maven.aliyun.com/repository/public/" : "https://repo.maven.apache.org/maven2/";
|
||||
LibraryLoader.loadDependencies(
|
||||
"org.apache.commons:commons-pool2:2.11.1", libRepo,
|
||||
"redis.clients:jedis:4.4.2", libRepo,
|
||||
"dev.dejvokep:boosted-yaml:1.3.1", libRepo,
|
||||
"com.zaxxer:HikariCP:5.0.1", libRepo,
|
||||
"net.objecthunter:exp4j:0.4.8", libRepo,
|
||||
"org.mariadb.jdbc:mariadb-java-client:3.1.4", libRepo,
|
||||
"mysql:mysql-connector-java:8.0.30", libRepo,
|
||||
"commons-io:commons-io:2.13.0", libRepo,
|
||||
"com.google.code.gson:gson:2.10.1", libRepo,
|
||||
"com.h2database:h2:2.2.220", libRepo,
|
||||
"org.mongodb:mongodb-driver-sync:4.10.2", libRepo,
|
||||
"org.xerial:sqlite-jdbc:3.42.0.0", libRepo,
|
||||
"dev.jorel:commandapi-bukkit-shade:9.1.0", "https://repo.maven.apache.org/maven2/"
|
||||
);
|
||||
}
|
||||
|
||||
private void disableNBTAPILogs() {
|
||||
MinecraftVersion.disableBStats();
|
||||
MinecraftVersion.disableUpdateCheck();
|
||||
VersionChecker.hideOk = true;
|
||||
try {
|
||||
Field field = MinecraftVersion.class.getDeclaredField("version");
|
||||
field.setAccessible(true);
|
||||
MinecraftVersion minecraftVersion;
|
||||
try {
|
||||
minecraftVersion = MinecraftVersion.valueOf(getVersionManager().getServerVersion().replace("v", "MC"));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
minecraftVersion = MinecraftVersion.UNKNOWN;
|
||||
}
|
||||
field.set(MinecraftVersion.class, minecraftVersion);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
boolean hasGsonSupport;
|
||||
try {
|
||||
Class.forName("com.google.gson.Gson");
|
||||
hasGsonSupport = true;
|
||||
} catch (Exception ex) {
|
||||
hasGsonSupport = false;
|
||||
}
|
||||
try {
|
||||
Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport");
|
||||
field.setAccessible(true);
|
||||
field.set(Boolean.class, hasGsonSupport);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public YamlConfiguration getConfig(String file) {
|
||||
File config = new File(this.getDataFolder(), file);
|
||||
if (!config.exists()) this.saveResource(file, false);
|
||||
return YamlConfiguration.loadConfiguration(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHookedPluginEnabled(String plugin) {
|
||||
return Bukkit.getPluginManager().isPluginEnabled(plugin);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ProtocolManager getProtocolManager() {
|
||||
return protocolManager;
|
||||
}
|
||||
|
||||
public void debug(String message) {
|
||||
if (!Config.debug) return;
|
||||
LogUtils.info(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package net.momirealms.customfishing.adventure;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.AdventureManager;
|
||||
import net.momirealms.customfishing.api.util.ReflectionUtils;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.Duration;
|
||||
|
||||
public class AdventureManagerImpl implements AdventureManager {
|
||||
|
||||
private final BukkitAudiences adventure;
|
||||
private static AdventureManager instance;
|
||||
|
||||
public AdventureManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.adventure = BukkitAudiences.create(plugin);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static AdventureManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (adventure != null)
|
||||
adventure.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponentFromMiniMessage(String text) {
|
||||
return MiniMessage.miniMessage().deserialize(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(CommandSender sender, String s) {
|
||||
if (s == null) return;
|
||||
if (sender instanceof Player player) sendPlayerMessage(player, s);
|
||||
else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageWithPrefix(CommandSender sender, String s) {
|
||||
if (s == null) return;
|
||||
if (sender instanceof Player player) sendPlayerMessage(player, Locale.MSG_Prefix + s);
|
||||
else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(Locale.MSG_Prefix + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendConsoleMessage(String s) {
|
||||
if (s == null) return;
|
||||
Audience au = adventure.sender(Bukkit.getConsoleSender());
|
||||
au.sendMessage(getComponentFromMiniMessage(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerMessage(Player player, String s) {
|
||||
if (s == null) return;
|
||||
Audience au = adventure.player(player);
|
||||
au.sendMessage(getComponentFromMiniMessage(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTitle(Player player, String title, String subtitle, int in, int duration, int out) {
|
||||
Audience au = adventure.player(player);
|
||||
Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out));
|
||||
au.showTitle(Title.title(getComponentFromMiniMessage(title), getComponentFromMiniMessage(subtitle), times));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTitle(Player player, Component title, Component subtitle, int in, int duration, int out) {
|
||||
Audience au = adventure.player(player);
|
||||
Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out));
|
||||
au.showTitle(Title.title(title, subtitle, times));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendActionbar(Player player, String s) {
|
||||
Audience au = adventure.player(player);
|
||||
au.sendActionBar(getComponentFromMiniMessage(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSound(Player player, Sound.Source source, Key key, float volume, float pitch) {
|
||||
Sound sound = Sound.sound(key, source, volume, pitch);
|
||||
Audience au = adventure.player(player);
|
||||
au.playSound(sound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSound(Player player, Sound sound) {
|
||||
Audience au = adventure.player(player);
|
||||
au.playSound(sound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String legacyToMiniMessage(String legacy) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
char[] chars = legacy.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (!isColorCode(chars[i])) {
|
||||
stringBuilder.append(chars[i]);
|
||||
continue;
|
||||
}
|
||||
if (i + 1 >= chars.length) {
|
||||
stringBuilder.append(chars[i]);
|
||||
continue;
|
||||
}
|
||||
switch (chars[i+1]) {
|
||||
case '0' -> stringBuilder.append("<black>");
|
||||
case '1' -> stringBuilder.append("<dark_blue>");
|
||||
case '2' -> stringBuilder.append("<dark_green>");
|
||||
case '3' -> stringBuilder.append("<dark_aqua>");
|
||||
case '4' -> stringBuilder.append("<dark_red>");
|
||||
case '5' -> stringBuilder.append("<dark_purple>");
|
||||
case '6' -> stringBuilder.append("<gold>");
|
||||
case '7' -> stringBuilder.append("<gray>");
|
||||
case '8' -> stringBuilder.append("<dark_gray>");
|
||||
case '9' -> stringBuilder.append("<blue>");
|
||||
case 'a' -> stringBuilder.append("<green>");
|
||||
case 'b' -> stringBuilder.append("<aqua>");
|
||||
case 'c' -> stringBuilder.append("<red>");
|
||||
case 'd' -> stringBuilder.append("<light_purple>");
|
||||
case 'e' -> stringBuilder.append("<yellow>");
|
||||
case 'f' -> stringBuilder.append("<white>");
|
||||
case 'r' -> stringBuilder.append("<r><!i>");
|
||||
case 'l' -> stringBuilder.append("<b>");
|
||||
case 'm' -> stringBuilder.append("<s>");
|
||||
case 'o' -> stringBuilder.append("<i>");
|
||||
case 'n' -> stringBuilder.append("<u>");
|
||||
case 'k' -> stringBuilder.append("<o>");
|
||||
case 'x' -> {
|
||||
if (i + 13 >= chars.length
|
||||
|| !isColorCode(chars[i+2])
|
||||
|| !isColorCode(chars[i+4])
|
||||
|| !isColorCode(chars[i+6])
|
||||
|| !isColorCode(chars[i+8])
|
||||
|| !isColorCode(chars[i+10])
|
||||
|| !isColorCode(chars[i+12])) {
|
||||
stringBuilder.append(chars[i]);
|
||||
continue;
|
||||
}
|
||||
stringBuilder
|
||||
.append("<#")
|
||||
.append(chars[i+3])
|
||||
.append(chars[i+5])
|
||||
.append(chars[i+7])
|
||||
.append(chars[i+9])
|
||||
.append(chars[i+11])
|
||||
.append(chars[i+13])
|
||||
.append(">");
|
||||
i += 13;
|
||||
}
|
||||
default -> {
|
||||
stringBuilder.append(chars[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isColorCode(char c) {
|
||||
return c == '§' || c == '&';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String componentToLegacy(Component component) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String componentToJson(Component component) {
|
||||
return GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object shadedComponentToPaperComponent(Component component) {
|
||||
Object cp;
|
||||
try {
|
||||
cp = ReflectionUtils.gsonDeserializeMethod.invoke(ReflectionUtils.gsonInstance, GsonComponentSerializer.gson().serialize(component));
|
||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.momirealms.customfishing.command;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPI;
|
||||
import dev.jorel.commandapi.CommandAPIBukkitConfig;
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import dev.jorel.commandapi.CommandPermission;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.CommandManager;
|
||||
import net.momirealms.customfishing.command.sub.CompetitionCommand;
|
||||
import net.momirealms.customfishing.command.sub.FishingBagCommand;
|
||||
import net.momirealms.customfishing.command.sub.ItemCommand;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
|
||||
public class CommandManagerImpl implements CommandManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
|
||||
public CommandManagerImpl(CustomFishingPluginImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommands() {
|
||||
new CommandAPICommand("customfishing")
|
||||
.withAliases("cfishing")
|
||||
.withPermission(CommandPermission.OP)
|
||||
.withSubcommands(
|
||||
getReloadCommand(),
|
||||
CompetitionCommand.INSTANCE.getCompetitionCommand(),
|
||||
ItemCommand.INSTANCE.getItemCommand()
|
||||
)
|
||||
.register();
|
||||
|
||||
if (CustomFishingPlugin.get().getBagManager().isBagEnabled()) {
|
||||
FishingBagCommand.INSTANCE.getBagCommand().register();
|
||||
}
|
||||
}
|
||||
|
||||
private CommandAPICommand getReloadCommand() {
|
||||
return new CommandAPICommand("reload")
|
||||
.withPermission("customfishing.command.reload")
|
||||
.executes((sender, args) -> {
|
||||
long time = System.currentTimeMillis();
|
||||
CustomFishingPlugin.get().reload();
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Reload.replace("{time}", String.valueOf(System.currentTimeMillis()-time)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package net.momirealms.customfishing.command.sub;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import dev.jorel.commandapi.arguments.ArgumentSuggestions;
|
||||
import dev.jorel.commandapi.arguments.StringArgument;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class CompetitionCommand {
|
||||
|
||||
public static CompetitionCommand INSTANCE = new CompetitionCommand();
|
||||
|
||||
public CommandAPICommand getCompetitionCommand() {
|
||||
return new CommandAPICommand("competition")
|
||||
.withPermission("customfishing.command.competition")
|
||||
.withSubcommands(
|
||||
getCompetitionStartCommand(),
|
||||
getCompetitionEndCommand(),
|
||||
getCompetitionStopCommand()
|
||||
);
|
||||
}
|
||||
|
||||
private CommandAPICommand getCompetitionStartCommand() {
|
||||
Set<String> allCompetitions = CustomFishingPlugin.get().getCompetitionManager().getAllCompetitions();
|
||||
var command = new CommandAPICommand("start")
|
||||
.withArguments(
|
||||
new StringArgument("id")
|
||||
.replaceSuggestions(
|
||||
ArgumentSuggestions.strings(allCompetitions)
|
||||
)
|
||||
);
|
||||
if (Config.redisRanking) command.withOptionalArguments(new StringArgument("-allservers"));
|
||||
command.executes((sender, args) -> {
|
||||
String id = (String) args.get(0);
|
||||
if (!allCompetitions.contains(id)) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Competition_Not_Exist.replace("{id}", id));
|
||||
return;
|
||||
}
|
||||
boolean allServer = args.getOrDefault(1, "").equals("-allservers");
|
||||
CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, allServer);
|
||||
});
|
||||
return command;
|
||||
}
|
||||
|
||||
private CommandAPICommand getCompetitionEndCommand() {
|
||||
var command = new CommandAPICommand("end");
|
||||
if (Config.redisRanking) command.withOptionalArguments(new StringArgument("-allservers"));
|
||||
command.executes((sender, args) -> {
|
||||
boolean allServer = args.getOrDefault(1, "").equals("-allservers");
|
||||
if (allServer) {
|
||||
RedisManager.getInstance().sendRedisMessage("cf_competition", "end");
|
||||
} else {
|
||||
FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition();
|
||||
if (competition != null) {
|
||||
competition.end();
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_End_Competition);
|
||||
} else {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_No_Competition_Ongoing);
|
||||
}
|
||||
}
|
||||
});
|
||||
return command;
|
||||
}
|
||||
|
||||
private CommandAPICommand getCompetitionStopCommand() {
|
||||
var command = new CommandAPICommand("stop");
|
||||
if (Config.redisRanking) command.withOptionalArguments(new StringArgument("-allservers"));
|
||||
command.executes((sender, args) -> {
|
||||
boolean allServer = args.getOrDefault(1, "").equals("-allservers");
|
||||
if (allServer) {
|
||||
RedisManager.getInstance().sendRedisMessage("cf_competition", "stop");
|
||||
} else {
|
||||
FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition();
|
||||
if (competition != null) {
|
||||
competition.stop();
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Stop_Competition);
|
||||
} else {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_No_Competition_Ongoing);
|
||||
}
|
||||
}
|
||||
});
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.momirealms.customfishing.command.sub;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import dev.jorel.commandapi.arguments.OfflinePlayerArgument;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.user.OfflineUser;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import net.momirealms.customfishing.storage.user.OfflineUserImpl;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FishingBagCommand {
|
||||
|
||||
public static FishingBagCommand INSTANCE = new FishingBagCommand();
|
||||
|
||||
public CommandAPICommand getBagCommand() {
|
||||
return new CommandAPICommand("fishingbag")
|
||||
.withPermission("fishingbag.user")
|
||||
.withSubcommand(getAdminCommand())
|
||||
.executesPlayer(((player, args) -> {
|
||||
var inv = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(player.getUniqueId());
|
||||
if (inv != null) player.openInventory(inv);
|
||||
}));
|
||||
}
|
||||
|
||||
private CommandAPICommand getAdminCommand() {
|
||||
return new CommandAPICommand("edit")
|
||||
.withPermission("fishingbag.admin")
|
||||
.withArguments(new OfflinePlayerArgument("player"))
|
||||
.executesPlayer(((player, args) -> {
|
||||
OfflinePlayer offlinePlayer = (OfflinePlayer) args.get("player");
|
||||
UUID uuid = offlinePlayer.getUniqueId();
|
||||
Inventory onlineInv = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(uuid);
|
||||
if (onlineInv != null) {
|
||||
player.openInventory(onlineInv);
|
||||
} else {
|
||||
CustomFishingPlugin.get().getStorageManager().getOfflineUser(uuid, false).thenAccept(optional -> {
|
||||
if (optional.isEmpty()) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, Locale.MSG_Unsafe_Modification);
|
||||
} else {
|
||||
OfflineUser offlineUser = optional.get();
|
||||
if (offlineUser == OfflineUserImpl.NEVER_PLAYED_USER) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, Locale.MSG_Never_Played);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package net.momirealms.customfishing.command.sub;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import dev.jorel.commandapi.arguments.ArgumentSuggestions;
|
||||
import dev.jorel.commandapi.arguments.EntitySelectorArgument;
|
||||
import dev.jorel.commandapi.arguments.IntegerArgument;
|
||||
import dev.jorel.commandapi.arguments.TextArgument;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Key;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ItemCommand {
|
||||
|
||||
public static ItemCommand INSTANCE = new ItemCommand();
|
||||
|
||||
public CommandAPICommand getItemCommand() {
|
||||
return new CommandAPICommand("items")
|
||||
.withPermission("customfishing.command.items")
|
||||
.withSubcommands(
|
||||
getSubCommand("loot"),
|
||||
getSubCommand("util"),
|
||||
getSubCommand("bait"),
|
||||
getSubCommand("rod")
|
||||
);
|
||||
}
|
||||
|
||||
private CommandAPICommand getSubCommand(String namespace) {
|
||||
Collection<String> items = CustomFishingPlugin.get()
|
||||
.getItemManager()
|
||||
.getAllItemsKey()
|
||||
.stream()
|
||||
.filter(it -> it.namespace().equals(namespace))
|
||||
.map(Key::value)
|
||||
.toList();
|
||||
return new CommandAPICommand(namespace)
|
||||
.withSubcommands(
|
||||
getCommand(namespace, items),
|
||||
giveCommand(namespace, items)
|
||||
);
|
||||
}
|
||||
|
||||
private CommandAPICommand getCommand(String namespace, Collection<String> items) {
|
||||
return new CommandAPICommand("get")
|
||||
.withArguments(new TextArgument("id").replaceSuggestions(ArgumentSuggestions.strings(items)))
|
||||
.withOptionalArguments(new IntegerArgument("amount", 1))
|
||||
.executesPlayer((player, args) -> {
|
||||
String id = (String) args.get("id");
|
||||
int amount = (int) args.getOrDefault("amount", 1);
|
||||
ItemStack item = CustomFishingPlugin.get().getItemManager().build(player, namespace, id);
|
||||
if (item != null) {
|
||||
int actual = giveCertainAmountOfItem(player, item, amount);
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, Locale.MSG_Get_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual)));
|
||||
} else {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, Locale.MSG_Item_Not_Exists);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CommandAPICommand giveCommand(String namespace, Collection<String> items) {
|
||||
return new CommandAPICommand("give")
|
||||
.withArguments(new EntitySelectorArgument.ManyPlayers("player"))
|
||||
.withArguments(new TextArgument("id").replaceSuggestions(ArgumentSuggestions.strings(items)))
|
||||
.withOptionalArguments(new IntegerArgument("amount", 1))
|
||||
.executes((sender, args) -> {
|
||||
Collection<Player> players = (Collection<Player>) args.get("player");
|
||||
String id = (String) args.get("id");
|
||||
int amount = (int) args.getOrDefault("amount", 1);
|
||||
ItemStack item = CustomFishingPlugin.get().getItemManager().build(players.stream().findAny().get(), namespace, id);
|
||||
if (item != null) {
|
||||
for (Player player : players) {
|
||||
int actual = giveCertainAmountOfItem(player, item, amount);
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Give_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual)).replace("{player}", player.getName()));
|
||||
}
|
||||
} else {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Item_Not_Exists);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int giveCertainAmountOfItem(Player player, ItemStack itemStack, int amount) {
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
String metaStr = itemStack.getItemMeta().getAsString();
|
||||
int maxStackSize = itemStack.getMaxStackSize();
|
||||
|
||||
if (amount > maxStackSize * 100) {
|
||||
LogUtils.warn("Detected too many items spawning. Lowering the amount to " + (maxStackSize * 100));
|
||||
amount = maxStackSize * 100;
|
||||
}
|
||||
|
||||
int actualAmount = amount;
|
||||
|
||||
for (ItemStack other : inventory.getStorageContents()) {
|
||||
if (other != null) {
|
||||
if (other.getType() == itemStack.getType() && other.getItemMeta().getAsString().equals(metaStr)) {
|
||||
if (other.getAmount() < maxStackSize) {
|
||||
int delta = maxStackSize - other.getAmount();
|
||||
if (amount > delta) {
|
||||
other.setAmount(maxStackSize);
|
||||
amount -= delta;
|
||||
} else {
|
||||
other.setAmount(amount + other.getAmount());
|
||||
return actualAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (amount > 0) {
|
||||
for (ItemStack other : inventory.getStorageContents()) {
|
||||
if (other == null) {
|
||||
if (amount > maxStackSize) {
|
||||
amount -= maxStackSize;
|
||||
ItemStack cloned = itemStack.clone();
|
||||
cloned.setAmount(maxStackSize);
|
||||
inventory.addItem(cloned);
|
||||
} else {
|
||||
ItemStack cloned = itemStack.clone();
|
||||
cloned.setAmount(amount);
|
||||
inventory.addItem(cloned);
|
||||
return actualAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (amount > 0) {
|
||||
for (int i = 0; i < amount / maxStackSize; i++) {
|
||||
ItemStack cloned = itemStack.clone();
|
||||
cloned.setAmount(maxStackSize);
|
||||
player.getWorld().dropItem(player.getLocation(), cloned);
|
||||
}
|
||||
int left = amount % maxStackSize;
|
||||
if (left != 0) {
|
||||
ItemStack cloned = itemStack.clone();
|
||||
cloned.setAmount(left);
|
||||
player.getWorld().dropItem(player.getLocation(), cloned);
|
||||
}
|
||||
}
|
||||
|
||||
return actualAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package net.momirealms.customfishing.compatibility;
|
||||
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.integration.EnchantmentInterface;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import net.momirealms.customfishing.api.integration.SeasonInterface;
|
||||
import net.momirealms.customfishing.api.manager.IntegrationManager;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.enchant.AdvancedEnchantmentsImpl;
|
||||
import net.momirealms.customfishing.compatibility.enchant.VanillaEnchantmentsImpl;
|
||||
import net.momirealms.customfishing.compatibility.item.*;
|
||||
import net.momirealms.customfishing.compatibility.level.*;
|
||||
import net.momirealms.customfishing.compatibility.season.CustomCropsSeasonImpl;
|
||||
import net.momirealms.customfishing.compatibility.season.RealisticSeasonsImpl;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class IntegrationManagerImpl implements IntegrationManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, LevelInterface> levelPluginMap;
|
||||
private final HashMap<String, EnchantmentInterface> enchantments;
|
||||
private SeasonInterface seasonInterface;
|
||||
|
||||
public IntegrationManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.levelPluginMap = new HashMap<>();
|
||||
this.enchantments = new HashMap<>();
|
||||
this.init();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.enchantments.clear();
|
||||
this.levelPluginMap.clear();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (plugin.isHookedPluginEnabled("ItemsAdder")) {
|
||||
plugin.getItemManager().registerItemLibrary(new ItemsAdderItemImpl());
|
||||
hookMessage("ItemsAdder");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("MMOItems")) {
|
||||
plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl());
|
||||
hookMessage("MMOItems");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("Oraxen")) {
|
||||
plugin.getItemManager().registerItemLibrary(new OraxenItemImpl());
|
||||
hookMessage("Oraxen");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("NeigeItems")) {
|
||||
plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl());
|
||||
hookMessage("NeigeItems");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("MythicMobs")) {
|
||||
plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl());
|
||||
hookMessage("MythicMobs");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("EcoJobs")) {
|
||||
registerLevelPlugin("EcoJobs", new EcoJobsImpl());
|
||||
hookMessage("EcoJobs");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("EcoSkills")) {
|
||||
registerLevelPlugin("EcoSkills", new EcoSkillsImpl());
|
||||
hookMessage("EcoSkills");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("Jobs")) {
|
||||
registerLevelPlugin("JobsReborn", new JobsRebornImpl());
|
||||
hookMessage("Jobs");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("MMOCore")) {
|
||||
registerLevelPlugin("MMOCore", new MMOCoreImpl());
|
||||
hookMessage("MMOCore");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("mcMMO")) {
|
||||
try {
|
||||
plugin.getItemManager().registerCustomItem("loot", "mcmmo", new McMMOBuildableItem());
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
LogUtils.warn("Failed to initialize mcMMO Treasure");
|
||||
}
|
||||
registerLevelPlugin("mcMMO", new McMMOImpl());
|
||||
hookMessage("mcMMO");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("AureliumSkills")) {
|
||||
registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl());
|
||||
hookMessage("AureliumSkills");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("EcoEnchants")) {
|
||||
this.enchantments.put("EcoEnchants", new VanillaEnchantmentsImpl());
|
||||
hookMessage("EcoEnchants");
|
||||
} else {
|
||||
this.enchantments.put("vanilla", new VanillaEnchantmentsImpl());
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("AdvancedEnchantments")) {
|
||||
this.enchantments.put("AdvancedEnchantments", new AdvancedEnchantmentsImpl());
|
||||
hookMessage("AdvancedEnchantments");
|
||||
}
|
||||
if (plugin.isHookedPluginEnabled("RealisticSeasons")) {
|
||||
this.seasonInterface = new RealisticSeasonsImpl();
|
||||
} else if (plugin.isHookedPluginEnabled("CustomCrops")) {
|
||||
this.seasonInterface = new CustomCropsSeasonImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerLevelPlugin(String plugin, LevelInterface level) {
|
||||
if (levelPluginMap.containsKey(plugin)) return false;
|
||||
levelPluginMap.put(plugin, level);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterLevelPlugin(String plugin) {
|
||||
return levelPluginMap.remove(plugin) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerEnchantment(String plugin, EnchantmentInterface enchantment) {
|
||||
if (enchantments.containsKey(plugin)) return false;
|
||||
enchantments.put(plugin, enchantment);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterEnchantment(String plugin) {
|
||||
return enchantments.remove(plugin) != null;
|
||||
}
|
||||
|
||||
private void hookMessage(String plugin) {
|
||||
AdventureManagerImpl.getInstance().sendConsoleMessage("[CustomFishing] <green>" + plugin + "</green> hooked!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelInterface getLevelHook(String plugin) {
|
||||
return levelPluginMap.get(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getEnchantments(ItemStack itemStack) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
for (EnchantmentInterface enchantmentInterface : enchantments.values()) {
|
||||
list.addAll(enchantmentInterface.getEnchants(itemStack));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SeasonInterface getSeasonInterface() {
|
||||
return seasonInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeasonInterface(SeasonInterface season) {
|
||||
this.seasonInterface = season;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.momirealms.customfishing.compatibility.block;
|
||||
|
||||
import dev.lone.itemsadder.api.CustomBlock;
|
||||
import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier;
|
||||
import net.momirealms.customfishing.api.mechanic.block.BlockLibrary;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemsAdderBlockImpl implements BlockLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "ItemsAdder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData getBlockData(Player player, String id, List<BlockDataModifier> modifiers) {
|
||||
BlockData blockData = CustomBlock.getBaseBlockData(id);
|
||||
for (BlockDataModifier modifier : modifiers) {
|
||||
modifier.apply(player, blockData);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.momirealms.customfishing.compatibility.block;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier;
|
||||
import net.momirealms.customfishing.api.mechanic.block.BlockLibrary;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class VanillaBlockImpl implements BlockLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "vanilla";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData getBlockData(Player player, String id, List<BlockDataModifier> modifiers) {
|
||||
BlockData blockData = Material.valueOf(id.toUpperCase(Locale.ENGLISH)).createBlockData();
|
||||
for (BlockDataModifier modifier : modifiers) {
|
||||
modifier.apply(player, blockData);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.compatibility.enchant;
|
||||
|
||||
import net.advancedplugins.ae.api.AEAPI;
|
||||
import net.momirealms.customfishing.api.integration.EnchantmentInterface;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AdvancedEnchantmentsImpl implements EnchantmentInterface {
|
||||
|
||||
@Override
|
||||
public List<String> getEnchants(ItemStack itemStack) {
|
||||
List<String> enchants = new ArrayList<>();
|
||||
for (Map.Entry<String, Integer> entry : AEAPI.getEnchantmentsOnItem(itemStack).entrySet()) {
|
||||
enchants.add("AE:" + entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
return enchants;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.compatibility.enchant;
|
||||
|
||||
import net.momirealms.customfishing.api.integration.EnchantmentInterface;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class VanillaEnchantmentsImpl implements EnchantmentInterface {
|
||||
|
||||
@Override
|
||||
public List<String> getEnchants(ItemStack itemStack) {
|
||||
Map<Enchantment, Integer> enchantments = itemStack.getEnchantments();
|
||||
List<String> enchants = new ArrayList<>(enchantments.size());
|
||||
for (Map.Entry<Enchantment, Integer> en : enchantments.entrySet()) {
|
||||
String key = en.getKey().getKey() + ":" + en.getValue();
|
||||
enchants.add(key);
|
||||
}
|
||||
return enchants;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class CustomFishingItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "CustomFishing";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
String[] split = id.split(":", 2);
|
||||
return CustomFishingPlugin.get().getItemManager().build(player, split[0], split[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
return CustomFishingPlugin.get().getItemManager().getItemID(itemStack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import dev.lone.itemsadder.api.CustomStack;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class ItemsAdderItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "ItemsAdder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
return CustomStack.getInstance(id).getItemStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
CustomStack customStack = CustomStack.byItemStack(itemStack);
|
||||
if (customStack == null) return null;
|
||||
return customStack.getNamespacedID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.Type;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class MMOItemsItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "MMOItems";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
String[] split = id.split(":");
|
||||
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH));
|
||||
return mmoItem == null ? new ItemStack(Material.AIR) : mmoItem.newBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null;
|
||||
return nbtItem.getString("MMOITEMS_ITEM_ID");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.momirealms.customfishing.compatibility.item;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.item.BuildableItem;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class McMMOBuildableItem implements BuildableItem {
|
||||
|
||||
private final Method getMcMMOPlayerMethod;
|
||||
private final Method getFishingManagerMethod;
|
||||
private final Method getFishingTreasureMethod;
|
||||
private final Method getItemStackMethod;
|
||||
|
||||
public McMMOBuildableItem() throws ClassNotFoundException, NoSuchMethodException {
|
||||
Class<?> userClass = Class.forName("com.gmail.nossr50.util.player.UserManager");
|
||||
getMcMMOPlayerMethod = userClass.getMethod("getPlayer", Player.class);
|
||||
Class<?> mcMMOPlayerClass = Class.forName("com.gmail.nossr50.datatypes.player.McMMOPlayer");
|
||||
getFishingManagerMethod = mcMMOPlayerClass.getMethod("getFishingManager");
|
||||
Class<?> fishingManagerClass = Class.forName("com.gmail.nossr50.skills.fishing.FishingManager");
|
||||
getFishingTreasureMethod = fishingManagerClass.getDeclaredMethod("getFishingTreasure");
|
||||
Class<?> treasureClass = Class.forName("com.gmail.nossr50.datatypes.treasure.Treasure");
|
||||
getItemStackMethod = treasureClass.getMethod("getDrop");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build(Player player, Map<String, String> placeholders) {
|
||||
ItemStack itemStack = null;
|
||||
while (itemStack == null) {
|
||||
try {
|
||||
Object mcMMOPlayer = getMcMMOPlayerMethod.invoke(null, player);
|
||||
Object fishingManager = getFishingManagerMethod.invoke(mcMMOPlayer);
|
||||
Object treasure = getFishingTreasureMethod.invoke(fishingManager);
|
||||
if (treasure != null) {
|
||||
itemStack = (ItemStack) getItemStackMethod.invoke(treasure);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import io.lumine.mythic.bukkit.MythicBukkit;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class MythicMobsItemImpl implements ItemLibrary {
|
||||
|
||||
private MythicBukkit mythicBukkit;
|
||||
|
||||
public MythicMobsItemImpl() {
|
||||
this.mythicBukkit = MythicBukkit.inst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "MythicMobs";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
if (mythicBukkit == null || mythicBukkit.isClosed()) {
|
||||
this.mythicBukkit = MythicBukkit.inst();
|
||||
}
|
||||
return mythicBukkit.getItemManager().getItemStack(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
return nbtItem.hasTag("MYTHIC_TYPE") ? nbtItem.getString("MYTHIC_TYPE") : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import pers.neige.neigeitems.item.ItemInfo;
|
||||
import pers.neige.neigeitems.manager.ItemManager;
|
||||
import pers.neige.neigeitems.utils.ItemUtils;
|
||||
|
||||
public class NeigeItemsItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "NeigeItems";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
return ItemManager.INSTANCE.getItemStack(id, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
ItemInfo itemInfo = ItemUtils.isNiItem(itemStack);
|
||||
if (itemInfo != null) {
|
||||
return itemInfo.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import io.th0rgal.oraxen.api.OraxenItems;
|
||||
import io.th0rgal.oraxen.items.ItemBuilder;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class OraxenItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "Oraxen";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
ItemBuilder itemBuilder = OraxenItems.getItemById(id);
|
||||
return itemBuilder == null ? new ItemStack(Material.AIR) : itemBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
return OraxenItems.getIdByItem(itemStack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.compatibility.item;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class VanillaItemImpl implements ItemLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "vanilla";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItem(Player player, String id) {
|
||||
return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
return itemStack.getType().name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import com.archyx.aureliumskills.api.AureliumAPI;
|
||||
import com.archyx.aureliumskills.leveler.Leveler;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class AureliumSkillsImpl implements LevelInterface {
|
||||
|
||||
private final Leveler leveler;
|
||||
|
||||
public AureliumSkillsImpl() {
|
||||
leveler = AureliumAPI.getPlugin().getLeveler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
leveler.addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
return AureliumAPI.getSkillLevel(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import com.willfp.ecojobs.api.EcoJobsAPI;
|
||||
import com.willfp.ecojobs.jobs.Job;
|
||||
import com.willfp.ecojobs.jobs.Jobs;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class EcoJobsImpl implements LevelInterface {
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
for (Job job : EcoJobsAPI.getActiveJobs(player)) {
|
||||
if (job.getId().equals(target)) {
|
||||
EcoJobsAPI.giveJobExperience(player, job, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
Job job = Jobs.getByID(target);
|
||||
if (job == null) return 0;
|
||||
return EcoJobsAPI.getJobLevel(player, job);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import com.willfp.ecoskills.api.EcoSkillsAPI;
|
||||
import com.willfp.ecoskills.skills.Skills;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class EcoSkillsImpl implements LevelInterface {
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
EcoSkillsAPI.giveSkillXP(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
return EcoSkillsAPI.getSkillLevel(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import com.gamingmesh.jobs.Jobs;
|
||||
import com.gamingmesh.jobs.container.Job;
|
||||
import com.gamingmesh.jobs.container.JobProgression;
|
||||
import com.gamingmesh.jobs.container.JobsPlayer;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JobsRebornImpl implements LevelInterface {
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player);
|
||||
if (jobsPlayer != null) {
|
||||
List<JobProgression> jobs = jobsPlayer.getJobProgression();
|
||||
Job job = Jobs.getJob(target);
|
||||
for (JobProgression progression : jobs)
|
||||
if (progression.getJob().equals(job))
|
||||
progression.addExperience(amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player);
|
||||
if (jobsPlayer != null) {
|
||||
List<JobProgression> jobs = jobsPlayer.getJobProgression();
|
||||
Job job = Jobs.getJob(target);
|
||||
for (JobProgression progression : jobs)
|
||||
if (progression.getJob().equals(job))
|
||||
return progression.getLevel();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.experience.EXPSource;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class MMOCoreImpl implements LevelInterface {
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player), amount, null ,EXPSource.OTHER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
return PlayerData.get(player).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.compatibility.level;
|
||||
|
||||
import com.gmail.nossr50.api.ExperienceAPI;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class McMMOImpl implements LevelInterface {
|
||||
|
||||
@Override
|
||||
public void addXp(Player player, String target, double amount) {
|
||||
ExperienceAPI.addRawXP(player, target, (float) amount, "UNKNOWN");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel(Player player, String target) {
|
||||
return ExperienceAPI.getLevel(player, PrimarySkillType.valueOf(target));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package net.momirealms.customfishing.compatibility.mob;
|
||||
|
||||
import io.lumine.mythic.api.adapters.AbstractLocation;
|
||||
import io.lumine.mythic.api.mobs.MythicMob;
|
||||
import io.lumine.mythic.bukkit.MythicBukkit;
|
||||
import io.lumine.mythic.bukkit.utils.serialize.Position;
|
||||
import io.lumine.mythic.core.mobs.ActiveMob;
|
||||
import net.momirealms.customfishing.api.mechanic.mob.MobLibrary;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class MythicMobsLibraryImpl implements MobLibrary {
|
||||
|
||||
private MythicBukkit mythicBukkit;
|
||||
|
||||
public MythicMobsLibraryImpl() {
|
||||
this.mythicBukkit = MythicBukkit.inst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "MythicMobs";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity spawn(Location location, String id, Map<String, Object> mobPropertyMap) {
|
||||
if (this.mythicBukkit == null || mythicBukkit.isClosed()) {
|
||||
this.mythicBukkit = MythicBukkit.inst();
|
||||
}
|
||||
Optional<MythicMob> mythicMob = mythicBukkit.getMobManager().getMythicMob(id);
|
||||
if (mythicMob.isPresent()) {
|
||||
MythicMob theMob = mythicMob.get();
|
||||
Position position = Position.of(location);
|
||||
AbstractLocation abstractLocation = new AbstractLocation(position);
|
||||
ActiveMob activeMob = theMob.spawn(abstractLocation, (Double) mobPropertyMap.get("{level}"));
|
||||
return activeMob.getEntity().getBukkitEntity();
|
||||
}
|
||||
throw new NullPointerException("MythicMobs: " + id + " doesn't exist.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.momirealms.customfishing.compatibility.mob;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.mob.MobLibrary;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class VanillaMobImpl implements MobLibrary {
|
||||
|
||||
@Override
|
||||
public String identification() {
|
||||
return "vanilla";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity spawn(Location location, String id, Map<String, Object> mobPropertyMap) {
|
||||
return location.getWorld().spawnEntity(location, EntityType.valueOf(id.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.momirealms.customfishing.compatibility.papi;
|
||||
|
||||
public class MiniPlaceholdersHook {
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.compatibility.papi;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ParseUtils {
|
||||
|
||||
public static String setPlaceholders(Player player, String text) {
|
||||
return PlaceholderAPI.setPlaceholders(player, text);
|
||||
}
|
||||
|
||||
public static String setPlaceholders(OfflinePlayer player, String text) {
|
||||
return PlaceholderAPI.setPlaceholders(player, text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package net.momirealms.customfishing.compatibility.papi;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlaceholderAPIHook extends PlaceholderExpansion {
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier() {
|
||||
return "customfishing";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor() {
|
||||
return "XiaoMoMi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
return "1.4";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) {
|
||||
return super.onRequest(player, params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package net.momirealms.customfishing.compatibility.papi;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.PlaceholderManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlaceholderManagerImpl implements PlaceholderManager {
|
||||
|
||||
private static PlaceholderManagerImpl instance;
|
||||
private CustomFishingPlugin plugin;
|
||||
private final boolean hasPapi;
|
||||
private final Pattern pattern;
|
||||
private final HashMap<String, String> customPlaceholderMap;
|
||||
|
||||
public PlaceholderManagerImpl(CustomFishingPlugin plugin) {
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
|
||||
this.pattern = Pattern.compile("\\{[^{}]+}");
|
||||
this.customPlaceholderMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.customPlaceholderMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setPlaceholders(Player player, String text) {
|
||||
return hasPapi ? ParseUtils.setPlaceholders(player, text) : text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setPlaceholders(OfflinePlayer player, String text) {
|
||||
return hasPapi ? ParseUtils.setPlaceholders(player, text) : text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> detectPlaceholders(String text) {
|
||||
List<String> placeholders = new ArrayList<>();
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
while (matcher.find()) placeholders.add(matcher.group());
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSingleValue(@Nullable Player player, String placeholder, Map<String, String> placeholders) {
|
||||
String result;
|
||||
result = placeholders.get(placeholder);
|
||||
if (result != null) return result;
|
||||
String custom = customPlaceholderMap.get(placeholder);
|
||||
if (custom == null) return placeholder;
|
||||
return setPlaceholders(player, custom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(@Nullable OfflinePlayer player, String text, Map<String, String> placeholders) {
|
||||
var list = detectPlaceholders(text);
|
||||
for (String papi : list) {
|
||||
String replacer = placeholders.get(papi);
|
||||
if (replacer == null) {
|
||||
String custom = customPlaceholderMap.get(papi);
|
||||
if (custom != null) {
|
||||
replacer = setPlaceholders(player, custom);
|
||||
}
|
||||
}
|
||||
if (replacer != null) {
|
||||
text = text.replace(papi, replacer);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> parse(@Nullable OfflinePlayer player, List<String> list, Map<String, String> replacements) {
|
||||
return list.stream()
|
||||
.map(s -> parse(player, s, replacements))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static PlaceholderManagerImpl getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.compatibility.season;
|
||||
|
||||
import net.momirealms.customcrops.api.CustomCropsAPI;
|
||||
import net.momirealms.customfishing.api.integration.SeasonInterface;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class CustomCropsSeasonImpl implements SeasonInterface {
|
||||
|
||||
private final CustomCropsAPI customCropsAPI;
|
||||
|
||||
public CustomCropsSeasonImpl() {
|
||||
customCropsAPI = CustomCropsAPI.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSeason(World world) {
|
||||
return Objects.requireNonNull(customCropsAPI.getSeason(world.getName())).getSeason();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.compatibility.season;
|
||||
|
||||
import me.casperge.realisticseasons.api.SeasonsAPI;
|
||||
import net.momirealms.customfishing.api.integration.SeasonInterface;
|
||||
import org.bukkit.World;
|
||||
|
||||
public class RealisticSeasonsImpl implements SeasonInterface {
|
||||
|
||||
@Override
|
||||
public String getSeason(World world) {
|
||||
return switch (SeasonsAPI.getInstance().getSeason(world)) {
|
||||
case WINTER -> "winter";
|
||||
case SPRING -> "spring";
|
||||
case SUMMER -> "summer";
|
||||
case FALL -> "autumn";
|
||||
case DISABLED -> "disabled";
|
||||
case RESTORE -> "restore";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents an element in a gui that will query all it's data when drawn.
|
||||
*/
|
||||
public class DynamicGuiElement extends GuiElement {
|
||||
private Function<HumanEntity, GuiElement> query;
|
||||
|
||||
private Map<UUID, CacheEntry> cachedElements = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Represents an element in a gui that will query all it's data when drawn.
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param query Query the element data, this should return an element with the information
|
||||
*/
|
||||
public DynamicGuiElement(char slotChar, Supplier<GuiElement> query) {
|
||||
this(slotChar, (h) -> query.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an element in a gui that will query all it's data when drawn.
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param query Query the element data, this should return an element with the information and handle null players properly
|
||||
*/
|
||||
public DynamicGuiElement(char slotChar, Function<HumanEntity, GuiElement> query) {
|
||||
super(slotChar);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query this element's state for every player who had it cached
|
||||
*/
|
||||
public void update() {
|
||||
for (UUID playerId : new ArrayList<>(cachedElements.keySet())) {
|
||||
Player p = gui.getPlugin().getServer().getPlayer(playerId);
|
||||
if (p != null && p.isOnline()) {
|
||||
update(p);
|
||||
} else {
|
||||
cachedElements.remove(playerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query this element's state for a certain player
|
||||
* @param player The player for whom to update the element
|
||||
*/
|
||||
public CacheEntry update(HumanEntity player) {
|
||||
CacheEntry cacheEntry = new CacheEntry(queryElement(player));
|
||||
if (cacheEntry.element instanceof DynamicGuiElement) {
|
||||
((DynamicGuiElement) cacheEntry.element).update(player);
|
||||
} else if (cacheEntry.element instanceof GuiElementGroup) {
|
||||
InventoryGui.updateElements(player, ((GuiElementGroup) cacheEntry.element).getElements());
|
||||
}
|
||||
cachedElements.put(player.getUniqueId(), cacheEntry);
|
||||
return cacheEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGui(InventoryGui gui) {
|
||||
super.setGui(gui);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
GuiElement element = getCachedElement(who);
|
||||
return element != null ? element.getItem(who, slot) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction(HumanEntity who) {
|
||||
GuiElement element = getCachedElement(who);
|
||||
return element != null ? element.getAction(who) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supplier for this element's content
|
||||
* @return The supplier query
|
||||
*/
|
||||
public Function<HumanEntity, GuiElement> getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the supplier for this element's content
|
||||
* @param query The supplier query to set
|
||||
*/
|
||||
public void setQuery(Function<HumanEntity, GuiElement> query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the element for a player
|
||||
* @param who The player
|
||||
* @return The GuiElement or null
|
||||
*/
|
||||
public GuiElement queryElement(HumanEntity who) {
|
||||
GuiElement element = getQuery().apply(who);
|
||||
if (element != null) {
|
||||
element.setGui(gui);
|
||||
element.setSlots(slots);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached element, creates a new one if there is none for that player.
|
||||
* Use {@link #getLastCached(HumanEntity)} to check if a player has something cached.
|
||||
* @param who The player to get the element for
|
||||
* @return The element that is currently cached
|
||||
*/
|
||||
public GuiElement getCachedElement(HumanEntity who) {
|
||||
CacheEntry cached = cachedElements.get(who.getUniqueId());
|
||||
if (cached == null) {
|
||||
cached = update(who);
|
||||
}
|
||||
return cached.getElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the cached element if the player has one.
|
||||
* @param who The player to remove the cached element for
|
||||
* @return The element that was cached or null if none was cached
|
||||
*/
|
||||
public GuiElement removeCachedElement(HumanEntity who) {
|
||||
CacheEntry cached = cachedElements.remove(who.getUniqueId());
|
||||
return cached != null ? cached.getElement() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which this element was last cached for a certain player
|
||||
* @param who The player to get the last cache time for
|
||||
* @return The timestamp from when it was last cached or -1 if it wasn't cached
|
||||
*/
|
||||
public long getLastCached(HumanEntity who) {
|
||||
CacheEntry cached = cachedElements.get(who.getUniqueId());
|
||||
return cached != null ? cached.getCreated() : -1;
|
||||
}
|
||||
|
||||
public class CacheEntry {
|
||||
private final GuiElement element;
|
||||
private final long created = System.currentTimeMillis();
|
||||
|
||||
CacheEntry(GuiElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
public GuiElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public long getCreated() {
|
||||
return created;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* An element that will not appear if there is no previous history,
|
||||
* but will go back one step if there is
|
||||
*/
|
||||
public class GuiBackElement extends StaticGuiElement {
|
||||
|
||||
private boolean close;
|
||||
|
||||
/**
|
||||
* An element used to go back in history of the gui if there is something to go back to.
|
||||
* Will not display when there is nothing to go back to.
|
||||
*
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The {@link ItemStack} representing this element
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public GuiBackElement(char slotChar, ItemStack item, String... text) {
|
||||
this(slotChar, item, false, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element used to go back in history of the gui
|
||||
*
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The {@link ItemStack} representing this element
|
||||
* @param close Whether to close the GUI if there is nothing to go back to.
|
||||
* Will not display item if set to false and nothing to go back to.
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public GuiBackElement(char slotChar, ItemStack item, boolean close, String... text) {
|
||||
super(slotChar, item, text);
|
||||
this.close = close;
|
||||
|
||||
setAction(click -> {
|
||||
if (canGoBack(click.getWhoClicked())) {
|
||||
InventoryGui.goBack(click.getWhoClicked());
|
||||
} else if (close) {
|
||||
click.getGui().close();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
if (!canGoBack(who) && !close) {
|
||||
return gui.getFiller() != null ? gui.getFiller().getItem(who, slot) : null;
|
||||
}
|
||||
|
||||
return super.getItem(who, slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this element can close the GUI when nothing to go back to
|
||||
* @return Close the GUI when nothing to go back
|
||||
*/
|
||||
public boolean canClose() {
|
||||
return close;
|
||||
}
|
||||
|
||||
private boolean canGoBack(HumanEntity who) {
|
||||
return InventoryGui.getHistory(who).size() > 1 || (InventoryGui.getHistory(who).size() == 1 && InventoryGui.getHistory(who).peekLast() != gui);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Represents an element in a gui
|
||||
*/
|
||||
public abstract class GuiElement {
|
||||
private final char slotChar;
|
||||
private Action action;
|
||||
protected int[] slots = new int[0];
|
||||
protected InventoryGui gui;
|
||||
|
||||
/**
|
||||
* Represents an element in a gui
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param action The action to run when the player clicks on this element
|
||||
*/
|
||||
public GuiElement(char slotChar, Action action) {
|
||||
this.slotChar = slotChar;
|
||||
setAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an element in a gui that doesn't have any action when clicked
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
*/
|
||||
public GuiElement(char slotChar) {
|
||||
this(slotChar, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the character in the gui setup that corresponds with this element
|
||||
* @return The character
|
||||
*/
|
||||
public char getSlotChar() {
|
||||
return slotChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item that is displayed by this element on a certain page
|
||||
* @param who The player who views the page
|
||||
* @param slot The slot to get the item for
|
||||
* @return The ItemStack that is displayed as this element
|
||||
*/
|
||||
public abstract ItemStack getItem(HumanEntity who, int slot);
|
||||
|
||||
/**
|
||||
* Get the action that is executed when clicking on this element
|
||||
* @param who The player who views the page
|
||||
* @return The action to run
|
||||
*/
|
||||
public Action getAction(HumanEntity who) {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action that is executed when clicking on this element
|
||||
* @param action The action to run. The {@link Action#onClick} method should
|
||||
* return whether or not the click event should be cancelled
|
||||
*/
|
||||
public void setAction(Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the indexes of the lots that this element is displayed in
|
||||
* @return An array of the lost indexes
|
||||
*/
|
||||
public int[] getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ids of the slots where this element is assigned to
|
||||
* @param slots An array of the slot ids where this element is displayed
|
||||
*/
|
||||
public void setSlots(int[] slots) {
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index that this slot has in the list of slots that this element is displayed in
|
||||
* @param slot The id of the slot
|
||||
* @return The index in the list of slots that this id has or <code>-1</code> if it isn't in that list
|
||||
*/
|
||||
public int getSlotIndex(int slot) {
|
||||
return getSlotIndex(slot, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index that this slot has in the list of slots that this element is displayed in
|
||||
* @param slot The id of the slot
|
||||
* @param pageNumber The number of the page that the gui is on
|
||||
* @return The index in the list of slots that this id has or <code>-1</code> if it isn't in that list
|
||||
*/
|
||||
public int getSlotIndex(int slot, int pageNumber) {
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
if (slots[i] == slot) {
|
||||
return i + slots.length * pageNumber;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the gui this element belongs to
|
||||
* @param gui The GUI that this element is in
|
||||
*/
|
||||
public void setGui(InventoryGui gui) {
|
||||
this.gui = gui;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gui this element belongs to
|
||||
* @return The GUI that this element is in
|
||||
*/
|
||||
public InventoryGui getGui() {
|
||||
return gui;
|
||||
}
|
||||
|
||||
public static interface Action {
|
||||
|
||||
/**
|
||||
* Executed when a player clicks on an element
|
||||
* @param click The Click class containing information about the click
|
||||
* @return Whether or not the click event should be cancelled
|
||||
*/
|
||||
boolean onClick(Click click);
|
||||
|
||||
}
|
||||
|
||||
public static class Click {
|
||||
private final InventoryGui gui;
|
||||
private final int slot;
|
||||
private final ClickType clickType;
|
||||
private ItemStack cursor;
|
||||
private final GuiElement element;
|
||||
private final InventoryInteractEvent event;
|
||||
|
||||
public Click(InventoryGui gui, int slot, ClickType clickType, ItemStack cursor, GuiElement element, InventoryInteractEvent event) {
|
||||
this.gui = gui;
|
||||
this.slot = slot;
|
||||
this.clickType = clickType;
|
||||
this.cursor = cursor;
|
||||
this.element = element;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slot of the GUI that was clicked
|
||||
* @return The clicked slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element that was clicked
|
||||
* @return The clicked GuiElement
|
||||
*/
|
||||
public GuiElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the click
|
||||
* @return The type of the click
|
||||
*/
|
||||
public ClickType getType() {
|
||||
return clickType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item on the cursor
|
||||
* @return The item on the cursor when this click occurred
|
||||
*/
|
||||
public ItemStack getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the item on the cursor after the click
|
||||
* @param cursor The new item on the cursor
|
||||
*/
|
||||
public void setCursor(ItemStack cursor) {
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get who clicked the element
|
||||
* @return The player that clicked
|
||||
*/
|
||||
public HumanEntity getWhoClicked() {
|
||||
return event.getWhoClicked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event of the inventory interaction
|
||||
* @return The InventoryInteractEvent associated with this Click
|
||||
*/
|
||||
public InventoryInteractEvent getRawEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public InventoryGui getGui() {
|
||||
return gui;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a group of multiple elements. Will be left-aligned by default.
|
||||
*/
|
||||
public class GuiElementGroup extends GuiElement {
|
||||
private List<GuiElement> elements = new ArrayList<>();
|
||||
private GuiElement filler = null;
|
||||
private Alignment alignment = Alignment.LEFT;
|
||||
|
||||
/**
|
||||
* A group of elements
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param elements The elements in this group
|
||||
*/
|
||||
public GuiElementGroup(char slotChar, GuiElement... elements) {
|
||||
super(slotChar, null);
|
||||
setAction(click -> {
|
||||
GuiElement element = getElement(click.getSlot(), click.getGui().getPageNumber(click.getWhoClicked()));
|
||||
if (element != null && element.getAction(click.getRawEvent().getWhoClicked()) != null) {
|
||||
return element.getAction(click.getWhoClicked()).onClick(click);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
Collections.addAll(this.elements, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
GuiElement element = getElement(slot, gui.getPageNumber(who));
|
||||
if (element != null) {
|
||||
return element.getItem(who, slot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGui(InventoryGui gui) {
|
||||
super.setGui(gui);
|
||||
for (GuiElement element : elements) {
|
||||
if (element != null) {
|
||||
element.setGui(gui);
|
||||
}
|
||||
}
|
||||
if (filler != null) {
|
||||
filler.setGui(gui);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSlots(int[] slots) {
|
||||
super.setSlots(slots);
|
||||
for (GuiElement element : elements) {
|
||||
if (element != null) {
|
||||
element.setSlots(slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element to this group
|
||||
* @param element The element to add
|
||||
*/
|
||||
public void addElement(GuiElement element){
|
||||
elements.add(element);
|
||||
if (element != null) {
|
||||
element.setGui(gui);
|
||||
element.setSlots(slots);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add elements to this group
|
||||
* @param elements The elements to add
|
||||
*/
|
||||
public void addElements(GuiElement... elements){
|
||||
for (GuiElement element : elements) {
|
||||
addElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add elements to this group
|
||||
* @param elements The elements to add
|
||||
*/
|
||||
public void addElements(Collection<GuiElement> elements){
|
||||
for (GuiElement element : elements) {
|
||||
addElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element in a certain slot
|
||||
* @param slot The slot to get the element for
|
||||
* @return The GuiElement in that slot or <code>null</code>
|
||||
*/
|
||||
public GuiElement getElement(int slot) {
|
||||
return getElement(slot, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element in a certain slot on a certain page
|
||||
* @param slot The slot to get the element for
|
||||
* @param pageNumber The number of the page that the gui is on
|
||||
* @return The GuiElement in that slot or <code>null</code>
|
||||
*/
|
||||
public GuiElement getElement(int slot, int pageNumber) {
|
||||
if (elements.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int index = getSlotIndex(slot, slots.length < elements.size() ? pageNumber : 0);
|
||||
if (index > -1) {
|
||||
if (alignment == Alignment.LEFT) {
|
||||
if (index < elements.size()) {
|
||||
return elements.get(index);
|
||||
}
|
||||
} else {
|
||||
int lineWidth = getLineWidth(slot);
|
||||
int linePosition = getLinePosition(slot);
|
||||
if (elements.size() - index > lineWidth - linePosition) {
|
||||
return elements.get(index);
|
||||
}
|
||||
int rest = elements.size() - (index - linePosition);
|
||||
int blankBefore = alignment == Alignment.CENTER ? (lineWidth - rest) / 2 : lineWidth - rest;
|
||||
if (linePosition < blankBefore || index - blankBefore >= elements.size()) {
|
||||
return filler;
|
||||
}
|
||||
return elements.get(index - blankBefore);
|
||||
}
|
||||
}
|
||||
return filler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the line the slot is in
|
||||
* @param slot The slot
|
||||
* @return The width of the line in the GUI setup of this group
|
||||
*/
|
||||
private int getLineWidth(int slot) {
|
||||
int width = gui.getWidth();
|
||||
int row = slot / width;
|
||||
|
||||
int amount = 0;
|
||||
for (int s : slots) {
|
||||
if (s >= row * width && s < (row + 1) * width) {
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of the slot in its line
|
||||
* @param slot The slot ID
|
||||
* @return The line position or -1 if not in its line. wat
|
||||
*/
|
||||
private int getLinePosition(int slot) {
|
||||
int width = gui.getWidth();
|
||||
int row = slot / width;
|
||||
|
||||
int position = -1;
|
||||
for (int s : slots) {
|
||||
if (s >= row * width && s < (row + 1) * width) {
|
||||
position++;
|
||||
if (s == slot) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all elements of this group. This list is immutable, use {@link #addElement(GuiElement)}
|
||||
* and {@link #clearElements()} to modify the elements in this group.
|
||||
* @return An immutable list of all elements in this group
|
||||
*/
|
||||
public List<GuiElement> getElements() {
|
||||
return Collections.unmodifiableList(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements in the group
|
||||
*/
|
||||
public void clearElements() {
|
||||
elements.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filler element for empty slots
|
||||
* @param item The item for the filler element
|
||||
*/
|
||||
public void setFiller(ItemStack item) {
|
||||
filler = new StaticGuiElement(' ', item, " ");
|
||||
filler.setGui(gui);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filler element for empty slots
|
||||
* @param filler The item for the filler element
|
||||
*/
|
||||
public void setFiller(GuiElement filler) {
|
||||
this.filler = filler;
|
||||
if (filler != null) {
|
||||
filler.setGui(gui);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filler element
|
||||
* @return The filler element
|
||||
*/
|
||||
public GuiElement getFiller() {
|
||||
return filler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of this group
|
||||
* @return The amount of elements that this group has
|
||||
*/
|
||||
public int size() {
|
||||
return elements.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alignment of the elements in this group
|
||||
* @param alignment The alignment
|
||||
*/
|
||||
public void setAlignment(Alignment alignment) {
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alignment of the elements in this group
|
||||
* @return The alignment
|
||||
*/
|
||||
public Alignment getAlignment() {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
public enum Alignment {
|
||||
LEFT,
|
||||
CENTER,
|
||||
RIGHT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* This is an element that allows for controlling the pagination of the gui.
|
||||
* <b>Untested und potentially unfinished.</b>
|
||||
*/
|
||||
public class GuiPageElement extends StaticGuiElement {
|
||||
private PageAction pageAction;
|
||||
private boolean silent = false;
|
||||
|
||||
/**
|
||||
* An element that allows for controlling the pagination of the gui.
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The {@link ItemStack} representing this element
|
||||
* @param pageAction What kind of page action you want to happen when interacting with the element.
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public GuiPageElement(char slotChar, ItemStack item, PageAction pageAction, String... text) {
|
||||
super(slotChar, item, text);
|
||||
setAction(click -> {
|
||||
switch (pageAction) {
|
||||
case NEXT:
|
||||
if (click.getGui().getPageNumber(click.getWhoClicked()) + 1 < click.getGui().getPageAmount(click.getWhoClicked())) {
|
||||
if (!isSilent()) {
|
||||
click.getGui().playClickSound();
|
||||
}
|
||||
click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageNumber(click.getWhoClicked()) + 1);
|
||||
}
|
||||
break;
|
||||
case PREVIOUS:
|
||||
if (click.getGui().getPageNumber(click.getWhoClicked()) > 0) {
|
||||
if (!isSilent()) {
|
||||
click.getGui().playClickSound();
|
||||
}
|
||||
click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageNumber(click.getWhoClicked()) - 1);
|
||||
}
|
||||
break;
|
||||
case FIRST:
|
||||
if (!isSilent()) {
|
||||
click.getGui().playClickSound();
|
||||
}
|
||||
click.getGui().setPageNumber(click.getWhoClicked(), 0);
|
||||
break;
|
||||
case LAST:
|
||||
if (!isSilent()) {
|
||||
click.getGui().playClickSound();
|
||||
}
|
||||
click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageAmount(click.getWhoClicked()) - 1);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.pageAction = pageAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not this element should make a sound when interacted with
|
||||
* @return Whether or not to make a sound when interacted with
|
||||
*/
|
||||
public boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this element should make a sound when interacted with
|
||||
* @param silent Whether or not to make a sound when interacted with
|
||||
*/
|
||||
public void setSilent(boolean silent) {
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
if (((pageAction == PageAction.FIRST || pageAction == PageAction.LAST) && gui.getPageAmount(who) < 3)
|
||||
|| (pageAction == PageAction.NEXT && gui.getPageNumber(who) + 1 >= gui.getPageAmount(who))
|
||||
|| (pageAction == PageAction.PREVIOUS && gui.getPageNumber(who) == 0)) {
|
||||
return gui.getFiller() != null ? gui.getFiller().getItem(who, slot) : null;
|
||||
}
|
||||
if (pageAction == PageAction.PREVIOUS) {
|
||||
setNumber(gui.getPageNumber(who));
|
||||
} else if (pageAction == PageAction.NEXT) {
|
||||
setNumber(gui.getPageNumber(who) + 2);
|
||||
} else if (pageAction == PageAction.LAST) {
|
||||
setNumber(gui.getPageAmount(who));
|
||||
}
|
||||
return super.getItem(who, slot).clone();
|
||||
}
|
||||
|
||||
public enum PageAction {
|
||||
NEXT,
|
||||
PREVIOUS,
|
||||
FIRST,
|
||||
LAST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An element that can switch between certain states. It automatically handles the switching
|
||||
* of the item in the slot that corresponds to the state that the element is in.
|
||||
*/
|
||||
public class GuiStateElement extends GuiElement {
|
||||
private Supplier<Integer> queryState = null;
|
||||
private boolean silent = false;
|
||||
private int currentState;
|
||||
private final State[] states;
|
||||
|
||||
/**
|
||||
* An element that can switch between certain states.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param defaultState The index of the default state.
|
||||
* @param states The list of different {@link State}s that this element can have.
|
||||
*/
|
||||
public GuiStateElement(char slotChar, int defaultState, State... states) {
|
||||
super(slotChar, null);
|
||||
if (states.length == 0) {
|
||||
throw new IllegalArgumentException("You need to add at least one State!");
|
||||
}
|
||||
this.currentState = defaultState;
|
||||
this.states = states;
|
||||
|
||||
setAction(click -> {
|
||||
State next = nextState();
|
||||
next.change.onChange(click);
|
||||
if (!isSilent()) {
|
||||
click.getGui().playClickSound();
|
||||
}
|
||||
gui.draw(click.getWhoClicked(), false);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* An element that can switch between certain states.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param defaultState The key of the default state.
|
||||
* @param states The list of different {@link State}s that this element can have.
|
||||
*/
|
||||
public GuiStateElement(char slotChar, String defaultState, State... states) {
|
||||
this(slotChar, getStateIndex(defaultState, states), states);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element that can switch between certain states.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param queryState Supplier for the current state.
|
||||
* @param states The list of different {@link State}s that this element can have.
|
||||
*/
|
||||
public GuiStateElement(char slotChar, Supplier<String> queryState, State... states) {
|
||||
this(slotChar, queryState.get(), states);
|
||||
this.queryState = () -> getStateIndex(queryState.get(), states);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element that can switch between certain states. The first state will be the default one.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param states The list of different {@link State}s that this element can have.
|
||||
*/
|
||||
public GuiStateElement(char slotChar, State... states) {
|
||||
this(slotChar, 0, states);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through the states of this element
|
||||
* @return The new state (next one to the old)
|
||||
*/
|
||||
public State nextState() {
|
||||
queryCurrentState();
|
||||
currentState = states.length > currentState + 1 ? currentState + 1 : 0;
|
||||
return states[currentState];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through the states of this element backwards
|
||||
* @return The new state (previous one to the old)
|
||||
*/
|
||||
public State previousState() {
|
||||
queryCurrentState();
|
||||
currentState = currentState > 0 ? currentState - 1 : states.length - 1;
|
||||
return states[currentState];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
return getState().getItem(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGui(InventoryGui gui) {
|
||||
super.setGui(gui);
|
||||
for (State state : states) {
|
||||
state.setGui(gui);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of this element
|
||||
* @return The current state of this element
|
||||
*/
|
||||
public State getState() {
|
||||
queryCurrentState();
|
||||
return states[currentState];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not this element should make a sound when interacted with
|
||||
* @return Whether or not to make a sound when interacted with
|
||||
*/
|
||||
public boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this element should make a sound when interacted with
|
||||
* @param silent Whether or not to make a sound when interacted with
|
||||
*/
|
||||
public void setSilent(boolean silent) {
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to query the current state if there is a query
|
||||
*/
|
||||
private void queryCurrentState() {
|
||||
if (queryState != null) {
|
||||
currentState = queryState.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current state with the state's key. Does not trigger the state's change.
|
||||
* @param key The key to search for.
|
||||
* @throws IllegalArgumentException Thrown if there is no state with the provided key.
|
||||
*/
|
||||
public void setState(String key) throws IllegalArgumentException {
|
||||
currentState = getStateIndex(key, states);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of a state from a key
|
||||
* @param key The key to search for.
|
||||
* @param states The states to search in.
|
||||
* @return The index of that key in the state array.
|
||||
* @throws IllegalArgumentException Thrown if there is no state with the provided key.
|
||||
*/
|
||||
private static int getStateIndex(String key, State[] states) throws IllegalArgumentException {
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (states[i].getKey().equals(key)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("This element does not have the state " + key);
|
||||
}
|
||||
|
||||
/**
|
||||
* A state that the {@link GuiStateElement} can have.
|
||||
*/
|
||||
public static class State {
|
||||
private final Change change;
|
||||
private final String key;
|
||||
private final ItemStack item;
|
||||
private String[] text;
|
||||
private InventoryGui gui;
|
||||
|
||||
/**
|
||||
* A state that the {@link GuiStateElement} can have.
|
||||
* @param change What to do when the state changes
|
||||
* @param key The state's string key
|
||||
* @param item The {@link ItemStack} to represent this state
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public State(Change change, String key, ItemStack item, String... text) {
|
||||
this.change = change;
|
||||
this.key = key;
|
||||
this.item = item;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this element's display text. If this is an empty array the item's name will be displayed
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public void setText(String... text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ItemStack} that represents this state.
|
||||
* @return The {@link ItemStack} that represents this state
|
||||
* @deprecated Use {@link #getItem(HumanEntity)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ItemStack getItem() {
|
||||
return getItem(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ItemStack} that represents this state.
|
||||
* @param who The player viewing the GUI
|
||||
* @return The {@link ItemStack} that represents this state
|
||||
*/
|
||||
public ItemStack getItem(HumanEntity who) {
|
||||
ItemStack clone = item.clone();
|
||||
gui.setItemText(who, clone, getText());
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string key of the state.
|
||||
* @return The state's string key
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text lines that describe this state.
|
||||
* @return The text lines for this state
|
||||
*/
|
||||
public String[] getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
private void setGui(InventoryGui gui) {
|
||||
this.gui = gui;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define what should happen when the state of the element' state changes to this state
|
||||
*/
|
||||
public interface Change {
|
||||
|
||||
/**
|
||||
* What should happen when the element's state changes to this state
|
||||
* @param click The click that triggered this change
|
||||
*/
|
||||
void onChange(Click click);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* This element is used to access an {@link Inventory}. The slots in the inventory are selected
|
||||
* by searching through the whole gui the element is in and getting the number of the spot
|
||||
* in the character group that this element is in. <br>
|
||||
* E.g. if you have five characters called "s" in the gui setup and the second element is
|
||||
* accessed by the player then it will translate to the second slot in the inventory.
|
||||
*/
|
||||
public class GuiStorageElement extends GuiElement {
|
||||
private final Inventory storage;
|
||||
private final int invSlot;
|
||||
private Runnable applyStorage;
|
||||
private Function<ValidatorInfo, Boolean> itemValidator;
|
||||
|
||||
/**
|
||||
* An element used to access an {@link Inventory}.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param storage The {@link Inventory} that this element is linked to.
|
||||
*/
|
||||
public GuiStorageElement(char slotChar, Inventory storage) {
|
||||
this(slotChar, storage, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element used to access a specific slot in an {@link Inventory}.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param storage The {@link Inventory} that this element is linked to.
|
||||
* @param invSlot The index of the slot to access in the {@link Inventory}.
|
||||
*/
|
||||
public GuiStorageElement(char slotChar, Inventory storage, int invSlot) {
|
||||
this(slotChar, storage, invSlot, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element used to access a specific slot in an {@link Inventory}.
|
||||
* @param slotChar The character to replace in the gui setup string.
|
||||
* @param storage The {@link Inventory} that this element is linked to.
|
||||
* @param invSlot The index of the slot to access in the {@link Inventory}.
|
||||
* @param applyStorage Apply the storage that this element represents.
|
||||
* @param itemValidator Should return <code>false</code> for items that should not work in that slot
|
||||
* Can be null if the storage is directly linked.
|
||||
*/
|
||||
public GuiStorageElement(char slotChar, Inventory storage, int invSlot, Runnable applyStorage, Function<ValidatorInfo, Boolean> itemValidator) {
|
||||
super(slotChar, null);
|
||||
this.invSlot = invSlot;
|
||||
this.applyStorage = applyStorage;
|
||||
this.itemValidator = itemValidator;
|
||||
setAction(click -> {
|
||||
if (getStorageSlot(click.getWhoClicked(), click.getSlot()) < 0) {
|
||||
return true;
|
||||
}
|
||||
ItemStack storageItem = getStorageItem(click.getWhoClicked(), click.getSlot());
|
||||
ItemStack slotItem = click.getRawEvent().getView().getTopInventory().getItem(click.getSlot());
|
||||
if (slotItem == null && storageItem != null && storageItem.getType() != Material.AIR
|
||||
|| storageItem == null && slotItem != null && slotItem.getType() != Material.AIR
|
||||
|| storageItem != null && !storageItem.equals(slotItem)) {
|
||||
gui.draw(click.getWhoClicked(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(click.getRawEvent() instanceof InventoryClickEvent)) {
|
||||
// Only the click event will be handled here, drag event is handled separately
|
||||
return true;
|
||||
}
|
||||
|
||||
InventoryClickEvent event = (InventoryClickEvent) click.getRawEvent();
|
||||
|
||||
ItemStack movedItem = null;
|
||||
switch (event.getAction()) {
|
||||
case NOTHING:
|
||||
case CLONE_STACK:
|
||||
return false;
|
||||
case MOVE_TO_OTHER_INVENTORY:
|
||||
if (event.getRawSlot() < click.getRawEvent().getView().getTopInventory().getSize()) {
|
||||
// Moved from storage
|
||||
|
||||
// Check if there is actually space (more advanced checks can unfortunately not be supported right now)
|
||||
if (click.getRawEvent().getView().getBottomInventory().firstEmpty() == -1) {
|
||||
// No empty slot, cancel
|
||||
return true;
|
||||
}
|
||||
movedItem = null;
|
||||
} else {
|
||||
// Moved to storage
|
||||
|
||||
// Check if there is actually space (more advanced checks can unfortunately not be supported right now)
|
||||
if (click.getRawEvent().getView().getTopInventory().firstEmpty() == -1) {
|
||||
// No empty slot, cancel
|
||||
return true;
|
||||
}
|
||||
movedItem = event.getCurrentItem();
|
||||
}
|
||||
// Update GUI to avoid display glitches
|
||||
gui.runTask(gui::draw);
|
||||
break;
|
||||
case HOTBAR_MOVE_AND_READD:
|
||||
case HOTBAR_SWAP:
|
||||
int button = event.getHotbarButton();
|
||||
if (button < 0) {
|
||||
return true;
|
||||
}
|
||||
ItemStack hotbarItem = click.getRawEvent().getView().getBottomInventory().getItem(button);
|
||||
if (hotbarItem != null) {
|
||||
movedItem = hotbarItem.clone();
|
||||
}
|
||||
break;
|
||||
case PICKUP_ONE:
|
||||
case DROP_ONE_SLOT:
|
||||
if (event.getCurrentItem() != null) {
|
||||
movedItem = event.getCurrentItem().clone();
|
||||
movedItem.setAmount(movedItem.getAmount() - 1);
|
||||
}
|
||||
break;
|
||||
case DROP_ALL_SLOT:
|
||||
movedItem = null;
|
||||
break;
|
||||
case PICKUP_HALF:
|
||||
if (event.getCurrentItem() != null) {
|
||||
movedItem = event.getCurrentItem().clone();
|
||||
movedItem.setAmount(movedItem.getAmount() / 2);
|
||||
}
|
||||
break;
|
||||
case PLACE_SOME:
|
||||
if (event.getCurrentItem() == null) {
|
||||
if (event.getCursor() != null) {
|
||||
movedItem = event.getCursor().clone();
|
||||
}
|
||||
} else {
|
||||
movedItem = event.getCurrentItem().clone();
|
||||
int newAmount = movedItem.getAmount() + (event.getCursor() != null ? event.getCursor().getAmount() : 0);
|
||||
if (newAmount < movedItem.getMaxStackSize()) {
|
||||
movedItem.setAmount(newAmount);
|
||||
} else {
|
||||
movedItem.setAmount(movedItem.getMaxStackSize());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLACE_ONE:
|
||||
if (event.getCursor() != null) {
|
||||
if (event.getCurrentItem() == null) {
|
||||
movedItem = event.getCursor().clone();
|
||||
movedItem.setAmount(1);
|
||||
} else {
|
||||
movedItem = event.getCursor().clone();
|
||||
movedItem.setAmount(event.getCurrentItem().getAmount() + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLACE_ALL:
|
||||
if (event.getCursor() != null) {
|
||||
movedItem = event.getCursor().clone();
|
||||
if (event.getCurrentItem() != null && event.getCurrentItem().getAmount() > 0) {
|
||||
movedItem.setAmount(event.getCurrentItem().getAmount() + movedItem.getAmount());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PICKUP_ALL:
|
||||
case SWAP_WITH_CURSOR:
|
||||
if (event.getCursor() != null) {
|
||||
movedItem = event.getCursor().clone();
|
||||
};
|
||||
break;
|
||||
case COLLECT_TO_CURSOR:
|
||||
if (event.getCursor() == null
|
||||
|| event.getCurrentItem() != null && event.getCurrentItem().getType() != Material.AIR) {
|
||||
return true;
|
||||
}
|
||||
gui.simulateCollectToCursor(click);
|
||||
return false;
|
||||
default:
|
||||
click.getRawEvent().getWhoClicked().sendMessage(ChatColor.RED + "The action " + event.getAction() + " is not supported! Sorry about that :(");
|
||||
return true;
|
||||
}
|
||||
return !setStorageItem(click.getWhoClicked(), click.getSlot(), movedItem);
|
||||
});
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
int index = getStorageSlot(who, slot);
|
||||
if (index > -1 && index < storage.getSize()) {
|
||||
return storage.getItem(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Inventory} that this element is linked to.
|
||||
* @return The {@link Inventory} that this element is linked to.
|
||||
*/
|
||||
public Inventory getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage slot index that corresponds to the InventoryGui slot
|
||||
* @param player The player which is using the GUI view
|
||||
* @param slot The slot in the GUI
|
||||
* @return The index of the storage slot or <code>-1</code> if it's outside the storage
|
||||
*/
|
||||
private int getStorageSlot(HumanEntity player, int slot) {
|
||||
int index = invSlot != -1 ? invSlot : getSlotIndex(slot, gui.getPageNumber(player));
|
||||
if (index < 0 || index >= storage.getSize()) {
|
||||
return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item in the storage that corresponds to the InventoryGui slot
|
||||
* @param slot The slot in the GUI
|
||||
* @return The {@link ItemStack} or <code>null</code> if the slot is outside of the item's size
|
||||
* @deprecated Use {@link #getStorageItem(HumanEntity, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ItemStack getStorageItem(int slot) {
|
||||
return getStorageItem(null, slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item in the storage that corresponds to the InventoryGui slot
|
||||
* @param player The player which is using the GUI view
|
||||
* @param slot The slot in the GUI
|
||||
* @return The {@link ItemStack} or <code>null</code> if the slot is outside of the item's size
|
||||
*/
|
||||
public ItemStack getStorageItem(HumanEntity player, int slot) {
|
||||
int index = getStorageSlot(player, slot);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return storage.getItem(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the item in the storage that corresponds to the InventoryGui slot.
|
||||
* @param slot The slot in the GUI
|
||||
* @param item The {@link ItemStack} to set
|
||||
* @return <code>true</code> if the item was set; <code>false</code> if the slot was outside of this storage
|
||||
* @deprecated Use {@link #setStorageItem(HumanEntity, int, ItemStack)}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean setStorageItem(int slot, ItemStack item) {
|
||||
return setStorageItem(null, slot, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the item in the storage that corresponds to the InventoryGui slot.
|
||||
* @param player The player using the GUI view
|
||||
* @param slot The slot in the GUI
|
||||
* @param item The {@link ItemStack} to set
|
||||
* @return <code>true</code> if the item was set; <code>false</code> if the slot was outside of this storage
|
||||
*/
|
||||
public boolean setStorageItem(HumanEntity player, int slot, ItemStack item) {
|
||||
int index = getStorageSlot(player, slot);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
if (!validateItem(slot, item)) {
|
||||
return false;
|
||||
}
|
||||
storage.setItem(index, item);
|
||||
if (applyStorage != null) {
|
||||
applyStorage.run();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the runnable that applies the storage
|
||||
* @return The storage applying runnable; might be null
|
||||
*/
|
||||
public Runnable getApplyStorage() {
|
||||
return applyStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set what should be done to apply the storage.
|
||||
* Not necessary if the storage is directly backed by a real inventory.
|
||||
* @param applyStorage How to apply the storage; can be null if nothing should be done
|
||||
*/
|
||||
public void setApplyStorage(Runnable applyStorage) {
|
||||
this.applyStorage = applyStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item validator
|
||||
* @return The item validator
|
||||
*/
|
||||
public Function<ValidatorInfo, Boolean> getItemValidator() {
|
||||
return itemValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a function that can validate whether or not an item can fit in the slot
|
||||
* @param itemValidator The item validator that takes a {@link ValidatorInfo} and returns <code>true</code> for items that
|
||||
* should and <code>false</code> for items that should not work in that slot
|
||||
*/
|
||||
public void setItemValidator(Function<ValidatorInfo, Boolean> itemValidator) {
|
||||
this.itemValidator = itemValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate whether or not an item can be put in a slot with the item validator set in {@link #setItemValidator(Function)}
|
||||
* @param slot The slot the item should be tested for
|
||||
* @param item The item to test
|
||||
* @return <code>true</code> for items that should and <code>false</code> for items that should not work in that slot
|
||||
*/
|
||||
public boolean validateItem(int slot, ItemStack item) {
|
||||
return itemValidator == null || itemValidator.apply(new ValidatorInfo(this, slot, item));
|
||||
}
|
||||
|
||||
public static class ValidatorInfo {
|
||||
private final GuiElement element;
|
||||
private final int slot;
|
||||
private final ItemStack item;
|
||||
|
||||
public ValidatorInfo(GuiElement element, int slot, ItemStack item) {
|
||||
this.item = item;
|
||||
this.slot = slot;
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
public GuiElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,159 @@
|
||||
package net.momirealms.customfishing.libraries.inventorygui;
|
||||
|
||||
/*
|
||||
* Copyright 2017 Max Lee (https://github.com/Phoenix616)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Represents a simple element in a gui to which an action can be assigned.
|
||||
* If you want the item to change on click you have to do that yourself.
|
||||
*/
|
||||
public class StaticGuiElement extends GuiElement {
|
||||
private ItemStack item;
|
||||
private int number;
|
||||
private String[] text;
|
||||
|
||||
/**
|
||||
* Represents an element in a gui
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The item this element displays
|
||||
* @param number The number, 1 will not display the number
|
||||
* @param action The action to run when the player clicks on this element
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
* @throws IllegalArgumentException If the number is below 1 or above the max stack count (currently 64)
|
||||
*/
|
||||
public StaticGuiElement(char slotChar, ItemStack item, int number, Action action, String... text) throws IllegalArgumentException {
|
||||
super(slotChar, action);
|
||||
this.item = item;
|
||||
this.text = text;
|
||||
setNumber(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an element in a gui
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The item this element displays
|
||||
* @param action The action to run when the player clicks on this element
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public StaticGuiElement(char slotChar, ItemStack item, Action action, String... text) {
|
||||
this(slotChar, item, item != null ? item.getAmount() : 1, action, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an element in a gui that doesn't have any action when clicked
|
||||
* @param slotChar The character to replace in the gui setup string
|
||||
* @param item The item this element displays
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public StaticGuiElement(char slotChar, ItemStack item, String... text) {
|
||||
this(slotChar, item, item != null ? item.getAmount() : 1, null, text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the item that is displayed by this element
|
||||
* @param item The item that should be displayed by this element
|
||||
*/
|
||||
public void setItem(ItemStack item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw item displayed by this element which was passed to the constructor or set with {@link #setItem(ItemStack)}.
|
||||
* This item will not have the amount or text applied! Use {@link #getItem(HumanEntity, int)} for that!
|
||||
* @return The raw item
|
||||
*/
|
||||
public ItemStack getRawItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(HumanEntity who, int slot) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
ItemStack clone = item.clone();
|
||||
gui.setItemText(who, clone, getText());
|
||||
if (number > 0 && number <= 64) {
|
||||
clone.setAmount(number);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this element's display text. If this is an empty array the item's name will be displayed
|
||||
* @param text The text to display on this element, placeholders are automatically
|
||||
* replaced, see {@link InventoryGui#replaceVars} for a list of the
|
||||
* placeholder variables. Empty text strings are also filter out, use
|
||||
* a single space if you want to add an empty line!<br>
|
||||
* If it's not set/empty the item's default name will be used
|
||||
*/
|
||||
public void setText(String... text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text that this element displays
|
||||
* @return The text that is displayed on this element
|
||||
*/
|
||||
public String[] getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number that this element should display (via the Item's amount)
|
||||
* @param number The number, 1 will not display the number
|
||||
* @return <code>true</code> if the number was set; <code>false</code> if it was below 1 or above 64
|
||||
*/
|
||||
public boolean setNumber(int number) {
|
||||
if (number < 1 || number > 64) {
|
||||
this.number = 1;
|
||||
return false;
|
||||
}
|
||||
this.number = number;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number that this element should display
|
||||
* @return The number (item amount) that this element currently has
|
||||
*/
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.momirealms.customfishing.libraries.libraryloader;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
|
||||
* into the classloader.
|
||||
*/
|
||||
public final class LibraryLoader {
|
||||
|
||||
@SuppressWarnings("Guava")
|
||||
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) CustomFishingPlugin.getInstance().getClass().getClassLoader()));
|
||||
|
||||
/**
|
||||
* Resolves all {@link MavenLibrary} annotations on the given object.
|
||||
*
|
||||
* @param object the object to load libraries for.
|
||||
*/
|
||||
public static void loadAll(Object object) {
|
||||
loadAll(object.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all {@link MavenLibrary} annotations on the given class.
|
||||
*
|
||||
* @param clazz the class to load libraries for.
|
||||
*/
|
||||
public static void loadAll(Class<?> clazz) {
|
||||
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
||||
for (MavenLibrary lib : libs) {
|
||||
load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url());
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(String groupId, String artifactId, String version, String repoUrl) {
|
||||
load(new Dependency(groupId, artifactId, version, repoUrl));
|
||||
}
|
||||
|
||||
public static void loadDependencies(String... libs) {
|
||||
if (libs == null || libs.length % 2 != 0)
|
||||
return;
|
||||
for (int i = 0; i < libs.length; i+=2) {
|
||||
String[] split = libs[i].split(":");
|
||||
load(new Dependency(
|
||||
split[0],
|
||||
split[1],
|
||||
split[2],
|
||||
libs[i+1]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(Dependency d) {
|
||||
LogUtils.info(String.format("Loading dependency %s:%s:%s from %s", d.groupId, d.artifactId, d.version, d.repoUrl));
|
||||
String name = d.artifactId() + "-" + d.version();
|
||||
File saveLocation = new File(getLibFolder(d), name + ".jar");
|
||||
if (!saveLocation.exists()) {
|
||||
try {
|
||||
LogUtils.info("Dependency '" + name + "' is not already in the libraries folder. Attempting to download...");
|
||||
URL url = d.getUrl();
|
||||
try (InputStream is = url.openStream()) {
|
||||
Files.copy(is, saveLocation.toPath());
|
||||
LogUtils.info("Dependency '" + name + "' successfully downloaded.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!saveLocation.exists())
|
||||
throw new RuntimeException("Unable to download dependency: " + d);
|
||||
try {
|
||||
URL_INJECTOR.get().addURL(saveLocation.toURI().toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to load dependency: " + saveLocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private static File getLibFolder(Dependency dependency) {
|
||||
File pluginDataFolder = CustomFishingPlugin.getInstance().getDataFolder();
|
||||
File serverDir = pluginDataFolder.getParentFile().getParentFile();
|
||||
|
||||
File helperDir = new File(serverDir, "libraries");
|
||||
String[] split = dependency.groupId().split("\\.");
|
||||
File jarDir;
|
||||
StringJoiner stringJoiner = new StringJoiner(File.separator);
|
||||
for (String str : split) {
|
||||
stringJoiner.add(str);
|
||||
}
|
||||
jarDir = new File(helperDir, stringJoiner + File.separator + dependency.artifactId + File.separator + dependency.version);
|
||||
jarDir.mkdirs();
|
||||
return jarDir;
|
||||
}
|
||||
|
||||
public record Dependency(String groupId, String artifactId, String version, String repoUrl) {
|
||||
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
|
||||
this.groupId = Objects.requireNonNull(groupId, "groupId");
|
||||
this.artifactId = Objects.requireNonNull(artifactId, "artifactId");
|
||||
this.version = Objects.requireNonNull(version, "version");
|
||||
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
|
||||
}
|
||||
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
String repo = this.repoUrl;
|
||||
if (!repo.endsWith("/")) {
|
||||
repo += "/";
|
||||
}
|
||||
repo += "%s/%s/%s/%s-%s.jar";
|
||||
|
||||
String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version);
|
||||
return new URL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof Dependency other)) return false;
|
||||
return this.groupId().equals(other.groupId()) &&
|
||||
this.artifactId().equals(other.artifactId()) &&
|
||||
this.version().equals(other.version()) &&
|
||||
this.repoUrl().equals(other.repoUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int PRIME = 59;
|
||||
int result = 1;
|
||||
result = result * PRIME + this.groupId().hashCode();
|
||||
result = result * PRIME + this.artifactId().hashCode();
|
||||
result = result * PRIME + this.version().hashCode();
|
||||
result = result * PRIME + this.repoUrl().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LibraryLoader.Dependency(" +
|
||||
"groupId=" + this.groupId() + ", " +
|
||||
"artifactId=" + this.artifactId() + ", " +
|
||||
"version=" + this.version() + ", " +
|
||||
"repoUrl=" + this.repoUrl() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.momirealms.customfishing.libraries.libraryloader;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate the required libraries for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibraries {
|
||||
|
||||
@NotNull
|
||||
MavenLibrary[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.momirealms.customfishing.libraries.libraryloader;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate a required library for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Repeatable(MavenLibraries.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibrary {
|
||||
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
@NotNull
|
||||
String groupId();
|
||||
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
@NotNull
|
||||
String artifactId();
|
||||
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
@NotNull
|
||||
String version();
|
||||
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
@NotNull
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.momirealms.customfishing.libraries.libraryloader;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Represents a maven repository.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.LOCAL_VARIABLE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Repository {
|
||||
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.momirealms.customfishing.libraries.libraryloader;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provides access to {@link URLClassLoader}#addURL.
|
||||
*/
|
||||
public abstract class URLClassLoaderAccess {
|
||||
|
||||
/**
|
||||
* Creates a {@link URLClassLoaderAccess} for the given class loader.
|
||||
*
|
||||
* @param classLoader the class loader
|
||||
* @return the access object
|
||||
*/
|
||||
static URLClassLoaderAccess create(URLClassLoader classLoader) {
|
||||
if (Unsafe.isSupported()) {
|
||||
return new Unsafe(classLoader);
|
||||
} else {
|
||||
return Noop.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
protected URLClassLoaderAccess(URLClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given URL to the class loader.
|
||||
*
|
||||
* @param url the URL to add
|
||||
*/
|
||||
public abstract void addURL(@NotNull URL url);
|
||||
|
||||
/**
|
||||
* Accesses using sun.misc.Unsafe, supported on Java 9+.
|
||||
*
|
||||
* @author Vaishnav Anil (https://github.com/slimjar/slimjar)
|
||||
*/
|
||||
private static class Unsafe extends URLClassLoaderAccess {
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
|
||||
static {
|
||||
sun.misc.Unsafe unsafe;
|
||||
try {
|
||||
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
|
||||
} catch (Throwable t) {
|
||||
unsafe = null;
|
||||
}
|
||||
UNSAFE = unsafe;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return UNSAFE != null;
|
||||
}
|
||||
|
||||
private final Collection<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
|
||||
} catch (Throwable e) {
|
||||
unopenedURLs = null;
|
||||
pathURLs = null;
|
||||
}
|
||||
this.unopenedURLs = unopenedURLs;
|
||||
this.pathURLs = pathURLs;
|
||||
}
|
||||
|
||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
return UNSAFE.getObject(object, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(@NotNull URL url) {
|
||||
this.unopenedURLs.add(url);
|
||||
this.pathURLs.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Noop extends URLClassLoaderAccess {
|
||||
private static final Noop INSTANCE = new Noop();
|
||||
|
||||
private Noop() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(@NotNull URL url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
package net.momirealms.customfishing.mechanic.action;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.ActionManager;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.action.ActionBuilder;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.ExperienceOrb;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class ActionManagerImpl implements ActionManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, ActionBuilder> actionBuilderMap;
|
||||
|
||||
public ActionManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.actionBuilderMap = new HashMap<>();
|
||||
this.registerInbuiltActions();
|
||||
}
|
||||
|
||||
private void registerInbuiltActions() {
|
||||
this.registerMessageAction();
|
||||
this.registerCommandAction();
|
||||
this.registerMendingAction();
|
||||
this.registerExpAction();
|
||||
this.registerChainAction();
|
||||
this.registerPotionAction();
|
||||
this.registerSoundAction();
|
||||
this.registerPluginExpAction();
|
||||
this.registerTitleAction();
|
||||
this.registerActionBarAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerAction(String type, ActionBuilder actionBuilder) {
|
||||
if (this.actionBuilderMap.containsKey(type)) return false;
|
||||
this.actionBuilderMap.put(type, actionBuilder);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterAction(String type) {
|
||||
return this.actionBuilderMap.remove(type) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction(ConfigurationSection section) {
|
||||
return getActionBuilder(section.getString("type")).build(section.get("value"), section.getDouble("chance", 1d));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Action[] getActions(ConfigurationSection section) {
|
||||
if (section == null) return null;
|
||||
ArrayList<Action> actionList = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
actionList.add(getAction(innerSection));
|
||||
}
|
||||
}
|
||||
return actionList.toArray(new Action[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionBuilder getActionBuilder(String type) {
|
||||
return actionBuilderMap.get(type);
|
||||
}
|
||||
|
||||
private void registerMessageAction() {
|
||||
registerAction("message", (args, chance) -> {
|
||||
ArrayList<String> msg = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
|
||||
condition.getPlayer(),
|
||||
msg,
|
||||
condition.getArgs()
|
||||
);
|
||||
for (String text : replaced) {
|
||||
AdventureManagerImpl.getInstance().sendPlayerMessage(condition.getPlayer(), text);
|
||||
}
|
||||
};
|
||||
});
|
||||
registerAction("broadcast", (args, chance) -> {
|
||||
ArrayList<String> msg = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
|
||||
condition.getPlayer(),
|
||||
msg,
|
||||
condition.getArgs()
|
||||
);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
for (String text : replaced) {
|
||||
AdventureManagerImpl.getInstance().sendPlayerMessage(player, text);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
registerAction("random-message", (args, chance) -> {
|
||||
ArrayList<String> msg = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
String random = msg.get(ThreadLocalRandom.current().nextInt(msg.size()));
|
||||
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
|
||||
AdventureManagerImpl.getInstance().sendPlayerMessage(condition.getPlayer(), random);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerCommandAction() {
|
||||
registerAction("command", (args, chance) -> {
|
||||
ArrayList<String> cmd = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
List<String> replaced = PlaceholderManagerImpl.getInstance().parse(
|
||||
condition.getPlayer(),
|
||||
cmd,
|
||||
condition.getArgs()
|
||||
);
|
||||
plugin.getScheduler().runTaskSync(() -> {
|
||||
for (String text : replaced) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text);
|
||||
}
|
||||
}, condition.getLocation());
|
||||
};
|
||||
});
|
||||
registerAction("random-command", (args, chance) -> {
|
||||
ArrayList<String> cmd = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
String random = cmd.get(ThreadLocalRandom.current().nextInt(cmd.size()));
|
||||
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
|
||||
String finalRandom = random;
|
||||
plugin.getScheduler().runTaskSync(() -> {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom);
|
||||
}, condition.getLocation());
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerActionBarAction() {
|
||||
registerAction("actionbar", (args, chance) -> {
|
||||
String text = (String) args;
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
String parsed = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), text, condition.getArgs());
|
||||
AdventureManagerImpl.getInstance().sendActionbar(condition.getPlayer(), parsed);
|
||||
};
|
||||
});
|
||||
registerAction("random-actionbar", (args, chance) -> {
|
||||
ArrayList<String> texts = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
String random = texts.get(ThreadLocalRandom.current().nextInt(texts.size()));
|
||||
random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs());
|
||||
AdventureManagerImpl.getInstance().sendActionbar(condition.getPlayer(), random);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerMendingAction() {
|
||||
registerAction("mending", (args, chance) -> {
|
||||
int xp = (int) args;
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
if (CustomFishingPlugin.get().getVersionManager().isSpigot()) {
|
||||
condition.getPlayer().getLocation().getWorld().spawn(condition.getPlayer().getLocation(), ExperienceOrb.class, e -> e.setExperience(xp));
|
||||
} else {
|
||||
condition.getPlayer().giveExp(xp, true);
|
||||
AdventureManagerImpl.getInstance().sendSound(condition.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerExpAction() {
|
||||
registerAction("exp", (args, chance) -> {
|
||||
int xp = (int) args;
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
condition.getPlayer().giveExp(xp);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerChainAction() {
|
||||
registerAction("chain", (args, chance) -> {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
actions.add(getAction(innerSection));
|
||||
}
|
||||
}
|
||||
}
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
for (Action action : actions) {
|
||||
action.trigger(condition);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerTitleAction() {
|
||||
registerAction("title", (args, chance) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
String title = section.getString("title");
|
||||
String subtitle = section.getString("subtitle");
|
||||
int fadeIn = section.getInt("fade-in", 20);
|
||||
int stay = section.getInt("stay", 30);
|
||||
int fadeOut = section.getInt("fade-out", 10);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
AdventureManagerImpl.getInstance().sendTitle(
|
||||
condition.getPlayer(),
|
||||
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), title, condition.getArgs()),
|
||||
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()),
|
||||
fadeIn * 50,
|
||||
stay * 50,
|
||||
fadeOut * 50
|
||||
);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
registerAction("random-title", (args, chance) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
List<String> titles = section.getStringList("titles");
|
||||
List<String> subtitles = section.getStringList("subtitles");
|
||||
int fadeIn = section.getInt("fade-in", 20);
|
||||
int stay = section.getInt("stay", 30);
|
||||
int fadeOut = section.getInt("fade-out", 10);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
AdventureManagerImpl.getInstance().sendTitle(
|
||||
condition.getPlayer(),
|
||||
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), condition.getArgs()),
|
||||
PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), condition.getArgs()),
|
||||
fadeIn * 50,
|
||||
stay * 50,
|
||||
fadeOut * 50
|
||||
);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void registerPotionAction() {
|
||||
registerAction("potion-effect", (args, chance) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
PotionEffect potionEffect = new PotionEffect(
|
||||
Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))),
|
||||
section.getInt("duration", 20),
|
||||
section.getInt("amplifier", 0)
|
||||
);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
condition.getPlayer().addPotionEffect(potionEffect);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private void registerSoundAction() {
|
||||
registerAction("sound", (args, chance) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
Sound sound = Sound.sound(
|
||||
Key.key(section.getString("key")),
|
||||
Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)),
|
||||
(float) section.getDouble("volume", 1),
|
||||
(float) section.getDouble("pitch", 1)
|
||||
);
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
AdventureManagerImpl.getInstance().sendSound(condition.getPlayer(), sound);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void registerPluginExpAction() {
|
||||
registerAction("plugin-exp", (args, chance) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
String pluginName = section.getString("plugin");
|
||||
double exp = section.getDouble("exp", 1);
|
||||
String target = section.getString("target");
|
||||
return condition -> {
|
||||
if (Math.random() > chance) return;
|
||||
Optional.ofNullable(plugin.getIntegrationManager().getLevelHook(pluginName)).ifPresentOrElse(it -> {
|
||||
it.addXp(condition.getPlayer(), target, exp);
|
||||
}, () -> LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."));
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package net.momirealms.customfishing.mechanic.bag;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ScoreComponent;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.BagManager;
|
||||
import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BagManagerImpl implements BagManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final ConcurrentHashMap<UUID, FishingBagHolder> bagMap;
|
||||
private final WindowPacketListener windowPacketListener;
|
||||
|
||||
public BagManagerImpl(CustomFishingPluginImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
this.bagMap = new ConcurrentHashMap<>();
|
||||
this.windowPacketListener = new WindowPacketListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBagEnabled() {
|
||||
return Config.enableFishingBag;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
CustomFishingPluginImpl.getProtocolManager().addPacketListener(windowPacketListener);
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
CustomFishingPluginImpl.getProtocolManager().removePacketListener(windowPacketListener);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
unload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getOnlineBagInventory(UUID uuid) {
|
||||
var onlinePlayer = plugin.getStorageManager().getOnlineUser(uuid);
|
||||
if (onlinePlayer == null) {
|
||||
LogUtils.warn("Player " + uuid + "'s bag data is not loaded.");
|
||||
return null;
|
||||
}
|
||||
return onlinePlayer.getHolder().getInventory();
|
||||
}
|
||||
|
||||
public static class WindowPacketListener extends PacketAdapter {
|
||||
|
||||
public WindowPacketListener() {
|
||||
super(CustomFishingPlugin.getInstance(), PacketType.Play.Server.OPEN_WINDOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
final PacketContainer packet = event.getPacket();
|
||||
StructureModifier<WrappedChatComponent> wrappedChatComponentStructureModifier = packet.getChatComponents();
|
||||
WrappedChatComponent component = wrappedChatComponentStructureModifier.getValues().get(0);
|
||||
String windowTitleJson = component.getJson();
|
||||
Component titleComponent = GsonComponentSerializer.gson().deserialize(windowTitleJson);
|
||||
if (titleComponent instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("bag")) {
|
||||
HashMap<String, String> placeholders = new HashMap<>();
|
||||
String uuidStr = scoreComponent.objective();
|
||||
UUID uuid = UUID.fromString(uuidStr);
|
||||
placeholders.put("{uuid}", uuidStr);
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
placeholders.put("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(uuidStr));
|
||||
wrappedChatComponentStructureModifier.write(0,
|
||||
WrappedChatComponent.fromJson(
|
||||
GsonComponentSerializer.gson().serialize(
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(
|
||||
PlaceholderManagerImpl.getInstance().parse(offlinePlayer, Config.bagTitle, placeholders)
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package net.momirealms.customfishing.mechanic.block;
|
||||
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.common.Tuple;
|
||||
import net.momirealms.customfishing.api.manager.BlockManager;
|
||||
import net.momirealms.customfishing.api.mechanic.block.*;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Loot;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.block.VanillaBlockImpl;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.Barrel;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Chest;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BlockManagerImpl implements BlockManager, Listener {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, BlockLibrary> blockLibraryMap;
|
||||
private final HashMap<String, BlockConfig> blockConfigMap;
|
||||
private final HashMap<String, BlockDataModifierBuilder> dataBuilderMap;
|
||||
private final HashMap<String, BlockStateModifierBuilder> stateBuilderMap;
|
||||
|
||||
public BlockManagerImpl(CustomFishingPluginImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
this.blockLibraryMap = new HashMap<>();
|
||||
this.blockConfigMap = new HashMap<>();
|
||||
this.dataBuilderMap = new HashMap<>();
|
||||
this.stateBuilderMap = new HashMap<>();
|
||||
this.registerBlockLibrary(new VanillaBlockImpl());
|
||||
this.registerInbuiltProperties();
|
||||
this.registerStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBlockLibrary(BlockLibrary library) {
|
||||
if (this.blockLibraryMap.containsKey(library.identification())) return false;
|
||||
this.blockLibraryMap.put(library.identification(), library);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterBlockLibrary(BlockLibrary library) {
|
||||
return unregisterBlockLibrary(library.identification());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterBlockLibrary(String library) {
|
||||
return blockLibraryMap.remove(library) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBlockDataModifierBuilder(String type, BlockDataModifierBuilder builder) {
|
||||
if (dataBuilderMap.containsKey(type)) return false;
|
||||
dataBuilderMap.put(type, builder);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBlockStateModifierBuilder(String type, BlockStateModifierBuilder builder) {
|
||||
if (stateBuilderMap.containsKey(type)) return false;
|
||||
stateBuilderMap.put(type, builder);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadConfig();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
private void registerInbuiltProperties() {
|
||||
this.registerDirectional();
|
||||
this.registerStorage();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HandlerList.unregisterAll(this);
|
||||
HashMap<String, BlockConfig> tempMap = new HashMap<>(this.blockConfigMap);
|
||||
this.blockConfigMap.clear();
|
||||
for (Map.Entry<String, BlockConfig> entry : tempMap.entrySet()) {
|
||||
if (entry.getValue().isPersist()) {
|
||||
tempMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.blockLibraryMap.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void loadConfig() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("blocks")) {
|
||||
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()) {
|
||||
this.loadSingleFile(subFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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<>();
|
||||
ConfigurationSection property = section.getConfigurationSection("properties");
|
||||
if (property != null) {
|
||||
for (Map.Entry<String, Object> innerEntry : property.getValues(false).entrySet()) {
|
||||
BlockDataModifierBuilder dataBuilder = dataBuilderMap.get(innerEntry.getKey());
|
||||
if (dataBuilder != null) {
|
||||
dataModifiers.add(dataBuilder.build(innerEntry.getValue()));
|
||||
continue;
|
||||
}
|
||||
BlockStateModifierBuilder stateBuilder = stateBuilderMap.get(innerEntry.getKey());
|
||||
if (stateBuilder != null) {
|
||||
stateModifiers.add(stateBuilder.build(innerEntry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
BlockConfig blockConfig = new BlockConfig.Builder()
|
||||
.blockID(blockID)
|
||||
.persist(false)
|
||||
.horizontalVector(section.getDouble("vector.horizontal", 1.1))
|
||||
.verticalVector(section.getDouble("vector.vertical", 1.2))
|
||||
.dataModifiers(dataModifiers)
|
||||
.stateModifiers(stateModifiers)
|
||||
.build();
|
||||
blockConfigMap.put(entry.getKey(), blockConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void summonBlock(Player player, Location hookLocation, Location playerLocation, Loot loot) {
|
||||
BlockConfig config = blockConfigMap.get(loot.getID());
|
||||
if (config == null) {
|
||||
LogUtils.warn("Block: " + loot.getID() + " doesn't exist.");
|
||||
return;
|
||||
}
|
||||
String blockID = config.getBlockID();
|
||||
BlockData blockData;
|
||||
if (blockID.contains(":")) {
|
||||
String[] split = blockID.split(":", 2);
|
||||
String lib = split[0];
|
||||
String id = split[1];
|
||||
blockData = blockLibraryMap.get(lib).getBlockData(player, id, config.getDataModifier());
|
||||
} else {
|
||||
blockData = blockLibraryMap.get("vanilla").getBlockData(player, blockID, config.getDataModifier());
|
||||
}
|
||||
FallingBlock fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData);
|
||||
fallingBlock.getPersistentDataContainer().set(
|
||||
Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())),
|
||||
PersistentDataType.STRING,
|
||||
loot.getID() + ";" + player.getName()
|
||||
);
|
||||
fallingBlock.setDropItem(false);
|
||||
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1);
|
||||
vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector());
|
||||
fallingBlock.setVelocity(vector);
|
||||
}
|
||||
|
||||
private void registerDirectional() {
|
||||
this.registerBlockDataModifierBuilder("directional", (args) -> {
|
||||
boolean arg = (boolean) args;
|
||||
return (player, blockData) -> {
|
||||
if (arg && blockData instanceof Directional directional) {
|
||||
directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0,6)]);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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 (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));
|
||||
}
|
||||
}
|
||||
return (player, blockState) -> {
|
||||
LinkedList<Integer> unused = new LinkedList<>();
|
||||
for (int i = 0; i < 27; i++) {
|
||||
unused.add(i);
|
||||
}
|
||||
Collections.shuffle(unused);
|
||||
if (blockState instanceof Chest chest) {
|
||||
for (Tuple<Double, String, Pair<Integer, Integer>> tuple : tempChanceList) {
|
||||
ItemStack itemStack = plugin.getItemManager().buildAnyItemByID(player, tuple.getMid());
|
||||
itemStack.setAmount(ThreadLocalRandom.current().nextInt(tuple.getRight().left(), tuple.getRight().right() + 1));
|
||||
if (tuple.getLeft() > Math.random()) {
|
||||
chest.getBlockInventory().setItem(unused.pop(), itemStack);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (blockState instanceof Barrel barrel) {
|
||||
for (Tuple<Double, String, Pair<Integer, Integer>> tuple : tempChanceList) {
|
||||
ItemStack itemStack = plugin.getItemManager().buildAnyItemByID(player, tuple.getMid());
|
||||
itemStack.setAmount(ThreadLocalRandom.current().nextInt(tuple.getRight().left(), tuple.getRight().right() + 1));
|
||||
if (tuple.getLeft() > Math.random()) {
|
||||
barrel.getInventory().setItem(unused.pop(), itemStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
LogUtils.warn("Invalid property format found at block storage.");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockLands(EntityChangeBlockEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
String temp = event.getEntity().getPersistentDataContainer().get(
|
||||
Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())),
|
||||
PersistentDataType.STRING
|
||||
);
|
||||
if (temp == null) return;
|
||||
String[] split = temp.split(";");
|
||||
BlockConfig blockConfig = blockConfigMap.get(split[0]);
|
||||
if (blockConfig == null) return;
|
||||
Player player = Bukkit.getPlayer(split[1]);
|
||||
if (player == null) {
|
||||
event.getEntity().remove();
|
||||
event.getBlock().setType(Material.AIR);
|
||||
return;
|
||||
}
|
||||
Location location = event.getBlock().getLocation();
|
||||
plugin.getScheduler().runTaskSyncLater(() -> {
|
||||
BlockState state = location.getBlock().getState();
|
||||
for (BlockStateModifier modifier : blockConfig.getStateModifierList()) {
|
||||
modifier.apply(player, state);
|
||||
}
|
||||
}, location, 50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package net.momirealms.customfishing.mechanic.competition;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfig;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.mechanic.competition.actionbar.ActionBarManager;
|
||||
import net.momirealms.customfishing.mechanic.competition.bossbar.BossBarManager;
|
||||
import net.momirealms.customfishing.mechanic.competition.ranking.LocalRankingImpl;
|
||||
import net.momirealms.customfishing.mechanic.competition.ranking.RedisRankingImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Competition implements FishingCompetition {
|
||||
|
||||
private final CompetitionConfig config;
|
||||
private CancellableTask competitionTimerTask;
|
||||
private final CompetitionGoal goal;
|
||||
private final ConcurrentHashMap<String, String> publicPlaceholders;
|
||||
private final Ranking ranking;
|
||||
private float progress;
|
||||
private long remainingTime;
|
||||
private long startTime;
|
||||
private BossBarManager bossBarManager;
|
||||
private ActionBarManager actionBarManager;
|
||||
|
||||
public Competition(CompetitionConfig config) {
|
||||
this.config = config;
|
||||
this.goal = config.getGoal() == CompetitionGoal.RANDOM ? CompetitionGoal.getRandom() : config.getGoal();
|
||||
if (Config.redisRanking) this.ranking = new RedisRankingImpl();
|
||||
else this.ranking = new LocalRankingImpl();
|
||||
this.publicPlaceholders = new ConcurrentHashMap<>();
|
||||
this.publicPlaceholders.put("{goal}", getCompetitionLocale(goal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.progress = 1;
|
||||
this.remainingTime = config.getDuration();
|
||||
this.startTime = Instant.now().getEpochSecond();
|
||||
this.updatePublicPlaceholders();
|
||||
|
||||
this.arrangeTimerTask();
|
||||
if (config.getBossBarConfig() != null) {
|
||||
this.bossBarManager = new BossBarManager(config.getBossBarConfig(), this);
|
||||
this.bossBarManager.load();
|
||||
}
|
||||
if (config.getActionBarConfig() != null) {
|
||||
this.actionBarManager = new ActionBarManager(config.getActionBarConfig(), this);
|
||||
this.actionBarManager.load();
|
||||
}
|
||||
|
||||
Action[] actions = config.getStartActions();
|
||||
if (actions != null) {
|
||||
Condition condition = new Condition();
|
||||
for (Action action : actions) {
|
||||
action.trigger(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void arrangeTimerTask() {
|
||||
this.competitionTimerTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> {
|
||||
if (decreaseTime()) {
|
||||
end();
|
||||
return;
|
||||
}
|
||||
updatePublicPlaceholders();
|
||||
}, 1, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void updatePublicPlaceholders() {
|
||||
for (int i = 1; i < Config.placeholderLimit + 1; i++) {
|
||||
int finalI = i;
|
||||
Optional.ofNullable(ranking.getPlayerAt(i)).ifPresentOrElse(player -> {
|
||||
publicPlaceholders.put("{" + finalI + "_player}", player);
|
||||
publicPlaceholders.put("{" + finalI + "_score}", String.format("%.2f", ranking.getScoreAt(finalI)));
|
||||
}, () -> {
|
||||
publicPlaceholders.put("{" + finalI + "_player}", Locale.MSG_No_Player);
|
||||
publicPlaceholders.put("{" + finalI + "_score}", Locale.MSG_No_Score);
|
||||
});
|
||||
}
|
||||
publicPlaceholders.put("{hour}", String.valueOf(remainingTime / 3600));
|
||||
publicPlaceholders.put("{minute}", String.valueOf((remainingTime % 3600) / 60));
|
||||
publicPlaceholders.put("{second}", String.valueOf(remainingTime % 60));
|
||||
publicPlaceholders.put("{time}", String.valueOf(remainingTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel();
|
||||
if (this.bossBarManager != null) this.bossBarManager.unload();
|
||||
if (this.actionBarManager != null) this.actionBarManager.unload();
|
||||
this.ranking.clear();
|
||||
this.remainingTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
// mark it as ended
|
||||
this.remainingTime = 0;
|
||||
|
||||
// cancel some sub tasks
|
||||
if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel();
|
||||
if (this.bossBarManager != null) this.bossBarManager.unload();
|
||||
if (this.actionBarManager != null) this.actionBarManager.unload();
|
||||
|
||||
// give prizes
|
||||
HashMap<String, Action[]> rewardsMap = config.getRewards();
|
||||
if (ranking.getSize() != 0 && rewardsMap != null) {
|
||||
Iterator<Pair<String, Double>> iterator = ranking.getIterator();
|
||||
int i = 1;
|
||||
while (iterator.hasNext()) {
|
||||
Pair<String, Double> competitionPlayer = iterator.next();
|
||||
this.publicPlaceholders.put("{" + i + "_player}", competitionPlayer.left());
|
||||
this.publicPlaceholders.put("{" + i + "_score}", String.format("%.2f", competitionPlayer.right()));
|
||||
if (i < rewardsMap.size()) {
|
||||
Player player = Bukkit.getPlayer(competitionPlayer.left());
|
||||
if (player != null)
|
||||
for (Action action : rewardsMap.get(String.valueOf(i)))
|
||||
action.trigger(new Condition(player));
|
||||
i++;
|
||||
} else {
|
||||
Action[] actions = rewardsMap.get("participation");
|
||||
if (actions != null) {
|
||||
iterator.forEachRemaining(playerName -> {
|
||||
Player player = Bukkit.getPlayer(competitionPlayer.left());
|
||||
if (player != null)
|
||||
for (Action action : actions)
|
||||
action.trigger(new Condition(player));
|
||||
});
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do end actions
|
||||
Action[] actions = config.getEndActions();
|
||||
if (actions != null) {
|
||||
Condition condition = new Condition(new HashMap<>(publicPlaceholders));
|
||||
for (Action action : actions) {
|
||||
action.trigger(condition);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 seconds delay for other servers to read the redis data
|
||||
CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(this.ranking::clear, 1500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnGoing() {
|
||||
return remainingTime > 0;
|
||||
}
|
||||
|
||||
private boolean decreaseTime() {
|
||||
long current = Instant.now().getEpochSecond();
|
||||
int duration = config.getDuration();
|
||||
remainingTime = duration - (current - startTime);
|
||||
progress = (float) remainingTime / duration;
|
||||
return remainingTime <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshData(Player player, double score, boolean doubleScore) {
|
||||
// if player join for the first time, trigger join actions
|
||||
if (!hasPlayerJoined(player)) {
|
||||
Action[] actions = config.getJoinActions();
|
||||
if (actions != null) {
|
||||
Condition condition = new Condition(player);
|
||||
for (Action action : actions) {
|
||||
action.trigger(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// show competition info
|
||||
if (this.bossBarManager != null) this.bossBarManager.showBossBarTo(player);
|
||||
if (this.actionBarManager != null) this.actionBarManager.showActionBarTo(player);
|
||||
|
||||
// refresh data
|
||||
switch (this.goal) {
|
||||
case CATCH_AMOUNT -> ranking.refreshData(player.getName(), doubleScore ? 2 : 1);
|
||||
case TOTAL_SIZE, TOTAL_SCORE -> ranking.refreshData(player.getName(), doubleScore ? 2 * score : score);
|
||||
case MAX_SIZE -> {
|
||||
if (score > ranking.getPlayerScore(player.getName())) {
|
||||
ranking.setData(player.getName(), score);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPlayerJoined(OfflinePlayer player) {
|
||||
return ranking.getPlayerRank(player.getName()) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingTime() {
|
||||
return remainingTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitionConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitionGoal getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ranking getRanking() {
|
||||
return ranking;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, String> getPublicPlaceholders() {
|
||||
return publicPlaceholders;
|
||||
}
|
||||
|
||||
private String getCompetitionLocale(CompetitionGoal goal) {
|
||||
switch (goal) {
|
||||
case MAX_SIZE -> {
|
||||
return Locale.MSG_Max_Size;
|
||||
}
|
||||
case CATCH_AMOUNT -> {
|
||||
return Locale.MSG_Catch_Amount;
|
||||
}
|
||||
case TOTAL_SCORE -> {
|
||||
return Locale.MSG_Total_Score;
|
||||
}
|
||||
case TOTAL_SIZE -> {
|
||||
return Locale.MSG_Total_Size;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package net.momirealms.customfishing.mechanic.competition;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.manager.CompetitionManager;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.*;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CompetitionManagerImpl implements CompetitionManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<CompetitionSchedule, CompetitionConfig> timeConfigMap;
|
||||
private final HashMap<String, CompetitionConfig> commandConfigMap;
|
||||
private Competition currentCompetition;
|
||||
private CancellableTask timerCheckTask;
|
||||
private int nextCompetitionSeconds;
|
||||
|
||||
public CompetitionManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.timeConfigMap = new HashMap<>();
|
||||
this.commandConfigMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
loadConfig();
|
||||
this.timerCheckTask = plugin.getScheduler().runTaskAsyncTimer(
|
||||
this::timerCheck,
|
||||
1,
|
||||
1,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
if (this.timerCheckTask != null && !this.timerCheckTask.isCancelled())
|
||||
this.timerCheckTask.cancel();
|
||||
this.commandConfigMap.clear();
|
||||
this.timeConfigMap.clear();
|
||||
if (currentCompetition != null && currentCompetition.isOnGoing())
|
||||
currentCompetition.end();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
if (this.timerCheckTask != null && !this.timerCheckTask.isCancelled())
|
||||
this.timerCheckTask.cancel();
|
||||
this.commandConfigMap.clear();
|
||||
this.timeConfigMap.clear();
|
||||
if (currentCompetition != null && currentCompetition.isOnGoing())
|
||||
currentCompetition.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllCompetitions() {
|
||||
return commandConfigMap.keySet();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void loadConfig() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("competitions")) {
|
||||
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()) {
|
||||
this.loadSingleFileCompetition(subFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSingleFileCompetition(File file) {
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
|
||||
CompetitionConfig.Builder builder = new CompetitionConfig.Builder(entry.getKey())
|
||||
.goal(CompetitionGoal.valueOf(section.getString("goal", "TOTAL_SCORE").toUpperCase(Locale.ENGLISH)))
|
||||
.minPlayers(section.getInt("min-players", 0))
|
||||
.duration(section.getInt("duration", 300))
|
||||
.rewards(getPrizeActions(section.getConfigurationSection("rewards")))
|
||||
.startActions(plugin.getActionManager().getActions(section.getConfigurationSection("start-actions")))
|
||||
.endActions(plugin.getActionManager().getActions(section.getConfigurationSection("end-actions")))
|
||||
.skipActions(plugin.getActionManager().getActions(section.getConfigurationSection("skip-actions")));
|
||||
|
||||
if (section.getBoolean("bossbar.enable", false)) {
|
||||
builder.bossbar(new BossBarConfig.Builder()
|
||||
.color(BarColor.valueOf(section.getString("bossbar.color", "WHITE").toUpperCase(Locale.ENGLISH)))
|
||||
.overlay(BossBarConfig.Overlay.valueOf(section.getString("bossbar.overlay", "PROGRESS").toUpperCase(Locale.ENGLISH)))
|
||||
.refreshRate(section.getInt("bossbar.refresh-rate", 20))
|
||||
.switchInterval(section.getInt("bossbar.switch-interval", 200))
|
||||
.showToAll(!section.getBoolean("bossbar.only-show-to-participants", true))
|
||||
.text(section.getStringList("bossbar.text").toArray(new String[0]))
|
||||
.build());
|
||||
}
|
||||
|
||||
if (section.getBoolean("actionbar.enable", false)) {
|
||||
builder.actionbar(new ActionBarConfig.Builder()
|
||||
.refreshRate(section.getInt("actionbar.refresh-rate", 5))
|
||||
.switchInterval(section.getInt("actionbar.switch-interval", 200))
|
||||
.showToAll(!section.getBoolean("actionbar.only-show-to-participants", true))
|
||||
.text(section.getStringList("actionbar.text").toArray(new String[0]))
|
||||
.build());
|
||||
}
|
||||
|
||||
CompetitionConfig competitionConfig = builder.build();
|
||||
List<Pair<Integer, Integer>> timePairs = section.getStringList("start-time")
|
||||
.stream().map(this::getTimePair).toList();
|
||||
List<Integer> weekdays = section.getIntegerList("start-weekday");
|
||||
if (weekdays.size() == 0) {
|
||||
weekdays.addAll(List.of(1,2,3,4,5,6,7));
|
||||
}
|
||||
for (Integer weekday : weekdays) {
|
||||
for (Pair<Integer, Integer> timePair : timePairs) {
|
||||
CompetitionSchedule schedule = new CompetitionSchedule(weekday, timePair.left(), timePair.right(), 0);
|
||||
timeConfigMap.put(schedule, competitionConfig);
|
||||
}
|
||||
}
|
||||
commandConfigMap.put(entry.getKey(), competitionConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HashMap<String, Action[]> getPrizeActions(ConfigurationSection section) {
|
||||
HashMap<String, Action[]> map = new HashMap<>();
|
||||
if (section == null) return map;
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
map.put(entry.getKey(), plugin.getActionManager().getActions(innerSection));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getTimePair(String time) {
|
||||
String[] split = time.split(":");
|
||||
return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
|
||||
}
|
||||
|
||||
public void timerCheck() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
CompetitionSchedule competitionSchedule = new CompetitionSchedule(
|
||||
now.getDayOfWeek().getValue(),
|
||||
now.getHour(),
|
||||
now.getMinute(),
|
||||
now.getSecond()
|
||||
);
|
||||
int seconds = competitionSchedule.getTotalSeconds();
|
||||
int nextCompetitionTime = 7 * 24 * 60 * 60;
|
||||
for (CompetitionSchedule schedule : timeConfigMap.keySet()) {
|
||||
nextCompetitionTime = Math.min(nextCompetitionTime, schedule.getTimeDelta(seconds));
|
||||
}
|
||||
this.nextCompetitionSeconds = nextCompetitionTime;
|
||||
CompetitionConfig config = timeConfigMap.get(competitionSchedule);
|
||||
if (config != null) {
|
||||
startCompetition(config, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCompetition(String competition, boolean force, boolean allServer) {
|
||||
CompetitionConfig config = commandConfigMap.get(competition);
|
||||
if (config == null) {
|
||||
LogUtils.warn("Competition " + competition + " doesn't exist.");
|
||||
return;
|
||||
}
|
||||
startCompetition(config, force, allServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FishingCompetition getOnGoingCompetition() {
|
||||
if (currentCompetition == null) return null;
|
||||
return currentCompetition.isOnGoing() ? currentCompetition : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCompetition(CompetitionConfig config, boolean force, boolean allServer) {
|
||||
if (!force)
|
||||
this.getPlayerCount().thenAccept(count -> {
|
||||
if (count < config.getMinPlayers()) {
|
||||
var actions = config.getSkipActions();
|
||||
if (actions != null)
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
for (Action action : actions) {
|
||||
action.trigger(new Condition(player));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
start(config);
|
||||
});
|
||||
else if (!allServer) {
|
||||
start(config);
|
||||
} else {
|
||||
RedisManager.getInstance().sendRedisMessage("cf_competition", "start;" + config.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void start(CompetitionConfig config) {
|
||||
if (getOnGoingCompetition() != null) {
|
||||
currentCompetition.end();
|
||||
plugin.getScheduler().runTaskAsyncLater(() -> {
|
||||
this.currentCompetition = new Competition(config);
|
||||
this.currentCompetition.start();
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
} else {
|
||||
this.currentCompetition = new Competition(config);
|
||||
this.currentCompetition.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextCompetitionSeconds() {
|
||||
return nextCompetitionSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getPlayerCount() {
|
||||
if (!Config.redisRanking) {
|
||||
return CompletableFuture.completedFuture(Bukkit.getOnlinePlayers().size());
|
||||
} else {
|
||||
return plugin.getStorageManager().getRedisPlayerCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CompetitionConfig getConfig(String key) {
|
||||
return commandConfigMap.get(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.momirealms.customfishing.mechanic.competition;
|
||||
|
||||
public class CompetitionSchedule {
|
||||
|
||||
private final int weekday;
|
||||
private final int hour;
|
||||
private final int minute;
|
||||
private final int second;
|
||||
|
||||
public CompetitionSchedule(int weekday, int hour, int minute, int second) {
|
||||
this.weekday = weekday;
|
||||
this.hour = hour;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public int getWeekday() {
|
||||
return weekday;
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return hour;
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return minute;
|
||||
}
|
||||
|
||||
public int getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
public int getTotalSeconds() {
|
||||
return second +
|
||||
minute * 60 +
|
||||
hour * 60 * 60 +
|
||||
weekday * 24 * 60 * 60;
|
||||
}
|
||||
|
||||
public int getTimeDelta(int totalSeconds) {
|
||||
int thisSeconds = getTotalSeconds();
|
||||
if (thisSeconds >= totalSeconds) {
|
||||
return thisSeconds - totalSeconds;
|
||||
} else {
|
||||
return (7 * 24 * 60 * 60) - (totalSeconds - thisSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 7;
|
||||
int result = 1;
|
||||
result = prime * result + weekday;
|
||||
result = prime * result + hour;
|
||||
result = prime * result + minute;
|
||||
result = prime * result + second;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CompetitionSchedule other = (CompetitionSchedule) obj;
|
||||
if (weekday != other.weekday)
|
||||
return false;
|
||||
if (hour != other.hour)
|
||||
return false;
|
||||
if (minute != other.minute)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.mechanic.competition.actionbar;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.ActionBarConfig;
|
||||
import net.momirealms.customfishing.mechanic.competition.Competition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ActionBarManager implements Listener {
|
||||
|
||||
private static final ConcurrentHashMap<UUID, ActionBarSender> senderMap = new ConcurrentHashMap<>();
|
||||
private final ActionBarConfig actionBarConfig;
|
||||
private final Competition competition;
|
||||
|
||||
public ActionBarManager(ActionBarConfig actionBarConfig, Competition competition) {
|
||||
this.actionBarConfig = actionBarConfig;
|
||||
this.competition = competition;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.getInstance());
|
||||
if (actionBarConfig.isShowToAll()) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
ActionBarSender sender = new ActionBarSender(player, actionBarConfig, competition);
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HandlerList.unregisterAll(this);
|
||||
for (ActionBarSender ActionBarSender : senderMap.values()) {
|
||||
ActionBarSender.hide();
|
||||
}
|
||||
senderMap.clear();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
ActionBarSender sender = senderMap.remove(player.getUniqueId());
|
||||
if (sender != null) {
|
||||
if (sender.isVisible())
|
||||
sender.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
CustomFishingPlugin.getInstance().getScheduler().runTaskAsyncLater(() -> {
|
||||
boolean hasJoined = competition.hasPlayerJoined(player);
|
||||
if ((hasJoined || actionBarConfig.isShowToAll())
|
||||
&& !senderMap.containsKey(player.getUniqueId())) {
|
||||
ActionBarSender sender = new ActionBarSender(player, actionBarConfig, competition);
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void showActionBarTo(Player player) {
|
||||
ActionBarSender sender = senderMap.get(player.getUniqueId());
|
||||
if (sender == null) {
|
||||
sender = new ActionBarSender(player, actionBarConfig, competition);
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.mechanic.competition.actionbar;
|
||||
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.ActionBarConfig;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.mechanic.competition.Competition;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import net.momirealms.customfishing.util.DynamicText;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ActionBarSender {
|
||||
|
||||
private final Player player;
|
||||
private int refreshTimer;
|
||||
private int switchTimer;
|
||||
private int counter;
|
||||
private final DynamicText[] texts;
|
||||
private CancellableTask senderTask;
|
||||
private final ActionBarConfig config;
|
||||
private boolean isShown;
|
||||
private final Competition competition;
|
||||
private final HashMap<String, String> privatePlaceholders;
|
||||
|
||||
public ActionBarSender(Player player, ActionBarConfig config, Competition competition) {
|
||||
this.player = player;
|
||||
this.config = config;
|
||||
this.isShown = false;
|
||||
this.competition = competition;
|
||||
this.privatePlaceholders = new HashMap<>();
|
||||
this.privatePlaceholders.put("{player}", player.getName());
|
||||
this.updatePrivatePlaceholders();
|
||||
|
||||
String[] str = config.getTexts();
|
||||
texts = new DynamicText[str.length];
|
||||
for (int i = 0; i < str.length; i++) {
|
||||
texts[i] = new DynamicText(player, str[i]);
|
||||
texts[i].update(privatePlaceholders);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void updatePrivatePlaceholders() {
|
||||
this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName())));
|
||||
int rank = competition.getRanking().getPlayerRank(player.getName());
|
||||
this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : Locale.MSG_No_Rank);
|
||||
this.privatePlaceholders.putAll(competition.getPublicPlaceholders());
|
||||
}
|
||||
|
||||
public void show() {
|
||||
this.isShown = true;
|
||||
senderTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> {
|
||||
switchTimer++;
|
||||
if (switchTimer > config.getSwitchInterval()) {
|
||||
switchTimer = 0;
|
||||
counter++;
|
||||
}
|
||||
if (refreshTimer < config.getRefreshRate()){
|
||||
refreshTimer++;
|
||||
} else {
|
||||
refreshTimer = 0;
|
||||
DynamicText text = texts[counter % (texts.length)];
|
||||
updatePrivatePlaceholders();
|
||||
text.update(privatePlaceholders);
|
||||
AdventureManagerImpl.getInstance().sendActionbar(
|
||||
player,
|
||||
AdventureManagerImpl.getInstance().legacyToMiniMessage(text.getLatestValue())
|
||||
);
|
||||
}
|
||||
}, 50, 50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (senderTask != null && !senderTask.isCancelled())
|
||||
senderTask.cancel();
|
||||
this.isShown = false;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return this.isShown;
|
||||
}
|
||||
|
||||
public ActionBarConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.mechanic.competition.bossbar;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.BossBarConfig;
|
||||
import net.momirealms.customfishing.mechanic.competition.Competition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BossBarManager implements Listener {
|
||||
|
||||
private static final ConcurrentHashMap<UUID, BossBarSender> senderMap = new ConcurrentHashMap<>();
|
||||
private final BossBarConfig bossBarConfig;
|
||||
private final Competition competition;
|
||||
|
||||
public BossBarManager(BossBarConfig bossBarConfig, Competition competition) {
|
||||
this.bossBarConfig = bossBarConfig;
|
||||
this.competition = competition;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.getInstance());
|
||||
if (bossBarConfig.isShowToAll()) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
BossBarSender sender = new BossBarSender(player, bossBarConfig, competition);
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HandlerList.unregisterAll(this);
|
||||
for (BossBarSender bossBarSender : senderMap.values()) {
|
||||
bossBarSender.hide();
|
||||
}
|
||||
senderMap.clear();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
BossBarSender sender = senderMap.remove(player.getUniqueId());
|
||||
if (sender != null) {
|
||||
if (sender.isVisible())
|
||||
sender.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
CustomFishingPlugin.getInstance().getScheduler().runTaskAsyncLater(() -> {
|
||||
boolean hasJoined = competition.hasPlayerJoined(player);
|
||||
if ((hasJoined || bossBarConfig.isShowToAll())
|
||||
&& !senderMap.containsKey(player.getUniqueId())) {
|
||||
BossBarSender sender = new BossBarSender(player, bossBarConfig, competition);
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void showBossBarTo(Player player) {
|
||||
BossBarSender sender = senderMap.get(player.getUniqueId());
|
||||
if (sender == null) {
|
||||
sender = new BossBarSender(player, bossBarConfig, competition);
|
||||
senderMap.put(player.getUniqueId(), sender);
|
||||
}
|
||||
if (!sender.isVisible()) {
|
||||
sender.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.mechanic.competition.bossbar;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.InternalStructure;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.BossBarConfig;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.api.util.ReflectionUtils;
|
||||
import net.momirealms.customfishing.mechanic.competition.Competition;
|
||||
import net.momirealms.customfishing.setting.Locale;
|
||||
import net.momirealms.customfishing.util.DynamicText;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BossBarSender {
|
||||
|
||||
private final Player player;
|
||||
private int refreshTimer;
|
||||
private int switchTimer;
|
||||
private int counter;
|
||||
private final DynamicText[] texts;
|
||||
private CancellableTask senderTask;
|
||||
private final UUID uuid;
|
||||
private final BossBarConfig config;
|
||||
private boolean isShown;
|
||||
private final Competition competition;
|
||||
private final HashMap<String, String> privatePlaceholders;
|
||||
|
||||
public BossBarSender(Player player, BossBarConfig config, Competition competition) {
|
||||
this.player = player;
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.config = config;
|
||||
this.isShown = false;
|
||||
this.competition = competition;
|
||||
this.privatePlaceholders = new HashMap<>();
|
||||
this.privatePlaceholders.put("{player}", player.getName());
|
||||
this.updatePrivatePlaceholders();
|
||||
|
||||
String[] str = config.getTexts();
|
||||
texts = new DynamicText[str.length];
|
||||
for (int i = 0; i < str.length; i++) {
|
||||
texts[i] = new DynamicText(player, str[i]);
|
||||
texts[i].update(privatePlaceholders);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void updatePrivatePlaceholders() {
|
||||
this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName())));
|
||||
int rank = competition.getRanking().getPlayerRank(player.getName());
|
||||
this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : Locale.MSG_No_Rank);
|
||||
this.privatePlaceholders.putAll(competition.getPublicPlaceholders());
|
||||
}
|
||||
|
||||
public void show() {
|
||||
this.isShown = true;
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getCreatePacket());
|
||||
senderTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> {
|
||||
switchTimer++;
|
||||
if (switchTimer > config.getSwitchInterval()) {
|
||||
switchTimer = 0;
|
||||
counter++;
|
||||
}
|
||||
if (refreshTimer < config.getRefreshRate()){
|
||||
refreshTimer++;
|
||||
} else {
|
||||
refreshTimer = 0;
|
||||
DynamicText text = texts[counter % (texts.length)];
|
||||
updatePrivatePlaceholders();
|
||||
if (text.update(privatePlaceholders)) {
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getUpdatePacket(text));
|
||||
}
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getProgressPacket());
|
||||
}
|
||||
}, 50, 50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return this.isShown;
|
||||
}
|
||||
|
||||
public BossBarConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getRemovePacket());
|
||||
if (senderTask != null && !senderTask.isCancelled()) senderTask.cancel();
|
||||
this.isShown = false;
|
||||
}
|
||||
|
||||
private PacketContainer getUpdatePacket(DynamicText text) {
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
|
||||
packet.getModifier().write(0, uuid);
|
||||
try {
|
||||
Object chatComponent = ReflectionUtils.iChatComponentMethod.invoke(null,
|
||||
GsonComponentSerializer.gson().serialize(
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(
|
||||
AdventureManagerImpl.getInstance().legacyToMiniMessage(text.getLatestValue())
|
||||
)));
|
||||
Object updatePacket = ReflectionUtils.updateConstructor.newInstance(chatComponent);
|
||||
packet.getModifier().write(1, updatePacket);
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private PacketContainer getProgressPacket() {
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
|
||||
packet.getModifier().write(0, uuid);
|
||||
try {
|
||||
Object updatePacket = ReflectionUtils.progressConstructor.newInstance(competition.getProgress());
|
||||
packet.getModifier().write(1, updatePacket);
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private PacketContainer getCreatePacket() {
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
|
||||
packet.getModifier().write(0, uuid);
|
||||
InternalStructure internalStructure = packet.getStructures().read(1);
|
||||
internalStructure.getChatComponents().write(0, WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(texts[0].getLatestValue()))));
|
||||
internalStructure.getFloat().write(0, competition.getProgress());
|
||||
internalStructure.getEnumModifier(BarColor.class, 2).write(0, config.getColor());
|
||||
internalStructure.getEnumModifier(BossBarConfig.Overlay.class, 3).write(0, config.getOverlay());
|
||||
internalStructure.getModifier().write(4, false);
|
||||
internalStructure.getModifier().write(5, false);
|
||||
internalStructure.getModifier().write(6, false);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private PacketContainer getRemovePacket() {
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
|
||||
packet.getModifier().write(0, uuid);
|
||||
packet.getModifier().write(1, ReflectionUtils.removeBossBarPacket);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.mechanic.competition.ranking;
|
||||
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LocalRankingImpl implements Ranking {
|
||||
|
||||
private final Set<CompetitionPlayer> competitionPlayers;
|
||||
|
||||
public LocalRankingImpl() {
|
||||
competitionPlayers = Collections.synchronizedSet(new TreeSet<>());
|
||||
}
|
||||
|
||||
public void addPlayer(CompetitionPlayer competitionPlayer) {
|
||||
competitionPlayers.add(competitionPlayer);
|
||||
}
|
||||
|
||||
public void removePlayer(CompetitionPlayer competitionPlayer) {
|
||||
competitionPlayers.removeIf(e -> e.equals(competitionPlayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
competitionPlayers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitionPlayer getCompetitionPlayer(String player) {
|
||||
for (CompetitionPlayer competitionPlayer : competitionPlayers) {
|
||||
if (competitionPlayer.getPlayer().equals(player)) {
|
||||
return competitionPlayer;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Pair<String, Double>> getIterator() {
|
||||
List<Pair<String, Double>> players = new ArrayList<>();
|
||||
for (CompetitionPlayer competitionPlayer: competitionPlayers){
|
||||
players.add(Pair.of(competitionPlayer.getPlayer(), competitionPlayer.getScore()));
|
||||
}
|
||||
return players.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return competitionPlayers.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerRank(String player) {
|
||||
int index = 1;
|
||||
for (CompetitionPlayer competitionPlayer : competitionPlayers) {
|
||||
if (competitionPlayer.getPlayer().equals(player)) {
|
||||
return index;
|
||||
}else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPlayerScore(String player) {
|
||||
for (CompetitionPlayer competitionPlayer : competitionPlayers) {
|
||||
if (competitionPlayer.getPlayer().equals(player)) {
|
||||
return competitionPlayer.getScore();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerAt(int i) {
|
||||
int index = 1;
|
||||
for (CompetitionPlayer competitionPlayer : competitionPlayers) {
|
||||
if (index == i) {
|
||||
return competitionPlayer.getPlayer();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getScoreAt(int i) {
|
||||
int index = 1;
|
||||
for (CompetitionPlayer competitionPlayer : competitionPlayers) {
|
||||
if (index == i) {
|
||||
return competitionPlayer.getScore();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshData(String player, double score) {
|
||||
CompetitionPlayer competitionPlayer = getCompetitionPlayer(player);
|
||||
if (competitionPlayer != null) {
|
||||
removePlayer(competitionPlayer);
|
||||
competitionPlayer.addScore(score);
|
||||
addPlayer(competitionPlayer);
|
||||
} else {
|
||||
competitionPlayer = new CompetitionPlayer(player, score);
|
||||
addPlayer(competitionPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(String player, double score) {
|
||||
CompetitionPlayer competitionPlayer = getCompetitionPlayer(player);
|
||||
if (competitionPlayer != null) {
|
||||
removePlayer(competitionPlayer);
|
||||
competitionPlayer.setScore(score);
|
||||
addPlayer(competitionPlayer);
|
||||
} else {
|
||||
competitionPlayer = new CompetitionPlayer(player, score);
|
||||
addPlayer(competitionPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.mechanic.competition.ranking;
|
||||
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer;
|
||||
import net.momirealms.customfishing.api.mechanic.competition.Ranking;
|
||||
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.resps.Tuple;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class RedisRankingImpl implements Ranking {
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
jedis.zremrangeByRank("cf_competition",0,-1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitionPlayer getCompetitionPlayer(String player) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
Double score = jedis.zscore("cf_competition", player);
|
||||
if (score == null || score == 0) return null;
|
||||
return new CompetitionPlayer(player, Float.parseFloat(score.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Pair<String, Double>> getIterator() {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
List<Tuple> players = jedis.zrevrangeWithScores("cf_competition", 0, -1);
|
||||
return players.stream().map(it -> Pair.of(it.getElement(), it.getScore())).toList().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
long size = jedis.zcard("cf_competition");
|
||||
return (int) size;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerRank(String player) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
Long rank = jedis.zrevrank("cf_competition", player);
|
||||
if (rank == null)
|
||||
return -1;
|
||||
return (int) (rank + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPlayerScore(String player) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
Double rank = jedis.zscore("cf_competition", player);
|
||||
if (rank == null)
|
||||
return 0;
|
||||
return rank.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshData(String player, double score) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
jedis.zincrby("cf_competition", score, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(String player, double score) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
jedis.zadd("cf_competition", score, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerAt(int rank) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
List<String> player = jedis.zrevrange("cf_competition", rank - 1, rank -1);
|
||||
if (player == null || player.size() == 0) return null;
|
||||
return player.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getScoreAt(int rank) {
|
||||
try (Jedis jedis = RedisManager.getInstance().getJedis()) {
|
||||
List<Tuple> players = jedis.zrevrangeWithScores("cf_competition", rank - 1, rank -1);
|
||||
if (players == null || players.size() == 0) return 0;
|
||||
return players.get(0).getScore();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.momirealms.customfishing.mechanic.effect;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Key;
|
||||
import net.momirealms.customfishing.api.manager.EffectManager;
|
||||
import net.momirealms.customfishing.api.mechanic.effect.Effect;
|
||||
import net.momirealms.customfishing.api.mechanic.effect.FishingEffect;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class EffectManagerImpl implements EffectManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
|
||||
private final HashMap<Key, Effect> effectMap;
|
||||
|
||||
public EffectManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.effectMap = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerEffect(Key key, Effect effect) {
|
||||
if (effectMap.containsKey(key)) return false;
|
||||
this.effectMap.put(key, effect);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterEffect(Key key) {
|
||||
return this.effectMap.remove(key) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Effect getEffect(String namespace, String id) {
|
||||
return effectMap.get(Key.of(namespace, id));
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public void load() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("rods", "baits", "enchants", "utils", "totems")) {
|
||||
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()) {
|
||||
this.loadSingleFile(subFile, StringUtils.chop(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
effectMap.put(Key.of(namespace, value), getFishingEffectFromSection(section));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Effect getFishingEffectFromSection(ConfigurationSection section) {
|
||||
if (section == null) return getInitialEffect();
|
||||
return new FishingEffect.Builder()
|
||||
.lootWeightModifier(ConfigUtils.getModifiers(section.getStringList("weight")))
|
||||
.timeModifier(section.getDouble("hook-time", 1))
|
||||
.difficultyModifier(section.getDouble("difficulty", 0))
|
||||
.multipleLootChance(section.getDouble("multiple-loot"))
|
||||
.lavaFishing(section.getBoolean("lava-fishing", false))
|
||||
.scoreMultiplier(section.getDouble("score-bonus", 1))
|
||||
.sizeMultiplier(section.getDouble("size-bonus", 1))
|
||||
.gameTimeModifier(section.getDouble("game-time", 0))
|
||||
.requirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), true))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HashMap<Key, Effect> temp = new HashMap<>(effectMap);
|
||||
effectMap.clear();
|
||||
for (Map.Entry<Key, Effect> entry : temp.entrySet()) {
|
||||
if (entry.getValue().persist()) {
|
||||
effectMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effect getInitialEffect() {
|
||||
return new FishingEffect.Builder().build();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.effectMap.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.momirealms.customfishing.mechanic.fishing;
|
||||
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.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;
|
||||
|
||||
public class BaitAnimationTask implements Runnable {
|
||||
|
||||
private final CancellableTask cancellableTask;
|
||||
private final int entityID;
|
||||
private final Player player;
|
||||
private final FishHook fishHook;
|
||||
|
||||
public BaitAnimationTask(CustomFishingPlugin plugin, Player player, FishHook fishHook, ItemStack baitItem) {
|
||||
this.player = player;
|
||||
this.fishHook = fishHook;
|
||||
entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getSpawnPacket(entityID, fishHook.getLocation()));
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(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 {
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getVelocity(entityID, fishHook.getVelocity()));
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getTpPacket(entityID, fishHook.getLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelAnimation() {
|
||||
cancellableTask.cancel();
|
||||
CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getDestroyPacket(entityID));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
package net.momirealms.customfishing.mechanic.fishing;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerJumpEvent;
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.event.LavaFishingEvent;
|
||||
import net.momirealms.customfishing.api.event.RodCastEvent;
|
||||
import net.momirealms.customfishing.api.manager.FishingManager;
|
||||
import net.momirealms.customfishing.api.manager.RequirementManager;
|
||||
import net.momirealms.customfishing.api.mechanic.TempFishingState;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation;
|
||||
import net.momirealms.customfishing.api.mechanic.effect.Effect;
|
||||
import net.momirealms.customfishing.api.mechanic.game.Game;
|
||||
import net.momirealms.customfishing.api.mechanic.game.GameConfig;
|
||||
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.Modifier;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.api.util.WeightUtils;
|
||||
import net.momirealms.customfishing.mechanic.loot.LootManagerImpl;
|
||||
import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Statistic;
|
||||
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.inventory.InventoryPickupItemEvent;
|
||||
import org.bukkit.event.player.PlayerAttemptPickupItemEvent;
|
||||
import org.bukkit.event.player.PlayerFishEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class FishingManagerImpl implements Listener, FishingManager {
|
||||
|
||||
private final CustomFishingPluginImpl 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, ItemStack> vanillaLootMap;
|
||||
|
||||
public FishingManagerImpl(CustomFishingPluginImpl 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 (Config.eventPriority != EventPriority.MONITOR) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onFishHIGHEST(PlayerFishEvent event) {
|
||||
if (Config.eventPriority != EventPriority.HIGHEST) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onFishHIGH(PlayerFishEvent event) {
|
||||
if (Config.eventPriority != EventPriority.HIGH) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onFishNORMAL(PlayerFishEvent event) {
|
||||
if (Config.eventPriority != EventPriority.NORMAL) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onFishLOW(PlayerFishEvent event) {
|
||||
if (Config.eventPriority != EventPriority.LOW) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onFishLOWEST(PlayerFishEvent event) {
|
||||
if (Config.eventPriority != EventPriority.LOWEST) return;
|
||||
this.selectState(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPickUp(PlayerAttemptPickupItemEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
ItemStack itemStack = event.getItem().getItemStack();
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
if (!nbtItem.hasTag("owner")) return;
|
||||
if (!Objects.equals(nbtItem.getString("owner"), event.getPlayer().getName())) {
|
||||
event.setCancelled(true);
|
||||
} else {
|
||||
nbtItem.removeKey("owner");
|
||||
itemStack.setItemMeta(nbtItem.getItem().getItemMeta());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onMove(InventoryPickupItemEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
ItemStack itemStack = event.getItem().getItemStack();
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
if (!nbtItem.hasTag("owner")) return;
|
||||
nbtItem.removeKey("owner");
|
||||
itemStack.setItemMeta(nbtItem.getItem().getItemMeta());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
this.removeHook(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSwapHand(PlayerSwapHandItemsEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCastRod(PlayerFishEvent event) {
|
||||
var player = event.getPlayer();
|
||||
var fishingPreparation = new FishingPreparation(player, plugin);
|
||||
if (!fishingPreparation.canFish()) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
// Check mechanic requirements
|
||||
if (!RequirementManager.isRequirementsMet(
|
||||
RequirementManagerImpl.mechanicRequirements,
|
||||
fishingPreparation
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
// Merge rod/bait/util effects
|
||||
Effect initialEffect = plugin.getEffectManager().getInitialEffect();
|
||||
initialEffect
|
||||
.merge(fishingPreparation.getRodEffect())
|
||||
.merge(fishingPreparation.getBaitEffect());
|
||||
|
||||
for (Effect utilEffect : fishingPreparation.getUtilEffects()) {
|
||||
initialEffect.merge(utilEffect);
|
||||
}
|
||||
|
||||
// Apply enchants
|
||||
for (String enchant : plugin.getIntegrationManager().getEnchantments(fishingPreparation.getRodItemStack())) {
|
||||
Effect enchantEffect = plugin.getEffectManager().getEffect("enchant", enchant);
|
||||
if (enchantEffect != null && enchantEffect.canMerge(fishingPreparation)) {
|
||||
initialEffect.merge(enchantEffect);
|
||||
}
|
||||
}
|
||||
//TODO Apply totem effects
|
||||
|
||||
// Call custom event
|
||||
RodCastEvent rodCastEvent = new RodCastEvent(event, 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);
|
||||
fishHook.setMaxWaitTime((int) (fishHook.getMaxWaitTime() * initialEffect.getTimeModifier()));
|
||||
fishHook.setMinWaitTime((int) (fishHook.getMinWaitTime() * initialEffect.getTimeModifier()));
|
||||
// Reduce amount & Send animation
|
||||
var baitItem = fishingPreparation.getBaitItemStack();
|
||||
if (baitItem != null) {
|
||||
if (Config.enableBaitAnimation) {
|
||||
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));
|
||||
}
|
||||
|
||||
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.BOOLEAN
|
||||
) != 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();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO It's unsure if the hook would hook into other entities when playing a game
|
||||
// TODO But it should not affect the game result
|
||||
}
|
||||
|
||||
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.tempFishingStateMap.get(uuid);
|
||||
if (temp != null ) {
|
||||
var loot = temp.getLoot();
|
||||
if (loot.getID().equals("vanilla")) {
|
||||
// put vanilla loot in map
|
||||
this.vanillaLootMap.put(uuid, item.getItemStack());
|
||||
}
|
||||
if (!loot.disableGame()) {
|
||||
// start the game if the loot has a game
|
||||
event.setCancelled(true);
|
||||
startFishingGame(player, temp.getLoot(), temp.getEffect());
|
||||
} else {
|
||||
// If the game is disabled, then do success actions
|
||||
success(temp, event.getHook());
|
||||
// Cancel the event because loots can be multiple and unique
|
||||
event.setCancelled(true);
|
||||
event.getHook().remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config.vanillaMechanicIfNoLoot) {
|
||||
event.setCancelled(true);
|
||||
event.getHook().remove();
|
||||
}
|
||||
}
|
||||
|
||||
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 = gamingPlayerMap.get(uuid);
|
||||
if (gamingPlayer != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the loot's game is instant
|
||||
TempFishingState temp = tempFishingStateMap.get(uuid);
|
||||
if (temp != null) {
|
||||
var loot = temp.getLoot();
|
||||
|
||||
Action[] actions = loot.getActions(ActionTrigger.HOOK);
|
||||
if (actions != null)
|
||||
for (Action action : actions)
|
||||
action.trigger(temp.getPreparation());
|
||||
|
||||
if (loot.instanceGame() && !loot.disableGame()) {
|
||||
startFishingGame(player, loot, temp.getEffect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onReelIn(PlayerFishEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final UUID uuid = player.getUniqueId();
|
||||
|
||||
// If player is in game
|
||||
GamingPlayer gamingPlayer = gamingPlayerMap.get(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 = this.tempFishingStateMap.get(uuid);
|
||||
if (temp != null ) {
|
||||
if (!temp.getLoot().disableGame()) {
|
||||
event.setCancelled(true);
|
||||
startFishingGame(player, temp.getLoot(), temp.getEffect());
|
||||
} else {
|
||||
success(temp, event.getHook());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTempFishingState(Player player) {
|
||||
this.tempFishingStateMap.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
@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 = tempFishingStateMap.remove(uuid);
|
||||
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);
|
||||
|
||||
if (gamingPlayer.isSucceeded())
|
||||
success(tempFishingState, fishHook);
|
||||
else
|
||||
fail(tempFishingState);
|
||||
|
||||
// remove hook because some games don't depend on right clicks
|
||||
fishHook.remove();
|
||||
gamingPlayer.cancel();
|
||||
gamingPlayerMap.remove(uuid);
|
||||
}
|
||||
|
||||
public void fail(TempFishingState state) {
|
||||
var loot = state.getLoot();
|
||||
var fishingPreparation = state.getPreparation();
|
||||
|
||||
if (loot.getID().equals("vanilla")) {
|
||||
ItemStack itemStack = this.vanillaLootMap.remove(fishingPreparation.getPlayer().getUniqueId());
|
||||
if (itemStack != null) {
|
||||
fishingPreparation.insertArg("loot", "<lang:item.minecraft." + itemStack.getType().toString().toLowerCase() + ">");
|
||||
}
|
||||
}
|
||||
|
||||
Action[] globalActions = LootManagerImpl.globalLootProperties.getActions(ActionTrigger.FAILURE);
|
||||
if (globalActions != null)
|
||||
for (Action action : globalActions)
|
||||
action.trigger(fishingPreparation);
|
||||
|
||||
Action[] actions = loot.getActions(ActionTrigger.FAILURE);
|
||||
if (actions != null)
|
||||
for (Action action : actions)
|
||||
action.trigger(fishingPreparation);
|
||||
}
|
||||
|
||||
public void success(TempFishingState state, FishHook hook) {
|
||||
var loot = state.getLoot();
|
||||
var effect = state.getEffect();
|
||||
var fishingPreparation = state.getPreparation();
|
||||
var player = fishingPreparation.getPlayer();
|
||||
|
||||
int amount = (int) effect.getMultipleLootChance();
|
||||
amount += Math.random() < (effect.getMultipleLootChance() - amount) ? 2 : 1;
|
||||
|
||||
fishingPreparation.insertArg("{amount}", String.valueOf(amount));
|
||||
fishingPreparation.insertArg("{score}", String.format("%.2f", loot.getScore() * effect.getScoreMultiplier()));
|
||||
fishingPreparation.insertArg("{size-multiplier}", String.format("%.2f", effect.getSizeMultiplier()));
|
||||
fishingPreparation.insertArg("{x}", String.valueOf(hook.getLocation().getBlockX()));
|
||||
fishingPreparation.insertArg("{y}", String.valueOf(hook.getLocation().getBlockY()));
|
||||
fishingPreparation.insertArg("{z}", String.valueOf(hook.getLocation().getBlockZ()));
|
||||
fishingPreparation.insertArg("{loot}", loot.getID());
|
||||
fishingPreparation.insertArg("{nick}", loot.getNick());
|
||||
fishingPreparation.insertArg("{score}", String.format("%.2f", loot.getScore()));
|
||||
|
||||
switch (loot.getType()) {
|
||||
case LOOT -> {
|
||||
// build the items for multiple times instead of using setAmount() to make sure that each item is unique
|
||||
if (loot.getID().equals("vanilla")) {
|
||||
ItemStack itemStack = vanillaLootMap.remove(player.getUniqueId());
|
||||
if (itemStack != null) {
|
||||
fishingPreparation.insertArg("{loot}", "<lang:item.minecraft." + itemStack.getType().toString().toLowerCase() + ">");
|
||||
for (int i = 0; i < amount; i++) {
|
||||
plugin.getItemManager().dropItem(hook.getLocation(), player.getLocation(), itemStack.clone());
|
||||
doActions(loot, fishingPreparation, player);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < amount; i++) {
|
||||
plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot, fishingPreparation.getArgs());
|
||||
doActions(loot, fishingPreparation, player);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case MOB -> plugin.getMobManager().summonMob(hook.getLocation(), player.getLocation(), loot);
|
||||
case BLOCK -> plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot);
|
||||
}
|
||||
doActions(loot, fishingPreparation, player);
|
||||
}
|
||||
|
||||
private void doActions(Loot loot, FishingPreparation fishingPreparation, Player player) {
|
||||
Action[] globalActions = LootManagerImpl.globalLootProperties.getActions(ActionTrigger.SUCCESS);
|
||||
if (globalActions != null)
|
||||
for (Action action : globalActions)
|
||||
action.trigger(fishingPreparation);
|
||||
|
||||
Action[] actions = loot.getActions(ActionTrigger.SUCCESS);
|
||||
if (actions != null)
|
||||
for (Action action : actions)
|
||||
action.trigger(fishingPreparation);
|
||||
|
||||
player.setStatistic(
|
||||
Statistic.FISH_CAUGHT,
|
||||
player.getStatistic(Statistic.FISH_CAUGHT) + 1
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Loot getNextLoot(Effect initialEffect, FishingPreparation fishingPreparation) {
|
||||
HashMap<String, Double> lootWithWeight = plugin.getRequirementManager().getLootWithWeight(fishingPreparation);
|
||||
if (lootWithWeight.size() == 0) {
|
||||
LogUtils.warn(String.format("No Loot found at %s for Player %s!", fishingPreparation.getPlayer().getLocation(), fishingPreparation.getPlayer().getName()));
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Pair<String, Modifier> pair : initialEffect.getLootWeightModifier()) {
|
||||
double previous = lootWithWeight.getOrDefault(pair.left(), 0d);
|
||||
lootWithWeight.put(pair.left(), pair.right().modify(previous));
|
||||
}
|
||||
|
||||
String key = WeightUtils.getRandom(lootWithWeight);
|
||||
Loot loot = plugin.getLootManager().getLoot(key);
|
||||
if (loot == null) {
|
||||
LogUtils.warn(String.format("Loot %s doesn't exist!", key));
|
||||
return null;
|
||||
}
|
||||
return loot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startFishingGame(Player player, Loot loot, Effect effect) {
|
||||
GameConfig gameConfig = loot.getGameConfig();
|
||||
if (gameConfig == null) {
|
||||
gameConfig = plugin.getGameManager().getRandomGameConfig();
|
||||
}
|
||||
var gamePair = gameConfig.getRandomGame(effect);
|
||||
if (gamePair == null) {
|
||||
return;
|
||||
}
|
||||
startFishingGame(player, gamePair.right(), gamePair.left());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startFishingGame(Player player, GameSettings settings, Game game) {
|
||||
this.gamingPlayerMap.put(player.getUniqueId(), game.start(player, settings, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTempFishingState(Player player, TempFishingState tempFishingState) {
|
||||
tempFishingStateMap.put(player.getUniqueId(), tempFishingState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHookCheckTask(Player player) {
|
||||
hookCheckMap.remove(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package net.momirealms.customfishing.mechanic.fishing;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.event.LavaFishingEvent;
|
||||
import net.momirealms.customfishing.api.mechanic.TempFishingState;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation;
|
||||
import net.momirealms.customfishing.api.mechanic.effect.Effect;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Loot;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.util.ArmorStandUtils;
|
||||
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.EntityType;
|
||||
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;
|
||||
|
||||
public class HookCheckTimerTask implements Runnable {
|
||||
|
||||
private final FishingManagerImpl manager;
|
||||
private final CancellableTask hookMovementTask;
|
||||
private LavaEffectTask lavaFishingTask;
|
||||
private final FishHook fishHook;
|
||||
private final FishingPreparation fishingPreparation;
|
||||
private final Effect initialEffect;
|
||||
private final int lureLevel;
|
||||
private boolean firstTime;
|
||||
private boolean fishHooked;
|
||||
private boolean reserve;
|
||||
private int jumpTimer;
|
||||
private Entity hookedEntity;
|
||||
|
||||
public HookCheckTimerTask(
|
||||
FishingManagerImpl manager,
|
||||
FishHook fishHook,
|
||||
FishingPreparation fishingPreparation,
|
||||
Effect initialEffect
|
||||
) {
|
||||
this.manager = manager;
|
||||
this.fishHook = fishHook;
|
||||
this.initialEffect = initialEffect;
|
||||
this.fishingPreparation = fishingPreparation;
|
||||
this.hookMovementTask = CustomFishingPlugin.get().getScheduler().runTaskSyncTimer(this, fishHook.getLocation(), 1, 1);
|
||||
this.lureLevel = fishingPreparation.getRodItemStack().getEnchantmentLevel(Enchantment.LURE);
|
||||
this.firstTime = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (
|
||||
!fishHook.isValid()
|
||||
|| fishHook.isOnGround()
|
||||
|| (fishHook.getHookedEntity() != null && fishHook.getHookedEntity().getType() != EntityType.ARMOR_STAND)
|
||||
) {
|
||||
// This task would be cancelled when hook is not at a proper place
|
||||
// or player reels in before it goes into water or lava
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
if (fishHook.getLocation().getBlock().getType() == Material.LAVA) {
|
||||
// if player can fish in lava
|
||||
if (!initialEffect.canLavaFishing()) {
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
if (firstTime) {
|
||||
this.fishingPreparation.insertArg("in-lava", "true");
|
||||
if (Config.enableSplashAnimation)
|
||||
ArmorStandUtils.sendAnimationToPlayer(
|
||||
fishingPreparation.getPlayer(),
|
||||
fishHook.getLocation(),
|
||||
CustomFishingPlugin.get().getItemManager().build(null, "util", Config.lavaSplashItem),
|
||||
Config.splashAnimationTime
|
||||
);
|
||||
firstTime = false;
|
||||
this.setTempState();
|
||||
}
|
||||
// simulate fishing mechanic
|
||||
if (fishHooked) {
|
||||
jumpTimer++;
|
||||
if (jumpTimer < 4)
|
||||
return;
|
||||
jumpTimer = 0;
|
||||
fishHook.setVelocity(new Vector(0,0.24,0));
|
||||
return;
|
||||
}
|
||||
if (!reserve) {
|
||||
if (jumpTimer < 5) {
|
||||
jumpTimer++;
|
||||
fishHook.setVelocity(new Vector(0,0.2 - jumpTimer * 0.02,0));
|
||||
return;
|
||||
}
|
||||
reserve = true;
|
||||
this.startLavaFishingMechanic();
|
||||
this.makeHookStatic(fishHook.getLocation());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (fishHook.isInWater()) {
|
||||
// if the hook is in water
|
||||
// then cancel the task
|
||||
this.fishingPreparation.insertArg("in-lava", "false");
|
||||
if (Config.enableSplashAnimation)
|
||||
ArmorStandUtils.sendAnimationToPlayer(
|
||||
fishingPreparation.getPlayer(),
|
||||
fishHook.getLocation(),
|
||||
CustomFishingPlugin.get().getItemManager().build(null, "util", Config.waterSplashItem),
|
||||
Config.splashAnimationTime
|
||||
);
|
||||
this.destroy();
|
||||
this.setTempState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
this.cancelSubTask();
|
||||
this.removeTempEntity();
|
||||
this.hookMovementTask.cancel();
|
||||
this.manager.removeHookCheckTask(fishingPreparation.getPlayer());
|
||||
}
|
||||
|
||||
public void cancelSubTask() {
|
||||
if (lavaFishingTask != null && !lavaFishingTask.isCancelled()) {
|
||||
lavaFishingTask.cancel();
|
||||
lavaFishingTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setTempState() {
|
||||
Loot nextLoot = manager.getNextLoot(initialEffect, fishingPreparation);
|
||||
if (nextLoot == null)
|
||||
return;
|
||||
fishingPreparation.insertArg("loot", nextLoot.getNick());
|
||||
fishingPreparation.insertArg("id", nextLoot.getID());
|
||||
CustomFishingPlugin.get().getScheduler().runTaskAsync(() -> manager.setTempFishingState(fishingPreparation.getPlayer(), new TempFishingState(
|
||||
initialEffect,
|
||||
fishingPreparation,
|
||||
nextLoot
|
||||
)));
|
||||
}
|
||||
|
||||
public void removeTempEntity() {
|
||||
if (hookedEntity != null && !hookedEntity.isDead())
|
||||
hookedEntity.remove();
|
||||
}
|
||||
|
||||
private void startLavaFishingMechanic() {
|
||||
// get random time
|
||||
int random = ThreadLocalRandom.current().nextInt(Config.lavaMinTime, Config.lavaMaxTime);
|
||||
random -= lureLevel * 100;
|
||||
random *= initialEffect.getTimeModifier();
|
||||
random = Math.max(Config.lavaMinTime, random);
|
||||
|
||||
// lava effect task (Three seconds in advance)
|
||||
this.lavaFishingTask = new LavaEffectTask(
|
||||
this,
|
||||
fishHook.getLocation(),
|
||||
random - 3 * 20
|
||||
);
|
||||
}
|
||||
|
||||
public void getHooked() {
|
||||
LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(fishingPreparation.getPlayer(), LavaFishingEvent.State.BITE, fishHook);
|
||||
Bukkit.getPluginManager().callEvent(lavaFishingEvent);
|
||||
if (lavaFishingEvent.isCancelled()) {
|
||||
this.startLavaFishingMechanic();
|
||||
return;
|
||||
}
|
||||
|
||||
this.fishHooked = true;
|
||||
this.removeTempEntity();
|
||||
|
||||
AdventureManagerImpl.getInstance().sendSound(
|
||||
fishingPreparation.getPlayer(),
|
||||
Sound.Source.NEUTRAL,
|
||||
Key.key("minecraft:block.pointed_dripstone.drip_lava_into_cauldron"),
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
CustomFishingPlugin.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", CustomFishingPlugin.get())),
|
||||
PersistentDataType.BOOLEAN,
|
||||
true
|
||||
);
|
||||
});
|
||||
fishHook.setHookedEntity(hookedEntity);
|
||||
}
|
||||
|
||||
public boolean isFishHooked() {
|
||||
return fishHooked;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.mechanic.fishing;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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;
|
||||
|
||||
public LavaEffectTask(HookCheckTimerTask hookCheckTimerTask, Location loc, int delay) {
|
||||
this.hookCheckTimerTask = hookCheckTimerTask;
|
||||
this.startLoc = loc.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 = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, delay * 50L, 50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
timer++;
|
||||
if (timer > 60) {
|
||||
lavaTask.cancel();
|
||||
CustomFishingPlugin.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);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (lavaTask != null && !lavaTask.isCancelled())
|
||||
lavaTask.cancel();
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return lavaTask.isCancelled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package net.momirealms.customfishing.mechanic.game;
|
||||
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.GameManager;
|
||||
import net.momirealms.customfishing.api.mechanic.game.*;
|
||||
import net.momirealms.customfishing.api.util.FontUtils;
|
||||
import net.momirealms.customfishing.api.util.OffsetUtils;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GameManagerImpl implements GameManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, GameCreator> gameCreatorMap;
|
||||
private final HashMap<String, Game> gameMap;
|
||||
private final HashMap<String, GameConfig> gameConfigMap;
|
||||
|
||||
public GameManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.gameCreatorMap = new HashMap<>();
|
||||
this.gameMap = new HashMap<>();
|
||||
this.gameConfigMap = new HashMap<>();
|
||||
this.registerInbuiltGames();
|
||||
}
|
||||
|
||||
private void registerInbuiltGames() {
|
||||
this.registerAccurateClickGame();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadGamesFromPluginFolder();
|
||||
this.loadGameConfigs();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
this.gameMap.clear();
|
||||
this.gameConfigMap.clear();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
unload();
|
||||
this.gameCreatorMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerGameType(String type, GameCreator gameCreator) {
|
||||
if (gameCreatorMap.containsKey(type))
|
||||
return false;
|
||||
else
|
||||
gameCreatorMap.put(type, gameCreator);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterGameType(String type) {
|
||||
return gameCreatorMap.remove(type) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public GameCreator getGameCreator(String type) {
|
||||
return gameCreatorMap.get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Game getGame(String key) {
|
||||
return gameMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public GameConfig getGameConfig(String key) {
|
||||
return gameConfigMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Game getRandomGame() {
|
||||
Collection<Game> collection = gameMap.values();
|
||||
return (Game) collection.toArray()[ThreadLocalRandom.current().nextInt(collection.size())];
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameConfig getRandomGameConfig() {
|
||||
Collection<GameConfig> collection = gameConfigMap.values();
|
||||
return (GameConfig) collection.toArray()[ThreadLocalRandom.current().nextInt(collection.size())];
|
||||
}
|
||||
|
||||
public void loadGameConfigs() {
|
||||
YamlConfiguration config = plugin.getConfig("game-groups.yml");
|
||||
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
if (section.contains("groups")) {
|
||||
gameConfigMap.put(entry.getKey(), new GameGroups(ConfigUtils.getWeights(section.getStringList("groups"))));
|
||||
} else if (section.contains("games")) {
|
||||
var pair1 = ConfigUtils.splitStringIntegerArgs(section.getString("difficulty", "1~100"));
|
||||
var pair2 = ConfigUtils.splitStringIntegerArgs(section.getString("time", "10~20"));
|
||||
gameConfigMap.put(entry.getKey(),
|
||||
new GameGroup(ConfigUtils.getWeights(section.getStringList("games")))
|
||||
.difficulty(pair1.left(), pair1.right())
|
||||
.time(pair2.left(), pair2.right())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadGamesFromPluginFolder() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + "minigames");
|
||||
if (!typeFolder.exists()) {
|
||||
if (!typeFolder.mkdirs()) return;
|
||||
plugin.saveResource("contents" + File.separator + "minigames" + 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()) {
|
||||
loadSingleFile(subFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
GameCreator creator = this.getGameCreator(section.getString("game-type"));
|
||||
if (creator != null) {
|
||||
gameMap.put(entry.getKey(), creator.setArgs(section));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerAccurateClickGame() {
|
||||
this.registerGameType("accurate_click", (section -> {
|
||||
|
||||
Set<String> chances = Objects.requireNonNull(section.getConfigurationSection("success-rate-sections")).getKeys(false);
|
||||
var widthPerSection = section.getInt("arguments.width-per-section", 16);
|
||||
var successRate = new double[chances.size()];
|
||||
for(int i = 0; i < chances.size(); i++)
|
||||
successRate[i] = section.getDouble("success-rate-sections." + (i + 1));
|
||||
var totalWidth = chances.size() * widthPerSection - 1;
|
||||
var pointerOffset = section.getInt("arguments.pointer-offset");
|
||||
var pointerWidth = section.getInt("arguments.pointer-width");
|
||||
var title = section.getString("title");
|
||||
var font = section.getString("subtitle.font");
|
||||
var barImage = section.getString("subtitle.bar");
|
||||
var pointerImage = section.getString("subtitle.pointer");
|
||||
|
||||
return (player, settings, manager) -> new AbstractGamingPlayer(player, settings, manager) {
|
||||
|
||||
private int progress;
|
||||
private boolean face;
|
||||
|
||||
@Override
|
||||
public void arrangeTask() {
|
||||
var period = ((double) 10*(200-settings.getDifficulty()))/((double) (1+4*settings.getDifficulty()));
|
||||
this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(
|
||||
this,
|
||||
50,
|
||||
(long) period,
|
||||
TimeUnit.MILLISECONDS
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if (face) progress++;
|
||||
else progress--;
|
||||
if (progress > totalWidth) {
|
||||
face = !face;
|
||||
progress = 2 * totalWidth - progress;
|
||||
} else if (progress < 0) {
|
||||
face = !face;
|
||||
progress = -progress;
|
||||
}
|
||||
showUI();
|
||||
}
|
||||
|
||||
public void showUI() {
|
||||
String bar = FontUtils.surroundWithFont(barImage, font)
|
||||
+ OffsetUtils.getOffsetChars(pointerOffset + progress)
|
||||
+ FontUtils.surroundWithFont(pointerImage, font)
|
||||
+ OffsetUtils.getOffsetChars(totalWidth - progress - pointerWidth);
|
||||
AdventureManagerImpl.getInstance().sendTitle(player, title, bar,0,500,0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSucceeded() {
|
||||
int last = progress / widthPerSection;
|
||||
return (Math.random() < successRate[last]);
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
package net.momirealms.customfishing.mechanic.item;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.*;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.common.Key;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.manager.ItemManager;
|
||||
import net.momirealms.customfishing.api.mechanic.item.BuildableItem;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemBuilder;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemLibrary;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Loot;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.item.CustomFishingItemImpl;
|
||||
import net.momirealms.customfishing.compatibility.item.VanillaItemImpl;
|
||||
import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.util.NBTUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
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;
|
||||
|
||||
public class ItemManagerImpl implements ItemManager {
|
||||
|
||||
private static ItemManager instance;
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<Key, BuildableItem> buildableItemMap;
|
||||
private final HashMap<String, ItemLibrary> itemLibraryMap;
|
||||
|
||||
public ItemManagerImpl(CustomFishingPlugin plugin) {
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.itemLibraryMap = new LinkedHashMap<>();
|
||||
this.buildableItemMap = new HashMap<>();
|
||||
this.registerItemLibrary(new CustomFishingItemImpl());
|
||||
this.registerItemLibrary(new VanillaItemImpl());
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadItemsFromPluginFolder();
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(Bukkit.getConsoleSender(), "<white>Loaded <green>" + buildableItemMap.size() + " <white>items.");
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HashMap<Key, BuildableItem> tempMap = new HashMap<>(this.buildableItemMap);
|
||||
this.buildableItemMap.clear();
|
||||
for (Map.Entry<Key, BuildableItem> entry : tempMap.entrySet()) {
|
||||
if (entry.getValue().persist()) {
|
||||
tempMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Key> getAllItemsKey() {
|
||||
return buildableItemMap.keySet();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.buildableItemMap.clear();
|
||||
this.itemLibraryMap.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public void loadItemsFromPluginFolder() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("loots", "baits", "rods", "utils")) {
|
||||
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()) {
|
||||
this.loadSingleFile(subFile, StringUtils.chop(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (buildableItemMap.containsKey(key)) {
|
||||
LogUtils.severe("Duplicated item key found: " + key + ".");
|
||||
} else {
|
||||
buildableItemMap.put(key, getItemBuilder(section, namespace, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerCustomItem(String namespace, String value, BuildableItem buildableItem) {
|
||||
Key key = Key.of(namespace, value);
|
||||
if (buildableItemMap.containsKey(key)) return false;
|
||||
buildableItemMap.put(key, buildableItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterCustomItem(String namespace, String value) {
|
||||
return buildableItemMap.remove(Key.of(namespace, value)) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build(Player player, String namespace, String value) {
|
||||
return build(player, namespace, value, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build(Player player, String namespace, String value, Map<String, String> placeholders) {
|
||||
BuildableItem buildableItem = buildableItemMap.get(Key.of(namespace, value));
|
||||
if (buildableItem == null) return null;
|
||||
return buildableItem.build(player, placeholders);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack build(Player player, ItemBuilder builder) {
|
||||
return build(player, builder, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BuildableItem getBuildableItem(String namespace, String value) {
|
||||
return buildableItemMap.get(Key.of(namespace, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnyItemID(ItemStack itemStack) {
|
||||
for (String plugin : Config.itemDetectOrder) {
|
||||
ItemLibrary itemLibrary = itemLibraryMap.get(plugin);
|
||||
if (itemLibrary != null) {
|
||||
String id = itemLibrary.getItemID(itemStack);
|
||||
if (id != null) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// should not reach this because vanilla library would always work
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildAnyItemByID(Player player, String id) {
|
||||
if (id.contains(":")) {
|
||||
String[] split = id.split(":", 2);
|
||||
return itemLibraryMap.get(split[0]).buildItem(player, split[1]);
|
||||
} else {
|
||||
return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getItemID(ItemStack itemStack) {
|
||||
if (itemStack == null || itemStack.getType() == Material.AIR) return null;
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
NBTCompound cfCompound = nbtItem.getCompound("CustomFishing");
|
||||
if (cfCompound == null) return null;
|
||||
return cfCompound.getString("id");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CFBuilder getItemBuilder(ConfigurationSection section, String type, String id) {
|
||||
if (section == null) return null;
|
||||
String material = section.getString("material", "PAPER");
|
||||
CFBuilder itemCFBuilder;
|
||||
if (material.contains(":")) {
|
||||
String[] split = material.split(":", 2);
|
||||
itemCFBuilder = CFBuilder.of(split[0], split[1]);
|
||||
} else {
|
||||
itemCFBuilder = CFBuilder.of("vanilla", material.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
itemCFBuilder
|
||||
.amount(section.getInt("amount", 1))
|
||||
.stackable(section.getBoolean("stackable", true))
|
||||
.size(getSizePair(section.getString("size")))
|
||||
.price((float) section.getDouble("price.base"), (float) section.getDouble("price.bonus"))
|
||||
.customModelData(section.getInt("custom-model-data"))
|
||||
.nbt(section.getConfigurationSection("nbt"))
|
||||
.maxDurability(section.getInt("max-durability"))
|
||||
.itemFlag(section.getStringList("item-flags").stream().map(flag -> ItemFlag.valueOf(flag.toUpperCase())).toList())
|
||||
.enchantment(getEnchantmentPair(section.getConfigurationSection("enchantments")), false)
|
||||
.enchantment(getEnchantmentPair(section.getConfigurationSection("stored-enchantments")), true)
|
||||
.tag(section.getBoolean("tag", true), type, id)
|
||||
.randomDamage(section.getBoolean("random-durability", false))
|
||||
.unbreakable(section.getBoolean("unbreakable", false))
|
||||
.preventGrabbing(section.getBoolean("prevent-grabbing", false))
|
||||
.head(section.getString("head64"))
|
||||
.name(section.getString("display.name"))
|
||||
.lore(section.getStringList("display.lore"));
|
||||
return itemCFBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build(Player player, ItemBuilder builder, Map<String, String> placeholders) {
|
||||
ItemStack temp = itemLibraryMap.get(builder.getLibrary()).buildItem(player, builder.getId());
|
||||
temp.setAmount(builder.getAmount());
|
||||
NBTItem nbtItem = new NBTItem(temp);
|
||||
for (ItemBuilder.ItemPropertyEditor editor : builder.getEditors()) {
|
||||
editor.edit(player, nbtItem, placeholders);
|
||||
}
|
||||
return nbtItem.getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerItemLibrary(ItemLibrary itemLibrary) {
|
||||
if (itemLibraryMap.containsKey(itemLibrary.identification())) return false;
|
||||
itemLibraryMap.put(itemLibrary.identification(), itemLibrary);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unRegisterItemLibrary(ItemLibrary itemLibrary) {
|
||||
return itemLibraryMap.remove(itemLibrary.identification(), itemLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unRegisterItemLibrary(String itemLibrary) {
|
||||
return itemLibraryMap.remove(itemLibrary) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropItem(Player player, Location hookLocation, Location playerLocation, Loot loot, Map<String, String> args) {
|
||||
ItemStack item = build(player, "loot", loot.getID(), args);
|
||||
if (item == null) {
|
||||
LogUtils.warn(String.format("Item %s not exists", loot.getID()));
|
||||
return;
|
||||
}
|
||||
if (item.getType() == Material.AIR) {
|
||||
return;
|
||||
}
|
||||
Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, item);
|
||||
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105);
|
||||
vector = vector.setY((vector.getY() + 0.2) * 1.18);
|
||||
itemEntity.setVelocity(vector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack) {
|
||||
Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack);
|
||||
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105);
|
||||
vector = vector.setY((vector.getY() + 0.2) * 1.18);
|
||||
itemEntity.setVelocity(vector);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<Pair<String, Short>> getEnchantmentPair(ConfigurationSection section) {
|
||||
List<Pair<String, Short>> list = new ArrayList<>();
|
||||
if (section == null) return list;
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
list.add(Pair.of(entry.getKey(), (short) entry.getValue()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Pair<Float, Float> getSizePair(String size) {
|
||||
if (size == null) return null;
|
||||
String[] split = size.split("~", 2);
|
||||
return Pair.of(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
|
||||
}
|
||||
|
||||
public static class CFBuilder implements ItemBuilder, BuildableItem {
|
||||
|
||||
private final String library;
|
||||
private final String id;
|
||||
private int amount;
|
||||
private final LinkedHashMap<String, ItemPropertyEditor> editors;
|
||||
|
||||
public CFBuilder(String library, String id) {
|
||||
this.id = id;
|
||||
this.library = library;
|
||||
this.editors = new LinkedHashMap<>();
|
||||
this.amount = 1;
|
||||
}
|
||||
|
||||
public static CFBuilder of(String library, String id) {
|
||||
return new CFBuilder(library, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build(Player player, Map<String, String> placeholders) {
|
||||
return ItemManagerImpl.instance.build(player, this, placeholders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder customModelData(int value) {
|
||||
if (value == 0) return this;
|
||||
editors.put("custom-model-data", (player, nbtItem, placeholders) -> nbtItem.setInteger("CustomModelData", value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder name(String name) {
|
||||
if (name == null) return this;
|
||||
String replacedName = AdventureManagerImpl.getInstance().legacyToMiniMessage(name);
|
||||
editors.put("name", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound displayCompound = nbtItem.getOrCreateCompound("display");
|
||||
displayCompound.setString("Name", AdventureManagerImpl.getInstance().componentToJson(
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(
|
||||
"<!i>" + PlaceholderManagerImpl.getInstance().parse(player, replacedName, placeholders)
|
||||
)
|
||||
));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder amount(int amount) {
|
||||
this.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder tag(boolean tag, String type, String id) {
|
||||
editors.put("tag", (player, nbtItem, placeholders) -> {
|
||||
if (!tag) return;
|
||||
NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing");
|
||||
cfCompound.setString("type", type);
|
||||
cfCompound.setString("id", id);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder unbreakable(boolean unbreakable) {
|
||||
editors.put("unbreakable", (player, nbtItem, placeholders) -> {
|
||||
if (!unbreakable) return;
|
||||
nbtItem.setByte("Unbreakable", (byte) 1);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder lore(List<String> lore) {
|
||||
if (lore.size() == 0) return this;
|
||||
List<String> replacedList = lore.stream().map(s -> AdventureManagerImpl.getInstance().legacyToMiniMessage(s)).toList();
|
||||
editors.put("lore", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound displayCompound = nbtItem.getOrCreateCompound("display");
|
||||
NBTList<String> list = displayCompound.getStringList("Lore");
|
||||
list.clear();
|
||||
list.addAll(replacedList.stream().map(s -> AdventureManagerImpl.getInstance().componentToJson(
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(
|
||||
"<!i>" + PlaceholderManagerImpl.getInstance().parse(player, s, placeholders)
|
||||
)
|
||||
)).toList());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder nbt(Map<String, Object> nbt) {
|
||||
if (nbt.size() == 0) return this;
|
||||
editors.put("nbt", (player, nbtItem, placeholders) -> NBTUtils.setTagsFromBukkitYAML(nbtItem, nbt));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder nbt(ConfigurationSection section) {
|
||||
if (section == null) return this;
|
||||
editors.put("nbt", (player, nbtItem, placeholders) -> NBTUtils.setTagsFromBukkitYAML(nbtItem, section.getValues(false)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder itemFlag(List<ItemFlag> itemFlags) {
|
||||
if (itemFlags.size() == 0) return this;
|
||||
editors.put("item-flag", (player, nbtItem, placeholders) -> {
|
||||
int flag = 0;
|
||||
for (ItemFlag itemFlag : itemFlags) {
|
||||
flag = flag | 1 << itemFlag.ordinal();
|
||||
}
|
||||
nbtItem.setInteger("HideFlags", flag);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder enchantment(List<Pair<String, Short>> enchantments, boolean store) {
|
||||
if (enchantments.size() == 0) return this;
|
||||
editors.put("enchantment", (player, nbtItem, placeholders) -> {
|
||||
NBTCompoundList list = nbtItem.getCompoundList(store ? "StoredEnchantments" : "Enchantments");
|
||||
for (Pair<String, Short> pair : enchantments) {
|
||||
NBTCompound nbtCompound = list.addCompound();
|
||||
nbtCompound.setString("id", pair.left());
|
||||
nbtCompound.setShort("lvl", pair.right());
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder maxDurability(int max) {
|
||||
if (max == 0) return this;
|
||||
editors.put("durability", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing");
|
||||
cfCompound.setInteger("max_dur", max);
|
||||
cfCompound.setInteger("cur_dur", max);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder price(float base, float bonus) {
|
||||
editors.put("price", (player, nbtItem, placeholders) -> {
|
||||
if (base != 0) {
|
||||
placeholders.put("{base}", String.format("%.2f", base));
|
||||
}
|
||||
if (bonus != 0) {
|
||||
placeholders.put("{bonus}", String.format("%.2f", bonus));
|
||||
}
|
||||
float size = Float.parseFloat(placeholders.getOrDefault("{size}", "0"));
|
||||
double price = CustomFishingPlugin.get().getMarketManager().getPrice(
|
||||
base,
|
||||
bonus,
|
||||
size
|
||||
);
|
||||
nbtItem.setDouble("Price", price);
|
||||
placeholders.put("{price}", String.format("%.2f", price));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder size(Pair<Float, Float> size) {
|
||||
if (size == null) return this;
|
||||
editors.put("size", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing");
|
||||
float random = size.left() + ThreadLocalRandom.current().nextFloat(size.right() - size.left());
|
||||
float bonus = Float.parseFloat(placeholders.getOrDefault("size-multiplier", "1.0"));
|
||||
random *= bonus;
|
||||
cfCompound.setFloat("size", random);
|
||||
placeholders.put("{size}", String.format("%.2f", random));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder stackable(boolean stackable) {
|
||||
if (stackable) return this;
|
||||
editors.put("stackable", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing");
|
||||
cfCompound.setUUID("uuid", UUID.randomUUID());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder preventGrabbing(boolean prevent) {
|
||||
if (!prevent) return this;
|
||||
editors.put("grabbing", (player, nbtItem, placeholders) -> {
|
||||
nbtItem.setString("owner", placeholders.get("player"));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder head(String base64) {
|
||||
if (base64 == null) return this;
|
||||
editors.put("head", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound nbtCompound = nbtItem.addCompound("SkullOwner");
|
||||
nbtCompound.setUUID("Id", UUID.nameUUIDFromBytes(base64.substring(0,8).getBytes()));
|
||||
NBTListCompound texture = nbtCompound.addCompound("Properties").getCompoundList("textures").addCompound();
|
||||
texture.setString("Value", base64);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder randomDamage(boolean damage) {
|
||||
if (!damage) return this;
|
||||
editors.put("damage", (player, nbtItem, placeholders) -> {
|
||||
NBTCompound cfCompound = nbtItem.getCompound("CustomFishing");
|
||||
if (cfCompound != null) {
|
||||
int i = cfCompound.getInteger("max_dur");
|
||||
if (i != 0) {
|
||||
int dur = ThreadLocalRandom.current().nextInt(i);
|
||||
cfCompound.setInteger("cur_dur", dur);
|
||||
nbtItem.setInteger("Damage", (int) (nbtItem.getItem().getType().getMaxDurability() * ((double) dur / i)));
|
||||
}
|
||||
} else {
|
||||
nbtItem.setInteger("Damage", ThreadLocalRandom.current().nextInt(nbtItem.getItem().getType().getMaxDurability()));
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getLibrary() {
|
||||
return library;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ItemPropertyEditor> getEditors() {
|
||||
return editors.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder removeEditor(String type) {
|
||||
editors.remove(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemBuilder registerCustomEditor(String type, ItemPropertyEditor editor) {
|
||||
editors.put(type, editor);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
package net.momirealms.customfishing.mechanic.loot;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.LootManager;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
|
||||
import net.momirealms.customfishing.api.mechanic.game.GameConfig;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Loot;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.LootType;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class LootManagerImpl implements LootManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, Loot> lootMap;
|
||||
public static CFLoot globalLootProperties;
|
||||
private boolean disableStats;
|
||||
private boolean disableGames;
|
||||
private boolean instantGame;
|
||||
private boolean showInFinder;
|
||||
private String gameGroup;
|
||||
|
||||
public LootManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.lootMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadGlobalLootProperties();
|
||||
this.loadLootsFromPluginFolder();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
this.lootMap.clear();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
unload();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public void loadLootsFromPluginFolder() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("loots", "mobs", "blocks")) {
|
||||
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()) {
|
||||
loadSingleFile(subFile, StringUtils.chop(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Loot getLoot(String key) {
|
||||
return lootMap.get(key);
|
||||
}
|
||||
|
||||
private void loadGlobalLootProperties() {
|
||||
YamlConfiguration config = plugin.getConfig("config.yml");
|
||||
globalLootProperties = getSingleSectionItem(
|
||||
Objects.requireNonNull(config.getConfigurationSection("mechanics.global-loot-properties")),
|
||||
"GLOBAL",
|
||||
"global"
|
||||
);
|
||||
disableStats = globalLootProperties.disableStats();
|
||||
disableGames = globalLootProperties.disableGame();
|
||||
instantGame = globalLootProperties.instanceGame();
|
||||
showInFinder = globalLootProperties.showInFinder();
|
||||
gameGroup = globalLootProperties.gameConfig;
|
||||
}
|
||||
|
||||
private void loadSingleFile(File file, String namespace) {
|
||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
|
||||
for (Map.Entry<String, Object> entry : yaml.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
var loot = getSingleSectionItem(
|
||||
section,
|
||||
namespace,
|
||||
entry.getKey()
|
||||
);
|
||||
if (lootMap.containsKey(entry.getKey())) {
|
||||
LogUtils.severe("Duplicated loot found: " + entry.getKey() + ".");
|
||||
} else {
|
||||
lootMap.put(entry.getKey(), loot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CFLoot getSingleSectionItem(ConfigurationSection section, String namespace, String key) {
|
||||
return new CFLoot.Builder(key, LootType.valueOf(namespace.toUpperCase(Locale.ENGLISH)))
|
||||
.disableStats(section.getBoolean("disable-stat", disableStats))
|
||||
.disableGames(section.getBoolean("disable-game", disableGames))
|
||||
.instantGame(section.getBoolean("instant-game", instantGame))
|
||||
.showInFinder(section.getBoolean("show-in-fishfinder", showInFinder))
|
||||
.gameConfig(section.getString("game-group", gameGroup))
|
||||
.nick(section.getString("nick", section.getString("display.name", key)))
|
||||
.addActions(getActionMap(section.getConfigurationSection("action")))
|
||||
.addTimesActions(getTimesActionMap(section.getConfigurationSection("action.success-times")))
|
||||
.build();
|
||||
}
|
||||
|
||||
private HashMap<ActionTrigger, Action[]> getActionMap(ConfigurationSection section) {
|
||||
HashMap<ActionTrigger, Action[]> actionMap = new HashMap<>();
|
||||
if (section == null) return actionMap;
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
actionMap.put(
|
||||
ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)),
|
||||
plugin.getActionManager().getActions(innerSection)
|
||||
);
|
||||
}
|
||||
}
|
||||
return actionMap;
|
||||
}
|
||||
|
||||
private HashMap<Integer, Action[]> getTimesActionMap(ConfigurationSection section) {
|
||||
HashMap<Integer, Action[]> actionMap = new HashMap<>();
|
||||
if (section == null) return actionMap;
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
actionMap.put(Integer.parseInt(entry.getKey()), plugin.getActionManager().getActions(innerSection));
|
||||
}
|
||||
}
|
||||
return actionMap;
|
||||
}
|
||||
|
||||
public static class CFLoot implements Loot {
|
||||
|
||||
private final String id;
|
||||
private final LootType type;
|
||||
private String gameConfig;
|
||||
private final HashMap<ActionTrigger, Action[]> actionMap;
|
||||
private final HashMap<Integer, Action[]> successTimesActionMap;
|
||||
private String nick;
|
||||
private boolean showInFinder;
|
||||
private boolean disableGame;
|
||||
private boolean disableStats;
|
||||
private boolean instanceGame;
|
||||
private double score;
|
||||
|
||||
public CFLoot(String id, LootType type) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.actionMap = new HashMap<>();
|
||||
this.successTimesActionMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public static CFLoot of(String id, LootType type) {
|
||||
return new CFLoot(id, type);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final CFLoot loot;
|
||||
|
||||
public Builder(String id, LootType type) {
|
||||
this.loot = new CFLoot(id, type);
|
||||
}
|
||||
|
||||
public Builder nick(String nick) {
|
||||
this.loot.nick = nick;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder showInFinder(boolean show) {
|
||||
this.loot.showInFinder = show;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder instantGame(boolean instant) {
|
||||
this.loot.instanceGame = instant;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder gameConfig(String gameConfig) {
|
||||
this.loot.gameConfig = gameConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder disableGames(boolean disable) {
|
||||
this.loot.disableGame = disable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder disableStats(boolean disable) {
|
||||
this.loot.disableStats = disable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder score(double score) {
|
||||
this.loot.score = score;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addActions(ActionTrigger trigger, Action[] actions) {
|
||||
this.loot.actionMap.put(trigger, actions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addActions(HashMap<ActionTrigger, Action[]> actionMap) {
|
||||
this.loot.actionMap.putAll(actionMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTimesActions(int times, Action[] actions) {
|
||||
this.loot.successTimesActionMap.put(times, actions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTimesActions(HashMap<Integer, Action[]> actionMap) {
|
||||
this.loot.successTimesActionMap.putAll(actionMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CFLoot build() {
|
||||
return loot;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean instanceGame() {
|
||||
return this.instanceGame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getNick() {
|
||||
return this.nick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInFinder() {
|
||||
return this.showInFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getScore() {
|
||||
return this.score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableGame() {
|
||||
return this.disableGame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableStats() {
|
||||
return this.disableStats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameConfig getGameConfig() {
|
||||
return CustomFishingPlugin.get().getGameManager().getGameConfig(this.gameConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(ActionTrigger actionTrigger) {
|
||||
return actionMap.get(actionTrigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getSuccessTimesActions(int times) {
|
||||
return successTimesActionMap.get(times);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<Integer, Action[]> getSuccessTimesActionMap() {
|
||||
return successTimesActionMap;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package net.momirealms.customfishing.mechanic.market;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.MarketManager;
|
||||
import net.momirealms.customfishing.api.mechanic.item.ItemBuilder;
|
||||
import net.momirealms.customfishing.api.mechanic.market.MarketGUI;
|
||||
import net.momirealms.customfishing.libraries.inventorygui.InventoryGui;
|
||||
import net.momirealms.customfishing.libraries.inventorygui.StaticGuiElement;
|
||||
import net.momirealms.customfishing.mechanic.item.ItemManagerImpl;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import net.objecthunter.exp4j.Expression;
|
||||
import net.objecthunter.exp4j.ExpressionBuilder;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MarketManagerImpl implements MarketManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, Double> priceMap;
|
||||
private String[] layout;
|
||||
private String title;
|
||||
private String formula;
|
||||
private final HashMap<Character, ItemBuilder> decorativeIcons;
|
||||
private char itemSlot;
|
||||
|
||||
public MarketManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.priceMap = new HashMap<>();
|
||||
this.decorativeIcons = new HashMap<>();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
YamlConfiguration config = plugin.getConfig("market.yml");
|
||||
this.layout = config.getStringList("layout").toArray(new String[0]);
|
||||
this.title = config.getString("title", "market.title");
|
||||
this.formula = config.getString("price-formula", "{base} + {bonus} * {size}");
|
||||
ConfigurationSection priceSection = config.getConfigurationSection("item-price");
|
||||
if (priceSection != null) {
|
||||
for (Map.Entry<String, Object> entry : priceSection.getValues(false).entrySet()) {
|
||||
this.priceMap.put(entry.getKey(), ConfigUtils.getDoubleValue(entry.getValue()));
|
||||
}
|
||||
}
|
||||
ConfigurationSection decorativeSection = config.getConfigurationSection("decorative-icons");
|
||||
if (decorativeSection != null) {
|
||||
for (Map.Entry<String, Object> entry : decorativeSection.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
char symbol = Objects.requireNonNull(innerSection.getString("symbol")).charAt(0);
|
||||
var builder = plugin.getItemManager().getItemBuilder(innerSection, "gui", entry.getKey());
|
||||
decorativeIcons.put(symbol, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
this.priceMap.clear();
|
||||
this.decorativeIcons.clear();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
unload();
|
||||
}
|
||||
|
||||
public void openMarketGUI(Player player) {
|
||||
player.closeInventory();
|
||||
|
||||
InventoryGui gui = new InventoryGui(
|
||||
plugin,
|
||||
new MarketGUI(),
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(title),
|
||||
layout
|
||||
);
|
||||
|
||||
gui.setCloseAction(close -> {
|
||||
var elements = gui.getElement(itemSlot);
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
for (Map.Entry<Character, ItemBuilder> entry : decorativeIcons.entrySet()) {
|
||||
gui.addElement(new StaticGuiElement(
|
||||
entry.getKey(),
|
||||
((ItemManagerImpl.CFBuilder) entry.getValue()).build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDate() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
return (calendar.get(Calendar.MONTH) +1) * 100 + calendar.get(Calendar.DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getItemPrice(ItemStack itemStack) {
|
||||
if (itemStack == null || itemStack.getType() == Material.AIR)
|
||||
return 0;
|
||||
NBTItem nbtItem = new NBTItem(itemStack);
|
||||
Double price = nbtItem.getDouble("Price");
|
||||
if (price != null && price != 0) {
|
||||
return price;
|
||||
}
|
||||
String itemID = itemStack.getType().name();
|
||||
if (nbtItem.hasTag("CustomModelData")) {
|
||||
itemID = itemID + ":" + nbtItem.getInteger("CustomModelData");
|
||||
}
|
||||
return priceMap.getOrDefault(itemID, 0d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormula() {
|
||||
return formula;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPrice(float base, float bonus, float size) {
|
||||
Expression expression = new ExpressionBuilder(getFormula())
|
||||
.variables("base", "bonus", "size")
|
||||
.build()
|
||||
.setVariable("base", base)
|
||||
.setVariable("bonus", bonus)
|
||||
.setVariable("size", size);
|
||||
return expression.evaluate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package net.momirealms.customfishing.mechanic.mob;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.manager.MobManager;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Loot;
|
||||
import net.momirealms.customfishing.api.mechanic.mob.MobConfig;
|
||||
import net.momirealms.customfishing.api.mechanic.mob.MobLibrary;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.compatibility.mob.VanillaMobImpl;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class MobManagerImpl implements MobManager {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private final HashMap<String, MobLibrary> mobLibraryMap;
|
||||
private final HashMap<String, MobConfig> mobConfigMap;
|
||||
|
||||
public MobManagerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.mobLibraryMap = new HashMap<>();
|
||||
this.mobConfigMap = new HashMap<>();
|
||||
this.registerMobLibrary(new VanillaMobImpl());
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
HashMap<String, MobConfig> tempMap = new HashMap<>(this.mobConfigMap);
|
||||
this.mobConfigMap.clear();
|
||||
for (Map.Entry<String, MobConfig> entry : tempMap.entrySet()) {
|
||||
if (entry.getValue().isPersist()) {
|
||||
tempMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerMobLibrary(MobLibrary mobLibrary) {
|
||||
if (mobLibraryMap.containsKey(mobLibrary.identification())) return false;
|
||||
else mobLibraryMap.put(mobLibrary.identification(), mobLibrary);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterMobLibrary(String lib) {
|
||||
return mobLibraryMap.remove(lib) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterMobLibrary(MobLibrary mobLibrary) {
|
||||
return unregisterMobLibrary(mobLibrary.identification());
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void loadConfig() {
|
||||
Deque<File> fileDeque = new ArrayDeque<>();
|
||||
for (String type : List.of("mobs")) {
|
||||
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()) {
|
||||
this.loadSingleFile(subFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 mobID = section.getString("mob");
|
||||
if (mobID == null) {
|
||||
LogUtils.warn("Mob 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));
|
||||
}
|
||||
MobConfig mobConfig = new MobConfig.Builder()
|
||||
.mobID(mobID)
|
||||
.persist(false)
|
||||
.horizontalVector(section.getDouble("vector.horizontal", 1.1))
|
||||
.verticalVector(section.getDouble("vector.vertical", 1.2))
|
||||
.propertyMap(propertyMap)
|
||||
.build();
|
||||
mobConfigMap.put(entry.getKey(), mobConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
unload();
|
||||
this.mobConfigMap.clear();
|
||||
this.mobLibraryMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void summonMob(Location hookLocation, Location playerLocation, Loot loot) {
|
||||
MobConfig config = mobConfigMap.get(loot.getID());
|
||||
if (config == null) {
|
||||
LogUtils.warn("Mob: " + loot.getID() + " doesn't exist.");
|
||||
return;
|
||||
}
|
||||
String mobID = config.getMobID();
|
||||
Entity entity;
|
||||
if (mobID.contains(":")) {
|
||||
String[] split = mobID.split(":", 2);
|
||||
String identification = split[0];
|
||||
String id = split[1];
|
||||
MobLibrary library = mobLibraryMap.get(identification);
|
||||
entity = library.spawn(hookLocation, id, config.getPropertyMap());
|
||||
} else {
|
||||
entity = mobLibraryMap.get("vanilla").spawn(hookLocation, mobID, config.getPropertyMap());
|
||||
}
|
||||
Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1);
|
||||
vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector());
|
||||
entity.setVelocity(vector);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.momirealms.customfishing.mechanic.requirement;
|
||||
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractRequirement implements Requirement {
|
||||
|
||||
private final List<Action> actions;
|
||||
|
||||
public AbstractRequirement(List<Action> actions) {
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
protected void triggerActions(Condition condition) {
|
||||
if (actions != null) {
|
||||
for (Action action : actions) {
|
||||
action.trigger(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package net.momirealms.customfishing.mechanic.requirement;
|
||||
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.mechanic.loot.Modifier;
|
||||
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ConditionalLoots {
|
||||
|
||||
private final List<Pair<String, Modifier>> modifierList;
|
||||
private final HashMap<String, ConditionalLoots> subLoots;
|
||||
private final Requirement[] requirements;
|
||||
|
||||
public ConditionalLoots(
|
||||
Requirement[] requirements,
|
||||
List<Pair<String, Modifier>> modifierList,
|
||||
HashMap<String, ConditionalLoots> subLoots
|
||||
) {
|
||||
this.modifierList = modifierList;
|
||||
this.requirements = requirements;
|
||||
this.subLoots = subLoots;
|
||||
}
|
||||
|
||||
synchronized public void combine(HashMap<String, Double> weightMap) {
|
||||
for (Pair<String, Modifier> modifierPair : this.modifierList) {
|
||||
double previous = weightMap.getOrDefault(modifierPair.left(), 0d);
|
||||
weightMap.put(modifierPair.left(), modifierPair.right().modify(previous));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConditionsMet(Condition condition) {
|
||||
for (Requirement requirement : requirements) {
|
||||
if (!requirement.isConditionMet(condition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public HashMap<String, ConditionalLoots> getSubLoots() {
|
||||
return subLoots;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
package net.momirealms.customfishing.mechanic.requirement;
|
||||
|
||||
import net.momirealms.biomeapi.BiomeAPI;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.api.common.Pair;
|
||||
import net.momirealms.customfishing.api.integration.LevelInterface;
|
||||
import net.momirealms.customfishing.api.integration.SeasonInterface;
|
||||
import net.momirealms.customfishing.api.manager.RequirementManager;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
|
||||
import net.momirealms.customfishing.api.mechanic.requirement.RequirementBuilder;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.mechanic.requirement.inbuilt.LogicRequirement;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.MemorySection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RequirementManagerImpl implements RequirementManager {
|
||||
|
||||
public static Requirement[] mechanicRequirements;
|
||||
private final CustomFishingPluginImpl plugin;
|
||||
private final HashMap<String, RequirementBuilder> requirementBuilderMap;
|
||||
private final LinkedHashMap<String, ConditionalLoots> conditionalLootsMap;
|
||||
|
||||
public RequirementManagerImpl(CustomFishingPluginImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
this.requirementBuilderMap = new HashMap<>();
|
||||
this.conditionalLootsMap = new LinkedHashMap<>();
|
||||
this.registerInbuiltRequirements();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.loadRequirementGroupFileConfig();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
this.conditionalLootsMap.clear();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
this.requirementBuilderMap.clear();
|
||||
this.conditionalLootsMap.clear();
|
||||
}
|
||||
|
||||
public void loadRequirementGroupFileConfig() {
|
||||
YamlConfiguration main = plugin.getConfig("config.yml");
|
||||
mechanicRequirements = getRequirements(main.getConfigurationSection("mechanics.mechanic-requirements"), true);
|
||||
|
||||
YamlConfiguration config = plugin.getConfig("loot-conditions.yml");
|
||||
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
conditionalLootsMap.put(entry.getKey(), getConditionalLoots(section));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerRequirement(String type, RequirementBuilder requirementBuilder) {
|
||||
if (this.requirementBuilderMap.containsKey(type)) return false;
|
||||
this.requirementBuilderMap.put(type, requirementBuilder);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterRequirement(String type) {
|
||||
return this.requirementBuilderMap.remove(type) != null;
|
||||
}
|
||||
|
||||
private void registerInbuiltRequirements() {
|
||||
this.registerTimeRequirement();
|
||||
this.registerYRequirement();
|
||||
this.registerLogicRequirement();
|
||||
this.registerCompare();
|
||||
this.registerBiomeRequirement();
|
||||
this.registerDateRequirement();
|
||||
this.registerPluginLevelRequirement();
|
||||
this.registerPermissionRequirement();
|
||||
this.registerWorldRequirement();
|
||||
this.registerWeatherRequirement();
|
||||
this.registerSeasonRequirement();
|
||||
this.registerInLavaRequirement();
|
||||
this.registerRodRequirement();
|
||||
this.registerBaitRequirement();
|
||||
}
|
||||
|
||||
public ConditionalLoots getConditionalLoots(ConfigurationSection section) {
|
||||
var sub = section.getConfigurationSection("sub-groups");
|
||||
if (sub == null) {
|
||||
return new ConditionalLoots(
|
||||
getRequirements(section.getConfigurationSection("conditions"), false),
|
||||
ConfigUtils.getModifiers(section.getStringList("list")),
|
||||
null
|
||||
);
|
||||
} else {
|
||||
HashMap<String, ConditionalLoots> subLoots = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : sub.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection innerSection) {
|
||||
subLoots.put(entry.getKey(), getConditionalLoots(innerSection));
|
||||
}
|
||||
}
|
||||
return new ConditionalLoots(
|
||||
getRequirements(section.getConfigurationSection("conditions"), false),
|
||||
ConfigUtils.getModifiers(section.getStringList("list")),
|
||||
subLoots
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<String, Double> getLootWithWeight(Condition condition) {
|
||||
HashMap<String, Double> lootWeightMap = new HashMap<>();
|
||||
Queue<HashMap<String, ConditionalLoots>> lootQueue = new LinkedList<>();
|
||||
lootQueue.add(conditionalLootsMap);
|
||||
while (!lootQueue.isEmpty()) {
|
||||
HashMap<String, ConditionalLoots> currentLootMap = lootQueue.poll();
|
||||
for (ConditionalLoots loots : currentLootMap.values()) {
|
||||
if (loots.isConditionsMet(condition)) {
|
||||
loots.combine(lootWeightMap);
|
||||
if (loots.getSubLoots() != null) {
|
||||
lootQueue.add(loots.getSubLoots());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lootWeightMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Requirement[] getRequirements(ConfigurationSection section, boolean advanced) {
|
||||
if (section == null) return null;
|
||||
List<Requirement> requirements = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
String typeOrName = entry.getKey();
|
||||
if (hasRequirement(typeOrName)) {
|
||||
requirements.add(getRequirementBuilder(typeOrName).build(entry.getValue(), null, advanced));
|
||||
} else {
|
||||
requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced));
|
||||
}
|
||||
}
|
||||
return requirements.toArray(new Requirement[0]);
|
||||
}
|
||||
|
||||
public boolean hasRequirement(String type) {
|
||||
return requirementBuilderMap.containsKey(type);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Requirement getRequirement(ConfigurationSection section, boolean advanced) {
|
||||
List<Action> actionList = null;
|
||||
if (advanced) {
|
||||
actionList = new ArrayList<>();
|
||||
if (section.contains("actions")) {
|
||||
for (Map.Entry<String, Object> entry : Objects.requireNonNull(section.getConfigurationSection("actions")).getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof MemorySection inner) {
|
||||
actionList.add(plugin.getActionManager().getAction(inner));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionList.size() == 0)
|
||||
actionList = null;
|
||||
}
|
||||
String type = section.getString("type");
|
||||
if (type == null) {
|
||||
throw new NullPointerException(section.getCurrentPath() + ".type" + " doesn't exist");
|
||||
}
|
||||
var builder = getRequirementBuilder(type);
|
||||
if (builder == null) {
|
||||
throw new NullPointerException("Requirement type: " + type + " doesn't exist");
|
||||
}
|
||||
return builder.build(section.get("value"), actionList, advanced);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Requirement getRequirement(String key, Object value) {
|
||||
return getRequirementBuilder(key).build(value);
|
||||
}
|
||||
|
||||
private Pair<Integer, Integer> getIntegerPair(String range) {
|
||||
String[] split = range.split("~");
|
||||
return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequirementBuilder getRequirementBuilder(String type) {
|
||||
return requirementBuilderMap.get(type);
|
||||
}
|
||||
|
||||
private void registerLogicRequirement() {
|
||||
registerRequirement("logic", (args, actions, advanced) ->
|
||||
new LogicRequirement(this, args, actions, advanced)
|
||||
);
|
||||
}
|
||||
|
||||
private void registerTimeRequirement() {
|
||||
registerRequirement("time", (args, actions, advanced) -> {
|
||||
List<Pair<Integer, Integer>> timePairs = ConfigUtils.stringListArgs(args).stream().map(this::getIntegerPair).toList();
|
||||
return condition -> {
|
||||
long time = condition.getLocation().getWorld().getTime();
|
||||
for (Pair<Integer, Integer> pair : timePairs)
|
||||
if (time >= pair.left() && time <= pair.right())
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerYRequirement() {
|
||||
registerRequirement("ypos", (args, actions, advanced) -> {
|
||||
List<Pair<Integer, Integer>> timePairs = ConfigUtils.stringListArgs(args).stream().map(this::getIntegerPair).toList();
|
||||
return condition -> {
|
||||
int y = condition.getLocation().getBlockY();
|
||||
for (Pair<Integer, Integer> pair : timePairs)
|
||||
if (y >= pair.left() && y <= pair.right())
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerInLavaRequirement() {
|
||||
registerRequirement("in-lava", (args, actions, advanced) -> {
|
||||
boolean inLava = (boolean) args;
|
||||
return condition -> {
|
||||
String current = condition.getArgs().get("in-lava");
|
||||
if (current.equals(String.valueOf(inLava)))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerBiomeRequirement() {
|
||||
registerRequirement("biome", (args, actions, advanced) -> {
|
||||
HashSet<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
|
||||
return condition -> {
|
||||
String currentBiome = BiomeAPI.getBiome(condition.getLocation());
|
||||
if (biomes.contains(currentBiome))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
registerRequirement("!biome", (args, actions, advanced) -> {
|
||||
HashSet<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
|
||||
return condition -> {
|
||||
String currentBiome = BiomeAPI.getBiome(condition.getLocation());
|
||||
if (!biomes.contains(currentBiome))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerWorldRequirement() {
|
||||
registerRequirement("world", (args, actions, advanced) -> {
|
||||
HashSet<String> worlds = new HashSet<>(ConfigUtils.stringListArgs(args));
|
||||
return condition -> {
|
||||
if (worlds.contains(condition.getLocation().getWorld().getName()))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
registerRequirement("!world", (args, actions, advanced) -> {
|
||||
HashSet<String> worlds = new HashSet<>(ConfigUtils.stringListArgs(args));
|
||||
return condition -> {
|
||||
if (!worlds.contains(condition.getLocation().getWorld().getName()))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerWeatherRequirement() {
|
||||
registerRequirement("weather", (args, actions, advanced) -> {
|
||||
List<String> weathers = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
String currentWeather;
|
||||
World world = condition.getLocation().getWorld();
|
||||
if (world.isThundering()) currentWeather = "thunder";
|
||||
else if (world.isClearWeather()) currentWeather = "clear";
|
||||
else currentWeather = "rain";
|
||||
for (String weather : weathers)
|
||||
if (weather.equalsIgnoreCase(currentWeather))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerDateRequirement() {
|
||||
registerRequirement("date", (args, actions, advanced) -> {
|
||||
HashSet<String> dates = new HashSet<>(ConfigUtils.stringListArgs(args));
|
||||
return condition -> {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE);
|
||||
if (dates.contains(current))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerPermissionRequirement() {
|
||||
registerRequirement("permission", (args, actions, advanced) -> {
|
||||
List<String> perms = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
for (String perm : perms)
|
||||
if (condition.getPlayer().hasPermission(perm))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
registerRequirement("!permission", (args, actions, advanced) -> {
|
||||
List<String> perms = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
for (String perm : perms)
|
||||
if (condition.getPlayer().hasPermission(perm)) {
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerSeasonRequirement() {
|
||||
registerRequirement("season", (args, actions, advanced) -> {
|
||||
List<String> seasons = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface();
|
||||
if (seasonInterface == null) return true;
|
||||
String season = seasonInterface.getSeason(condition.getLocation().getWorld());
|
||||
if (seasons.contains(season)) return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerRodRequirement() {
|
||||
registerRequirement("rod", (args, actions, advanced) -> {
|
||||
List<String> rods = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
String id = condition.getArg("rod");
|
||||
if (rods.contains(id)) return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
registerRequirement("!rod", (args, actions, advanced) -> {
|
||||
List<String> rods = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
String id = condition.getArg("rod");
|
||||
if (!rods.contains(id)) return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerBaitRequirement() {
|
||||
registerRequirement("bait", (args, actions, advanced) -> {
|
||||
List<String> baits = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
String id = condition.getArg("bait");
|
||||
if (baits.contains(id)) return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
registerRequirement("!bait", (args, actions, advanced) -> {
|
||||
List<String> baits = ConfigUtils.stringListArgs(args);
|
||||
return condition -> {
|
||||
String id = condition.getArg("bait");
|
||||
if (!baits.contains(id)) return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void registerPluginLevelRequirement() {
|
||||
registerRequirement("plugin-level", (args, actions, advanced) -> {
|
||||
if (args instanceof ConfigurationSection section) {
|
||||
String pluginName = section.getString("plugin");
|
||||
int level = section.getInt("level");
|
||||
String target = section.getString("target");
|
||||
return condition -> {
|
||||
LevelInterface levelInterface = plugin.getIntegrationManager().getLevelHook(pluginName);
|
||||
if (levelInterface == null) {
|
||||
LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.");
|
||||
return true;
|
||||
}
|
||||
if (levelInterface.getLevel(condition.getPlayer(), target) >= level)
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void registerCompare() {
|
||||
registerRequirement("compare", (args, actions, advanced) -> condition -> {
|
||||
if (evaluateExpression((String) args, condition.getPlayer()))
|
||||
return true;
|
||||
if (advanced) triggerActions(actions, condition);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void triggerActions(List<Action> actions, Condition condition) {
|
||||
if (actions != null)
|
||||
for (Action action : actions)
|
||||
action.trigger(condition);
|
||||
}
|
||||
|
||||
private double doubleArg(String s, Player player) {
|
||||
double arg = 0;
|
||||
try {
|
||||
arg = Double.parseDouble(s);
|
||||
} catch (NumberFormatException e1) {
|
||||
try {
|
||||
arg = Double.parseDouble(plugin.getPlaceholderManager().setPlaceholders(player, s));
|
||||
} catch (NumberFormatException e2) {
|
||||
LogUtils.severe(String.format("Invalid placeholder %s", s), e2);
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
public boolean evaluateExpression(String input, Player player) {
|
||||
input = input.replace("\\s", "");
|
||||
Pattern pattern = Pattern.compile("(-?\\d+\\.?\\d*)(==|!=|<=?|>=?)(-?\\d+\\.?\\d*)");
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
if (matcher.matches()) {
|
||||
double num1 = doubleArg(matcher.group(1), player);
|
||||
String operator = matcher.group(2);
|
||||
double num2 = doubleArg(matcher.group(3), player);
|
||||
return switch (operator) {
|
||||
case ">" -> num1 > num2;
|
||||
case "<" -> num1 < num2;
|
||||
case ">=" -> num1 >= num2;
|
||||
case "<=" -> num1 <= num2;
|
||||
case "==" -> num1 == num2;
|
||||
case "!=" -> num1 != num2;
|
||||
default -> throw new IllegalArgumentException("Unsupported operator: " + operator);
|
||||
};
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid input format: " + input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package net.momirealms.customfishing.mechanic.requirement.inbuilt;
|
||||
|
||||
import net.momirealms.customfishing.api.manager.RequirementManager;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.mechanic.condition.Condition;
|
||||
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
|
||||
import net.momirealms.customfishing.mechanic.requirement.AbstractRequirement;
|
||||
import org.bukkit.configuration.MemorySection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LogicRequirement extends AbstractRequirement {
|
||||
|
||||
private List<Requirement> requirementList;
|
||||
private boolean checkAction;
|
||||
|
||||
public LogicRequirement(RequirementManager requirementManager, Object args, List<Action> actions, boolean checkAction) {
|
||||
super(actions);
|
||||
if (!(args instanceof MemorySection section1))
|
||||
return;
|
||||
this.requirementList = new ArrayList<>();
|
||||
this.checkAction = checkAction;
|
||||
Deque<StackElement> stack = new ArrayDeque<>();
|
||||
stack.push(new StackElement(section1.getValues(false), requirementList));
|
||||
while (!stack.isEmpty()) {
|
||||
StackElement stackElement = stack.pop();
|
||||
Map<String, Object> currentMap = stackElement.getMap();
|
||||
List<Requirement> currentResult = stackElement.getRequirements();
|
||||
for (Map.Entry<String, Object> entry : currentMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof MemorySection section2) {
|
||||
Map<String, Object> sectionMap = section2.getValues(false);
|
||||
if (key.startsWith("&&")) {
|
||||
List<Requirement> andReqList = new ArrayList<>();
|
||||
currentResult.add(new AndRequirement(andReqList));
|
||||
stack.push(new StackElement(sectionMap, andReqList));
|
||||
} else if (key.startsWith("||")) {
|
||||
List<Requirement> orReqList = new ArrayList<>();
|
||||
currentResult.add(new OrRequirement(orReqList));
|
||||
stack.push(new StackElement(sectionMap, orReqList));
|
||||
} else {
|
||||
currentResult.add(requirementManager.getRequirement(section2, checkAction));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StackElement {
|
||||
|
||||
private final Map<String, Object> map;
|
||||
private final List<Requirement> requirements;
|
||||
|
||||
public StackElement(Map<String, Object> map, List<Requirement> requirements) {
|
||||
this.map = map;
|
||||
this.requirements = requirements;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public List<Requirement> getRequirements() {
|
||||
return requirements;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConditionMet(Condition condition) {
|
||||
for (Requirement requirement : requirementList) {
|
||||
if (!requirement.isConditionMet(condition)) {
|
||||
if (checkAction) super.triggerActions(condition);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class AndRequirement implements Requirement {
|
||||
|
||||
private final List<Requirement> requirementList;
|
||||
|
||||
public AndRequirement(List<Requirement> requirementList) {
|
||||
this.requirementList = requirementList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConditionMet(Condition condition) {
|
||||
for (Requirement requirement : requirementList) {
|
||||
if (!requirement.isConditionMet(condition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OrRequirement implements Requirement {
|
||||
|
||||
private final List<Requirement> requirementList;
|
||||
|
||||
public OrRequirement(List<Requirement> requirementList) {
|
||||
this.requirementList = requirementList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConditionMet(Condition condition) {
|
||||
for (Requirement requirement : requirementList) {
|
||||
if (requirement.isConditionMet(condition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.momirealms.customfishing.mechanic.totem;
|
||||
|
||||
public class TotemManagerImpl {
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.scheduler;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
public class BukkitSchedulerImpl implements SyncScheduler {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
|
||||
public BukkitSchedulerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSyncTask(Runnable runnable, Location location) {
|
||||
if (Bukkit.isPrimaryThread())
|
||||
runnable.run();
|
||||
else
|
||||
Bukkit.getScheduler().runTask(plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) {
|
||||
return new BukkitCancellableTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) {
|
||||
return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay));
|
||||
}
|
||||
|
||||
public static class BukkitCancellableTask implements CancellableTask {
|
||||
|
||||
private final BukkitTask bukkitTask;
|
||||
|
||||
public BukkitCancellableTask(BukkitTask bukkitTask) {
|
||||
this.bukkitTask = bukkitTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.bukkitTask.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.bukkitTask.isCancelled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.scheduler;
|
||||
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class FoliaSchedulerImpl implements SyncScheduler {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
|
||||
public FoliaSchedulerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSyncTask(Runnable runnable, Location location) {
|
||||
Bukkit.getRegionScheduler().execute(plugin, location, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) {
|
||||
return new FoliaCancellableTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, (scheduledTask -> runnable.run()), delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) {
|
||||
return new FoliaCancellableTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, (scheduledTask -> runnable.run()), delay));
|
||||
}
|
||||
|
||||
public static class FoliaCancellableTask implements CancellableTask {
|
||||
|
||||
private final ScheduledTask scheduledTask;
|
||||
|
||||
public FoliaCancellableTask(ScheduledTask scheduledTask) {
|
||||
this.scheduledTask = scheduledTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.scheduledTask.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.scheduledTask.isCancelled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.scheduler;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.api.scheduler.Scheduler;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SchedulerImpl implements Scheduler {
|
||||
|
||||
private final SyncScheduler syncScheduler;
|
||||
private final ScheduledThreadPoolExecutor schedule;
|
||||
private final CustomFishingPlugin plugin;
|
||||
|
||||
public SchedulerImpl(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.syncScheduler = plugin.getVersionManager().isFolia() ?
|
||||
new FoliaSchedulerImpl(plugin) : new BukkitSchedulerImpl(plugin);
|
||||
this.schedule = new ScheduledThreadPoolExecutor(4);
|
||||
this.schedule.setMaximumPoolSize(4);
|
||||
this.schedule.setKeepAliveTime(10, TimeUnit.SECONDS);
|
||||
this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
this.schedule.setCorePoolSize(Config.corePoolSize);
|
||||
this.schedule.setKeepAliveTime(Config.keepAliveTime, TimeUnit.SECONDS);
|
||||
this.schedule.setMaximumPoolSize(Config.maximumPoolSize);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (this.schedule != null && !this.schedule.isShutdown())
|
||||
this.schedule.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTaskSync(Runnable runnable, Location location) {
|
||||
this.syncScheduler.runSyncTask(runnable, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTaskAsync(Runnable runnable) {
|
||||
this.schedule.execute(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) {
|
||||
return this.syncScheduler.runTaskSyncTimer(runnable, location, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit) {
|
||||
return new ScheduledTask(schedule.schedule(runnable, delay, timeUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit) {
|
||||
return new ScheduledTask(schedule.schedule(() -> {
|
||||
runTaskSync(runnable, location);
|
||||
}, delay, timeUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) {
|
||||
return this.syncScheduler.runTaskSyncLater(runnable, location, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
|
||||
return new ScheduledTask(schedule.scheduleAtFixedRate(runnable, delay, period, timeUnit));
|
||||
}
|
||||
|
||||
public static class ScheduledTask implements CancellableTask {
|
||||
|
||||
private final ScheduledFuture<?> scheduledFuture;
|
||||
|
||||
public ScheduledTask(ScheduledFuture<?> scheduledFuture) {
|
||||
this.scheduledFuture = scheduledFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.scheduledFuture.cancel(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.scheduledFuture.isCancelled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.scheduler;
|
||||
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public interface SyncScheduler {
|
||||
|
||||
void runSyncTask(Runnable runnable, Location location);
|
||||
|
||||
CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period);
|
||||
|
||||
CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package net.momirealms.customfishing.setting;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
|
||||
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.action.Action;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.api.util.OffsetUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.event.EventPriority;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Config {
|
||||
|
||||
// config version
|
||||
public static String configVersion = "26";
|
||||
|
||||
// language
|
||||
public static String language;
|
||||
|
||||
// update checker
|
||||
public static boolean updateChecker;
|
||||
|
||||
// BStats
|
||||
public static boolean metrics;
|
||||
|
||||
// fishing event priority
|
||||
public static EventPriority eventPriority;
|
||||
|
||||
// thread pool settings
|
||||
public static int corePoolSize;
|
||||
public static int maximumPoolSize;
|
||||
public static int keepAliveTime;
|
||||
|
||||
// detection order for item id
|
||||
public static List<String> itemDetectOrder;
|
||||
|
||||
// fishing bag
|
||||
public static boolean enableFishingBag;
|
||||
public static boolean bagStoreLoots;
|
||||
public static String bagTitle;
|
||||
public static List<Material> bagWhiteListItems;
|
||||
|
||||
// Animation
|
||||
public static boolean enableBaitAnimation;
|
||||
public static boolean enableSplashAnimation;
|
||||
public static int splashAnimationTime;
|
||||
public static String lavaSplashItem;
|
||||
public static String waterSplashItem;
|
||||
|
||||
// Lava fishing
|
||||
public static int lavaMinTime;
|
||||
public static int lavaMaxTime;
|
||||
|
||||
// Exception
|
||||
public static boolean vanillaMechanicIfNoLoot;
|
||||
public static Action[] noLootActions;
|
||||
public static boolean debug;
|
||||
|
||||
// Competition
|
||||
public static boolean redisRanking;
|
||||
public static int placeholderLimit;
|
||||
|
||||
//
|
||||
public static int dataSaveInterval;
|
||||
|
||||
public static void load() {
|
||||
try {
|
||||
YamlDocument.create(
|
||||
new File(CustomFishingPlugin.getInstance().getDataFolder(), "config.yml"),
|
||||
Objects.requireNonNull(CustomFishingPlugin.getInstance().getResource("config.yml")),
|
||||
GeneralSettings.DEFAULT,
|
||||
LoaderSettings
|
||||
.builder()
|
||||
.setAutoUpdate(true)
|
||||
.build(),
|
||||
DumperSettings.DEFAULT,
|
||||
UpdaterSettings
|
||||
.builder()
|
||||
.setVersioning(new BasicVersioning("config-version"))
|
||||
.addIgnoredRoute(configVersion, "mechanics.mechanic-requirements", '.')
|
||||
.addIgnoredRoute(configVersion, "mechanics.global-loot-properties", '.')
|
||||
.build()
|
||||
);
|
||||
loadSettings(CustomFishingPlugin.getInstance().getConfig("config.yml"));
|
||||
} catch (IOException e) {
|
||||
LogUtils.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadSettings(YamlConfiguration config) {
|
||||
debug = config.getBoolean("debug", false);
|
||||
|
||||
language = config.getString("lang", "english");
|
||||
updateChecker = config.getBoolean("update-checker");
|
||||
metrics = config.getBoolean("metrics");
|
||||
eventPriority = EventPriority.valueOf(config.getString("other-settings.event-priority", "NORMAL").toUpperCase(Locale.ENGLISH));
|
||||
|
||||
corePoolSize = config.getInt("other-settings.thread-pool-settings.corePoolSize", 4);
|
||||
maximumPoolSize = config.getInt("other-settings.thread-pool-settings.maximumPoolSize", 8);
|
||||
keepAliveTime = config.getInt("other-settings.thread-pool-settings.keepAliveTime", 10);
|
||||
|
||||
itemDetectOrder = config.getStringList("other-settings.item-detection-order");
|
||||
|
||||
enableFishingBag = config.getBoolean("mechanics.fishing-bag.enable", true);
|
||||
bagTitle = config.getString("mechanics.fishing-bag.bag-title");
|
||||
bagStoreLoots = config.getBoolean("mechanics.fishing-bag.can-store-loot", false);
|
||||
bagWhiteListItems = config.getStringList("mechanics.fishing-bag.whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList();
|
||||
|
||||
lavaMinTime = config.getInt("mechanics.lava-fishing.min-wait-time", 100);
|
||||
lavaMaxTime = config.getInt("mechanics.lava-fishing.max-wait-time", 600);
|
||||
|
||||
enableSplashAnimation = config.getBoolean("mechanics.animation.splash.enable", true);
|
||||
enableBaitAnimation = config.getBoolean("mechanics.animation.bait.enable", true);
|
||||
waterSplashItem = config.getString("mechanics.animation.splash.water");
|
||||
lavaSplashItem = config.getString("mechanics.animation.splash.lava");
|
||||
splashAnimationTime = config.getInt("mechanics.animation.splash.duration");
|
||||
|
||||
vanillaMechanicIfNoLoot = config.getBoolean("mechanics.vanilla-mechanic-if-no-loot.enable", false);
|
||||
noLootActions = CustomFishingPlugin.get().getActionManager().getActions(config.getConfigurationSection("mechanics.vanilla-mechanic-if-no-loot.actions"));
|
||||
|
||||
redisRanking = config.getBoolean("mechanics.competition.redis-ranking", false);
|
||||
placeholderLimit = config.getInt("mechanics.competition.placeholder-limit", 3);
|
||||
|
||||
dataSaveInterval = config.getInt("other-settings.data-saving-interval", 600);
|
||||
|
||||
OffsetUtils.loadConfig(config.getConfigurationSection("other-settings.offset-characters"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package net.momirealms.customfishing.setting;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
|
||||
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
|
||||
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Locale {
|
||||
public static String MSG_Total_Size;
|
||||
public static String MSG_Catch_Amount;
|
||||
public static String MSG_Total_Score;
|
||||
public static String MSG_Max_Size;
|
||||
public static String MSG_No_Player;
|
||||
public static String MSG_No_Score;
|
||||
public static String MSG_Prefix;
|
||||
public static String MSG_Reload;
|
||||
public static String MSG_Competition_Not_Exist;
|
||||
public static String MSG_No_Competition_Ongoing;
|
||||
public static String MSG_End_Competition;
|
||||
public static String MSG_Stop_Competition;
|
||||
public static String MSG_No_Rank;
|
||||
public static String MSG_Item_Not_Exists;
|
||||
public static String MSG_Get_Item;
|
||||
public static String MSG_Give_Item;
|
||||
public static String MSG_Never_Played;
|
||||
public static String MSG_Unsafe_Modification;
|
||||
|
||||
public static void load() {
|
||||
try {
|
||||
YamlDocument.create(
|
||||
new File(CustomFishingPlugin.getInstance().getDataFolder(), "messages/" + Config.language + ".yml"),
|
||||
Objects.requireNonNull(CustomFishingPlugin.getInstance().getResource("messages/" + Config.language + ".yml")),
|
||||
GeneralSettings.DEFAULT,
|
||||
LoaderSettings
|
||||
.builder()
|
||||
.setAutoUpdate(true)
|
||||
.build(),
|
||||
DumperSettings.DEFAULT,
|
||||
UpdaterSettings
|
||||
.builder()
|
||||
.setVersioning(new BasicVersioning("config-version"))
|
||||
.build()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
LogUtils.warn(e.getMessage());
|
||||
}
|
||||
loadSettings(CustomFishingPlugin.get().getConfig("messages/" + Config.language + ".yml"));
|
||||
}
|
||||
|
||||
private static void loadSettings(YamlConfiguration locale) {
|
||||
ConfigurationSection msgSection = locale.getConfigurationSection("messages");
|
||||
if (msgSection != null) {
|
||||
MSG_Prefix = msgSection.getString("prefix");
|
||||
MSG_Reload = msgSection.getString("reload");
|
||||
MSG_Competition_Not_Exist = msgSection.getString("competition-not-exist");
|
||||
MSG_No_Competition_Ongoing = msgSection.getString("no-competition-ongoing");
|
||||
MSG_Stop_Competition = msgSection.getString("stop-competition");
|
||||
MSG_End_Competition = msgSection.getString("end-competition");
|
||||
MSG_No_Player = msgSection.getString("no-player");
|
||||
MSG_No_Score = msgSection.getString("no-score");
|
||||
MSG_No_Rank = msgSection.getString("no-rank");
|
||||
MSG_Catch_Amount = msgSection.getString("goal-catch-amount");
|
||||
MSG_Max_Size = msgSection.getString("goal-max-size");
|
||||
MSG_Total_Score = msgSection.getString("goal-total-score");
|
||||
MSG_Total_Size = msgSection.getString("goal-total-size");
|
||||
MSG_Item_Not_Exists = msgSection.getString("item-not-exist");
|
||||
MSG_Get_Item = msgSection.getString("get-item");
|
||||
MSG_Give_Item = msgSection.getString("give-item");
|
||||
MSG_Never_Played = msgSection.getString("never-played");
|
||||
MSG_Unsafe_Modification = msgSection.getString("unsafe-modification");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package net.momirealms.customfishing.storage;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.momirealms.customfishing.CustomFishingPluginImpl;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.DataStorageInterface;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.api.data.user.OfflineUser;
|
||||
import net.momirealms.customfishing.api.manager.StorageManager;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.storage.method.database.nosql.MongoDBImpl;
|
||||
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
|
||||
import net.momirealms.customfishing.storage.method.database.sql.H2Impl;
|
||||
import net.momirealms.customfishing.storage.method.database.sql.MariaDBImpl;
|
||||
import net.momirealms.customfishing.storage.method.database.sql.MySQLImpl;
|
||||
import net.momirealms.customfishing.storage.method.database.sql.SQLiteImpl;
|
||||
import net.momirealms.customfishing.storage.method.file.JsonImpl;
|
||||
import net.momirealms.customfishing.storage.method.file.YAMLImpl;
|
||||
import net.momirealms.customfishing.storage.user.OfflineUserImpl;
|
||||
import net.momirealms.customfishing.storage.user.OnlineUserImpl;
|
||||
import org.bukkit.Bukkit;
|
||||
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.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StorageManagerImpl implements StorageManager, Listener {
|
||||
|
||||
private final CustomFishingPlugin plugin;
|
||||
private DataStorageInterface dataSource;
|
||||
private StorageType previousType;
|
||||
private final ConcurrentHashMap<UUID, OnlineUserImpl> onlineUserMap;
|
||||
private final HashSet<UUID> locked;
|
||||
private boolean hasRedis;
|
||||
private RedisManager redisManager;
|
||||
private String uniqueID;
|
||||
|
||||
public StorageManagerImpl(CustomFishingPluginImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
this.locked = new HashSet<>();
|
||||
this.onlineUserMap = new ConcurrentHashMap<>();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
uniqueID = config.getString("unique-server-id", "default");
|
||||
StorageType storageType = StorageType.valueOf(config.getString("data-storage-method", "H2"));
|
||||
if (storageType != previousType) {
|
||||
if (this.dataSource != null) this.dataSource.disable();
|
||||
this.previousType = storageType;
|
||||
switch (storageType) {
|
||||
case H2 -> this.dataSource = new H2Impl(plugin);
|
||||
case JSON -> this.dataSource = new JsonImpl(plugin);
|
||||
case YAML -> this.dataSource = new YAMLImpl(plugin);
|
||||
case SQLite -> this.dataSource = new SQLiteImpl(plugin);
|
||||
case MySQL -> this.dataSource = new MySQLImpl(plugin);
|
||||
case MariaDB -> this.dataSource = new MariaDBImpl(plugin);
|
||||
case MongoDB -> this.dataSource = new MongoDBImpl(plugin);
|
||||
}
|
||||
if (dataSource != null) this.dataSource.initialize();
|
||||
else LogUtils.severe("No storage type is set.");
|
||||
}
|
||||
if (!hasRedis && config.getBoolean("Redis.enable", false)) {
|
||||
hasRedis = true;
|
||||
this.redisManager = new RedisManager(plugin);
|
||||
this.redisManager.initialize();
|
||||
}
|
||||
if (hasRedis && !config.getBoolean("Redis.enable", false) && redisManager != null) {
|
||||
redisManager.disable();
|
||||
redisManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this);
|
||||
if (this.dataSource != null)
|
||||
dataSource.disable();
|
||||
if (this.redisManager != null)
|
||||
redisManager.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueID() {
|
||||
return uniqueID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnlineUserImpl getOnlineUser(UUID uuid) {
|
||||
return onlineUserMap.get(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<OfflineUser>> getOfflineUser(UUID uuid, boolean force) {
|
||||
var optionalDataFuture = dataSource.getPlayerData(uuid, force);
|
||||
return optionalDataFuture.thenCompose(optionalUser -> {
|
||||
if (optionalUser.isEmpty()) {
|
||||
// locked
|
||||
return CompletableFuture.completedFuture(Optional.empty());
|
||||
}
|
||||
PlayerData data = optionalUser.get();
|
||||
if (data == PlayerData.NEVER_PLAYED) {
|
||||
return CompletableFuture.completedFuture(Optional.of(OfflineUserImpl.NEVER_PLAYED_USER));
|
||||
} else {
|
||||
OfflineUserImpl offlineUser = new OfflineUserImpl(uuid, data.getName(), data);
|
||||
return CompletableFuture.completedFuture(Optional.of(offlineUser));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getRedisPlayerCount() {
|
||||
return redisManager.getPlayerCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStorageInterface getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
UUID uuid = player.getUniqueId();
|
||||
locked.add(uuid);
|
||||
if (!hasRedis) {
|
||||
waitForDataLockRelease(uuid, 1);
|
||||
} else {
|
||||
redisReadingData(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
UUID uuid = player.getUniqueId();
|
||||
if (locked.contains(uuid))
|
||||
return;
|
||||
|
||||
OnlineUserImpl onlineUser = onlineUserMap.remove(uuid);
|
||||
if (onlineUser == null) return;
|
||||
PlayerData data = onlineUser.getPlayerData();
|
||||
|
||||
if (hasRedis) {
|
||||
redisManager.setChangeServer(uuid).thenRun(
|
||||
() -> redisManager.setPlayData(uuid, data, true).thenRun(
|
||||
() -> dataSource.setPlayData(uuid, data, true).thenAccept(
|
||||
result -> {
|
||||
if (result) locked.remove(uuid);
|
||||
})));
|
||||
} else {
|
||||
dataSource.setPlayData(uuid, data, true).thenAccept(
|
||||
result -> {
|
||||
if (result) locked.remove(uuid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void redisReadingData(UUID uuid) {
|
||||
// delay 0.5s for another server to insert the key
|
||||
plugin.getScheduler().runTaskAsyncLater(() -> redisManager.getChangeServer(uuid).thenAccept(result -> {
|
||||
if (!result) {
|
||||
waitForDataLockRelease(uuid, 3);
|
||||
} else {
|
||||
new RedisGetDataTask(uuid);
|
||||
}
|
||||
}), 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public class RedisGetDataTask implements Runnable {
|
||||
|
||||
private final UUID uuid;
|
||||
private int triedTimes;
|
||||
private final CancellableTask task;
|
||||
|
||||
public RedisGetDataTask(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
this.task = plugin.getScheduler().runTaskAsyncTimer(this, 200, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
triedTimes++;
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player == null || !player.isOnline()) {
|
||||
// offline
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
if (triedTimes >= 10) {
|
||||
waitForDataLockRelease(uuid, 3);
|
||||
return;
|
||||
}
|
||||
redisManager.getPlayerData(uuid, false).thenAccept(optionalData -> {
|
||||
if (optionalData.isPresent()) {
|
||||
putDataInCache(player, optionalData.get());
|
||||
task.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// wait 1 second for the lock to release
|
||||
// try three times at most
|
||||
public void waitForDataLockRelease(UUID uuid, int times) {
|
||||
plugin.getScheduler().runTaskAsyncLater(() -> {
|
||||
var player = Bukkit.getPlayer(uuid);
|
||||
if (player == null || !player.isOnline() || times > 3)
|
||||
return;
|
||||
this.dataSource.getPlayerData(uuid, false).thenAccept(optionalData -> {
|
||||
if (optionalData.isEmpty()) {
|
||||
waitForDataLockRelease(uuid, times + 1);
|
||||
} else {
|
||||
putDataInCache(player, optionalData.get());
|
||||
}
|
||||
});
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void putDataInCache(Player player, PlayerData playerData) {
|
||||
locked.remove(player.getUniqueId());
|
||||
OnlineUserImpl bukkitUser = new OnlineUserImpl(player, playerData);
|
||||
onlineUserMap.put(player.getUniqueId(), bukkitUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedisEnabled() {
|
||||
return hasRedis;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RedisManager getRedisManager() {
|
||||
return redisManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toBytes(@NotNull PlayerData data) {
|
||||
return toJson(data).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toJson(@NotNull PlayerData data) {
|
||||
return new GsonBuilder().create().toJson(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PlayerData fromBytes(byte[] data) {
|
||||
try {
|
||||
return new GsonBuilder().create().fromJson(new String(data, StandardCharsets.UTF_8), PlayerData.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new DataSerializationException("Failed to get PlayerData from bytes", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataSerializationException extends RuntimeException {
|
||||
protected DataSerializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.momirealms.customfishing.storage.method;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.DataStorageInterface;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public abstract class AbstractStorage implements DataStorageInterface {
|
||||
|
||||
protected CustomFishingPlugin plugin;
|
||||
|
||||
public AbstractStorage(CustomFishingPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
|
||||
}
|
||||
|
||||
public int getCurrentSeconds() {
|
||||
return (int) Instant.now().getEpochSecond();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package net.momirealms.customfishing.storage.method.database.nosql;
|
||||
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.result.InsertOneResult;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.storage.method.AbstractStorage;
|
||||
import org.bson.Document;
|
||||
import org.bson.UuidRepresentation;
|
||||
import org.bson.types.Binary;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MongoDBImpl extends AbstractStorage {
|
||||
|
||||
private MongoClient mongoClient;
|
||||
private MongoDatabase database;
|
||||
private String collectionPrefix;
|
||||
|
||||
public MongoDBImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
ConfigurationSection section = config.getConfigurationSection("MongoDB");
|
||||
if (section == null) {
|
||||
LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one.");
|
||||
return;
|
||||
}
|
||||
|
||||
collectionPrefix = section.getString("collection-prefix", "customfishing");
|
||||
var settings = MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD);
|
||||
if (!section.getString("connection-uri", "").equals("")) {
|
||||
settings.applyConnectionString(new ConnectionString(section.getString("connection-uri", "")));
|
||||
mongoClient = MongoClients.create(settings.build());
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.contains("user")) {
|
||||
MongoCredential credential = MongoCredential.createCredential(
|
||||
section.getString("user", "root"),
|
||||
section.getString("database", "minecraft"),
|
||||
section.getString("password", "password").toCharArray()
|
||||
);
|
||||
settings.credential(credential);
|
||||
}
|
||||
|
||||
settings.applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(new ServerAddress(
|
||||
section.getString("host", "localhost"),
|
||||
section.getInt("port", 27017)
|
||||
))));
|
||||
this.mongoClient = MongoClients.create(settings.build());
|
||||
this.database = mongoClient.getDatabase(section.getString("database", "minecraft"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if (this.mongoClient != null) {
|
||||
this.mongoClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCollectionName(String sub) {
|
||||
return getCollectionPrefix() + "_" + sub;
|
||||
}
|
||||
|
||||
public String getCollectionPrefix() {
|
||||
return collectionPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.MongoDB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean force) {
|
||||
var future = new CompletableFuture<Optional<PlayerData>>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
MongoCollection<Document> collection = database.getCollection("movies");
|
||||
Document doc = collection.find(Filters.eq("uuid", uuid)).first();
|
||||
if (doc == null) {
|
||||
if (Bukkit.getPlayer(uuid) != null) {
|
||||
future.complete(Optional.of(PlayerData.empty()));
|
||||
} else {
|
||||
future.complete(Optional.of(PlayerData.NEVER_PLAYED));
|
||||
}
|
||||
} else {
|
||||
if (!force && doc.getInteger("lock") != 0) {
|
||||
future.complete(Optional.empty());
|
||||
return;
|
||||
}
|
||||
Binary binary = (Binary) doc.get("data");
|
||||
future.complete(Optional.of(plugin.getStorageManager().fromBytes(binary.getData())));
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean unlock) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
MongoCollection<Document> collection = database.getCollection(getCollectionName("data"));
|
||||
try {
|
||||
InsertOneResult result = collection.insertOne(new Document()
|
||||
.append("_id", new ObjectId())
|
||||
.append("uuid", uuid)
|
||||
.append("lock", unlock ? 0 : getCurrentSeconds())
|
||||
.append("data", new Binary(plugin.getStorageManager().toBytes(playerData))));
|
||||
future.complete(result.wasAcknowledged());
|
||||
} catch (MongoException e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package net.momirealms.customfishing.storage.method.database.nosql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.storage.method.AbstractStorage;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import redis.clients.jedis.*;
|
||||
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
import redis.clients.jedis.resps.Tuple;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RedisManager extends AbstractStorage {
|
||||
|
||||
private static RedisManager instance;
|
||||
private JedisPool jedisPool;
|
||||
private String password;
|
||||
private int port;
|
||||
private String host;
|
||||
private JedisPoolConfig jedisPoolConfig;
|
||||
private boolean useSSL;
|
||||
private Jedis subscriber;
|
||||
private JedisPubSub pubSub;
|
||||
private CancellableTask checkConnectionTask;
|
||||
|
||||
public RedisManager(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static RedisManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Jedis getJedis() {
|
||||
return jedisPool.getResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
ConfigurationSection section = config.getConfigurationSection("Redis");
|
||||
if (section == null) {
|
||||
LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one.");
|
||||
return;
|
||||
}
|
||||
|
||||
jedisPoolConfig = new JedisPoolConfig();
|
||||
jedisPoolConfig.setTestWhileIdle(true);
|
||||
jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000));
|
||||
jedisPoolConfig.setNumTestsPerEvictionRun(-1);
|
||||
jedisPoolConfig.setMinEvictableIdleTime(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis",1800000)));
|
||||
jedisPoolConfig.setMaxTotal(section.getInt("MaxTotal",8));
|
||||
jedisPoolConfig.setMaxIdle(section.getInt("MaxIdle",8));
|
||||
jedisPoolConfig.setMinIdle(section.getInt("MinIdle",1));
|
||||
jedisPoolConfig.setMaxWait(Duration.ofMillis(section.getInt("MaxWaitMillis")));
|
||||
|
||||
password = section.getString("password", "");
|
||||
port = section.getInt("port", 6379);
|
||||
host = section.getString("host", "localhost");
|
||||
useSSL = section.getBoolean("use-ssl", false);
|
||||
|
||||
if (password.isBlank()) {
|
||||
jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, useSSL);
|
||||
} else {
|
||||
jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, password, useSSL);
|
||||
}
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.ping();
|
||||
} catch (JedisException e) {
|
||||
LogUtils.warn("Failed to connect redis.", e);
|
||||
}
|
||||
|
||||
this.checkConnectionTask = plugin.getScheduler().runTaskAsyncTimer(() -> {
|
||||
try {
|
||||
pubSub.ping();
|
||||
} catch (JedisConnectionException e) {
|
||||
subscribe();
|
||||
}
|
||||
}, 30, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.removeServerPlayers(plugin.getStorageManager().getUniqueID());
|
||||
if (checkConnectionTask != null && !checkConnectionTask.isCancelled())
|
||||
checkConnectionTask.cancel();
|
||||
if (jedisPool != null && !jedisPool.isClosed())
|
||||
jedisPool.close();
|
||||
if (pubSub != null && !pubSub.isSubscribed())
|
||||
pubSub.unsubscribe();
|
||||
if (subscriber != null)
|
||||
subscriber.close();
|
||||
}
|
||||
|
||||
public void sendRedisMessage(@NotNull String channel, @NotNull String message) {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.publish(channel, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void subscribe() {
|
||||
new Thread(() -> {
|
||||
try (final Jedis jedis = password.isBlank() ?
|
||||
new Jedis(host, port, 0, useSSL) :
|
||||
new Jedis(host, port, DefaultJedisClientConfig
|
||||
.builder()
|
||||
.password(password)
|
||||
.timeoutMillis(0)
|
||||
.ssl(useSSL)
|
||||
.build())
|
||||
) {
|
||||
subscriber = jedis;
|
||||
subscriber.connect();
|
||||
pubSub = new JedisPubSub() {
|
||||
@Override
|
||||
public void onMessage(String channel, String message) {
|
||||
if (!channel.equals("cf_competition")) {
|
||||
return;
|
||||
}
|
||||
String[] split = message.split(";");
|
||||
String action = split[0];
|
||||
switch (action) {
|
||||
case "start" -> {
|
||||
// start competition for all the servers that connected to redis
|
||||
plugin.getCompetitionManager().startCompetition(split[1], true, false);
|
||||
}
|
||||
case "end" -> {
|
||||
if (plugin.getCompetitionManager().getOnGoingCompetition() != null)
|
||||
plugin.getCompetitionManager().getOnGoingCompetition().end();
|
||||
}
|
||||
case "stop" -> {
|
||||
if (plugin.getCompetitionManager().getOnGoingCompetition() != null)
|
||||
plugin.getCompetitionManager().getOnGoingCompetition().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
subscriber.subscribe(pubSub, "cf_competition");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.Redis;
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> getPlayerCount() {
|
||||
var future = new CompletableFuture<Integer>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
int players = 0;
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
var list = jedis.zrangeWithScores("cf_players",0, -1);
|
||||
for (Tuple tuple : list) {
|
||||
players += (int) tuple.getScore();
|
||||
}
|
||||
}
|
||||
future.complete(players);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> setServerPlayers(int amount, String unique) {
|
||||
var future = new CompletableFuture<Void>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.zadd("cf_players", amount, unique);
|
||||
}
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void removeServerPlayers(String unique) {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.zrem("cf_players", unique);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> setChangeServer(UUID uuid) {
|
||||
var future = new CompletableFuture<Void>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.setex(
|
||||
getRedisKey("cf_server", uuid),
|
||||
10,
|
||||
new byte[0]
|
||||
);
|
||||
}
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> getChangeServer(UUID uuid) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
byte[] key = getRedisKey("cf_server", uuid);
|
||||
if (jedis.get(key) != null) {
|
||||
jedis.del(key);
|
||||
future.complete(true);
|
||||
} else {
|
||||
future.complete(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean ignore) {
|
||||
var future = new CompletableFuture<Optional<PlayerData>>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
byte[] key = getRedisKey("cf_data", uuid);
|
||||
byte[] data = jedis.get(key);
|
||||
jedis.del(key);
|
||||
if (data != null) {
|
||||
future.complete(Optional.of(plugin.getStorageManager().fromBytes(data)));
|
||||
} else {
|
||||
future.complete(Optional.empty());
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean ignore) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.setex(
|
||||
getRedisKey("cf_data", uuid),
|
||||
10,
|
||||
plugin.getStorageManager().toBytes(playerData)
|
||||
);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private byte[] getRedisKey(String key, @NotNull UUID uuid) {
|
||||
return (key + ":" + uuid).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public abstract class AbstractHikariDatabase extends AbstractSQLDatabase {
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
private final String driverClass;
|
||||
private final String sqlBrand;
|
||||
|
||||
public AbstractHikariDatabase(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
this.driverClass = getStorageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
|
||||
this.sqlBrand = getStorageType() == StorageType.MariaDB ? "MariaDB" : "MySQL";
|
||||
try {
|
||||
Class.forName(this.driverClass);
|
||||
} catch (ClassNotFoundException e1) {
|
||||
if (getStorageType() == StorageType.MariaDB) {
|
||||
LogUtils.warn("No MariaDB driver is found");
|
||||
} else if (getStorageType() == StorageType.MySQL) {
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
LogUtils.warn("It seems that you are not using MySQL 8.0+. It's recommended to update.");
|
||||
} catch (ClassNotFoundException e2) {
|
||||
LogUtils.warn("No MySQL driver is found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
ConfigurationSection section = config.getConfigurationSection(sqlBrand);
|
||||
if (section == null) {
|
||||
LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one.");
|
||||
return;
|
||||
}
|
||||
|
||||
super.tablePrefix = section.getString("table-prefix", "customfishing");
|
||||
dataSource = new HikariDataSource();
|
||||
dataSource.setDriverClassName(driverClass);
|
||||
dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
|
||||
sqlBrand.toLowerCase(Locale.ENGLISH),
|
||||
section.getString("host", "localhost"),
|
||||
section.getString("port", "3306"),
|
||||
section.getString("database", "minecraft"),
|
||||
section.getString("connection-parameters")
|
||||
));
|
||||
|
||||
dataSource.setUsername(section.getString("user", "root"));
|
||||
dataSource.setPassword(section.getString("password", "pa55w0rd"));
|
||||
|
||||
dataSource.setMaximumPoolSize(section.getInt("Pool-Settings.max-pool-size", 10));
|
||||
dataSource.setMinimumIdle(section.getInt("Pool-Settings.min-idle", 10));
|
||||
dataSource.setMaxLifetime(section.getLong("Pool-Settings.max-lifetime", 180000L));
|
||||
dataSource.setKeepaliveTime(section.getLong("Pool-Settings.keep-alive-time", 60000L));
|
||||
dataSource.setConnectionTimeout(section.getLong("Pool-Settings.time-out", 20000L));
|
||||
dataSource.setPoolName("CustomFishingHikariPool");
|
||||
|
||||
final Properties properties = new Properties();
|
||||
properties.putAll(
|
||||
Map.of("cachePrepStmts", "true",
|
||||
"prepStmtCacheSize", "250",
|
||||
"prepStmtCacheSqlLimit", "2048",
|
||||
"useServerPrepStmts", "true",
|
||||
"useLocalSessionState", "true",
|
||||
"useLocalTransactionState", "true"
|
||||
));
|
||||
properties.putAll(
|
||||
Map.of(
|
||||
"rewriteBatchedStatements", "true",
|
||||
"cacheResultSetMetadata", "true",
|
||||
"cacheServerConfiguration", "true",
|
||||
"elideSetAutoCommits", "true",
|
||||
"maintainTimeStats", "false")
|
||||
);
|
||||
dataSource.setDataSourceProperties(properties);
|
||||
super.createTableIfNotExist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if (dataSource != null && !dataSource.isClosed())
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import net.momirealms.customfishing.storage.method.AbstractStorage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class AbstractSQLDatabase extends AbstractStorage {
|
||||
|
||||
protected String tablePrefix;
|
||||
|
||||
public AbstractSQLDatabase(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
||||
|
||||
public void createTableIfNotExist() {
|
||||
try (Connection connection = getConnection()) {
|
||||
final String[] databaseSchema = getSchema(getStorageType().name().toLowerCase(Locale.ENGLISH));
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
for (String tableCreationStatement : databaseSchema) {
|
||||
statement.execute(tableCreationStatement);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to create tables", e);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to get sql connection", e);
|
||||
} catch (IOException e) {
|
||||
LogUtils.warn("Failed to get schema resource", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getSchema(@NotNull String fileName) throws IOException {
|
||||
return replaceSchemaPlaceholder(new String(Objects.requireNonNull(plugin.getResource("schema/" + fileName + ".sql"))
|
||||
.readAllBytes(), StandardCharsets.UTF_8)).split(";");
|
||||
}
|
||||
|
||||
private String replaceSchemaPlaceholder(@NotNull String sql) {
|
||||
return sql.replace("{prefix}", tablePrefix);
|
||||
}
|
||||
|
||||
public String getTableName(String sub) {
|
||||
return getTablePrefix() + "_" + sub;
|
||||
}
|
||||
|
||||
public String getTablePrefix() {
|
||||
return tablePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean force) {
|
||||
var future = new CompletableFuture<Optional<PlayerData>>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setString(1, uuid.toString());
|
||||
ResultSet rs = statement.executeQuery();
|
||||
if (rs.next()) {
|
||||
int lock = rs.getInt(2);
|
||||
if (!force && (lock != 0 && getCurrentSeconds() - Config.dataSaveInterval <= lock)) {
|
||||
statement.close();
|
||||
rs.close();
|
||||
connection.close();
|
||||
future.complete(Optional.empty());
|
||||
return;
|
||||
}
|
||||
final Blob blob = rs.getBlob("data");
|
||||
final byte[] dataByteArray = blob.getBytes(1, (int) blob.length());
|
||||
blob.free();
|
||||
future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray)));
|
||||
} else if (Bukkit.getPlayer(uuid) != null) {
|
||||
var data = PlayerData.empty();
|
||||
insertPlayerData(uuid, data);
|
||||
future.complete(Optional.of(data));
|
||||
} else {
|
||||
future.complete(Optional.of(PlayerData.NEVER_PLAYED));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to get " + uuid + "'s data.", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean unlock) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setInt(1, unlock ? 0 : getCurrentSeconds());
|
||||
statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData)));
|
||||
statement.setString(3, uuid.toString());
|
||||
statement.executeUpdate();
|
||||
future.complete(true);
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to update " + uuid + "'s data.", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void insertPlayerData(UUID uuid, PlayerData playerData) {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.setInt(2, getCurrentSeconds());
|
||||
statement.setBlob(3, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData)));
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to insert " + uuid + "'s data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SqlConstants {
|
||||
public static final String SQL_SELECT_BY_UUID = "SELECT * FROM `%s` WHERE `uuid` = ?";
|
||||
public static final String SQL_UPDATE_BY_UUID = "UPDATE `%s` SET `lock` = ?, `data` = ? WHERE `uuid` = ?";
|
||||
public static final String SQL_INSERT_DATA_BY_UUID = "INSERT INTO `%s`(`uuid`, `lock`, `data`) VALUES(?, ?, ?)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.h2.jdbcx.JdbcConnectionPool;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class H2Impl extends AbstractSQLDatabase {
|
||||
|
||||
private JdbcConnectionPool connectionPool;
|
||||
|
||||
public H2Impl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
File databaseFile = new File(plugin.getDataFolder(), config.getString("H2.file", "data.db"));
|
||||
super.tablePrefix = config.getString("H2.table-prefix", "customfishing");
|
||||
|
||||
final String url = String.format("jdbc:h2:%s", databaseFile.getAbsolutePath());
|
||||
this.connectionPool = JdbcConnectionPool.create(url, "sa", "");
|
||||
super.createTableIfNotExist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if (connectionPool != null) {
|
||||
connectionPool.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.H2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return connectionPool.getConnection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
|
||||
public class MariaDBImpl extends AbstractHikariDatabase {
|
||||
|
||||
public MariaDBImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.MariaDB;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
|
||||
public class MySQLImpl extends AbstractHikariDatabase {
|
||||
|
||||
public MySQLImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.MySQL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package net.momirealms.customfishing.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.sqlite.SQLiteConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SQLiteImpl extends AbstractSQLDatabase {
|
||||
|
||||
private Connection connection;
|
||||
private File databaseFile;
|
||||
|
||||
public SQLiteImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
YamlConfiguration config = plugin.getConfig("database.yml");
|
||||
this.databaseFile = new File(plugin.getDataFolder(), config.getString("SQLite.file", "data") + ".db");
|
||||
super.tablePrefix = config.getString("SQLite.table-prefix", "customfishing");
|
||||
super.createTableIfNotExist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
try {
|
||||
if (connection != null && !connection.isClosed())
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.SQLite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
if (connection == null || connection.isClosed()) {
|
||||
setConnection();
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean force) {
|
||||
var future = new CompletableFuture<Optional<PlayerData>>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setString(1, uuid.toString());
|
||||
ResultSet rs = statement.executeQuery();
|
||||
if (rs.next()) {
|
||||
int lock = rs.getInt(2);
|
||||
if (!force && (lock != 0 && getCurrentSeconds() - Config.dataSaveInterval <= lock)) {
|
||||
statement.close();
|
||||
rs.close();
|
||||
connection.close();
|
||||
future.complete(Optional.empty());
|
||||
return;
|
||||
}
|
||||
final byte[] dataByteArray = rs.getBytes("data");
|
||||
future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray)));
|
||||
} else if (Bukkit.getPlayer(uuid) != null) {
|
||||
var data = PlayerData.empty();
|
||||
insertPlayerData(uuid, data);
|
||||
future.complete(Optional.of(data));
|
||||
} else {
|
||||
future.complete(Optional.of(PlayerData.NEVER_PLAYED));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to get " + uuid + "'s data.", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean unlock) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setInt(1, unlock ? 0 : getCurrentSeconds());
|
||||
statement.setBytes(2, plugin.getStorageManager().toBytes(playerData));
|
||||
statement.setString(3, uuid.toString());
|
||||
statement.executeUpdate();
|
||||
future.complete(true);
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to update " + uuid + "'s data.", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertPlayerData(UUID uuid, PlayerData playerData) {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.setInt(2, getCurrentSeconds());
|
||||
statement.setBytes(3, plugin.getStorageManager().toBytes(playerData));
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to insert " + uuid + "'s data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setConnection() {
|
||||
try {
|
||||
if (!databaseFile.exists()) databaseFile.createNewFile();
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
SQLiteConfig config = new SQLiteConfig();
|
||||
config.enforceForeignKeys(true);
|
||||
config.setEncoding(SQLiteConfig.Encoding.UTF8);
|
||||
config.setSynchronous(SQLiteConfig.SynchronousMode.FULL);
|
||||
connection = DriverManager.getConnection(
|
||||
String.format("jdbc:sqlite:%s", databaseFile.getAbsolutePath()),
|
||||
config.toProperties()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
LogUtils.warn("Failed to create the SQLite database.", e);
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to initialize SQLite database.", e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
LogUtils.warn("Failed to find SQLite driver.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.storage.method.file;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StorageType;
|
||||
import net.momirealms.customfishing.storage.method.AbstractStorage;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class JsonImpl extends AbstractStorage {
|
||||
|
||||
public JsonImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
File folder = new File(plugin.getDataFolder(), "data");
|
||||
if (!folder.exists()) folder.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.JSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean ignore) {
|
||||
File file = getPlayerDataFile(uuid);
|
||||
PlayerData playerData;
|
||||
if (file.exists()) {
|
||||
playerData = readFromJsonFile(file, PlayerData.class);
|
||||
} else if (Bukkit.getPlayer(uuid) != null) {
|
||||
playerData = PlayerData.empty();
|
||||
} else {
|
||||
playerData = PlayerData.NEVER_PLAYED;
|
||||
}
|
||||
return CompletableFuture.completedFuture(Optional.of(playerData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean ignore) {
|
||||
this.saveToJsonFile(playerData, getPlayerDataFile(uuid));
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
public File getPlayerDataFile(UUID uuid) {
|
||||
return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".json");
|
||||
}
|
||||
|
||||
public void saveToJsonFile(Object obj, File filepath) {
|
||||
Gson gson = new Gson();
|
||||
try (FileWriter file = new FileWriter(filepath)) {
|
||||
gson.toJson(obj, file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T readFromJsonFile(File file, Class<T> classOfT) {
|
||||
Gson gson = new Gson();
|
||||
String jsonContent = new String(readFileToByteArray(file), StandardCharsets.UTF_8);
|
||||
return gson.fromJson(jsonContent, classOfT);
|
||||
}
|
||||
|
||||
public byte[] readFileToByteArray(File file) {
|
||||
byte[] fileBytes = new byte[(int) file.length()];
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
fis.read(fileBytes);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return fileBytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.momirealms.customfishing.storage.method.file;
|
||||
|
||||
import net.momirealms.customfishing.api.CustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.data.*;
|
||||
import net.momirealms.customfishing.api.util.LogUtils;
|
||||
import net.momirealms.customfishing.storage.method.AbstractStorage;
|
||||
import net.momirealms.customfishing.util.ConfigUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class YAMLImpl extends AbstractStorage {
|
||||
|
||||
public YAMLImpl(CustomFishingPlugin plugin) {
|
||||
super(plugin);
|
||||
File folder = new File(plugin.getDataFolder(), "data");
|
||||
if (!folder.exists()) folder.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.YAML;
|
||||
}
|
||||
|
||||
public File getPlayerDataFile(UUID uuid) {
|
||||
return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".yml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid, boolean ignore) {
|
||||
File dataFile = getPlayerDataFile(uuid);
|
||||
if (!dataFile.exists()) {
|
||||
if (Bukkit.getPlayer(uuid) != null) {
|
||||
return CompletableFuture.completedFuture(Optional.of(PlayerData.empty()));
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(Optional.of(PlayerData.NEVER_PLAYED));
|
||||
}
|
||||
}
|
||||
YamlConfiguration data = ConfigUtils.readData(dataFile);
|
||||
PlayerData playerData = new PlayerData.Builder()
|
||||
.setBagData(new InventoryData(data.getString("bag", ""), data.getInt("size", 9)))
|
||||
.setEarningData(new EarningData(data.getDouble("earnings"), data.getInt("date")))
|
||||
.setStats(getStatistics(data.getConfigurationSection("stats")))
|
||||
.setName(data.getString("name"))
|
||||
.build();
|
||||
return CompletableFuture.completedFuture(Optional.of(playerData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> setPlayData(UUID uuid, PlayerData playerData, boolean ignore) {
|
||||
YamlConfiguration data = new YamlConfiguration();
|
||||
data.set("name", playerData.getName());
|
||||
data.set("bag", playerData.getBagData().serialized);
|
||||
data.set("size", playerData.getBagData().size);
|
||||
data.set("date", playerData.getEarningData().date);
|
||||
data.set("earnings", playerData.getEarningData().earnings);
|
||||
ConfigurationSection section = data.createSection("stats");
|
||||
for (Map.Entry<String, Integer> entry : playerData.getStatistics().statisticMap.entrySet()) {
|
||||
section.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
try {
|
||||
data.save(getPlayerDataFile(uuid));
|
||||
} catch (IOException e) {
|
||||
LogUtils.warn("Failed to save player data", e);
|
||||
}
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
public StatisticData getStatistics(ConfigurationSection section) {
|
||||
if (section == null)
|
||||
return StatisticData.empty();
|
||||
else {
|
||||
HashMap<String, Integer> map = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
map.put(entry.getKey(), (Integer) entry.getValue());
|
||||
}
|
||||
return new StatisticData(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.momirealms.customfishing.storage.user;
|
||||
|
||||
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customfishing.api.data.EarningData;
|
||||
import net.momirealms.customfishing.api.data.InventoryData;
|
||||
import net.momirealms.customfishing.api.data.PlayerData;
|
||||
import net.momirealms.customfishing.api.data.StatisticData;
|
||||
import net.momirealms.customfishing.api.data.user.OfflineUser;
|
||||
import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder;
|
||||
import net.momirealms.customfishing.api.mechanic.statistic.Statistics;
|
||||
import net.momirealms.customfishing.api.util.InventoryUtils;
|
||||
import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl;
|
||||
import net.momirealms.customfishing.setting.Config;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflineUserImpl implements OfflineUser {
|
||||
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final FishingBagHolder holder;
|
||||
private final EarningData earningData;
|
||||
private final Statistics statistics;
|
||||
public static OfflineUserImpl NEVER_PLAYED_USER = new OfflineUserImpl(UUID.randomUUID(), "", PlayerData.empty());
|
||||
|
||||
public OfflineUserImpl(UUID uuid, String name, PlayerData playerData) {
|
||||
this.name = name;
|
||||
this.uuid = uuid;
|
||||
this.holder = new FishingBagHolder(uuid);
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
this.holder.setInventory(InventoryUtils.createInventory(this.holder, playerData.getBagData().size,
|
||||
AdventureManagerImpl.getInstance().getComponentFromMiniMessage(
|
||||
PlaceholderManagerImpl.getInstance().parse(
|
||||
offlinePlayer, Config.bagTitle, Map.of("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(String.valueOf(uuid)))
|
||||
)
|
||||
)));
|
||||
this.holder.setItems(InventoryUtils.getInventoryItems(playerData.getBagData().serialized));
|
||||
this.earningData = playerData.getEarningData();
|
||||
this.statistics = new Statistics(playerData.getStatistics());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FishingBagHolder getHolder() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EarningData getEarningData() {
|
||||
return earningData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
return player != null && player.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerData getPlayerData() {
|
||||
return new PlayerData.Builder()
|
||||
.setBagData(new InventoryData(InventoryUtils.stacksToBase64(holder.getInventory().getStorageContents()), holder.getInventory().getSize()))
|
||||
.setEarningData(earningData)
|
||||
.setStats(new StatisticData(statistics.getStatisticMap()))
|
||||
.setName(name)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user