diff --git a/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java b/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java index c0f15ca3..b68efd9c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java +++ b/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java @@ -43,6 +43,7 @@ public abstract class CustomFishingPlugin extends JavaPlugin { protected CompetitionManager competitionManager; protected StorageManager storageManager; protected PlaceholderManager placeholderManager; + protected StatisticsManager statisticsManager; private static CustomFishingPlugin instance; @@ -126,6 +127,10 @@ public abstract class CustomFishingPlugin extends JavaPlugin { return integrationManager; } + public StatisticsManager getStatisticsManager() { + return statisticsManager; + } + public PlaceholderManager getPlaceholderManager() { return placeholderManager; } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java index 4c45b537..347583cc 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java @@ -18,12 +18,12 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionBuilder; +import net.momirealms.customfishing.api.mechanic.action.ActionFactory; import org.bukkit.configuration.ConfigurationSection; public interface ActionManager { - boolean registerAction(String type, ActionBuilder actionBuilder); + boolean registerAction(String type, ActionFactory actionFactory); boolean unregisterAction(String type); @@ -31,5 +31,5 @@ public interface ActionManager { Action[] getActions(ConfigurationSection section); - ActionBuilder getActionBuilder(String type); + ActionFactory getActionBuilder(String type); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java index 3ae2faaf..862b315a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java @@ -18,6 +18,7 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfig; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,8 @@ import java.util.concurrent.CompletableFuture; public interface CompetitionManager { Set getAllCompetitions(); + String getCompetitionLocale(CompetitionGoal goal); + void startCompetition(String competition, boolean force, boolean allServers); @Nullable diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java index fad19882..6c4911e4 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java @@ -19,7 +19,7 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.TempFishingState; import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.mechanic.game.Game; +import net.momirealms.customfishing.api.mechanic.game.GameInstance; import net.momirealms.customfishing.api.mechanic.game.GameSettings; import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; import net.momirealms.customfishing.api.mechanic.loot.Loot; @@ -44,5 +44,5 @@ public interface FishingManager { void startFishingGame(Player player, Loot loot, Effect effect); - void startFishingGame(Player player, GameSettings settings, Game game); + void startFishingGame(Player player, GameSettings settings, GameInstance gameInstance); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java index 98642b63..e2ab8d82 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java @@ -17,25 +17,25 @@ package net.momirealms.customfishing.api.manager; -import net.momirealms.customfishing.api.mechanic.game.Game; import net.momirealms.customfishing.api.mechanic.game.GameConfig; -import net.momirealms.customfishing.api.mechanic.game.GameCreator; +import net.momirealms.customfishing.api.mechanic.game.GameFactory; +import net.momirealms.customfishing.api.mechanic.game.GameInstance; import org.jetbrains.annotations.Nullable; public interface GameManager { - boolean registerGameType(String type, GameCreator gameCreator); + boolean registerGameType(String type, GameFactory gameFactory); boolean unregisterGameType(String type); - @Nullable GameCreator getGameCreator(String type); + @Nullable GameFactory getGameCreator(String type); - @Nullable Game getGame(String key); + @Nullable GameInstance getGame(String key); @Nullable GameConfig getGameConfig(String key); - Game getRandomGame(); + GameInstance getRandomGame(); GameConfig getRandomGameConfig(); diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java index ee34fa1c..86238f5b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java @@ -19,7 +19,7 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import net.momirealms.customfishing.api.mechanic.requirement.RequirementBuilder; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory; import org.bukkit.configuration.ConfigurationSection; import org.jetbrains.annotations.Nullable; @@ -27,7 +27,7 @@ import java.util.HashMap; public interface RequirementManager { - boolean registerRequirement(String type, RequirementBuilder requirementBuilder); + boolean registerRequirement(String type, RequirementFactory requirementFactory); boolean unregisterRequirement(String type); @@ -39,7 +39,7 @@ public interface RequirementManager { Requirement getRequirement(String key, Object value); - RequirementBuilder getRequirementBuilder(String type); + RequirementFactory getRequirementBuilder(String type); static boolean isRequirementsMet(Requirement[] requirements, Condition condition) { if (requirements == null) return true; diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/MiniPlaceholdersHook.java b/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/MiniPlaceholdersHook.java rename to api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java index ed1c2dc3..7ae25b0a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/MiniPlaceholdersHook.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java @@ -15,7 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.papi; +package net.momirealms.customfishing.api.manager; -public class MiniPlaceholdersHook { +import net.momirealms.customfishing.api.mechanic.statistic.Statistics; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.UUID; + +public interface StatisticsManager { + Statistics getStatistics(UUID uuid); + + @Nullable List getCategory(String key); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java new file mode 100644 index 00000000..39ab1ba0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java @@ -0,0 +1,12 @@ +package net.momirealms.customfishing.api.mechanic.action; + +public abstract class ActionExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getActionType(); + + public abstract ActionFactory getActionFactory(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java similarity index 96% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionBuilder.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java index cda5fd36..fdaae29d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionBuilder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java @@ -17,7 +17,7 @@ package net.momirealms.customfishing.api.mechanic.action; -public interface ActionBuilder { +public interface ActionFactory { Action build(Object args, double chance); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java index 3c9efdb0..6f52519f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java @@ -22,5 +22,6 @@ public enum ActionTrigger { SUCCESS, FAILURE, HOOK, - CONSUME + CONSUME, + CAST } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java index 56a7c892..72d95ec0 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java @@ -20,6 +20,8 @@ package net.momirealms.customfishing.api.mechanic.competition; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import java.util.concurrent.ConcurrentHashMap; + public interface FishingCompetition { void start(); @@ -29,7 +31,7 @@ public interface FishingCompetition { boolean isOnGoing(); - void refreshData(Player player, double score, boolean doubleScore); + void refreshData(Player player, double score); boolean hasPlayerJoined(OfflinePlayer player); @@ -44,4 +46,8 @@ public interface FishingCompetition { CompetitionGoal getGoal(); Ranking getRanking(); + + ConcurrentHashMap getCachedPlaceholders(); + + String getCachedPlaceholder(String papi); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameConfig.java index 7f069027..6de88a4a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameConfig.java @@ -24,5 +24,5 @@ import org.jetbrains.annotations.Nullable; public interface GameConfig { @Nullable - Pair getRandomGame(Effect effect); + Pair getRandomGame(Effect effect); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java index 4d7f0b31..082c1e1e 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java @@ -8,5 +8,5 @@ public abstract class GameExpansion { public abstract String getGameType(); - public abstract GameCreator getGameCreator(); + public abstract GameFactory getGameFactory(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java similarity index 56% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java index 4378136c..bd5c02b8 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java @@ -2,8 +2,8 @@ package net.momirealms.customfishing.api.mechanic.game; import org.bukkit.configuration.ConfigurationSection; -public interface GameCreator { +public interface GameFactory { - Game setArgs(ConfigurationSection section); + GameInstance setArgs(ConfigurationSection section); } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroup.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroup.java index 5fa205e6..0c3a1910 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroup.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroup.java @@ -62,10 +62,10 @@ public class GameGroup implements GameConfig { @Override @Nullable - public Pair getRandomGame(Effect effect) { + public Pair getRandomGame(Effect effect) { String key = WeightUtils.getRandom(gamePairs); - Game game = CustomFishingPlugin.get().getGameManager().getGame(key); - if (game == null) { + GameInstance gameInstance = CustomFishingPlugin.get().getGameManager().getGame(key); + if (gameInstance == null) { CustomFishingPlugin.get().getLogger().warning(String.format("Game %s doesn't exist!", key)); return null; } @@ -73,6 +73,6 @@ public class GameGroup implements GameConfig { ThreadLocalRandom.current().nextInt(minTime, maxTime + 1), (int) (ThreadLocalRandom.current().nextInt(minDifficulty, maxDifficulty + 1) + effect.getDifficultyModifier()) ); - return Pair.of(game, settings); + return Pair.of(gameInstance, settings); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroups.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroups.java index ce8b7d84..968ec6c8 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroups.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameGroups.java @@ -34,7 +34,7 @@ public class GameGroups implements GameConfig { } @Override - public @Nullable Pair getRandomGame(Effect effect) { + public @Nullable Pair getRandomGame(Effect effect) { String group = WeightUtils.getRandom(gamesWithWeight); GameConfig gameConfig = CustomFishingPlugin.get().getGameManager().getGameConfig(group); if (gameConfig == null) { diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java similarity index 96% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java index 64734371..cca32618 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java @@ -20,7 +20,7 @@ package net.momirealms.customfishing.api.mechanic.game; import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; -public interface Game { +public interface GameInstance { GamingPlayer start(Player player, FishHook hook, GameSettings settings); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java new file mode 100644 index 00000000..c4120512 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java @@ -0,0 +1,12 @@ +package net.momirealms.customfishing.api.mechanic.requirement; + +public abstract class RequirementExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getRequirementType(); + + public abstract RequirementFactory getRequirementFactory(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java similarity index 96% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementBuilder.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java index ef633576..d3ca670b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementBuilder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java @@ -21,7 +21,7 @@ import net.momirealms.customfishing.api.mechanic.action.Action; import java.util.List; -public interface RequirementBuilder { +public interface RequirementFactory { Requirement build(Object args, List notMetActions, boolean checkAction); diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java index 15f9c692..33940d02 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java @@ -75,7 +75,7 @@ public class Statistics { } } - public int getFishAmount(String key) { + public int getLootAmount(String key) { Integer amount = statisticMap.get(key); return amount == null ? 0 : amount; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java index ed5b22ed..595591af 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java @@ -41,6 +41,7 @@ import net.momirealms.customfishing.mechanic.loot.LootManagerImpl; import net.momirealms.customfishing.mechanic.market.MarketManagerImpl; import net.momirealms.customfishing.mechanic.mob.MobManagerImpl; import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; +import net.momirealms.customfishing.mechanic.statistic.StatisticsManagerImpl; import net.momirealms.customfishing.scheduler.SchedulerImpl; import net.momirealms.customfishing.setting.Config; import net.momirealms.customfishing.setting.Locale; @@ -92,6 +93,7 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { this.storageManager = new StorageManagerImpl(this); this.competitionManager = new CompetitionManagerImpl(this); this.integrationManager = new IntegrationManagerImpl(this); + this.statisticsManager = new StatisticsManagerImpl(this); this.reload(); if (Config.updateChecker) this.versionManager.checkUpdate().thenAccept(result -> { @@ -118,6 +120,8 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { ((StorageManagerImpl) this.storageManager).disable(); ((CompetitionManagerImpl) this.competitionManager).disable(); ((PlaceholderManagerImpl) this.placeholderManager).disable(); + ((StatisticsManagerImpl) this.statisticsManager).disable(); + ((ActionManagerImpl) this.actionManager).disable(); } @Override @@ -127,6 +131,10 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { ((SchedulerImpl) this.scheduler).reload(); ((RequirementManagerImpl) this.requirementManager).unload(); ((RequirementManagerImpl) this.requirementManager).load(); + ((ActionManagerImpl) this.actionManager).unload(); + ((ActionManagerImpl) this.actionManager).load(); + ((GameManagerImpl) this.gameManager).unload(); + ((GameManagerImpl) this.gameManager).load(); ((ItemManagerImpl) this.itemManager).unload(); ((ItemManagerImpl) this.itemManager).load(); ((LootManagerImpl) this.lootManager).unload(); @@ -141,13 +149,13 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { ((BagManagerImpl) this.bagManager).load(); ((BlockManagerImpl) this.blockManager).unload(); ((BlockManagerImpl) this.blockManager).load(); - ((GameManagerImpl) this.gameManager).unload(); - ((GameManagerImpl) this.gameManager).load(); ((MobManagerImpl) this.mobManager).unload(); ((MobManagerImpl) this.mobManager).load(); ((CompetitionManagerImpl) this.competitionManager).unload(); ((CompetitionManagerImpl) this.competitionManager).load(); ((StorageManagerImpl) this.storageManager).reload(); + ((StatisticsManagerImpl) this.statisticsManager).unload(); + ((StatisticsManagerImpl) this.statisticsManager).load(); ((PlaceholderManagerImpl) this.placeholderManager).unload(); ((PlaceholderManagerImpl) this.placeholderManager).load(); this.commandManager.loadCommands(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java new file mode 100644 index 00000000..fc2f8ced --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java @@ -0,0 +1,146 @@ +/* + * 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.compatibility.papi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.setting.Locale; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CompetitionPapi extends PlaceholderExpansion { + + private final CustomFishingPlugin plugin; + + public CompetitionPapi(CustomFishingPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + super.register(); + } + + public void unload() { + super.unregister(); + } + + @Override + public @NotNull String getIdentifier() { + return "cfcompetition"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "2.0"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + switch (params) { + case "goingon" -> { + return String.valueOf(plugin.getCompetitionManager().getOnGoingCompetition() != null); + } + case "nextseconds" -> { + return String.valueOf(plugin.getCompetitionManager().getNextCompetitionSeconds()); + } + case "nextminutes" -> { + return String.valueOf(plugin.getCompetitionManager().getNextCompetitionSeconds() / 60); + } + case "nextsecond" -> { + return plugin.getCompetitionManager().getNextCompetitionSeconds() % 60 + Locale.FORMAT_Second; + } + case "nextminute" -> { + int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int min = (sec % 3600) / 60; + return sec < 60 ? "" : min + Locale.FORMAT_Minute; + } + case "nexthour" -> { + int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int h = (sec % (3600 * 24)) / 3600; + return sec < 3600 ? "" : h + Locale.FORMAT_Hour; + } + case "nextday" -> { + int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int day = sec / (3600 * 24); + return day == 0 ? "" : day + Locale.FORMAT_Day; + } + case "rank" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + else return String.valueOf(competition.getRanking().getPlayerRank(player.getName())); + } + case "goal" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + else return competition.getGoal().name(); + } + case "seconds" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + return competition.getCachedPlaceholder("{seconds}"); + } + case "second" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + return competition.getCachedPlaceholder("{second}"); + } + case "minute" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + return competition.getCachedPlaceholder("{minute}"); + } + case "hour" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + return competition.getCachedPlaceholder("{hour}"); + } + } + + String[] split = params.split("_", 2); + switch (split[0]) { + case "score" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + if (split[1].equals("")) { + return String.format("%.2f", competition.getRanking().getPlayerScore(player.getName())); + } else { + return String.format("%.2f", competition.getRanking().getScoreAt(Integer.parseInt(split[1]))); + } + } + case "player" -> { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition == null) return ""; + if (split[1].equals("")) return "Invalid format"; + return competition.getRanking().getPlayerAt(Integer.parseInt(split[1])); + } + } + return "null"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderAPIHook.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderAPIHook.java deleted file mode 100644 index 512ae04e..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderAPIHook.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.compatibility.papi; - -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.bukkit.OfflinePlayer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PlaceholderAPIHook extends PlaceholderExpansion { - - private CustomFishingPlugin plugin; - - public PlaceholderAPIHook(CustomFishingPlugin plugin) { - this.plugin = plugin; - } - - public void load() { - super.register(); - } - - public void unload() { - super.unregister(); - } - - @Override - public @NotNull String getIdentifier() { - return "customfishing"; - } - - @Override - public @NotNull String getAuthor() { - return "XiaoMoMi"; - } - - @Override - public @NotNull String getVersion() { - return "1.4"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { - - - - return super.onRequest(player, params); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java index 9ba6e5c2..34b47423 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java @@ -37,11 +37,12 @@ import java.util.stream.Collectors; public class PlaceholderManagerImpl implements PlaceholderManager { private static PlaceholderManagerImpl instance; - private CustomFishingPlugin plugin; + private final CustomFishingPlugin plugin; private final boolean hasPapi; private final Pattern pattern; private final HashMap customPlaceholderMap; - private PlaceholderAPIHook placeholderAPIHook; + private CompetitionPapi competitionPapi; + private StatisticsPapi statisticsPapi; public PlaceholderManagerImpl(CustomFishingPlugin plugin) { instance = this; @@ -50,17 +51,20 @@ public class PlaceholderManagerImpl implements PlaceholderManager { this.pattern = Pattern.compile("\\{[^{}]+}"); this.customPlaceholderMap = new HashMap<>(); if (this.hasPapi) { - placeholderAPIHook = new PlaceholderAPIHook(plugin); + competitionPapi = new CompetitionPapi(plugin); + statisticsPapi = new StatisticsPapi(plugin); } } public void load() { - if (placeholderAPIHook != null) placeholderAPIHook.load(); + if (competitionPapi != null) competitionPapi.load(); + if (statisticsPapi != null) statisticsPapi.load(); loadCustomPlaceholders(); } public void unload() { - if (placeholderAPIHook != null) placeholderAPIHook.unload(); + if (competitionPapi != null) competitionPapi.unload(); + if (statisticsPapi != null) statisticsPapi.unload(); } public void disable() { diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java new file mode 100644 index 00000000..1ccaa3e7 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java @@ -0,0 +1,104 @@ +/* + * 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.compatibility.papi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.data.user.OnlineUser; +import net.momirealms.customfishing.api.mechanic.statistic.Statistics; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class StatisticsPapi extends PlaceholderExpansion { + + private final CustomFishingPlugin plugin; + + public StatisticsPapi(CustomFishingPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + super.register(); + } + + public void unload() { + super.unregister(); + } + + @Override + public @NotNull String getIdentifier() { + return "fishingstats"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "2.0"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + OnlineUser onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + if (onlineUser == null) return ""; + Statistics statistics = onlineUser.getStatistics(); + if (params.equals("total")) { + return String.valueOf(statistics.getTotalCatchAmount()); + } + + String[] split = params.split("_"); + switch (split[0]) { + case "hascaught" -> { + return String.valueOf(statistics.getLootAmount(split[1]) != 0); + } + case "category" -> { + List category = plugin.getStatisticsManager().getCategory(split[2]); + if (category == null) return "0"; + if (split[1].equals("total")) { + int total = 0; + for (String loot : category) { + total += statistics.getLootAmount(loot); + } + return String.valueOf(total); + } else if (split[1].equals("progress")) { + int size = category.size(); + int unlocked = 0; + for (String loot : category) { + if (statistics.getLootAmount(loot) != 0) unlocked++; + } + double percent = (double) unlocked / size; + String progress = String.format("%.1f", percent); + return progress.equals("100.0") ? "100" : progress; + } + } + } + + return "null"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java index 0445a0fc..47509011 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java @@ -23,9 +23,11 @@ import net.momirealms.customfishing.adventure.AdventureManagerImpl; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.manager.ActionManager; import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionBuilder; +import net.momirealms.customfishing.api.mechanic.action.ActionExpansion; +import net.momirealms.customfishing.api.mechanic.action.ActionFactory; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; +import net.momirealms.customfishing.util.ClassUtils; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; @@ -35,6 +37,9 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -42,7 +47,8 @@ import java.util.concurrent.TimeUnit; public class ActionManagerImpl implements ActionManager { private final CustomFishingPlugin plugin; - private final HashMap actionBuilderMap; + private final HashMap actionBuilderMap; + private final String EXPANSION_FOLDER = "expansions/actions"; public ActionManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; @@ -65,10 +71,22 @@ public class ActionManagerImpl implements ActionManager { this.registerDelayedAction(); } + public void load() { + this.loadExpansions(); + } + + public void unload() { + + } + + public void disable() { + this.actionBuilderMap.clear(); + } + @Override - public boolean registerAction(String type, ActionBuilder actionBuilder) { + public boolean registerAction(String type, ActionFactory actionFactory) { if (this.actionBuilderMap.containsKey(type)) return false; - this.actionBuilderMap.put(type, actionBuilder); + this.actionBuilderMap.put(type, actionFactory); return true; } @@ -96,7 +114,7 @@ public class ActionManagerImpl implements ActionManager { } @Override - public ActionBuilder getActionBuilder(String type) { + public ActionFactory getActionBuilder(String type) { return actionBuilderMap.get(type); } @@ -371,4 +389,34 @@ public class ActionManagerImpl implements ActionManager { return null; }); } + + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, ActionExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterAction(expansion.getActionType()); + registerAction(expansion.getActionType(), expansion.getActionFactory()); + LogUtils.info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java index 8ef6cc28..93e07e85 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java @@ -17,29 +17,14 @@ package net.momirealms.customfishing.mechanic.bag; -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ScoreComponent; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureManagerImpl; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.manager.BagManager; import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; import net.momirealms.customfishing.setting.Config; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; import org.bukkit.inventory.Inventory; -import java.util.HashMap; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -47,12 +32,10 @@ public class BagManagerImpl implements BagManager { private final CustomFishingPlugin plugin; private final ConcurrentHashMap bagMap; - private final WindowPacketListener windowPacketListener; public BagManagerImpl(CustomFishingPluginImpl plugin) { this.plugin = plugin; this.bagMap = new ConcurrentHashMap<>(); - this.windowPacketListener = new WindowPacketListener(); } @Override @@ -61,11 +44,11 @@ public class BagManagerImpl implements BagManager { } public void load() { - CustomFishingPluginImpl.getProtocolManager().addPacketListener(windowPacketListener); + } public void unload() { - CustomFishingPluginImpl.getProtocolManager().removePacketListener(windowPacketListener); + } public void disable() { @@ -81,34 +64,4 @@ public class BagManagerImpl implements BagManager { } return onlinePlayer.getHolder().getInventory(); } - - public static class WindowPacketListener extends PacketAdapter { - - public WindowPacketListener() { - super(CustomFishingPlugin.getInstance(), PacketType.Play.Server.OPEN_WINDOW); - } - - @Override - public void onPacketSending(PacketEvent event) { - final PacketContainer packet = event.getPacket(); - StructureModifier wrappedChatComponentStructureModifier = packet.getChatComponents(); - WrappedChatComponent component = wrappedChatComponentStructureModifier.getValues().get(0); - String windowTitleJson = component.getJson(); - Component titleComponent = GsonComponentSerializer.gson().deserialize(windowTitleJson); - if (titleComponent instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("bag")) { - HashMap placeholders = new HashMap<>(); - String uuidStr = scoreComponent.objective(); - UUID uuid = UUID.fromString(uuidStr); - placeholders.put("{uuid}", uuidStr); - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); - placeholders.put("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(uuidStr)); - wrappedChatComponentStructureModifier.write(0, - WrappedChatComponent.fromJson( - GsonComponentSerializer.gson().serialize( - AdventureManagerImpl.getInstance().getComponentFromMiniMessage( - PlaceholderManagerImpl.getInstance().parse(offlinePlayer, Config.bagTitle, placeholders) - )))); - } - } - } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java index 59f92490..f4522a8a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java @@ -62,7 +62,7 @@ public class Competition implements FishingCompetition { if (Config.redisRanking) this.ranking = new RedisRankingImpl(); else this.ranking = new LocalRankingImpl(); this.publicPlaceholders = new ConcurrentHashMap<>(); - this.publicPlaceholders.put("{goal}", getCompetitionLocale(goal)); + this.publicPlaceholders.put("{goal}", CustomFishingPlugin.get().getCompetitionManager().getCompetitionLocale(goal)); } @Override @@ -112,10 +112,10 @@ public class Competition implements FishingCompetition { publicPlaceholders.put("{" + finalI + "_score}", Locale.MSG_No_Score); }); } - publicPlaceholders.put("{hour}", String.valueOf(remainingTime / 3600)); - publicPlaceholders.put("{minute}", String.valueOf((remainingTime % 3600) / 60)); - publicPlaceholders.put("{second}", String.valueOf(remainingTime % 60)); - publicPlaceholders.put("{time}", String.valueOf(remainingTime)); + publicPlaceholders.put("{hour}", remainingTime < 3600 ? "" : (remainingTime / 3600) + Locale.FORMAT_Hour); + publicPlaceholders.put("{minute}", remainingTime < 60 ? "" : (remainingTime % 3600) / 60 + Locale.FORMAT_Minute); + publicPlaceholders.put("{second}", remainingTime == 0 ? "" : remainingTime % 60 + Locale.FORMAT_Second); + publicPlaceholders.put("{seconds}", remainingTime + Locale.FORMAT_Second); } @Override @@ -195,7 +195,7 @@ public class Competition implements FishingCompetition { } @Override - public void refreshData(Player player, double score, boolean doubleScore) { + public void refreshData(Player player, double score) { // if player join for the first time, trigger join actions if (!hasPlayerJoined(player)) { Action[] actions = config.getJoinActions(); @@ -213,8 +213,8 @@ public class Competition implements FishingCompetition { // refresh data switch (this.goal) { - case CATCH_AMOUNT -> ranking.refreshData(player.getName(), doubleScore ? 2 : 1); - case TOTAL_SIZE, TOTAL_SCORE -> ranking.refreshData(player.getName(), doubleScore ? 2 * score : score); + case CATCH_AMOUNT -> ranking.refreshData(player.getName(), 1); + case TOTAL_SIZE, TOTAL_SCORE -> ranking.refreshData(player.getName(), score); case MAX_SIZE -> { if (score > ranking.getPlayerScore(player.getName())) { ranking.setData(player.getName(), score); @@ -258,25 +258,13 @@ public class Competition implements FishingCompetition { return ranking; } - public ConcurrentHashMap getPublicPlaceholders() { + @Override + public ConcurrentHashMap getCachedPlaceholders() { return publicPlaceholders; } - private String getCompetitionLocale(CompetitionGoal goal) { - switch (goal) { - case MAX_SIZE -> { - return Locale.MSG_Max_Size; - } - case CATCH_AMOUNT -> { - return Locale.MSG_Catch_Amount; - } - case TOTAL_SCORE -> { - return Locale.MSG_Total_Score; - } - case TOTAL_SIZE -> { - return Locale.MSG_Total_Size; - } - } - return ""; + @Override + public String getCachedPlaceholder(String papi) { + return publicPlaceholders.get(papi); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java index a4c6b4a0..0cf6e5fa 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java @@ -201,6 +201,25 @@ public class CompetitionManagerImpl implements CompetitionManager { } } + @Override + public String getCompetitionLocale(CompetitionGoal goal) { + switch (goal) { + case MAX_SIZE -> { + return net.momirealms.customfishing.setting.Locale.MSG_Max_Size; + } + case CATCH_AMOUNT -> { + return net.momirealms.customfishing.setting.Locale.MSG_Catch_Amount; + } + case TOTAL_SCORE -> { + return net.momirealms.customfishing.setting.Locale.MSG_Total_Score; + } + case TOTAL_SIZE -> { + return net.momirealms.customfishing.setting.Locale.MSG_Total_Size; + } + } + return ""; + } + @Override public void startCompetition(String competition, boolean force, boolean allServer) { CompetitionConfig config = commandConfigMap.get(competition); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java index 81ff6f5b..d7eb14e8 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java @@ -64,7 +64,7 @@ public class ActionBarSender { this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); int rank = competition.getRanking().getPlayerRank(player.getName()); this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : Locale.MSG_No_Rank); - this.privatePlaceholders.putAll(competition.getPublicPlaceholders()); + this.privatePlaceholders.putAll(competition.getCachedPlaceholders()); } public void show() { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java index 9f06db11..6e0d0343 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java @@ -77,7 +77,7 @@ public class BossBarSender { this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); int rank = competition.getRanking().getPlayerRank(player.getName()); this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : Locale.MSG_No_Rank); - this.privatePlaceholders.putAll(competition.getPublicPlaceholders()); + this.privatePlaceholders.putAll(competition.getCachedPlaceholders()); } public void show() { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java index a39d1920..6bbbdaaf 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java @@ -28,10 +28,11 @@ import net.momirealms.customfishing.api.manager.RequirementManager; import net.momirealms.customfishing.api.mechanic.TempFishingState; import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.mechanic.game.Game; import net.momirealms.customfishing.api.mechanic.game.GameConfig; +import net.momirealms.customfishing.api.mechanic.game.GameInstance; import net.momirealms.customfishing.api.mechanic.game.GameSettings; import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; import net.momirealms.customfishing.api.mechanic.loot.Loot; @@ -488,20 +489,16 @@ public class FishingManagerImpl implements Listener, FishingManager { var fishingPreparation = state.getPreparation(); var player = fishingPreparation.getPlayer(); - fishingPreparation.insertArg("{score}", String.format("%.2f", loot.getScore() * effect.getScoreMultiplier())); fishingPreparation.insertArg("{size-multiplier}", String.format("%.2f", effect.getSizeMultiplier())); fishingPreparation.insertArg("{x}", String.valueOf(hook.getLocation().getBlockX())); fishingPreparation.insertArg("{y}", String.valueOf(hook.getLocation().getBlockY())); fishingPreparation.insertArg("{z}", String.valueOf(hook.getLocation().getBlockZ())); - fishingPreparation.insertArg("{loot}", loot.getID()); - fishingPreparation.insertArg("{nick}", loot.getNick()); plugin.getScheduler().runTaskSync(() -> { switch (loot.getType()) { case LOOT -> { int amount = (int) effect.getMultipleLootChance(); amount += Math.random() < (effect.getMultipleLootChance() - amount) ? 2 : 1; - fishingPreparation.insertArg("{amount}", String.valueOf(amount)); // build the items for multiple times instead of using setAmount() to make sure that each item is unique if (loot.getID().equals("vanilla")) { ItemStack itemStack = vanillaLootMap.remove(player.getUniqueId()); @@ -509,13 +506,13 @@ public class FishingManagerImpl implements Listener, FishingManager { fishingPreparation.insertArg("{loot}", ""); for (int i = 0; i < amount; i++) { plugin.getItemManager().dropItem(hook.getLocation(), player.getLocation(), itemStack.clone()); - doSuccessActions(loot, fishingPreparation, player); + doSuccessActions(loot, effect, fishingPreparation, player); } } } else { for (int i = 0; i < amount; i++) { plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot, fishingPreparation.getArgs()); - doSuccessActions(loot, fishingPreparation, player); + doSuccessActions(loot, effect, fishingPreparation, player); } } return; @@ -523,11 +520,41 @@ public class FishingManagerImpl implements Listener, FishingManager { case MOB -> plugin.getMobManager().summonMob(hook.getLocation(), player.getLocation(), loot); case BLOCK -> plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot); } - doSuccessActions(loot, fishingPreparation, player); + doSuccessActions(loot, effect, fishingPreparation, player); }, hook.getLocation()); } - private void doSuccessActions(Loot loot, FishingPreparation fishingPreparation, Player player) { + private void doSuccessActions(Loot loot, Effect effect, FishingPreparation fishingPreparation, Player player) { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition != null) { + switch (competition.getGoal()) { + case CATCH_AMOUNT -> { + fishingPreparation.insertArg("{score}", "1"); + competition.refreshData(player, 1); + } + case MAX_SIZE, TOTAL_SIZE -> { + String size = fishingPreparation.getArg("{size}"); + if (size != null) { + fishingPreparation.insertArg("{score}", size); + competition.refreshData(player, Double.parseDouble(size)); + } else { + fishingPreparation.insertArg("{score}", "0"); + } + } + case TOTAL_SCORE -> { + double score = loot.getScore(); + if (score != 0) { + fishingPreparation.insertArg("{score}", String.format("%.2f", score * effect.getScoreMultiplier())); + competition.refreshData(player, score * effect.getScoreMultiplier()); + } else { + fishingPreparation.insertArg("{score}", "0"); + } + } + } + } else { + fishingPreparation.insertArg("{score}","-1"); + } + Action[] globalActions = LootManagerImpl.globalLootProperties.getActions(ActionTrigger.SUCCESS); if (globalActions != null) for (Action action : globalActions) @@ -580,10 +607,10 @@ public class FishingManagerImpl implements Listener, FishingManager { } @Override - public void startFishingGame(Player player, GameSettings settings, Game game) { + public void startFishingGame(Player player, GameSettings settings, GameInstance gameInstance) { Optional hook = getHook(player.getUniqueId()); if (hook.isPresent()) { - this.gamingPlayerMap.put(player.getUniqueId(), game.start(player, hook.get(), settings)); + this.gamingPlayerMap.put(player.getUniqueId(), gameInstance.start(player, hook.get(), settings)); } else { LogUtils.warn("It seems that player " + player.getName() + " is not fishing. Fishing game failed to start."); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java index 83fd088a..bb502f4c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java @@ -161,8 +161,8 @@ public class HookCheckTimerTask implements Runnable { Loot nextLoot = manager.getNextLoot(initialEffect, fishingPreparation); if (nextLoot == null) return; - fishingPreparation.insertArg("loot", nextLoot.getNick()); - fishingPreparation.insertArg("id", nextLoot.getID()); + fishingPreparation.insertArg("{nick}", nextLoot.getNick()); + fishingPreparation.insertArg("{loot}", nextLoot.getID()); CustomFishingPlugin.get().getScheduler().runTaskAsync(() -> manager.setTempFishingState(fishingPreparation.getPlayer(), new TempFishingState( initialEffect, fishingPreparation, diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java index 0f5d9136..4b974208 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java @@ -40,8 +40,8 @@ import java.util.concurrent.TimeUnit; public class GameManagerImpl implements GameManager { private final CustomFishingPlugin plugin; - private final HashMap gameCreatorMap; - private final HashMap gameMap; + private final HashMap gameCreatorMap; + private final HashMap gameMap; private final HashMap gameConfigMap; private final String EXPANSION_FOLDER = "expansions/minigames"; @@ -76,11 +76,11 @@ public class GameManagerImpl implements GameManager { } @Override - public boolean registerGameType(String type, GameCreator gameCreator) { + public boolean registerGameType(String type, GameFactory gameFactory) { if (gameCreatorMap.containsKey(type)) return false; else - gameCreatorMap.put(type, gameCreator); + gameCreatorMap.put(type, gameFactory); return true; } @@ -91,13 +91,13 @@ public class GameManagerImpl implements GameManager { @Override @Nullable - public GameCreator getGameCreator(String type) { + public GameFactory getGameCreator(String type) { return gameCreatorMap.get(type); } @Override @Nullable - public Game getGame(String key) { + public GameInstance getGame(String key) { return gameMap.get(key); } @@ -108,9 +108,9 @@ public class GameManagerImpl implements GameManager { } @Override - public Game getRandomGame() { - Collection collection = gameMap.values(); - return (Game) collection.toArray()[ThreadLocalRandom.current().nextInt(collection.size())]; + public GameInstance getRandomGame() { + Collection collection = gameMap.values(); + return (GameInstance) collection.toArray()[ThreadLocalRandom.current().nextInt(collection.size())]; } @Override @@ -164,7 +164,7 @@ public class GameManagerImpl implements GameManager { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : config.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection section) { - GameCreator creator = this.getGameCreator(section.getString("game-type")); + GameFactory creator = this.getGameCreator(section.getString("game-type")); if (creator != null) { gameMap.put(entry.getKey(), creator.setArgs(section)); } @@ -489,13 +489,12 @@ public class GameManagerImpl implements GameManager { } } } - try { for (Class expansionClass : classes) { GameExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); unregisterGameType(expansion.getGameType()); - registerGameType(expansion.getGameType(), expansion.getGameCreator()); - LogUtils.info("Loaded expansion: " + expansion.getGameType() + " made by " + expansion.getAuthor() + "[" + expansion.getVersion() + "]"); + registerGameType(expansion.getGameType(), expansion.getGameFactory()); + LogUtils.info("Loaded minigame expansion: " + expansion.getGameType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); } } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { LogUtils.warn("Error occurred when creating expansion instance.", e); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java index be48b10a..0f4a355e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java @@ -25,10 +25,13 @@ import net.momirealms.customfishing.api.integration.SeasonInterface; import net.momirealms.customfishing.api.manager.RequirementManager; import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.game.GameExpansion; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import net.momirealms.customfishing.api.mechanic.requirement.RequirementBuilder; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementExpansion; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.compatibility.papi.ParseUtils; +import net.momirealms.customfishing.util.ClassUtils; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; @@ -37,14 +40,18 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; public class RequirementManagerImpl implements RequirementManager { public static Requirement[] mechanicRequirements; private final CustomFishingPluginImpl plugin; - private final HashMap requirementBuilderMap; + private final HashMap requirementBuilderMap; private final LinkedHashMap conditionalLootsMap; + private final String EXPANSION_FOLDER = "expansions/requirements"; public RequirementManagerImpl(CustomFishingPluginImpl plugin) { this.plugin = plugin; @@ -54,6 +61,7 @@ public class RequirementManagerImpl implements RequirementManager { } public void load() { + this.loadExpansions(); this.loadRequirementGroupFileConfig(); } @@ -79,9 +87,9 @@ public class RequirementManagerImpl implements RequirementManager { } @Override - public boolean registerRequirement(String type, RequirementBuilder requirementBuilder) { + public boolean registerRequirement(String type, RequirementFactory requirementFactory) { if (this.requirementBuilderMap.containsKey(type)) return false; - this.requirementBuilderMap.put(type, requirementBuilder); + this.requirementBuilderMap.put(type, requirementFactory); return true; } @@ -212,7 +220,7 @@ public class RequirementManagerImpl implements RequirementManager { } @Override - public RequirementBuilder getRequirementBuilder(String type) { + public RequirementFactory getRequirementBuilder(String type) { return requirementBuilderMap.get(type); } @@ -724,4 +732,34 @@ public class RequirementManagerImpl implements RequirementManager { for (Action action : actions) action.trigger(condition); } + + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, RequirementExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterRequirement(expansion.getRequirementType()); + registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); + LogUtils.info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java new file mode 100644 index 00000000..7b315e65 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java @@ -0,0 +1,96 @@ +/* + * 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.mechanic.statistic; + +import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.data.user.OnlineUser; +import net.momirealms.customfishing.api.manager.StatisticsManager; +import net.momirealms.customfishing.api.mechanic.statistic.Statistics; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; + +public class StatisticsManagerImpl implements StatisticsManager { + + private final CustomFishingPlugin plugin; + private final HashMap> categoryMap; + + public StatisticsManagerImpl(CustomFishingPlugin plugin) { + this.plugin = plugin; + this.categoryMap = new HashMap<>(); + } + + public void load() { + this.loadCategoriesFromPluginFolder(); + } + + public void unload() { + this.categoryMap.clear(); + } + + public void disable() { + unload(); + } + + @Override + public Statistics getStatistics(UUID uuid) { + OnlineUser onlineUser = plugin.getStorageManager().getOnlineUser(uuid); + if (onlineUser == null) return null; + return onlineUser.getStatistics(); + } + + @SuppressWarnings("DuplicatedCode") + public void loadCategoriesFromPluginFolder() { + Deque fileDeque = new ArrayDeque<>(); + for (String type : List.of("categories")) { + File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); + if (!typeFolder.exists()) { + if (!typeFolder.mkdirs()) return; + plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); + } + fileDeque.push(typeFolder); + while (!fileDeque.isEmpty()) { + File file = fileDeque.pop(); + File[] files = file.listFiles(); + if (files == null) continue; + for (File subFile : files) { + if (subFile.isDirectory()) { + fileDeque.push(subFile); + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { + this.loadSingleFile(subFile); + } + } + } + } + } + + private void loadSingleFile(File file) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String key : config.getKeys(false)) { + categoryMap.put(key, config.getStringList(key)); + } + } + + @Override + @Nullable + public List getCategory(String key) { + return categoryMap.get(key); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/setting/Locale.java b/plugin/src/main/java/net/momirealms/customfishing/setting/Locale.java index 5e1faca0..70330a06 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/setting/Locale.java +++ b/plugin/src/main/java/net/momirealms/customfishing/setting/Locale.java @@ -51,6 +51,10 @@ public class Locale { public static String MSG_Give_Item; public static String MSG_Never_Played; public static String MSG_Unsafe_Modification; + public static String FORMAT_Day; + public static String FORMAT_Hour; + public static String FORMAT_Minute; + public static String FORMAT_Second; public static void load() { try { diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java index b3c4d3e8..b98e9fc0 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java @@ -26,6 +26,7 @@ public class ClassUtils { URL jar = file.toURI().toURL(); URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); List matches = new ArrayList<>(); + List> classes = new ArrayList<>(); try (JarInputStream stream = new JarInputStream(jar.openStream())) { JarEntry entry; @@ -41,14 +42,16 @@ public class ClassUtils { try { Class loaded = loader.loadClass(match); if (clazz.isAssignableFrom(loaded)) { - loader.close(); - return loaded.asSubclass(clazz); + classes.add(loaded.asSubclass(clazz)); } } catch (NoClassDefFoundError ignored) { } } } - loader.close(); - return null; + if (classes.isEmpty()) { + loader.close(); + return null; + } + return classes.get(0); } } diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index dbabf579..6ea1be57 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -93,8 +93,7 @@ mechanics: competition: # Use redis for cross server data synchronization redis-ranking: false - # Increase this value would increase cpu load - # But would allow you to use more placeholders like {4_player} {5_score} + # Increase this value would allow you to use more placeholders like {4_player} {5_score} in sacrifice of some performance placeholder-limit: 3 # Enable vanilla fishing mechanic if there's no loot available diff --git a/plugin/src/main/resources/contents/categories/default.yml b/plugin/src/main/resources/contents/categories/default.yml new file mode 100644 index 00000000..71b8d971 --- /dev/null +++ b/plugin/src/main/resources/contents/categories/default.yml @@ -0,0 +1,51 @@ +# %fishingstats_amount_% get the number of a certain fish caught +# %fishingstats_hascaught_% return if a player has caught this fish +# %fishingstats_category_total_% get the total number of a certain category's fish caught +# %fishingstats_category_progress_% get the player's exploration of this category of fish +normal_fish: + - tuna_fish + - pike_fish + - gold_fish + - perch_fish + - mullet_fish + - sardine_fish + - carp_fish + - cat_fish + - octopus + - sunfish + - red_snapper_fish + - salmon_void_fish + - woodskip_fish + - sturgeon_fish + +sliver_star_fish: + - tuna_fish_silver_star + - pike_fish_silver_star + - gold_fish_silver_star + - perch_fish_silver_star + - mullet_fish_silver_star + - sardine_fish_silver_star + - carp_fish_silver_star + - cat_fish_silver_star + - octopus_silver_star + - sunfish_silver_star + - red_snapper_fish_silver_star + - salmon_void_fish_silver_star + - woodskip_fish_silver_star + - sturgeon_fish_silver_star + +golden_star_fish: + - tuna_fish_golden_star + - pike_fish_golden_star + - gold_fish_golden_star + - perch_fish_golden_star + - mullet_fish_golden_star + - sardine_fish_golden_star + - carp_fish_golden_star + - cat_fish_golden_star + - octopus_golden_star + - sunfish_golden_star + - red_snapper_fish_golden_star + - salmon_void_fish_golden_star + - woodskip_fish_golden_star + - sturgeon_fish_golden_star \ No newline at end of file diff --git a/plugin/src/main/resources/contents/competitions/default.yml b/plugin/src/main/resources/contents/competitions/default.yml index a3f182ed..e68e7db6 100644 --- a/plugin/src/main/resources/contents/competitions/default.yml +++ b/plugin/src/main/resources/contents/competitions/default.yml @@ -34,9 +34,9 @@ example: color: WHITE overlay: PROGRESS text: - - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{time}s | Your Rank: <#E6E6FA>{rank} | No.1 Player: <#E6E6FA>{1_player}' - - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{minute}m{second}s | Your Score: <#E6E6FA>{score} | No.1 Score: <#E6E6FA>{1_score}' - - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{minute}m{second}s | Winning condition: <#E6E6FA>{goal}' + - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{seconds} | Your Rank: <#E6E6FA>{rank} | No.1 Player: <#E6E6FA>{1_player}' + - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{minute}{second} | Your Score: <#E6E6FA>{score} | No.1 Score: <#E6E6FA>{1_score}' + - '[<#87CEFA>🎣] Time Left: <#E6E6FA>{minute}{second} | Winning condition: <#E6E6FA>{goal}' refresh-rate: 20 switch-interval: 200 only-show-to-participants: true @@ -44,9 +44,9 @@ example: actionbar: enable: false text: - - 'Time Left: <#E6E6FA>{time}s | Your Rank: <#E6E6FA>{rank} | No.1 Player: <#E6E6FA>{1_player}' - - 'Time Left: <#E6E6FA>{minute}m{second}s | Your Score: <#E6E6FA>{score} | No.1 Score: <#E6E6FA>{1_score}' - - 'Time Left: <#E6E6FA>{minute}m{second}s | Winning condition: <#E6E6FA>{goal}' + - 'Time Left: <#E6E6FA>{seconds} | Your Rank: <#E6E6FA>{rank} | No.1 Player: <#E6E6FA>{1_player}' + - 'Time Left: <#E6E6FA>{minute}{second} | Your Score: <#E6E6FA>{score} | No.1 Score: <#E6E6FA>{1_score}' + - 'Time Left: <#E6E6FA>{minute}{second} | Winning condition: <#E6E6FA>{goal}' refresh-rate: 5 switch-interval: 200 only-show-to-participants: true