9
0
mirror of https://github.com/Xiao-MoMi/Custom-Fishing.git synced 2025-12-29 11:59:11 +00:00

Updated expansions & placeholders

This commit is contained in:
XiaoMoMi
2023-09-06 22:23:30 +08:00
parent 5b71284d0b
commit 20b4a73716
41 changed files with 697 additions and 231 deletions

View File

@@ -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();

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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";
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.compatibility.papi;
public class MiniPlaceholdersHook {
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -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<String, String> 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() {

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<String> 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";
}
}

View File

@@ -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<String, ActionBuilder> actionBuilderMap;
private final HashMap<String, ActionFactory> 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<Class<? extends ActionExpansion>> classes = new ArrayList<>();
File[] expansionJars = expansionFolder.listFiles();
if (expansionJars == null) return;
for (File expansionJar : expansionJars) {
if (expansionJar.getName().endsWith(".jar")) {
try {
Class<? extends ActionExpansion> 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<? extends ActionExpansion> 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);
}
}
}

View File

@@ -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<UUID, FishingBagHolder> 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<WrappedChatComponent> 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<String, String> 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)
))));
}
}
}
}

View File

@@ -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<String, String> getPublicPlaceholders() {
@Override
public ConcurrentHashMap<String, String> 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);
}
}

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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}", "<lang:item.minecraft." + itemStack.getType().toString().toLowerCase() + ">");
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<FishHook> 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.");
}

View File

@@ -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,

View File

@@ -40,8 +40,8 @@ import java.util.concurrent.TimeUnit;
public class GameManagerImpl implements GameManager {
private final CustomFishingPlugin plugin;
private final HashMap<String, GameCreator> gameCreatorMap;
private final HashMap<String, Game> gameMap;
private final HashMap<String, GameFactory> gameCreatorMap;
private final HashMap<String, GameInstance> gameMap;
private final HashMap<String, GameConfig> 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<Game> collection = gameMap.values();
return (Game) collection.toArray()[ThreadLocalRandom.current().nextInt(collection.size())];
public GameInstance getRandomGame() {
Collection<GameInstance> 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<String, Object> 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<? extends GameExpansion> 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);

View File

@@ -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<String, RequirementBuilder> requirementBuilderMap;
private final HashMap<String, RequirementFactory> requirementBuilderMap;
private final LinkedHashMap<String, ConditionalLoots> 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<Class<? extends RequirementExpansion>> classes = new ArrayList<>();
File[] expansionJars = expansionFolder.listFiles();
if (expansionJars == null) return;
for (File expansionJar : expansionJars) {
if (expansionJar.getName().endsWith(".jar")) {
try {
Class<? extends RequirementExpansion> 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<? extends RequirementExpansion> 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);
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<String, List<String>> 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<File> 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<String> getCategory(String key) {
return categoryMap.get(key);
}
}

View File

@@ -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 {

View File

@@ -26,6 +26,7 @@ public class ClassUtils {
URL jar = file.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader());
List<String> matches = new ArrayList<>();
List<Class<? extends T>> 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);
}
}

View File

@@ -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

View File

@@ -0,0 +1,51 @@
# %fishingstats_amount_<id>% get the number of a certain fish caught
# %fishingstats_hascaught_<id>% return if a player has caught this fish
# %fishingstats_category_total_<id>% get the total number of a certain category's fish caught
# %fishingstats_category_progress_<id>% 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

View File

@@ -34,9 +34,9 @@ example:
color: WHITE
overlay: PROGRESS
text:
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{time}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Rank: <#E6E6FA>{rank} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Player: <#E6E6FA>{1_player}'
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}m{second}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Score: <#E6E6FA>{score} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Score: <#E6E6FA>{1_score}'
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}m{second}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Winning condition: <#E6E6FA>{goal}'
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{seconds} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Rank: <#E6E6FA>{rank} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Player: <#E6E6FA>{1_player}'
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}{second} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Score: <#E6E6FA>{score} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Score: <#E6E6FA>{1_score}'
- '<gray>[<#87CEFA>🎣<gray>] <gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}{second} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Winning condition: <#E6E6FA>{goal}'
refresh-rate: 20
switch-interval: 200
only-show-to-participants: true
@@ -44,9 +44,9 @@ example:
actionbar:
enable: false
text:
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{time}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Rank: <#E6E6FA>{rank} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Player: <#E6E6FA>{1_player}'
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}m{second}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Score: <#E6E6FA>{score} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Score: <#E6E6FA>{1_score}'
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}m{second}s <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Winning condition: <#E6E6FA>{goal}'
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{seconds} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Rank: <#E6E6FA>{rank} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Player: <#E6E6FA>{1_player}'
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}{second} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Your Score: <#E6E6FA>{score} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>No.1 Score: <#E6E6FA>{1_score}'
- '<gradient:#F0F8FF:#87CEFA:#F0F8FF>Time Left: <#E6E6FA>{minute}{second} <gray>| <gradient:#F0F8FF:#87CEFA:#F0F8FF>Winning condition: <#E6E6FA>{goal}'
refresh-rate: 5
switch-interval: 200
only-show-to-participants: true