初始化项目

This commit is contained in:
yhy
2026-06-04 06:58:32 +08:00
commit a60609bb3b
60 changed files with 3863 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
target/
out/
lib/
libs/
.vscode/
.codex/
.idea/
*.iml
*.class
*.log
logs/
*.db
*.sqlite
*.sqlite3
.DS_Store
Thumbs.db

130
pom.xml Normal file
View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>AuQuestEngine</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>public-survive</id>
<url>https://repo.aurora-pixels.com/repository/public-survive/</url>
</repository>
<repository>
<id>momi-releases</id>
<url>https://repo.momirealms.net/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip.placeholderapi</groupId>
<artifactId>PlaceholderAPI</artifactId>
<version>2.12.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.citizensnpcs</groupId>
<artifactId>Citizens</artifactId>
<version>2.0.41</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine.mythic.bukkit</groupId>
<artifactId>MythicMobs</artifactId>
<version>5.12.0-SNAPSHOT-548b7d33</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.momirealms</groupId>
<artifactId>custom-fishing</artifactId>
<version>2.3.23.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.momirealms</groupId>
<artifactId>custom-crops</artifactId>
<version>3.6.52</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<relocations>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>com.io.yaohun.questengine.libs.hikari</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,101 @@
package com.io.yaohun.questengine;
import com.io.yaohun.questengine.command.QuestAdminCommand;
import com.io.yaohun.questengine.command.QuestCommand;
import com.io.yaohun.questengine.config.Config;
import com.io.yaohun.questengine.hook.QuestPlaceholderExpansion;
import com.io.yaohun.questengine.listener.PlayerQuestListener;
import com.io.yaohun.questengine.listener.task.*;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.storage.PlayerQuestSaveQueue;
import com.io.yaohun.questengine.player.storage.PlayerQuestStorageManager;
import com.io.yaohun.questengine.quest.QuestManager;
import com.io.yaohun.questengine.quest.QuestResetManager;
import com.io.yaohun.questengine.util.MessageUtil;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class QEMain extends JavaPlugin {
private static QEMain instance;
public static boolean DEBUG = false;
@Override
public void onEnable() {
instance = this;
saveDefaultConfig();
Config.reloadConfig(this,false);
MessageUtil.init(this);
PlayerQuestStorageManager.init(this);
QuestManager.reloadQuestManager(this);
Bukkit.getPluginManager().registerEvents(new PlayerQuestListener(), this);
registerQuestListener();
getCommand("aquest").setExecutor(new QuestCommand());
getCommand("aquestadmin").setExecutor(new QuestAdminCommand());
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
new QuestPlaceholderExpansion().register();
getLogger().info("已接入 PlaceholderAPI");
Bukkit.getConsoleSender().sendMessage("[AuQuest] 已接入前置 §ePlaceholderAPI");
}
PlayerQuestSaveQueue.start();
QuestResetManager.start();
}
private void registerQuestListener() {
getServer().getPluginManager().registerEvents(new QuestBlockBreakListener(), this);
getServer().getPluginManager().registerEvents(new QuestBlockPlaceListener(), this);
getServer().getPluginManager().registerEvents(new QuestBreedListener(), this);
getServer().getPluginManager().registerEvents(new QuestCraftItemListener(), this);
getServer().getPluginManager().registerEvents(new QuestExpGainListener(), this);
getServer().getPluginManager().registerEvents(new QuestFeedAnimalListener(), this);
getServer().getPluginManager().registerEvents(new QuestFeedPlayerListener(), this);
getServer().getPluginManager().registerEvents(new QuestInteractEntityListener(), this);
getServer().getPluginManager().registerEvents(new QuestKillTypeListener(), this);
getServer().getPluginManager().registerEvents(new QuestSmeltFoodListener(), this);
getServer().getPluginManager().registerEvents(new QuestSmeltOreListener(), this);
getServer().getPluginManager().registerEvents(new QuestCommandListener(), this);
getServer().getPluginManager().registerEvents(new QuestOpenGuiListener(), this);
// 2026-6-4 新增任务前置
getServer().getPluginManager().registerEvents(new QuestPickupItemListener(), this);
getServer().getPluginManager().registerEvents(new QuestBucketListener(), this);
getServer().getPluginManager().registerEvents(new QuestCollectListener(), this);
getServer().getPluginManager().registerEvents(new QuestTameEntityListener(), this);
getServer().getPluginManager().registerEvents(new QuestVillagerListener(), this);
getServer().getPluginManager().registerEvents(new QuestBrewPotionListener(), this);
getServer().getPluginManager().registerEvents(new QuestUseAnvilListener(), this);
getServer().getPluginManager().registerEvents(new QuestInteractItemListener(), this);
Bukkit.getConsoleSender().sendMessage("[AuQuest] 正在接入任务前置: ");
if (Bukkit.getPluginManager().getPlugin("Citizens") != null) {
getServer().getPluginManager().registerEvents(new QuestInteractNPCListener(), this);
Bukkit.getConsoleSender().sendMessage(" - §eCitizens");
}
if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) {
getServer().getPluginManager().registerEvents(new QuestKillMythicListener(), this);
Bukkit.getConsoleSender().sendMessage(" - §eMythicMobs");
}
if (Bukkit.getPluginManager().getPlugin("CustomFishing") != null) {
getServer().getPluginManager().registerEvents(new QuestFishItemListener(), this);
Bukkit.getConsoleSender().sendMessage(" - §eCustomFishing");
}
if (Bukkit.getPluginManager().getPlugin("CustomCrops") != null) {
getServer().getPluginManager().registerEvents(new QuestHarvestCropListener(), this);
Bukkit.getConsoleSender().sendMessage(" - §eCustomCrops");
}
}
@Override
public void onDisable() {
QuestResetManager.stop();
PlayerQuestSaveQueue.flushSync();
PlayerQuestManager.saveAll();
PlayerQuestStorageManager.close();
}
public static QEMain inst() {
return instance;
}
}

View File

@@ -0,0 +1,137 @@
package com.io.yaohun.questengine.api;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.player.storage.PlayerQuestSaveQueue;
import com.io.yaohun.questengine.quest.Quest;
import com.io.yaohun.questengine.quest.QuestManager;
import com.io.yaohun.questengine.quest.QuestTask;
import com.io.yaohun.questengine.quest.QuestType;
import com.io.yaohun.questengine.util.ColorUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class AuQuestAPI {
public static String getQuestDisplayName(String questId) {
Quest quest = QuestManager.getQuest(questId);
return quest == null ? null : quest.getDisplayName();
}
public static List<String> getQuestDescription(String questId) {
Quest quest = QuestManager.getQuest(questId);
if (quest == null) {
return Collections.emptyList();
}
return new ArrayList<>(quest.getDescription());
}
public static QuestType getQuestType(String questId) {
Quest quest = QuestManager.getQuest(questId);
return quest == null ? null : quest.getType();
}
public static boolean hasCompletedQuest(UUID uuid, String questId) {
PlayerQuestProgress progress = PlayerQuestManager.getProgress(uuid, questId);
return progress != null && progress.isCompleted();
}
public static boolean hasActiveQuest(UUID uuid, String questId) {
PlayerQuestProgress progress = PlayerQuestManager.getProgress(uuid, questId);
return progress != null && !progress.isCompleted();
}
public static List<String> getActiveQuests(UUID uuid) {
PlayerQuestData data = PlayerQuestManager.getPlayerData(uuid);
List<String> list = new ArrayList<>();
for (PlayerQuestProgress progress : data.getQuestProgressMap().values()) {
if (!progress.isCompleted()) {
list.add(progress.getQuestId());
}
}
return list;
}
public static List<String> getCompletedQuests(UUID uuid){
PlayerQuestData data = PlayerQuestManager.getPlayerData(uuid);
List<String> list = new ArrayList<>();
for(PlayerQuestProgress progress : data.getQuestProgressMap().values()){
if(progress.isCompleted()){
list.add(progress.getQuestId());
}
}
return list;
}
public static boolean abandonQuest(UUID uuid, String questId) {
PlayerQuestData data = PlayerQuestManager.getPlayerData(uuid);
if (!data.hasQuest(questId)) {
return false;
}
data.removeQuest(questId);
PlayerQuestSaveQueue.markDirty(uuid);
return true;
}
public static boolean startQuest(UUID uuid, String questId) {
if (!QuestManager.hasQuest(questId)) {
return false;
}
return PlayerQuestManager.acceptQuest(uuid, questId);
}
public static boolean directCompleteQuest(UUID uuid, String questId){
Quest quest = QuestManager.getQuest(questId);
if(quest == null){
return false;
}
PlayerQuestData data = PlayerQuestManager.getPlayerData(uuid);
PlayerQuestProgress progress = data.getQuestProgress(questId);
if(progress == null){
progress = new PlayerQuestProgress(questId);
data.addQuest(progress);
}
if(progress.isCompleted()){
return false;
}
for (QuestTask task : quest.getTasks().values()){
progress.setProgress(task.getId(), task.getAmount());
}
progress.setCompleted(true);
PlayerQuestSaveQueue.markDirty(uuid);
Player player = Bukkit.getPlayer(uuid);
if(player != null) {
// 完成消息
for (String msg : quest.getCompleteMessages()) {
player.sendMessage(ColorUtil.color(msg));
}
// reward message
for (String msg : quest.getReward().getMessages()) {
player.sendMessage(ColorUtil.color(msg.replace("{name}", quest.getDisplayName())));
}
// reward command
for (String cmd : quest.getReward().getCommands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", player.getName()));
}
}
return true;
}
}

View File

@@ -0,0 +1,76 @@
package com.io.yaohun.questengine.command;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.config.Config;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.storage.PlayerQuestSaveQueue;
import com.io.yaohun.questengine.player.storage.PlayerQuestStorage;
import com.io.yaohun.questengine.player.storage.PlayerQuestStorageManager;
import com.io.yaohun.questengine.player.storage.SQLitePlayerQuestStorage;
import com.io.yaohun.questengine.quest.Quest;
import com.io.yaohun.questengine.quest.QuestManager;
import com.io.yaohun.questengine.util.ColorUtil;
import com.io.yaohun.questengine.util.MessageUtil;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.File;
public class QuestAdminCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission("admin.use")) {
return true;
}
String prefix = "§f[§cAuQuest§f] §a";
if(args.length >= 1 && "debug".equalsIgnoreCase(args[0])){
QEMain.DEBUG = !QEMain.DEBUG;
sender.sendMessage(prefix+"已切换调试: §e"+QEMain.DEBUG);
return true;
}
if(args.length >= 1 && "reload".equalsIgnoreCase(args[0])){
// 先强制保存脏数据
PlayerQuestSaveQueue.flushSync();
// 再保存全部缓存
PlayerQuestManager.saveAll();
QEMain plugin = QEMain.inst();
plugin.reloadConfig();
Config.reloadConfig(plugin, true);
MessageUtil.init(plugin);
QuestManager.reloadQuestManager(plugin);
sender.sendMessage(prefix+"配置文件已重载,当前任务数量: §e"+QuestManager.getAllQuests().size()+"");
return true;
}
if(args.length >= 2 && "clear".equalsIgnoreCase(args[0])){
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
if(target.getUniqueId() == null){
sender.sendMessage(prefix+"玩家 "+target.getName()+" 不存在");
return true;
}
PlayerQuestManager.deletePlayerData(target.getUniqueId());
sender.sendMessage(prefix+"已清空玩家任务数据: &e" + target.getName());
return true;
}
if (args.length >= 1 && "outsqlite".equalsIgnoreCase(args[0])) {
PlayerQuestStorage storage = PlayerQuestStorageManager.getStorage();
if (!(storage instanceof SQLitePlayerQuestStorage)) {
sender.sendMessage(ColorUtil.color("&7[任务系统] &c当前存储模式不是 SQLite无法导出。"));
return true;
}
SQLitePlayerQuestStorage sqliteStorage = (SQLitePlayerQuestStorage) storage;
File file = sqliteStorage.exportToYaml();
if (file == null) {
sender.sendMessage(ColorUtil.color("&7[任务系统] &cSQLite 数据导出失败,请查看后台报错。"));
return true;
}
sender.sendMessage(ColorUtil.color("&7[任务系统] &aSQLite 数据已导出: &f" + file.getName()));
return true;
}
return true;
}
}

