diff --git a/build.gradle b/build.gradle
index 1376862b..2da92674 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ plugins {
id 'java'
}
-group 'net.william278'
+group 'org.inksnow.husk'
version "$ext.plugin_version${versionMetadata()}"
description "$ext.plugin_description"
defaultTasks 'licenseFormat', 'build'
@@ -28,30 +28,12 @@ ext {
publishing {
repositories {
- if (System.getenv("RELEASES_MAVEN_USERNAME") != null) {
- maven {
- name = "william278-releases"
- url = "https://repo.william278.net/releases"
- credentials {
- username = System.getenv("RELEASES_MAVEN_USERNAME")
- password = System.getenv("RELEASES_MAVEN_PASSWORD")
- }
- authentication {
- basic(BasicAuthentication)
- }
- }
- }
- if (System.getenv("SNAPSHOTS_MAVEN_USERNAME") != null) {
- maven {
- name = "william278-snapshots"
- url = "https://repo.william278.net/snapshots"
- credentials {
- username = System.getenv("SNAPSHOTS_MAVEN_USERNAME")
- password = System.getenv("SNAPSHOTS_MAVEN_PASSWORD")
- }
- authentication {
- basic(BasicAuthentication)
- }
+ maven {
+ name = 'husk-release'
+ url = findProperty("repository.huskrelease.url")
+ credentials {
+ username = findProperty("repository.huskrelease.username")
+ password = findProperty("repository.huskrelease.password")
}
}
}
@@ -63,23 +45,26 @@ allprojects {
apply plugin: 'java'
compileJava.options.encoding = 'UTF-8'
- compileJava.options.release.set 17
+ compileJava.options.release.set 8
+
javadoc.options.encoding = 'UTF-8'
javadoc.options.addStringOption('Xdoclint:none', '-quiet')
repositories {
mavenLocal()
mavenCentral()
- maven { url 'https://repo.william278.net/releases/' }
+ maven {
+ url findProperty('repository.huskpublic.url')
+ credentials {
+ username = findProperty('repository.huskpublic.username')
+ password = findProperty('repository.huskpublic.password')
+ }
+ }
+ maven { url 'https://r.irepo.space/maven/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url "https://repo.dmulloy2.net/repository/public/" }
maven { url 'https://repo.codemc.io/repository/maven-public/' }
- maven { url 'https://repo.minebench.de/' }
- maven { url 'https://repo.alessiodp.com/releases/' }
- maven { url 'https://jitpack.io' }
- maven { url 'https://mvn-repo.arim.space/lesser-gpl3/' }
- maven { url 'https://libraries.minecraft.net/' }
}
dependencies {
@@ -148,7 +133,7 @@ subprojects {
if (['common'].contains(project.name)) {
publications {
mavenJavaCommon(MavenPublication) {
- groupId = 'net.william278.husksync'
+ groupId = 'org.inksnow.husk.husksync'
artifactId = 'husksync-common'
version = "$rootProject.version"
artifact shadowJar
@@ -161,7 +146,7 @@ subprojects {
if (['bukkit'].contains(project.name)) {
publications {
mavenJavaBukkit(MavenPublication) {
- groupId = 'net.william278.husksync'
+ groupId = 'org.inksnow.husk.husksync'
artifactId = 'husksync-bukkit'
version = "$rootProject.version"
artifact shadowJar
@@ -174,7 +159,7 @@ subprojects {
if (['fabric'].contains(project.name)) {
publications {
mavenJavaFabric(MavenPublication) {
- groupId = 'net.william278.husksync'
+ groupId = 'org.inksnow.husk.husksync'
artifactId = 'husksync-fabric'
version = "$rootProject.version+${fabric_minecraft_version}"
artifact remapJar
diff --git a/bukkit/build.gradle b/bukkit/build.gradle
index 1a8465c0..a77b04b9 100644
--- a/bukkit/build.gradle
+++ b/bukkit/build.gradle
@@ -1,15 +1,22 @@
dependencies {
implementation project(path: ':common')
- implementation 'net.william278.uniform:uniform-bukkit:1.2.1'
+ implementation 'org.slf4j:slf4j-api:1.7.36'
+ implementation 'org.inksnow.husk.uniform:uniform-bukkit:1.2.1-ad25f16'
implementation 'net.william278:mpdbdataconverter:1.0.1'
- implementation 'net.william278:hsldataconverter:1.0'
+ implementation 'org.inksnow.husk:hsldataconverter:1.0'
implementation 'net.william278:mapdataapi:1.0.3'
implementation 'org.bstats:bstats-bukkit:3.0.2'
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
implementation 'dev.triumphteam:triumph-gui:3.1.10'
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
implementation 'de.tr7zw:item-nbt-api:2.13.2'
+ implementation 'com.google.guava:guava:33.2.1-jre'
+ implementation ("redis.clients:jedis:$jedis_version") {
+ exclude group: 'org.slf4j', module: 'slf4j-api'
+ }
+ implementation "org.xerial.snappy:snappy-java:$snappy_version"
+ implementation "org.mongodb:mongodb-driver-sync:$mongodb_driver_version"
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'com.github.retrooper.packetevents:spigot:2.3.0'
@@ -17,24 +24,27 @@ dependencies {
compileOnly 'org.projectlombok:lombok:1.18.34'
compileOnly 'commons-io:commons-io:2.16.1'
compileOnly 'org.json:json:20240303'
- compileOnly 'net.william278:minedown:1.8.2'
- compileOnly 'de.exlll:configlib-yaml:4.5.0'
- compileOnly 'com.zaxxer:HikariCP:5.1.0'
- compileOnly 'net.william278:DesertWell:2.0.4'
+ compileOnly 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT'
+ compileOnly 'org.inksnow.husk:configlib-yaml:4.5.4'
+ compileOnly 'com.zaxxer:HikariCP:4.0.3'
+ compileOnly 'org.inksnow.husk:desertwell:2.0.5-9962d59:all'
compileOnly 'net.william278:AdvancementAPI:97a9583413'
- compileOnly "redis.clients:jedis:$jedis_version"
annotationProcessor 'org.projectlombok:lombok:1.18.34'
}
shadowJar {
- dependencies {
- exclude(dependency('com.mojang:brigadier'))
- }
+ mergeServiceFiles()
+
+ relocate 'org.inksnow.cputil', 'net.william278.husksync.libraries.cputil'
+ relocate 'org.slf4j', 'net.william278.husksync.libraries.slf4j'
+ relocate 'org.objectweb.asm', 'net.william278.husksync.libraries.asm'
+
relocate 'org.apache.commons.io', 'net.william278.husksync.libraries.commons.io'
relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text'
relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3'
relocate 'com.google.gson', 'net.william278.husksync.libraries.gson'
+ relocate 'com.google.common', 'net.william278.husksync.libraries.guava'
relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries'
@@ -54,6 +64,4 @@ shadowJar {
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi'
-
- minimize()
-}
\ No newline at end of file
+}
diff --git a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
index a75e16c2..4b9993ef 100644
--- a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
+++ b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
@@ -57,6 +57,8 @@ import net.william278.husksync.util.BukkitLegacyConverter;
import net.william278.husksync.util.BukkitMapPersister;
import net.william278.husksync.util.BukkitTask;
import net.william278.husksync.util.LegacyConverter;
+import net.william278.husksync.util.StringUtil;
+import net.william278.husksync.util.ref.RefUtil;
import net.william278.uniform.Uniform;
import net.william278.uniform.bukkit.BukkitUniform;
import org.bstats.bukkit.Metrics;
@@ -64,6 +66,9 @@ import org.bukkit.entity.Player;
import org.bukkit.map.MapView;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
+import org.inksnow.cputil.AuroraCputil;
+import org.inksnow.cputil.AuroraUrl;
+import org.inksnow.cputil.logger.AuroraLoggerFactory;
import org.jetbrains.annotations.NotNull;
import space.arim.morepaperlib.MorePaperLib;
import space.arim.morepaperlib.scheduling.AsynchronousScheduler;
@@ -81,10 +86,21 @@ import java.util.stream.Collectors;
public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.Supplier,
BukkitEventDispatcher, BukkitMapPersister {
+ static {
+ AuroraLoggerFactory.instance().nameMapping(it -> {
+ int split = it.lastIndexOf('.');
+ if (split != -1) {
+ return "HuskSync-Aurora " + it.substring(split + 1);
+ } else {
+ return "HuskSync-Aurora " + it;
+ }
+ });
+ }
+
/**
- * Metrics ID for HuskSync on Bukkit.
+ * Metrics ID for HuskSync-Aurora.
*/
- private static final int METRICS_ID = 13140;
+ private static final int METRICS_ID = 23157;
private static final String PLATFORM_TYPE_ID = "bukkit";
private final TreeMap> serializers = Maps.newTreeMap(
@@ -151,15 +167,21 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
// Prepare serializers
initialize("data serializers", (plugin) -> {
- registerSerializer(Identifier.PERSISTENT_DATA, new BukkitSerializer.PersistentData(this));
+ if (RefUtil.bootstrapVirtualMethod("Lorg/bukkit/persistence/PersistentDataHolder;getPersistentDataContainer()Lorg/bukkit/persistence/PersistentDataContainer;") != null) {
+ registerSerializer(Identifier.PERSISTENT_DATA, new BukkitSerializer.PersistentData(this));
+ }
registerSerializer(Identifier.INVENTORY, new BukkitSerializer.Inventory(this));
registerSerializer(Identifier.ENDER_CHEST, new BukkitSerializer.EnderChest(this));
registerSerializer(Identifier.ADVANCEMENTS, new BukkitSerializer.Advancements(this));
- registerSerializer(Identifier.STATISTICS, new Serializer.Json<>(this, BukkitData.Statistics.class));
+ if (RefUtil.bootstrapStaticFieldGet("Lorg/bukkit/Registry;STATISTIC:Lorg/bukkit/Registry;") != null) {
+ registerSerializer(Identifier.STATISTICS, new Serializer.Json<>(this, BukkitData.Statistics.class));
+ }
registerSerializer(Identifier.POTION_EFFECTS, new BukkitSerializer.PotionEffects(this));
registerSerializer(Identifier.GAME_MODE, new Serializer.Json<>(this, BukkitData.GameMode.class));
registerSerializer(Identifier.FLIGHT_STATUS, new Serializer.Json<>(this, BukkitData.FlightStatus.class));
- registerSerializer(Identifier.ATTRIBUTES, new Serializer.Json<>(this, BukkitData.Attributes.class));
+ if (RefUtil.bootstrapStaticFieldGet("Lorg/bukkit/Registry;ATTRIBUTE:Lorg/bukkit/Registry;") != null) {
+ registerSerializer(Identifier.ATTRIBUTES, new Serializer.Json<>(this, BukkitData.Attributes.class));
+ }
registerSerializer(Identifier.HEALTH, new Serializer.Json<>(this, BukkitData.Health.class));
registerSerializer(Identifier.HUNGER, new Serializer.Json<>(this, BukkitData.Hunger.class));
registerSerializer(Identifier.EXPERIENCE, new Serializer.Json<>(this, BukkitData.Experience.class));
@@ -178,11 +200,24 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
// Initialize the database
initialize(getSettings().getDatabase().getType().getDisplayName() + " database connection", (plugin) -> {
- this.database = switch (settings.getDatabase().getType()) {
- case MYSQL, MARIADB -> new MySqlDatabase(this);
- case POSTGRES -> new PostgresDatabase(this);
- case MONGO -> new MongoDbDatabase(this);
- };
+ switch (settings.getDatabase().getType()) {
+ case MYSQL:
+ case MARIADB: {
+ this.database = new MySqlDatabase(this);
+ break;
+ }
+ case POSTGRES: {
+ this.database = new PostgresDatabase(this);
+ break;
+ }
+ case MONGO: {
+ this.database = new MongoDbDatabase(this);
+ break;
+ }
+ default: {
+ // do nothing
+ }
+ }
this.database.initialize();
});
@@ -295,14 +330,14 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
// Register bStats metrics
public void registerMetrics(int metricsId) {
- if (!getPluginVersion().getMetadata().isBlank()) {
+ if (!StringUtil.isBlank(getPluginVersion().getMetadata())) {
return;
}
try {
new Metrics(this, metricsId);
} catch (Throwable e) {
- log(Level.WARNING, "Failed to register bStats metrics (%s)".formatted(e.getMessage()));
+ log(Level.WARNING, "Failed to register bStats metrics (" + e.getMessage() + ")");
}
}
diff --git a/bukkit/src/main/java/net/william278/husksync/data/BukkitData.java b/bukkit/src/main/java/net/william278/husksync/data/BukkitData.java
index 539597ce..eb86216f 100644
--- a/bukkit/src/main/java/net/william278/husksync/data/BukkitData.java
+++ b/bukkit/src/main/java/net/william278/husksync/data/BukkitData.java
@@ -33,6 +33,7 @@ import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.config.Settings.SynchronizationSettings.AttributeSettings;
import net.william278.husksync.user.BukkitUser;
+import net.william278.husksync.util.ref.RefUtil;
import org.bukkit.*;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.attribute.AttributeInstance;
@@ -92,8 +93,8 @@ public abstract class BukkitData implements Data {
stack.hasItemMeta() && Objects.requireNonNull(stack.getItemMeta()).hasEnchants() ?
stack.getItemMeta().getEnchants().keySet().stream()
.map(enchantment -> enchantment.getKey().getKey())
- .toList()
- : List.of()
+ .collect(Collectors.toList())
+ : Collections.emptyList()
) : null)
.toArray(Stack[]::new);
}
@@ -118,7 +119,8 @@ public abstract class BukkitData implements Data {
@Override
public boolean equals(Object obj) {
- if (obj instanceof BukkitData.Items items) {
+ if (obj instanceof BukkitData.Items) {
+ BukkitData.Items items = (BukkitData.Items) obj;
return Arrays.equals(contents, items.getContents());
}
return false;
@@ -189,7 +191,7 @@ public abstract class BukkitData implements Data {
@NotNull
public static BukkitData.Items.EnderChest adapt(@NotNull Collection items) {
- return adapt(items.toArray(ItemStack[]::new));
+ return adapt(items.toArray(new ItemStack[0]));
}
@NotNull
@@ -212,7 +214,7 @@ public abstract class BukkitData implements Data {
@NotNull
public static ItemArray adapt(@NotNull Collection drops) {
- return new ItemArray(drops.toArray(ItemStack[]::new));
+ return new ItemArray(drops.toArray(new ItemStack[0]));
}
@NotNull
@@ -238,26 +240,28 @@ public abstract class BukkitData implements Data {
@NotNull
public static BukkitData.PotionEffects from(@NotNull Collection sei) {
- return new BukkitData.PotionEffects(Lists.newArrayList(sei.stream().filter(e -> !e.isAmbient()).toList()));
-
+ return new BukkitData.PotionEffects(Lists.newArrayList(
+ sei.stream().filter(e -> !e.isAmbient())
+ .collect(Collectors.toList())
+ ));
}
@NotNull
public static BukkitData.PotionEffects adapt(@NotNull Collection effects) {
return from(effects.stream()
.map(effect -> {
- final PotionEffectType type = matchEffectType(effect.type());
+ final PotionEffectType type = matchEffectType(effect.getType());
return type != null ? new PotionEffect(
type,
- effect.duration(),
- effect.amplifier(),
+ effect.getDuration(),
+ effect.getAmplifier(),
effect.isAmbient(),
- effect.showParticles(),
- effect.hasIcon()
+ effect.isShowParticles(),
+ effect.isHasIcon()
) : null;
})
.filter(Objects::nonNull)
- .toList());
+ .collect(Collectors.toList()));
}
@NotNull
@@ -290,7 +294,7 @@ public abstract class BukkitData implements Data {
potionEffect.hasParticles(),
potionEffect.hasIcon()
))
- .toList();
+ .collect(Collectors.toList());
}
}
@@ -335,16 +339,19 @@ public abstract class BukkitData implements Data {
final Optional record = completed.stream()
.filter(r -> r.getKey().equals(advancement.getKey().toString()))
.findFirst();
- if (record.isEmpty()) {
- this.setAdvancement(plugin, advancement, player, user, List.of(), progress.getAwardedCriteria());
+ if (!record.isPresent()) {
+ this.setAdvancement(plugin, advancement, player, user, Collections.emptyList(), progress.getAwardedCriteria());
return;
}
final Map criteria = record.get().getCompletedCriteria();
this.setAdvancement(
plugin, advancement, player, user,
- criteria.keySet().stream().filter(key -> !progress.getAwardedCriteria().contains(key)).toList(),
- progress.getAwardedCriteria().stream().filter(key -> !criteria.containsKey(key)).toList()
+ criteria.keySet().stream()
+ .filter(key -> !progress.getAwardedCriteria().contains(key))
+ .collect(Collectors.toList()),
+ progress.getAwardedCriteria().stream().filter(key -> !criteria.containsKey(key))
+ .collect(Collectors.toList())
);
}));
}
@@ -426,7 +433,7 @@ public abstract class BukkitData implements Data {
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
try {
final org.bukkit.Location location = new org.bukkit.Location(
- Bukkit.getWorld(world.name()), x, y, z, yaw, pitch
+ Bukkit.getWorld(world.getName()), x, y, z, yaw, pitch
);
user.getPlayer().teleport(location);
} catch (Throwable e) {
@@ -457,10 +464,25 @@ public abstract class BukkitData implements Data {
items = Maps.newHashMap(), entities = Maps.newHashMap();
Registry.STATISTIC.forEach(id -> {
switch (id.getType()) {
- case UNTYPED -> addStatistic(player, id, generic);
- case BLOCK -> addMaterialStatistic(player, id, blocks, true);
- case ITEM -> addMaterialStatistic(player, id, items, false);
- case ENTITY -> addEntityStatistic(player, id, entities);
+ case UNTYPED: {
+ addStatistic(player, id, generic);
+ break;
+ }
+ case BLOCK: {
+ addMaterialStatistic(player, id, blocks, true);
+ break;
+ }
+ case ITEM: {
+ addMaterialStatistic(player, id, items, false);
+ break;
+ }
+ case ENTITY: {
+ addEntityStatistic(player, id, entities);
+ break;
+ }
+ default: {
+ // do nothing
+ }
}
});
return new BukkitData.Statistics(generic, blocks, items, entities);
@@ -527,9 +549,22 @@ public abstract class BukkitData implements Data {
try {
switch (type) {
- case UNTYPED -> player.setStatistic(stat, value);
- case BLOCK, ITEM -> player.setStatistic(stat, Objects.requireNonNull(matchMaterial(key[0])), value);
- case ENTITY -> player.setStatistic(stat, Objects.requireNonNull(matchEntityType(key[0])), value);
+ case UNTYPED: {
+ player.setStatistic(stat, value);
+ break;
+ }
+ case BLOCK:
+ case ITEM: {
+ player.setStatistic(stat, Objects.requireNonNull(matchMaterial(key[0])), value);
+ break;
+ }
+ case ENTITY: {
+ player.setStatistic(stat, Objects.requireNonNull(matchEntityType(key[0])), value);
+ break;
+ }
+ default: {
+ // do nothing
+ }
}
} catch (Throwable ignored) {
}
@@ -591,7 +626,7 @@ public abstract class BukkitData implements Data {
}
public Optional getAttribute(@NotNull org.bukkit.attribute.Attribute id) {
- return attributes.stream().filter(attribute -> attribute.name().equals(id.getKey().toString())).findFirst();
+ return attributes.stream().filter(attribute -> attribute.getName().equals(id.getKey().toString())).findFirst();
}
@SuppressWarnings("unused")
@@ -648,10 +683,10 @@ public abstract class BukkitData implements Data {
if (instance == null) {
return;
}
- instance.setBaseValue(attribute == null ? instance.getDefaultValue() : attribute.baseValue());
+ instance.setBaseValue(attribute == null ? instance.getDefaultValue() : attribute.getBaseValue());
instance.getModifiers().forEach(instance::removeModifier);
if (attribute != null) {
- attribute.modifiers().stream()
+ attribute.getModifiers().stream()
.filter(mod -> instance.getModifiers().stream().map(AttributeModifier::getName)
.noneMatch(n -> n.equals(mod.name())))
.distinct()
@@ -722,19 +757,19 @@ public abstract class BukkitData implements Data {
}
/**
- * @deprecated Use {@link #from(double, double, boolean)} instead
+ * @deprecated Use {@link #from(double, double, boolean)} instead, since 3.5.4
*/
@NotNull
- @Deprecated(since = "3.5.4")
+ @Deprecated
public static BukkitData.Health from(double health, double scale) {
return from(health, scale, false);
}
/**
- * @deprecated Use {@link #from(double, double, boolean)} instead
+ * @deprecated Use {@link #from(double, double, boolean)} instead, since 3.5
*/
@NotNull
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
public static BukkitData.Health from(double health, @SuppressWarnings("unused") double max, double scale) {
return from(health, scale, false);
}
@@ -757,7 +792,7 @@ public abstract class BukkitData implements Data {
try {
player.setHealth(Math.min(health, player.getMaxHealth()));
} catch (Throwable e) {
- plugin.log(Level.WARNING, "Error setting %s's health to %s".formatted(player.getName(), health), e);
+ plugin.log(Level.WARNING, "Error setting " + player.getName() + "'s health to " + health, e);
}
// Set health scale
@@ -766,7 +801,7 @@ public abstract class BukkitData implements Data {
player.setHealthScale(scale);
player.setHealthScaled(isHealthScaled);
} catch (Throwable e) {
- plugin.log(Level.WARNING, "Error setting %s's health scale to %s".formatted(player.getName(), scale), e);
+ plugin.log(Level.WARNING, "Error setting " + player.getName() + "'s health scale to " + scale, e);
}
}
@@ -855,7 +890,7 @@ public abstract class BukkitData implements Data {
}
@NotNull
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
@SuppressWarnings("unused")
public static BukkitData.GameMode from(@NotNull String gameMode, boolean allowFlight, boolean isFlying) {
return new BukkitData.GameMode(gameMode);
diff --git a/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java b/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java
index 7f900165..e9c4f058 100644
--- a/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java
+++ b/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java
@@ -33,6 +33,7 @@ import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.api.HuskSyncAPI;
+import net.william278.husksync.util.ref.RefUtil;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
@@ -127,13 +128,13 @@ public class BukkitSerializer {
@Nullable
default ItemStack[] getItems(@NotNull ReadWriteNBT tag, @NotNull Version mcVersion) {
if (mcVersion.compareTo(getPlugin().getMinecraftVersion()) < 0) {
- return upgradeItemStacks((NBTCompound) tag, mcVersion);
+ return private$upgradeItemStacks((NBTCompound) tag, mcVersion);
}
return NBT.itemStackArrayFromNBT(tag);
}
@NotNull
- private ItemStack @NotNull [] upgradeItemStacks(@NotNull NBTCompound itemsNbt, @NotNull Version mcVersion) {
+ default ItemStack @NotNull [] private$upgradeItemStacks(@NotNull NBTCompound itemsNbt, @NotNull Version mcVersion) {
final ReadWriteNBTCompoundList items = itemsNbt.getCompoundList("items");
final ItemStack[] itemStacks = new ItemStack[itemsNbt.getInteger("size")];
for (int i = 0; i < items.size(); i++) {
@@ -142,7 +143,7 @@ public class BukkitSerializer {
continue;
}
try {
- itemStacks[i] = NBT.itemStackFromNBT(upgradeItemData(items.get(i), mcVersion));
+ itemStacks[i] = NBT.itemStackFromNBT(private$upgradeItemData(items.get(i), mcVersion));
} catch (Throwable e) {
itemStacks[i] = new ItemStack(Material.AIR);
}
@@ -151,23 +152,47 @@ public class BukkitSerializer {
}
@NotNull
- private ReadWriteNBT upgradeItemData(@NotNull ReadWriteNBT tag, @NotNull Version mcVersion)
+ default ReadWriteNBT private$upgradeItemData(@NotNull ReadWriteNBT tag, @NotNull Version mcVersion)
throws NoSuchFieldException, IllegalAccessException {
- return DataFixerUtil.fixUpItemData(tag, getDataVersion(mcVersion), DataFixerUtil.getCurrentVersion());
+ return DataFixerUtil.fixUpItemData(tag, private$getDataVersion(mcVersion), DataFixerUtil.getCurrentVersion());
}
- private int getDataVersion(@NotNull Version mcVersion) {
- return switch (mcVersion.toStringWithoutMetadata()) {
- case "1.16", "1.16.1", "1.16.2", "1.16.3", "1.16.4", "1.16.5" -> DataFixerUtil.VERSION1_16_5;
- case "1.17", "1.17.1" -> DataFixerUtil.VERSION1_17_1;
- case "1.18", "1.18.1", "1.18.2" -> DataFixerUtil.VERSION1_18_2;
- case "1.19", "1.19.1", "1.19.2" -> DataFixerUtil.VERSION1_19_2;
- case "1.20", "1.20.1", "1.20.2" -> DataFixerUtil.VERSION1_20_2;
- case "1.20.3", "1.20.4" -> DataFixerUtil.VERSION1_20_4;
- case "1.20.5", "1.20.6" -> DataFixerUtil.VERSION1_20_5;
- case "1.21" -> DataFixerUtil.VERSION1_21;
- default -> DataFixerUtil.getCurrentVersion();
- };
+ default int private$getDataVersion(@NotNull Version mcVersion) {
+ switch (mcVersion.toStringWithoutMetadata()) {
+ case "1.12": case "1.12.1": case "1.12.2":
+ case "1.13": case "1.13.1": case "1.13.2":
+ case "1.14": case "1.14.1": case "1.14.2": case "1.14.3": case "1.14.4":
+ case "1.15": case "1.15.1": case "1.15.2": {
+ return DataFixerUtil.VERSION1_12_2;
+ }
+ case "1.16": case "1.16.1": case "1.16.2": case "1.16.3": case "1.16.4": case "1.16.5": {
+ return DataFixerUtil.VERSION1_16_5;
+ }
+ case "1.17": case "1.17.1": {
+ return DataFixerUtil.VERSION1_17_1;
+ }
+ case "1.18": case "1.18.1": case "1.18.2": {
+ return DataFixerUtil.VERSION1_18_2;
+ }
+ case "1.19": case "1.19.1": case "1.19.2": {
+ return DataFixerUtil.VERSION1_19_2;
+ }
+ case "1.20": case "1.20.1": case "1.20.2": {
+ return DataFixerUtil.VERSION1_20_2;
+ }
+ case "1.20.3": case "1.20.4": {
+ return DataFixerUtil.VERSION1_20_4;
+ }
+ case "1.20.5": case "1.20.6": {
+ return DataFixerUtil.VERSION1_20_5;
+ }
+ case "1.21": {
+ return DataFixerUtil.VERSION1_21;
+ }
+ default: {
+ return DataFixerUtil.getCurrentVersion();
+ }
+ }
}
@NotNull
@@ -176,7 +201,7 @@ public class BukkitSerializer {
public static class PotionEffects extends BukkitSerializer implements Serializer {
- private static final TypeToken> TYPE = new TypeToken<>() {
+ private static final TypeToken> TYPE = new TypeToken>() {
};
public PotionEffects(@NotNull HuskSync plugin) {
@@ -200,7 +225,7 @@ public class BukkitSerializer {
public static class Advancements extends BukkitSerializer implements Serializer {
- private static final TypeToken> TYPE = new TypeToken<>() {
+ private static final TypeToken> TYPE = new TypeToken>() {
};
public Advancements(@NotNull HuskSync plugin) {
@@ -241,9 +266,9 @@ public class BukkitSerializer {
}
/**
- * @deprecated Use {@link Serializer.Json} in the common module instead
+ * @deprecated Use {@link Serializer.Json} in the common module instead, since 2.6
*/
- @Deprecated(since = "2.6")
+ @Deprecated
public class Json extends Serializer.Json {
public Json(@NotNull HuskSync plugin, @NotNull Class type) {
diff --git a/bukkit/src/main/java/net/william278/husksync/data/BukkitUserDataHolder.java b/bukkit/src/main/java/net/william278/husksync/data/BukkitUserDataHolder.java
index 9eb93aaf..54f3ef94 100644
--- a/bukkit/src/main/java/net/william278/husksync/data/BukkitUserDataHolder.java
+++ b/bukkit/src/main/java/net/william278/husksync/data/BukkitUserDataHolder.java
@@ -32,22 +32,22 @@ public interface BukkitUserDataHolder extends UserDataHolder {
@Override
default Optional extends Data> getData(@NotNull Identifier id) {
if (!id.isCustom()) {
- return switch (id.getKeyValue()) {
- case "inventory" -> getInventory();
- case "ender_chest" -> getEnderChest();
- case "potion_effects" -> getPotionEffects();
- case "advancements" -> getAdvancements();
- case "location" -> getLocation();
- case "statistics" -> getStatistics();
- case "health" -> getHealth();
- case "hunger" -> getHunger();
- case "attributes" -> getAttributes();
- case "experience" -> getExperience();
- case "game_mode" -> getGameMode();
- case "flight_status" -> getFlightStatus();
- case "persistent_data" -> getPersistentData();
- default -> throw new IllegalStateException(String.format("Unexpected data type: %s", id));
- };
+ switch (id.getKeyValue()) {
+ case "inventory": return getInventory();
+ case "ender_chest": return getEnderChest();
+ case "potion_effects": return getPotionEffects();
+ case "advancements": return getAdvancements();
+ case "location": return getLocation();
+ case "statistics": return getStatistics();
+ case "health": return getHealth();
+ case "hunger": return getHunger();
+ case "attributes": return getAttributes();
+ case "experience": return getExperience();
+ case "game_mode": return getGameMode();
+ case "flight_status": return getFlightStatus();
+ case "persistent_data": return getPersistentData();
+ default: throw new IllegalStateException(String.format("Unexpected data type: %s", id));
+ }
}
return Optional.ofNullable(getCustomDataStore().get(id));
}
@@ -154,9 +154,9 @@ public interface BukkitUserDataHolder extends UserDataHolder {
Player getPlayer();
/**
- * @deprecated Use {@link #getPlayer()} instead
+ * @deprecated Use {@link #getPlayer()} instead, since 3.6
*/
- @Deprecated(since = "3.6")
+ @Deprecated
@NotNull
default Player getBukkitPlayer() {
return getPlayer();
diff --git a/bukkit/src/main/java/net/william278/husksync/event/BukkitEventDispatcher.java b/bukkit/src/main/java/net/william278/husksync/event/BukkitEventDispatcher.java
index 28a78eae..e23c5d95 100644
--- a/bukkit/src/main/java/net/william278/husksync/event/BukkitEventDispatcher.java
+++ b/bukkit/src/main/java/net/william278/husksync/event/BukkitEventDispatcher.java
@@ -30,7 +30,7 @@ public interface BukkitEventDispatcher extends EventDispatcher {
@Override
default boolean fireIsCancelled(@NotNull T event) {
Bukkit.getPluginManager().callEvent((org.bukkit.event.Event) event);
- return event instanceof Cancellable cancellable && cancellable.isCancelled();
+ return event instanceof Cancellable && ((Cancellable) event).isCancelled();
}
@NotNull
diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java
index 7bdea7d7..16eb7ae2 100644
--- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java
+++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java
@@ -23,6 +23,7 @@ import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.data.BukkitData;
import net.william278.husksync.user.BukkitUser;
import net.william278.husksync.user.OnlineUser;
+import net.william278.husksync.util.MaterialUtil;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -82,7 +83,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
public void handlePlayerQuit(@NotNull BukkitUser bukkitUser) {
final Player player = bukkitUser.getPlayer();
final ItemStack itemOnCursor = player.getItemOnCursor();
- if (!bukkitUser.isLocked() && !itemOnCursor.getType().isAir()) {
+ if (!bukkitUser.isLocked() && !MaterialUtil.isAir(itemOnCursor.getType())) {
player.setItemOnCursor(null);
player.getWorld().dropItem(player.getLocation(), itemOnCursor);
plugin.debug("Dropped " + itemOnCursor + " for " + player.getName() + " on quit");
diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java
index f20ea4a1..52a247a3 100644
--- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java
+++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java
@@ -60,8 +60,8 @@ public class BukkitLockedEventListener implements LockedHandler, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) {
final Projectile projectile = event.getEntity();
- if (projectile.getShooter() instanceof Player player) {
- cancelPlayerEvent(player.getUniqueId(), event);
+ if (projectile.getShooter() instanceof Player) {
+ cancelPlayerEvent(((Player) projectile.getShooter()).getUniqueId(), event);
}
}
@@ -72,8 +72,8 @@ public class BukkitLockedEventListener implements LockedHandler, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPickupItem(@NotNull EntityPickupItemEvent event) {
- if (event.getEntity() instanceof Player player) {
- cancelPlayerEvent(player.getUniqueId(), event);
+ if (event.getEntity() instanceof Player) {
+ cancelPlayerEvent(event.getEntity().getUniqueId(), event);
}
}
@@ -104,8 +104,8 @@ public class BukkitLockedEventListener implements LockedHandler, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onInventoryOpen(@NotNull InventoryOpenEvent event) {
- if (event.getPlayer() instanceof Player player) {
- cancelPlayerEvent(player.getUniqueId(), event);
+ if (event.getPlayer() instanceof Player) {
+ cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
}
}
@@ -116,8 +116,8 @@ public class BukkitLockedEventListener implements LockedHandler, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
- if (event.getEntity() instanceof Player player) {
- cancelPlayerEvent(player.getUniqueId(), event);
+ if (event.getEntity() instanceof Player) {
+ cancelPlayerEvent(event.getEntity().getUniqueId(), event);
}
}
diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitPacketEventsLockedPacketListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitPacketEventsLockedPacketListener.java
index dc66a8c4..50597a53 100644
--- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitPacketEventsLockedPacketListener.java
+++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitPacketEventsLockedPacketListener.java
@@ -25,6 +25,7 @@ import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import net.william278.husksync.BukkitHuskSync;
@@ -59,7 +60,7 @@ public class BukkitPacketEventsLockedPacketListener extends BukkitLockedEventLis
private static class PlayerPacketAdapter extends PacketListenerAbstract {
- private static final Set ALLOWED_PACKETS = Set.of(
+ private static final Set ALLOWED_PACKETS = ImmutableSet.of(
PacketType.Play.Client.KEEP_ALIVE, PacketType.Play.Client.PONG, PacketType.Play.Client.PLUGIN_MESSAGE, // Connection packets
PacketType.Play.Client.CHAT_MESSAGE, PacketType.Play.Client.CHAT_COMMAND, PacketType.Play.Client.CHAT_SESSION_UPDATE, // Chat / command packets
PacketType.Play.Client.PLAYER_POSITION, PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION, PacketType.Play.Client.PLAYER_ROTATION, // Movement packets
@@ -79,10 +80,10 @@ public class BukkitPacketEventsLockedPacketListener extends BukkitLockedEventLis
@Override
public void onPacketReceive(PacketReceiveEvent event) {
- if (!(event.getPacketType() instanceof PacketType.Play.Client client)) {
+ if (!(event.getPacketType() instanceof PacketType.Play.Client)) {
return;
}
- if (!CANCEL_PACKETS.contains(client)) {
+ if (!CANCEL_PACKETS.contains((PacketType.Play.Client) event.getPacketType())) {
return;
}
if (listener.cancelPlayerEvent(event.getUser().getUUID())) {
@@ -92,10 +93,10 @@ public class BukkitPacketEventsLockedPacketListener extends BukkitLockedEventLis
@Override
public void onPacketSend(PacketSendEvent event) {
- if (!(event.getPacketType() instanceof PacketType.Play.Client client)) {
+ if (!(event.getPacketType() instanceof PacketType.Play.Client)) {
return;
}
- if (!CANCEL_PACKETS.contains(client)) {
+ if (!CANCEL_PACKETS.contains((PacketType.Play.Client) event.getPacketType())) {
return;
}
if (listener.cancelPlayerEvent(event.getUser().getUUID())) {
diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitProtocolLibLockedPacketListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitProtocolLibLockedPacketListener.java
index eac661b0..04d2ae7a 100644
--- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitProtocolLibLockedPacketListener.java
+++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitProtocolLibLockedPacketListener.java
@@ -24,6 +24,7 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import net.william278.husksync.BukkitHuskSync;
import org.jetbrains.annotations.NotNull;
@@ -50,7 +51,7 @@ public class BukkitProtocolLibLockedPacketListener extends BukkitLockedEventList
private static class PlayerPacketAdapter extends PacketAdapter {
// Packets we want the player to still be able to send/receiver to/from the server
- private static final Set ALLOWED_PACKETS = Set.of(
+ private static final Set ALLOWED_PACKETS = ImmutableSet.of(
Client.KEEP_ALIVE, Client.PONG, Client.CUSTOM_PAYLOAD, // Connection packets
Client.CHAT_COMMAND, Client.CLIENT_COMMAND, Client.CHAT, Client.CHAT_SESSION_UPDATE, // Chat / command packets
Client.POSITION, Client.POSITION_LOOK, Client.LOOK, // Movement packets
diff --git a/bukkit/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java b/bukkit/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
index 3b9eff40..49183fcb 100644
--- a/bukkit/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
+++ b/bukkit/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
@@ -22,6 +22,7 @@ package net.william278.husksync.migrator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zaxxer.hikari.HikariDataSource;
+import lombok.Value;
import me.william278.husksync.bukkit.data.DataSerializer;
import net.william278.hslmigrator.HSLConverter;
import net.william278.husksync.HuskSync;
@@ -43,6 +44,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import static net.william278.husksync.config.Settings.DatabaseSettings;
@@ -95,14 +97,12 @@ public class LegacyMigrator extends Migrator {
plugin.log(Level.INFO, "Downloading raw data from the legacy database (this might take a while)...");
final List dataToMigrate = Lists.newArrayList();
try (final Connection connection = connectionPool.getConnection()) {
- try (final PreparedStatement statement = connection.prepareStatement("""
- SELECT `uuid`, `username`, `inventory`, `ender_chest`, `health`, `max_health`, `health_scale`, `hunger`, `saturation`, `saturation_exhaustion`, `selected_slot`, `status_effects`, `total_experience`, `exp_level`, `exp_progress`, `game_mode`, `statistics`, `is_flying`, `advancements`, `location`
- FROM `%source_players_table%`
- INNER JOIN `%source_data_table%`
- ON `%source_players_table%`.`id` = `%source_data_table%`.`player_id`
- WHERE `username` IS NOT NULL;
- """.replaceAll(Pattern.quote("%source_players_table%"), sourcePlayersTable)
- .replaceAll(Pattern.quote("%source_data_table%"), sourceDataTable))) {
+ try (final PreparedStatement statement = connection.prepareStatement(
+ "SELECT `uuid`, `username`, `inventory`, `ender_chest`, `health`, `max_health`, `health_scale`, `hunger`, `saturation`, `saturation_exhaustion`, `selected_slot`, `status_effects`, `total_experience`, `exp_level`, `exp_progress`, `game_mode`, `statistics`, `is_flying`, `advancements`, `location` " +
+ "FROM `" + sourcePlayersTable + "` " +
+ "INNER JOIN `" + sourceDataTable + "` " +
+ "ON `" + sourcePlayersTable + "`.`id` = `" + sourceDataTable + "`.`player_id` " +
+ "WHERE `username` IS NOT NULL;")) {
try (final ResultSet resultSet = statement.executeQuery()) {
int playersMigrated = 0;
while (resultSet.next()) {
@@ -142,11 +142,11 @@ public class LegacyMigrator extends Migrator {
final AtomicInteger playersConverted = new AtomicInteger();
dataToMigrate.forEach(data -> {
final DataSnapshot.Packed convertedData = data.toUserData(hslConverter, plugin);
- plugin.getDatabase().ensureUser(data.user());
+ plugin.getDatabase().ensureUser(data.getUser());
try {
- plugin.getDatabase().addSnapshot(data.user(), convertedData);
+ plugin.getDatabase().addSnapshot(data.getUser(), convertedData);
} catch (Throwable e) {
- plugin.log(Level.SEVERE, "Failed to migrate legacy data for " + data.user().getUsername() + ": " + e.getMessage());
+ plugin.log(Level.SEVERE, "Failed to migrate legacy data for " + data.getUser().getUsername() + ": " + e.getMessage());
return;
}
@@ -167,41 +167,53 @@ public class LegacyMigrator extends Migrator {
@Override
public void handleConfigurationCommand(@NotNull String[] args) {
if (args.length == 2) {
- if (switch (args[0].toLowerCase(Locale.ENGLISH)) {
- case "host" -> {
+ boolean $yield;
+ switch (args[0].toLowerCase(Locale.ENGLISH)) {
+ case "host": {
this.sourceHost = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "port" -> {
+ case "port": {
try {
this.sourcePort = Integer.parseInt(args[1]);
- yield true;
+ $yield = true;
} catch (NumberFormatException e) {
- yield false;
+ $yield = false;
}
+ break;
}
- case "username" -> {
+ case "username": {
this.sourceUsername = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "password" -> {
+ case "password": {
this.sourcePassword = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "database" -> {
+ case "database": {
this.sourceDatabase = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "players_table" -> {
+ case "players_table": {
this.sourcePlayersTable = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "data_table" -> {
+ case "data_table": {
this.sourceDataTable = args[1];
- yield true;
+ $yield = true;
+ break;
}
- default -> false;
- }) {
+ default: {
+ $yield = false;
+ break;
+ }
+ }
+ if ($yield) {
plugin.log(Level.INFO, getHelpMenu());
plugin.log(Level.INFO, "Successfully set " + args[0] + " to " +
obfuscateDataString(args[1]));
@@ -229,59 +241,65 @@ public class LegacyMigrator extends Migrator {
@NotNull
@Override
public String getHelpMenu() {
- return """
- === HuskSync v1.x --> v3.x Migration Wizard =========
- This will migrate all user data from HuskSync v1.x to
- HuskSync v3.x's new format. To perform the migration,
- please follow the steps below carefully.
-
- [!] Existing data in the database will be wiped. [!]
-
- STEP 1] Please ensure no players are on any servers.
-
- STEP 2] HuskSync will need to connect to the database
- used to hold the existing, legacy HuskSync data.
- If this is the same database as the one you are
- currently using, you probably don't need to change
- anything.
- Please check that the credentials below are the
- correct credentials of the source legacy HuskSync
- database.
- - host: %source_host%
- - port: %source_port%
- - username: %source_username%
- - password: %source_password%
- - database: %source_database%
- - players_table: %source_players_table%
- - data_table: %source_data_table%
- If any of these are not correct, please correct them
- using the command:
- "husksync migrate legacy set "
- (e.g.: "husksync migrate legacy set host 1.2.3.4")
-
- STEP 3] HuskSync will migrate data into the database
- tables configures in the config.yml file of this
- server. Please make sure you're happy with this
- before proceeding.
-
- STEP 4] To start the migration, please run:
- "husksync migrate legacy start"
- """.replaceAll(Pattern.quote("%source_host%"), obfuscateDataString(sourceHost))
- .replaceAll(Pattern.quote("%source_port%"), Integer.toString(sourcePort))
- .replaceAll(Pattern.quote("%source_username%"), obfuscateDataString(sourceUsername))
- .replaceAll(Pattern.quote("%source_password%"), obfuscateDataString(sourcePassword))
- .replaceAll(Pattern.quote("%source_database%"), sourceDatabase)
- .replaceAll(Pattern.quote("%source_players_table%"), sourcePlayersTable)
- .replaceAll(Pattern.quote("%source_data_table%"), sourceDataTable);
+ return "=== HuskSync v1.x --> v3.x Migration Wizard =========\n" +
+ "This will migrate all user data from HuskSync v1.x to\n" +
+ "HuskSync v3.x's new format. To perform the migration,\n" +
+ "please follow the steps below carefully.\n" +
+ "\n" +
+ "[!] Existing data in the database will be wiped. [!]\n" +
+ "\n" +
+ "STEP 1] Please ensure no players are on any servers.\n" +
+ "\n" +
+ "STEP 2] HuskSync will need to connect to the database\n" +
+ "used to hold the existing, legacy HuskSync data.\n" +
+ "If this is the same database as the one you are\n" +
+ "currently using, you probably don't need to change\n" +
+ "anything.\n" +
+ "Please check that the credentials below are the\n" +
+ "correct credentials of the source legacy HuskSync\n" +
+ "database.\n" +
+ "- host: " + obfuscateDataString(sourceHost) + "\n" +
+ "- port: " + sourcePort + "\n" +
+ "- username: " + obfuscateDataString(sourceUsername) + "\n" +
+ "- password: " + obfuscateDataString(sourcePassword) + "\n" +
+ "- database: " + sourceDatabase + "\n" +
+ "- players_table: " + sourcePlayersTable + "\n" +
+ "- data_table: " + sourceDataTable + "\n" +
+ "If any of these are not correct, please correct them\n" +
+ "using the command:\n" +
+ "\"husksync migrate legacy set \"\n" +
+ "(e.g.: \"husksync migrate legacy set host 1.2.3.4\")\n" +
+ "\n" +
+ "STEP 3] HuskSync will migrate data into the database\n" +
+ "tables configures in the config.yml file of this\n" +
+ "server. Please make sure you're happy with this\n" +
+ "before proceeding.\n" +
+ "\n" +
+ "STEP 4] To start the migration, please run:\n" +
+ "\"husksync migrate legacy start\"\n";
}
- private record LegacyData(@NotNull User user,
- @NotNull String serializedInventory, @NotNull String serializedEnderChest,
- double health, double maxHealth, double healthScale, int hunger, float saturation,
- float saturationExhaustion, int selectedSlot, @NotNull String serializedPotionEffects,
- int totalExp, int expLevel, float expProgress,
- @NotNull String gameMode, @NotNull String serializedStatistics, boolean isFlying,
- @NotNull String serializedAdvancements, @NotNull String serializedLocation) {
+ @Value
+ private class LegacyData {
+ @NotNull User user;
+ @NotNull String serializedInventory;
+ @NotNull String serializedEnderChest;
+ double health;
+ double maxHealth;
+ double healthScale;
+ int hunger;
+ float saturation;
+ float saturationExhaustion;
+ int selectedSlot;
+ @NotNull String serializedPotionEffects;
+ int totalExp;
+ int expLevel;
+ float expProgress;
+ @NotNull String gameMode;
+ @NotNull String serializedStatistics;
+ boolean isFlying;
+ @NotNull String serializedAdvancements;
+ @NotNull String serializedLocation;
@NotNull
public DataSnapshot.Packed toUserData(@NotNull HSLConverter converter, @NotNull HuskSync plugin) {
@@ -319,7 +337,7 @@ public class LegacyMigrator extends Migrator {
.advancements(BukkitData.Advancements.from(converter
.deserializeAdvancementData(serializedAdvancements).stream()
.map(data -> Data.Advancements.Advancement.adapt(data.key(), data.criteriaMap()))
- .toList()))
+ .collect(Collectors.toList())))
// Stats
.statistics(BukkitData.Statistics.from(
diff --git a/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java b/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
index d32f1d9d..baa2e8ad 100644
--- a/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
+++ b/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
@@ -21,6 +21,7 @@ package net.william278.husksync.migrator;
import com.google.common.collect.Lists;
import com.zaxxer.hikari.HikariDataSource;
+import lombok.Value;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.BukkitData;
@@ -104,16 +105,14 @@ public class MpdbMigrator extends Migrator {
plugin.log(Level.INFO, "Downloading raw data from the MySQLPlayerDataBridge database (this might take a while)...");
final List dataToMigrate = Lists.newArrayList();
try (final Connection connection = connectionPool.getConnection()) {
- try (final PreparedStatement statement = connection.prepareStatement("""
- SELECT `%source_inventory_table%`.`player_uuid`, `%source_inventory_table%`.`player_name`, `inventory`, `armor`, `enderchest`, `exp_lvl`, `exp`, `total_exp`
- FROM `%source_inventory_table%`
- INNER JOIN `%source_ender_chest_table%`
- ON `%source_inventory_table%`.`player_uuid` = `%source_ender_chest_table%`.`player_uuid`
- INNER JOIN `%source_xp_table%`
- ON `%source_inventory_table%`.`player_uuid` = `%source_xp_table%`.`player_uuid`;
- """.replaceAll(Pattern.quote("%source_inventory_table%"), sourceInventoryTable)
- .replaceAll(Pattern.quote("%source_ender_chest_table%"), sourceEnderChestTable)
- .replaceAll(Pattern.quote("%source_xp_table%"), sourceExperienceTable))) {
+ try (final PreparedStatement statement = connection.prepareStatement(
+ "SELECT `" + sourceInventoryTable + "`.`player_uuid`, `" + sourceInventoryTable + "`.`player_name`, `inventory`, `armor`, `enderchest`, `exp_lvl`, `exp`, `total_exp` " +
+ "FROM `" + sourceInventoryTable + "` " +
+ " INNER JOIN `" + sourceEnderChestTable + "` " +
+ " ON `" + sourceInventoryTable + "`.`player_uuid` = `" + sourceEnderChestTable + "`.`player_uuid` " +
+ " INNER JOIN `" + sourceExperienceTable + "` " +
+ " ON `" + sourceInventoryTable + "`.`player_uuid` = `" + sourceExperienceTable + "`.`player_uuid`;"
+ )) {
try (final ResultSet resultSet = statement.executeQuery()) {
int playersMigrated = 0;
while (resultSet.next()) {
@@ -141,8 +140,8 @@ public class MpdbMigrator extends Migrator {
final AtomicInteger playersConverted = new AtomicInteger();
dataToMigrate.forEach(data -> {
final DataSnapshot.Packed convertedData = data.toUserData(mpdbConverter, plugin);
- plugin.getDatabase().ensureUser(data.user());
- plugin.getDatabase().addSnapshot(data.user(), convertedData);
+ plugin.getDatabase().ensureUser(data.getUser());
+ plugin.getDatabase().addSnapshot(data.getUser(), convertedData);
playersConverted.getAndIncrement();
if (playersConverted.get() % 50 == 0) {
plugin.log(Level.INFO, "Converted MySQLPlayerDataBridge data for " + playersConverted + " players...");
@@ -160,45 +159,57 @@ public class MpdbMigrator extends Migrator {
@Override
public void handleConfigurationCommand(@NotNull String[] args) {
if (args.length == 2) {
- if (switch (args[0].toLowerCase(Locale.ENGLISH)) {
- case "host" -> {
+ boolean $yield;
+ switch (args[0].toLowerCase(Locale.ENGLISH)) {
+ case "host": {
this.sourceHost = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "port" -> {
+ case "port": {
try {
this.sourcePort = Integer.parseInt(args[1]);
- yield true;
+ $yield = true;
} catch (NumberFormatException e) {
- yield false;
+ $yield = false;
}
+ break;
}
- case "username" -> {
+ case "username": {
this.sourceUsername = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "password" -> {
+ case "password": {
this.sourcePassword = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "database" -> {
+ case "database": {
this.sourceDatabase = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "inventory_table" -> {
+ case "inventory_table": {
this.sourceInventoryTable = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "ender_chest_table" -> {
+ case "ender_chest_table": {
this.sourceEnderChestTable = args[1];
- yield true;
+ $yield = true;
+ break;
}
- case "experience_table" -> {
+ case "experience_table": {
this.sourceExperienceTable = args[1];
- yield true;
+ $yield = true;
+ break;
}
- default -> false;
- }) {
+ default: {
+ $yield = false;
+ }
+ }
+ if ($yield) {
plugin.log(Level.INFO, getHelpMenu());
plugin.log(Level.INFO, "Successfully set " + args[0] + " to " +
obfuscateDataString(args[1]));
@@ -226,77 +237,81 @@ public class MpdbMigrator extends Migrator {
@NotNull
@Override
public String getHelpMenu() {
- return """
- === MySQLPlayerDataBridge Migration Wizard ==========
- NOTE: This migrator currently WORKS WITH MPDB version
- v4.9.2 and below!
-
- This will migrate inventories, ender chests and XP
- from the MySQLPlayerDataBridge plugin to HuskSync.
-
- To prevent excessive migration times, other non-vital
- data will not be transferred.
-
- [!] Existing data in the database will be wiped. [!]
-
- STEP 1] Please ensure no players are on any servers.
-
- STEP 2] HuskSync will need to connect to the database
- used to hold the source MySQLPlayerDataBridge data.
- Please check these database parameters are OK:
- - host: %source_host%
- - port: %source_port%
- - username: %source_username%
- - password: %source_password%
- - database: %source_database%
- - inventory_table: %source_inventory_table%
- - ender_chest_table: %source_ender_chest_table%
- - experience_table: %source_xp_table%
- If any of these are not correct, please correct them
- using the command:
- "husksync migrate mpdb set "
- (e.g.: "husksync migrate set mpdb host 1.2.3.4")
-
- STEP 3] HuskSync will migrate data into the database
- tables configures in the config.yml file of this
- server. Please make sure you're happy with this
- before proceeding.
-
- STEP 4] To start the migration, please run:
- "husksync migrate start mpdb"
-
- NOTE: This migrator currently WORKS WITH MPDB version
- v4.9.2 and below!
- """.replaceAll(Pattern.quote("%source_host%"), obfuscateDataString(sourceHost))
- .replaceAll(Pattern.quote("%source_port%"), Integer.toString(sourcePort))
- .replaceAll(Pattern.quote("%source_username%"), obfuscateDataString(sourceUsername))
- .replaceAll(Pattern.quote("%source_password%"), obfuscateDataString(sourcePassword))
- .replaceAll(Pattern.quote("%source_database%"), sourceDatabase)
- .replaceAll(Pattern.quote("%source_inventory_table%"), sourceInventoryTable)
- .replaceAll(Pattern.quote("%source_ender_chest_table%"), sourceEnderChestTable)
- .replaceAll(Pattern.quote("%source_xp_table%"), sourceExperienceTable);
+ return "=== MySQLPlayerDataBridge Migration Wizard ==========\n" +
+ "NOTE: This migrator currently WORKS WITH MPDB version\n" +
+ "v4.9.2 and below!\n" +
+ "\n" +
+ "This will migrate inventories, ender chests and XP\n" +
+ "from the MySQLPlayerDataBridge plugin to HuskSync.\n" +
+ "\n" +
+ "To prevent excessive migration times, other non-vital\n" +
+ "data will not be transferred.\n" +
+ "\n" +
+ "[!] Existing data in the database will be wiped. [!]\n" +
+ "\n" +
+ "STEP 1] Please ensure no players are on any servers.\n" +
+ "\n" +
+ "STEP 2] HuskSync will need to connect to the database\n" +
+ "used to hold the source MySQLPlayerDataBridge data.\n" +
+ "Please check these database parameters are OK:\n" +
+ "- host: " + obfuscateDataString(sourceHost) + "\n" +
+ "- port: " + sourcePort + "\n" +
+ "- username: " + obfuscateDataString(sourceUsername) + "\n" +
+ "- password: " + obfuscateDataString(sourcePassword) + "\n" +
+ "- database: " + sourceDatabase + "\n" +
+ "- inventory_table: " + sourceInventoryTable + "\n" +
+ "- ender_chest_table: " + sourceEnderChestTable + "\n" +
+ "- experience_table: " + sourceExperienceTable + "\n" +
+ "If any of these are not correct, please correct them\n" +
+ "using the command:\n" +
+ "\"husksync migrate mpdb set \"\n" +
+ "(e.g.: \"husksync migrate set mpdb host 1.2.3.4\")\n" +
+ "\n" +
+ "STEP 3] HuskSync will migrate data into the database\n" +
+ "tables configures in the config.yml file of this\n" +
+ "server. Please make sure you're happy with this\n" +
+ "before proceeding.\n" +
+ "\n" +
+ "STEP 4] To start the migration, please run:\n" +
+ "\"husksync migrate start mpdb\"\n" +
+ "\n" +
+ "NOTE: This migrator currently WORKS WITH MPDB version\n" +
+ "v4.9.2 and below!\n";
}
/**
* Represents data exported from the MySQLPlayerDataBridge source database
- *
- * @param user The user whose data is being migrated
- * @param serializedInventory The serialized inventory data
- * @param serializedArmor The serialized armor data
- * @param serializedEnderChest The serialized ender chest data
- * @param expLevel The player's current XP level
- * @param expProgress The player's current XP progress
- * @param totalExp The player's total XP score
*/
- private record MpdbData(
- @NotNull User user,
- @NotNull String serializedInventory,
- @NotNull String serializedArmor,
- @NotNull String serializedEnderChest,
- int expLevel,
- float expProgress,
- int totalExp
- ) {
+ @Value
+ private static class MpdbData {
+ /**
+ * The user whose data is being migrated
+ */
+ @NotNull User user;
+ /**
+ * The serialized inventory data
+ */
+ @NotNull String serializedInventory;
+ /**
+ * The serialized armor data
+ */
+ @NotNull String serializedArmor;
+ /**
+ * The serialized ender chest data
+ */
+ @NotNull String serializedEnderChest;
+ /**
+ * The player's current XP level
+ */
+ int expLevel;
+ /**
+ * The player's current XP progress
+ */
+ float expProgress;
+ /**
+ * The player's total XP score
+ */
+ int totalExp;
/**
* Converts exported MySQLPlayerDataBridge data into HuskSync's {@link DataSnapshot} object format
diff --git a/bukkit/src/main/java/net/william278/husksync/user/BukkitUser.java b/bukkit/src/main/java/net/william278/husksync/user/BukkitUser.java
index 47130b07..f7567d16 100644
--- a/bukkit/src/main/java/net/william278/husksync/user/BukkitUser.java
+++ b/bukkit/src/main/java/net/william278/husksync/user/BukkitUser.java
@@ -62,7 +62,7 @@ public class BukkitUser extends OnlineUser implements BukkitUserDataHolder {
}
@Override
- @Deprecated(since = "3.6.7")
+ @Deprecated
public void sendToast(@NotNull MineDown title, @NotNull MineDown description,
@NotNull String iconMaterial, @NotNull String backgroundType) {
plugin.log(Level.WARNING, "Toast notifications are deprecated. " +
diff --git a/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java b/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java
index 633a344a..53772263 100644
--- a/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java
+++ b/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java
@@ -77,7 +77,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
@NotNull
private Map readStatusData(@NotNull JSONObject object) {
if (!object.has("status_data")) {
- return Map.of();
+ return Collections.emptyMap();
}
final JSONObject status = object.getJSONObject("status_data");
diff --git a/bukkit/src/main/java/net/william278/husksync/util/BukkitMapPersister.java b/bukkit/src/main/java/net/william278/husksync/util/BukkitMapPersister.java
index 0621dbbd..2f99177d 100644
--- a/bukkit/src/main/java/net/william278/husksync/util/BukkitMapPersister.java
+++ b/bukkit/src/main/java/net/william278/husksync/util/BukkitMapPersister.java
@@ -69,7 +69,7 @@ public interface BukkitMapPersister {
if (!getPlugin().getSettings().getSynchronization().isPersistLockedMaps()) {
return items;
}
- return forEachMap(items, map -> this.persistMapView(map, delegateRenderer));
+ return private$forEachMap(items, map -> this.persistMapView(map, delegateRenderer));
}
/**
@@ -83,21 +83,26 @@ public interface BukkitMapPersister {
if (!getPlugin().getSettings().getSynchronization().isPersistLockedMaps()) {
return items;
}
- return forEachMap(items, this::applyMapView);
+ return private$forEachMap(items, this::private$applyMapView);
}
// Perform an operation on each map in an array of ItemStacks
@NotNull
- private ItemStack[] forEachMap(ItemStack[] items, @NotNull Function function) {
+ default ItemStack[] private$forEachMap(ItemStack[] items, @NotNull Function function) {
for (int i = 0; i < items.length; i++) {
final ItemStack item = items[i];
if (item == null) {
continue;
}
- if (item.getType() == Material.FILLED_MAP && item.hasItemMeta()) {
+ if (MaterialUtil.isFilledMap(item.getType()) && item.hasItemMeta()) {
items[i] = function.apply(item);
- } else if (item.getItemMeta() instanceof BlockStateMeta b && b.getBlockState() instanceof ShulkerBox box) {
- forEachMap(box.getInventory().getContents(), function);
+ } else if (item.getItemMeta() instanceof BlockStateMeta &&
+ ((BlockStateMeta) item.getItemMeta()).getBlockState() instanceof ShulkerBox
+ ) {
+ BlockStateMeta b = (BlockStateMeta) item.getItemMeta();
+ ShulkerBox box = (ShulkerBox) b.getBlockState();
+
+ private$forEachMap(box.getInventory().getContents(), function);
b.setBlockState(box);
}
}
@@ -105,7 +110,7 @@ public interface BukkitMapPersister {
}
@NotNull
- private ItemStack persistMapView(@NotNull ItemStack map, @NotNull Player delegateRenderer) {
+ default ItemStack persistMapView(@NotNull ItemStack map, @NotNull Player delegateRenderer) {
final MapMeta meta = Objects.requireNonNull((MapMeta) map.getItemMeta());
if (!meta.hasMapView()) {
return map;
@@ -139,7 +144,7 @@ public interface BukkitMapPersister {
}
@NotNull
- private ItemStack applyMapView(@NotNull ItemStack map) {
+ default ItemStack private$applyMapView(@NotNull ItemStack map) {
final MapMeta meta = Objects.requireNonNull((MapMeta) map.getItemMeta());
NBT.get(map, nbt -> {
if (!nbt.hasTag(MAP_DATA_KEY)) {
@@ -186,8 +191,8 @@ public interface BukkitMapPersister {
}
// Add a renderer to the map with the data and save to file
- final MapView view = generateRenderedMap(canvasData);
- final String worldUid = getDefaultMapWorld().getUID().toString();
+ final MapView view = private$generateRenderedMap(canvasData);
+ final String worldUid = private$getDefaultMapWorld().getUID().toString();
meta.setMapView(view);
map.setItemMeta(meta);
saveMapToFile(canvasData, view.getId());
@@ -204,7 +209,7 @@ public interface BukkitMapPersister {
}
default void renderMapFromFile(@NotNull MapView view) {
- final File mapFile = new File(getMapCacheFolder(), view.getId() + ".dat");
+ final File mapFile = new File(private$getMapCacheFolder(), view.getId() + ".dat");
if (!mapFile.exists()) {
return;
}
@@ -232,7 +237,7 @@ public interface BukkitMapPersister {
default void saveMapToFile(@NotNull MapData data, int id) {
getPlugin().runAsync(() -> {
- final File mapFile = new File(getMapCacheFolder(), id + ".dat");
+ final File mapFile = new File(private$getMapCacheFolder(), id + ".dat");
if (mapFile.exists()) {
return;
}
@@ -248,7 +253,7 @@ public interface BukkitMapPersister {
}
@NotNull
- private File getMapCacheFolder() {
+ default File private$getMapCacheFolder() {
final File mapCache = new File(getPlugin().getDataFolder(), "maps");
if (!mapCache.exists() && !mapCache.mkdirs()) {
getPlugin().log(Level.WARNING, "Failed to create maps folder");
@@ -258,8 +263,8 @@ public interface BukkitMapPersister {
// Sets the renderer of a map, and returns the generated MapView
@NotNull
- private MapView generateRenderedMap(@NotNull MapData canvasData) {
- final MapView view = Bukkit.createMap(getDefaultMapWorld());
+ default MapView private$generateRenderedMap(@NotNull MapData canvasData) {
+ final MapView view = Bukkit.createMap(private$getDefaultMapWorld());
view.getRenderers().clear();
// Create a new map view renderer with the map data color at each pixel
@@ -275,7 +280,7 @@ public interface BukkitMapPersister {
}
@NotNull
- private static World getDefaultMapWorld() {
+ static World private$getDefaultMapWorld() {
final World world = Bukkit.getWorlds().get(0);
if (world == null) {
throw new IllegalStateException("No worlds are loaded on the server!");
@@ -318,35 +323,86 @@ public interface BukkitMapPersister {
cursors.removeCursor(cursors.getCursor(0));
}
- canvasData.getBanners().forEach(banner -> cursors.addCursor(createBannerCursor(banner)));
+ canvasData.getBanners().forEach(banner -> cursors.addCursor(private$createBannerCursor(banner)));
canvas.setCursors(cursors);
}
}
@NotNull
- private static MapCursor createBannerCursor(@NotNull MapBanner banner) {
+ static MapCursor private$createBannerCursor(@NotNull MapBanner banner) {
+ MapCursor.Type type;
+ switch (banner.getColor().toLowerCase(Locale.ENGLISH)) {
+ case "white": {
+ type = MapCursor.Type.BANNER_WHITE;
+ break;
+ }
+ case "orange": {
+ type = MapCursor.Type.BANNER_ORANGE;
+ break;
+ }
+ case "magenta": {
+ type = MapCursor.Type.BANNER_MAGENTA;
+ break;
+ }
+ case "light_blue": {
+ type = MapCursor.Type.BANNER_LIGHT_BLUE;
+ break;
+ }
+ case "yellow": {
+ type = MapCursor.Type.BANNER_YELLOW;
+ break;
+ }
+ case "lime": {
+ type = MapCursor.Type.BANNER_LIME;
+ break;
+ }
+ case "pink": {
+ type = MapCursor.Type.BANNER_PINK;
+ break;
+ }
+ case "gray": {
+ type = MapCursor.Type.BANNER_GRAY;
+ break;
+ }
+ case "light_gray": {
+ type = MapCursor.Type.BANNER_LIGHT_GRAY;
+ break;
+ }
+ case "cyan": {
+ type = MapCursor.Type.BANNER_CYAN;
+ break;
+ }
+ case "purple": {
+ type = MapCursor.Type.BANNER_PURPLE;
+ break;
+ }
+ case "blue": {
+ type = MapCursor.Type.BANNER_BLUE;
+ break;
+ }
+ case "brown": {
+ type = MapCursor.Type.BANNER_BROWN;
+ break;
+ }
+ case "green": {
+ type = MapCursor.Type.BANNER_GREEN;
+ break;
+ }
+ case "red": {
+ type = MapCursor.Type.BANNER_RED;
+ break;
+ }
+ default: {
+ type = MapCursor.Type.BANNER_BLACK;
+ break;
+ }
+ }
+
return new MapCursor(
(byte) banner.getPosition().getX(),
(byte) banner.getPosition().getZ(),
(byte) 8, // Always rotate banners upright
- switch (banner.getColor().toLowerCase(Locale.ENGLISH)) {
- case "white" -> MapCursor.Type.BANNER_WHITE;
- case "orange" -> MapCursor.Type.BANNER_ORANGE;
- case "magenta" -> MapCursor.Type.BANNER_MAGENTA;
- case "light_blue" -> MapCursor.Type.BANNER_LIGHT_BLUE;
- case "yellow" -> MapCursor.Type.BANNER_YELLOW;
- case "lime" -> MapCursor.Type.BANNER_LIME;
- case "pink" -> MapCursor.Type.BANNER_PINK;
- case "gray" -> MapCursor.Type.BANNER_GRAY;
- case "light_gray" -> MapCursor.Type.BANNER_LIGHT_GRAY;
- case "cyan" -> MapCursor.Type.BANNER_CYAN;
- case "purple" -> MapCursor.Type.BANNER_PURPLE;
- case "blue" -> MapCursor.Type.BANNER_BLUE;
- case "brown" -> MapCursor.Type.BANNER_BROWN;
- case "green" -> MapCursor.Type.BANNER_GREEN;
- case "red" -> MapCursor.Type.BANNER_RED;
- default -> MapCursor.Type.BANNER_BLACK;
- },
+ type,
true,
banner.getText().isEmpty() ? null : banner.getText()
);
@@ -409,11 +465,14 @@ public interface BukkitMapPersister {
@NotNull
private String getDimension() {
- return mapView.getWorld() != null ? switch (mapView.getWorld().getEnvironment()) {
- case NETHER -> "minecraft:the_nether";
- case THE_END -> "minecraft:the_end";
- default -> "minecraft:overworld";
- } : "minecraft:overworld";
+ if (mapView.getWorld() == null) {
+ return "minecraft:overworld";
+ }
+ switch (mapView.getWorld().getEnvironment()) {
+ case NETHER: return "minecraft:the_nether";
+ case THE_END: return "minecraft:the_end";
+ default: return "minecraft:overworld";
+ }
}
/**
@@ -442,7 +501,7 @@ public interface BukkitMapPersister {
}
} catch (Throwable ignored) {
}
- return MapData.fromPixels(pixels, getDimension(), (byte) 2, banners, List.of());
+ return MapData.fromPixels(pixels, getDimension(), (byte) 2, banners, Collections.emptyList());
}
}
diff --git a/bukkit/src/main/java/net/william278/husksync/util/MaterialUtil.java b/bukkit/src/main/java/net/william278/husksync/util/MaterialUtil.java
new file mode 100644
index 00000000..b6f52895
--- /dev/null
+++ b/bukkit/src/main/java/net/william278/husksync/util/MaterialUtil.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of HuskSync, licensed under the Apache License 2.0.
+ *
+ * Copyright (c) William278
+ * Copyright (c) contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.william278.husksync.util;
+
+import lombok.SneakyThrows;
+import lombok.experimental.UtilityClass;
+import net.william278.husksync.util.ref.RefUtil;
+import org.bukkit.Material;
+
+import java.lang.invoke.MethodHandle;
+
+@UtilityClass
+public class MaterialUtil {
+ private static final MethodHandle v$isAirMethod = RefUtil.bootstrapVirtualMethod("Lorg/bukkit/Material;isAir()Z");
+
+ @SneakyThrows
+ public static boolean isAir(Material $this) {
+ if (v$isAirMethod != null) {
+ return (boolean) v$isAirMethod.invoke($this);
+ }
+ switch ($this.name()) {
+ case "AIR":
+ case "CAVE_AIR":
+ case "VOID_AIR":
+ case "LEGACY_AIR":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isFilledMap(Material $this) {
+ switch ($this.name()) {
+ case "FILLED_MAP":
+ case "LEGACY_MAP":
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/bukkit/src/main/java/net/william278/husksync/util/ref/RefUtil.java b/bukkit/src/main/java/net/william278/husksync/util/ref/RefUtil.java
new file mode 100644
index 00000000..97765653
--- /dev/null
+++ b/bukkit/src/main/java/net/william278/husksync/util/ref/RefUtil.java
@@ -0,0 +1,220 @@
+/*
+ * This file is part of HuskSync, licensed under the Apache License 2.0.
+ *
+ * Copyright (c) William278
+ * Copyright (c) contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.william278.husksync.util.ref;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+public final class RefUtil {
+ private RefUtil() {
+ throw new UnsupportedOperationException("This class cannot be instantiated");
+ }
+
+ private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ public static MethodHandle bootstrapStaticMethod(String methodDesc) {
+ String[] descParts = splitMethodDesc(methodDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ MethodType methodType;
+ try {
+ methodType = MethodType.fromMethodDescriptorString(descParts[2], owner.getClassLoader());
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findStatic(owner, descParts[1], methodType);
+ } catch (NoSuchMethodException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle bootstrapVirtualMethod(String methodDesc) {
+ String[] descParts = splitMethodDesc(methodDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ MethodType methodType;
+ try {
+ methodType = MethodType.fromMethodDescriptorString(descParts[2], owner.getClassLoader());
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findVirtual(owner, descParts[1], methodType);
+ } catch (NoSuchMethodException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String[] splitMethodDesc(String methodDesc) {
+ if (methodDesc.length() < 5) { // L;()V
+ throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc);
+ }
+
+ int descSplit = methodDesc.indexOf(';');
+ if (descSplit == -1) {
+ throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc);
+ }
+
+ int argsStart = methodDesc.indexOf('(');
+ if (argsStart == -1) {
+ throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc);
+ }
+
+ return new String[] {
+ methodDesc.substring(1, descSplit), // java/lang/Object
+ methodDesc.substring(descSplit + 1, argsStart), // method name
+ methodDesc.substring(argsStart) // (Ljava/lang/Object;)V
+ };
+ }
+
+ public static RuntimeException sneakyThrow(Throwable t) throws T {
+ throw (T) t;
+ }
+
+ public static MethodHandle bootstrapStaticFieldGet(String fieldDesc) {
+ String[] descParts = splitFieldDesc(fieldDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ Class> fieldType;
+ try {
+ fieldType = MethodType.fromMethodDescriptorString("()" + descParts[2], owner.getClassLoader()).returnType();
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findStaticGetter(owner, descParts[1], fieldType);
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle bootstrapStaticFieldSet(String fieldDesc) {
+ String[] descParts = splitFieldDesc(fieldDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ Class> fieldType;
+ try {
+ fieldType = MethodType.fromMethodDescriptorString("()" + descParts[2], owner.getClassLoader()).returnType();
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findStaticSetter(owner, descParts[1], fieldType);
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle bootstrapVirtualFieldGet(String fieldDesc) {
+ String[] descParts = splitFieldDesc(fieldDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ Class> fieldType;
+ try {
+ fieldType = MethodType.fromMethodDescriptorString("()" + descParts[2], owner.getClassLoader()).returnType();
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findGetter(owner, descParts[1], fieldType);
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle bootstrapVirtualFieldSet(String fieldDesc) {
+ String[] descParts = splitFieldDesc(fieldDesc);
+ Class> owner;
+ try {
+ owner = Class.forName(descParts[0].replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ Class> fieldType;
+ try {
+ fieldType = MethodType.fromMethodDescriptorString("()" + descParts[2], owner.getClassLoader()).returnType();
+ } catch (TypeNotPresentException e) {
+ return null;
+ }
+ try {
+ return lookup.findSetter(owner, descParts[1], fieldType);
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String[] splitFieldDesc(String fieldDesc) {
+ if (fieldDesc.length() < 3) { // L;:
+ throw new IllegalArgumentException("Invalid field descriptor: " + fieldDesc);
+ }
+
+ int descSplit = fieldDesc.indexOf(';');
+ if (descSplit == -1) {
+ throw new IllegalArgumentException("Invalid field descriptor: " + fieldDesc);
+ }
+
+ int argsStart = fieldDesc.indexOf(':');
+ if (argsStart == -1) {
+ throw new IllegalArgumentException("Invalid field descriptor: " + fieldDesc);
+ }
+
+ return new String[] {
+ fieldDesc.substring(1, descSplit), // java/lang/Object
+ fieldDesc.substring(descSplit + 1, argsStart), // field name
+ fieldDesc.substring(argsStart + 1) // Ljava/lang/Object;
+ };
+ }
+
+}
diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml
index 71aa4c2d..34ac5027 100644
--- a/bukkit/src/main/resources/plugin.yml
+++ b/bukkit/src/main/resources/plugin.yml
@@ -1,20 +1,13 @@
name: 'HuskSync'
version: '${version}'
main: 'net.william278.husksync.BukkitHuskSync'
-api-version: 1.17
-author: 'William278'
+api-version: 1.13
+author: 'William278, InkerBot'
description: '${description}'
-website: 'https://william278.net'
+website: 'https://git.inker.bot/HuskLegacy/HuskSync'
folia-supported: true
softdepend:
- 'packetevents'
- 'ProtocolLib'
- 'MysqlPlayerDataBridge'
- 'Plan'
-libraries:
- - 'redis.clients:jedis:${jedis_version}'
- - 'com.mysql:mysql-connector-j:${mysql_driver_version}'
- - 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
- - 'org.postgresql:postgresql:${postgres_driver_version}'
- - 'org.mongodb:mongodb-driver-sync:${mongodb_driver_version}'
- - 'org.xerial.snappy:snappy-java:${snappy_version}'
\ No newline at end of file
diff --git a/common/build.gradle b/common/build.gradle
index a9186070..39d400d2 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -3,21 +3,21 @@ plugins {
}
dependencies {
+ api 'org.inksnow.cputil:logger:1.13'
+ api 'org.inksnow.cputil:database:1.13'
+
api 'commons-io:commons-io:2.16.1'
api 'org.apache.commons:commons-text:1.12.0'
- api 'net.william278:minedown:1.8.2'
+ api 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT'
api 'org.json:json:20240303'
api 'com.google.code.gson:gson:2.11.0'
api 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2'
- api 'de.exlll:configlib-yaml:4.5.0'
- api 'net.william278:paginedown:1.1.2'
- api 'net.william278:DesertWell:2.0.4'
- api('com.zaxxer:HikariCP:5.1.0') {
- exclude module: 'slf4j-api'
- }
+ api 'org.inksnow.husk:configlib-yaml:4.5.4'
+ api 'org.inksnow.husk:paginedown:1.1.3-ac18d11'
+ api 'org.inksnow.husk:desertwell:2.0.5-9962d59:all'
+ api 'com.mojang:brigadier:1.1.8'
- compileOnly 'net.william278.uniform:uniform-common:1.2.1'
- compileOnly 'com.mojang:brigadier:1.1.8'
+ compileOnly 'org.inksnow.husk.uniform:uniform-common:1.2.1-ad25f16'
compileOnly 'org.projectlombok:lombok:1.18.34'
compileOnly 'org.jetbrains:annotations:24.1.0'
compileOnly 'net.kyori:adventure-api:4.17.0'
@@ -35,7 +35,7 @@ dependencies {
testImplementation "org.xerial.snappy:snappy-java:$snappy_version"
testImplementation 'com.google.guava:guava:33.2.1-jre'
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
- testCompileOnly 'de.exlll:configlib-yaml:4.5.0'
+ testCompileOnly 'org.inksnow.husk:configlib-yaml:4.5.4'
testCompileOnly 'org.jetbrains:annotations:24.1.0'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
diff --git a/common/src/main/java/net/william278/husksync/HuskSync.java b/common/src/main/java/net/william278/husksync/HuskSync.java
index 962c272a..c79f9d75 100644
--- a/common/src/main/java/net/william278/husksync/HuskSync.java
+++ b/common/src/main/java/net/william278/husksync/HuskSync.java
@@ -196,7 +196,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
// Get the debug log message format
@NotNull
- private String getDebugString(@NotNull String message) {
+ default String getDebugString(@NotNull String message) {
return String.format("[DEBUG] [%s] %s", new SimpleDateFormat("mm:ss.SSS").format(new Date()), message);
}
@@ -327,16 +327,16 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
*/
final class FailedToLoadException extends IllegalStateException {
- private static final String FORMAT = """
- HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
- Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
-
- 1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml
- 2) Make sure your Redis server details are also correct in config.yml
- 3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)
- 4) Check the error below for more details
-
- Caused by: %s""";
+ private static final String FORMAT =
+ "HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.\n" +
+ "Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):\n" +
+ "\n" +
+ "1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml\n" +
+ "2) Make sure your Redis server details are also correct in config.yml\n" +
+ "3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)\n" +
+ "4) Check the error below for more details\n" +
+ "\n" +
+ "Caused by: %s";
FailedToLoadException(@NotNull String message, @NotNull Throwable cause) {
super(String.format(FORMAT, message), cause);
diff --git a/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java b/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java
index 451d9b90..4d40d79b 100644
--- a/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java
+++ b/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java
@@ -29,15 +29,19 @@ import net.william278.husksync.data.Serializer;
import net.william278.husksync.sync.DataSyncer;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.Formatter;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* The common implementation of the HuskSync API, containing cross-platform API calls.
@@ -138,7 +142,7 @@ public class HuskSyncAPI {
public CompletableFuture> getCurrentData(@NotNull User user) {
return plugin.getRedisManager()
.getUserData(UUID.randomUUID(), user)
- .thenApply(data -> data.or(() -> plugin.getDatabase().getLatestSnapshot(user)))
+ .thenApply(data -> OptionalUtil.or(data, () -> plugin.getDatabase().getLatestSnapshot(user)))
.thenApply(data -> data.map(snapshot -> snapshot.unpack(plugin)));
}
@@ -154,8 +158,8 @@ public class HuskSyncAPI {
*/
public void setCurrentData(@NotNull User user, @NotNull DataSnapshot data) {
plugin.runAsync(() -> {
- final DataSnapshot.Packed packed = data instanceof DataSnapshot.Unpacked unpacked
- ? unpacked.pack(plugin) : (DataSnapshot.Packed) data;
+ final DataSnapshot.Packed packed = data instanceof DataSnapshot.Unpacked
+ ? ((DataSnapshot.Unpacked) data).pack(plugin) : (DataSnapshot.Packed) data;
addSnapshot(user, packed);
plugin.getRedisManager().sendUserDataUpdate(user, packed);
});
@@ -190,7 +194,7 @@ public class HuskSyncAPI {
return plugin.supplyAsync(
() -> plugin.getDatabase().getAllSnapshots(user).stream()
.map(snapshot -> snapshot.unpack(plugin))
- .toList()
+ .collect(Collectors.toList())
);
}
@@ -205,9 +209,10 @@ public class HuskSyncAPI {
*/
public CompletableFuture> getSnapshot(@NotNull User user, @NotNull UUID versionId) {
return plugin.supplyAsync(
- () -> plugin.getDatabase().getSnapshot(user, versionId).stream()
+ () -> plugin.getDatabase().getSnapshot(user, versionId)
+ .map(Stream::of).orElseGet(Stream::empty)
.map(snapshot -> snapshot.unpack(plugin))
- .toList()
+ .collect(Collectors.toList())
);
}
@@ -274,8 +279,8 @@ public class HuskSyncAPI {
@Nullable BiConsumer callback) {
plugin.runAsync(() -> plugin.getDataSyncer().saveData(
user,
- snapshot instanceof DataSnapshot.Unpacked unpacked
- ? unpacked.pack(plugin) : (DataSnapshot.Packed) snapshot,
+ snapshot instanceof DataSnapshot.Unpacked
+ ? ((DataSnapshot.Unpacked) snapshot).pack(plugin) : (DataSnapshot.Packed) snapshot,
callback
));
}
@@ -304,8 +309,8 @@ public class HuskSyncAPI {
*/
public void updateSnapshot(@NotNull User user, @NotNull DataSnapshot snapshot) {
plugin.runAsync(() -> plugin.getDatabase().updateSnapshot(
- user, snapshot instanceof DataSnapshot.Unpacked unpacked
- ? unpacked.pack(plugin) : (DataSnapshot.Packed) snapshot
+ user, snapshot instanceof DataSnapshot.Unpacked
+ ? ((DataSnapshot.Unpacked) snapshot).pack(plugin) : (DataSnapshot.Packed) snapshot
));
}
@@ -439,8 +444,8 @@ public class HuskSyncAPI {
* @since 3.0
*/
public int getSnapshotFileSize(@NotNull DataSnapshot snapshot) {
- return (snapshot instanceof DataSnapshot.Packed packed)
- ? packed.getFileSize(plugin)
+ return (snapshot instanceof DataSnapshot.Packed)
+ ? ((DataSnapshot.Packed) snapshot).getFileSize(plugin)
: ((DataSnapshot.Unpacked) snapshot).pack(plugin).getFileSize(plugin);
}
@@ -511,15 +516,14 @@ public class HuskSyncAPI {
*/
static final class NotRegisteredException extends IllegalStateException {
- private static final String REASONS = """
- This may be because:
- 1) HuskSync has failed to enable successfully
- 2) Your plugin isn't set to load after HuskSync has
- (Check if it set as a (soft)depend in plugin.yml or to load: BEFORE in paper-plugin.yml?)
- 3) You are attempting to access HuskSync on plugin construction/before your plugin has enabled.""";
+ private static final String REASONS = "This may be because:\n" +
+ "1) HuskSync has failed to enable successfully\n" +
+ "2) Your plugin isn't set to load after HuskSync has\n" +
+ " (Check if it set as a (soft)depend in plugin.yml or to load: BEFORE in paper-plugin.yml?)\n" +
+ "3) You are attempting to access HuskSync on plugin construction/before your plugin has enabled.";
NotRegisteredException(@NotNull String reasons) {
- super("Could not access the HuskSync API as it has not yet been registered. %s".formatted(reasons));
+ super("Could not access the HuskSync API as it has not yet been registered. " + reasons);
}
NotRegisteredException() {
diff --git a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
index 81395e9b..157c0a4b 100644
--- a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
@@ -19,6 +19,7 @@
package net.william278.husksync.command;
+import com.google.common.collect.ImmutableList;
import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.Data;
@@ -37,14 +38,14 @@ import java.util.Optional;
public class EnderChestCommand extends ItemsCommand {
public EnderChestCommand(@NotNull HuskSync plugin) {
- super("enderchest", List.of("echest", "openechest"), plugin);
+ super("enderchest", ImmutableList.of("echest", "openechest"), plugin);
}
@Override
protected void showItems(@NotNull OnlineUser viewer, @NotNull DataSnapshot.Unpacked snapshot,
@NotNull User user, boolean allowEdit) {
final Optional optionalEnderChest = snapshot.getEnderChest();
- if (optionalEnderChest.isEmpty()) {
+ if (!optionalEnderChest.isPresent()) {
plugin.getLocales().getLocale("error_no_data_to_display")
.ifPresent(viewer::sendMessage);
return;
@@ -76,7 +77,7 @@ public class EnderChestCommand extends ItemsCommand {
@SuppressWarnings("DuplicatedCode")
private void updateItems(@NotNull OnlineUser viewer, @NotNull Data.Items.Items items, @NotNull User holder) {
final Optional latestData = plugin.getDatabase().getLatestSnapshot(holder);
- if (latestData.isEmpty()) {
+ if (!latestData.isPresent()) {
plugin.getLocales().getLocale("error_no_data_to_display")
.ifPresent(viewer::sendMessage);
return;
diff --git a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java
index 80d38823..b1de1014 100644
--- a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java
@@ -19,6 +19,7 @@
package net.william278.husksync.command;
+import com.google.common.collect.ImmutableList;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import de.themoep.minedown.adventure.MineDown;
@@ -30,9 +31,11 @@ import net.kyori.adventure.text.format.TextColor;
import net.william278.desertwell.about.AboutMenu;
import net.william278.desertwell.util.UpdateChecker;
import net.william278.husksync.HuskSync;
+import net.william278.husksync.data.Identifier;
import net.william278.husksync.database.Database;
import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.user.CommandUser;
+import net.william278.husksync.util.StringUtil;
import net.william278.uniform.BaseCommand;
import net.william278.uniform.CommandProvider;
import net.william278.uniform.Permission;
@@ -41,7 +44,7 @@ import org.apache.commons.text.WordUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -52,7 +55,7 @@ public class HuskSyncCommand extends PluginCommand {
private final AboutMenu aboutMenu;
public HuskSyncCommand(@NotNull HuskSync plugin) {
- super("husksync", List.of(), Permission.Default.TRUE, ExecutionScope.ALL, plugin);
+ super("husksync", ImmutableList.of(), Permission.Default.TRUE, ExecutionScope.ALL, plugin);
this.updateChecker = plugin.getUpdateChecker();
this.aboutMenu = AboutMenu.builder()
.title(Component.text("HuskSync"))
@@ -110,7 +113,8 @@ public class HuskSyncCommand extends PluginCommand {
plugin.getLocales().getLocale("system_status_header").ifPresent(user::sendMessage);
user.sendMessage(Component.join(
JoinConfiguration.newlines(),
- Arrays.stream(StatusLine.values()).map(s -> s.get(plugin)).toList()
+ Arrays.stream(StatusLine.values()).map(s -> s.get(plugin))
+ .collect(Collectors.toList())
));
});
}
@@ -202,7 +206,7 @@ public class HuskSyncCommand extends PluginCommand {
private enum StatusLine {
PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata())
- .appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty()
+ .appendSpace().append(StringUtil.isBlank(plugin.getPluginVersion().getMetadata()) ? Component.empty()
: Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))),
SERVER_VERSION(plugin -> Component.text(plugin.getServerVersion())),
LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())),
@@ -210,7 +214,7 @@ public class HuskSyncCommand extends PluginCommand {
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
- CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())),
+ CLUSTER_ID(plugin -> Component.text(StringUtil.isBlank(plugin.getSettings().getClusterId()) ? "None" : plugin.getSettings().getClusterId())),
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(
plugin.getSettings().getSynchronization().getMode().toString()
))),
@@ -224,10 +228,10 @@ public class HuskSyncCommand extends PluginCommand {
),
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())),
USING_REDIS_SENTINEL(plugin -> getBoolean(
- !plugin.getSettings().getRedis().getSentinel().getMaster().isBlank()
+ !StringUtil.isBlank(plugin.getSettings().getRedis().getSentinel().getMaster())
)),
USING_REDIS_PASSWORD(plugin -> getBoolean(
- !plugin.getSettings().getRedis().getCredentials().getPassword().isBlank()
+ !StringUtil.isBlank(plugin.getSettings().getRedis().getCredentials().getPassword())
)),
REDIS_USING_SSL(plugin -> getBoolean(
plugin.getSettings().getRedis().getCredentials().isUseSsl()
@@ -243,13 +247,13 @@ public class HuskSyncCommand extends PluginCommand {
.hoverEvent(HoverEvent.showText(
Component.text(i.isEnabled() ? "Enabled" : "Disabled")
.append(Component.newline())
- .append(Component.text("Dependencies: %s".formatted(i.getDependencies()
- .isEmpty() ? "(None)" : i.getDependencies().stream()
- .map(d -> "%s (%s)".formatted(
- d.getKey().value(), d.isRequired() ? "Required" : "Optional"
- )).collect(Collectors.joining(", ")))
- ).color(NamedTextColor.GRAY))
- ))).toList()
+ .append(i.getDependencies().isEmpty()
+ ? Component.text("Dependencies: None").color(NamedTextColor.GRAY)
+ : Component.text("Dependencies: " + i.getDependencies().stream()
+ .map(d -> d.getKey().value() + " (" + (d.isRequired() ? "Required" : "Optional") + ")")
+ .collect(Collectors.joining(", "))
+ ).color(NamedTextColor.GRAY))
+ ))).collect(Collectors.toList())
));
private final Function supplier;
diff --git a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
index 0eadbb0a..e9fa06c3 100644
--- a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
@@ -19,6 +19,7 @@
package net.william278.husksync.command;
+import com.google.common.collect.ImmutableList;
import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.Data;
@@ -37,14 +38,14 @@ import java.util.Optional;
public class InventoryCommand extends ItemsCommand {
public InventoryCommand(@NotNull HuskSync plugin) {
- super("inventory", List.of("invsee", "openinv"), plugin);
+ super("inventory", ImmutableList.of("invsee", "openinv"), plugin);
}
@Override
protected void showItems(@NotNull OnlineUser viewer, @NotNull DataSnapshot.Unpacked snapshot,
@NotNull User user, boolean allowEdit) {
final Optional optionalInventory = snapshot.getInventory();
- if (optionalInventory.isEmpty()) {
+ if (!optionalInventory.isPresent()) {
viewer.sendMessage(new MineDown("what the FUCK is happening"));
plugin.getLocales().getLocale("error_no_data_to_display")
.ifPresent(viewer::sendMessage);
@@ -77,7 +78,7 @@ public class InventoryCommand extends ItemsCommand {
@SuppressWarnings("DuplicatedCode")
private void updateItems(@NotNull OnlineUser viewer, @NotNull Data.Items.Items items, @NotNull User holder) {
final Optional latestData = plugin.getDatabase().getLatestSnapshot(holder);
- if (latestData.isEmpty()) {
+ if (!latestData.isPresent()) {
plugin.getLocales().getLocale("error_no_data_to_display")
.ifPresent(viewer::sendMessage);
return;
diff --git a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java
index ecdee745..6ed10343 100644
--- a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java
@@ -24,6 +24,7 @@ import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.CommandUser;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
import net.william278.uniform.BaseCommand;
import net.william278.uniform.Permission;
import org.jetbrains.annotations.NotNull;
@@ -44,32 +45,32 @@ public abstract class ItemsCommand extends PluginCommand {
final User user = ctx.getArgument("username", User.class);
final UUID version = ctx.getArgument("version", UUID.class);
final CommandUser executor = user(command, ctx);
- if (!(executor instanceof OnlineUser online)) {
+ if (!(executor instanceof OnlineUser)) {
plugin.getLocales().getLocale("error_in_game_command_only")
.ifPresent(executor::sendMessage);
return;
}
- this.showSnapshotItems(online, user, version);
+ this.showSnapshotItems((OnlineUser) executor, user, version);
}, user("username"), uuid("version"));
command.addSyntax((ctx) -> {
final User user = ctx.getArgument("username", User.class);
final CommandUser executor = user(command, ctx);
- if (!(executor instanceof OnlineUser online)) {
+ if (!(executor instanceof OnlineUser)) {
plugin.getLocales().getLocale("error_in_game_command_only")
.ifPresent(executor::sendMessage);
return;
}
- this.showLatestItems(online, user);
+ this.showLatestItems((OnlineUser) executor, user);
}, user("username"));
}
// View (and edit) the latest user data
private void showLatestItems(@NotNull OnlineUser viewer, @NotNull User user) {
- plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> data
- .or(() -> plugin.getDatabase().getLatestSnapshot(user))
- .or(() -> {
- plugin.getLocales().getLocale("error_no_data_to_display")
- .ifPresent(viewer::sendMessage);
+ plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> OptionalUtil.or(
+ OptionalUtil.or(data, () ->
+ plugin.getDatabase().getLatestSnapshot(user)),
+ () -> {
+ plugin.getLocales().getLocale("error_no_data_to_display").ifPresent(viewer::sendMessage);
return Optional.empty();
})
.flatMap(packed -> {
@@ -87,8 +88,8 @@ public abstract class ItemsCommand extends PluginCommand {
// View a specific version of the user data
private void showSnapshotItems(@NotNull OnlineUser viewer, @NotNull User user, @NotNull UUID version) {
- plugin.getDatabase().getSnapshot(user, version)
- .or(() -> {
+ OptionalUtil.or(plugin.getDatabase().getSnapshot(user, version),
+ () -> {
plugin.getLocales().getLocale("error_invalid_version_uuid")
.ifPresent(viewer::sendMessage);
return Optional.empty();
diff --git a/common/src/main/java/net/william278/husksync/command/PluginCommand.java b/common/src/main/java/net/william278/husksync/command/PluginCommand.java
index b1eda665..d0bc247e 100644
--- a/common/src/main/java/net/william278/husksync/command/PluginCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/PluginCommand.java
@@ -46,7 +46,7 @@ public abstract class PluginCommand extends Command {
}
private static String getDescription(@NotNull HuskSync plugin, @NotNull String name) {
- return plugin.getLocales().getRawLocale("%s_command_description".formatted(name)).orElse("");
+ return plugin.getLocales().getRawLocale(name + "_command_description").orElse("");
}
@NotNull
@@ -72,7 +72,10 @@ public abstract class PluginCommand extends Command {
@NotNull
protected CommandUser adapt(net.william278.uniform.CommandUser user) {
- return user.getUuid() == null ? plugin.getConsole() : plugin.getOnlineUser(user.getUuid()).orElseThrow();
+ return user.getUuid() == null
+ ? plugin.getConsole()
+ : plugin.getOnlineUser(user.getUuid())
+ .orElseThrow(() -> new IllegalStateException("Online not found"));
}
@NotNull
diff --git a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java
index 08f25bfd..0d0f4443 100644
--- a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java
@@ -29,12 +29,14 @@ import net.william278.husksync.user.User;
import net.william278.husksync.util.DataDumper;
import net.william278.husksync.util.DataSnapshotList;
import net.william278.husksync.util.DataSnapshotOverview;
+import net.william278.husksync.util.OptionalUtil;
import net.william278.uniform.BaseCommand;
import net.william278.uniform.CommandProvider;
import net.william278.uniform.Permission;
import net.william278.uniform.element.ArgumentElement;
import org.jetbrains.annotations.NotNull;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -44,7 +46,7 @@ import java.util.logging.Level;
public class UserDataCommand extends PluginCommand {
public UserDataCommand(@NotNull HuskSync plugin) {
- super("userdata", List.of("playerdata"), Permission.Default.IF_OP, ExecutionScope.ALL, plugin);
+ super("userdata", Collections.singletonList("playerdata"), Permission.Default.IF_OP, ExecutionScope.ALL, plugin);
}
@Override
@@ -59,35 +61,37 @@ public class UserDataCommand extends PluginCommand {
// Show the latest snapshot
private void viewLatestSnapshot(@NotNull CommandUser executor, @NotNull User user) {
- plugin.getDatabase().getLatestSnapshot(user).ifPresentOrElse(
- data -> {
- if (data.isInvalid()) {
- plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
- .ifPresent(executor::sendMessage);
- return;
- }
- DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
- .show(executor);
- },
- () -> plugin.getLocales().getLocale("error_no_data_to_display")
- .ifPresent(executor::sendMessage)
+ OptionalUtil.ifPresentOrElse(
+ plugin.getDatabase().getLatestSnapshot(user),
+ data -> {
+ if (data.isInvalid()) {
+ plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
+ .ifPresent(executor::sendMessage);
+ return;
+ }
+ DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
+ .show(executor);
+ },
+ () -> plugin.getLocales().getLocale("error_no_data_to_display")
+ .ifPresent(executor::sendMessage)
);
}
// Show the specified snapshot
private void viewSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
- plugin.getDatabase().getSnapshot(user, version).ifPresentOrElse(
- data -> {
- if (data.isInvalid()) {
- plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
- .ifPresent(executor::sendMessage);
- return;
- }
- DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
- .show(executor);
- },
- () -> plugin.getLocales().getLocale("error_invalid_version_uuid")
- .ifPresent(executor::sendMessage)
+ OptionalUtil.ifPresentOrElse(
+ plugin.getDatabase().getSnapshot(user, version),
+ data -> {
+ if (data.isInvalid()) {
+ plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
+ .ifPresent(executor::sendMessage);
+ return;
+ }
+ DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
+ .show(executor);
+ },
+ () -> plugin.getLocales().getLocale("error_invalid_version_uuid")
+ .ifPresent(executor::sendMessage)
);
}
@@ -121,7 +125,7 @@ public class UserDataCommand extends PluginCommand {
// Restore a snapshot
private void restoreSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
final Optional optionalData = plugin.getDatabase().getSnapshot(user, version);
- if (optionalData.isEmpty()) {
+ if (!optionalData.isPresent()) {
plugin.getLocales().getLocale("error_invalid_version_uuid")
.ifPresent(executor::sendMessage);
return;
@@ -155,7 +159,7 @@ public class UserDataCommand extends PluginCommand {
// Pin a snapshot
private void pinSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
final Optional optionalData = plugin.getDatabase().getSnapshot(user, version);
- if (optionalData.isEmpty()) {
+ if (!optionalData.isPresent()) {
plugin.getLocales().getLocale("error_invalid_version_uuid")
.ifPresent(executor::sendMessage);
return;
@@ -177,7 +181,7 @@ public class UserDataCommand extends PluginCommand {
private void dumpSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version,
@NotNull DumpType type) {
final Optional data = plugin.getDatabase().getSnapshot(user, version);
- if (data.isEmpty()) {
+ if (!data.isPresent()) {
plugin.getLocales().getLocale("error_invalid_version_uuid")
.ifPresent(executor::sendMessage);
return;
@@ -265,12 +269,15 @@ public class UserDataCommand extends PluginCommand {
private ArgumentElement dumpType() {
return new ArgumentElement<>("type", reader -> {
final String type = reader.readString();
- return switch (type.toLowerCase(Locale.ENGLISH)) {
- case "web" -> DumpType.WEB;
- case "file" -> DumpType.FILE;
- default -> throw CommandSyntaxException.BUILT_IN_EXCEPTIONS
- .dispatcherUnknownArgument().createWithContext(reader);
- };
+ switch (type.toLowerCase(Locale.ENGLISH)) {
+ case "web":
+ return DumpType.WEB;
+ case "file":
+ return DumpType.FILE;
+ default:
+ throw CommandSyntaxException.BUILT_IN_EXCEPTIONS
+ .dispatcherUnknownArgument().createWithContext(reader);
+ }
}, (context, builder) -> {
builder.suggest("web");
builder.suggest("file");
diff --git a/common/src/main/java/net/william278/husksync/config/Locales.java b/common/src/main/java/net/william278/husksync/config/Locales.java
index 6c7e5002..9246210c 100644
--- a/common/src/main/java/net/william278/husksync/config/Locales.java
+++ b/common/src/main/java/net/william278/husksync/config/Locales.java
@@ -42,14 +42,14 @@ import java.util.Optional;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Locales {
- static final String CONFIG_HEADER = """
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ HuskSync - Locales ┃
- ┃ Developed by William278 ┃
- ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
- ┣╸ See plugin about menu for international locale credits
- ┣╸ Formatted in MineDown: https://github.com/Phoenix616/MineDown
- ┗╸ Translate HuskSync: https://william278.net/docs/husksync/translations""";
+ static final String CONFIG_HEADER =
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n" +
+ "┃ HuskSync - Locales ┃\n" +
+ "┃ Developed by William278 ┃\n" +
+ "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" +
+ "┣╸ See plugin about menu for international locale credits\n" +
+ "┣╸ Formatted in MineDown: https://github.com/Phoenix616/MineDown\n" +
+ "┗╸ Translate HuskSync: https://william278.net/docs/husksync/translations";
protected static final String DEFAULT_LOCALE = "en-gb";
@@ -202,9 +202,9 @@ public class Locales {
/**
* Displays the notification in an Advancement Toast
*
- * @deprecated No longer supported
+ * @deprecated No longer supported, since 3.6.7
*/
- @Deprecated(since = "3.6.7")
+ @Deprecated
TOAST,
/**
diff --git a/common/src/main/java/net/william278/husksync/config/Server.java b/common/src/main/java/net/william278/husksync/config/Server.java
index 663f8f2e..7eec4fd0 100644
--- a/common/src/main/java/net/william278/husksync/config/Server.java
+++ b/common/src/main/java/net/william278/husksync/config/Server.java
@@ -27,6 +27,7 @@ import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
+import java.nio.file.Paths;
@Getter
@Configuration
@@ -34,13 +35,13 @@ import java.nio.file.Path;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Server {
- static final String CONFIG_HEADER = """
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ HuskSync - Server ID ┃
- ┃ Developed by William278 ┃
- ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
- ┣╸ This file should contain the ID of this server as defined in your proxy config.
- ┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)""";
+ static final String CONFIG_HEADER =
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n" +
+ "┃ HuskSync - Server ID ┃\n" +
+ "┃ Developed by William278 ┃\n" +
+ "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" +
+ "┣╸ This file should contain the ID of this server as defined in your proxy config.\n" +
+ "┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)";
private String name = getDefault();
@@ -55,14 +56,14 @@ public class Server {
@NotNull
private static String getDefault() {
final String serverFolder = System.getProperty("user.dir");
- return serverFolder == null ? "server" : Path.of(serverFolder).getFileName().toString().trim();
+ return serverFolder == null ? "server" : Paths.get(serverFolder).getFileName().toString().trim();
}
@Override
public boolean equals(@NotNull Object other) {
// If the name of this server matches another, the servers are the same.
- if (other instanceof Server server) {
- return server.getName().equalsIgnoreCase(this.getName());
+ if (other instanceof Server) {
+ return ((Server) other).getName().equalsIgnoreCase(this.getName());
}
return super.equals(other);
}
diff --git a/common/src/main/java/net/william278/husksync/config/Settings.java b/common/src/main/java/net/william278/husksync/config/Settings.java
index e087ffd0..dae114e6 100644
--- a/common/src/main/java/net/william278/husksync/config/Settings.java
+++ b/common/src/main/java/net/william278/husksync/config/Settings.java
@@ -19,6 +19,7 @@
package net.william278.husksync.config;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import de.exlll.configlib.Comment;
import de.exlll.configlib.Configuration;
@@ -47,15 +48,14 @@ import java.util.Map;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Settings {
- protected static final String CONFIG_HEADER = """
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ HuskSync Config ┃
- ┃ Developed by William278 ┃
- ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
- ┣╸ Information: https://william278.net/project/husksync
- ┣╸ Config Help: https://william278.net/docs/husksync/config-file/
- ┗╸ Documentation: https://william278.net/docs/husksync""";
-
+ protected static final String CONFIG_HEADER =
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n" +
+ "┃ HuskSync Config ┃\n" +
+ "┃ Developed by William278 ┃\n" +
+ "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" +
+ "┣╸ Information: https://william278.net/project/husksync\n" +
+ "┣╸ Config Help: https://william278.net/docs/husksync/config-file/\n" +
+ "┗╸ Documentation: https://william278.net/docs/husksync";
// Top-level settings
@Comment({"Locale of the default language file to use.", "Docs: https://william278.net/docs/husksync/translations"})
private String language = Locales.DEFAULT_LOCALE;
@@ -207,7 +207,7 @@ public class Settings {
@Comment({"List of save cause IDs for which a snapshot will be automatically pinned (so it won't be rotated).",
"Docs: https://william278.net/docs/husksync/data-rotation#save-causes"})
@Getter(AccessLevel.NONE)
- private List autoPinnedSaveCauses = List.of(
+ private List autoPinnedSaveCauses = ImmutableList.of(
DataSnapshot.SaveCause.INVENTORY_COMMAND.name(),
DataSnapshot.SaveCause.ENDERCHEST_COMMAND.name(),
DataSnapshot.SaveCause.BACKUP_RESTORE.name(),
@@ -265,7 +265,7 @@ public class Settings {
private Map features = Identifier.getConfigMap();
@Comment("Commands which should be blocked before a player has finished syncing (Use * to block all commands)")
- private List blacklistedCommandsWhileLocked = new ArrayList<>(List.of("*"));
+ private List blacklistedCommandsWhileLocked = new ArrayList<>(ImmutableList.of("*"));
@Comment("Configuration for how to sync attributes")
private AttributeSettings attributes = new AttributeSettings();
@@ -278,21 +278,21 @@ public class Settings {
@Comment({"Which attributes should not be saved when syncing users. Supports wildcard matching.",
"(e.g. ['minecraft:generic.max_health', 'minecraft:generic.*'])"})
@Getter(AccessLevel.NONE)
- private List ignoredAttributes = new ArrayList<>(List.of(""));
+ private List ignoredAttributes = new ArrayList<>(ImmutableList.of(""));
@Comment({"Which modifiers should not be saved when syncing users. Supports wildcard matching.",
"(e.g. ['minecraft:effect.speed', 'minecraft:effect.*'])"})
@Getter(AccessLevel.NONE)
- private List ignoredModifiers = new ArrayList<>(List.of(
+ private List ignoredModifiers = new ArrayList<>(ImmutableList.of(
"minecraft:effect.*", "minecraft:creative_mode_*"
));
private boolean matchesWildcard(@NotNull String pat, @NotNull String value) {
if (!pat.contains(":")) {
- pat = "minecraft:%s".formatted(pat);
+ pat = "minecraft:" + pat;
}
if (!value.contains(":")) {
- value = "minecraft:%s".formatted(value);
+ value = "minecraft:" + value;
}
return pat.contains("*") ? value.matches(pat.replace("*", ".*")) : pat.equals(value);
}
diff --git a/common/src/main/java/net/william278/husksync/data/Data.java b/common/src/main/java/net/william278/husksync/data/Data.java
index 46074204..d0a337b4 100644
--- a/common/src/main/java/net/william278/husksync/data/Data.java
+++ b/common/src/main/java/net/william278/husksync/data/Data.java
@@ -25,6 +25,7 @@ import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import lombok.Value;
import lombok.experimental.Accessors;
import net.kyori.adventure.key.Key;
import net.william278.husksync.HuskSync;
@@ -65,9 +66,13 @@ public interface Data {
return getStack().length;
}
- record Stack(@NotNull String material, int amount, @Nullable String name,
- @Nullable List lore, @NotNull List enchantments) {
-
+ @Value
+ class Stack {
+ @NotNull String material;
+ int amount;
+ @Nullable String name;
+ @Nullable List lore;
+ @NotNull List enchantments;
}
default boolean isEmpty() {
@@ -129,23 +134,32 @@ public interface Data {
@NotNull
List getActiveEffects();
- /**
- * Represents a potion effect
- *
- * @param type the type of potion effect
- * @param amplifier the amplifier of the potion effect
- * @param duration the duration of the potion effect
- * @param isAmbient whether the potion effect is ambient
- * @param showParticles whether the potion effect shows particles
- * @param hasIcon whether the potion effect displays a HUD icon
- */
- record Effect(@SerializedName("type") @NotNull String type,
- @SerializedName("amplifier") int amplifier,
- @SerializedName("duration") int duration,
- @SerializedName("is_ambient") boolean isAmbient,
- @SerializedName("show_particles") boolean showParticles,
- @SerializedName("has_icon") boolean hasIcon) {
-
+ @Value
+ class Effect {
+ /**
+ * The type of potion effect
+ */
+ @SerializedName("type") @NotNull String type;
+ /**
+ * The amplifier of the potion effect
+ */
+ @SerializedName("amplifier") int amplifier;
+ /**
+ * The duration of the potion effect
+ */
+ @SerializedName("duration") int duration;
+ /**
+ * Whether the potion effect is ambient
+ */
+ @SerializedName("is_ambient") boolean isAmbient;
+ /**
+ * Whether the potion effect shows particles
+ */
+ @SerializedName("show_particles") boolean showParticles;
+ /**
+ * Whether the potion effect displays a HUD icon
+ */
+ @SerializedName("has_icon") boolean hasIcon;
}
}
@@ -162,7 +176,9 @@ public interface Data {
@NotNull
default List getCompletedExcludingRecipes() {
- return getCompleted().stream().filter(adv -> !adv.getKey().startsWith(RECIPE_ADVANCEMENT)).toList();
+ return getCompleted().stream()
+ .filter(adv -> !adv.getKey().startsWith(RECIPE_ADVANCEMENT))
+ .collect(Collectors.toList());
}
void setCompleted(@NotNull List completed);
@@ -249,11 +265,11 @@ public interface Data {
void setWorld(@NotNull World world);
- record World(
- @SerializedName("name") @NotNull String name,
- @SerializedName("uuid") @NotNull UUID uuid,
- @SerializedName("environment") @NotNull String environment
- ) {
+ @Value
+ class World {
+ @SerializedName("name") @NotNull String name;
+ @SerializedName("uuid") @NotNull UUID uuid;
+ @SerializedName("environment") @NotNull String environment;
}
}
@@ -295,17 +311,17 @@ public interface Data {
void setHealth(double health);
/**
- * @deprecated Use {@link Attributes#getMaxHealth()} instead
+ * @deprecated Use {@link Attributes#getMaxHealth()} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default double getMaxHealth() {
return getHealth();
}
/**
- * @deprecated Use {@link Attributes#setMaxHealth(double)} instead
+ * @deprecated Use {@link Attributes#setMaxHealth(double)} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default void setMaxHealth(double maxHealth) {
}
@@ -323,11 +339,11 @@ public interface Data {
List getAttributes();
- record Attribute(
- @NotNull String name,
- double baseValue,
- @NotNull Set modifiers
- ) {
+ @Value
+ class Attribute {
+ @NotNull String name;
+ double baseValue;
+ @NotNull Set modifiers;
public double getValue() {
double value = baseValue;
@@ -366,7 +382,8 @@ public interface Data {
@Override
public boolean equals(Object obj) {
- if (obj instanceof Modifier other) {
+ if (obj instanceof Modifier) {
+ Modifier other = (Modifier) obj;
if (uuid == null || other.uuid == null) {
return name.equals(other.name);
}
@@ -376,12 +393,16 @@ public interface Data {
}
public double modify(double value) {
- return switch (operationType) {
- case 0 -> value + amount;
- case 1 -> value * amount;
- case 2 -> value * (1 + amount);
- default -> value;
- };
+ switch (operationType) {
+ case 0:
+ return value + amount;
+ case 1:
+ return value * amount;
+ case 2:
+ return value * (1 + amount);
+ default:
+ return value;
+ }
}
public boolean hasUuid() {
@@ -397,12 +418,12 @@ public interface Data {
default Optional getAttribute(@NotNull Key key) {
return getAttributes().stream()
- .filter(attribute -> attribute.name().equals(key.asString()))
+ .filter(attribute -> attribute.getName().equals(key.asString()))
.findFirst();
}
default void removeAttribute(@NotNull Key key) {
- getAttributes().removeIf(attribute -> attribute.name().equals(key.asString()));
+ getAttributes().removeIf(attribute -> attribute.getName().equals(key.asString()));
}
default double getMaxHealth() {
@@ -481,9 +502,9 @@ public interface Data {
*
* @return {@code false} since v3.5
* @deprecated Moved to its own data type. This will always return {@code false}.
- * Use {@link FlightStatus#isAllowFlight()} instead
+ * Use {@link FlightStatus#isAllowFlight()} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default boolean getAllowFlight() {
return false;
}
@@ -492,9 +513,9 @@ public interface Data {
* Set if the player can fly.
*
* @deprecated Moved to its own data type.
- * Use {@link FlightStatus#setAllowFlight(boolean)} instead
+ * Use {@link FlightStatus#setAllowFlight(boolean)} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default void setAllowFlight(boolean allowFlight) {
}
@@ -503,9 +524,9 @@ public interface Data {
*
* @return {@code false} since v3.5
* @deprecated Moved to its own data type. This will always return {@code false}.
- * Use {@link FlightStatus#isFlying()} instead
+ * Use {@link FlightStatus#isFlying()} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default boolean getIsFlying() {
return false;
}
@@ -514,9 +535,9 @@ public interface Data {
* Set if the player is flying.
*
* @deprecated Moved to its own data type.
- * Use {@link FlightStatus#setFlying(boolean)} instead
+ * Use {@link FlightStatus#setFlying(boolean)} instead, since 3.5
*/
- @Deprecated(forRemoval = true, since = "3.5")
+ @Deprecated
default void setIsFlying(boolean isFlying) {
}
diff --git a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java
index 2068bc30..528994d2 100644
--- a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java
+++ b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java
@@ -383,7 +383,7 @@ public class DataSnapshot {
private Unpacked(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp,
@NotNull String saveCause, @NotNull String serverName, @NotNull TreeMap data,
@NotNull Version minecraftVersion, @NotNull String platformType, int formatVersion) {
- super(id, pinned, timestamp, saveCause, serverName, Map.of(), minecraftVersion, platformType, formatVersion);
+ super(id, pinned, timestamp, saveCause, serverName, Collections.emptyMap(), minecraftVersion, platformType, formatVersion);
this.deserialized = data;
}
@@ -392,7 +392,10 @@ public class DataSnapshot {
private TreeMap deserializeData(@NotNull HuskSync plugin) {
return data.entrySet().stream()
.filter(e -> plugin.getIdentifier(e.getKey()).isPresent())
- .map(entry -> Map.entry(plugin.getIdentifier(entry.getKey()).orElseThrow(), entry.getValue()))
+ .map(entry -> Maps.immutableEntry(
+ plugin.getIdentifier(entry.getKey()).orElseThrow(() -> new IllegalStateException("Invalid identifier")),
+ entry.getValue()
+ ))
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> plugin.deserializeData(entry.getKey(), entry.getValue(), getMinecraftVersion()),
@@ -939,7 +942,7 @@ public class DataSnapshot {
@NotNull
public String getLocale(@NotNull HuskSync plugin) {
return plugin.getLocales()
- .getRawLocale("save_cause_%s".formatted(name().toLowerCase(Locale.ENGLISH)))
+ .getRawLocale("save_cause_" + name().toLowerCase(Locale.ENGLISH))
.orElse(getDisplayName());
}
diff --git a/common/src/main/java/net/william278/husksync/data/Identifier.java b/common/src/main/java/net/william278/husksync/data/Identifier.java
index bf915f9a..5e2efbb3 100644
--- a/common/src/main/java/net/william278/husksync/data/Identifier.java
+++ b/common/src/main/java/net/william278/husksync/data/Identifier.java
@@ -19,6 +19,9 @@
package net.william278.husksync.data;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import lombok.*;
import net.kyori.adventure.key.InvalidKeyException;
import net.kyori.adventure.key.Key;
@@ -150,7 +153,7 @@ public class Identifier {
private static Identifier huskSync(@Subst("null") @NotNull String name,
@SuppressWarnings("SameParameterValue") boolean configDefault,
@NotNull Dependency... dependents) throws InvalidKeyException {
- return new Identifier(Key.key("husksync", name), configDefault, Set.of(dependents));
+ return new Identifier(Key.key("husksync", name), configDefault, ImmutableSet.copyOf(dependents));
}
/**
@@ -163,7 +166,7 @@ public class Identifier {
@ApiStatus.Internal
@SuppressWarnings("unchecked")
public static Map getConfigMap() {
- return Map.ofEntries(Stream.of(
+ return ImmutableMap.ofEntries(Stream.of(
INVENTORY, ENDER_CHEST, POTION_EFFECTS, ADVANCEMENTS, LOCATION, STATISTICS,
HEALTH, HUNGER, ATTRIBUTES, EXPERIENCE, GAME_MODE, FLIGHT_STATUS, PERSISTENT_DATA
)
@@ -230,7 +233,8 @@ public class Identifier {
*/
@Override
public boolean equals(Object obj) {
- if (obj instanceof Identifier other) {
+ if (obj instanceof Identifier) {
+ Identifier other = (Identifier) obj;
return key.equals(other.key);
}
return false;
@@ -239,7 +243,7 @@ public class Identifier {
// Get the config entry for the identifier
@NotNull
private Map.Entry getConfigEntry() {
- return Map.entry(getKeyValue(), enabledByDefault);
+ return Maps.immutableEntry(getKeyValue(), enabledByDefault);
}
/**
@@ -260,7 +264,7 @@ public class Identifier {
if (i1.dependsOn(i2)) {
if (i2.dependsOn(i1)) {
throw new IllegalArgumentException(
- "Found circular dependency between %s and %s".formatted(i1.getKey(), i2.getKey())
+ "Found circular dependency between " + i1.getKey() + " and " + i2.getKey()
);
}
return 1;
@@ -310,7 +314,8 @@ public class Identifier {
@Override
public boolean equals(Object obj) {
- if (obj instanceof Dependency other) {
+ if (obj instanceof Dependency) {
+ Dependency other = (Dependency) obj;
return key.equals(other.key);
}
return false;
diff --git a/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java b/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java
index 32e46a21..7157ed83 100644
--- a/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java
+++ b/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.logging.Level;
+import java.util.stream.Collectors;
public interface SerializerRegistry {
@@ -52,7 +53,7 @@ public interface SerializerRegistry {
@SuppressWarnings("unchecked")
default void registerSerializer(@NotNull Identifier id, @NotNull Serializer extends Data> serializer) {
if (id.isCustom()) {
- getPlugin().log(Level.INFO, "Registered custom data type: %s".formatted(id));
+ getPlugin().log(Level.INFO, "Registered custom data type: " + id);
}
id.setEnabled(id.isCustom() || getPlugin().getSettings().getSynchronization().isFeatureEnabled(id));
getSerializers().put(id, (Serializer) serializer);
@@ -72,11 +73,11 @@ public interface SerializerRegistry {
final List unmet = identifier.getDependencies().stream()
.filter(Identifier.Dependency::isRequired)
.filter(dep -> !isDataTypeAvailable(dep.getKey().asString()))
- .map(dep -> dep.getKey().asString()).toList();
+ .map(dep -> dep.getKey().asString())
+ .collect(Collectors.toList());
if (!unmet.isEmpty()) {
identifier.setEnabled(false);
- getPlugin().log(Level.WARNING, "Disabled %s syncing as the following types need to be on: %s"
- .formatted(identifier, String.join(", ", unmet)));
+ getPlugin().log(Level.WARNING, "Disabled " + identifier + " syncing as the following types need to be on: " + String.join(", ", unmet));
}
});
}
@@ -116,7 +117,7 @@ public interface SerializerRegistry {
@NotNull
default String serializeData(@NotNull Identifier identifier, @NotNull Data data) throws IllegalStateException {
return getSerializer(identifier).map(serializer -> serializer.serialize(data))
- .orElseThrow(() -> new IllegalStateException("No serializer found for %s".formatted(identifier)));
+ .orElseThrow(() -> new IllegalStateException("No serializer found for " + identifier));
}
/**
@@ -133,7 +134,7 @@ public interface SerializerRegistry {
default Data deserializeData(@NotNull Identifier identifier, @NotNull String data,
@NotNull Version dataMcVersion) throws IllegalStateException {
return getSerializer(identifier).map(serializer -> serializer.deserialize(data, dataMcVersion)).orElseThrow(
- () -> new IllegalStateException("No serializer found for %s".formatted(identifier))
+ () -> new IllegalStateException("No serializer found for " + identifier)
);
}
@@ -144,10 +145,10 @@ public interface SerializerRegistry {
* @param data the data to deserialize
* @return the deserialized data
* @since 3.5.4
- * @deprecated Use {@link #deserializeData(Identifier, String, Version)} instead
+ * @deprecated Use {@link #deserializeData(Identifier, String, Version)} instead, since 3.6.5
*/
@NotNull
- @Deprecated(since = "3.6.5")
+ @Deprecated
default Data deserializeData(@NotNull Identifier identifier, @NotNull String data) {
return deserializeData(identifier, data, getPlugin().getMinecraftVersion());
}
@@ -164,7 +165,7 @@ public interface SerializerRegistry {
}
// Returns if a data type is available and enabled in the config
- private boolean isDataTypeAvailable(@NotNull String key) {
+ default boolean isDataTypeAvailable(@NotNull String key) {
return getIdentifier(key).map(Identifier::isEnabled).orElse(false);
}
diff --git a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java
index 5f1cb9ff..0e75f22d 100644
--- a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java
+++ b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java
@@ -19,6 +19,7 @@
package net.william278.husksync.data;
+import com.google.common.collect.Maps;
import net.william278.desertwell.util.ThrowingConsumer;
import net.william278.husksync.HuskSync;
import org.jetbrains.annotations.ApiStatus;
@@ -44,7 +45,7 @@ public interface UserDataHolder extends DataHolder {
default Map getData() {
return getPlugin().getRegisteredDataTypes().stream()
.filter(Identifier::isEnabled)
- .map(id -> Map.entry(id, getData(id)))
+ .map(id -> Maps.immutableEntry(id, getData(id)))
.filter(data -> data.getValue().isPresent())
.collect(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll);
}
diff --git a/common/src/main/java/net/william278/husksync/database/Database.java b/common/src/main/java/net/william278/husksync/database/Database.java
index 732b641f..ebd1c0bb 100644
--- a/common/src/main/java/net/william278/husksync/database/Database.java
+++ b/common/src/main/java/net/william278/husksync/database/Database.java
@@ -19,11 +19,14 @@
package net.william278.husksync.database;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import lombok.Getter;
import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.InputStreamUtil;
import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull;
@@ -56,8 +59,10 @@ public abstract class Database {
@SuppressWarnings("SameParameterValue")
@NotNull
protected final String[] getSchemaStatements(@NotNull String schemaFileName) throws IOException {
- return formatStatementTables(new String(Objects.requireNonNull(plugin.getResource(schemaFileName))
- .readAllBytes(), StandardCharsets.UTF_8)).split(";");
+ return formatStatementTables(new String(
+ InputStreamUtil.readAllBytes(Objects.requireNonNull(plugin.getResource(schemaFileName))),
+ StandardCharsets.UTF_8
+ )).split(";");
}
/**
@@ -285,13 +290,13 @@ public abstract class Database {
@NotNull
private Map.Entry toEntry() {
- return Map.entry(name().toLowerCase(Locale.ENGLISH), defaultName);
+ return Maps.immutableEntry(name().toLowerCase(Locale.ENGLISH), defaultName);
}
@SuppressWarnings("unchecked")
@NotNull
public static Map getDefaults() {
- return Map.ofEntries(Arrays.stream(values())
+ return ImmutableMap.ofEntries(Arrays.stream(values())
.map(TableName::toEntry)
.toArray(Map.Entry[]::new));
}
diff --git a/common/src/main/java/net/william278/husksync/database/MongoDbDatabase.java b/common/src/main/java/net/william278/husksync/database/MongoDbDatabase.java
index 08da5091..7a79b6d9 100644
--- a/common/src/main/java/net/william278/husksync/database/MongoDbDatabase.java
+++ b/common/src/main/java/net/william278/husksync/database/MongoDbDatabase.java
@@ -30,6 +30,7 @@ import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.database.mongo.MongoCollectionHelper;
import net.william278.husksync.database.mongo.MongoConnectionHandler;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.Binary;
@@ -43,6 +44,7 @@ import java.util.Optional;
import java.util.TimeZone;
import java.util.UUID;
import java.util.logging.Level;
+import java.util.stream.Collectors;
public class MongoDbDatabase extends Database {
private MongoConnectionHandler mongoConnectionHandler;
@@ -103,7 +105,7 @@ public class MongoDbDatabase extends Database {
@Override
public void ensureUser(@NotNull User user) {
try {
- getUser(user.getUuid()).ifPresentOrElse(
+ OptionalUtil.ifPresentOrElse(getUser(user.getUuid()),
existingUser -> {
if (!existingUser.getUsername().equals(user.getUsername())) {
// Update a user's name if it has changed in the database
@@ -277,7 +279,8 @@ public class MongoDbDatabase extends Database {
protected void rotateSnapshots(@NotNull User user) {
try {
final List unpinnedUserData = getAllSnapshots(user).stream()
- .filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
+ .filter(dataSnapshot -> !dataSnapshot.isPinned())
+ .collect(Collectors.toList());
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
if (unpinnedUserData.size() > maxSnapshots) {
diff --git a/common/src/main/java/net/william278/husksync/database/MySqlDatabase.java b/common/src/main/java/net/william278/husksync/database/MySqlDatabase.java
index f3b38dc1..9751f274 100644
--- a/common/src/main/java/net/william278/husksync/database/MySqlDatabase.java
+++ b/common/src/main/java/net/william278/husksync/database/MySqlDatabase.java
@@ -20,11 +20,12 @@
package net.william278.husksync.database;
import com.google.common.collect.Lists;
-import com.zaxxer.hikari.HikariDataSource;
import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.DataAdapter;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
+import org.inksnow.cputil.db.AuroraDatabase;
import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull;
@@ -34,6 +35,7 @@ import java.sql.*;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.logging.Level;
+import java.util.stream.Collectors;
import static net.william278.husksync.config.Settings.DatabaseSettings;
@@ -41,15 +43,17 @@ public class MySqlDatabase extends Database {
private static final String DATA_POOL_NAME = "HuskSyncHikariPool";
private final String flavor;
- private final String driverClass;
- private HikariDataSource dataSource;
+ private final org.inksnow.cputil.db.Database databaseType;
+ private AuroraDatabase dataSource;
public MySqlDatabase(@NotNull HuskSync plugin) {
super(plugin);
final Type type = plugin.getSettings().getDatabase().getType();
this.flavor = type.getProtocol();
- this.driverClass = type == Type.MARIADB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
+ this.databaseType = type == Type.MARIADB
+ ? new org.inksnow.cputil.db.mariadb.MariadbDatabase()
+ : new org.inksnow.cputil.db.mysql.MysqlDatabase();
}
/**
@@ -72,48 +76,44 @@ public class MySqlDatabase extends Database {
public void initialize() throws IllegalStateException {
// Initialize the Hikari pooled connection
final DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
- dataSource = new HikariDataSource();
- dataSource.setDriverClassName(driverClass);
- dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
- flavor,
- credentials.getHost(),
- credentials.getPort(),
- credentials.getDatabase(),
- credentials.getParameters()
- ));
-
- // Authenticate with the database
- dataSource.setUsername(credentials.getUsername());
- dataSource.setPassword(credentials.getPassword());
-
- // Set connection pool options
- final DatabaseSettings.PoolSettings pool = plugin.getSettings().getDatabase().getConnectionPool();
- dataSource.setMaximumPoolSize(pool.getMaximumPoolSize());
- dataSource.setMinimumIdle(pool.getMinimumIdle());
- dataSource.setMaxLifetime(pool.getMaximumLifetime());
- dataSource.setKeepaliveTime(pool.getKeepaliveTime());
- dataSource.setConnectionTimeout(pool.getConnectionTimeout());
- dataSource.setPoolName(DATA_POOL_NAME);
-
- // Set additional connection pool properties
- final Properties properties = new Properties();
- properties.putAll(
- Map.of("cachePrepStmts", "true",
- "prepStmtCacheSize", "250",
- "prepStmtCacheSqlLimit", "2048",
- "useServerPrepStmts", "true",
- "useLocalSessionState", "true",
- "useLocalTransactionState", "true"
- ));
- properties.putAll(
- Map.of(
- "rewriteBatchedStatements", "true",
- "cacheResultSetMetadata", "true",
- "cacheServerConfiguration", "true",
- "elideSetAutoCommits", "true",
- "maintainTimeStats", "false")
- );
- dataSource.setDataSourceProperties(properties);
+
+ try {
+ dataSource = AuroraDatabase.builder()
+ .databaseType(databaseType)
+ .jdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
+ flavor,
+ credentials.getHost(),
+ credentials.getPort(),
+ credentials.getDatabase(),
+ credentials.getParameters()
+ ))
+ .username(credentials.getUsername())
+ .password(credentials.getPassword())
+ .extension(dataSource -> {
+ final DatabaseSettings.PoolSettings pool = plugin.getSettings().getDatabase().getConnectionPool();
+ dataSource.setMaximumPoolSize(pool.getMaximumPoolSize());
+ dataSource.setMinimumIdle(pool.getMinimumIdle());
+ dataSource.setMaxLifetime(pool.getMaximumLifetime());
+ dataSource.setKeepaliveTime(pool.getKeepaliveTime());
+ dataSource.setConnectionTimeout(pool.getConnectionTimeout());
+ dataSource.setPoolName(DATA_POOL_NAME);
+ })
+ .driverProperty("cachePrepStmts", "true")
+ .driverProperty("prepStmtCacheSize", "250")
+ .driverProperty("prepStmtCacheSqlLimit", "2048")
+ .driverProperty("useServerPrepStmts", "true")
+ .driverProperty("useLocalSessionState", "true")
+ .driverProperty("useLocalTransactionState", "true")
+
+ .driverProperty("rewriteBatchedStatements", "true")
+ .driverProperty("cacheResultSetMetadata", "true")
+ .driverProperty("cacheServerConfiguration", "true")
+ .driverProperty("elideSetAutoCommits", "true")
+ .driverProperty("maintainTimeStats", "false")
+ .build();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to initialize the Aurora database", e);
+ }
// Prepare database schema; make tables if they don't exist
try (Connection connection = dataSource.getConnection()) {
@@ -135,15 +135,14 @@ public class MySqlDatabase extends Database {
@Blocking
@Override
public void ensureUser(@NotNull User user) {
- getUser(user.getUuid()).ifPresentOrElse(
+ OptionalUtil.ifPresentOrElse(getUser(user.getUuid()),
existingUser -> {
if (!existingUser.getUsername().equals(user.getUsername())) {
// Update a user's name if it has changed in the database
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- UPDATE `%users_table%`
- SET `username`=?
- WHERE `uuid`=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "UPDATE `%users_table%` SET `username`=? WHERE `uuid`=?"
+ ))) {
statement.setString(1, user.getUsername());
statement.setString(2, existingUser.getUuid().toString());
@@ -158,9 +157,9 @@ public class MySqlDatabase extends Database {
() -> {
// Insert new player data into the database
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- INSERT INTO `%users_table%` (`uuid`,`username`)
- VALUES (?,?);"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "INSERT INTO `%users_table%` (`uuid`,`username`) VALUES (?,?);"
+ ))) {
statement.setString(1, user.getUuid().toString());
statement.setString(2, user.getUsername());
@@ -177,10 +176,9 @@ public class MySqlDatabase extends Database {
@Override
public Optional getUser(@NotNull UUID uuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT `uuid`, `username`
- FROM `%users_table%`
- WHERE `uuid`=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT `uuid`, `username` FROM `%users_table%` WHERE `uuid`=?"
+ ))) {
statement.setString(1, uuid.toString());
@@ -200,10 +198,9 @@ public class MySqlDatabase extends Database {
@Override
public Optional getUserByName(@NotNull String username) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT `uuid`, `username`
- FROM `%users_table%`
- WHERE `username`=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT `uuid`, `username` FROM `%users_table%` WHERE `username`=?"
+ ))) {
statement.setString(1, username);
final ResultSet resultSet = statement.executeQuery();
@@ -222,12 +219,13 @@ public class MySqlDatabase extends Database {
@Override
public Optional getLatestSnapshot(@NotNull User user) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT `version_uuid`, `timestamp`, `data`
- FROM `%user_data_table%`
- WHERE `player_uuid`=?
- ORDER BY `timestamp` DESC
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT `version_uuid`, `timestamp`, `data` " +
+ "FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? " +
+ "ORDER BY `timestamp` DESC " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, user.getUuid().toString());
final ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
@@ -253,11 +251,12 @@ public class MySqlDatabase extends Database {
public List getAllSnapshots(@NotNull User user) {
final List retrievedData = Lists.newArrayList();
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT `version_uuid`, `timestamp`, `data`
- FROM `%user_data_table%`
- WHERE `player_uuid`=?
- ORDER BY `timestamp` DESC;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT `version_uuid`, `timestamp`, `data` " +
+ "FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? " +
+ "ORDER BY `timestamp` DESC;"
+ ))) {
statement.setString(1, user.getUuid().toString());
final ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
@@ -282,12 +281,13 @@ public class MySqlDatabase extends Database {
@Override
public Optional getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT `version_uuid`, `timestamp`, `data`
- FROM `%user_data_table%`
- WHERE `player_uuid`=? AND `version_uuid`=?
- ORDER BY `timestamp` DESC
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT `version_uuid`, `timestamp`, `data` " +
+ "FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? AND `version_uuid`=? " +
+ "ORDER BY `timestamp` DESC " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, user.getUuid().toString());
statement.setString(2, versionUuid.toString());
final ResultSet resultSet = statement.executeQuery();
@@ -311,17 +311,17 @@ public class MySqlDatabase extends Database {
@Override
protected void rotateSnapshots(@NotNull User user) {
final List unpinnedUserData = getAllSnapshots(user).stream()
- .filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
+ .filter(dataSnapshot -> !dataSnapshot.isPinned())
+ .collect(Collectors.toList());
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
if (unpinnedUserData.size() > maxSnapshots) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM `%user_data_table%`
- WHERE `player_uuid`=?
- AND `pinned` IS FALSE
- ORDER BY `timestamp` ASC
- LIMIT %entry_count%;""".replace("%entry_count%",
- Integer.toString(unpinnedUserData.size() - maxSnapshots))))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? " +
+ "AND `pinned` IS FALSE " +
+ "ORDER BY `timestamp` ASC " +
+ "LIMIT " + (unpinnedUserData.size() - maxSnapshots) + ";"))) {
statement.setString(1, user.getUuid().toString());
statement.executeUpdate();
}
@@ -335,10 +335,11 @@ public class MySqlDatabase extends Database {
@Override
public boolean deleteSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM `%user_data_table%`
- WHERE `player_uuid`=? AND `version_uuid`=?
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? AND `version_uuid`=? " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, user.getUuid().toString());
statement.setString(2, versionUuid.toString());
return statement.executeUpdate() > 0;
@@ -353,11 +354,12 @@ public class MySqlDatabase extends Database {
@Override
protected void rotateLatestSnapshot(@NotNull User user, @NotNull OffsetDateTime within) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM `%user_data_table%`
- WHERE `player_uuid`=? AND `timestamp`>? AND `pinned` IS FALSE
- ORDER BY `timestamp` ASC
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM `%user_data_table%` " +
+ "WHERE `player_uuid`=? AND `timestamp`>? AND `pinned` IS FALSE " +
+ "ORDER BY `timestamp` ASC " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, user.getUuid().toString());
statement.setTimestamp(2, Timestamp.from(within.toInstant()));
statement.executeUpdate();
@@ -371,10 +373,11 @@ public class MySqlDatabase extends Database {
@Override
protected void createSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- INSERT INTO `%user_data_table%`
- (`player_uuid`,`version_uuid`,`timestamp`,`save_cause`,`pinned`,`data`)
- VALUES (?,?,?,?,?,?);"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "INSERT INTO `%user_data_table%` " +
+ "(`player_uuid`,`version_uuid`,`timestamp`,`save_cause`,`pinned`,`data`) " +
+ "VALUES (?,?,?,?,?,?);"
+ ))) {
statement.setString(1, user.getUuid().toString());
statement.setString(2, data.getId().toString());
statement.setTimestamp(3, Timestamp.from(data.getTimestamp().toInstant()));
@@ -392,11 +395,12 @@ public class MySqlDatabase extends Database {
@Override
public void updateSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- UPDATE `%user_data_table%`
- SET `save_cause`=?,`pinned`=?,`data`=?
- WHERE `player_uuid`=? AND `version_uuid`=?
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "UPDATE `%user_data_table%` " +
+ "SET `save_cause`=?,`pinned`=?,`data`=? " +
+ "WHERE `player_uuid`=? AND `version_uuid`=? " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, data.getSaveCause().name());
statement.setBoolean(2, data.isPinned());
statement.setBlob(3, new ByteArrayInputStream(data.asBytes(plugin)));
diff --git a/common/src/main/java/net/william278/husksync/database/PostgresDatabase.java b/common/src/main/java/net/william278/husksync/database/PostgresDatabase.java
index f414d2f5..dc554dfc 100644
--- a/common/src/main/java/net/william278/husksync/database/PostgresDatabase.java
+++ b/common/src/main/java/net/william278/husksync/database/PostgresDatabase.java
@@ -25,6 +25,8 @@ import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.DataAdapter;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
+import org.inksnow.cputil.db.AuroraDatabase;
import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull;
@@ -33,6 +35,7 @@ import java.sql.*;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.logging.Level;
+import java.util.stream.Collectors;
import static net.william278.husksync.config.Settings.DatabaseSettings;
@@ -40,7 +43,7 @@ public class PostgresDatabase extends Database {
private static final String DATA_POOL_NAME = "HuskSyncHikariPool";
private final String flavor;
- private final String driverClass;
+ private final org.inksnow.cputil.db.Database databaseType;
private HikariDataSource dataSource;
public PostgresDatabase(@NotNull HuskSync plugin) {
@@ -48,7 +51,7 @@ public class PostgresDatabase extends Database {
final Type type = plugin.getSettings().getDatabase().getType();
this.flavor = type.getProtocol();
- this.driverClass = "org.postgresql.Driver";
+ this.databaseType = new org.inksnow.cputil.db.postgres.PostgresqlDatabase();
}
/**
@@ -71,48 +74,44 @@ public class PostgresDatabase extends Database {
public void initialize() throws IllegalStateException {
// Initialize the Hikari pooled connection
final DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
- dataSource = new HikariDataSource();
- dataSource.setDriverClassName(driverClass);
- dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
- flavor,
- credentials.getHost(),
- credentials.getPort(),
- credentials.getDatabase(),
- credentials.getParameters()
- ));
-
- // Authenticate with the database
- dataSource.setUsername(credentials.getUsername());
- dataSource.setPassword(credentials.getPassword());
-
- // Set connection pool options
- final DatabaseSettings.PoolSettings pool = plugin.getSettings().getDatabase().getConnectionPool();
- dataSource.setMaximumPoolSize(pool.getMaximumPoolSize());
- dataSource.setMinimumIdle(pool.getMinimumIdle());
- dataSource.setMaxLifetime(pool.getMaximumLifetime());
- dataSource.setKeepaliveTime(pool.getKeepaliveTime());
- dataSource.setConnectionTimeout(pool.getConnectionTimeout());
- dataSource.setPoolName(DATA_POOL_NAME);
-
- // Set additional connection pool properties
- final Properties properties = new Properties();
- properties.putAll(
- Map.of("cachePrepStmts", "true",
- "prepStmtCacheSize", "250",
- "prepStmtCacheSqlLimit", "2048",
- "useServerPrepStmts", "true",
- "useLocalSessionState", "true",
- "useLocalTransactionState", "true"
- ));
- properties.putAll(
- Map.of(
- "rewriteBatchedStatements", "true",
- "cacheResultSetMetadata", "true",
- "cacheServerConfiguration", "true",
- "elideSetAutoCommits", "true",
- "maintainTimeStats", "false")
- );
- dataSource.setDataSourceProperties(properties);
+
+ try {
+ dataSource = AuroraDatabase.builder()
+ .databaseType(databaseType)
+ .jdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
+ flavor,
+ credentials.getHost(),
+ credentials.getPort(),
+ credentials.getDatabase(),
+ credentials.getParameters()
+ ))
+ .extension(dataSource -> {
+ final DatabaseSettings.PoolSettings pool = plugin.getSettings().getDatabase().getConnectionPool();
+ dataSource.setMaximumPoolSize(pool.getMaximumPoolSize());
+ dataSource.setMinimumIdle(pool.getMinimumIdle());
+ dataSource.setMaxLifetime(pool.getMaximumLifetime());
+ dataSource.setKeepaliveTime(pool.getKeepaliveTime());
+ dataSource.setConnectionTimeout(pool.getConnectionTimeout());
+ dataSource.setPoolName(DATA_POOL_NAME);
+ })
+ .username(credentials.getUsername())
+ .password(credentials.getPassword())
+ .driverProperty("cachePrepStmts", "true")
+ .driverProperty("prepStmtCacheSize", "250")
+ .driverProperty("prepStmtCacheSqlLimit", "2048")
+ .driverProperty("useServerPrepStmts", "true")
+ .driverProperty("useLocalSessionState", "true")
+ .driverProperty("useLocalTransactionState", "true")
+
+ .driverProperty("rewriteBatchedStatements", "true")
+ .driverProperty("cacheResultSetMetadata", "true")
+ .driverProperty("cacheServerConfiguration", "true")
+ .driverProperty("elideSetAutoCommits", "true")
+ .driverProperty("maintainTimeStats", "false")
+ .build();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to initialize the Aurora database", e);
+ }
// Prepare database schema; make tables if they don't exist
try (Connection connection = dataSource.getConnection()) {
@@ -134,15 +133,16 @@ public class PostgresDatabase extends Database {
@Blocking
@Override
public void ensureUser(@NotNull User user) {
- getUser(user.getUuid()).ifPresentOrElse(
+ OptionalUtil.ifPresentOrElse(getUser(user.getUuid()),
existingUser -> {
if (!existingUser.getUsername().equals(user.getUsername())) {
// Update a user's name if it has changed in the database
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- UPDATE "%users_table%"
- SET "username"=?
- WHERE "uuid"=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "UPDATE \"%users_table%\" " +
+ "SET \"username\"=? " +
+ "WHERE \"uuid\"=?"
+ ))) {
statement.setString(1, user.getUsername());
statement.setObject(2, existingUser.getUuid());
@@ -157,9 +157,10 @@ public class PostgresDatabase extends Database {
() -> {
// Insert new player data into the database
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- INSERT INTO "%users_table%" ("uuid","username")
- VALUES (?,?);"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "INSERT INTO \"%users_table%\" (\"uuid\",\"username\") " +
+ "VALUES (?,?);"
+ ))) {
statement.setObject(1, user.getUuid());
statement.setString(2, user.getUsername());
@@ -176,10 +177,11 @@ public class PostgresDatabase extends Database {
@Override
public Optional getUser(@NotNull UUID uuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT "uuid", "username"
- FROM "%users_table%"
- WHERE "uuid"=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT \"uuid\", \"username\" " +
+ "FROM \"%users_table%\" " +
+ "WHERE \"uuid\"=?"
+ ))) {
statement.setObject(1, uuid);
@@ -199,10 +201,11 @@ public class PostgresDatabase extends Database {
@Override
public Optional getUserByName(@NotNull String username) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT "uuid", "username"
- FROM "%users_table%"
- WHERE "username"=?"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT \"uuid\", \"username\" " +
+ "FROM \"%users_table%\" " +
+ "WHERE \"username\"=?"
+ ))) {
statement.setString(1, username);
final ResultSet resultSet = statement.executeQuery();
@@ -221,12 +224,13 @@ public class PostgresDatabase extends Database {
@Override
public Optional getLatestSnapshot(@NotNull User user) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT "version_uuid", "timestamp", "data"
- FROM "%user_data_table%"
- WHERE "player_uuid"=?
- ORDER BY "timestamp" DESC
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT \"version_uuid\", \"timestamp\", \"data\" " +
+ "FROM \"%user_data_table%\" " +
+ "WHERE \"player_uuid\"=? " +
+ "ORDER BY \"timestamp\" DESC " +
+ "LIMIT 1;"
+ ))) {
statement.setObject(1, user.getUuid());
final ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
@@ -250,11 +254,12 @@ public class PostgresDatabase extends Database {
public List getAllSnapshots(@NotNull User user) {
final List retrievedData = Lists.newArrayList();
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT "version_uuid", "timestamp", "data"
- FROM "%user_data_table%"
- WHERE "player_uuid"=?
- ORDER BY "timestamp" DESC;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT \"version_uuid\", \"timestamp\", \"data\" " +
+ "FROM \"%user_data_table%\" " +
+ "WHERE \"player_uuid\"=? " +
+ "ORDER BY \"timestamp\" DESC;"
+ ))) {
statement.setObject(1, user.getUuid());
final ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
@@ -277,12 +282,13 @@ public class PostgresDatabase extends Database {
@Override
public Optional getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- SELECT "version_uuid", "timestamp", "data"
- FROM "%user_data_table%"
- WHERE "player_uuid"=? AND "version_uuid"=?
- ORDER BY "timestamp" DESC
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "SELECT \"version_uuid\", \"timestamp\", \"data\" " +
+ "FROM \"%user_data_table%\" " +
+ "WHERE \"player_uuid\"=? AND \"version_uuid\"=? " +
+ "ORDER BY \"timestamp\" DESC " +
+ "LIMIT 1;"
+ ))) {
statement.setObject(1, user.getUuid());
statement.setObject(2, versionUuid);
final ResultSet resultSet = statement.executeQuery();
@@ -304,17 +310,18 @@ public class PostgresDatabase extends Database {
@Override
protected void rotateSnapshots(@NotNull User user) {
final List unpinnedUserData = getAllSnapshots(user).stream()
- .filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
+ .filter(dataSnapshot -> !dataSnapshot.isPinned())
+ .collect(Collectors.toList());
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
if (unpinnedUserData.size() > maxSnapshots) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM "%user_data_table%"
- WHERE "player_uuid"=?
- AND "pinned" = FALSE
- ORDER BY "timestamp" ASC
- LIMIT %entry_count%;""".replace("%entry_count%",
- Integer.toString(unpinnedUserData.size() - maxSnapshots))))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM \"%user_data_table%\"\n" +
+ "WHERE \"player_uuid\"=?\n" +
+ "AND \"pinned\" = FALSE\n" +
+ "ORDER BY \"timestamp\" ASC\n" +
+ "LIMIT " + (unpinnedUserData.size() - maxSnapshots) + ";"
+ ))) {
statement.setObject(1, user.getUuid());
statement.executeUpdate();
}
@@ -328,10 +335,11 @@ public class PostgresDatabase extends Database {
@Override
public boolean deleteSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM "%user_data_table%"
- WHERE "player_uuid"=? AND "version_uuid"=?
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM \"%user_data_table%\" " +
+ "WHERE \"player_uuid\"=? AND \"version_uuid\"=? " +
+ "LIMIT 1;"
+ ))) {
statement.setObject(1, user.getUuid());
statement.setString(2, versionUuid.toString());
return statement.executeUpdate() > 0;
@@ -346,15 +354,16 @@ public class PostgresDatabase extends Database {
@Override
protected void rotateLatestSnapshot(@NotNull User user, @NotNull OffsetDateTime within) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- DELETE FROM "%user_data_table%"
- WHERE "player_uuid"=? AND "timestamp" = (
- SELECT "timestamp"
- FROM "%user_data_table%"
- WHERE "player_uuid"=? AND "timestamp" > ? AND "pinned" = FALSE
- ORDER BY "timestamp" ASC
- LIMIT 1
- );"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "DELETE FROM \"%user_data_table%\" " +
+ "WHERE \"player_uuid\"=? AND \"timestamp\" = ( " +
+ " SELECT \"timestamp\" " +
+ " FROM \"%user_data_table%\" " +
+ " WHERE \"player_uuid\"=? AND \"timestamp\" > ? AND \"pinned\" = FALSE " +
+ " ORDER BY \"timestamp\" ASC " +
+ " LIMIT 1 " +
+ ");"
+ ))) {
statement.setObject(1, user.getUuid());
statement.setObject(2, user.getUuid());
statement.setTimestamp(3, Timestamp.from(within.toInstant()));
@@ -369,10 +378,11 @@ public class PostgresDatabase extends Database {
@Override
protected void createSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- INSERT INTO "%user_data_table%"
- ("player_uuid","version_uuid","timestamp","save_cause","pinned","data")
- VALUES (?,?,?,?,?,?);"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "INSERT INTO \"%user_data_table%\" " +
+ "(\"player_uuid\",\"version_uuid\",\"timestamp\",\"save_cause\",\"pinned\",\"data\") " +
+ "VALUES (?,?,?,?,?,?);"
+ ))) {
statement.setObject(1, user.getUuid());
statement.setObject(2, data.getId());
statement.setTimestamp(3, Timestamp.from(data.getTimestamp().toInstant()));
@@ -390,11 +400,12 @@ public class PostgresDatabase extends Database {
@Override
public void updateSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
try (Connection connection = getConnection()) {
- try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
- UPDATE "%user_data_table%"
- SET "save_cause"=?,"pinned"=?,"data"=?
- WHERE "player_uuid"=? AND "version_uuid"=?
- LIMIT 1;"""))) {
+ try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(
+ "UPDATE \"%user_data_table%\" " +
+ "SET \"save_cause\"=?,\"pinned\"=?,\"data\"=? " +
+ "WHERE \"player_uuid\"=? AND \"version_uuid\"=? " +
+ "LIMIT 1;"
+ ))) {
statement.setString(1, data.getSaveCause().name());
statement.setBoolean(2, data.isPinned());
statement.setBytes(3, data.asBytes(plugin));
diff --git a/common/src/main/java/net/william278/husksync/hook/PlanHook.java b/common/src/main/java/net/william278/husksync/hook/PlanHook.java
index 7ffd7fb2..3d35116c 100644
--- a/common/src/main/java/net/william278/husksync/hook/PlanHook.java
+++ b/common/src/main/java/net/william278/husksync/hook/PlanHook.java
@@ -34,6 +34,7 @@ import net.william278.husksync.data.DataSnapshot;
import org.jetbrains.annotations.NotNull;
import java.time.OffsetDateTime;
+import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Level;
@@ -248,7 +249,8 @@ public class PlanHook {
return getLatestSnapshot(playerUUID)
.flatMap(DataHolder::getAdvancements)
.map(Data.Advancements::getCompleted)
- .stream().count();
+ .map(List::size)
+ .orElse(0);
}
@Conditional("hasSynced")
diff --git a/common/src/main/java/net/william278/husksync/listener/EventListener.java b/common/src/main/java/net/william278/husksync/listener/EventListener.java
index 3cd22d3d..6b31a412 100644
--- a/common/src/main/java/net/william278/husksync/listener/EventListener.java
+++ b/common/src/main/java/net/william278/husksync/listener/EventListener.java
@@ -19,6 +19,8 @@
package net.william278.husksync.listener;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.Data;
import net.william278.husksync.data.DataSnapshot;
@@ -159,13 +161,13 @@ public abstract class EventListener {
@NotNull
private Map.Entry toEntry() {
- return Map.entry(name().toLowerCase(), defaultPriority.name());
+ return Maps.immutableEntry(name().toLowerCase(), defaultPriority.name());
}
@SuppressWarnings("unchecked")
@NotNull
public static Map getDefaults() {
- return Map.ofEntries(Arrays.stream(values())
+ return ImmutableMap.ofEntries(Arrays.stream(values())
.map(ListenerType::toEntry)
.toArray(Map.Entry[]::new));
}
diff --git a/common/src/main/java/net/william278/husksync/migrator/Migrator.java b/common/src/main/java/net/william278/husksync/migrator/Migrator.java
index c9389055..619d4ed6 100644
--- a/common/src/main/java/net/william278/husksync/migrator/Migrator.java
+++ b/common/src/main/java/net/william278/husksync/migrator/Migrator.java
@@ -19,6 +19,7 @@
package net.william278.husksync.migrator;
+import com.google.common.base.Strings;
import net.william278.husksync.HuskSync;
import org.jetbrains.annotations.NotNull;
@@ -56,7 +57,9 @@ public abstract class Migrator {
* @return The data string obfuscated with stars (*)
*/
protected final String obfuscateDataString(@NotNull String dataString) {
- return (dataString.length() > 1 ? dataString.charAt(0) + "*".repeat(dataString.length() - 1) : "");
+ return (dataString.length() > 1
+ ? dataString.charAt(0) + Strings.repeat("*", dataString.length() - 1)
+ : "");
}
@NotNull
diff --git a/common/src/main/java/net/william278/husksync/redis/RedisManager.java b/common/src/main/java/net/william278/husksync/redis/RedisManager.java
index 4ffa324e..c6b90ae8 100644
--- a/common/src/main/java/net/william278/husksync/redis/RedisManager.java
+++ b/common/src/main/java/net/william278/husksync/redis/RedisManager.java
@@ -23,6 +23,7 @@ import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.CompletableFutureUtil;
import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull;
import redis.clients.jedis.*;
@@ -127,8 +128,7 @@ public class RedisManager extends JedisPubSub {
}
if (reconnected) {
- plugin.log(Level.WARNING, "Redis Server connection lost. Attempting reconnect in %ss..."
- .formatted(RECONNECTION_TIME / 1000), t);
+ plugin.log(Level.WARNING, "Redis Server connection lost. Attempting reconnect in " + (RECONNECTION_TIME / 1000) + "s...", t);
}
try {
this.unsubscribe();
@@ -157,7 +157,8 @@ public class RedisManager extends JedisPubSub {
final RedisMessage redisMessage = RedisMessage.fromJson(plugin, message);
switch (messageType) {
- case UPDATE_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
+ case UPDATE_USER_DATA: {
+ plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
user -> {
try {
final DataSnapshot.Packed data = DataSnapshot.deserialize(plugin, redisMessage.getPayload());
@@ -167,14 +168,19 @@ public class RedisManager extends JedisPubSub {
user.completeSync(false, DataSnapshot.UpdateCause.UPDATED, plugin);
}
}
- );
- case REQUEST_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
+ );
+ break;
+ }
+ case REQUEST_USER_DATA: {
+ plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
user -> RedisMessage.create(
- UUID.fromString(new String(redisMessage.getPayload(), StandardCharsets.UTF_8)),
- user.createSnapshot(DataSnapshot.SaveCause.INVENTORY_COMMAND).asBytes(plugin)
+ UUID.fromString(new String(redisMessage.getPayload(), StandardCharsets.UTF_8)),
+ user.createSnapshot(DataSnapshot.SaveCause.INVENTORY_COMMAND).asBytes(plugin)
).dispatch(plugin, RedisMessage.Type.RETURN_USER_DATA)
- );
- case RETURN_USER_DATA -> {
+ );
+ break;
+ }
+ case RETURN_USER_DATA: {
final CompletableFuture> future = pendingRequests.get(
redisMessage.getTargetUuid()
);
@@ -188,6 +194,10 @@ public class RedisManager extends JedisPubSub {
}
pendingRequests.remove(redisMessage.getTargetUuid());
}
+ break;
+ }
+ default: {
+ // Do nothing
}
}
}
@@ -234,8 +244,7 @@ public class RedisManager extends JedisPubSub {
);
redisMessage.dispatch(plugin, RedisMessage.Type.REQUEST_USER_DATA);
});
- return future
- .orTimeout(
+ return CompletableFutureUtil.orTimeout(future,
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds(),
TimeUnit.MILLISECONDS
)
diff --git a/common/src/main/java/net/william278/husksync/sync/DataSyncer.java b/common/src/main/java/net/william278/husksync/sync/DataSyncer.java
index 749f5c41..f171907f 100644
--- a/common/src/main/java/net/william278/husksync/sync/DataSyncer.java
+++ b/common/src/main/java/net/william278/husksync/sync/DataSyncer.java
@@ -26,6 +26,7 @@ import net.william278.husksync.database.Database;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
+import net.william278.husksync.util.OptionalUtil;
import net.william278.husksync.util.Task;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Blocking;
@@ -158,12 +159,12 @@ public abstract class DataSyncer {
@ApiStatus.Internal
protected void setUserFromDatabase(@NotNull OnlineUser user) {
try {
- getDatabase().getLatestSnapshot(user).ifPresentOrElse(
+ OptionalUtil.ifPresentOrElse(getDatabase().getLatestSnapshot(user),
snapshot -> user.applySnapshot(snapshot, DataSnapshot.UpdateCause.SYNCHRONIZED),
() -> user.completeSync(true, DataSnapshot.UpdateCause.NEW_USER, plugin)
);
} catch (Throwable e) {
- plugin.log(Level.WARNING, "Failed to set %s's data from the database".formatted(user.getUsername()), e);
+ plugin.log(Level.WARNING, "Failed to set " + user.getUsername() + "'s data from the database", e);
user.completeSync(false, DataSnapshot.UpdateCause.SYNCHRONIZED, plugin);
}
}
diff --git a/common/src/main/java/net/william278/husksync/sync/LockstepDataSyncer.java b/common/src/main/java/net/william278/husksync/sync/LockstepDataSyncer.java
index ad2329c8..66c2e1af 100644
--- a/common/src/main/java/net/william278/husksync/sync/LockstepDataSyncer.java
+++ b/common/src/main/java/net/william278/husksync/sync/LockstepDataSyncer.java
@@ -23,6 +23,7 @@ import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.user.OnlineUser;
+import net.william278.husksync.util.OptionalUtil;
import org.jetbrains.annotations.NotNull;
public class LockstepDataSyncer extends DataSyncer {
@@ -49,7 +50,7 @@ public class LockstepDataSyncer extends DataSyncer {
return false;
}
getRedis().setUserCheckedOut(user, true);
- getRedis().getUserData(user).ifPresentOrElse(
+ OptionalUtil.ifPresentOrElse(getRedis().getUserData(user),
data -> user.applySnapshot(data, DataSnapshot.UpdateCause.SYNCHRONIZED),
() -> this.setUserFromDatabase(user)
);
diff --git a/common/src/main/java/net/william278/husksync/user/OnlineUser.java b/common/src/main/java/net/william278/husksync/user/OnlineUser.java
index c64a4881..b7af1bf7 100644
--- a/common/src/main/java/net/william278/husksync/user/OnlineUser.java
+++ b/common/src/main/java/net/william278/husksync/user/OnlineUser.java
@@ -89,9 +89,9 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
* @param description the description of the toast
* @param iconMaterial the namespace-keyed material to use as an hasIcon of the toast
* @param backgroundType the background ("ToastType") of the toast
- * @deprecated No longer supported
+ * @deprecated No longer supported, since 3.6.7
*/
- @Deprecated(since = "3.6.7")
+ @Deprecated
public abstract void sendToast(@NotNull MineDown title, @NotNull MineDown description,
@NotNull String iconMaterial, @NotNull String backgroundType);
@@ -145,8 +145,17 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
public void completeSync(boolean succeeded, @NotNull DataSnapshot.UpdateCause cause, @NotNull HuskSync plugin) {
if (succeeded) {
switch (plugin.getSettings().getSynchronization().getNotificationDisplaySlot()) {
- case CHAT -> cause.getCompletedLocale(plugin).ifPresent(this::sendMessage);
- case ACTION_BAR -> cause.getCompletedLocale(plugin).ifPresent(this::sendActionBar);
+ case CHAT: {
+ cause.getCompletedLocale(plugin).ifPresent(this::sendMessage);
+ break;
+ }
+ case ACTION_BAR:{
+ cause.getCompletedLocale(plugin).ifPresent(this::sendActionBar);
+ break;
+ }
+ default: {
+ // do nothing
+ }
}
plugin.fireEvent(
plugin.getSyncCompleteEvent(this),
diff --git a/common/src/main/java/net/william278/husksync/user/User.java b/common/src/main/java/net/william278/husksync/user/User.java
index 19c4bbc1..6f65b0d7 100644
--- a/common/src/main/java/net/william278/husksync/user/User.java
+++ b/common/src/main/java/net/william278/husksync/user/User.java
@@ -55,7 +55,8 @@ public class User {
@Override
public boolean equals(Object object) {
- if (object instanceof User other) {
+ if (object instanceof User) {
+ User other = (User) object;
return this.getUuid().equals(other.getUuid());
}
return super.equals(object);
diff --git a/common/src/main/java/net/william278/husksync/util/CompletableFutureUtil.java b/common/src/main/java/net/william278/husksync/util/CompletableFutureUtil.java
new file mode 100644
index 00000000..201f48e6
--- /dev/null
+++ b/common/src/main/java/net/william278/husksync/util/CompletableFutureUtil.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of HuskSync, licensed under the Apache License 2.0.
+ *
+ * Copyright (c) William278
+ * Copyright (c) contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.william278.husksync.util;
+
+import lombok.experimental.UtilityClass;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+
+@UtilityClass
+public class CompletableFutureUtil {
+ public static CompletableFuture orTimeout(CompletableFuture $this, long timeout, TimeUnit unit) {
+ if (unit == null)
+ throw new NullPointerException();
+ if (!$this.isDone()) {
+ $this.whenComplete(new Canceller(Delayer.delay(new Timeout($this),
+ timeout, unit)));
+ }
+ return $this;
+ }
+
+ static final class Canceller implements BiConsumer