mirror of
https://github.com/Xiao-MoMi/Custom-Nameplates.git
synced 2026-01-06 15:42:00 +00:00
migration system
This commit is contained in:
@@ -32,6 +32,8 @@ dependencies {
|
||||
compileOnly("com.github.LoneDev6:api-itemsadder:3.5.0c-r5")
|
||||
compileOnly("io.th0rgal:oraxen:1.165.0")
|
||||
compileOnly("com.github.FrancoBM12:API-MagicCosmetics:2.2.5")
|
||||
compileOnly("commons-io:commons-io:2.15.1")
|
||||
|
||||
|
||||
// chat channels
|
||||
compileOnly(files("libs/VentureChat-3.7.1.jar"))
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
package net.momirealms.customnameplates.paper;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.event.CustomNameplatesReloadEvent;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
@@ -42,13 +41,12 @@ import net.momirealms.customnameplates.paper.scheduler.SchedulerImpl;
|
||||
import net.momirealms.customnameplates.paper.setting.CNConfig;
|
||||
import net.momirealms.customnameplates.paper.setting.CNLocale;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageManagerImpl;
|
||||
import net.momirealms.customnameplates.paper.util.Migration;
|
||||
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.TimeZone;
|
||||
@@ -66,6 +64,11 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (Migration.check()) {
|
||||
LogUtils.warn("Please read /CustomNameplates/README.txt to finish the migration.");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
this.adventureManager = new AdventureManagerImpl(this);
|
||||
this.versionManager = new VersionManagerImpl(this);
|
||||
this.scheduler = new SchedulerImpl(this);
|
||||
@@ -94,21 +97,21 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
((SchedulerImpl) this.scheduler).shutdown();
|
||||
((ActionBarManagerImpl) actionBarManager).unload();
|
||||
((NameplateManagerImpl) this.nameplateManager).disable();
|
||||
((TeamManagerImpl) this.teamManager).unload();
|
||||
((BossBarManagerImpl) this.bossBarManager).unload();
|
||||
((ImageManagerImpl) this.imageManager).unload();
|
||||
((BackGroundManagerImpl) this.backGroundManager).unload();
|
||||
((PlaceholderManagerImpl) this.placeholderManager).unload();
|
||||
((BubbleManagerImpl) this.bubbleManager).unload();
|
||||
((RequirementManagerImpl) this.requirementManager).unload();
|
||||
((ResourcePackManagerImpl) this.resourcePackManager).unload();
|
||||
((WidthManagerImpl) this.widthManager).unload();
|
||||
((StorageManagerImpl) this.storageManager).disable();
|
||||
((AdventureManagerImpl) this.adventureManager).close();
|
||||
HandlerList.unregisterAll((VersionManagerImpl) versionManager);
|
||||
if (scheduler != null) ((SchedulerImpl) this.scheduler).shutdown();
|
||||
if (actionBarManager != null) ((ActionBarManagerImpl) actionBarManager).unload();
|
||||
if (nameplateManager != null) ((NameplateManagerImpl) this.nameplateManager).disable();
|
||||
if (teamManager != null) ((TeamManagerImpl) this.teamManager).unload();
|
||||
if (bossBarManager != null) ((BossBarManagerImpl) this.bossBarManager).unload();
|
||||
if (imageManager != null) ((ImageManagerImpl) this.imageManager).unload();
|
||||
if (backGroundManager != null) ((BackGroundManagerImpl) this.backGroundManager).unload();
|
||||
if (placeholderManager != null) ((PlaceholderManagerImpl) this.placeholderManager).unload();
|
||||
if (bubbleManager != null) ((BubbleManagerImpl) this.bubbleManager).unload();
|
||||
if (requirementManager != null) ((RequirementManagerImpl) this.requirementManager).unload();
|
||||
if (resourcePackManager != null) ((ResourcePackManagerImpl) this.resourcePackManager).unload();
|
||||
if (widthManager != null) ((WidthManagerImpl) this.widthManager).unload();
|
||||
if (storageManager != null) ((StorageManagerImpl) this.storageManager).disable();
|
||||
if (adventureManager != null) ((AdventureManagerImpl) this.adventureManager).close();
|
||||
if (versionManager != null) HandlerList.unregisterAll((VersionManagerImpl) versionManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,7 +161,7 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
|
||||
"com.zaxxer:HikariCP:5.0.1", mavenRepo,
|
||||
"org.mariadb.jdbc:mariadb-java-client:3.3.0", mavenRepo,
|
||||
"com.mysql:mysql-connector-j:8.2.0", mavenRepo,
|
||||
"commons-io:commons-io:2.14.0", mavenRepo,
|
||||
"commons-io:commons-io:2.15.1", mavenRepo,
|
||||
"com.google.code.gson:gson:2.10.1", mavenRepo,
|
||||
"com.h2database:h2:2.2.224", mavenRepo,
|
||||
"org.mongodb:mongodb-driver-sync:4.11.1", mavenRepo,
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
|
||||
package net.momirealms.customnameplates.paper.command;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import dev.jorel.commandapi.CommandAPI;
|
||||
import dev.jorel.commandapi.CommandAPIBukkitConfig;
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
@@ -25,21 +29,36 @@ import dev.jorel.commandapi.arguments.BooleanArgument;
|
||||
import dev.jorel.commandapi.arguments.PlayerArgument;
|
||||
import dev.jorel.commandapi.arguments.StringArgument;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.data.DataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.OnlineUser;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.mechanic.bubble.Bubble;
|
||||
import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer;
|
||||
import net.momirealms.customnameplates.api.util.CompletableFutures;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customnameplates.paper.setting.CNConfig;
|
||||
import net.momirealms.customnameplates.paper.setting.CNLocale;
|
||||
import net.momirealms.customnameplates.paper.storage.method.database.sql.MariaDBImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.method.database.sql.MySQLImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.method.file.YAMLImpl;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class CommandManager {
|
||||
@@ -57,7 +76,8 @@ public class CommandManager {
|
||||
.withAliases("nameplates", "cnameplates")
|
||||
.withSubcommands(
|
||||
NameplatesCommands.getReloadCommand(),
|
||||
NameplatesCommands.getAboutCommand()
|
||||
NameplatesCommands.getAboutCommand(),
|
||||
NameplatesCommands.getDataCommand()
|
||||
);
|
||||
if (CNConfig.nameplateModule) {
|
||||
command1.withSubcommands(
|
||||
@@ -332,5 +352,191 @@ public class CommandManager {
|
||||
AdventureManagerImpl.getInstance().sendMessage(sender, "<#FFD700>⭐ <click:open_url:https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates>Document</click> <#A9A9A9>| <#FAFAD2>⛏ <click:open_url:https://github.com/Xiao-MoMi/Custom-Nameplates>Github</click> <#A9A9A9>| <#48D1CC>\uD83D\uDD14 <click:open_url:https://polymart.org/resource/customnameplates.2543>Polymart</click>");
|
||||
});
|
||||
}
|
||||
|
||||
public static CommandAPICommand getDataCommand() {
|
||||
return new CommandAPICommand("data")
|
||||
.withPermission("customnameplates.admin")
|
||||
.withSubcommands(
|
||||
new CommandAPICommand("export")
|
||||
.executes((sender, args) -> {
|
||||
CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting <aqua>export</aqua>.");
|
||||
DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource();
|
||||
|
||||
Set<UUID> uuids = dataStorageInterface.getUniqueUsers(false);
|
||||
Set<CompletableFuture<Void>> futures = new HashSet<>();
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
Map<UUID, String> out = Collections.synchronizedMap(new TreeMap<>());
|
||||
|
||||
int amount = uuids.size();
|
||||
for (UUID uuid : uuids) {
|
||||
futures.add(dataStorageInterface.getPlayerData(uuid).thenAccept(it -> {
|
||||
if (it.isPresent()) {
|
||||
out.put(uuid, plugin.getStorageManager().toJson(it.get()));
|
||||
userCount.incrementAndGet();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> overallFuture = CompletableFutures.allOf(futures);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
overallFuture.get(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (TimeoutException e) {
|
||||
LogUtils.info("Progress: " + userCount.get() + "/" + amount);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
JsonObject outJson = new JsonObject();
|
||||
for (Map.Entry<UUID, String> entry : out.entrySet()) {
|
||||
outJson.addProperty(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
|
||||
String formattedDate = formatter.format(new Date());
|
||||
File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz");
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) {
|
||||
new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed.");
|
||||
});
|
||||
}),
|
||||
new CommandAPICommand("export-legacy")
|
||||
.withArguments(new StringArgument("method")
|
||||
.replaceSuggestions(ArgumentSuggestions.strings("MySQL", "MariaDB", "YAML")))
|
||||
.executes((sender, args) -> {
|
||||
String arg = (String) args.get("method");
|
||||
if (arg == null) return;
|
||||
CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting <aqua>export</aqua>.");
|
||||
|
||||
LegacyDataStorageInterface dataStorageInterface;
|
||||
switch (arg) {
|
||||
case "MySQL" -> dataStorageInterface = new MySQLImpl(plugin);
|
||||
case "MariaDB" -> dataStorageInterface = new MariaDBImpl(plugin);
|
||||
case "YAML" -> dataStorageInterface = new YAMLImpl(plugin);
|
||||
default -> {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "No such legacy storage method.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dataStorageInterface.initialize();
|
||||
Set<UUID> uuids = dataStorageInterface.getUniqueUsers(true);
|
||||
Set<CompletableFuture<Void>> futures = new HashSet<>();
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
Map<UUID, String> out = Collections.synchronizedMap(new TreeMap<>());
|
||||
|
||||
for (UUID uuid : uuids) {
|
||||
futures.add(dataStorageInterface.getLegacyPlayerData(uuid).thenAccept(it -> {
|
||||
if (it.isPresent()) {
|
||||
out.put(uuid, plugin.getStorageManager().toJson(it.get()));
|
||||
userCount.incrementAndGet();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> overallFuture = CompletableFutures.allOf(futures);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
overallFuture.get(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (TimeoutException e) {
|
||||
LogUtils.info("Progress: " + userCount.get() + "/" + uuids.size());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
JsonObject outJson = new JsonObject();
|
||||
for (Map.Entry<UUID, String> entry : out.entrySet()) {
|
||||
outJson.addProperty(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
|
||||
String formattedDate = formatter.format(new Date());
|
||||
File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz");
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) {
|
||||
new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
dataStorageInterface.disable();
|
||||
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed.");
|
||||
});
|
||||
})
|
||||
,
|
||||
new CommandAPICommand("import")
|
||||
.withArguments(new StringArgument("file"))
|
||||
.executes((sender, args) -> {
|
||||
String fileName = (String) args.get("file");
|
||||
if (fileName == null) return;
|
||||
CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get();
|
||||
File file = new File(plugin.getDataFolder(), fileName);
|
||||
if (!file.exists()) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "File not exists.");
|
||||
return;
|
||||
}
|
||||
if (!file.getName().endsWith(".json.gz")) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Invalid file.");
|
||||
return;
|
||||
}
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting <aqua>import</aqua>.");
|
||||
JsonObject data;
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file.toPath())), StandardCharsets.UTF_8))) {
|
||||
data = new GsonBuilder().disableHtmlEscaping().create().fromJson(reader, JsonObject.class);
|
||||
} catch (IOException e) {
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Error occurred when reading the backup file.");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource();
|
||||
var entrySet = data.entrySet();
|
||||
int amount = entrySet.size();
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
Set<CompletableFuture<Void>> futures = new HashSet<>();
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : entrySet) {
|
||||
UUID uuid = UUID.fromString(entry.getKey());
|
||||
if (entry.getValue() instanceof JsonPrimitive primitive) {
|
||||
PlayerData playerData = plugin.getStorageManager().fromJson(primitive.getAsString());
|
||||
futures.add(dataStorageInterface.updateOrInsertPlayerData(uuid, playerData).thenAccept(it -> userCount.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
CompletableFuture<Void> overallFuture = CompletableFutures.allOf(futures);
|
||||
while (true) {
|
||||
try {
|
||||
overallFuture.get(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (TimeoutException e) {
|
||||
LogUtils.info("Progress: " + userCount.get() + "/" + amount);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed.");
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
|
||||
if (!(barEntry.getValue() instanceof ConfigurationSection section))
|
||||
return;
|
||||
|
||||
this.config = ActionBarConfig.Builder.of()
|
||||
this.config = ActionBarConfig.builder()
|
||||
.checkFrequency(section.getInt("check-frequency", 10))
|
||||
.requirement(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions")))
|
||||
.displayOrder(ConfigUtils.getTimeLimitTexts(section.getConfigurationSection("text-display-order")))
|
||||
|
||||
@@ -19,13 +19,15 @@ package net.momirealms.customnameplates.paper.mechanic.bubble;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.momirealms.customnameplates.api.data.OnlineUser;
|
||||
import net.momirealms.customnameplates.api.event.BubblesSpawnEvent;
|
||||
import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent;
|
||||
import net.momirealms.customnameplates.api.manager.BubbleManager;
|
||||
import net.momirealms.customnameplates.api.mechanic.bubble.Bubble;
|
||||
import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger;
|
||||
import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar;
|
||||
import net.momirealms.customnameplates.api.mechanic.nameplate.CachedNameplate;
|
||||
import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer;
|
||||
import net.momirealms.customnameplates.api.mechanic.tag.unlimited.EntityTagPlayer;
|
||||
import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextEntity;
|
||||
import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextTagSetting;
|
||||
@@ -45,6 +47,8 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
@@ -308,6 +312,22 @@ public class BubbleManagerImpl implements BubbleManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
||||
public void onDataLoaded(NameplateDataLoadEvent event) {
|
||||
OnlineUser data = event.getOnlineUser();
|
||||
String bubble = data.getBubbleKey();
|
||||
if (!bubble.equals("none") && !containsBubble(bubble)) {
|
||||
if (bubble.equals(defaultBubble)) {
|
||||
LogUtils.severe("Default nameplate doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
LogUtils.severe("Bubble " + bubble + " doesn't exist. To prevent bugs, player " + event.getUUID() + " 's bubble data is reset");
|
||||
data.setBubble("none");
|
||||
plugin.getStorageManager().saveOnlinePlayerData(event.getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getBubbleKeys() {
|
||||
return bubbleMap.keySet();
|
||||
@@ -356,6 +376,11 @@ public class BubbleManagerImpl implements BubbleManager {
|
||||
return bubbleMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBubble(String key) {
|
||||
return bubbleMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equipBubble(Player player, String bubbleKey) {
|
||||
Bubble bubble = getBubble(bubbleKey);
|
||||
|
||||
@@ -17,15 +17,12 @@
|
||||
|
||||
package net.momirealms.customnameplates.paper.mechanic.image;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.manager.ImageManager;
|
||||
import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger;
|
||||
import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.setting.CNConfig;
|
||||
import net.momirealms.customnameplates.paper.util.ImageUtils;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
@@ -19,22 +19,15 @@ package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataValue;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customnameplates.common.team.TeamColor;
|
||||
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
|
||||
import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager;
|
||||
import net.momirealms.customnameplates.paper.util.FakeEntityUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.setting.CNConfig;
|
||||
import net.momirealms.customnameplates.paper.util.ConfigUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.codehaus.plexus.util.FileUtils;
|
||||
@@ -44,7 +43,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ResourcePackManagerImpl implements ResourcePackManager {
|
||||
|
||||
@@ -263,11 +261,6 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!CNConfig.legacyUnicodes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int ascent : ascentUnicodes) {
|
||||
String line;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -581,7 +574,7 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
|
||||
|
||||
public static final String Animated_Text_FSH =
|
||||
"\n" +
|
||||
"vec2 p1 = round(pos1 / (posID == 0 ? 1 - coord.x : 1 - coord.y));\n" +
|
||||
" vec2 p1 = round(pos1 / (posID == 0 ? 1 - coord.x : 1 - coord.y));\n" +
|
||||
" vec2 p2 = round(pos2 / (posID == 0 ? coord.y : coord.x));\n" +
|
||||
" ivec2 resolution = ivec2(abs(p1 - p2));\n" +
|
||||
" ivec2 corner = ivec2(min(p1, p2));\n" +
|
||||
|
||||
@@ -26,7 +26,6 @@ import net.momirealms.customnameplates.api.mechanic.font.OffsetFont;
|
||||
import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.mechanic.placeholder.*;
|
||||
import net.momirealms.customnameplates.api.util.FontUtils;
|
||||
import net.momirealms.customnameplates.paper.setting.CNLocale;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.Objects;
|
||||
|
||||
public class CNConfig {
|
||||
|
||||
public static String configVersion = "22";
|
||||
public static String configVersion = "23";
|
||||
public static int cacheSize;
|
||||
public static int corePoolSize;
|
||||
public static long keepAliveTime;
|
||||
|
||||
@@ -1,48 +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.customnameplates.paper.storage;
|
||||
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface DataStorageInterface {
|
||||
|
||||
/**
|
||||
* Initialize the data resource
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Close the data resource
|
||||
*/
|
||||
void disable();
|
||||
|
||||
/**
|
||||
* Get the storage data source type
|
||||
*
|
||||
* @return {@link StorageType}
|
||||
*/
|
||||
StorageType getStorageType();
|
||||
|
||||
CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid);
|
||||
|
||||
CompletableFuture<Boolean> updatePlayerData(UUID uuid, PlayerData playerData);
|
||||
}
|
||||
@@ -20,10 +20,13 @@ package net.momirealms.customnameplates.paper.storage;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.momirealms.customnameplates.api.data.DataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.OnlineUser;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent;
|
||||
import net.momirealms.customnameplates.api.manager.StorageManager;
|
||||
import net.momirealms.customnameplates.api.scheduler.CancellableTask;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.method.database.nosql.MongoDBImpl;
|
||||
@@ -36,6 +39,7 @@ import net.momirealms.customnameplates.paper.storage.method.file.JsonImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.method.file.YAMLImpl;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
@@ -50,6 +54,7 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This class implements the StorageManager interface and is responsible for managing player data storage.
|
||||
@@ -112,7 +117,19 @@ public class StorageManagerImpl implements Listener, StorageManager {
|
||||
if (onlineUser != null) {
|
||||
return CompletableFuture.completedFuture(Optional.of(onlineUser.toPlayerData()));
|
||||
}
|
||||
return dataSource.getPlayerData(uuid);
|
||||
if (hasRedis) {
|
||||
return redisManager.getPlayerData(uuid).thenCompose(
|
||||
result -> {
|
||||
if (result.isEmpty()) {
|
||||
return dataSource.getPlayerData(uuid);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return dataSource.getPlayerData(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,12 +138,23 @@ public class StorageManagerImpl implements Listener, StorageManager {
|
||||
if (onlineUser == null) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return dataSource.updatePlayerData(uuid, onlineUser.toPlayerData());
|
||||
return savePlayerData(uuid, onlineUser.toPlayerData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> savePlayerData(UUID uuid, PlayerData playerData) {
|
||||
return dataSource.updatePlayerData(uuid, playerData);
|
||||
if (hasRedis) {
|
||||
return redisManager.updatePlayerData(uuid, playerData).thenCompose(
|
||||
result -> {
|
||||
if (!result) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return dataSource.updatePlayerData(uuid, playerData);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return dataSource.updatePlayerData(uuid, playerData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,6 +256,11 @@ public class StorageManagerImpl implements Listener, StorageManager {
|
||||
return gson.toJson(playerData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStorageInterface getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom exception class for data serialization errors.
|
||||
*/
|
||||
|
||||
@@ -1,30 +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.customnameplates.paper.storage;
|
||||
|
||||
public enum StorageType {
|
||||
|
||||
JSON,
|
||||
YAML,
|
||||
H2,
|
||||
SQLite,
|
||||
MySQL,
|
||||
MariaDB,
|
||||
MongoDB,
|
||||
Redis
|
||||
}
|
||||
@@ -18,7 +18,11 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.paper.storage.DataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.DataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* An abstract class that implements the DataStorageInterface and provides common functionality for data storage.
|
||||
@@ -40,4 +44,10 @@ public abstract class AbstractStorage implements DataStorageInterface {
|
||||
public void disable() {
|
||||
// This method can be overridden in subclasses to perform cleanup or shutdown tasks specific to the storage type.
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> updateOrInsertPlayerData(UUID uuid, PlayerData playerData) {
|
||||
// By default, delegate to the updatePlayerData method to update or insert player data.
|
||||
return updatePlayerData(uuid, playerData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,16 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method.database.nosql;
|
||||
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.*;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Projections;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import com.mongodb.client.model.Updates;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.paper.storage.method.AbstractStorage;
|
||||
import org.bson.Document;
|
||||
import org.bson.UuidRepresentation;
|
||||
@@ -39,9 +37,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -145,6 +141,30 @@ public class MongoDBImpl extends AbstractStorage {
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of unique player UUIDs from the MongoDB database.
|
||||
*
|
||||
* @param legacy Flag indicating whether to retrieve legacy data.
|
||||
* @return A set of unique player UUIDs.
|
||||
*/
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers(boolean legacy) {
|
||||
// no legacy files
|
||||
Set<UUID> uuids = new HashSet<>();
|
||||
MongoCollection<Document> collection = database.getCollection(getCollectionName("data"));
|
||||
try {
|
||||
Bson projectionFields = Projections.fields(Projections.include("uuid"));
|
||||
try (MongoCursor<Document> cursor = collection.find().projection(projectionFields).iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
uuids.add(cursor.next().get("uuid", UUID.class));
|
||||
}
|
||||
}
|
||||
} catch (MongoException e) {
|
||||
LogUtils.warn("Failed to get unique data.", e);
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection name for a specific subcategory of data.
|
||||
*
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method.database.nosql;
|
||||
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.paper.storage.method.AbstractStorage;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
@@ -32,7 +32,9 @@ import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
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.concurrent.CompletableFuture;
|
||||
|
||||
@@ -71,6 +73,11 @@ public class RedisManager extends AbstractStorage {
|
||||
return jedisPool.getResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers(boolean legacy) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Redis connection and configuration based on the plugin's YAML configuration.
|
||||
*/
|
||||
@@ -87,7 +94,7 @@ public class RedisManager extends AbstractStorage {
|
||||
jedisPoolConfig.setTestWhileIdle(true);
|
||||
jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000));
|
||||
jedisPoolConfig.setNumTestsPerEvictionRun(-1);
|
||||
jedisPoolConfig.setMinEvictableIdleTime(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis",1800000)));
|
||||
jedisPoolConfig.setMinEvictableIdleDuration(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis", 1800000)));
|
||||
jedisPoolConfig.setMaxTotal(section.getInt("MaxTotal",8));
|
||||
jedisPoolConfig.setMaxIdle(section.getInt("MaxIdle",8));
|
||||
jedisPoolConfig.setMinIdle(section.getInt("MinIdle",1));
|
||||
@@ -168,7 +175,7 @@ public class RedisManager extends AbstractStorage {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.setex(
|
||||
getRedisKey("cn_data", uuid),
|
||||
300,
|
||||
600,
|
||||
plugin.getStorageManager().toBytes(playerData)
|
||||
);
|
||||
future.complete(true);
|
||||
|
||||
@@ -20,21 +20,24 @@ package net.momirealms.customnameplates.paper.storage.method.database.sql;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* An abstract base class for SQL databases using the HikariCP connection pool, which handles player data storage.
|
||||
*/
|
||||
public abstract class AbstractHikariDatabase extends AbstractSQLDatabase {
|
||||
public abstract class AbstractHikariDatabase extends AbstractSQLDatabase implements LegacyDataStorageInterface {
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
private final String driverClass;
|
||||
@@ -117,6 +120,30 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase {
|
||||
super.createTableIfNotExist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getLegacyPlayerData(UUID uuid) {
|
||||
var future = new CompletableFuture<Optional<PlayerData>>();
|
||||
String sql = String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"));
|
||||
try (Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, uuid.toString());
|
||||
ResultSet rs = statement.executeQuery();
|
||||
if (rs.next()) {
|
||||
String nameplate = rs.getString(2);
|
||||
String bubbles = rs.getString(3);
|
||||
future.complete(Optional.of(PlayerData.builder()
|
||||
.setBubble(bubbles)
|
||||
.setNameplate(nameplate)
|
||||
.build()));
|
||||
} else {
|
||||
future.complete(Optional.empty());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the database by closing the connection pool.
|
||||
*/
|
||||
|
||||
@@ -28,10 +28,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -73,6 +70,36 @@ public abstract class AbstractSQLDatabase extends AbstractStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or insert a player's data into the SQL database.
|
||||
*
|
||||
* @param uuid The UUID of the player.
|
||||
* @param playerData The player data to update or insert.
|
||||
* @return A CompletableFuture indicating the success of the operation.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Boolean> updateOrInsertPlayerData(UUID uuid, PlayerData playerData) {
|
||||
var future = new CompletableFuture<Boolean>();
|
||||
plugin.getScheduler().runTaskAsync(() -> {
|
||||
try (
|
||||
Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data")))
|
||||
) {
|
||||
statement.setString(1, uuid.toString());
|
||||
ResultSet rs = statement.executeQuery();
|
||||
if (rs.next()) {
|
||||
updatePlayerData(uuid, playerData).thenRun(() -> future.complete(true));
|
||||
} else {
|
||||
insertPlayerData(uuid, playerData);
|
||||
future.complete(true);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to update " + uuid + "'s data.", e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL schema from a resource file.
|
||||
*
|
||||
@@ -177,6 +204,29 @@ public abstract class AbstractSQLDatabase extends AbstractStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of unique user UUIDs from the SQL database.
|
||||
*
|
||||
* @param legacy Whether to include legacy data in the retrieval.
|
||||
* @return A set of unique user UUIDs.
|
||||
*/
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers(boolean legacy) {
|
||||
Set<UUID> uuids = new HashSet<>();
|
||||
try (Connection connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, legacy ? getTableName("fishingbag") : getTableName("data")))) {
|
||||
try (ResultSet rs = statement.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
UUID uuid = UUID.fromString(rs.getString("uuid"));
|
||||
uuids.add(uuid);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogUtils.warn("Failed to get unique data.", e);
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants defining SQL statements used for database operations.
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.h2.jdbcx.JdbcConnectionPool;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
|
||||
public class MariaDBImpl extends AbstractHikariDatabase {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package net.momirealms.customnameplates.paper.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
|
||||
public class MySQLImpl extends AbstractHikariDatabase {
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ package net.momirealms.customnameplates.paper.storage.method.database.sql;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.sqlite.SQLiteConfig;
|
||||
|
||||
@@ -19,8 +19,8 @@ package net.momirealms.customnameplates.paper.storage.method.file;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.paper.storage.method.AbstractStorage;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@@ -29,7 +29,9 @@ import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -125,4 +127,21 @@ public class JsonImpl extends AbstractStorage {
|
||||
this.saveToJsonFile(playerData, getPlayerDataFile(uuid));
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
// Retrieve a set of unique user UUIDs based on JSON data files in the 'data' folder.
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers(boolean legacy) {
|
||||
// No legacy files
|
||||
File folder = new File(plugin.getDataFolder(), "data");
|
||||
Set<UUID> uuids = new HashSet<>();
|
||||
if (folder.exists()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
uuids.add(UUID.fromString(file.getName().substring(file.getName().length() - 5)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,27 +17,30 @@
|
||||
|
||||
package net.momirealms.customnameplates.paper.storage.method.file;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface;
|
||||
import net.momirealms.customnameplates.api.data.PlayerData;
|
||||
import net.momirealms.customnameplates.api.data.StorageType;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
|
||||
import net.momirealms.customnameplates.paper.storage.StorageType;
|
||||
import net.momirealms.customnameplates.paper.storage.method.AbstractStorage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A data storage implementation that uses YAML files to store player data, with support for legacy data.
|
||||
*/
|
||||
public class YAMLImpl extends AbstractStorage {
|
||||
public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterface {
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public YAMLImpl(CustomNameplatesPluginImpl plugin) {
|
||||
public YAMLImpl(CustomNameplatesPlugin plugin) {
|
||||
super(plugin);
|
||||
File folder = new File(plugin.getDataFolder(), "data");
|
||||
if (!folder.exists()) folder.mkdirs();
|
||||
@@ -102,4 +105,35 @@ public class YAMLImpl extends AbstractStorage {
|
||||
}
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers(boolean legacy) {
|
||||
File folder;
|
||||
if (legacy) {
|
||||
folder = new File(plugin.getDataFolder(), "player_data");
|
||||
} else {
|
||||
folder = new File(plugin.getDataFolder(), "data");
|
||||
}
|
||||
Set<UUID> uuids = new HashSet<>();
|
||||
if (folder.exists()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
uuids.add(UUID.fromString(file.getName().substring(0, file.getName().length() - 4)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<PlayerData>> getLegacyPlayerData(UUID uuid) {
|
||||
File dataFile = new File(plugin.getDataFolder(), "player_data" + File.separator + uuid + ".yml");
|
||||
YamlConfiguration data = readData(dataFile);
|
||||
PlayerData playerData = new PlayerData.Builder()
|
||||
.setBubble(data.getString("bubbles", ""))
|
||||
.setNameplate(data.getString("nameplate", ""))
|
||||
.build();
|
||||
return CompletableFuture.completedFuture(Optional.of(playerData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,21 @@ public class ImageUtils {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAnimatedImage(File file, int frames) {
|
||||
try {
|
||||
BufferedImage inputImage = ImageIO.read(file);
|
||||
int width = inputImage.getWidth();
|
||||
int height = inputImage.getHeight();
|
||||
int frameHeight = height / frames;
|
||||
BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
int newPixel = (1 << 24) | (10 << 16) | (width << 8) | frameHeight;
|
||||
for (int i = 0; i < frames; i++) {
|
||||
outputImage.setRGB(0, frameHeight * i, newPixel);
|
||||
}
|
||||
ImageIO.write(outputImage, "png", file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
package net.momirealms.customnameplates.paper.util;
|
||||
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
|
||||
import net.momirealms.customnameplates.api.util.LogUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class Migration {
|
||||
|
||||
public static boolean check() {
|
||||
File readMe = new File(CustomNameplatesPlugin.get().getDataFolder(), "README.text");
|
||||
if (readMe.exists()) {
|
||||
return true;
|
||||
}
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "config.yml");
|
||||
if (!file.exists()) {
|
||||
return false;
|
||||
}
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
boolean version = config.contains("other-settings.default-character-width");
|
||||
if (!version) {
|
||||
return false;
|
||||
}
|
||||
config.set("other-settings.default-character-width", null);
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
CustomNameplatesPlugin.get().saveResource("README.txt", true);
|
||||
updateBossBar();
|
||||
updateActionBar();
|
||||
updateBubble();
|
||||
updateNameplate();
|
||||
updateCustomPlaceholders();
|
||||
deleteFiles();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void deleteFiles() {
|
||||
try {
|
||||
FileUtils.delete(new File(CustomNameplatesPlugin.get().getDataFolder(), "database.yml"));
|
||||
FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "unicodes"));
|
||||
FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "templates"));
|
||||
FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "ResourcePack"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateBossBar() {
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "bossbar.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
section.set("check-frequency", 1);
|
||||
int refresh = section.getInt("refresh-rate");
|
||||
if (section.contains("text")) {
|
||||
String text = section.getString("text");
|
||||
section.set("text-display-order.1.duration", 1000);
|
||||
section.set("text-display-order.1.refresh-frequency", refresh);
|
||||
section.set("text-display-order.1.text", text);
|
||||
} else if (section.contains("dynamic-text")) {
|
||||
int switchInt = section.getInt("switch-interval", 15) * 20;
|
||||
int i = 0;
|
||||
for (String text : section.getStringList("dynamic-text")) {
|
||||
i++;
|
||||
section.set("text-display-order."+i+".duration", switchInt);
|
||||
section.set("text-display-order."+i+".refresh-frequency", refresh);
|
||||
section.set("text-display-order."+i+".text", text);
|
||||
}
|
||||
}
|
||||
section.set("switch-interval", null);
|
||||
section.set("dynamic-text", null);
|
||||
section.set("refresh-rate", null);
|
||||
section.set("text", null);
|
||||
}
|
||||
}
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateActionBar() {
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "actionbar.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection section) {
|
||||
String text = section.getString("text", "");
|
||||
section.set("text", null);
|
||||
section.set("check-frequency", 1);
|
||||
section.set("text-display-order.1.text", text);
|
||||
section.set("text-display-order.1.refresh-frequency", 1);
|
||||
section.set("text-display-order.1.duration", 1000);
|
||||
}
|
||||
}
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateBubble() {
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "bubble.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
config.set("mode", null);
|
||||
config.set("text-display-options", null);
|
||||
config.set("default-bubble", config.getString("default-bubbles","none"));
|
||||
config.set("default-bubbles", null);
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateNameplate() {
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "nameplate.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
String mode = config.getString("mode","");
|
||||
if (!mode.equalsIgnoreCase("team")) {
|
||||
config.set("mode", "Unlimited");
|
||||
}
|
||||
|
||||
config.set("nameplate.player-name", config.getString("player-name","%player_name%"));
|
||||
config.set("nameplate.refresh-frequency", 10);
|
||||
config.set("nameplate.prefix", config.getString("prefix", ""));
|
||||
config.set("nameplate.prefix", config.getString("suffix",""));
|
||||
|
||||
if (mode.equalsIgnoreCase("team")) {
|
||||
config.createSection("unlimited");
|
||||
ConfigurationSection section = config.createSection("team");
|
||||
section.set("refresh-frequency", 20);
|
||||
section.set("prefix", "");
|
||||
section.set("suffix", "");
|
||||
} else {
|
||||
ConfigurationSection old;
|
||||
if (mode.equalsIgnoreCase("armor_stand")) {
|
||||
old = config.getConfigurationSection("armor_stand");
|
||||
} else if (mode.equalsIgnoreCase("text_display")) {
|
||||
old = config.getConfigurationSection("text_display");
|
||||
} else {
|
||||
LogUtils.severe("No mode is set for nameplate. Failed to migrate!");
|
||||
return;
|
||||
}
|
||||
ConfigurationSection newSection = config.createSection("unlimited", old.getValues(false));
|
||||
for (Map.Entry<String, Object> entry : newSection.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection inner) {
|
||||
ConfigurationSection conditions = inner.getConfigurationSection("conditions");
|
||||
if (conditions != null) {
|
||||
inner.createSection("owner-conditions", conditions.getValues(false));
|
||||
inner.createSection("viewer-conditions");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
config.set("player-name", null);
|
||||
config.set("prefix", null);
|
||||
config.set("suffix", null);
|
||||
config.set("hide-prefix-when-equipping-nameplate", null);
|
||||
config.set("hide-suffix-when-equipping-nameplate", null);
|
||||
config.set("create-fake-team", null);
|
||||
config.set("text_display", null);
|
||||
config.set("armor_stand", null);
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateCustomPlaceholders() {
|
||||
File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "custom-placeholders.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
config.createSection("switch-text");
|
||||
ConfigurationSection descentTSection = config.getConfigurationSection("descent-text");
|
||||
ConfigurationSection descentUSection = config.getConfigurationSection("descent-unicode");
|
||||
if (descentTSection != null && descentUSection != null) {
|
||||
for (Map.Entry<String, Object> entry : descentUSection.getValues(false).entrySet()) {
|
||||
if (entry.getValue() instanceof ConfigurationSection inner) {
|
||||
String text = inner.getString("text");
|
||||
int descent = inner.getInt("descent");
|
||||
ConfigurationSection newSection;
|
||||
if (descentTSection.contains(entry.getKey())) {
|
||||
newSection = descentTSection.createSection(entry.getKey() + "_");
|
||||
LogUtils.warn("Duplicated key '" + entry.getKey() + "' found during migration. Please manually fix that.");
|
||||
} else {
|
||||
newSection = descentTSection.createSection(entry.getKey());
|
||||
}
|
||||
newSection.set("text", text);
|
||||
newSection.set("descent", descent);
|
||||
newSection.set("is-unicode", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
config.set("descent-unicode", null);
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
paper/src/main/resources/README.txt
Normal file
3
paper/src/main/resources/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Hi! It seems that you are updating the plugin from 2.2 to 2.3.
|
||||
Please read this and follow the guide to update your configs
|
||||
https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/2.2-to-2.3-migration-guide
|
||||
@@ -1,5 +1,5 @@
|
||||
# Do not change
|
||||
config-version: '22'
|
||||
config-version: '23'
|
||||
|
||||
# Debug mode
|
||||
debug: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# You can only create "actionbar" section in this config file
|
||||
# Player can only receive at most 1 actionbar at the same time
|
||||
actionbar:
|
||||
check-frequency: 1
|
||||
check-frequency: 10
|
||||
conditions:
|
||||
permission: "actionbar.show"
|
||||
text-display-order:
|
||||
|
||||
@@ -4,7 +4,7 @@ blacklist-channels:
|
||||
- Staff
|
||||
|
||||
# Default bubble to display if player's bubble is "none"
|
||||
default-bubbles: 'chat'
|
||||
default-bubble: 'chat'
|
||||
|
||||
# Text format when bubble is "none"
|
||||
default-format:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
conditional-text:
|
||||
actionbar:
|
||||
priority_1:
|
||||
text: '%nameplates_background_actionbar%'
|
||||
text: '%nameplates_background_other_actionbar%'
|
||||
conditions:
|
||||
'!=':
|
||||
value1: '%player_remaining_air%'
|
||||
@@ -122,9 +122,4 @@ vanilla-hud:
|
||||
full: stamina_2
|
||||
placeholder:
|
||||
value: '1.1'
|
||||
max-value: '2'
|
||||
|
||||
cached-text:
|
||||
cached:
|
||||
text: '%super_complex_javascript%'
|
||||
refresh-interval: 10000
|
||||
max-value: '2'
|
||||
@@ -28,14 +28,16 @@ unlimited:
|
||||
tag_1:
|
||||
text: '%nameplates_nametag%'
|
||||
vertical-offset: -1
|
||||
owner-conditions: { }
|
||||
owner-conditions:
|
||||
potion-effect: "INVISIBILITY=0"
|
||||
viewer-conditions: { }
|
||||
tag_2:
|
||||
text: "IP: %player_ip% My IP: %viewer_player_ip%"
|
||||
vertical-offset: -0.7
|
||||
refresh-frequency: 10
|
||||
check-frequency: 20
|
||||
owner-conditions: { }
|
||||
owner-conditions:
|
||||
potion-effect: "INVISIBILITY=0"
|
||||
viewer-conditions:
|
||||
condition_1:
|
||||
type: permission
|
||||
|
||||
Reference in New Issue
Block a user