diff --git a/src/main/java/com/alttd/altitudequests/AQuest.java b/src/main/java/com/alttd/altitudequests/AQuest.java index ed37698..c61d00a 100644 --- a/src/main/java/com/alttd/altitudequests/AQuest.java +++ b/src/main/java/com/alttd/altitudequests/AQuest.java @@ -1,11 +1,8 @@ package com.alttd.altitudequests; import com.alttd.altitudequests.commands.CommandManager; -import com.alttd.altitudequests.config.Config; -import com.alttd.altitudequests.config.DatabaseConfig; -import com.alttd.altitudequests.config.LocalConfig; -import com.alttd.altitudequests.config.MessagesConfig; -import com.alttd.altitudequests.events.TalkToQuest; +import com.alttd.altitudequests.config.*; +import com.alttd.altitudequests.events.*; import com.alttd.altitudequests.util.Logger; import org.bukkit.plugin.java.JavaPlugin; @@ -24,13 +21,9 @@ public final class AQuest extends JavaPlugin { @Override public void onEnable() { - Config.reload(); - DatabaseConfig.reload(); - MessagesConfig.reload(); - LocalConfig.reload(); - + reloadConfigs(); registerEvents(); - CommandManager commandManager = new CommandManager(); + new CommandManager(); Logger.info("--------------------------------------------------"); Logger.info("AQuest started"); @@ -42,7 +35,21 @@ public final class AQuest extends JavaPlugin { // Plugin shutdown logic } + public void reloadConfigs() { + Config.reload(); + DatabaseConfig.reload(); + MessagesConfig.reload(); + LocalConfig.reload(); + QuestsConfig.reload(); + } + private void registerEvents() { getServer().getPluginManager().registerEvents(new TalkToQuest(), this); + getServer().getPluginManager().registerEvents(new MineBlocks(), this); + getServer().getPluginManager().registerEvents(new LoginEvent(), this); + getServer().getPluginManager().registerEvents(new LogoutEvent(), this); + getServer().getPluginManager().registerEvents(new QuestComplete(), this); + getServer().getMessenger().registerOutgoingPluginChannel(this, "aquest:player-data"); + getServer().getMessenger().registerIncomingPluginChannel(this, "aquest:player-data", new PluginMessageListener()); } } diff --git a/src/main/java/com/alttd/altitudequests/commands/CommandManager.java b/src/main/java/com/alttd/altitudequests/commands/CommandManager.java index 5c29fc7..6bc7388 100644 --- a/src/main/java/com/alttd/altitudequests/commands/CommandManager.java +++ b/src/main/java/com/alttd/altitudequests/commands/CommandManager.java @@ -1,9 +1,7 @@ package com.alttd.altitudequests.commands; import com.alttd.altitudequests.AQuest; -import com.alttd.altitudequests.commands.subcommands.CommandCreateScruff; -import com.alttd.altitudequests.commands.subcommands.CommandHelp; -import com.alttd.altitudequests.commands.subcommands.CommandReload; +import com.alttd.altitudequests.commands.subcommands.*; import com.alttd.altitudequests.config.Config; import com.alttd.altitudequests.util.Logger; import org.bukkit.command.*; @@ -32,8 +30,10 @@ public class CommandManager implements CommandExecutor, TabExecutor { subCommands = Arrays.asList( new CommandHelp(this), - new CommandReload(), - new CommandCreateScruff()); + new CommandReload(AQuest.getInstance()), + new CommandCreateScruff(), + new CommandChangeQuest(), + new CommandTurnIn()); } @Override @@ -65,7 +65,7 @@ public class CommandManager implements CommandExecutor, TabExecutor { ); } else { SubCommand subCommand = getSubCommand(args[0]); - if (subCommand != null && commandSender.hasPermission(subCommand.getPermission())) + if (subCommand != null && subCommand.shouldTabComplete() && commandSender.hasPermission(subCommand.getPermission())) res.addAll(subCommand.getTabComplete(commandSender, args)); } return res; diff --git a/src/main/java/com/alttd/altitudequests/commands/SubCommand.java b/src/main/java/com/alttd/altitudequests/commands/SubCommand.java index 4cae5a6..c012821 100644 --- a/src/main/java/com/alttd/altitudequests/commands/SubCommand.java +++ b/src/main/java/com/alttd/altitudequests/commands/SubCommand.java @@ -28,4 +28,6 @@ public abstract class SubCommand { protected MiniMessage getMiniMessage() { return miniMessage; } + + public abstract boolean shouldTabComplete(); } diff --git a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandChangeQuest.java b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandChangeQuest.java new file mode 100644 index 0000000..2c169d1 --- /dev/null +++ b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandChangeQuest.java @@ -0,0 +1,37 @@ +package com.alttd.altitudequests.commands.subcommands; + +import com.alttd.altitudequests.commands.SubCommand; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public class CommandChangeQuest extends SubCommand { + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + //TODO check if they have money + //TODO error return if not, take money if they do + //TODO give new quest + return false; + } + + @Override + public String getName() { + return "change"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return new ArrayList<>(); + } + + @Override + public String getHelpMessage() { + return "\b"; + } + + @Override + public boolean shouldTabComplete() { + return false; + } +} diff --git a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandCreateScruff.java b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandCreateScruff.java index d6a560e..dd68aa9 100644 --- a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandCreateScruff.java +++ b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandCreateScruff.java @@ -55,7 +55,7 @@ public class CommandCreateScruff extends SubCommand { LocalConfig.setActiveNPC(uuid); commandSender.sendMiniMessage("Spawned Scruff", null); - return true; + return true; //TODO make sure scruff can't be put in boats or killed } @Override @@ -105,4 +105,9 @@ public class CommandCreateScruff extends SubCommand { public String getHelpMessage() { return MessagesConfig.CREATE_SCRUFF_MESSAGE; } + + @Override + public boolean shouldTabComplete() { + return true; + } } diff --git a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandHelp.java b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandHelp.java index 713dadb..1acd043 100644 --- a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandHelp.java +++ b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandHelp.java @@ -42,4 +42,9 @@ public class CommandHelp extends SubCommand { public String getHelpMessage() { return MessagesConfig.HELP_MESSAGE; } + + @Override + public boolean shouldTabComplete() { + return true; + } } diff --git a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandReload.java b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandReload.java index c50f189..bb6485a 100644 --- a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandReload.java +++ b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandReload.java @@ -1,10 +1,8 @@ package com.alttd.altitudequests.commands.subcommands; +import com.alttd.altitudequests.AQuest; import com.alttd.altitudequests.commands.SubCommand; -import com.alttd.altitudequests.config.Config; -import com.alttd.altitudequests.config.DatabaseConfig; -import com.alttd.altitudequests.config.LocalConfig; -import com.alttd.altitudequests.config.MessagesConfig; +import com.alttd.altitudequests.config.*; import org.bukkit.command.CommandSender; import java.util.ArrayList; @@ -12,12 +10,15 @@ import java.util.List; public class CommandReload extends SubCommand { + private final AQuest plugin; + + public CommandReload(AQuest plugin) { + this.plugin = plugin; + } + @Override public boolean onCommand(CommandSender commandSender, String[] args) { - Config.reload(); - DatabaseConfig.reload(); - MessagesConfig.reload(); - LocalConfig.reload(); + plugin.reloadConfigs(); commandSender.sendMiniMessage("Reloaded AltitudeQuests config.", null); return true; } @@ -36,4 +37,9 @@ public class CommandReload extends SubCommand { public String getHelpMessage() { return MessagesConfig.RELOAD_HELP_MESSAGE; } + + @Override + public boolean shouldTabComplete() { + return true; + } } diff --git a/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandTurnIn.java b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandTurnIn.java new file mode 100644 index 0000000..b08bee2 --- /dev/null +++ b/src/main/java/com/alttd/altitudequests/commands/subcommands/CommandTurnIn.java @@ -0,0 +1,55 @@ +package com.alttd.altitudequests.commands.subcommands; + +import com.alttd.altitudequests.commands.SubCommand; +import com.alttd.altitudequests.config.Config; +import com.alttd.altitudequests.config.LocalConfig; +import com.alttd.altitudequests.objects.Quest; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class CommandTurnIn extends SubCommand { + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + if (args.length != 1) + return true; + if (!(commandSender instanceof Player player)) { + commandSender.sendMiniMessage(Config.NO_CONSOLE, null); + return true; + } + Quest dailyQuest = Quest.getDailyQuest(player.getUniqueId()); + if (dailyQuest == null) { + player.sendMiniMessage(Config.DAILY_ALREADY_DONE, null); + return true; + } + if (player.getNearbyEntities(5, 5, 5).stream() + .noneMatch(entity -> entity.getUniqueId().equals(LocalConfig.activeNPC))) { + player.sendMiniMessage(Config.TOO_FAR_FROM_NPC, null); + return true; + } + dailyQuest.turnIn(player); + return true; + } + + @Override + public String getName() { + return "turnin"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return new ArrayList<>(); + } + + @Override + public String getHelpMessage() { + return "\b"; + } + + @Override + public boolean shouldTabComplete() { + return false; + } +} diff --git a/src/main/java/com/alttd/altitudequests/config/Config.java b/src/main/java/com/alttd/altitudequests/config/Config.java index f32be8a..a2572be 100644 --- a/src/main/java/com/alttd/altitudequests/config/Config.java +++ b/src/main/java/com/alttd/altitudequests/config/Config.java @@ -35,6 +35,13 @@ public final class Config extends AbstractConfig { QUEST_PAGES = config.getStringList("book.pages", QUEST_PAGES); } + public static String TOO_FAR_FROM_NPC = "You are too far from Scruff";//TODO replace scruff with ? + public static String DAILY_ALREADY_DONE = "You already completed your daily quest"; + private static void loadMessages() { + TOO_FAR_FROM_NPC = config.getString("messages.too-far-from-npc", TOO_FAR_FROM_NPC); + DAILY_ALREADY_DONE = config.getString("messages.daily-already-done", DAILY_ALREADY_DONE); + } + private static void loadGUIText() { } diff --git a/src/main/java/com/alttd/altitudequests/config/QuestsConfig.java b/src/main/java/com/alttd/altitudequests/config/QuestsConfig.java index 47b4bbb..e929d3b 100644 --- a/src/main/java/com/alttd/altitudequests/config/QuestsConfig.java +++ b/src/main/java/com/alttd/altitudequests/config/QuestsConfig.java @@ -1,10 +1,7 @@ package com.alttd.altitudequests.config;; import com.alttd.altitudequests.objects.MineQuestObject; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import com.alttd.altitudequests.util.Logger; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; @@ -12,47 +9,38 @@ import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; public class QuestsConfig extends AbstractConfig { static QuestsConfig config; - static int version; public QuestsConfig() { - super(new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "AltitudeQuests"), "quests.yml"); + super(new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + + File.separator + "AltitudeQuests"), "quests.yml"); } public static void reload() { config = new QuestsConfig(); - - version = config.getInt("config-version", 1); - config.set("config-version", 1); - config.readConfig(QuestsConfig.class, null); } - String getString(String path, String def) { - yaml.addDefault(path, def); - return yaml.getString(path, yaml.getString(path)); - } - public static List MINE_QUESTS = new ArrayList<>(); + private static void loadMineQuests() { MINE_QUESTS.clear(); ConfigurationSection configurationSection = config.getConfigurationSection("mining.possible_tasks"); + if (configurationSection == null) { + Logger.warning("No mining quests in config"); + return; + } Set keys = configurationSection.getKeys(false); - MiniMessage miniMessage = MiniMessage.miniMessage(); for (String key : keys) { - Material material = Material.valueOf(configurationSection.getString(key + "material")); - - TagResolver resolver = TagResolver.resolver(Placeholder.unparsed("block", material.name().toLowerCase())); - List collect = configurationSection.getStringList(key + "pages").stream() - .map(page -> miniMessage.deserialize(page, resolver)) - .collect(Collectors.toList()); + Material material = Material.valueOf(configurationSection.getString(key + ".material")); +//TODO maybe have pages for in progress and pages for done??? + List collect = configurationSection.getStringList(key + ".pages"); MINE_QUESTS.add(new MineQuestObject(key, - configurationSection.getString(key + "name"), + configurationSection.getString(key + ".name"), material, - configurationSection.getInt(key + "amount"), + configurationSection.getInt(key + ".amount"), collect)); } } diff --git a/src/main/java/com/alttd/altitudequests/database/Database.java b/src/main/java/com/alttd/altitudequests/database/Database.java index 7440689..931efcc 100644 --- a/src/main/java/com/alttd/altitudequests/database/Database.java +++ b/src/main/java/com/alttd/altitudequests/database/Database.java @@ -5,6 +5,9 @@ import com.alttd.altitudequests.config.DatabaseConfig; import com.alttd.altitudequests.util.Logger; import org.bukkit.Bukkit; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -12,11 +15,9 @@ import java.sql.SQLException; public class Database { private static Database instance = null; - public static Connection connection = null; + private Connection connection = null; - private Database() { - - } + private Database() {} public static Database getDatabase(){ if (instance == null) @@ -31,8 +32,22 @@ public class Database { e.printStackTrace(); } - // Tables - createUserPointsTable(); + //Run all create table functions + for (Method method : Database.class.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex.getCause()); + } catch (Exception ex) { + Logger.severe("Error invoking %.", method.toString()); + ex.printStackTrace(); + } + } + } + } } /** @@ -61,16 +76,26 @@ public class Database { } } + public Connection getConnection() { + try { + openConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + return connection; + } + private static void createUserPointsTable() { try { - String sql = "CREATE TABLE IF NOT EXISTS quest_progress(" + + String sql = "CREATE TABLE IF NOT EXISTS generic_quest_progress(" + "uuid VARCHAR(36) NOT NULL, " + "quest VARCHAR(36) NOT NULL, " + - "prepare_progress INT NOT NULL, " + - "turn_in_progress INT NOT NULL, " + + "quest_variant VARCHAR(36) NOT NULL, " + + "step_1_progress INT NOT NULL, " + + "step_2_progress INT NOT NULL, " + "PRIMARY KEY (UUID, quest)" + ")"; - connection.prepareStatement(sql).executeUpdate(); + getDatabase().getConnection().prepareStatement(sql).executeUpdate(); } catch (SQLException e) { e.printStackTrace(); Logger.severe("Error while trying to create user point table"); diff --git a/src/main/java/com/alttd/altitudequests/database/Queries.java b/src/main/java/com/alttd/altitudequests/database/Queries.java index 63f6681..10a820b 100644 --- a/src/main/java/com/alttd/altitudequests/database/Queries.java +++ b/src/main/java/com/alttd/altitudequests/database/Queries.java @@ -15,7 +15,7 @@ public class Queries { long time; try { - PreparedStatement preparedStatement = Database.connection.prepareStatement(sql); + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); preparedStatement.setString(1, uuid.toString()); preparedStatement.setInt(3, progress); preparedStatement.setString(4, uuid.toString()); diff --git a/src/main/java/com/alttd/altitudequests/events/LoginEvent.java b/src/main/java/com/alttd/altitudequests/events/LoginEvent.java new file mode 100644 index 0000000..54be8d6 --- /dev/null +++ b/src/main/java/com/alttd/altitudequests/events/LoginEvent.java @@ -0,0 +1,13 @@ +package com.alttd.altitudequests.events; + +import com.alttd.altitudequests.objects.Quest; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class LoginEvent implements Listener { + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Quest.tryLoadDailyQuest(event.getPlayer().getUniqueId()); + } +} diff --git a/src/main/java/com/alttd/altitudequests/events/LogoutEvent.java b/src/main/java/com/alttd/altitudequests/events/LogoutEvent.java new file mode 100644 index 0000000..e5e7013 --- /dev/null +++ b/src/main/java/com/alttd/altitudequests/events/LogoutEvent.java @@ -0,0 +1,31 @@ +package com.alttd.altitudequests.events; + +import com.alttd.altitudequests.AQuest; +import com.alttd.altitudequests.config.Config; +import com.alttd.altitudequests.objects.Quest; +import com.alttd.altitudequests.util.Logger; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.UUID; + +public class LogoutEvent implements Listener { + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + UUID uuid = event.getPlayer().getUniqueId(); + + if (Config.DEBUG) + Logger.info("Syncing %", event.getPlayer().getName()); + Quest.unloadUser(uuid); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("try-unlock"); + out.writeUTF(uuid.toString()); + Bukkit.getServer().sendPluginMessage(AQuest.getInstance(), + "aquest:player-data", + out.toByteArray()); + } +} diff --git a/src/main/java/com/alttd/altitudequests/events/MineBlocks.java b/src/main/java/com/alttd/altitudequests/events/MineBlocks.java index 2e8bedf..7fce06d 100644 --- a/src/main/java/com/alttd/altitudequests/events/MineBlocks.java +++ b/src/main/java/com/alttd/altitudequests/events/MineBlocks.java @@ -12,7 +12,7 @@ import java.util.UUID; public class MineBlocks implements Listener { - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR) public void onBlockBreak(BlockBreakEvent event) { Player player = event.getPlayer(); UUID uuid = player.getUniqueId(); @@ -20,7 +20,7 @@ public class MineBlocks implements Listener { if (quest == null || quest.isDone()) return; if (quest instanceof MineQuest mineQuest) - mineQuest.mine(event.getBlock(), player); + mineQuest.mine(event.getBlock()); } } diff --git a/src/main/java/com/alttd/altitudequests/events/PluginMessageListener.java b/src/main/java/com/alttd/altitudequests/events/PluginMessageListener.java new file mode 100644 index 0000000..d50869c --- /dev/null +++ b/src/main/java/com/alttd/altitudequests/events/PluginMessageListener.java @@ -0,0 +1,71 @@ +package com.alttd.altitudequests.events; + +import com.alttd.altitudequests.AQuest; +import com.alttd.altitudequests.config.Config; +import com.alttd.altitudequests.objects.Quest; +import com.alttd.altitudequests.util.Logger; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class PluginMessageListener implements org.bukkit.plugin.messaging.PluginMessageListener { + + @Override + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] bytes) { + if (!channel.equals("aquest:player-data")) { + Logger.warning("Received plugin message on invalid channel"); + return; + } + ByteArrayDataInput in = ByteStreams.newDataInput(bytes); + switch (in.readUTF()) { + case "try-lock-result" -> { + if (!in.readBoolean()) { + Logger.warning("Unable to lock row"); + return; //TODO handle + } + UUID uuid = UUID.fromString(in.readUTF()); + if (Config.DEBUG) + Logger.warning("Received positive log result for %, loading user", uuid.toString()); + loadUser(uuid); + } + case "queue-lock-failed" -> Logger.warning("Encountered uuid that was locked and had a lock queued: %, lock is from %", in.readUTF(), in.readUTF()); + case "try-unlock-result" -> { + if (in.readBoolean()) { + // ignore? + return; + } + Logger.severe("Unable to unlock %.", in.readUTF()); + } + case "locked-queue-lock" -> { + if (!in.readBoolean()) { + Logger.warning("Got false back from locked queue lock"); + return; //TODO handle + } + UUID uuid = UUID.fromString(in.readUTF()); + if (Config.DEBUG) + Logger.warning("Received positive log result for %, loading user", uuid.toString()); + loadUser(uuid); + } + case "check-lock-result" -> { + + } + } + } + + private void loadUser(UUID uuid) { + new BukkitRunnable() { + @Override + public void run() { + //TODO load user from database + //TODO if no quest in database create one + System.out.println("creating quest"); + Quest.createDailyQuest(Bukkit.getPlayer(uuid)); + } + }.runTaskAsynchronously(AQuest.getInstance()); + } +} diff --git a/src/main/java/com/alttd/altitudequests/events/TalkToQuest.java b/src/main/java/com/alttd/altitudequests/events/TalkToQuest.java index 02b3fce..b600eed 100644 --- a/src/main/java/com/alttd/altitudequests/events/TalkToQuest.java +++ b/src/main/java/com/alttd/altitudequests/events/TalkToQuest.java @@ -3,7 +3,9 @@ package com.alttd.altitudequests.events; import com.alttd.altitudequests.AQuest; import com.alttd.altitudequests.config.Config; import com.alttd.altitudequests.config.LocalConfig; +import com.alttd.altitudequests.objects.Quest; import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -24,41 +26,54 @@ public class TalkToQuest implements Listener { private static final MiniMessage miniMessage = MiniMessage.miniMessage(); - private static final Set tmp = new HashSet<>(); + private static final Set inProcess = new HashSet<>(); @EventHandler(priority = EventPriority.HIGH) public void onEntityInteract(PlayerInteractEntityEvent event) { if (LocalConfig.activeNPC == null || !LocalConfig.activeNPC.equals(event.getRightClicked().getUniqueId())) return; event.setCancelled(true); - if (!tmp.remove(event.getPlayer().getUniqueId())) { - tmp.add(event.getPlayer().getUniqueId()); + final Player player = event.getPlayer(); + final UUID uniqueId = player.getUniqueId(); + if (inProcess.contains(uniqueId)) return; - } - event.getPlayer().sendMiniMessage("Interacted with scruff - DEBUG", null); + + inProcess.add(uniqueId); new BukkitRunnable() { @Override public void run() { - event.getPlayer().openBook(getBook(event)); + player.openBook(getBook(event)); + inProcess.remove(uniqueId); } - }.runTaskTimerAsynchronously(AQuest.getInstance(), 0, 0); - //TODO make it so there can be one book config per quest + }.runTaskAsynchronously(AQuest.getInstance()); //TODO make it so everything can be done with commands and just don't let them tab complete and do them through the book instead //TODO in config allow a multitude of events to be prepared and randomly select from them at certain times of day? } private Book getBook (PlayerInteractEntityEvent event) { Player player = event.getPlayer(); + return Book.builder() .author(miniMessage.deserialize(Config.QUEST_BOOK_AUTHOR)) .title(miniMessage.deserialize(Config.QUEST_BOOK_TITLE)) - .pages(getPages(player).stream() - .map(page -> miniMessage.deserialize(page, TagResolver.resolver(Placeholder.component("player", player.name())))) - .collect(Collectors.toList())) + .pages(getPages(player)) .build(); } - private List getPages(Player player) { - return (Config.QUEST_PAGES); + private static final Component error = MiniMessage.miniMessage().deserialize("Error retrieving quest data"); + private List getPages(Player player) { + Quest dailyQuest = Quest.getDailyQuest(player.getUniqueId()); + if (dailyQuest == null) + return List.of(error); + TagResolver tagResolver = TagResolver.resolver( + TagResolver.resolver(Placeholder.component("player", player.name())), + TagResolver.resolver(Placeholder.parsed("br", "\n")), + dailyQuest.getTagResolvers() + ); + //TODO add weekly quest? + List pages = dailyQuest.getPages().stream() + .map(page -> miniMessage.deserialize(page, tagResolver)) + .collect(Collectors.toList()); + return (pages); } } \ No newline at end of file diff --git a/src/main/java/com/alttd/altitudequests/objects/MineQuestObject.java b/src/main/java/com/alttd/altitudequests/objects/MineQuestObject.java index 508c784..fc031f2 100644 --- a/src/main/java/com/alttd/altitudequests/objects/MineQuestObject.java +++ b/src/main/java/com/alttd/altitudequests/objects/MineQuestObject.java @@ -1,6 +1,5 @@ package com.alttd.altitudequests.objects; -import net.kyori.adventure.text.Component; import org.bukkit.Material; import java.util.List; @@ -11,9 +10,9 @@ public class MineQuestObject { String name; Material material; int amount; - List pages; + List pages; - public MineQuestObject(String internalName, String name, Material material, int amount, List pages) { + public MineQuestObject(String internalName, String name, Material material, int amount, List pages) { this.internalName = internalName; this.name = name; this.material = material; @@ -37,7 +36,7 @@ public class MineQuestObject { return amount; } - public List getPages() { + public List getPages() { return pages; } } diff --git a/src/main/java/com/alttd/altitudequests/objects/Quest.java b/src/main/java/com/alttd/altitudequests/objects/Quest.java index ee7ce51..b78d2a3 100644 --- a/src/main/java/com/alttd/altitudequests/objects/Quest.java +++ b/src/main/java/com/alttd/altitudequests/objects/Quest.java @@ -1,40 +1,48 @@ package com.alttd.altitudequests.objects; -import com.alttd.altitudequests.config.QuestsConfig; +import com.alttd.altitudequests.AQuest; +import com.alttd.altitudequests.config.Config; import com.alttd.altitudequests.objects.quests.MineQuest; +import com.alttd.altitudequests.util.Logger; +import com.alttd.altitudequests.util.Utilities; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; -import java.util.HashMap; -import java.util.Random; -import java.util.UUID; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; public abstract class Quest { private static final HashMap dailyQuests = new HashMap<>(); private static Quest weeklyQuest = null; - private static final String[] possibleQuests; + private static final List> possibleQuests = new ArrayList<>(); //TODO add all data every quest needs static { - possibleQuests = new String[]{"MineQuest"}; - } - - public Quest() { + possibleQuests.add(MineQuest.class); } public static void createDailyQuest(Player player) { - Random random = new Random(); - String questName = possibleQuests[random.nextInt(0, possibleQuests.length - 1)]; - Quest quest = null; - switch (questName) { - case "MineQuest" -> { - quest = new MineQuest(QuestsConfig.MINE_QUESTS.get(random.nextInt(0, QuestsConfig.MINE_QUESTS.size() - 1))); - } + if (possibleQuests.size() == 0) { + player.sendMiniMessage("Unable to create quest, no quests in config", null); + return; + } + + Class questClass = possibleQuests.get(Utilities.randomOr0(possibleQuests.size() - 1)); + try { + Constructor constructor = questClass.getDeclaredConstructor(UUID.class); + dailyQuests.put(player.getUniqueId(), constructor.newInstance(player.getUniqueId())); + } catch (InvocationTargetException | IllegalAccessException | InstantiationException | NoSuchMethodException e) { + player.sendMiniMessage("Unable to create quest, contact an admin", null); + e.printStackTrace(); + Logger.severe("% does not have a constructor with a UUID input or has improper access.", questClass.getName()); } - if (quest == null) - return; //TODO error - dailyQuests.put(player.getUniqueId(), quest); } public static Quest getDailyQuest(UUID uuid) { @@ -48,5 +56,48 @@ public abstract class Quest { Quest.weeklyQuest = newQuest; } + private static HashSet queriedUsers = new HashSet<>(); + public static void tryLoadDailyQuest(UUID uuid) { //TODO set up a way to listen to the response and load stuff + if (queriedUsers.contains(uuid) || dailyQuests.containsKey(uuid)) + return; + queriedUsers.add(uuid); + new BukkitRunnable() { + @Override + public void run() { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("try-lock"); + out.writeUTF(uuid.toString()); + Bukkit.getServer().sendPluginMessage(AQuest.getInstance(), + "aquest:player-data", + out.toByteArray()); + if (Config.DEBUG) + Logger.info("Send lock request for %", uuid.toString()); + } + }.runTaskAsynchronously(AQuest.getInstance()); + } + + public static void unloadUser(UUID uuid) { + queriedUsers.remove(uuid); + Quest quest = dailyQuests.remove(uuid); + if (quest == null) + return; + new BukkitRunnable() { + @Override + public void run() { + quest.save(); + } + }.runTaskAsynchronously(AQuest.getInstance()); + } + + protected abstract void save(); + public abstract boolean isDone(); + + public abstract String getName(); + + public abstract List getPages(); + + public abstract TagResolver getTagResolvers(); + + public abstract int turnIn(Player player); } diff --git a/src/main/java/com/alttd/altitudequests/objects/quests/MineQuest.java b/src/main/java/com/alttd/altitudequests/objects/quests/MineQuest.java index 116181c..f0a4d55 100644 --- a/src/main/java/com/alttd/altitudequests/objects/quests/MineQuest.java +++ b/src/main/java/com/alttd/altitudequests/objects/quests/MineQuest.java @@ -1,46 +1,139 @@ package com.alttd.altitudequests.objects.quests; import com.alttd.altitudequests.config.QuestsConfig; +import com.alttd.altitudequests.database.Database; import com.alttd.altitudequests.objects.MineQuestObject; import com.alttd.altitudequests.objects.Quest; import com.alttd.altitudequests.objects.QuestCompleteEvent; +import com.alttd.altitudequests.util.Utilities; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; -import java.util.Optional; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; public class MineQuest extends Quest { - int mined; - int turnedIn; - MineQuestObject mineQuestObject; - boolean isDone = false; + private final UUID uuid; + private int mined; + private int turnedIn; + private final MineQuestObject mineQuestObject; + private boolean isDone = false; - public MineQuest(MineQuestObject mineQuestObject) { + public MineQuest(UUID uuid) { + this.uuid = uuid; mined = 0; turnedIn = 0; - this.mineQuestObject = mineQuestObject; + this.mineQuestObject = QuestsConfig.MINE_QUESTS.get(Utilities.randomOr0(QuestsConfig.MINE_QUESTS.size() - 1)); } - public MineQuest(int mined, int turnedIn, String internalName) { + public MineQuest(int mined, int turnedIn, String variantInternalName, UUID uuid) { this.mined = mined; this.turnedIn = turnedIn; - Optional any = QuestsConfig.MINE_QUESTS.stream().filter(object -> internalName.equals(object.getInternalName())).findAny(); - if (any.isEmpty()) + this.uuid = uuid; + Optional any = QuestsConfig.MINE_QUESTS.stream().filter(object -> variantInternalName.equals(object.getInternalName())).findAny(); + if (any.isEmpty()) { + this.mineQuestObject = null; return; //TODO error + } this.mineQuestObject = any.get(); } + @Override + protected void save() { + String sql = "INSERT INTO generic_quest_progress " + + "(uuid, quest, quest_variant, step_1_progress, step_2_progress) " + + "VALUES (?, ?, ?, ?) " + + "ON DUPLICATE KEY UPDATE" + + "quest = ?, quest_variant = ?, step_1_progress = ?, step_2_progress = ?"; + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + statement.setString(1, uuid.toString()); + statement.setString(2, getName()); + statement.setString(3, mineQuestObject.getInternalName()); + statement.setInt(4, mined); + statement.setInt(5, turnedIn); + statement.setString(6, getName()); + statement.setString(7, mineQuestObject.getInternalName()); + statement.setInt(8, mined); + statement.setInt(9, turnedIn); + } catch (SQLException exception) { + exception.printStackTrace(); + } + } + @Override public boolean isDone() { return isDone; } - public void mine(Block block, Player player) { - if (!isDone && !block.getType().equals(mineQuestObject.getMaterial())) + @Override + public String getName() { + return "mining"; + } + + @Override + public List getPages() { + return mineQuestObject.getPages(); + } + + @Override + public TagResolver getTagResolvers() { + return TagResolver.resolver( + Placeholder.unparsed("block", mineQuestObject.getMaterial().name()), + Placeholder.parsed("mined", mined == mineQuestObject.getAmount() ? + "" + mined + "" : "" + mined + ""), + Placeholder.parsed("total_to_mine", String.valueOf(mineQuestObject.getAmount())), + Placeholder.parsed("turned_in", turnedIn == mineQuestObject.getAmount() ? + "" + turnedIn + "" : "" + turnedIn + ""), + Placeholder.parsed("total_to_turn_in", String.valueOf(mineQuestObject.getAmount())) + ); + } + + @Override + public int turnIn(Player player) { + PlayerInventory inventory = player.getInventory(); + int maxToTurnIn = Math.min(mineQuestObject.getAmount() - turnedIn, mined); + + if (maxToTurnIn == 0) + return 0; + var ref = new Object() { + int tmpAmount = maxToTurnIn; + }; + + Arrays.stream(inventory.getContents()) + .filter(Objects::nonNull) + .filter(itemStack -> itemStack.getType().equals(mineQuestObject.getMaterial())) + .forEach(itemStack -> { + if (ref.tmpAmount == 0) + return; + if (itemStack.getAmount() > ref.tmpAmount) { + itemStack.setAmount(itemStack.getAmount() - ref.tmpAmount); + ref.tmpAmount = 0; + } else { + ref.tmpAmount -= itemStack.getAmount(); + itemStack.setAmount(0); + } + }); + int totalTurnedIn = maxToTurnIn - ref.tmpAmount; + turnedIn += totalTurnedIn; + checkDone(player); + return totalTurnedIn; + } + + public void mine(Block block) { + if (isDone || mined == mineQuestObject.getAmount() || !block.getType().equals(mineQuestObject.getMaterial())) return; mined += 1; - if (mined == mineQuestObject.getAmount()) { + } + + public void checkDone(Player player) { + if (turnedIn == mineQuestObject.getAmount() && mined == mineQuestObject.getAmount()) { isDone = true; QuestCompleteEvent event = new QuestCompleteEvent(player, this, true); event.callEvent(); diff --git a/src/main/java/com/alttd/altitudequests/util/Utilities.java b/src/main/java/com/alttd/altitudequests/util/Utilities.java index 29d8784..95ceb44 100644 --- a/src/main/java/com/alttd/altitudequests/util/Utilities.java +++ b/src/main/java/com/alttd/altitudequests/util/Utilities.java @@ -1,8 +1,16 @@ package com.alttd.altitudequests.util; +import java.util.Random; + public class Utilities { public static double round(double num, int precision) { double scale = Math.pow(10, precision); return ((int) (num * scale)) / scale; } + + public static int randomOr0(int max) { + if (max <= 1) + return 0; + return new Random().nextInt(0, max - 1); + } }