View File

@@ -0,0 +1,135 @@
package com.io.yaohun.questengine.command;
import com.io.yaohun.questengine.api.AuQuestAPI;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.quest.Quest;
import com.io.yaohun.questengine.quest.QuestConditionChecker;
import com.io.yaohun.questengine.quest.QuestManager;
import com.io.yaohun.questengine.quest.QuestTask;
import com.io.yaohun.questengine.util.ColorUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.UUID;
public class QuestCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
String prefix = "§f[§cAuQuest§f] §a";
if(args.length >= 1 && "direc".equalsIgnoreCase(args[0]) && sender.isOp()){
if(args.length < 2){
sender.sendMessage(prefix+"用法: /aquest direc <任务ID>");
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(prefix+"该命令只能由玩家执行");
return true;
}
String questId = args[1];
UUID playerUuid = player.getUniqueId();
AuQuestAPI.directCompleteQuest(playerUuid, questId);
sender.sendMessage(prefix+"已直接完成任务: §e"+questId);
return true;
}
if(args.length >= 1 && "info".equalsIgnoreCase(args[0])){
String playerName = sender.getName();
if(sender.hasPermission("admin.use") && args.length >= 2){
playerName = args[1];
}
Player player = Bukkit.getPlayer(playerName);
if(player == null){
sender.sendMessage(prefix+"玩家 "+playerName+" 不在线");
return true;
}
PlayerQuestData questData = PlayerQuestManager.getPlayerData(player.getUniqueId());
sender.sendMessage("§8§m————————————————————");
sender.sendMessage(prefix+"玩家: §e"+playerName);
sender.sendMessage(prefix+"任务数量: §e"+questData.getQuestProgressMap().size());
int completedCount = 0;
int activeCount = 0;
for(PlayerQuestProgress progress : questData.getQuestProgressMap().values()){
Quest quest = QuestManager.getQuest(progress.getQuestId());
String questName = quest == null ? progress.getQuestId() : quest.getDisplayName();
if(progress.isCompleted()) {
completedCount++;
sender.sendMessage(ColorUtil.color("&a[已完成] &f" + questName + " &7(" + progress.getQuestId() + ")"));
continue;
}
activeCount++;
sender.sendMessage(ColorUtil.color("&e[进行中] &f" + questName + " &7(" + progress.getQuestId() + ")"));
if(quest != null){
for(QuestTask task : quest.getTasks().values()){
int current = progress.getProgress(task.getId());
int max = task.getAmount();
if(current > max){
current = max;
}
sender.sendMessage(ColorUtil.color(
" &8- &7" + task.getDisplayName()
+ " &f" + current + "&7/&f" + max
));
}
}
}
sender.sendMessage(ColorUtil.color("&7进行中: &e" + activeCount + " &7已完成: &a" + completedCount));
sender.sendMessage("§8§m————————————————————");
return true;
}
if (args.length < 3) {
sender.sendMessage("§c用法: /aquest js <任务ID> <玩家名>");
return true;
}
String sub = args[0];
// /aquest js quest_1 yaohun
if (sub.equalsIgnoreCase("js")) {
String questId = args[1];
Player target = Bukkit.getPlayer(args[2]);
if (target == null) {
sender.sendMessage("§c玩家不存在");
return true;
}
Quest quest = QuestManager.getQuest(questId);
if (quest == null) {
sender.sendMessage("§c任务不存在");
return true;
}
if (!QuestConditionChecker.canAccept(target, quest, true)) {
sender.sendMessage(ColorUtil.color("&7[任务系统] &c玩家不满足任务接受条件。"));
return true;
}
boolean success = PlayerQuestManager.acceptQuest(
target.getUniqueId(),
questId
);
if (!success) {
sender.sendMessage("§c该玩家已经接受过这个任务");
return true;
}
// 接受消息
for (String msg : quest.getReceiveMessages()) {
target.sendMessage(ColorUtil.color(msg));
}
sender.sendMessage("§a任务发放成功");
return true;
}
return true;
}
}

View File

@@ -0,0 +1,36 @@
package com.io.yaohun.questengine.config;
import com.io.yaohun.questengine.QEMain;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.ArrayList;
import java.util.List;
public class Config {
private static List<String> foodTypeList = new ArrayList<>();
private static List<String> oreTypeList = new ArrayList<>();
public static void reloadConfig(QEMain plugin, boolean reload){
if(reload){
reload(plugin);
}
FileConfiguration config = plugin.getConfig();
foodTypeList = config.getStringList("FoodType");
oreTypeList = config.getStringList("OreType");
}
private static void reload(QEMain plugin){
plugin.reloadConfig();
plugin.saveConfig();
}
public static List<String> getFoodTypeList() {
return foodTypeList;
}
public static List<String> getOreTypeList() {
return oreTypeList;
}
}

View File

@@ -0,0 +1,126 @@
package com.io.yaohun.questengine.hook;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.quest.Quest;
import com.io.yaohun.questengine.quest.QuestManager;
import com.io.yaohun.questengine.quest.QuestTask;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class QuestPlaceholderExpansion extends PlaceholderExpansion {
@Override
public @NotNull String getIdentifier() {
return "aquest";
}
@Override
public @NotNull String getAuthor() {
return "yaohun";
}
@Override
public @NotNull String getVersion() {
return "1.0.0";
}
@Override
public boolean persist() {
return true;
}
@Override
public String onPlaceholderRequest(Player player, String params) {
if (player == null) {
return "";
}
// %aqe_quest_quest_1_name%
if (params.startsWith("quest_") && params.endsWith("_name")) {
String questId = params.substring("quest_".length(), params.length() - "_name".length());
Quest quest = QuestManager.getQuest(questId);
return quest == null ? "" : quest.getDisplayName();
}
// %aqe_quest_quest_1_completed%
if (params.startsWith("quest_") && params.endsWith("_completed")) {
String questId = params.substring("quest_".length(), params.length() - "_completed".length());
PlayerQuestProgress progress = PlayerQuestManager.getProgress(player.getUniqueId(), questId);
return progress != null && progress.isCompleted() ? "已完成" : "未完成";
}
// %aqe_quest_quest_1_progress%
if (params.startsWith("quest_") && params.endsWith("_progress")) {
String questId = params.substring("quest_".length(), params.length() - "_progress".length());
return getQuestProgressText(player, questId);
}
// %aqe_task_quest_1_id_1%
if (params.startsWith("task_")) {
String raw = params.substring("task_".length());
int lastIndex = raw.lastIndexOf("_id_");
if (lastIndex == -1) {
return "";
}
String questId = raw.substring(0, lastIndex);
String taskId = "id_" + raw.substring(lastIndex + "_id_".length());
return getTaskProgressText(player, questId, taskId);
}
return "";
}
private String getQuestProgressText(Player player, String questId) {
Quest quest = QuestManager.getQuest(questId);
if (quest == null) {
return "";
}
PlayerQuestProgress progress = PlayerQuestManager.getProgress(player.getUniqueId(), questId);
if (progress == null) {
return "未接受";
}
int current = 0;
int max = 0;
for (QuestTask task : quest.getTasks().values()) {
current += Math.min(progress.getProgress(task.getId()), task.getAmount());
max += task.getAmount();
}
return current + "/" + max;
}
private String getTaskProgressText(Player player, String questId, String taskId) {
Quest quest = QuestManager.getQuest(questId);
if (quest == null) {
return "";
}
QuestTask task = quest.getTasks().get(taskId);
if (task == null) {
return "";
}
PlayerQuestProgress progress = PlayerQuestManager.getProgress(player.getUniqueId(), questId);
if (progress == null) {
return "0/" + task.getAmount();
}
int current = Math.min(progress.getProgress(taskId), task.getAmount());
return current + "/" + task.getAmount();
}
}

View File

@@ -0,0 +1,23 @@
package com.io.yaohun.questengine.listener;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerQuestListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e){
PlayerQuestManager.loadPlayer(e.getPlayer().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent event) {
PlayerQuestManager.savePlayerNow(event.getPlayer().getUniqueId());
PlayerQuestManager.unloadPlayer(event.getPlayer().getUniqueId());
}
}

View File

@@ -0,0 +1,33 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
public class QuestBlockBreakListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
String target = event.getBlock().getType().name();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<BREAK_BLOCK>] 玩家 "+player.getName()+" 破坏了方块 "+target);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.BREAK_BLOCK,
target,
1
);
}
}

View File

@@ -0,0 +1,29 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
public class QuestBlockPlaceListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlace(BlockPlaceEvent e){
Player player = e.getPlayer();
String target = e.getBlock().getType().name();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<PLACE_BLOCK>] 玩家 "+player.getName()+" 放置了方块 "+target);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.PLACE_BLOCK,
target,
1
);
}
}

View File

@@ -0,0 +1,31 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityBreedEvent;
public class QuestBreedListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onBreed(EntityBreedEvent e){
if(e.getBreeder() instanceof Player player){
if(e.getEntity() instanceof Animals animals){
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<BREED>] 玩家 "+player.getName()+" 人工繁殖了 "+animals.getType().name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.BREED,
animals.getType().name(),
1
);
}
}
}
}

View File

@@ -0,0 +1,57 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.PortalType;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.inventory.BrewEvent;
import org.bukkit.inventory.BrewerInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionType;
public class QuestBrewPotionListener implements Listener {
//BREW_POTION("酿造药水"),
@EventHandler(ignoreCancelled = true)
public void onBrew(BrewEvent e){
BrewerInventory inventory = e.getContents();
for (int slot = 0; slot < 3; slot++){
ItemStack item = inventory.getItem(slot);
if(item == null || item.getType().isAir()){
continue;
}
if (!(item.getItemMeta() instanceof PotionMeta potionMeta)) {
continue;
}
PotionType potionType = potionMeta.getBasePotionType();
for (HumanEntity viewer : e.getContents().getViewers()){
if (!(viewer instanceof Player player)) {
continue;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<BREW_POTION>] 玩家 " + player.getName() + " 酿造了 " + potionType.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.BREW_POTION,
potionType.name(),
1
);
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.BREW_POTION_COUNT,
"ALL",
1
);
}
}
}
}

