diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java index eefc8aa4..7932ec45 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java @@ -53,6 +53,8 @@ public class ContextKeys { public static final ContextKeys SCORE_FORMATTED = of("score_formatted", String.class); public static final ContextKeys SCORE = of("score", Double.class); public static final ContextKeys CUSTOM_SCORE = of("custom_score", Double.class); + public static final ContextKeys MIN_SIZE = of("min_size", Double.class); + public static final ContextKeys MAX_SIZE = of("max_size", Double.class); public static final ContextKeys RANK = of("rank", String.class); public static final ContextKeys OTHER_LOCATION = of("other_location", Location.class); public static final ContextKeys OTHER_X = of("other_x", Integer.class); diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java index 32f9d41a..926657f2 100644 --- a/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java +++ b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java @@ -40,6 +40,8 @@ import org.incendo.cloud.exception.handling.ExceptionContext; import org.incendo.cloud.minecraft.extras.MinecraftExceptionHandler; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -138,6 +140,11 @@ public abstract class AbstractCommandManager implements CustomFishingCommandM @Override public void registerDefaultFeatures() { YamlDocument document = plugin.getConfigManager().loadConfig(commandsFile); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "commands.yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } this.getFeatures().values().forEach(feature -> { CommandConfig config = getCommandConfig(document, feature.getFeatureID()); if (config.isEnable()) { diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java index 58a416b3..dba34d66 100644 --- a/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java @@ -75,6 +75,9 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_STATISTICS_RESET_SUCCESS = Component.translatable().key("command.statistics.reset.success"); TranslatableComponent.Builder COMMAND_STATISTICS_QUERY_AMOUNT = Component.translatable().key("command.statistics.query.amount"); TranslatableComponent.Builder COMMAND_STATISTICS_QUERY_SIZE = Component.translatable().key("command.statistics.query.size"); + TranslatableComponent.Builder COMMAND_DEBUG_LOOT_FAILURE_ROD = Component.translatable().key("command.debug.loot.failure.rod"); + TranslatableComponent.Builder COMMAND_DEBUG_LOOT_SUCCESS = Component.translatable().key("command.debug.loot.success"); + TranslatableComponent.Builder COMMAND_DEBUG_LOOT_FAILURE_NO_LOOT = Component.translatable().key("command.debug.loot.failure.no_loot"); // TranslatableComponent.Builder GUI_SELECT_FILE = Component.translatable().key("gui.select_file"); // TranslatableComponent.Builder GUI_SELECT_ITEM = Component.translatable().key("gui.select_item"); diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java b/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java index 13f33ab3..a1b19736 100644 --- a/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java @@ -26,6 +26,7 @@ import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; import net.momirealms.customfishing.common.util.Pair; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -37,7 +38,7 @@ import java.util.stream.Stream; public class TranslationManager { public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; - private static final List locales = List.of("en"); + private static final List locales = List.of("en", "zh_cn"); private static TranslationManager instance; private final CustomFishingPlugin plugin; @@ -57,7 +58,6 @@ public class TranslationManager { MiniMessageTranslator.translator().removeSource(this.registry); this.installed.clear(); } - for (String lang : locales) { this.plugin.getConfigManager().saveResource("translations/" + lang + ".yml"); } @@ -158,6 +158,11 @@ public class TranslationManager { Map bundle = new HashMap<>(); YamlDocument document = plugin.getConfigManager().loadConfig("translations" + "\\" + translationFile.getFileName(), '@'); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "translations" + "\\" + translationFile.getFileName())); + } catch (IOException e) { + throw new IllegalStateException("Could not update translation file: " + translationFile.getFileName(), e); + } Map map = document.getStringRouteMappedValues(false); map.remove("config-version"); for (Map.Entry entry : map.entrySet()) { diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java index 6fe85a2c..1bd0d40d 100644 --- a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java @@ -21,6 +21,7 @@ import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.context.Context; import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.common.util.RandomUtils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; @@ -70,6 +71,9 @@ public class CustomFishingPapi extends PlaceholderExpansion { if (player == null) return ""; switch (split[0]) { + case "random" -> { + return String.valueOf(RandomUtils.generateRandomDouble(0,1)); + } case "market" -> { if (split.length < 2) return null; diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java index b62ad251..3a4209bb 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java @@ -54,7 +54,8 @@ public class BukkitCommandManager extends AbstractCommandManager new AddStatisticsCommand(this), new SetStatisticsCommand(this), new ResetStatisticsCommand(this), - new QueryStatisticsCommand(this) + new QueryStatisticsCommand(this), + new DebugLootCommand(this) ); private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/DebugLootCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/DebugLootCommand.java new file mode 100644 index 00000000..33e35cdc --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/DebugLootCommand.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.mechanic.fishing.FishingGears; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.parser.standard.UUIDParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +public class DebugLootCommand extends BukkitCommandFeature { + + public DebugLootCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("surrounding", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(Stream.of("lava", "water", "void").map(Suggestion::suggestion).toList()); + } + })) + .handler(context -> { + String surrounding = context.get("surrounding"); + if (context.sender().getInventory().getItemInMainHand().getType() != Material.FISHING_ROD) { + handleFeedback(context, MessageConstants.COMMAND_DEBUG_LOOT_FAILURE_ROD); + return; + } + final Player player = context.sender(); + + Context playerContext = Context.player(player); + FishingGears gears = new FishingGears(playerContext); + + Effect effect = Effect.newInstance(); + // The effects impact mechanism at this stage + for (EffectModifier modifier : gears.effectModifiers()) { + for (TriConsumer, Integer> consumer : modifier.modifiers()) { + consumer.accept(effect, playerContext, 0); + } + } + + playerContext.arg(ContextKeys.SURROUNDING, surrounding); + Effect tempEffect = effect.copy(); + for (EffectModifier modifier : gears.effectModifiers()) { + for (TriConsumer, Integer> consumer : modifier.modifiers()) { + consumer.accept(tempEffect, playerContext, 1); + } + } + + playerContext.arg(ContextKeys.OTHER_LOCATION, player.getLocation()); + playerContext.arg(ContextKeys.OTHER_X, player.getLocation().getBlockX()); + playerContext.arg(ContextKeys.OTHER_Y, player.getLocation().getBlockY()); + playerContext.arg(ContextKeys.OTHER_Z, player.getLocation().getBlockZ()); + + Map weightMap = BukkitCustomFishingPlugin.getInstance().getLootManager().getWeightedLoots(tempEffect, playerContext); + + if (weightMap.isEmpty()) { + handleFeedback(context, MessageConstants.COMMAND_DEBUG_LOOT_FAILURE_NO_LOOT); + return; + } + + List loots = new ArrayList<>(); + double sum = 0; + for (Map.Entry entry : weightMap.entrySet()) { + double weight = entry.getValue(); + String loot = entry.getKey(); + if (weight <= 0) continue; + loots.add(new LootWithWeight(loot, weight)); + sum += weight; + } + LootWithWeight[] lootArray = loots.toArray(new LootWithWeight[0]); + quickSort(lootArray, 0,lootArray.length - 1); + Component component = Component.text().build(); + for (LootWithWeight loot : lootArray) { + component = component.append(Component.newline()) + .append(Component.text(loot.key + ": ").color(NamedTextColor.WHITE)) + .append(Component.text(String.format("%.4f", loot.weight * 100 / sum) + "% ").color(NamedTextColor.GOLD)) + .append(Component.text("(" + loot.weight + ")").color(NamedTextColor.GRAY)); + } + + handleFeedback(context, MessageConstants.COMMAND_DEBUG_LOOT_SUCCESS, component); + }); + } + + @Override + public String getFeatureID() { + return "debug_loot"; + } + + public record LootWithWeight(String key, double weight) { + } + + private static void quickSort(LootWithWeight[] loot, int low, int high) { + if (low < high) { + int pi = partition(loot, low, high); + quickSort(loot, low, pi - 1); + quickSort(loot, pi + 1, high); + } + } + + private static int partition(LootWithWeight[] loot, int low, int high) { + double pivot = loot[high].weight(); + int i = low - 1; + for (int j = low; j <= high - 1; j++) { + if (loot[j].weight() > pivot) { + i++; + swap(loot, i, j); + } + } + swap(loot, i + 1, high); + return i + 1; + } + + private static void swap(LootWithWeight[] loot, int i, int j) { + LootWithWeight temp = loot[i]; + loot[i] = loot[j]; + loot[j] = temp; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java index b5c30ee3..4cebb290 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java @@ -439,6 +439,8 @@ public class BukkitConfigManager extends ConfigManager { float size = (float) RandomUtils.generateRandomDouble(minSize, maxSize); item.setTag(size, "CustomFishing", "size"); context.arg(ContextKeys.SIZE, size); + context.arg(ContextKeys.MIN_SIZE, minSize); + context.arg(ContextKeys.MAX_SIZE, maxSize); context.arg(ContextKeys.SIZE_FORMATTED, String.format("%.2f", size)); }; }, 1_000, "size"); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/migration/Migration.java b/core/src/main/java/net/momirealms/customfishing/bukkit/migration/Migration.java index 11db703f..018e2a5d 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/migration/Migration.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/migration/Migration.java @@ -155,6 +155,7 @@ public class Migration { try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(outPut), StandardCharsets.UTF_8))) { writer.write(sb.toString() + .replace("{sold-item-amount}", "{sold_item_amount}") .replace("{size}", "{size_formatted}") .replace("{record}", "{record_formatted}") .replace("{loot}", "{id}") diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java index 380c5635..4c69d84f 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java @@ -46,6 +46,8 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashSet; @@ -77,6 +79,11 @@ public class BukkitStorageManager implements StorageManager, Listener { @Override public void reload() { YamlDocument config = plugin.getConfigManager().loadConfig("database.yml"); + try { + config.save(new File(plugin.getDataFolder(), "database.yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } this.serverID = config.getString("unique-server-id", "default"); // Check if storage type has changed and reinitialize if necessary @@ -291,7 +298,7 @@ public class BukkitStorageManager implements StorageManager, Listener { if (player == null || !player.isOnline()) return; if (times > 3) { - plugin.getPluginLogger().warn("Tried 3 times when getting data for " + uuid + ". Giving up."); + plugin.getPluginLogger().warn("Tried 3 times getting data for " + uuid + ". Giving up."); return; } this.dataSource.getPlayerData(uuid, ConfigManager.lockData()).thenAccept(optionalData -> { diff --git a/core/src/main/resources/commands.yml b/core/src/main/resources/commands.yml index 63e514cb..27e769b7 100644 --- a/core/src/main/resources/commands.yml +++ b/core/src/main/resources/commands.yml @@ -183,4 +183,13 @@ statistics_add: permission: customfishing.command.statistics usage: - /customfishing statistics add - - /cfishing statistics add \ No newline at end of file + - /cfishing statistics add + +# A command to debug the loot table +# Usage: [COMMAND] [surrounding] +debug_loot: + enable: true + permission: customfishing.command.debug + usage: + - /customfishing debug loot + - /cfishing debug loot \ No newline at end of file diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 427fa637..f3d813ce 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -417,6 +417,7 @@ other-settings: '{date}': '%server_time_yyyy-MM-dd-HH:mm:ss%' # Requires player expansion '{yaw}': '%player_yaw%' + '{random}': '%customfishing_random%' # CustomFishing supports using items/blocks from other plugins # If items share the same id, they would inherit the effects # Check the wiki for examples diff --git a/core/src/main/resources/contents/item/default.yml b/core/src/main/resources/contents/item/default.yml index 932c585e..0e51cbe6 100644 --- a/core/src/main/resources/contents/item/default.yml +++ b/core/src/main/resources/contents/item/default.yml @@ -117,7 +117,7 @@ rubbish: display: name: <#00BFFF>Radioactive Fish lore: - - Protect the environment... + - Time to protect the environment custom-model-data: 50000 show-in-fishfinder: false disable-stat: true diff --git a/core/src/main/resources/translations/en.yml b/core/src/main/resources/translations/en.yml index a64c0cc3..5121d779 100644 --- a/core/src/main/resources/translations/en.yml +++ b/core/src/main/resources/translations/en.yml @@ -76,6 +76,9 @@ command.statistics.modify.success: "Successfully modified the fishing sta command.statistics.reset.success: "Successfully reset the fishing statistics for " command.statistics.query.size: "Max sizes: " command.statistics.query.amount: "Amount of fish caught: " +command.debug.loot.failure.rod: "Please hold a fishing rod before using this command" +command.debug.loot.failure.no_loot: "No loot available" +command.debug.loot.success: "Available loots: " competition.no_score: "No Score" competition.no_player: "No Player" competition.no_rank: "No Rank" diff --git a/core/src/main/resources/translations/zh_cn.yml b/core/src/main/resources/translations/zh_cn.yml index 79d36238..8b7a0328 100644 --- a/core/src/main/resources/translations/zh_cn.yml +++ b/core/src/main/resources/translations/zh_cn.yml @@ -76,6 +76,8 @@ command.statistics.modify.success: "成功修改 的钓鱼统计 command.statistics.reset.success: "成功重置 的钓鱼统计数据" command.statistics.query.size: "最长的鱼的长度: " command.statistics.query.amount: "钓到鱼的数量: " +command.debug.loot.failure.rod: "请手持鱼竿后再执行此命令" +command.debug.loot.success: "战利品概率表: " competition.no_score: "暂无得分" competition.no_player: "暂无玩家" competition.no_rank: "暂无排名" diff --git a/gradle.properties b/gradle.properties index 33df6cc7..536e50a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Project settings # Rule: [major update].[feature update].[bug fix] project_version=2.2.0 -config_version=33 +config_version=34 project_group=net.momirealms # Dependency settings