9
0
mirror of https://github.com/Xiao-MoMi/Custom-Fishing.git synced 2025-12-29 11:59:11 +00:00
This commit is contained in:
XiaoMoMi
2023-11-02 02:30:51 +08:00
parent fbf040573b
commit ee7f225cc5
13 changed files with 169 additions and 229 deletions

Binary file not shown.

View File

@@ -131,8 +131,8 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin {
((RequirementManagerImpl) this.requirementManager).disable();
((SchedulerImpl) this.scheduler).shutdown();
((IntegrationManagerImpl) this.integrationManager).disable();
((StorageManagerImpl) this.storageManager).disable();
((CompetitionManagerImpl) this.competitionManager).disable();
((StorageManagerImpl) this.storageManager).disable();
((PlaceholderManagerImpl) this.placeholderManager).disable();
((StatisticsManagerImpl) this.statisticsManager).disable();
((ActionManagerImpl) this.actionManager).disable();

View File

@@ -55,10 +55,7 @@ public class CompetitionCommand {
ArgumentSuggestions.strings(allCompetitions)
)
);
if (CFConfig.redisRanking) command.withOptionalArguments(new BooleanArgument("servers").replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(new IStringTooltip[]{
StringTooltip.ofString("true", "all the servers that connected to Redis"),
StringTooltip.ofString("false", "only this server")
})));
if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group"));
command.executes((sender, args) -> {
String id = (String) args.get(0);
assert id != null;
@@ -66,22 +63,23 @@ public class CompetitionCommand {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Competition_Not_Exist.replace("{id}", id));
return;
}
boolean allServer = (boolean) args.getOrDefault("servers", false);
CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, allServer);
Object server = args.get("server-group");
if (server != null) {
CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, (String) server);
} else {
CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, null);
}
});
return command;
}
private CommandAPICommand getCompetitionEndCommand() {
var command = new CommandAPICommand("end");
if (CFConfig.redisRanking) command.withOptionalArguments(new BooleanArgument("servers").replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(new IStringTooltip[]{
StringTooltip.ofString("true", "all the servers that connected to Redis"),
StringTooltip.ofString("false", "only this server")
})));
if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group"));
command.executes((sender, args) -> {
boolean allServer = (boolean) args.getOrDefault("servers", false);
if (allServer) {
RedisManager.getInstance().sendRedisMessage("cf_competition", "end");
Object server = args.get("server-group");
if (server != null) {
RedisManager.getInstance().publishRedisMessage((String) server, "end");
} else {
FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition();
if (competition != null) {
@@ -97,14 +95,11 @@ public class CompetitionCommand {
private CommandAPICommand getCompetitionStopCommand() {
var command = new CommandAPICommand("stop");
if (CFConfig.redisRanking) command.withOptionalArguments(new BooleanArgument("servers").replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(new IStringTooltip[]{
StringTooltip.ofString("true", "all the servers that connected to Redis"),
StringTooltip.ofString("false", "only this server")
})));
if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group"));
command.executes((sender, args) -> {
boolean allServer = (boolean) args.getOrDefault("servers", false);
if (allServer) {
RedisManager.getInstance().sendRedisMessage("cf_competition", "stop");
Object server = args.get("server-group");
if (server != null) {
RedisManager.getInstance().publishRedisMessage((String) server, "stop");
} else {
FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition();
if (competition != null) {

View File

@@ -146,6 +146,7 @@ public class IntegrationManagerImpl implements IntegrationManager {
if (plugin.isHookedPluginEnabled("ClueScrolls")) {
ClueScrollsHook clueScrollsHook = new ClueScrollsHook();
clueScrollsHook.register();
Bukkit.getPluginManager().registerEvents(clueScrollsHook, plugin);
hookMessage("ClueScrolls");
}
if (plugin.isHookedPluginEnabled("BetonQuest")) {

View File

@@ -49,21 +49,21 @@ public class ClueScrollsHook implements Listener {
idClue.handle(
player,
event.getAmount(),
new ClueDataPair("loot_id", "any")
new ClueDataPair("id", "any")
);
Loot loot = event.getLoot();
if (loot != null)
idClue.handle(
player,
event.getAmount(),
new ClueDataPair("loot_id", loot.getID())
new ClueDataPair("id", loot.getID())
);
if (loot != null && loot.getLootGroup() != null)
for (String group : event.getLoot().getLootGroup()) {
groupClue.handle(
player,
event.getAmount(),
new ClueDataPair("loot_group", group)
new ClueDataPair("group", group)
);
}
}

View File

@@ -1,8 +0,0 @@
package net.momirealms.customfishing.gui;
import org.bukkit.configuration.ConfigurationSection;
public interface ItemPage extends YamlPage {
ConfigurationSection getSection();
}

View File

@@ -1,136 +0,0 @@
package net.momirealms.customfishing.gui.page.item;
import net.momirealms.customfishing.adventure.AdventureManagerImpl;
import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper;
import net.momirealms.customfishing.api.CustomFishingPlugin;
import net.momirealms.customfishing.gui.ItemPage;
import net.momirealms.customfishing.gui.icon.BackGroundItem;
import net.momirealms.customfishing.gui.icon.BackToPageItem;
import net.momirealms.customfishing.gui.icon.NextPageItem;
import net.momirealms.customfishing.gui.icon.PreviousPageItem;
import net.momirealms.customfishing.gui.icon.property.item.*;
import net.momirealms.customfishing.gui.icon.property.loot.*;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.invui.gui.Gui;
import xyz.xenondevs.invui.gui.PagedGui;
import xyz.xenondevs.invui.gui.structure.Markers;
import xyz.xenondevs.invui.item.Item;
import xyz.xenondevs.invui.item.ItemProvider;
import xyz.xenondevs.invui.item.builder.ItemBuilder;
import xyz.xenondevs.invui.item.impl.AbstractItem;
import xyz.xenondevs.invui.item.impl.SimpleItem;
import xyz.xenondevs.invui.window.AnvilWindow;
import java.util.ArrayList;
import java.util.List;
public class ItemEditor implements ItemPage {
private final Player player;
private final ItemSelector itemSelector;
private final ConfigurationSection section;
private final String key;
public ItemEditor(Player player, String key, ItemSelector itemSelector, ConfigurationSection section) {
this.player = player;
this.section = section;
this.itemSelector = itemSelector;
this.key = key;
this.reOpen();
}
@Override
public void reOpen() {
Gui upperGui = Gui.normal()
.setStructure(
"# a #"
)
.addIngredient('a', new RefreshExample())
.addIngredient('#', new ItemStack(Material.AIR))
.build();
var gui = PagedGui.items()
.setStructure(
"x x x x x x x x x",
"x x x x x x x x x",
"x x x x x x x x x",
"# # a # c # b # #"
)
.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
.addIngredient('#', new BackGroundItem())
.addIngredient('a', new PreviousPageItem())
.addIngredient('b', new NextPageItem())
.addIngredient('c', new BackToPageItem(itemSelector))
.setContent(getItemList())
.build();
var window = AnvilWindow.split()
.setViewer(player)
.setTitle(new ShadedAdventureComponentWrapper(
AdventureManagerImpl.getInstance().getComponentFromMiniMessage("Edit " + key)
))
.setUpperGui(upperGui)
.setLowerGui(gui)
.build();
window.open();
}
@Override
public void save() {
itemSelector.save();
}
public class RefreshExample extends AbstractItem {
@Override
public ItemProvider getItemProvider() {
return new ItemBuilder(CustomFishingPlugin.get().getItemManager().getItemBuilder(section, "item", key).build(player));
}
@Override
public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) {
notifyWindows();
}
}
public List<Item> getItemList() {
ArrayList<Item> items = new ArrayList<>();
items.add(new MaterialItem(this));
items.add(new NickItem(this));
items.add(new DisplayNameItem(this));
items.add(new LoreItem(this));
items.add(new CMDItem(this));
items.add(new AmountItem(this));
items.add(new TagItem(this));
items.add(new UnbreakableItem(this));
items.add(new DurabilityItem(this));
items.add(new RandomDurabilityItem(this));
items.add(new StackableItem(this));
items.add(new PreventGrabItem(this));
items.add(new PriceItem(this));
items.add(new ShowInFinderItem(this));
items.add(new DisableStatsItem(this));
items.add(new DisableGameItem(this));
items.add(new InstantGameItem(this));
items.add(new ScoreItem(this));
items.add(new SizeItem(this));
items.add(new ItemFlagItem(this));
items.add(new Head64Item(this));
items.add(new NBTItem(this));
items.add(new EnchantmentItem(this));
items.add(new StoredEnchantmentItem(this));
return items;
}
@Override
public ConfigurationSection getSection() {
return section;
}
}

View File

@@ -25,6 +25,7 @@ import net.momirealms.customfishing.api.mechanic.competition.*;
import net.momirealms.customfishing.api.mechanic.condition.Condition;
import net.momirealms.customfishing.api.scheduler.CancellableTask;
import net.momirealms.customfishing.api.util.LogUtils;
import net.momirealms.customfishing.setting.CFConfig;
import net.momirealms.customfishing.setting.CFLocale;
import net.momirealms.customfishing.storage.method.database.nosql.RedisManager;
import net.momirealms.customfishing.util.ConfigUtils;
@@ -32,7 +33,6 @@ import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -210,7 +210,7 @@ public class CompetitionManagerImpl implements CompetitionManager {
this.nextCompetitionSeconds = nextCompetitionTime;
CompetitionConfig config = timeConfigMap.get(competitionSchedule);
if (config != null) {
startCompetition(config, false, false);
startCompetition(config, false, null);
}
}
@@ -240,22 +240,14 @@ public class CompetitionManagerImpl implements CompetitionManager {
return "";
}
/**
* Starts a competition with the specified name, allowing for the option to force start it or apply it to the entire server.
*
* @param competition The name of the competition to start.
* @param force Whether to force start the competition even if amount of the online players is lower than the requirement
* @param allServer Whether to apply the competition to the servers that connected to Redis.
* @return {@code true} if the competition was started successfully, {@code false} otherwise.
*/
@Override
public boolean startCompetition(String competition, boolean force, boolean allServer) {
public boolean startCompetition(String competition, boolean force, String serverGroup) {
CompetitionConfig config = commandConfigMap.get(competition);
if (config == null) {
LogUtils.warn("Competition " + competition + " doesn't exist.");
return false;
}
return startCompetition(config, force, allServer);
return startCompetition(config, force, serverGroup);
}
/**
@@ -270,16 +262,8 @@ public class CompetitionManagerImpl implements CompetitionManager {
return currentCompetition.isOnGoing() ? currentCompetition : null;
}
/**
* Starts a competition using the specified configuration.
*
* @param config The configuration of the competition to start.
* @param force Whether to force the start of the competition.
* @param allServer Whether the competition should start across all servers in the network.
* @return True if the competition was started successfully, false otherwise.
*/
@Override
public boolean startCompetition(CompetitionConfig config, boolean force, boolean allServer) {
public boolean startCompetition(CompetitionConfig config, boolean force, @Nullable String serverGroup) {
if (!force) {
int players = Bukkit.getOnlinePlayers().size();
if (players < config.getMinPlayersToStart()) {
@@ -294,11 +278,11 @@ public class CompetitionManagerImpl implements CompetitionManager {
}
start(config);
return true;
} else if (!allServer) {
} else if (serverGroup == null) {
start(config);
return true;
} else {
RedisManager.getInstance().sendRedisMessage("cf_competition", "start;" + config.getKey());
RedisManager.getInstance().publishRedisMessage(serverGroup, "start;" + config.getKey());
return true;
}
}

View File

@@ -26,6 +26,7 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import net.momirealms.customfishing.api.CustomFishingPlugin;
import net.momirealms.customfishing.api.util.LogUtils;
import net.momirealms.customfishing.api.util.OffsetUtils;
import net.momirealms.customfishing.util.ConfigUtils;
import org.bukkit.Material;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventPriority;
@@ -75,6 +76,7 @@ public class CFConfig {
// Competition
public static boolean redisRanking;
public static List<String> serverGroup;
public static int placeholderLimit;
// Data save interval
@@ -146,6 +148,7 @@ public class CFConfig {
redisRanking = config.getBoolean("mechanics.competition.redis-ranking", false);
placeholderLimit = config.getInt("mechanics.competition.placeholder-limit", 3);
serverGroup = ConfigUtils.stringListArgs(config.get("mechanics.competition.server-group","default"));
dataSaveInterval = config.getInt("other-settings.data-saving-interval", 600);
lockData = config.getBoolean("other-settings.lock-data", true);

View File

@@ -21,19 +21,21 @@ import net.momirealms.customfishing.api.CustomFishingPlugin;
import net.momirealms.customfishing.api.data.PlayerData;
import net.momirealms.customfishing.api.data.StorageType;
import net.momirealms.customfishing.api.util.LogUtils;
import net.momirealms.customfishing.setting.CFConfig;
import net.momirealms.customfishing.storage.method.AbstractStorage;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CompletableFuture;
/**
@@ -42,11 +44,14 @@ import java.util.concurrent.CompletableFuture;
public class RedisManager extends AbstractStorage {
private static RedisManager instance;
private final static String STREAM = "customfishing";
private JedisPool jedisPool;
private String password;
private int port;
private String host;
private boolean useSSL;
private BlockingThreadTask threadTask;
private boolean isNewerThan5;
public RedisManager(CustomFishingPlugin plugin) {
super(plugin);
@@ -103,14 +108,25 @@ public class RedisManager extends AbstractStorage {
} else {
jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, password, useSSL);
}
String info;
try (Jedis jedis = jedisPool.getResource()) {
jedis.ping();
info = jedis.info();
LogUtils.info("Redis server connected.");
} catch (JedisException e) {
LogUtils.warn("Failed to connect redis.", e);
return;
}
subscribe();
String version = parseRedisVersion(info);
if (isRedisNewerThan5(version)) {
// For Redis 5.0+
this.threadTask = new BlockingThreadTask();
this.isNewerThan5 = true;
} else {
// For Redis 2.0+
this.subscribe();
this.isNewerThan5 = false;
}
}
/**
@@ -118,6 +134,8 @@ public class RedisManager extends AbstractStorage {
*/
@Override
public void disable() {
if (threadTask != null)
threadTask.stop();
if (jedisPool != null && !jedisPool.isClosed())
jedisPool.close();
}
@@ -125,13 +143,22 @@ public class RedisManager extends AbstractStorage {
/**
* Send a message to Redis on a specified channel.
*
* @param channel The Redis channel to send the message to.
* @param server The Redis channel to send the message to.
* @param message The message to send.
*/
public void sendRedisMessage(@NotNull String channel, @NotNull String message) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.publish(channel, message);
plugin.debug("Sent Redis message: " + message);
public void publishRedisMessage(@NotNull String server, @NotNull String message) {
message = server + ";" + message;
plugin.debug("Sent Redis message: " + message);
if (isNewerThan5) {
try (Jedis jedis = jedisPool.getResource()) {
HashMap<String, String> messages = new HashMap<>();
messages.put("value", message);
jedis.xadd(getStream(), StreamEntryID.NEW_ENTRY, messages);
}
} else {
try (Jedis jedis = jedisPool.getResource()) {
jedis.publish(getStream(), message);
}
}
}
@@ -153,33 +180,43 @@ public class RedisManager extends AbstractStorage {
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
if (!channel.equals("cf_competition")) {
if (!channel.equals(getStream())) {
return;
}
plugin.debug("Received Redis message: " + message);
String[] split = message.split(";");
String action = split[0];
switch (action) {
case "start" -> {
// start competition for all the servers that connected to redis
plugin.getCompetitionManager().startCompetition(split[1], true, false);
}
case "end" -> {
if (plugin.getCompetitionManager().getOnGoingCompetition() != null)
plugin.getCompetitionManager().getOnGoingCompetition().end();
}
case "stop" -> {
if (plugin.getCompetitionManager().getOnGoingCompetition() != null)
plugin.getCompetitionManager().getOnGoingCompetition().stop();
}
}
handleMessage(message);
}
}, "cf_competition");
}, getStream());
}
});
thread.start();
}
private static void handleMessage(String message) {
CustomFishingPlugin.get().debug("Received Redis message: " + message);
String[] split = message.split(";");
String server = split[0];
if (!CFConfig.serverGroup.contains(server)) {
return;
}
String action = split[1];
CustomFishingPlugin.get().getScheduler().runTaskSync(() -> {
switch (action) {
case "start" -> {
// start competition for all the servers that connected to redis
CustomFishingPlugin.get().getCompetitionManager().startCompetition(split[2], true, null);
}
case "end" -> {
if (CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition() != null)
CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition().end();
}
case "stop" -> {
if (CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition() != null)
CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition().stop();
}
}
}, new Location(Bukkit.getWorlds().get(0),0,0,0));
}
@Override
public StorageType getStorageType() {
return StorageType.Redis;
@@ -226,7 +263,6 @@ public class RedisManager extends AbstractStorage {
future.complete(false);
plugin.debug("Server data retrieved for " + uuid + "; value: false");
}
}
});
return future;
@@ -312,4 +348,70 @@ public class RedisManager extends AbstractStorage {
private byte[] getRedisKey(String key, @NotNull UUID uuid) {
return (key + ":" + uuid).getBytes(StandardCharsets.UTF_8);
}
public static String getStream() {
return STREAM;
}
private static boolean isRedisNewerThan5(String version) {
String[] split = version.split("\\.");
int major = Integer.parseInt(split[0]);
if (major < 7) {
LogUtils.warn(String.format("Detected that you are running an outdated Redis server. v%s. ", version));
LogUtils.warn("It's recommended to update to avoid security vulnerabilities!");
}
return major >= 5;
}
private static String parseRedisVersion(String info) {
for (String line : info.split("\n")) {
if (line.startsWith("redis_version:")) {
return line.split(":")[1];
}
}
return "Unknown";
}
public class BlockingThreadTask {
private boolean stopped;
public void stop() {
stopped = true;
}
public BlockingThreadTask() {
Thread thread = new Thread(() -> {
var map = new HashMap<String, StreamEntryID>();
map.put(getStream(), StreamEntryID.LAST_ENTRY);
while (!this.stopped) {
try {
var connection = getJedis();
if (connection != null) {
var messages = connection.xread(XReadParams.xReadParams().count(1).block(2000), map);
connection.close();
if (messages != null && messages.size() != 0) {
for (Map.Entry<String, List<StreamEntry>> message : messages) {
if (message.getKey().equals(getStream())) {
var value = message.getValue().get(0).getFields().get("value");
handleMessage(value);
}
}
}
} else {
Thread.sleep(2000);
}
} catch (Exception e) {
LogUtils.warn("Failed to connect redis. Try reconnecting 10s later",e);
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
this.stopped = true;
}
}
}
});
thread.start();
}
}
}

View File

@@ -125,6 +125,8 @@ mechanics:
competition:
# Use redis for cross server data synchronization
redis-ranking: false
# Server group
server-group: default
# Increase this value would allow you to use more placeholders like {4_player} {5_score} in sacrifice of some performance
placeholder-limit: 3

View File

@@ -13,9 +13,6 @@
#
data-storage-method: H2
# Server unique id, make sure that all the servers are using different ids
server-unique-id: "plz_edit_this"
SQLite:
file: 'sqlite'
table-prefix: customfishing