View File

@@ -0,0 +1,47 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerBucketFillEvent;
import org.bukkit.event.player.PlayerFishEvent;
public class QuestBucketListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onFillBucket(PlayerBucketFillEvent e){
Player player = e.getPlayer();
Material material = e.getBucket();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<PLACE_BUCKET>] 玩家 "+player.getName()+" 装取了 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.FILL_BUCKET,
material.name(),
1
);
}
@EventHandler(ignoreCancelled = true)
public void onPlaceBucket(PlayerBucketEmptyEvent e){
Player player = e.getPlayer();
Material material = e.getBucket();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<PLACE_BUCKET>] 玩家 " + player.getName() + " 倒出了 " + material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.PLACE_BUCKET,
material.name(),
1
);
}
}

View File

@@ -0,0 +1,108 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.type.Beehive;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Cow;
import org.bukkit.entity.Player;
import org.bukkit.entity.Sheep;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerShearEntityEvent;
import org.bukkit.inventory.ItemStack;
import java.util.UUID;
public class QuestCollectListener implements Listener {
// COLLECT_ENTITY("剪羊毛,取蘑菇煲"),
// COLLECT_BLOCK("收集蜂蜜"),
@EventHandler(ignoreCancelled = true)
public void onShearSheep(PlayerShearEntityEvent e){
if(e.getEntity() instanceof Sheep sheep) {
if (sheep.isSheared()) {
return;
}
Player player = e.getPlayer();
ItemStack stack = e.getItem();
Material material = stack.getType();
if (QEMain.DEBUG) {
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<COLLECT_ENTITY>] 玩家 " + player.getName() + " 剪羊毛 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.COLLECT_ENTITY,
"SHEEP",
1
);
}
}
@EventHandler(ignoreCancelled = true)
public void onMilkCow(PlayerInteractEntityEvent e){
if(e.getRightClicked() instanceof Cow){
Player player = e.getPlayer();
ItemStack hand = player.getInventory().getItemInMainHand();
if(hand == null || hand.getType() != Material.BUCKET){
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<COLLECT_ENTITY>] 玩家 "+player.getName()+" 挤牛奶 Cow");
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.COLLECT_ENTITY,
"COW",
1
);
}
}
@EventHandler(ignoreCancelled = true)
public void onBeehive(PlayerInteractEvent e){
if(e.getAction() == Action.RIGHT_CLICK_BLOCK){
Player player = e.getPlayer();
Block block = e.getClickedBlock();
if (block == null) {
return;
}
Material blockType = block.getType();
if(blockType != Material.BEEHIVE && blockType != Material.BEE_NEST){
return;
}
ItemStack hand = player.getInventory().getItemInMainHand();
if(hand == null || hand.getType().isAir()){
return;
}
Material handType = hand.getType();
if (handType != Material.GLASS_BOTTLE && handType != Material.SHEARS) {
return;
}
BlockData data = block.getBlockData();
if(data instanceof Beehive hive){
if(hive.getHoneyLevel() < hive.getMaximumHoneyLevel()){
return;
}
String target = handType == Material.GLASS_BOTTLE ? "HONEY_BOTTLE" : "HONEYCOMB";
if (QEMain.DEBUG) {
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<COLLECT_BLOCK>] 玩家 " + player.getName() + " 收集蜂箱 " + target);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.COLLECT_BLOCK,
target,
1
);
}
}
}
}

View File

@@ -0,0 +1,35 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.inventory.Inventory;
import java.util.UUID;
public class QuestCommandListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onCommand(PlayerCommandPreprocessEvent e) {
Player player = e.getPlayer();
String command = e.getMessage();
if (command == null || command.isEmpty()) {
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<SEND_COMMAND>] 玩家 "+player.getName()+" 执行了命令 "+command);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.SEND_COMMAND,
command,
1
);
}
}

View File

@@ -0,0 +1,36 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.CraftItemEvent;
public class QuestCraftItemListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onCraft(CraftItemEvent event) {
if(event.getWhoClicked() instanceof Player player){
if (event.getRecipe() == null || event.getRecipe().getResult() == null) {
return;
}
Material material = event.getRecipe().getResult().getType();
if(material.isAir()){
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<CRAFT_ITEM>] 玩家 "+player.getName()+" 合成了 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.CRAFT_ITEM,
material.name(),
1
);
}
}
}

View File

@@ -0,0 +1,37 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.enchantment.EnchantItemEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.ItemStack;
public class QuestEnchanItemListener implements Listener {
//ENCHANT_ITEM("使用附魔台"),
@EventHandler(ignoreCancelled = true)
public void onEnchant(EnchantItemEvent e){
Player player = e.getEnchanter();
ItemStack item = e.getItem();
if(item == null || item.getType().isAir()){
return;
}
Material material = item.getType();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<ENCHANT_ITEM>] 玩家 " + player.getName() + " 附魔了 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.ENCHANT_ITEM,
material.name(),
1
);
}
}

View File

@@ -0,0 +1,30 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerExpChangeEvent;
public class QuestExpGainListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onExo(PlayerExpChangeEvent e){
if(e.getAmount() <= 0){
return;
}
Player player = e.getPlayer();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<EXP_GAIN>] 玩家 "+player.getName()+" 获得经验 "+e.getAmount());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.EXP_GAIN,
null,
e.getAmount()
);
}
}

View File

@@ -0,0 +1,38 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
public class QuestFeedAnimalListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onFeed(PlayerInteractEntityEvent e) {
if(e.getRightClicked() instanceof Animals animals){
Player player = e.getPlayer();
ItemStack hand = player.getInventory().getItemInMainHand();
if (hand == null || hand.getType().isAir()) {
return;
}
if (!animals.isBreedItem(hand)) {
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<FEED_ANIMAL>] 玩家 "+player.getName()+" 投喂了 "+animals.getType().name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.FEED_ANIMAL,
animals.getType().name(),
1
);
}
}
}

View File

@@ -0,0 +1,33 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.ItemStack;
public class QuestFeedPlayerListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onFeed(PlayerItemConsumeEvent e){
Player player = e.getPlayer();
ItemStack item = e.getItem();
if(item == null || item.getType().isAir()){
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<FEED_PLAYER>] 玩家 "+player.getName()+" 吃掉了 "+item.getType().name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.FEED_PLAYER,
item.getType().name(),
1
);
}
}

View File

@@ -0,0 +1,68 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import net.momirealms.customfishing.api.event.FishingResultEvent;
import net.momirealms.customfishing.api.mechanic.loot.Loot;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
public class QuestFishItemListener implements Listener {
// FISH_ITEM("钓鱼获得物品"),
// FISH_STAR("钓起鱼的星级"),
@EventHandler(ignoreCancelled = true)
public void onFishItem(FishingResultEvent e){
if(e.getResult() != FishingResultEvent.Result.SUCCESS){
return;
}
Player player = e.getPlayer();
Loot loot = e.getLoot();
if(loot == null){
return;
}
int amount = e.getAmount();
if(amount <= 0){
amount = 1;
}
String fishId = loot.id();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<FISH_ITEM>] 玩家 "+player.getName()+" 钓起来了 "+fishId+"x"+amount);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.FISH_ITEM,
fishId,
amount
);
String[] startGroup = loot.lootGroup();
if(startGroup == null){
return;
}
for (String group : startGroup){
if(group == null){
continue;
}
if("no_star".equalsIgnoreCase(group)){
continue;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<FISH_STAR>] 玩家 "+player.getName()+" 钓起来了星级鱼 "+group);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.FISH_STAR,
group,
1
);
}
}
}

View File

@@ -0,0 +1,65 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import net.momirealms.customcrops.api.event.CropBreakEvent;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class QuestHarvestCropListener implements Listener {
// HARVEST_CROP("收获农作物"), 原版
/*@EventHandler(ignoreCancelled = true)
public void onHarvestCrop(BlockBreakEvent event) {
Player player = event.getPlayer();
Block block = event.getBlock();
BlockData data = block.getBlockData();
if(data instanceof Ageable ageable) {
if(ageable.getAge() < ageable.getMaximumAir()){
return;
}
Material material = block.getType();
if (QEMain.DEBUG) {
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<HARVEST_CROP>] 玩家 " + player.getName() + " 收获了 " + material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.HARVEST_CROP,
material.name(),
1
);
}
}*/
// HARVEST_CROP("收获农作物"), 星露谷
@EventHandler(ignoreCancelled = true)
public void onHarvestCrop(CropBreakEvent e){
if (!(e.entityBreaker() instanceof Player player)) {
return;
}
Block block = e.blockBreaker();
BlockData data = block.getBlockData();
if (data instanceof Ageable ageable) {
if (ageable.getAge() < ageable.getMaximumAge()) {
return;
}
}
String cropId = e.cropConfig().id();
if (QEMain.DEBUG) {
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<HARVEST_CROP>] 玩家 " + player.getName() + " 收获了 " + cropId);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.HARVEST_CROP,
cropId,
1
);
}
}

View File

@@ -0,0 +1,27 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent;
public class QuestInteractEntityListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEntityEvent e){
Player player = e.getPlayer();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<INTERACT_ENTITY>] 玩家 "+player.getName()+" 正在交互实体 "+e.getRightClicked().getType().name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.INTERACT_ENTITY,
e.getRightClicked().getType().name(),
1
);
}
}

View File

@@ -0,0 +1,38 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
public class QuestInteractItemListener implements Listener {
// INTERACT_ITEM("交互手持物品"),
@EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEvent e){
if(e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {
ItemStack item = e.getItem();
if (item == null || item.getType().isAir()) {
return;
}
Material material = item.getType();
Player player = e.getPlayer();
if (QEMain.DEBUG) {
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<INTERACT_ITEM>] 玩家 " + player.getName() + " 正在交互手持物品 " + material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.INTERACT_ITEM,
material.name(),
1
);
}
}
}

View File

@@ -0,0 +1,33 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class QuestInteractNPCListener implements Listener {
@EventHandler
public void onClickNpc(NPCRightClickEvent e){
Player player = e.getClicker();
NPC npc = e.getNPC();
if(npc == null){
return;
}
String target = String.valueOf(npc.getName());
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<INTERACT_NPC>] 玩家 "+player.getName()+" 正在交互NPC "+target);
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.INTERACT_NPC,
target,
1
);
}
}

View File

@@ -0,0 +1,31 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import io.lumine.mythic.api.mobs.MythicMob;
import io.lumine.mythic.bukkit.events.MythicMobDeathEvent;
import io.lumine.mythic.core.mobs.MobType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class QuestKillMythicListener implements Listener {
@EventHandler
public void onKill(MythicMobDeathEvent e){
if(e.getKiller() instanceof Player player){
MythicMob mobType = e.getMobType();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<KILL_MYTHIC>] 玩家 "+player.getName()+" 击杀了 "+mobType.getInternalName());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.KILL_MYTHIC,
mobType.getInternalName(),
1
);
}
}
}

View File

