diff --git a/README.md b/README.md index 276740f..f746cd7 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ ```text # claim [奖励ID] -@ 玩家指令 +@ 玩家指令 (TimeReward.claim) - 为自己手动领取对应奖励。 - 若不填写奖励ID,则自动领取全部可领取的奖励。 @@ -72,8 +72,9 @@ 变量如下: ```text -# %TimeReward_time% -- 得到玩家总共的在线时长(秒)。 +# %TimeReward_time_<时间类型>% +- 得到玩家的在线时长(秒)。 +- 时间类型可选 TOTAL, DAILY, WEEKLY, MONTHLY 。 # %TimeReward_reward_<奖励ID>% - 得到某个奖励配置的名称。 diff --git a/pom.xml b/pom.xml index eb28232..9067d1c 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,8 @@ ${project.jdk.version} UTF-8 UTF-8 - - 1.5.5 + + 1.5.7 0.4.7 2.8.0 @@ -49,7 +49,7 @@ Carm's Repo https://repo.carm.cc/repository/maven-public/ - + github GitHub Packages @@ -66,7 +66,7 @@ https://mvn.lumine.io/repository/maven-public/ - + https://github.com/CarmJos/TimeReward/releases @@ -84,7 +84,7 @@ compile true - + cc.carm.lib easyplugin-main @@ -92,7 +92,15 @@ compile true - + + + cc.carm.lib + easyplugin-user + ${deps.easyplugin.version} + compile + true + + cc.carm.lib easyplugin-command @@ -117,7 +125,7 @@ compile true - + cc.carm.lib easysql-beecp @@ -125,7 +133,7 @@ compile true - + de.themoep minedown @@ -141,7 +149,7 @@ compile true - + org.spigotmc spigot-api @@ -174,7 +182,7 @@ clean package - + org.apache.maven.plugins @@ -199,7 +207,7 @@ - + org.apache.maven.plugins maven-compiler-plugin @@ -211,13 +219,13 @@ -parameters - + org.apache.maven.plugins maven-jar-plugin 3.2.0 - + org.apache.maven.plugins maven-source-plugin @@ -231,7 +239,7 @@ - + org.apache.maven.plugins maven-shade-plugin @@ -277,7 +285,7 @@ - + org.apache.maven.plugins maven-surefire-plugin @@ -287,7 +295,7 @@ - + src/main/resources diff --git a/src/main/java/cc/carm/plugin/timereward/Main.java b/src/main/java/cc/carm/plugin/timereward/Main.java index 5096709..a18e645 100644 --- a/src/main/java/cc/carm/plugin/timereward/Main.java +++ b/src/main/java/cc/carm/plugin/timereward/Main.java @@ -8,6 +8,7 @@ import cc.carm.lib.mineconfiguration.bukkit.MineConfiguration; import cc.carm.plugin.timereward.command.MainCommand; import cc.carm.plugin.timereward.conf.PluginConfig; import cc.carm.plugin.timereward.conf.PluginMessages; +import cc.carm.plugin.timereward.conf.RewardsConfig; import cc.carm.plugin.timereward.hooker.PAPIExpansion; import cc.carm.plugin.timereward.listener.UserListener; import cc.carm.plugin.timereward.manager.RewardManager; @@ -15,12 +16,14 @@ import cc.carm.plugin.timereward.manager.UserManager; import cc.carm.plugin.timereward.storage.database.MySQLStorage; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; public class Main extends EasyPlugin { private static Main instance; protected ConfigurationProvider configProvider; protected ConfigurationProvider messageProvider; + protected ConfigurationProvider rewardProvider; protected MySQLStorage storage; protected UserManager userManager; @@ -38,6 +41,9 @@ public class Main extends EasyPlugin { this.messageProvider = MineConfiguration.from(this, "messages.yml"); this.messageProvider.initialize(PluginMessages.class); + + this.rewardProvider = MineConfiguration.from(this, "rewards.yml"); + this.rewardProvider.initialize(RewardsConfig.class); } @Override @@ -53,10 +59,10 @@ public class Main extends EasyPlugin { } log("加载用户管理器..."); - this.userManager = new UserManager(); - if (Bukkit.getOnlinePlayers().size() > 0) { + this.userManager = new UserManager(this); + if (!Bukkit.getOnlinePlayers().isEmpty()) { log("加载现有用户数据..."); - this.userManager.loadAll(); + this.userManager.loadOnline(Entity::getUniqueId); } log("加载奖励管理器..."); @@ -139,4 +145,7 @@ public class Main extends EasyPlugin { return messageProvider; } + public ConfigurationProvider getRewardProvider() { + return rewardProvider; + } } \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/timereward/TimeRewardAPI.java b/src/main/java/cc/carm/plugin/timereward/TimeRewardAPI.java index 2668fe5..91869d3 100644 --- a/src/main/java/cc/carm/plugin/timereward/TimeRewardAPI.java +++ b/src/main/java/cc/carm/plugin/timereward/TimeRewardAPI.java @@ -11,6 +11,9 @@ import java.util.List; public class TimeRewardAPI { + private TimeRewardAPI() { + } + public static UserManager getUserManager() { return Main.getInstance().userManager; } @@ -18,7 +21,7 @@ public class TimeRewardAPI { public static RewardManager getRewardManager() { return Main.getInstance().rewardManager; } - + public static void executeCommands(Player player, List commands) { if (commands == null || commands.isEmpty()) return; for (String command : commands) { diff --git a/src/main/java/cc/carm/plugin/timereward/command/sub/ClaimCommand.java b/src/main/java/cc/carm/plugin/timereward/command/sub/ClaimCommand.java index 9e7fe39..fab112c 100644 --- a/src/main/java/cc/carm/plugin/timereward/command/sub/ClaimCommand.java +++ b/src/main/java/cc/carm/plugin/timereward/command/sub/ClaimCommand.java @@ -1,12 +1,11 @@ package cc.carm.plugin.timereward.command.sub; import cc.carm.lib.easyplugin.command.SubCommand; -import cc.carm.plugin.timereward.Main; import cc.carm.plugin.timereward.TimeRewardAPI; import cc.carm.plugin.timereward.command.MainCommand; import cc.carm.plugin.timereward.conf.PluginMessages; -import cc.carm.plugin.timereward.manager.RewardManager; import cc.carm.plugin.timereward.data.RewardContents; +import cc.carm.plugin.timereward.manager.RewardManager; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; @@ -40,11 +39,8 @@ public class ClaimCommand extends SubCommand { return null; } - Main.getInstance().getScheduler().run(() -> unclaimedRewards.forEach( - // 在同步进程中为玩家发放奖励 - unclaimedReward -> manager.claimReward(player, unclaimedReward, false) - )); - + // 为玩家发放奖励 + manager.claimRewards(player, unclaimedRewards, false); } else { RewardContents reward = manager.getReward(rewardID); @@ -57,8 +53,16 @@ public class ClaimCommand extends SubCommand { PluginMessages.NOT_CLAIMABLE.send(sender, reward.getDisplayName()); return null; } - Main.getInstance().getScheduler().run(() -> manager.claimReward(player, reward, false)); + + // 为玩家发放奖励 + manager.claimReward(player, reward, false); } return null; } + + @Override + public boolean hasPermission(@NotNull CommandSender sender) { + return sender.hasPermission("TimeReward.claim"); + } + } diff --git a/src/main/java/cc/carm/plugin/timereward/command/sub/ListCommand.java b/src/main/java/cc/carm/plugin/timereward/command/sub/ListCommand.java index fe7bcc8..3efeb11 100644 --- a/src/main/java/cc/carm/plugin/timereward/command/sub/ListCommand.java +++ b/src/main/java/cc/carm/plugin/timereward/command/sub/ListCommand.java @@ -25,12 +25,12 @@ public class ListCommand extends SubCommand { for (RewardContents reward : awards) { if (reward.getPermission() != null) { PluginMessages.LIST.OBJECT_PERM.send(sender, - reward.getRewardID(), reward.getDisplayName(), + reward.getRewardID(), reward.getDisplayName(), reward.getType().name(), reward.getTime(), reward.getPermission() ); } else { PluginMessages.LIST.OBJECT.send(sender, - reward.getRewardID(), reward.getDisplayName(), reward.getTime() + reward.getRewardID(), reward.getDisplayName(), reward.getType().name(), reward.getTime() ); } } diff --git a/src/main/java/cc/carm/plugin/timereward/command/sub/ReloadCommand.java b/src/main/java/cc/carm/plugin/timereward/command/sub/ReloadCommand.java index bd0ed0c..cf0d088 100644 --- a/src/main/java/cc/carm/plugin/timereward/command/sub/ReloadCommand.java +++ b/src/main/java/cc/carm/plugin/timereward/command/sub/ReloadCommand.java @@ -24,6 +24,7 @@ public class ReloadCommand extends SubCommand { try { Main.getInstance().getConfigProvider().reload(); Main.getInstance().getMessageProvider().reload(); + Main.getInstance().getRewardProvider().reload(); PluginMessages.RELOAD.COMPLETE.send(sender, System.currentTimeMillis() - s1, TimeRewardAPI.getRewardManager().listRewards().size()); } catch (Exception e) { diff --git a/src/main/java/cc/carm/plugin/timereward/command/sub/UserCommand.java b/src/main/java/cc/carm/plugin/timereward/command/sub/UserCommand.java index 5a7b69c..6b73db5 100644 --- a/src/main/java/cc/carm/plugin/timereward/command/sub/UserCommand.java +++ b/src/main/java/cc/carm/plugin/timereward/command/sub/UserCommand.java @@ -5,13 +5,15 @@ import cc.carm.lib.easyplugin.command.SubCommand; import cc.carm.plugin.timereward.TimeRewardAPI; import cc.carm.plugin.timereward.command.MainCommand; import cc.carm.plugin.timereward.conf.PluginMessages; -import cc.carm.plugin.timereward.data.UserData; +import cc.carm.plugin.timereward.data.IntervalType; +import cc.carm.plugin.timereward.user.UserRewardData; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import java.time.format.DateTimeFormatter; import java.util.List; public class UserCommand extends SubCommand { @@ -30,11 +32,21 @@ public class UserCommand extends SubCommand { return null; } - UserData user = TimeRewardAPI.getUserManager().getData(player); + UserRewardData user = TimeRewardAPI.getUserManager().get(player); PluginMessages.USER_INFO.send(sender, - player.getName(), user.getAllSeconds(), - user.getClaimedRewards().size(), String.join("&8, &f", user.getClaimedRewards()) + player.getName(), + user.getOnlineDuration(IntervalType.DAILY).getSeconds(), + user.getOnlineDuration(IntervalType.WEEKLY).getSeconds(), + user.getOnlineDuration(IntervalType.MONTHLY).getSeconds(), + user.getOnlineDuration(IntervalType.TOTAL).getSeconds(), + user.getClaimedRewards().size() ); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + user.getClaimedRewards().forEach((id, time) -> { + PluginMessages.USER_RECEIVED.send(sender, id, time.format(formatter)); + }); + return null; } diff --git a/src/main/java/cc/carm/plugin/timereward/conf/PluginConfig.java b/src/main/java/cc/carm/plugin/timereward/conf/PluginConfig.java index be3cfcd..dbc5045 100644 --- a/src/main/java/cc/carm/plugin/timereward/conf/PluginConfig.java +++ b/src/main/java/cc/carm/plugin/timereward/conf/PluginConfig.java @@ -32,27 +32,6 @@ public class PluginConfig extends ConfigurationRoot { .serializeValue(DayOfWeek::getValue) .defaults(DayOfWeek.MONDAY).build(); - @HeaderComment({"奖励相关设定,包含以下设定:", - " [id] 配置键名即奖励ID,支持英文、数字与下划线。", - " | 确定后请不要更改,因为该键值用于存储玩家是否领取的数据", - " | 如果更改,原先领取过该奖励的玩家将会自动再领取一次!", - " [name] 奖励的显示名称,可以是任意字符串", - " | 可以在 commands 中使用 %(name) 来获取该奖励的名称", - " | 也可以使用变量 %TimeReward_reward_<奖励ID>% 来获取对应奖励的名称", - " [permission] 领取奖励时后台执行的指令", - " | 支持PlaceholderAPI变量,指令中可以使用 %(name) 来获取该奖励的名称。", - " [commands] 该奖励领取权限,可以不设置。", - " | 若为空则所有人都可以领取;若不为空,则需要拥有该权限的玩家才能领取。", - " [auto] 该奖励是否自动领取,可以不设置,默认为true。", - " | 若关闭自动领取,则需要玩家手动输入/tr claim 领取奖励。", - }) - public static final ConfigValue REWARDS = ConfigValue.builder() - .asValue(RewardsConfig.RewardGroup.class).fromSection() - .parseValue((v, d) -> RewardsConfig.RewardGroup.parse(v)) - .serializeValue(RewardsConfig.RewardGroup::serialize) - .defaults(RewardsConfig.RewardGroup.defaults()) - .build(); - } diff --git a/src/main/java/cc/carm/plugin/timereward/conf/PluginMessages.java b/src/main/java/cc/carm/plugin/timereward/conf/PluginMessages.java index 44b5e1b..750e4c3 100644 --- a/src/main/java/cc/carm/plugin/timereward/conf/PluginMessages.java +++ b/src/main/java/cc/carm/plugin/timereward/conf/PluginMessages.java @@ -91,15 +91,20 @@ public class PluginMessages extends ConfigurationRoot { ).build(); public static final ConfiguredMessageList USER_INFO = list().defaults( - "&f玩家 &6%(player) &f已在线&e%(time)&f秒,共领取了 &e%(amount)&f 次奖励。", - "&7已领取的奖励列表如下:&r%(rewards) &7。" - ).params("player", "time", "amount", "rewards").build(); + "&f玩家 &6%(player) &f的在线时间数据:", + "&f 本日在线 &e%(daily)&f秒,本周在线 &e%(weekly)&f秒,", + "&f 本月在线 &e%(monthly)&f秒,累积在线 &e%(total)&f秒。", + "&7已领取了 &f%(amount) &7种奖励,如下所示:" + ).params("player", "daily", "weekly", "monthly", "total", "amount").build(); + + public static final ConfiguredMessageList USER_RECEIVED = list().defaults( + "&7- &e%(reward) &7于 &f%(time) &7领取" + ).params("reward", "time").build(); public static final ConfiguredMessageList COMMAND_LIST = list().defaults( "&f正在执行奖励 %(award) 的指令列表..." ).params("award").build(); - public static class LIST extends ConfigurationRoot { public static final ConfiguredMessageList HEADER = list().defaults( @@ -109,15 +114,17 @@ public class PluginMessages extends ConfigurationRoot { public static final ConfiguredMessageList OBJECT = list().defaults( "&8# &f%(id)", "&8- &7奖励名称 &f%(name)", + "&8- &7奖励类型 &f%(type)", "&8- &7领取时间 &f&e%(time)&f秒" - ).params("id", "name", "time").build(); + ).params("id", "name", "type", "time").build(); public static final ConfiguredMessageList OBJECT_PERM = list().defaults( "&8# &f%(id)", "&8- &7奖励名称 &f%(name)", + "&8- &7奖励类型 &f%(type)", "&8- &7领取时间 &f&e%(time)&f秒", "&8- &7需要权限 &f%(permission)" - ).params("id", "name", "time", "permission").build(); + ).params("id", "name", "type", "time", "permission").build(); } diff --git a/src/main/java/cc/carm/plugin/timereward/conf/RewardsConfig.java b/src/main/java/cc/carm/plugin/timereward/conf/RewardsConfig.java index 3b6ced7..69e1fdd 100644 --- a/src/main/java/cc/carm/plugin/timereward/conf/RewardsConfig.java +++ b/src/main/java/cc/carm/plugin/timereward/conf/RewardsConfig.java @@ -14,6 +14,9 @@ import java.util.Map; " [id] 配置键名即奖励ID,支持英文、数字与下划线。", " | 确定后请不要更改,因为该键值用于存储玩家是否领取的数据", " | 如果更改,原先领取过该奖励的玩家将会自动再领取一次!", + " [type] 奖励的类型序号", + " | “0”代表总计时间奖励,“1”代表每日在线奖励,", + " | “2”代表每周在线奖励,“3”代表每月在线奖励。", " [name] 奖励的显示名称,可以是任意字符串", " | 可以在 commands 中使用 %(name) 来获取该奖励的名称", " | 也可以使用变量 %TimeReward_reward_<奖励ID>% 来获取对应奖励的名称", @@ -26,17 +29,11 @@ import java.util.Map; }) public class RewardsConfig extends ConfigurationRoot { - @HeaderComment("每日在线时长的奖励") - public static final ConfigValue DAILY = create(); + public static final ConfigValue REWARDS = create(); - @HeaderComment("每周在线时长的奖励") - public static final ConfigValue WEEKLY = create(); - - @HeaderComment("每月在线时长的奖励") - public static final ConfigValue MONTHLY = create(); - - @HeaderComment("总在线时长的奖励") - public static final ConfigValue TOTAL = create(); + public static Map getContents() { + return REWARDS.getNotNull().getContents(); + } private static ConfigValue create() { return ConfigValue.builder() diff --git a/src/main/java/cc/carm/plugin/timereward/data/IntervalType.java b/src/main/java/cc/carm/plugin/timereward/data/IntervalType.java new file mode 100644 index 0000000..bb80d08 --- /dev/null +++ b/src/main/java/cc/carm/plugin/timereward/data/IntervalType.java @@ -0,0 +1,101 @@ +package cc.carm.plugin.timereward.data; + +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +public enum IntervalType { + + TOTAL( + 0, time -> false, + (timeRecord, join) -> Duration.between(join, LocalDateTime.now()).plus(timeRecord.getTotalTime()) + ), + + DAILY( + 1, time -> time.isBefore(TimeRecord.getTodayStart()), + (timeRecord, join) -> { + LocalDateTime now = LocalDateTime.now(); + if (now.toLocalDate().isEqual(timeRecord.getDate())) { + // 和记录还在同一天 + return Duration.between(join, now).plus(timeRecord.getDailyTime()); + } else if (now.toLocalDate().isEqual(join.toLocalDate())) { + // 加入的时间和现在的时间在同一天 + return Duration.between(join, now); + } else { + // 加入的时间和现在的时间不在同一天 + return Duration.between(TimeRecord.getTodayStart(), now); + } + } + ), + WEEKLY( + 2, time -> time.isBefore(TimeRecord.getThisWeekStart()), + (timeRecord, join) -> { + LocalDateTime now = LocalDateTime.now(); + if (TimeRecord.isSameWeek(timeRecord.getDate(), now)) { + return Duration.between(join, now).plus(timeRecord.getWeeklyTime()); + } else if (TimeRecord.isSameWeek(join, now)) { + return Duration.between(join, now); + } else { + return Duration.between(TimeRecord.getThisWeekStart(), now); + } + } + ), + + MONTHLY( + 3, time -> time.isBefore(TimeRecord.getThisMonthStart()), + (timeRecord, join) -> { + LocalDateTime now = LocalDateTime.now(); + if (TimeRecord.isSameMonth(timeRecord.getDate(), now.toLocalDate())) { + return Duration.between(join, now).plus(timeRecord.getMonthlyTime()); + } else if (TimeRecord.isSameMonth(join.toLocalDate(), now.toLocalDate())) { + return Duration.between(join, now); + } else { + return Duration.between(TimeRecord.getThisMonthStart(), now); + } + } + ); + + private final int id; + private final @NotNull Predicate reclaimPreficate; + private final @NotNull BiFunction calculator; + + IntervalType(int id, + @NotNull Predicate reclaimablePredicate, + @NotNull BiFunction calculator) { + this.id = id; + this.reclaimPreficate = reclaimablePredicate; + this.calculator = calculator; + } + + public int getID() { + return id; + } + + public @NotNull BiFunction getCalculator() { + return calculator; + } + + public @NotNull Predicate getReclaimablePredicate() { + return reclaimPreficate; + } + + public Duration calculate(@NotNull TimeRecord timeRecord, @NotNull LocalDateTime joinTime) { + return calculator.apply(timeRecord, joinTime); + } + + public boolean isReclaimable(@NotNull LocalDateTime claimedDate) { + return reclaimPreficate.test(claimedDate); + } + + public static IntervalType parse(String input) { + if (input == null) return null; + else return Arrays.stream(values()) + .filter(t -> t.name().equalsIgnoreCase(input) || Integer.toString(t.id).equals(input)) + .findFirst().orElse(null); + } + +} diff --git a/src/main/java/cc/carm/plugin/timereward/data/RewardContents.java b/src/main/java/cc/carm/plugin/timereward/data/RewardContents.java index 86276ea..20b5179 100644 --- a/src/main/java/cc/carm/plugin/timereward/data/RewardContents.java +++ b/src/main/java/cc/carm/plugin/timereward/data/RewardContents.java @@ -13,18 +13,20 @@ public class RewardContents { public final @NotNull String id; + public final @NotNull IntervalType type; private final long time; + private final @Nullable String name; private final @Nullable String permission; private final @NotNull List commands; private final boolean auto; - - public RewardContents(@NotNull String id, long time, + public RewardContents(@NotNull String id, @NotNull IntervalType type, long time, @Nullable String name, @Nullable String permission, @NotNull List commands, boolean auto) { this.id = id; + this.type = type; this.time = time; this.name = name; this.permission = permission; @@ -32,10 +34,14 @@ public class RewardContents { this.auto = auto; } - public String getRewardID() { + public @NotNull String getRewardID() { return id; } + public @NotNull IntervalType getType() { + return type; + } + public long getTime() { return time; } @@ -64,13 +70,11 @@ public class RewardContents { return permission == null || player.hasPermission(permission); } - public boolean isTimeEnough(long requireSeconds) { - return requireSeconds >= getTime(); - } public Map serialize() { Map map = new LinkedHashMap<>(); map.put("time", getTime()); + map.put("type", getType().getID()); if (getName() != null) map.put("name", getName()); if (getPermission() != null) map.put("permission", getPermission()); map.put("commands", getCommands()); @@ -85,8 +89,14 @@ public class RewardContents { return null; } + IntervalType intervalType = IntervalType.parse(Objects.toString(section.get("type"))); + if (intervalType == null) { + Main.severe("奖励 " + id + " 的类型配置错误,请检查配置文件。"); + return null; + } + return new RewardContents( - id, time, + id, intervalType, time, section.getString("name"), section.getString("permission"), section.getStringList("commands"), @@ -96,7 +106,7 @@ public class RewardContents { public static RewardContents defaults(String id) { return new RewardContents( - id, 7200, + id, IntervalType.TOTAL, 7200, "&f[初级奖励] &e总在线时长 2小时", "TimeReward.vip", Collections.singletonList("say &f恭喜 &b%player_name% &f领取了奖励 &r%(name) &f!"), true @@ -117,5 +127,4 @@ public class RewardContents { } - } diff --git a/src/main/java/cc/carm/plugin/timereward/data/TimeRecord.java b/src/main/java/cc/carm/plugin/timereward/data/TimeRecord.java index e9293ef..30755b3 100644 --- a/src/main/java/cc/carm/plugin/timereward/data/TimeRecord.java +++ b/src/main/java/cc/carm/plugin/timereward/data/TimeRecord.java @@ -3,21 +3,38 @@ package cc.carm.plugin.timereward.data; import cc.carm.plugin.timereward.conf.PluginConfig; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.Temporal; import java.time.temporal.TemporalField; import java.time.temporal.WeekFields; public class TimeRecord { + public static TimeRecord empty() { + return new TimeRecord(LocalDate.now(), 0, 0, 0, 0); + } + + protected final @NotNull LocalDate date; - protected final int daily; - protected final int weekly; - protected final int monthly; + protected final @NotNull Duration daily; + protected final @NotNull Duration weekly; + protected final @NotNull Duration monthly; - protected final int total; + protected final @NotNull Duration total; - public TimeRecord(@NotNull LocalDate date, int daily, int weekly, int monthly, int total) { + public TimeRecord(@NotNull LocalDate date, long daily, long weekly, long monthly, long total) { + this(date, + Duration.ofSeconds(daily), Duration.ofSeconds(weekly), + Duration.ofSeconds(monthly), Duration.ofSeconds(total) + ); + } + + public TimeRecord(@NotNull LocalDate date, + @NotNull Duration daily, @NotNull Duration weekly, + @NotNull Duration monthly, @NotNull Duration total) { this.date = date; this.daily = daily; this.weekly = weekly; @@ -29,16 +46,20 @@ public class TimeRecord { return date; } - public int getDailyTime() { - return isDayUpdated() ? 0 : daily; + public @NotNull Duration getDailyTime() { + return daily; } - public int getWeeklyTime() { - return isWeekUpdated() ? 0 : weekly; + public @NotNull Duration getWeeklyTime() { + return weekly; } - public int getMonthlyTime() { - return isMonthUpdated() ? 0 : monthly; + public @NotNull Duration getMonthlyTime() { + return monthly; + } + + public @NotNull Duration getTotalTime() { + return total; } public boolean isDayUpdated() { @@ -47,9 +68,7 @@ public class TimeRecord { public boolean isWeekUpdated() { if (!isDayUpdated()) return false; // Same day always same week - - TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear(); - return getDate().get(woy) != LocalDate.now().get(woy); + return !isSameWeek(LocalDate.now(), getDate()); } public boolean isMonthUpdated() { @@ -59,5 +78,36 @@ public class TimeRecord { return LocalDate.now().getMonth().compareTo(getDate().getMonth()) > 0; } + public static boolean isSameWeek(Temporal a, Temporal b) { + TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear(); + return a.get(woy) == b.get(woy); + } + public static boolean isSameMonth(LocalDate a, LocalDate b) { + return a.getMonth().equals(b.getMonth()); + } + + public static LocalDateTime getTodayStart() { + return LocalDate.now().atTime(0, 0); + } + + public static LocalDateTime getThisWeekStart() { + TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear(); + return LocalDate.now().with(woy, 1).atTime(0, 0); + } + + public static LocalDateTime getThisMonthStart() { + return LocalDate.now().withDayOfMonth(1).atTime(0, 0); + } + + @Override + public String toString() { + return "TimeRecord{" + + "date=" + date + + ", daily=" + daily + + ", weekly=" + weekly + + ", monthly=" + monthly + + ", total=" + total + + '}'; + } } diff --git a/src/main/java/cc/carm/plugin/timereward/data/UserData.java b/src/main/java/cc/carm/plugin/timereward/data/UserData.java deleted file mode 100644 index fce996e..0000000 --- a/src/main/java/cc/carm/plugin/timereward/data/UserData.java +++ /dev/null @@ -1,86 +0,0 @@ -package cc.carm.plugin.timereward.data; - -import org.jetbrains.annotations.NotNull; - -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * 用户奖励数据,用于存储用户的奖励的领取情况。 - */ -public class UserData { - - protected final @NotNull UUID userUUID; - private final Set<@NotNull String> claimedRewards; // 记录已领取的奖励ID - private final long storedSeconds; // 记录已经游玩的时间 - private final long joinMillis; // 记录本次加入的时间 - - public UserData(@NotNull UUID userUUID) { - this(userUUID, 0, new LinkedHashSet<>()); - } - - public UserData(@NotNull UUID userUUID, long storedSeconds, - Set<@NotNull String> claimedRewards) { - this(userUUID, storedSeconds, claimedRewards, System.currentTimeMillis()); - } - - public UserData(@NotNull UUID userUUID, long storedSeconds, - Set<@NotNull String> claimedRewards, long joinMillis) { - this.userUUID = userUUID; - this.storedSeconds = storedSeconds; - this.claimedRewards = claimedRewards; - this.joinMillis = joinMillis; - } - - public @NotNull UUID getUserUUID() { - return userUUID; - } - - public long getStoredSeconds() { - return storedSeconds; - } - - /** - * 得到本次加入游戏的时间 - * - * @return 本次加入游戏时间 - */ - public long getJoinMillis() { - return joinMillis; - } - - /** - * 获取玩家本次加入服务器的时间 ,即为 当前时间-加入时间。 - * - * @return 玩家本次加入服务器的时间 - */ - public long getCurrentSeconds() { - return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - getJoinMillis()); - } - - /** - * 获取 玩家在数据库中已经游玩的时间 + (当前时间-加入时间) - * - * @return 之前游玩的世界与本次游玩时间之和 - */ - public long getAllSeconds() { - return getStoredSeconds() + getCurrentSeconds(); - } - - public Set getClaimedRewards() { - return claimedRewards; - } - - public boolean isClaimed(@NotNull String rewardId) { - return this.claimedRewards.contains(rewardId); - } - - public boolean addClaimedReward(@NotNull String rewardId) { - if (isClaimed(rewardId)) return false; // 已经领取过了 - this.claimedRewards.add(rewardId); - return true; - } - -} diff --git a/src/main/java/cc/carm/plugin/timereward/hooker/PAPIExpansion.java b/src/main/java/cc/carm/plugin/timereward/hooker/PAPIExpansion.java index ff4537e..280f117 100644 --- a/src/main/java/cc/carm/plugin/timereward/hooker/PAPIExpansion.java +++ b/src/main/java/cc/carm/plugin/timereward/hooker/PAPIExpansion.java @@ -3,8 +3,9 @@ package cc.carm.plugin.timereward.hooker; import cc.carm.lib.easyplugin.papi.EasyPlaceholder; import cc.carm.lib.easyplugin.papi.handler.PlaceholderHandler; import cc.carm.plugin.timereward.TimeRewardAPI; +import cc.carm.plugin.timereward.data.IntervalType; import cc.carm.plugin.timereward.data.RewardContents; -import cc.carm.plugin.timereward.data.UserData; +import cc.carm.plugin.timereward.user.UserRewardData; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -18,7 +19,14 @@ public class PAPIExpansion extends EasyPlaceholder { public PAPIExpansion(@NotNull JavaPlugin plugin, @NotNull String rootIdentifier) { super(plugin, rootIdentifier); - handle("time", userHandler(UserData::getAllSeconds)); + handle("time", userHandler((user, args) -> { + if (args.length < 1) return "请填写时间类型"; + IntervalType type = IntervalType.parse(args[0]); + + if (type == null) return "时间类型不存在"; + + return user.getOnlineDuration(type).getSeconds(); + }), Collections.singletonList("<时间类型>")); handle("reward", rewardHandler(RewardContents::getDisplayName), @@ -27,7 +35,11 @@ public class PAPIExpansion extends EasyPlaceholder { handle("claimed", userHandler((user, args) -> { if (args.length < 1) return "请填写奖励ID"; - else return user.isClaimed(args[0]); + + RewardContents reward = TimeRewardAPI.getRewardManager().getReward(args[0]); + if (reward == null) return "奖励不存在"; + + return user.isClaimed(reward); }), Collections.singletonList("<奖励ID>")); handle("claimable", (offlinePlayer, args) -> { @@ -43,14 +55,14 @@ public class PAPIExpansion extends EasyPlaceholder { handle("version", (player, args) -> getVersion()); } - protected PlaceholderHandler userHandler(Function userFunction) { + protected PlaceholderHandler userHandler(Function userFunction) { return userHandler((user, args) -> userFunction.apply(user)); } - protected PlaceholderHandler userHandler(BiFunction userFunction) { + protected PlaceholderHandler userHandler(BiFunction userFunction) { return (player, args) -> { if (player == null || !player.isOnline()) return "加载中..."; - return userFunction.apply(TimeRewardAPI.getUserManager().getData((Player) player), args); + return userFunction.apply(TimeRewardAPI.getUserManager().get((Player) player), args); }; } diff --git a/src/main/java/cc/carm/plugin/timereward/listener/UserListener.java b/src/main/java/cc/carm/plugin/timereward/listener/UserListener.java index 3330e2e..980f866 100644 --- a/src/main/java/cc/carm/plugin/timereward/listener/UserListener.java +++ b/src/main/java/cc/carm/plugin/timereward/listener/UserListener.java @@ -1,7 +1,8 @@ package cc.carm.plugin.timereward.listener; -import cc.carm.plugin.timereward.Main; import cc.carm.plugin.timereward.TimeRewardAPI; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; @@ -11,12 +12,17 @@ public class UserListener implements Listener { @EventHandler public void onJoin(PlayerJoinEvent event) { - Main.getInstance().getScheduler().runAsync(() -> TimeRewardAPI.getUserManager().loadData(event.getPlayer().getUniqueId())); + TimeRewardAPI.getUserManager().load(event.getPlayer().getUniqueId(), true).thenAccept(data -> { + Player player = Bukkit.getPlayer(data.getUserUUID()); + if (player == null || !player.isOnline()) { + TimeRewardAPI.getUserManager().unload(data.getUserUUID()); + } // 不在线自动卸载 + }); } @EventHandler public void onQuit(PlayerQuitEvent event) { - Main.getInstance().getScheduler().runAsync(() -> TimeRewardAPI.getUserManager().unloadData(event.getPlayer().getUniqueId())); + TimeRewardAPI.getUserManager().unload(event.getPlayer().getUniqueId()); } } diff --git a/src/main/java/cc/carm/plugin/timereward/manager/RewardManager.java b/src/main/java/cc/carm/plugin/timereward/manager/RewardManager.java index 6aed813..38db6a1 100644 --- a/src/main/java/cc/carm/plugin/timereward/manager/RewardManager.java +++ b/src/main/java/cc/carm/plugin/timereward/manager/RewardManager.java @@ -3,9 +3,9 @@ package cc.carm.plugin.timereward.manager; import cc.carm.lib.easyplugin.utils.MessageUtils; import cc.carm.plugin.timereward.Main; import cc.carm.plugin.timereward.TimeRewardAPI; -import cc.carm.plugin.timereward.conf.PluginConfig; +import cc.carm.plugin.timereward.conf.RewardsConfig; import cc.carm.plugin.timereward.data.RewardContents; -import cc.carm.plugin.timereward.data.UserData; +import cc.carm.plugin.timereward.user.UserRewardData; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -13,9 +13,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; public class RewardManager { @@ -34,10 +34,8 @@ public class RewardManager { .collect(Collectors.toList()); if (rewards.isEmpty()) continue; - main.getScheduler().run(() -> rewards.forEach(r -> { - // 在同步进程中为玩家发放奖励 - claimReward(player, r, true); // 二次检查避免重复发奖 - })); + //为玩家发放奖励 + claimRewards(player, rewards, true); // 二次检查避免重复发奖 } } }; @@ -50,7 +48,7 @@ public class RewardManager { } protected Map getRewards() { - return PluginConfig.REWARDS.getNotNull().getContents(); + return RewardsConfig.getContents(); } public @Nullable RewardContents getReward(String rewardID) { @@ -58,7 +56,7 @@ public class RewardManager { } public Map listRewards() { - return Collections.unmodifiableMap(PluginConfig.REWARDS.getNotNull().getContents()); + return Collections.unmodifiableMap(RewardsConfig.getContents()); } @Unmodifiable @@ -69,18 +67,48 @@ public class RewardManager { } public boolean isClaimable(Player player, RewardContents reward) { - UserData user = TimeRewardAPI.getUserManager().getData(player); - return !user.isClaimed(reward.getRewardID()) // 未曾领取 - && reward.isTimeEnough(user.getAllSeconds()) // 时间足够 + UserRewardData user = TimeRewardAPI.getUserManager().get(player); + + return !user.isClaimed(reward) // 未曾领取 + && user.isTimeEnough(reward)// 时间足够 && reward.checkPermission(player); // 满足权限 } - public boolean claimReward(Player player, RewardContents reward, boolean check) { - if (check && !isClaimable(player, reward)) return false; + public CompletableFuture claimReward(Player player, RewardContents reward, boolean check) { + return claimRewards(player, Collections.singletonList(reward), check); + } - TimeRewardAPI.getUserManager().getData(player).addClaimedReward(reward.getRewardID()); - executeCommand(player, reward); - return true; + public CompletableFuture claimRewards(Player player, Collection rewards, boolean check) { + Set contents = rewards.stream() + .filter(r -> !check || isClaimable(player, r)) + .collect(Collectors.toSet()); + + Map map = new LinkedHashMap<>(); + contents.forEach(reward -> map.put(reward.getRewardID(), LocalDateTime.now())); + + return Main.getInstance().supplyAsync(() -> { + try { + + UserRewardData user = TimeRewardAPI.getUserManager().get(player); + Main.getStorage().addClaimedData(player.getUniqueId(), map); + contents.forEach(user::addClaimedReward); + + return true; + } catch (Exception ex) { + Main.severe("为玩家 " + player.getName() + " 领取奖励时发生错误。"); + ex.printStackTrace(); + return false; + } + }).thenCompose(result -> { + if (result) { + return Main.getInstance().supplySync(() -> { + rewards.forEach(reward -> executeCommand(player, reward)); + return true; + }); + } else { + return CompletableFuture.completedFuture(false); + } + }); } public void executeCommand(Player player, RewardContents reward) { @@ -91,8 +119,4 @@ public class RewardManager { TimeRewardAPI.executeCommands(player, finalCommands); // 执行命令 } - public boolean claimReward(Player player, RewardContents reward) { - return claimReward(player, reward, true); - } - } \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/timereward/manager/UserManager.java b/src/main/java/cc/carm/plugin/timereward/manager/UserManager.java index 9dea736..ba445ce 100644 --- a/src/main/java/cc/carm/plugin/timereward/manager/UserManager.java +++ b/src/main/java/cc/carm/plugin/timereward/manager/UserManager.java @@ -1,132 +1,62 @@ package cc.carm.plugin.timereward.manager; +import cc.carm.lib.easyplugin.EasyPlugin; +import cc.carm.lib.easyplugin.user.UserDataManager; import cc.carm.plugin.timereward.Main; -import cc.carm.plugin.timereward.util.DataTaskRunner; -import cc.carm.plugin.timereward.data.UserData; +import cc.carm.plugin.timereward.data.IntervalType; +import cc.carm.plugin.timereward.data.TimeRecord; import cc.carm.plugin.timereward.storage.database.MySQLStorage; -import com.google.common.collect.ImmutableMap; -import org.bukkit.Bukkit; +import cc.carm.plugin.timereward.user.LockedRewardData; +import cc.carm.plugin.timereward.user.UserRewardData; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; -import java.util.HashMap; -import java.util.Map; +import java.time.Duration; +import java.time.LocalDate; +import java.util.LinkedHashMap; import java.util.UUID; -public class UserManager { +public class UserManager extends UserDataManager { - private final HashMap userDataMap; - - public UserManager() { - this.userDataMap = new HashMap<>(); + public UserManager(@NotNull EasyPlugin plugin) { + super(plugin); } - @NotNull - public UserData readData(UUID userUUID) { - try { - long start = System.currentTimeMillis(); - MySQLStorage storage = Main.getStorage(); - - UserData data = storage.loadData(userUUID); - - if (data == null) { - Main.debugging("当前还不存在玩家 " + userUUID + " 的数据,视作新档。"); - return new UserData(userUUID); - } - - Main.debugging("读取 " + userUUID + " 的用户数据完成, 耗时 " + (System.currentTimeMillis() - start) + "ms。"); - - return data; - } catch (Exception e) { - Main.severe("无法正常读取玩家数据,玩家操作将不会被保存,请检查数据配置!"); - Main.severe("Could not loadData user's data, please check the data conf!"); - e.printStackTrace(); - return new UserData(userUUID); - } + public @NotNull UserRewardData get(Player player) { + return get(player.getUniqueId()); } - public void saveData(UserData data) { - try { - long start = System.currentTimeMillis(); - MySQLStorage storage = Main.getStorage(); - - Main.debugging("正在保存 " + data.getUserUUID() + " 的用户数据..."); - storage.saveClaimedData(data.getUserUUID(), data.getClaimedRewards()); - storage.savePlayTime(data.getUserUUID(), data.getAllSeconds()); - Main.debugging("保存 " + data.getUserUUID() + " 的用户数据完成,耗时 " + (System.currentTimeMillis() - start) + "ms。"); - - } catch (Exception e) { - Main.severe("无法正常保存玩家数据,请检查数据配置!"); - Main.severe("Could not saveData user's data, please check the data conf!"); - e.printStackTrace(); - } + @Override + protected @Nullable UserRewardData loadData(@NotNull UUID key) throws Exception { + return Main.getStorage().loadData(key); } - public void loadData(UUID userUUID) { - getUserDataMap().put(userUUID, readData(userUUID)); + @Override + public void saveData(@NotNull UserRewardData data) throws Exception { + if (data instanceof LockedRewardData) return; // 不保存 + + MySQLStorage storage = Main.getStorage(); + + // 只需要保存游玩时间数据,领取数据已经实时保存了 + LocalDate date = LocalDate.now(); + Duration daily = data.getOnlineDuration(IntervalType.DAILY); + Duration weekly = data.getOnlineDuration(IntervalType.WEEKLY); + Duration monthly = data.getOnlineDuration(IntervalType.MONTHLY); + Duration total = data.getOnlineDuration(IntervalType.TOTAL); + TimeRecord newRecord = new TimeRecord(date, daily, weekly, monthly, total); + + storage.savePlayTime(data.getUserUUID(), newRecord); } - public void unloadData(UUID userUUID) { - unloadData(userUUID, true); + @Override + public @NotNull UserRewardData emptyUser(@NotNull UUID key) { + return new UserRewardData(key, TimeRecord.empty(), new LinkedHashMap<>()); } - public void unloadData(UUID userUUID, boolean save) { - UserData data = getData(userUUID); - if (data == null) return; - if (save) saveData(data); - getUserDataMap().remove(userUUID); - } - - public void loadAll() { - for (Player player : Bukkit.getOnlinePlayers()) { - if (getUserDataMap().containsKey(player.getUniqueId())) continue; - loadData(player.getUniqueId()); - } - } - - public void saveAll() { - getUserDataMap().values().forEach(this::saveData); - } - - public void unloadAll(boolean save) { - if (save) saveAll(); - getUserDataMap().clear(); - } - - @Nullable - public UserData getData(UUID userUUID) { - return getUserDataMap().get(userUUID); - } - - @NotNull - public UserData getData(Player player) { - return getUserDataMap().get(player.getUniqueId()); - } - - public void editData(@NotNull DataTaskRunner task) { - try { - task.run(Main.getStorage()); - } catch (Exception exception) { - Main.severe("无法正常更改玩家数据,请检查数据配置!"); - Main.severe("Could not edit user's data, please check the data conf!"); - exception.printStackTrace(); - } - } - - public void editDataAsync(@NotNull DataTaskRunner task) { - Main.getInstance().getScheduler().runAsync(() -> editData(task)); - } - - @NotNull - @Unmodifiable - public Map listUserData() { - return ImmutableMap.copyOf(getUserDataMap()); - } - - protected @NotNull HashMap getUserDataMap() { - return userDataMap; + @Override + public @NotNull UserRewardData errorUser(@NotNull UUID key) { + return new LockedRewardData(key); // 避免影响数据存储 } } diff --git a/src/main/java/cc/carm/plugin/timereward/storage/database/DatabaseTables.java b/src/main/java/cc/carm/plugin/timereward/storage/database/DatabaseTables.java index 19d1e7d..455ac2d 100644 --- a/src/main/java/cc/carm/plugin/timereward/storage/database/DatabaseTables.java +++ b/src/main/java/cc/carm/plugin/timereward/storage/database/DatabaseTables.java @@ -4,6 +4,7 @@ import cc.carm.lib.configuration.core.value.ConfigValue; import cc.carm.lib.easysql.api.SQLManager; import cc.carm.lib.easysql.api.SQLTable; import cc.carm.lib.easysql.api.builder.TableCreateBuilder; +import cc.carm.lib.easysql.api.enums.IndexType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,9 +20,9 @@ public enum DatabaseTables implements SQLTable { table.addColumn("uuid", "CHAR(36) NOT NULL PRIMARY KEY"); // 用户的UUID table.addColumn("date", "DATE NOT NULL"); // 日期 - table.addColumn("day_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户日在线时间(秒) - table.addColumn("week_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户周在线时间(秒) - table.addColumn("month_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒) + table.addColumn("daily_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户日在线时间(秒) + table.addColumn("weekly_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户周在线时间(秒) + table.addColumn("monthly_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒) table.addColumn("total_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户总在线时间(秒) table.addColumn("update", @@ -36,11 +37,12 @@ public enum DatabaseTables implements SQLTable { * 用于记录用户奖励领取情况的表 */ USER_CLAIMED(DatabaseConfig.TABLES.USER_CLAIMED, (table) -> { - table.addAutoIncrementColumn("id", false, true); // 排序键 - table.addColumn("uuid", "CHAR(36) NOT NULL PRIMARY KEY"); // 用户的UUID 主键 + table.addColumn("uuid", "CHAR(36) NOT NULL"); // 用户的UUID 主键 + table.addColumn("reward", "VARCHAR(64) NOT NULL"); // 已领取的奖励ID - table.addColumn("value", "MEDIUMTEXT"); // 已领取的奖励ID table.addColumn("time", "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP"); // 领取时间 + + table.setIndex(IndexType.PRIMARY_KEY, "pk_timereward_user", "uuid", "reward"); }); private final Consumer builder; diff --git a/src/main/java/cc/carm/plugin/timereward/storage/database/MySQLStorage.java b/src/main/java/cc/carm/plugin/timereward/storage/database/MySQLStorage.java index e99553e..fd93f7c 100644 --- a/src/main/java/cc/carm/plugin/timereward/storage/database/MySQLStorage.java +++ b/src/main/java/cc/carm/plugin/timereward/storage/database/MySQLStorage.java @@ -3,18 +3,19 @@ package cc.carm.plugin.timereward.storage.database; import cc.carm.lib.easysql.EasySQL; import cc.carm.lib.easysql.api.SQLManager; import cc.carm.plugin.timereward.Main; -import cc.carm.plugin.timereward.data.UserData; +import cc.carm.plugin.timereward.data.TimeRecord; +import cc.carm.plugin.timereward.user.UserRewardData; import com.google.gson.Gson; -import com.google.gson.JsonElement; import com.google.gson.JsonParser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; public class MySQLStorage { protected static final Gson GSON = new Gson(); @@ -56,62 +57,85 @@ public class MySQLStorage { return sqlManager; } - public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { - Long playTime = loadPlayTime(uuid); - Set claimedData = loadClaimedData(uuid); - return new UserData(uuid, playTime, claimedData); + public @Nullable UserRewardData loadData(@NotNull UUID uuid) throws Exception { + TimeRecord recordDate = loadTimeRecord(uuid); + System.out.println(recordDate.toString()); + Map claimedData = loadClaimedData(uuid); + return new UserRewardData(uuid, recordDate, claimedData); } - public long loadPlayTime(@NotNull UUID uuid) throws Exception { + public TimeRecord loadTimeRecord(@NotNull UUID uuid) throws Exception { return DatabaseTables.USER_TIMES.createQuery() - .selectColumns("uuid", "time") .addCondition("uuid", uuid).setLimit(1).build() - .executeFunction((query) -> { - ResultSet resultSet = query.getResultSet(); - if (resultSet == null || !resultSet.next()) return 0L; - return resultSet.getLong("time"); - }, 0L); + .executeFunction(query -> { + ResultSet rs = query.getResultSet(); + if (rs == null || !rs.next()) return TimeRecord.empty(); + + LocalDate date = rs.getDate("date").toLocalDate(); + long daily = rs.getLong("daily_time"); + long weekly = rs.getLong("weekly_time"); + long monthly = rs.getLong("monthly_time"); + long total = rs.getLong("total_time"); + + return new TimeRecord(date, daily, weekly, monthly, total); + }, TimeRecord.empty()); } @NotNull - public Set loadClaimedData(@NotNull UUID uuid) throws Exception { + public Map loadClaimedData(@NotNull UUID uuid) throws Exception { return DatabaseTables.USER_CLAIMED.createQuery() - .selectColumns("uuid", "value") - .addCondition("uuid", uuid).setLimit(1).build() + .addCondition("uuid", uuid).build() .executeFunction((query) -> { - ResultSet resultSet = query.getResultSet(); - if (!resultSet.next()) return new LinkedHashSet<>(); - String json = resultSet.getString("value"); - if (json == null) return new LinkedHashSet<>(); - JsonElement element = PARSER.parse(json); - if (!element.isJsonArray()) return new LinkedHashSet<>(); - Set ids = new LinkedHashSet<>(); - for (JsonElement e : element.getAsJsonArray()) { - ids.add(e.getAsString()); + ResultSet rs = query.getResultSet(); + Map map = new LinkedHashMap<>(); + + while (rs.next()) { + String rewardID = rs.getString("reward"); + LocalDateTime time = rs.getTimestamp("time").toLocalDateTime(); + map.put(rewardID, time); } - return ids; - }, new LinkedHashSet<>()); + + return map; + }, new LinkedHashMap<>()); } - public void saveClaimedData(@NotNull UUID uuid, @Nullable Set claimedRewards) throws Exception { - if (claimedRewards != null) { - DatabaseTables.USER_CLAIMED.createReplace() - .setColumnNames("uuid", "value") - .setParams(uuid.toString(), GSON.toJson(claimedRewards)) - .execute(); + public void addClaimedData(@NotNull UUID uuid, String reward) throws Exception { + HashMap time = new HashMap<>(); + time.put(reward, LocalDateTime.now()); + addClaimedData(uuid, time); + } + + public void addClaimedData(@NotNull UUID uuid, @NotNull Map data) throws Exception { + if (!data.isEmpty()) { + List values = data.entrySet().stream() + .map(entry -> new Object[]{uuid.toString(), entry.getKey(), entry.getValue()}) + .collect(Collectors.toList()); + DatabaseTables.USER_CLAIMED.createReplaceBatch() + .setColumnNames("uuid", "reward", "time") + .setAllParams(values).execute(); } else { DatabaseTables.USER_CLAIMED.createDelete() .addCondition("uuid", uuid).setLimit(1) - .build() - .execute(); + .build().execute(); } } - public void savePlayTime(@NotNull UUID uuid, long time) throws Exception { - DatabaseTables.USER_TIMES.createReplace() - .setColumnNames("uuid", "time") - .setParams(uuid.toString(), time) - .execute(); + public void savePlayTime(@NotNull UUID uuid, @Nullable TimeRecord newRecord) throws Exception { + if (newRecord != null) { + DatabaseTables.USER_TIMES.createReplace() + .setColumnNames("uuid", "date", "daily_time", "weekly_time", "monthly_time", "total_time") + .setParams( + uuid.toString(), newRecord.getDate(), + newRecord.getDailyTime().getSeconds(), + newRecord.getWeeklyTime().getSeconds(), + newRecord.getMonthlyTime().getSeconds(), + newRecord.getTotalTime().getSeconds() + ).execute(); + } else { + DatabaseTables.USER_TIMES.createDelete() + .addCondition("uuid", uuid).setLimit(1) + .build().execute(); + } } } diff --git a/src/main/java/cc/carm/plugin/timereward/user/LockedRewardData.java b/src/main/java/cc/carm/plugin/timereward/user/LockedRewardData.java new file mode 100644 index 0000000..1f8e9a7 --- /dev/null +++ b/src/main/java/cc/carm/plugin/timereward/user/LockedRewardData.java @@ -0,0 +1,37 @@ +package cc.carm.plugin.timereward.user; + +import cc.carm.plugin.timereward.data.IntervalType; +import cc.carm.plugin.timereward.data.RewardContents; +import cc.carm.plugin.timereward.data.TimeRecord; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.util.Collections; +import java.util.UUID; + +public class LockedRewardData extends UserRewardData { + public LockedRewardData(@NotNull UUID userUUID) { + super(userUUID, TimeRecord.empty(), Collections.emptyMap()); + } + + @Override + public Duration getOnlineDuration(@NotNull IntervalType type) { + return Duration.ZERO; + } + + @Override + public boolean isClaimed(@NotNull RewardContents reward) { + return false; + } + + @Override + public boolean isTimeEnough(RewardContents reward) { + return false; + } + + @Override + public boolean addClaimedReward(@NotNull RewardContents reward) { + return false; + } + +} diff --git a/src/main/java/cc/carm/plugin/timereward/user/UserRewardData.java b/src/main/java/cc/carm/plugin/timereward/user/UserRewardData.java new file mode 100644 index 0000000..981c1a9 --- /dev/null +++ b/src/main/java/cc/carm/plugin/timereward/user/UserRewardData.java @@ -0,0 +1,84 @@ +package cc.carm.plugin.timereward.user; + +import cc.carm.lib.easyplugin.user.UserData; +import cc.carm.plugin.timereward.data.IntervalType; +import cc.carm.plugin.timereward.data.RewardContents; +import cc.carm.plugin.timereward.data.TimeRecord; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.UUID; + +/** + * 用户奖励数据,用于存储用户的奖励的领取情况。 + */ +public class UserRewardData extends UserData { + + private final @NotNull Map claimedRewards; // 记录已领取的奖励ID + + private final @NotNull TimeRecord recordTime; + private final @NotNull LocalDateTime joinTime; + + public UserRewardData(@NotNull UUID userUUID, @NotNull TimeRecord timeRecord, @NotNull Map claimedRewards) { + this(userUUID, claimedRewards, timeRecord, LocalDateTime.now()); + } + + public UserRewardData(@NotNull UUID userUUID, @NotNull Map claimedRewards, + @NotNull TimeRecord recordTime, @NotNull LocalDateTime joinTime) { + super(userUUID); + this.claimedRewards = claimedRewards; + this.recordTime = recordTime; + this.joinTime = joinTime; + } + + public @NotNull UUID getUserUUID() { + return getKey(); + } + + /** + * @return 数据库中的时间记录 + */ + public @NotNull TimeRecord getTimeRecord() { + return recordTime; + } + + /** + * 得到本次加入游戏的时间 + * + * @return 本次加入游戏时间 + */ + public @NotNull LocalDateTime getJoinTime() { + return joinTime; + } + + /** + * @return 玩家的在线时间周期,需指定周期类型{@link IntervalType} + */ + public Duration getOnlineDuration(@NotNull IntervalType type) { + return type.calculate(getTimeRecord(), getJoinTime()); + } + + public boolean isTimeEnough(RewardContents reward) { + return getOnlineDuration(reward.getType()).getSeconds() >= reward.getTime(); + } + + + public @NotNull Map getClaimedRewards() { + return claimedRewards; + } + + public boolean isClaimed(@NotNull RewardContents reward) { + LocalDateTime claimedDate = claimedRewards.get(reward.getRewardID()); + if (claimedDate == null) return false; + return !reward.getType().isReclaimable(claimedDate); + } + + public boolean addClaimedReward(@NotNull RewardContents reward) { + if (isClaimed(reward)) return false; // 已经领取过了 + this.claimedRewards.put(reward.getRewardID(), LocalDateTime.now()); + return true; + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 75b9307..cec6194 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -16,6 +16,9 @@ permissions: "TimeReward.admin": description: "在线自动领奖的管理员权限。" default: op + "TimeReward.claim": + description: "使用领奖指令的权限,若关闭则玩家不得直接通过指令领取奖励。" + default: true commands: "TimeReward":