@@ -0,0 +1,32 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import net.citizensnpcs.api.event.NPCLeftClickEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.NPC;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
public class QuestKillTypeListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onKill(EntityDeathEvent e){
Player killer = e.getEntity().getKiller();
if(killer == null){
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<KILL_TYPE>] 玩家 "+killer.getName()+" 击杀了 "+e.getEntity().getType().name());
}
PlayerQuestManager.addProgress(
killer.getUniqueId(),
QuestTaskType.KILL_TYPE,
e.getEntity().getType().name(),
1
);
}
}

View File

@@ -0,0 +1,58 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.inventory.Inventory;
import java.util.UUID;
public class QuestOpenGuiListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onOpenGui(InventoryOpenEvent e){
if(e.getPlayer() instanceof Player player){
String viewTitle = e.getView().getTitle();
if (viewTitle == null || viewTitle.isEmpty()) {
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<OPEN_GUI_TITLE>] 玩家 "+player.getName()+" 正在打开 "+viewTitle);
}
// 去色标题,方便配置不写颜色也能匹配
String strippedTitle = ChatColor.stripColor(viewTitle);
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.OPEN_GUI_TITLE,
strippedTitle,
1
);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onOpenType(InventoryOpenEvent e){
if(e.getPlayer() instanceof Player player){
Inventory inventory = e.getInventory();
if (inventory == null) {
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<OPEN_GUI_TYPE>] 玩家 "+player.getName()+" 正在打开 "+inventory.getType().name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.OPEN_GUI_TYPE,
inventory.getType().name(),
1
);
}
}
}

View File

@@ -0,0 +1,32 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.inventory.ItemStack;
public class QuestPickupItemListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPickUp(EntityPickupItemEvent e){
if(e.getEntity() instanceof Player player){
ItemStack stack = e.getItem().getItemStack();
Material material = stack.getType();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<PICKUP_ITEM>] 玩家 "+player.getName()+" 拾取了 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.PICKUP_ITEM,
material.name(),
1
);
}
}
}

View File

@@ -0,0 +1,50 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.config.Config;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.FurnaceExtractEvent;
import java.util.List;
public class QuestSmeltFoodListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onSmelt(FurnaceExtractEvent e){
Player player = e.getPlayer();
Material result = e.getItemType();
int amount = e.getItemAmount();
if (!isFood(result)) {
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<SMELT_FOOD>] 玩家 "+player.getName()+" 在熔炉取出了 "+result.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.SMELT_FOOD,
result.name(),
amount
);
}
private boolean isFood(Material material) {
List<String> foodTypeList = Config.getFoodTypeList();
for (String food : foodTypeList) {
if (material.name().contains(food)) {
return true;
}
}
return switch (material) {
case COOKED_BEEF, COOKED_CHICKEN, COOKED_MUTTON, COOKED_RABBIT, BAKED_POTATO -> true;
default -> false;
};
}
}

View File

@@ -0,0 +1,51 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.config.Config;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.FurnaceExtractEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import java.util.List;
public class QuestSmeltOreListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onSmelt(FurnaceExtractEvent e){
Player player = e.getPlayer();
Material result = e.getItemType();
int amount = e.getItemAmount();
if(!isOre(result)){
return;
}
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<SMELT_ORE>] 玩家 "+player.getName()+" 在熔炉取出了 "+result.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.SMELT_ORE,
result.name(),
amount
);
}
private boolean isOre(Material material){
List<String> oreList = Config.getOreTypeList();
for (String ore : oreList) {
if(material.name().contains(ore)){
return true;
}
}
return switch (material) {
case IRON_INGOT, GOLD_INGOT, COPPER_INGOT, NETHERITE_SCRAP, DIAMOND, EMERALD, REDSTONE, COAL, QUARTZ,
LAPIS_LAZULI -> true;
default -> false;
};
}
}

View File

@@ -0,0 +1,32 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityTameEvent;
public class QuestTameEntityListener implements Listener {
// TAME_ENTITY("驯服实体"),
@EventHandler(ignoreCancelled = true)
public void onTameEntity(EntityTameEvent e){
if(e.getOwner() instanceof Player player){
EntityType entityType = e.getEntity().getType();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<TAME_ENTITY>] 玩家 "+player.getName()+" 驯服了 "+entityType.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.TAME_ENTITY,
entityType.name(),
1
);
}
}
}

View File

@@ -0,0 +1,45 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class QuestUseAnvilListener implements Listener {
//USE_ANVIL("使用铁砧"),
@EventHandler(ignoreCancelled = true)
public void onUseAnvil(InventoryClickEvent e){
if (!(e.getWhoClicked() instanceof Player player)) {
return;
}
if (!(e.getInventory() instanceof AnvilInventory)) {
return;
}
if(e.getRawSlot() != 2){
return;
}
ItemStack result = e.getCurrentItem();
if(result == null || result.getType().isAir()){
return;
}
Material material = result.getType();
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<USE_ANVIL>] 玩家 " + player.getName() + " 使用了铁砧 "+material.name());
}
PlayerQuestManager.addProgress(
player.getUniqueId(),
QuestTaskType.USE_ANVIL,
material.name(),
1
);
}
}

View File

@@ -0,0 +1,66 @@
package com.io.yaohun.questengine.listener.task;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.quest.QuestTaskType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantInventory;
import org.bukkit.inventory.MerchantRecipe;
import java.util.UUID;
public class QuestVillagerListener implements Listener {
//VILLAGER_TRADE_ITEM("村民交易物品"),
//VILLAGER_TRADE_JOB("村民交易职业"),
@EventHandler(ignoreCancelled = true)
public void onVillagerTrade(InventoryClickEvent e){
if (!(e.getWhoClicked() instanceof Player player)) {
return;
}
if (!(e.getInventory() instanceof MerchantInventory inventory)) {
return;
}
if(e.getRawSlot() != 2){
return;
}
ItemStack current = e.getCurrentItem();
if(current == null || current.getType().isAir()){
return;
}
MerchantRecipe recipe = inventory.getSelectedRecipe();
if(recipe == null){
return;
}
UUID playerUuid = player.getUniqueId();
ItemStack result = recipe.getResult();
// 物品交易任务
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<VILLAGER_TRADE_ITEM>] 玩家 "+player.getName()+" 向村民交易物品 "+result.getType().name());
}
PlayerQuestManager.addProgress(
playerUuid,
QuestTaskType.VILLAGER_TRADE_ITEM,
result.getType().name(),
result.getAmount()
);
// 职业判定任务
if(inventory.getMerchant() instanceof Villager villager){
if(QEMain.DEBUG){
Bukkit.getConsoleSender().sendMessage("[调试 - 任务<VILLAGER_TRADE_JOB>] 玩家 "+player.getName()+""+villager.getProfession().name()+" 村民进行交易");
}
PlayerQuestManager.addProgress(
playerUuid,
QuestTaskType.VILLAGER_TRADE_JOB,
villager.getProfession().name(),
recipe.getVillagerExperience()
);
}
}
}

View File

@@ -0,0 +1,47 @@
package com.io.yaohun.questengine.player;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class PlayerQuestData {
private final UUID uuid;
private final Map<String, PlayerQuestProgress> questProgressMap = new ConcurrentHashMap<>();
public PlayerQuestData(UUID uuid) {
this.uuid = uuid;
}
public UUID getUuid() {
return uuid;
}
public boolean hasQuest(String questId) {
return questProgressMap.containsKey(questId);
}
public void addQuest(PlayerQuestProgress progress) {
questProgressMap.put(progress.getQuestId(), progress);
}
public PlayerQuestProgress getQuestProgress(String questId) {
return questProgressMap.get(questId);
}
public void removeQuest(String questId) {
questProgressMap.remove(questId);
}
public Map<String, PlayerQuestProgress> getQuestProgressMap() {
return questProgressMap;
}
public PlayerQuestData copy() {
PlayerQuestData copy = new PlayerQuestData(uuid);
for (PlayerQuestProgress progress : questProgressMap.values()) {
copy.addQuest(progress.copy());
}
return copy;
}
}

View File

@@ -0,0 +1,219 @@
package com.io.yaohun.questengine.player;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.storage.PlayerQuestSaveQueue;
import com.io.yaohun.questengine.player.storage.PlayerQuestStorageManager;
import com.io.yaohun.questengine.quest.*;
import com.io.yaohun.questengine.util.ColorUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class PlayerQuestManager {
private static final Map<UUID, PlayerQuestData> playerDataMap = new ConcurrentHashMap<>();
public static PlayerQuestData getPlayerData(UUID uuid) {
return playerDataMap.computeIfAbsent(
uuid,
id -> PlayerQuestStorageManager.getStorage().load(id)
);
}
public static PlayerQuestData getCachedPlayerData(UUID uuid) {
return playerDataMap.get(uuid);
}
public static void loadPlayer(UUID uuid){
getPlayerData(uuid);
}
public static void savePlayer(UUID uuid) {
PlayerQuestData data = playerDataMap.get(uuid);
if (data != null) {
PlayerQuestSaveQueue.markDirty(uuid);
}
}
public static void saveAll() {
for (PlayerQuestData data : playerDataMap.values()) {
PlayerQuestStorageManager.getStorage().save(data);
}
}
public static boolean hasAccepted(UUID uuid, String questId) {
return getPlayerData(uuid).hasQuest(questId);
}
public static boolean acceptQuest(UUID uuid, String questId) {
PlayerQuestData data = getPlayerData(uuid);
if (data.hasQuest(questId)) {
return false;
}
data.addQuest(new PlayerQuestProgress(questId));
PlayerQuestSaveQueue.markDirty(uuid);
return true;
}
public static PlayerQuestProgress getProgress(UUID uuid, String questId) {
return getPlayerData(uuid).getQuestProgress(questId);
}
public static void removeQuest(UUID uuid, String questId) {
PlayerQuestData data = getPlayerData(uuid);
data.removeQuest(questId);
PlayerQuestSaveQueue.markDirty(uuid);
}
public static void removeQuestsFromCache(Collection<String> questIds) {
for (PlayerQuestData data : playerDataMap.values()) {
for (String questId : questIds) {
data.removeQuest(questId);
}
PlayerQuestSaveQueue.markDirty(data.getUuid());
}
}
public static void clearPlayer(UUID uuid) {
savePlayerNow(uuid);
playerDataMap.remove(uuid);
}
public static void savePlayerNow(UUID uuid) {
PlayerQuestData data = playerDataMap.get(uuid);
if (data != null) {
PlayerQuestStorageManager.getStorage().save(data);
}
}
public static void unloadPlayer(UUID uuid){
playerDataMap.remove(uuid);
PlayerQuestSaveQueue.removeDirty(uuid);
}
public static void deletePlayerData(UUID uuid){
playerDataMap.remove(uuid);
PlayerQuestStorageManager
.getStorage()
.deletePlayer(uuid);
}
public static boolean clearAllQuests(UUID uuid){
PlayerQuestData data = playerDataMap.get(uuid);
if(data != null){
data.getQuestProgressMap().clear();
}
savePlayerNow(uuid);
return true;
}
public static Map<UUID, PlayerQuestData> getPlayerDataMap() {
return playerDataMap;
}
public static void addProgress(UUID uuid, QuestTaskType type, String target, int amount) {
PlayerQuestData playerData = getPlayerData(uuid);
boolean changed = false;
for (PlayerQuestProgress progress : playerData.getQuestProgressMap().values()) {
// 已完成任务跳过
if (progress.isCompleted()) {
continue;
}
Quest quest = QuestManager.getQuest(progress.getQuestId());
if (quest == null) {
continue;
}
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
continue;
}
if (!QuestConditionChecker.canProgress(player, quest)) {
continue;
}
boolean allCompleted = true;
for (QuestTask task : quest.getTasks().values()) {
// 类型不匹配
if (task.getType() != type) {
allCompleted = false;
continue;
}
// 目标不匹配
if (!task.isTarget(target)) {
allCompleted = false;
continue;
}
int current = progress.getProgress(task.getId());
// 已达成
if (current >= task.getAmount()) {
continue;
}
progress.addProgress(task.getId(), amount);
changed = true;
int newValue = progress.getProgress(task.getId());
if (QEMain.DEBUG) {
int fixedValue = Math.min(newValue, task.getAmount());
player.sendMessage(
ColorUtil.color(
"&7[任务调试] &f"
+ task.getDisplayName()
+ " &a+"
+ amount
+ " &7("
+ fixedValue
+ "/"
+ task.getAmount()
+ ")"
)
);
}
// 防止超出
if (newValue > task.getAmount()) {
progress.setProgress(task.getId(), task.getAmount());
}
}
// 检查是否全部完成
for (QuestTask task : quest.getTasks().values()) {
int current = progress.getProgress(task.getId());
if (current < task.getAmount()) {
allCompleted = false;
break;
}
}
// 完成任务
if (allCompleted) {
progress.setCompleted(true);
changed = true;
// 完成消息
for (String msg : quest.getCompleteMessages()) {
player.sendMessage(ColorUtil.color(msg));
}
// reward message
for (String msg : quest.getReward().getMessages()) {
player.sendMessage(ColorUtil.color(msg.replace("{name}", quest.getDisplayName())));
}
// reward command
for (String cmd : quest.getReward().getCommands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", player.getName()));
}
}
}
if (changed) {
PlayerQuestSaveQueue.markDirty(uuid);
}
}
}

View File

@@ -0,0 +1,53 @@
package com.io.yaohun.questengine.player;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PlayerQuestProgress {
private final String questId;
private final Map<String, Integer> taskProgressMap = new ConcurrentHashMap<>();
private volatile boolean completed;
public PlayerQuestProgress(String questId) {
this.questId = questId;
}
public String getQuestId() {
return questId;
}
public int getProgress(String taskId) {
return taskProgressMap.getOrDefault(taskId, 0);
}
public void addProgress(String taskId, int amount) {
int current = getProgress(taskId);
taskProgressMap.put(taskId, current + amount);
}
public void setProgress(String taskId, int amount) {
taskProgressMap.put(taskId, amount);
}
public Map<String, Integer> getTaskProgressMap() {
return taskProgressMap;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public PlayerQuestProgress copy() {
PlayerQuestProgress copy = new PlayerQuestProgress(questId);
copy.setCompleted(completed);
for (Map.Entry<String, Integer> entry : taskProgressMap.entrySet()) {
copy.setProgress(entry.getKey(), entry.getValue());
}
return copy;
}
}

View File

@@ -0,0 +1,247 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.quest.QuestType;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import java.sql.*;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
public class MySQLPlayerQuestStorage implements PlayerQuestStorage{
private final QEMain plugin;
private HikariDataSource dataSource;
private String table;
public MySQLPlayerQuestStorage(QEMain plugin){
this.plugin = plugin;
}
@Override
public void init() {
try {
FileConfiguration ymal = plugin.getConfig();
String host = ymal.getString("Storage.MySQL.Host");
int port = ymal.getInt("Storage.MySQL.Port");
String database = ymal.getString("Storage.MySQL.Database");
String username = ymal.getString("Storage.MySQL.Username");
String password = ymal.getString("Storage.MySQL.Password");
String prefix = ymal.getString("Storage.MySQL.TablePrefix","auquest_");
this.table = prefix + "player_progress";
HikariConfig config = new HikariConfig();
config.setJdbcUrl(
"jdbc:mysql://" + host + ":" + port + "/" + database
+ "?useSSL=false"
+ "&characterEncoding=utf8"
+ "&serverTimezone=Asia/Shanghai"
+ "&rewriteBatchedStatements=true"
);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(ymal.getInt("Storage.MySQL.Pool.MaximumPoolSize", 10));
config.setMinimumIdle(ymal.getInt("Storage.MySQL.Pool.MinimumIdle", 2));
config.setConnectionTimeout(ymal.getLong("Storage.MySQL.Pool.ConnectionTimeout", 10000));
config.setIdleTimeout(ymal.getLong("Storage.MySQL.Pool.IdleTimeout", 600000));
config.setMaxLifetime(ymal.getLong("Storage.MySQL.Pool.MaxLifetime", 1800000));
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource = new HikariDataSource(config);
createTable();
Bukkit.getConsoleSender().sendMessage("[AuQuest] MySQLStorage 初始化完成.");
} catch (Exception e) {
plugin.getLogger().warning("[AuQuest] MySQLStorage 初始化失败.");
e.printStackTrace();
}
}
private void createTable() throws SQLException {
String progressSql =
"CREATE TABLE IF NOT EXISTS " + table + " (" +
"uuid VARCHAR(36) NOT NULL," +
"quest_id VARCHAR(64) NOT NULL," +
"task_id VARCHAR(64) NOT NULL," +
"progress INT NOT NULL DEFAULT 0," +
"completed TINYINT(1) NOT NULL DEFAULT 0," +
"PRIMARY KEY(uuid, quest_id, task_id)" +
");";
String resetTable = getResetTable();
String resetSql =
"CREATE TABLE IF NOT EXISTS " + resetTable + " (" +
"reset_type VARCHAR(16) NOT NULL," +
"reset_key VARCHAR(32) NOT NULL," +
"reset_time BIGINT NOT NULL," +
"PRIMARY KEY(reset_type, reset_key)" +
");";
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate(progressSql);
statement.executeUpdate(resetSql);
}
}
private String getResetTable() {
String prefix = plugin.getConfig().getString("Storage.MySQL.TablePrefix", "auquest_");
return prefix + "reset_record";
}
@Override
public PlayerQuestData load(UUID uuid) {
PlayerQuestData data = new PlayerQuestData(uuid);
String sql = "SELECT quest_id, task_id, progress, completed FROM " + table + " WHERE uuid=?";
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, uuid.toString());
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String questId = rs.getString("quest_id");
String taskId = rs.getString("task_id");
int progressValue = rs.getInt("progress");
boolean completed = rs.getBoolean("completed");
PlayerQuestProgress progress = data.getQuestProgress(questId);
if (progress == null) {
progress = new PlayerQuestProgress(questId);
progress.setCompleted(completed);
data.addQuest(progress);
}
progress.setProgress(taskId, progressValue);
if (completed) {
progress.setCompleted(true);
}
}
}
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] 玩家数据读取失败: " + data.getUuid());
e.printStackTrace();
}
return data;
}
@Override
public void save(PlayerQuestData data) {
PlayerQuestData snapshot = data.copy();
String deleteSql = "DELETE FROM " + table + " WHERE uuid=?";
String insertSql = "INSERT INTO " + table + " (uuid, quest_id, task_id, progress, completed) " + "VALUES (?, ?, ?, ?, ?)";
try (Connection connection = dataSource.getConnection()) {
connection.setAutoCommit(false);
try (PreparedStatement deletePs = connection.prepareStatement(deleteSql)) {
deletePs.setString(1, snapshot.getUuid().toString());
deletePs.executeUpdate();
}
try (PreparedStatement insertPs = connection.prepareStatement(insertSql)) {
for (PlayerQuestProgress progress : snapshot.getQuestProgressMap().values()) {
if (progress.getTaskProgressMap().isEmpty()) {
insertPs.setString(1, snapshot.getUuid().toString());
insertPs.setString(2, progress.getQuestId());
insertPs.setString(3, "__quest__");
insertPs.setInt(4, 0);
insertPs.setBoolean(5, progress.isCompleted());
insertPs.addBatch();
continue;
}
for (Map.Entry<String, Integer> entry : progress.getTaskProgressMap().entrySet()) {
insertPs.setString(1, snapshot.getUuid().toString());
insertPs.setString(2, progress.getQuestId());
insertPs.setString(3, entry.getKey());
insertPs.setInt(4, entry.getValue());
insertPs.setBoolean(5, progress.isCompleted());
insertPs.addBatch();
}
}
insertPs.executeBatch();
}
connection.commit();
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] 玩家数据保存失败: " + snapshot.getUuid());
e.printStackTrace();
}
}
@Override
public void close() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
Bukkit.getConsoleSender().sendMessage("[AuQuest] MySQLStorage 已正常关闭.");
}
}
@Override
public void deleteQuestsByIds(Collection<String> questIds) {
if (questIds == null || questIds.isEmpty()) {
return;
}
String sql = "DELETE FROM " + table + " WHERE quest_id = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
for (String questId : questIds) {
ps.setString(1, questId);
ps.addBatch();
}
ps.executeBatch();
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] MySQLStorage 删除刷新任务数据失败.");
e.printStackTrace();
}
}
@Override
public boolean tryMarkReset(QuestType type, String resetKey) {
String sql = "INSERT IGNORE INTO " + getResetTable() +
" (reset_type, reset_key, reset_time) VALUES (?, ?, ?)";
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, type.name());
ps.setString(2, resetKey);
ps.setLong(3, System.currentTimeMillis());
return ps.executeUpdate() == 1;
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] MySQLStorage 记录任务刷新状态失败: " + type + " / " + resetKey);
e.printStackTrace();
return false;
}
}
@Override
public void deletePlayer(UUID uuid) {
String sql =
"DELETE FROM " + table + " WHERE uuid=?";
try(Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)){
ps.setString(1, uuid.toString());
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,78 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class PlayerQuestSaveQueue {
private static final Set<UUID> dirtyPlayers = new HashSet<>();
private static BukkitTask task;
public static void markDirty(UUID uuid) {
synchronized (dirtyPlayers) {
dirtyPlayers.add(uuid);
}
}
public static void start() {
int intervalSeconds = QEMain.inst().getConfig().getInt("Storage.SaveInterval", 10);
task = Bukkit.getScheduler().runTaskTimerAsynchronously(
QEMain.inst(),
PlayerQuestSaveQueue::flushAsync,
intervalSeconds * 20L,
intervalSeconds * 20L
);
}
public static void removeDirty(UUID uuid){
synchronized (dirtyPlayers) {
dirtyPlayers.remove(uuid);
}
}
public static void flushAsync() {
Set<UUID> copy;
synchronized (dirtyPlayers) {
if (dirtyPlayers.isEmpty()) {
return;
}
copy = new HashSet<>(dirtyPlayers);
dirtyPlayers.clear();
}
for (UUID uuid : copy) {
PlayerQuestData data = PlayerQuestManager.getCachedPlayerData(uuid);
if (data != null) {
PlayerQuestStorageManager.getStorage().save(data);
}
}
}
public static void flushSync() {
if (task != null) {
task.cancel();
task = null;
}
Set<UUID> copy;
synchronized (dirtyPlayers) {
copy = new HashSet<>(dirtyPlayers);
dirtyPlayers.clear();
}
for (UUID uuid : copy) {
PlayerQuestData data = PlayerQuestManager.getCachedPlayerData(uuid);
if (data != null) {
PlayerQuestStorageManager.getStorage().save(data);
}
}
}
}

View File

@@ -0,0 +1,24 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.quest.QuestType;
import java.util.Collection;
import java.util.UUID;
public interface PlayerQuestStorage {
void init();
PlayerQuestData load(UUID uuid);
void save(PlayerQuestData data);
void deleteQuestsByIds(Collection<String> questIds);
boolean tryMarkReset(QuestType type, String resetKey);
void deletePlayer(UUID uuid);
void close();
}

View File

@@ -0,0 +1,40 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.QEMain;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
public class PlayerQuestStorageManager {
private static PlayerQuestStorage storage;
public static void init(QEMain plugin){
FileConfiguration config = plugin.getConfig();
String type = config.getString("Storage.Type","yaml").toLowerCase();
Bukkit.getConsoleSender().sendMessage("[AuQuest] 数据存储模式: §e"+type);
switch (type){
case "yaml":
storage = new YamlPlayerQuestStorage(plugin);
break;
case "sqlite":
storage = new SQLitePlayerQuestStorage(plugin);
break;
case "mysql":
storage = new MySQLPlayerQuestStorage(plugin);
break;
default:
storage = new YamlPlayerQuestStorage(plugin);
}
storage.init();
}
public static PlayerQuestStorage getStorage(){
return storage;
}
public static void close(){
if(storage != null){
storage.close();
}
}
}

View File

@@ -0,0 +1,257 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.quest.QuestType;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.sql.*;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
public class SQLitePlayerQuestStorage implements PlayerQuestStorage{
private final QEMain plugin;
private Connection connection;
public SQLitePlayerQuestStorage(QEMain plugin){
this.plugin = plugin;
}
// plugin.getLogger().warning("[AuQuest] 玩家数据保存失败: " + data.getUuid());
@Override
public void init() {
try {
File folder = plugin.getDataFolder();
if (!folder.exists()) {
folder.mkdirs();
}
FileConfiguration config = plugin.getConfig();
String fileName = config.getString("Storage.SQLite.File", "PlayerData.db");
File databaseFile = new File(folder, fileName);
connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getAbsolutePath());
createTable();
Bukkit.getConsoleSender().sendMessage("[AuQuest] SQLiteStorage 初始化完成.");
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] SQLiteStorage 初始化失败.");
e.printStackTrace();
}
}
private void createTable() throws SQLException {
String sql = """
CREATE TABLE IF NOT EXISTS quest_player_progress (
uuid TEXT NOT NULL,
quest_id TEXT NOT NULL,
task_id TEXT NOT NULL,
progress INTEGER NOT NULL DEFAULT 0,
completed INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (uuid, quest_id, task_id)
);
""";
try (Statement statement = connection.createStatement()) {
statement.executeUpdate(sql);
}
String resetSql = """
CREATE TABLE IF NOT EXISTS quest_reset_record (
reset_type TEXT NOT NULL,
reset_key TEXT NOT NULL,
reset_time INTEGER NOT NULL,
PRIMARY KEY(reset_type, reset_key)
);
""";
try (Statement statement = connection.createStatement()) {
statement.executeUpdate(sql);
statement.executeUpdate(resetSql);
}
}
@Override
public synchronized PlayerQuestData load(UUID uuid) {
PlayerQuestData data = new PlayerQuestData(uuid);
String sql = "SELECT quest_id, task_id, progress, completed FROM quest_player_progress WHERE uuid = ?";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, uuid.toString());
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String questId = rs.getString("quest_id");
String taskId = rs.getString("task_id");
int progressValue = rs.getInt("progress");
boolean completed = rs.getInt("completed") == 1;
PlayerQuestProgress progress = data.getQuestProgress(questId);
if (progress == null) {
progress = new PlayerQuestProgress(questId);
progress.setCompleted(completed);
data.addQuest(progress);
}
progress.setProgress(taskId, progressValue);
if (completed) {
progress.setCompleted(true);
}
}
}
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] 玩家数据读取失败: " + data.getUuid());
e.printStackTrace();
}
return data;
}
@Override
public synchronized void save(PlayerQuestData data) {
PlayerQuestData snapshot = data.copy();
String deleteSql = "DELETE FROM quest_player_progress WHERE uuid = ?";
String insertSql = """
INSERT INTO quest_player_progress
(uuid, quest_id, task_id, progress, completed)
VALUES (?, ?, ?, ?, ?)
""";
try {
connection.setAutoCommit(false);
try (PreparedStatement deletePs = connection.prepareStatement(deleteSql)) {
deletePs.setString(1, snapshot.getUuid().toString());
deletePs.executeUpdate();
}
try (PreparedStatement insertPs = connection.prepareStatement(insertSql)) {
for (PlayerQuestProgress progress : snapshot.getQuestProgressMap().values()) {
for (Map.Entry<String, Integer> entry : progress.getTaskProgressMap().entrySet()) {
insertPs.setString(1, snapshot.getUuid().toString());
insertPs.setString(2, progress.getQuestId());
insertPs.setString(3, entry.getKey());
insertPs.setInt(4, entry.getValue());
insertPs.setInt(5, progress.isCompleted() ? 1 : 0);
insertPs.addBatch();
}
if (progress.getTaskProgressMap().isEmpty()) {
insertPs.setString(1, snapshot.getUuid().toString());
insertPs.setString(2, progress.getQuestId());
insertPs.setString(3, "__quest__");
insertPs.setInt(4, 0);
insertPs.setInt(5, progress.isCompleted() ? 1 : 0);
insertPs.addBatch();
}
}
insertPs.executeBatch();
}
connection.commit();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException ignored) {
}
plugin.getLogger().warning("[AuQuest] 玩家数据保存失败: " + snapshot.getUuid());
e.printStackTrace();
}finally {
try {
connection.setAutoCommit(true);
} catch (SQLException ignored) {
}
}
}
@Override
public synchronized void close() {
if (connection == null) {
return;
}try {
connection.close();
Bukkit.getConsoleSender().sendMessage("[AuQuest] SQLiteStorage 已正常关闭.");
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public synchronized void deleteQuestsByIds(Collection<String> questIds) {
if (questIds == null || questIds.isEmpty()) {
return;
}
String sql = "DELETE FROM quest_player_progress WHERE quest_id = ?";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
for (String questId : questIds) {
ps.setString(1, questId);
ps.addBatch();
}
ps.executeBatch();
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] SQLiteStorage 删除刷新任务数据失败.");
e.printStackTrace();
}
}
@Override
public synchronized void deletePlayer(UUID uuid) {
String sql =
"DELETE FROM quest_player_progress WHERE uuid=?";
try(PreparedStatement ps = connection.prepareStatement(sql)){
ps.setString(1, uuid.toString());
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public synchronized boolean tryMarkReset(QuestType type, String resetKey) {
String sql = "INSERT OR IGNORE INTO quest_reset_record (reset_type, reset_key, reset_time) VALUES (?, ?, ?)";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, type.name());
ps.setString(2, resetKey);
ps.setLong(3, System.currentTimeMillis());
return ps.executeUpdate() == 1;
} catch (SQLException e) {
plugin.getLogger().warning("[AuQuest] SQLiteStorage 记录任务刷新状态失败: " + type + " / " + resetKey);
e.printStackTrace();
return false;
}
}
public synchronized File exportToYaml() {
File outFile = new File(plugin.getDataFolder(), "out-sqlite-" + System.currentTimeMillis() + ".yml");
YamlConfiguration yaml = new YamlConfiguration();
String sql = "SELECT uuid, quest_id, task_id, progress, completed FROM quest_player_progress ORDER BY uuid, quest_id, task_id";
try (PreparedStatement ps = connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String uuid = rs.getString("uuid");
String questId = rs.getString("quest_id");
String taskId = rs.getString("task_id");
int progress = rs.getInt("progress");
boolean completed = rs.getInt("completed") == 1;
String path = "players." + uuid + ".quests." + questId;
yaml.set(path + ".completed", completed);
yaml.set(path + ".progress." + taskId, progress);
}
yaml.save(outFile);
return outFile;
} catch (Exception e) {
plugin.getLogger().warning("[AuQuest] SQLiteStorage 数据导出失败");
e.printStackTrace();
return null;
}
}
}

View File

@@ -0,0 +1,159 @@
package com.io.yaohun.questengine.player.storage;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestData;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.quest.QuestType;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
public class YamlPlayerQuestStorage implements PlayerQuestStorage {
private final QEMain plugin;
public YamlPlayerQuestStorage(QEMain plugin){
this.plugin = plugin;
}
@Override
public void init() {
File folder = getPlayerFolder();
if (!folder.exists()) {
folder.mkdirs();
}
Bukkit.getConsoleSender().sendMessage("[AuQuest] YamlStorage 初始化完成.");
}
@Override
public PlayerQuestData load(UUID uuid) {
PlayerQuestData data = new PlayerQuestData(uuid);
File file = getPlayerFile(uuid);
if(!file.exists()){
return data;
}
FileConfiguration yaml = YamlConfiguration.loadConfiguration(file);
ConfigurationSection questsSection = yaml.getConfigurationSection("quests");
if(questsSection == null){
return data;
}
for (String questId : questsSection.getKeys(false)){
PlayerQuestProgress progress = new PlayerQuestProgress(questId);
boolean completed = yaml.getBoolean("quests." + questId + ".completed", false);
progress.setCompleted(completed);
ConfigurationSection progressSection = yaml.getConfigurationSection("quests." + questId + ".progress");
if (progressSection != null) {
for (String taskId : progressSection.getKeys(false)) {
int value = progressSection.getInt(taskId);
progress.setProgress(taskId, value);
}
}
data.addQuest(progress);
}
return data;
}
@Override
public void save(PlayerQuestData data) {
PlayerQuestData snapshot = data.copy();
File file = getPlayerFile(snapshot.getUuid());
YamlConfiguration yaml = new YamlConfiguration();
for (PlayerQuestProgress progress : snapshot.getQuestProgressMap().values()) {
String path = "quests." + progress.getQuestId();
yaml.set(path + ".completed", progress.isCompleted());
for (Map.Entry<String, Integer> entry : progress.getTaskProgressMap().entrySet()) {
yaml.set(path + ".progress." + entry.getKey(), entry.getValue());
}
}
try {
yaml.save(file);
} catch (IOException e) {
plugin.getLogger().warning("[AuQuest] 玩家数据保存失败: " + snapshot.getUuid());
e.printStackTrace();
}
}
@Override
public void deleteQuestsByIds(Collection<String> questIds) {
File folder = getPlayerFolder();
File[] files = folder.listFiles((dir, name) -> name.endsWith(".yml"));
if (files == null) {
return;
}
for (File file : files) {
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
boolean changed = false;
for (String questId : questIds) {
String path = "quests." + questId;
if (yaml.contains(path)) {
yaml.set(path, null);
changed = true;
}
}
if (changed) {
try {
yaml.save(file);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public boolean tryMarkReset(QuestType type, String resetKey) {
File file = new File(plugin.getDataFolder(), "last_reset.yml");
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
String path = "reset." + type.name();
String oldKey = yaml.getString(path);
if (resetKey.equals(oldKey)) {
return false;
}
yaml.set(path, resetKey);
yaml.set(path + "_time", System.currentTimeMillis());
try {
yaml.save(file);
return true;
} catch (Exception e) {
plugin.getLogger().warning("YAML 记录任务刷新状态失败: " + type + " / " + resetKey);
e.printStackTrace();
return false;
}
}
@Override
public void close() {
Bukkit.getConsoleSender().sendMessage("[AuQuest] YamlStorage 已正常关闭.");
}
@Override
public void deletePlayer(UUID uuid) {
File file = getPlayerFile(uuid);
if(file.exists()){
file.delete();
}
}
private File getPlayerFolder() {
return new File(plugin.getDataFolder(), "PlayerData");
}
private File getPlayerFile(UUID uuid) {
return new File(getPlayerFolder(), uuid + ".yml");
}
}

View File

@@ -0,0 +1,75 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.util.ColorUtil;
import java.util.List;
import java.util.Map;
public class Quest {
private final String id;
private final String displayName;
private final List<String> description;
private final QuestType type;
private final List<String> receiveMessages;
private final List<String> completeMessages;
private final Map<String, QuestTask> tasks;
private final QuestCondition condition;
private final QuestReward reward;
public Quest(String id, String displayName,
List<String> description,
QuestType type,
List<String> receiveMessages,
List<String> completeMessages,
Map<String, QuestTask> tasks,
QuestCondition condition,
QuestReward reward) {
this.id = id;
this.displayName = ColorUtil.color(displayName);
this.description = ColorUtil.color(description);
this.type = type;
this.receiveMessages = ColorUtil.color(receiveMessages);
this.completeMessages = ColorUtil.color(completeMessages);
this.tasks = tasks;
this.condition = condition;
this.reward = reward;
}
public String getId() {
return id;
}
public String getDisplayName() {
return displayName;
}
public List<String> getDescription() {
return description;
}
public QuestType getType() {
return type;
}
public List<String> getReceiveMessages() {
return receiveMessages;
}
public List<String> getCompleteMessages() {
return completeMessages;
}
public Map<String, QuestTask> getTasks() {
return tasks;
}
public QuestCondition getCondition() {
return condition;
}
public QuestReward getReward() {
return reward;
}
}

View File

@@ -0,0 +1,40 @@
package com.io.yaohun.questengine.quest;
import java.util.List;
public class QuestCondition {
private final int level;
private final List<String> completedQuests;
private final List<String> worlds;
public QuestCondition(int level, List<String> completedQuests, List<String> worlds) {
this.level = level;
this.completedQuests = completedQuests;
this.worlds = worlds;
}
public int getLevel() {
return level;
}
public List<String> getCompletedQuests() {
return completedQuests;
}
public List<String> getWorlds() {
return worlds;
}
public boolean hasLevelLimit() {
return level > 0;
}
public boolean hasCompletedQuestLimit() {
return completedQuests != null && !completedQuests.isEmpty();
}
public boolean hasWorldLimit() {
return worlds != null && !worlds.isEmpty();
}
}

View File

@@ -0,0 +1,67 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.PlayerQuestProgress;
import com.io.yaohun.questengine.util.ColorUtil;
import com.io.yaohun.questengine.util.MessageUtil;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
public class QuestConditionChecker {
public static boolean canAccept(Player player, Quest quest, boolean sendMessage) {
QuestCondition condition = quest.getCondition();
if (condition == null) {
return true;
}
if (condition.hasLevelLimit()) {
int playerLevel = getPlayerLevel(player);
if (playerLevel < condition.getLevel()) {
if (sendMessage) {
String message = MessageUtil.getMessage("condition_not_quest");
message = message.replace("{level}", String.valueOf(condition.getLevel()));
MessageUtil.sendMessage(player, message, Sound.ENTITY_VILLAGER_NO);
}
return false;
}
}
if (condition.hasCompletedQuestLimit()) {
for (String needQuestId : condition.getCompletedQuests()) {
PlayerQuestProgress progress = PlayerQuestManager.getProgress(player.getUniqueId(), needQuestId);
if (progress == null || !progress.isCompleted()) {
if (sendMessage) {
String message = MessageUtil.getMessage("condition_not_quest");
message = message.replace("{quest}", needQuestId);
MessageUtil.sendMessage(player, message, Sound.ENTITY_VILLAGER_NO);
}
return false;
}
}
}
if (condition.hasWorldLimit()) {
String worldName = player.getWorld().getName();
if (!condition.getWorlds().contains(worldName)) {
if (sendMessage) {
MessageUtil.sendMessageKey(player, "condition_not_world", Sound.ENTITY_VILLAGER_NO);
}
return false;
}
}
return true;
}
public static boolean canProgress(Player player, Quest quest) {
QuestCondition condition = quest.getCondition();
if (condition == null || !condition.hasWorldLimit()) {
return true;
}
return condition.getWorlds().contains(player.getWorld().getName());
}
private static int getPlayerLevel(Player player) {
return player.getLevel(); // 这里先用原版等级后续可替换成你的等级系统API
}
}

View File

@@ -0,0 +1,149 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.QEMain;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.util.*;
public class QuestLoader {
private final QEMain plugin;
public QuestLoader(QEMain plugin){
this.plugin = plugin;
}
public Map<String, Quest> loadQuests(){
Map<String, Quest> questMap = new HashMap<>();
File folder = new File(plugin.getDataFolder(), "Quests");
if(!folder.exists()){
folder.mkdirs();
return questMap;
}
File[] files = folder.listFiles(((dir, name) -> name.endsWith(".yml")));
if(files == null || files.length == 0){
return questMap;
}
int dayCount = 0;
int weekCount = 0;
int monthCount = 0;
int fixedCount = 0;
for (File file : files){
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
for (String questId : config.getKeys(false)){
ConfigurationSection section = config.getConfigurationSection(questId);
if(section == null){
continue;
}
Quest quest = loadQuest(questId, section, file.getName());
if(quest == null){
continue;
}
questMap.put(questId, quest);
switch (quest.getType()){
case DAILY:
dayCount++;
break;
case WEEKLY:
weekCount++;
break;
case MONTHLY:
monthCount++;
break;
case FIXED:
fixedCount++;
break;
}
}
}
Bukkit.getConsoleSender().sendMessage("[AuQuest] 已载入任务: ["+questMap.size()+"个]");
Bukkit.getConsoleSender().sendMessage(" - 每日 > §e"+dayCount+"");
Bukkit.getConsoleSender().sendMessage(" - 每周 > §e"+weekCount+"");
Bukkit.getConsoleSender().sendMessage(" - 每月 > §e"+monthCount+"");
Bukkit.getConsoleSender().sendMessage(" - 长期 > §e"+fixedCount+"");
return questMap;
}
private Quest loadQuest(String questId, ConfigurationSection section, String fileName){
try {
String displayName = section.getString("display_name", questId);
List<String> description = section.getStringList("description");
QuestType questType = QuestType.valueOf(
section.getString("type", "FIXED").toUpperCase()
);
List<String> receiveMessages = section.getStringList("messages.receive");
List<String> completeMessages = section.getStringList("messages.complete");
Map<String, QuestTask> tasks = loadTasks(section.getConfigurationSection("tasks"), questId);
if (tasks.isEmpty()) {
plugin.getLogger().warning("任务 " + questId + " 没有有效任务目标,文件: " + fileName);
return null;
}
QuestCondition condition = loadCondition(section.getConfigurationSection("conditions"));
QuestReward reward = loadReward(section.getConfigurationSection("rewards"), displayName);
return new Quest(questId, displayName, description, questType, receiveMessages, completeMessages, tasks, condition, reward);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private Map<String, QuestTask> loadTasks(ConfigurationSection section, String questId) {
Map<String, QuestTask> taskMap = new LinkedHashMap<>();
if (section == null) {
return taskMap;
}
for (String taskId : section.getKeys(false)) {
ConfigurationSection taskSection = section.getConfigurationSection(taskId);
if (taskSection == null) {
continue;
}
try {
QuestTaskType taskType = QuestTaskType.valueOf(taskSection.getString("type", "BREAK").toUpperCase());
String displayName = taskSection.getString("display_name", taskId);
int amount = taskSection.getInt("amount", 1);
Set<String> targets = new HashSet<>(taskSection.getStringList("targets")
);
if (targets.isEmpty()) {
//plugin.getLogger().warning("任务 " + questId + " 的目标 " + taskId + " 没有配置 targets");
continue;
}
QuestTask task = new QuestTask(taskId, taskType, displayName, amount, targets);
taskMap.put(taskId, task);
} catch (Exception e) {
e.printStackTrace();
}
}
return taskMap;
}
private QuestCondition loadCondition(ConfigurationSection section) {
if (section == null) {
return new QuestCondition(0, Collections.emptyList(), Collections.emptyList());
}
int level = section.getInt("level", 0);
List<String> completedQuests = section.getStringList("completed_quests");
List<String> worlds = section.getStringList("worlds");
return new QuestCondition(level, completedQuests, worlds);
}
private QuestReward loadReward(ConfigurationSection section,String questName) {
if (section == null) {
return new QuestReward(Collections.emptyList(), Collections.emptyList());
}
List<String> commands = section.getStringList("commands");
List<String> messages = new ArrayList<>(section.getStringList("messages"));
for (int i = 0; i < messages.size(); i++) {
String line = messages.get(i);
if (line.contains("{name}")) {
messages.set(i, line.replace("{name}", questName));
}
}
return new QuestReward(commands, messages);
}
}

View File

@@ -0,0 +1,38 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.QEMain;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class QuestManager {
private static Map<String, Quest> questMap = new HashMap<>();
public static void reloadQuestManager(QEMain plugin) {
QuestLoader questLoader = new QuestLoader(plugin);
questMap = questLoader.loadQuests();
}
public static Quest getQuest(String questId) {
return questMap.get(questId);
}
public static boolean hasQuest(String questId) {
return questMap.containsKey(questId);
}
public static Collection<Quest> getAllQuests() {
return Collections.unmodifiableCollection(questMap.values());
}
public static Map<String, Quest> getQuestMap() {
return Collections.unmodifiableMap(questMap);
}
public static int size() {
return questMap.size();
}
}

View File

@@ -0,0 +1,98 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.QEMain;
import com.io.yaohun.questengine.player.PlayerQuestManager;
import com.io.yaohun.questengine.player.storage.PlayerQuestStorageManager;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
public class QuestResetManager {
private static int lastCheckMinute = -1;
private static BukkitTask task;
public static void start() {
stop();
int min = 60;
int max = 180;
int rand = ThreadLocalRandom.current().nextInt(min, max + 1);
task = Bukkit.getScheduler().runTaskTimerAsynchronously(
QEMain.inst(),
QuestResetManager::checkReset,
20L,
20L * rand
);
}
public static void stop() {
if (task != null) {
task.cancel();
task = null;
}
}
private static void checkReset() {
LocalDateTime now = LocalDateTime.now();
if (now.getHour() != 0 || now.getMinute() != 0) {
return;
}
int minuteKey = now.getYear() * 100000 + now.getDayOfYear() * 100 + now.getMinute();
if (lastCheckMinute == minuteKey) {
return;
}
lastCheckMinute = minuteKey;
resetByType(QuestType.DAILY, getDailyKey(now.toLocalDate()));
if (now.getDayOfWeek() == DayOfWeek.MONDAY) {
resetByType(QuestType.WEEKLY, getWeeklyKey(now.toLocalDate()));
}
if (now.getDayOfMonth() == 1) {
resetByType(QuestType.MONTHLY, getMonthlyKey(now.toLocalDate()));
}
}
private static void resetByType(QuestType type, String resetKey) {
List<String> questIds = QuestManager.getAllQuests().stream()
.filter(quest -> quest.getType() == type)
.map(Quest::getId)
.collect(Collectors.toList());
if (questIds.isEmpty()) {
return;
}
boolean canReset = PlayerQuestStorageManager.getStorage().tryMarkReset(type, resetKey);
if (!canReset) {
QEMain.inst().getLogger().info(type + " 任务今日/本期已由其他实例刷新,跳过: " + resetKey);
return;
}
PlayerQuestStorageManager.getStorage().deleteQuestsByIds(questIds);
Bukkit.getScheduler().runTask(
QEMain.inst(),
() -> PlayerQuestManager.removeQuestsFromCache(questIds)
);
QEMain.inst().getLogger().info("[AuQests] 已刷新 " + type + " 任务resetKey=" + resetKey + ",清理任务数: " + questIds.size());
}
private static String getDailyKey(LocalDate date) {
return "DAILY_" + date;
}
private static String getWeeklyKey(LocalDate date) {
WeekFields weekFields = WeekFields.of(Locale.CHINA);
int week = date.get(weekFields.weekOfWeekBasedYear());
int year = date.get(weekFields.weekBasedYear());
return "WEEKLY_" + year + "-W" + week;
}
private static String getMonthlyKey(LocalDate date) {
return "MONTHLY_" + date.getYear() + "-" + String.format("%02d", date.getMonthValue());
}
}

View File

@@ -0,0 +1,25 @@
package com.io.yaohun.questengine.quest;
import com.io.yaohun.questengine.util.ColorUtil;
import org.bukkit.entity.Player;
import java.util.List;
public class QuestReward {
private final List<String> commands;
private final List<String> messages;
public QuestReward(List<String> commands, List<String> messages) {
this.commands = commands;
this.messages = ColorUtil.color(messages);
}
public List<String> getCommands() {
return commands;
}
public List<String> getMessages() {
return messages;
}
}

View File

@@ -0,0 +1,46 @@
package com.io.yaohun.questengine.quest;
import org.bukkit.Bukkit;
import java.util.Set;
public class QuestTask {
private final String id;
private final QuestTaskType type;
private final String displayName;
private final int amount;
private final Set<String> targets;
public QuestTask(String id, QuestTaskType type, String displayName, int amount, Set<String> targets) {
this.id = id;
this.type = type;
this.displayName = displayName;
this.amount = amount;
this.targets = targets;
}
public String getId() {
return id;
}
public QuestTaskType getType() {
return type;
}
public String getDisplayName() {
return displayName;
}
public int getAmount() {
return amount;
}
public Set<String> getTargets() {
return targets;
}
public boolean isTarget(String target) {
return targets.contains(target);
}
}

View File

@@ -0,0 +1,54 @@
package com.io.yaohun.questengine.quest;
public enum QuestTaskType {
BREAK_BLOCK("破坏方块"),
PLACE_BLOCK("放置方块"),
CRAFT_ITEM("合成物品"),
PICKUP_ITEM("拾取物品"),
FISH_ITEM("钓鱼获得物品"),
FISH_STAR("钓起鱼的星级"),
PLACE_BUCKET("监听倒水、倒岩浆、放置粉雪"),
FILL_BUCKET("监听装水、装岩浆、装牛奶"),
HARVEST_CROP("收获农作物"),
EXP_GAIN("经验获取"),
EXP_RPG("RPG经验获取"), // 预留接口
ONLINE_TODAY("今日在线时长"), // 预留接口
ONLINE_WEEK("本周在线时长"), // 预留接口
ONLINE_MONTH("本月在线时长"), // 预留接口
BREED("繁殖动物"),
COLLECT_LOOT("战利品获取"), // 预留接口
COLLECT_ENTITY("剪羊毛,取蘑菇煲"),
COLLECT_BLOCK("收集蜂蜜"),
TAME_ENTITY("驯服实体"),
VILLAGER_TRADE_ITEM("村民交易"),
VILLAGER_TRADE_JOB("村民交易"),
BREW_POTION("酿造药水"),
BREW_POTION_COUNT("酿造药水次数"),
USE_ANVIL("铁砧使用"),
USE_ITEM("使用物品"), // 预留接口
ENCHANT_ITEM("附魔物品"),
FEED_ANIMAL("喂动物"),
FEED_PLAYER("玩家东西"),
SMELT_ORE("烧制矿物"),
SMELT_FOOD("烧制食物"),
INTERACT_NPC("交互NPC"),
INTERACT_ENTITY("交互实体"),
INTERACT_ITEM("交互手持物品"),
SEND_COMMAND("发送命令"),
SEND_CHAT("发送聊天"),
OPEN_GUI_TITLE("打开指定标题菜单"),
OPEN_GUI_TYPE("打开指定Type菜单"),
KILL_TYPE("击杀原版怪"),
KILL_MYTHIC("击杀MM怪");
private final String displayName;
QuestTaskType(String displayName){
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}

View File

@@ -0,0 +1,19 @@
package com.io.yaohun.questengine.quest;
public enum QuestType {
DAILY("每日"),
WEEKLY("每周"),
MONTHLY("每月"),
FIXED("长期");
private final String displayName;
QuestType(String displayName){
this.displayName = displayName;
}
public String getDisplayName(){
return displayName;
}
}

View File

@@ -0,0 +1,37 @@
package com.io.yaohun.questengine.util;
import org.bukkit.ChatColor;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class ColorUtil {
private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
public static String color(String text) {
if (text == null) {
return "";
}
Matcher matcher = HEX_PATTERN.matcher(text);
while (matcher.find()) {
String hexCode = text.substring(matcher.start(), matcher.end());
String replaceSharp = hexCode.replace('#', 'x');
char[] ch = replaceSharp.toCharArray();
StringBuilder builder = new StringBuilder();
for (char c : ch) {
builder.append("&").append(c);
}
text = text.replace(hexCode, builder.toString());
matcher = HEX_PATTERN.matcher(text);
}
return ChatColor.translateAlternateColorCodes('&', text);
}
public static List<String> color(List<String> list) {
return list.stream().map(ColorUtil::color).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,71 @@
package com.io.yaohun.questengine.util;
import com.io.yaohun.questengine.QEMain;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.HashMap;
public class MessageUtil {
private static HashMap<String,String> messageMap = new HashMap<>();
public static void init(QEMain plugin) {
messageMap.clear();
File file = new File(plugin.getDataFolder(),"lang.yml");
// 如果没有这个文件就新建一个~
if(!file.exists()){
plugin.saveResource("lang.yml",false);
}
FileConfiguration yml = YamlConfiguration.loadConfiguration(file);
for (String ymlKey : yml.getKeys(false)){
ConfigurationSection section = yml.getConfigurationSection(ymlKey);
for (String key : section.getKeys(false)){
String message = ColorUtil.color(section.getString(key));
messageMap.put(key,message.replace("&","§"));
}
}
}
public static String getMessage(String key){
if(messageMap.containsKey(key)){
return messageMap.get(key);
}
return "§c未找到节点.§7#"+key;
}
public static void broadcast(String msg) {
Bukkit.getServer().broadcastMessage( msg.replace("&","§"));
}
public static void sendMessage(CommandSender sender, String message){
sender.sendMessage(message);
}
public static void sendMessageKey(CommandSender sender, String key, Sound sound){
sender.sendMessage(getMessage(key));
if(sender instanceof Player player){
player.playSound(player.getLocation(),sound,1,1);
}
}
public static void sendMessage(CommandSender sender, String message, Sound sound){
sender.sendMessage(message);
if(sender instanceof Player player){
player.playSound(player.getLocation(),sound,1,1);
}
}
public static void sendDescMessage(CommandSender sender, String message, Sound sound){
sender.sendMessage("§f[§c消息§f] §a正确用法: §e"+message);
if(sender instanceof Player player){
player.playSound(player.getLocation(),sound,1,1);
}
}
}

View File

@@ -0,0 +1,44 @@
# 任务代号 玩家接受任务 /aquest js 代号 玩家名
quest_1:
# 任务名
display_name: "&a[初级] 新手成长之路①"
# 任务描述内容
description:
- "&7完成基础生存挑战任务"
- "&7砍伐指定木材熟悉基础资源收集"
# 任务类型 DAILY、WEEKLY、MONTHLY、FIXED
type: DAILY
# 消息
messages:
receive:
- "&a你接受了任务&f新手成长之路①"
complete:
- "&a任务完成&f新手成长之路①"
- "&7奖励已发放请继续成长。"
conditions:
level: 0
completed_quests: []
worlds: []
# 任务列表
tasks:
# 任务id
id_1:
# 任务类型暂定: BREAK、PLACE、KILL
type: BREAK_BLOCK
# 任务名
display_name: 砍伐橡木*128
# 任务数量
amount: 128
# 触发目标列表
targets:
- SPRUCE_LOG
- SPRUCE_WOOD
- STRIPPED_SPRUCE_LOG
- STRIPPED_SPRUCE_WOOD
# 完成任务后触发的内容
rewards:
commands:
- "dlevel give %player% 12000"
- "points give %player% 5"
messages:
- "&a{name}任务已完成"

View File

@@ -0,0 +1,36 @@
Storage:
Type: yaml # yaml / sqlite / mysql
SQLite:
File: PlayerData.db
SaveInterval: 10
MySQL:
Host: localhost
Port: 3306
Database: quest
Username: root
Password: root
TablePrefix: AuQuest_
Pool:
MaximumPoolSize: 10
MinimumIdle: 2
ConnectionTimeout: 10000
IdleTimeout: 600000
MaxLifetime: 1800000
FoodType:
- COOKED_BEEF
- COOKED_CHICKEN
- COOKED_MUTTON
- COOKED_RABBIT
- BAKED_POTATO
OreType:
- IRON_INGOT
- GOLD_INGOT
- COPPER_INGOT
- NETHERITE_SCRAP
- DIAMOND
- EMERALD
- REDSTONE
- COAL
- QUARTZ
- LAPIS_LAZULI

View File

@@ -0,0 +1,4 @@
Message:
condition_not_level: "等级不足,需要将等级提高至 {level}"
condition_not_quest: "需要将 {quest} 任务完成后才能接受此任务."
condition_not_world: "需要在指定世界才能执行此任务."

View File

@@ -0,0 +1,14 @@
name: AuQuestEngine
main: com.io.yaohun.questengine.QEMain
version: 1.0.6
api-version: 1.21
author: yaohun
softdepend:
- Citizens
- MythicMobs
- PlaceholderAPI
- CustomFishing
- CustomCrops
commands:
aquest:
aquestadmin: