mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-27 10:39:11 +00:00
Compare commits
3 Commits
3.8.2
...
test/debug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70d6b671f2 | ||
|
|
98576c72fb | ||
|
|
546e663e4e |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -54,17 +54,13 @@ jobs:
|
|||||||
paper-1.20.1
|
paper-1.20.1
|
||||||
paper-1.21.1
|
paper-1.21.1
|
||||||
paper-1.21.4
|
paper-1.21.4
|
||||||
paper-1.21.5
|
|
||||||
fabric-1.20.1
|
fabric-1.20.1
|
||||||
fabric-1.21.1
|
fabric-1.21.1
|
||||||
fabric-1.21.4
|
fabric-1.21.4
|
||||||
fabric-1.21.5
|
|
||||||
distro-groups: |
|
distro-groups: |
|
||||||
paper
|
paper
|
||||||
paper
|
paper
|
||||||
paper
|
paper
|
||||||
paper
|
|
||||||
fabric
|
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
@@ -72,17 +68,13 @@ jobs:
|
|||||||
Paper 1.20.1
|
Paper 1.20.1
|
||||||
Paper 1.21.1
|
Paper 1.21.1
|
||||||
Paper 1.21.4
|
Paper 1.21.4
|
||||||
Paper 1.21.5
|
|
||||||
Fabric 1.20.1
|
Fabric 1.20.1
|
||||||
Fabric 1.21.1
|
Fabric 1.21.1
|
||||||
Fabric 1.21.4
|
Fabric 1.21.4
|
||||||
Fabric 1.21.5
|
|
||||||
files: |
|
files: |
|
||||||
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.20.1.jar
|
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.20.1.jar
|
||||||
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.1.jar
|
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.1.jar
|
||||||
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.4.jar
|
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.4.jar
|
||||||
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.5.jar
|
|
||||||
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.20.1.jar
|
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.20.1.jar
|
||||||
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.21.1.jar
|
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.21.1.jar
|
||||||
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.21.4.jar
|
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.21.4.jar
|
||||||
target/HuskSync-Fabric-${{ env.version_name }}+mc.1.21.5.jar
|
|
||||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -43,17 +43,13 @@ jobs:
|
|||||||
paper-1.20.1
|
paper-1.20.1
|
||||||
paper-1.21.1
|
paper-1.21.1
|
||||||
paper-1.21.4
|
paper-1.21.4
|
||||||
paper-1.21.5
|
|
||||||
fabric-1.20.1
|
fabric-1.20.1
|
||||||
fabric-1.21.1
|
fabric-1.21.1
|
||||||
fabric-1.21.4
|
fabric-1.21.4
|
||||||
fabric-1.21.5
|
|
||||||
distro-groups: |
|
distro-groups: |
|
||||||
paper
|
paper
|
||||||
paper
|
paper
|
||||||
paper
|
paper
|
||||||
paper
|
|
||||||
fabric
|
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
@@ -61,17 +57,13 @@ jobs:
|
|||||||
Paper 1.20.1
|
Paper 1.20.1
|
||||||
Paper 1.21.1
|
Paper 1.21.1
|
||||||
Paper 1.21.4
|
Paper 1.21.4
|
||||||
Paper 1.21.5
|
|
||||||
Fabric 1.20.1
|
Fabric 1.20.1
|
||||||
Fabric 1.21.1
|
Fabric 1.21.1
|
||||||
Fabric 1.21.4
|
Fabric 1.21.4
|
||||||
Fabric 1.21.5
|
|
||||||
files: |
|
files: |
|
||||||
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.20.1.jar
|
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.20.1.jar
|
||||||
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.1.jar
|
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.1.jar
|
||||||
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.4.jar
|
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.4.jar
|
||||||
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.5.jar
|
|
||||||
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.20.1.jar
|
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.20.1.jar
|
||||||
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.21.1.jar
|
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.21.1.jar
|
||||||
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.21.4.jar
|
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.21.4.jar
|
||||||
target/HuskSync-Fabric-${{ github.event.release.tag_name }}+mc.1.21.5.jar
|
|
||||||
21
README.md
21
README.md
@@ -46,17 +46,16 @@
|
|||||||
## Compatibility
|
## Compatibility
|
||||||
HuskSync supports the following [compatible versions](https://william278.net/docs/husksync/compatibility) of Minecraft. Since v3.7, you must download the correct version of HuskSync for your server:
|
HuskSync supports the following [compatible versions](https://william278.net/docs/husksync/compatibility) of Minecraft. Since v3.7, you must download the correct version of HuskSync for your server:
|
||||||
|
|
||||||
| Minecraft | Latest HuskSync | Java Version | Platforms | Support Status |
|
| Minecraft | Latest HuskSync | Java Version | Platforms | Support Status |
|
||||||
|:---------------:|:---------------:|:------------:|:--------------|:------------------------------|
|
|:---------------:|:---------------:|:------------:|:--------------|:-----------------------------|
|
||||||
| 1.21.5 | _latest_ | 21 | Paper | ✅ **Active Release** |
|
| 1.21.4 | _latest_ | 21 | Paper, Fabric | ✅ **Active Release** |
|
||||||
| 1.21.4 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (Non-LTS) |
|
| 1.21.3 | 3.7.1 | 21 | Paper, Fabric | 🗃️ Archived (December 2024) |
|
||||||
| 1.21.3 | 3.7.1 | 21 | Paper, Fabric | 🗃️ Archived (December 2024) |
|
| 1.21.1 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
||||||
| 1.21.1 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
| 1.20.6 | 3.6.8 | 17 | Paper | 🗃️ Archived (October 2024) |
|
||||||
| 1.20.6 | 3.6.8 | 17 | Paper | 🗃️ Archived (October 2024) |
|
| 1.20.4 | 3.6.8 | 17 | Paper | 🗃️ Archived (July 2024) |
|
||||||
| 1.20.4 | 3.6.8 | 17 | Paper | 🗃️ Archived (July 2024) |
|
| 1.20.1 | _latest_ | 17 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
||||||
| 1.20.1 | _latest_ | 17 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
| 1.17.1 - 1.19.4 | 3.6.8 | 17 | Paper | 🗃️ Archived |
|
||||||
| 1.17.1 - 1.19.4 | 3.6.8 | 17 | Paper | 🗃️ Archived |
|
| 1.16.5 | 3.2.1 | 16 | Paper | 🗃️ Archived |
|
||||||
| 1.16.5 | 3.2.1 | 16 | Paper | 🗃️ Archived |
|
|
||||||
|
|
||||||
HuskSync is primarily developed against the latest release. Old Minecraft versions are allocated a support channel based on popularity, mod support, etc:
|
HuskSync is primarily developed against the latest release. Old Minecraft versions are allocated a support channel based on popularity, mod support, etc:
|
||||||
|
|
||||||
|
|||||||
10
build.gradle
10
build.gradle
@@ -3,7 +3,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.gradleup.shadow' version '8.3.6'
|
id 'com.gradleup.shadow' version '8.3.6'
|
||||||
id 'org.cadixdev.licenser' version '0.6.1' apply false
|
id 'org.cadixdev.licenser' version '0.6.1' apply false
|
||||||
id 'dev.architectury.loom' version '1.9-SNAPSHOT' apply false
|
id 'fabric-loom' version "$fabric_loom_version" apply false
|
||||||
id 'gg.essential.multi-version.root' apply false
|
id 'gg.essential.multi-version.root' apply false
|
||||||
id 'org.ajoberstar.grgit' version '5.3.0'
|
id 'org.ajoberstar.grgit' version '5.3.0'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
@@ -89,9 +89,9 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(platform("org.junit:junit-bom:5.12.2"))
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.4'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.4'
|
||||||
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ subprojects {
|
|||||||
version += "+mc.${project.name}"
|
version += "+mc.${project.name}"
|
||||||
|
|
||||||
if (project.parent?.name?.equals('fabric')) {
|
if (project.parent?.name?.equals('fabric')) {
|
||||||
apply plugin: 'dev.architectury.loom'
|
apply plugin: 'fabric-loom'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
minecraft_version_numeric=12105
|
|
||||||
minecraft_api_version=1.21
|
|
||||||
paper_api_version=1.21.5-R0.1-SNAPSHOT
|
|
||||||
@@ -8,32 +8,32 @@ plugins {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':common')
|
implementation project(path: ':common')
|
||||||
|
|
||||||
implementation 'net.william278.uniform:uniform-bukkit:1.3.3'
|
implementation 'net.william278.uniform:uniform-bukkit:1.3.1'
|
||||||
implementation 'net.william278.uniform:uniform-paper:1.3.4'
|
implementation 'net.william278.uniform:uniform-paper:1.3.1'
|
||||||
implementation 'net.william278.toilet:toilet-bukkit:1.0.13'
|
implementation 'net.william278.toilet:toilet-bukkit:1.0.12'
|
||||||
implementation 'net.william278:mpdbdataconverter:1.0.1'
|
implementation 'net.william278:mpdbdataconverter:1.0.1'
|
||||||
implementation 'net.william278:hsldataconverter:1.0'
|
implementation 'net.william278:hsldataconverter:1.0'
|
||||||
implementation 'net.william278:mapdataapi:2.0'
|
implementation 'net.william278:mapdataapi:2.0'
|
||||||
implementation 'org.bstats:bstats-bukkit:3.1.0'
|
implementation 'org.bstats:bstats-bukkit:3.1.0'
|
||||||
implementation 'net.kyori:adventure-platform-bukkit:4.4.0'
|
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
||||||
implementation 'dev.triumphteam:triumph-gui:3.1.12'
|
implementation 'dev.triumphteam:triumph-gui:3.1.11'
|
||||||
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
||||||
implementation 'de.tr7zw:item-nbt-api:2.15.0'
|
implementation 'de.tr7zw:item-nbt-api:2.14.2-SNAPSHOT'
|
||||||
|
|
||||||
compileOnly "io.papermc.paper:paper-api:${paper_api_version}"
|
compileOnly "io.papermc.paper:paper-api:${paper_api_version}"
|
||||||
compileOnly 'com.github.retrooper:packetevents-spigot:2.7.0'
|
compileOnly 'com.github.retrooper:packetevents-spigot:2.7.0'
|
||||||
compileOnly 'com.github.dmulloy2:ProtocolLib:5.3.0'
|
compileOnly 'com.github.dmulloy2:ProtocolLib:5.3.0'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.38'
|
compileOnly 'org.projectlombok:lombok:1.18.36'
|
||||||
compileOnly 'commons-io:commons-io:2.19.0'
|
compileOnly 'commons-io:commons-io:2.18.0'
|
||||||
compileOnly 'org.json:json:20250107'
|
compileOnly 'org.json:json:20250107'
|
||||||
compileOnly 'net.william278:minedown:1.8.2'
|
compileOnly 'net.william278:minedown:1.8.2'
|
||||||
compileOnly 'de.exlll:configlib-yaml:4.6.1'
|
compileOnly 'de.exlll:configlib-yaml:4.5.0'
|
||||||
compileOnly 'com.zaxxer:HikariCP:6.3.0'
|
compileOnly 'com.zaxxer:HikariCP:6.2.1'
|
||||||
compileOnly 'net.william278:DesertWell:2.0.4'
|
compileOnly 'net.william278:DesertWell:2.0.4'
|
||||||
compileOnly 'net.william278:AdvancementAPI:97a9583413'
|
compileOnly 'net.william278:AdvancementAPI:97a9583413'
|
||||||
compileOnly "redis.clients:jedis:$jedis_version"
|
compileOnly "redis.clients:jedis:$jedis_version"
|
||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
@@ -93,9 +93,5 @@ shadowJar {
|
|||||||
tasks {
|
tasks {
|
||||||
runServer {
|
runServer {
|
||||||
minecraftVersion(project.name)
|
minecraftVersion(project.name)
|
||||||
|
|
||||||
downloadPlugins {
|
|
||||||
github("plan-player-analytics", "Plan", "5.6.2965", "Plan-5.6-build-2965.jar")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,6 @@ import com.google.common.collect.Lists;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import de.tr7zw.changeme.nbtapi.NBT;
|
|
||||||
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
|
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -93,12 +92,13 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
private static final int METRICS_ID = 13140;
|
private static final int METRICS_ID = 13140;
|
||||||
private static final String PLATFORM_TYPE_ID = "bukkit";
|
private static final String PLATFORM_TYPE_ID = "bukkit";
|
||||||
|
|
||||||
private final HashMap<Identifier, Serializer<? extends Data>> serializers = Maps.newHashMap();
|
private final TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
||||||
|
SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR
|
||||||
|
);
|
||||||
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
||||||
private final Map<Integer, MapView> mapViews = Maps.newConcurrentMap();
|
private final Map<Integer, MapView> mapViews = Maps.newConcurrentMap();
|
||||||
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
||||||
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
||||||
private final Set<UUID> disconnectingPlayers = Sets.newConcurrentHashSet();
|
|
||||||
|
|
||||||
private boolean disabling;
|
private boolean disabling;
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
@@ -148,12 +148,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
// Check compatibility
|
// Check compatibility
|
||||||
checkCompatibility();
|
checkCompatibility();
|
||||||
|
|
||||||
// Preload NBT-API
|
|
||||||
if (!NBT.preloadApi()) {
|
|
||||||
log(Level.SEVERE, "Failed to load NBT API. HuskSync will not be initialized!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register commands
|
// Register commands
|
||||||
initialize("commands", (plugin) -> getUniform().register(PluginCommand.Type.create(this)));
|
initialize("commands", (plugin) -> getUniform().register(PluginCommand.Type.create(this)));
|
||||||
|
|
||||||
@@ -355,8 +349,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
case "1.20.5", "1.20.6" -> DataFixerUtil.VERSION1_20_5;
|
case "1.20.5", "1.20.6" -> DataFixerUtil.VERSION1_20_5;
|
||||||
case "1.21", "1.21.1" -> DataFixerUtil.VERSION1_21;
|
case "1.21", "1.21.1" -> DataFixerUtil.VERSION1_21;
|
||||||
case "1.21.2", "1.21.3" -> DataFixerUtil.VERSION1_21_2;
|
case "1.21.2", "1.21.3" -> DataFixerUtil.VERSION1_21_2;
|
||||||
case "1.21.4" -> 4189;
|
case "1.21.4" -> 4189/*DataFixerUtil.VERSION1_21_4*/;
|
||||||
case "1.21.5" -> DataFixerUtil.VERSION1_21_5;
|
|
||||||
default -> DataFixerUtil.getCurrentVersion();
|
default -> DataFixerUtil.getCurrentVersion();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,6 +375,9 @@ public abstract class BukkitData implements Data {
|
|||||||
|
|
||||||
// Performs a consuming function for every advancement registered on the server
|
// Performs a consuming function for every advancement registered on the server
|
||||||
private static void forEachAdvancement(@NotNull ThrowingConsumer<org.bukkit.advancement.Advancement> consumer) {
|
private static void forEachAdvancement(@NotNull ThrowingConsumer<org.bukkit.advancement.Advancement> consumer) {
|
||||||
|
final StringJoiner joiner = new StringJoiner(", ");
|
||||||
|
Bukkit.getServer().advancementIterator().forEachRemaining(a -> joiner.add(a.toString()));
|
||||||
|
Bukkit.getLogger().log(Level.INFO, "Advancements: %s".formatted(joiner.toString()));
|
||||||
Bukkit.getServer().advancementIterator().forEachRemaining(consumer);
|
Bukkit.getServer().advancementIterator().forEachRemaining(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,7 @@ public interface BukkitUserDataHolder extends UserDataHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
||||||
if (id.isCustom()) {
|
if (!id.isCustom()) {
|
||||||
return Optional.ofNullable(getCustomDataStore().get(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return switch (id.getKeyValue()) {
|
return switch (id.getKeyValue()) {
|
||||||
case "inventory" -> getInventory();
|
case "inventory" -> getInventory();
|
||||||
case "ender_chest" -> getEnderChest();
|
case "ender_chest" -> getEnderChest();
|
||||||
@@ -52,10 +48,8 @@ public interface BukkitUserDataHolder extends UserDataHolder {
|
|||||||
case "persistent_data" -> getPersistentData();
|
case "persistent_data" -> getPersistentData();
|
||||||
default -> throw new IllegalStateException(String.format("Unexpected data type: %s", id));
|
default -> throw new IllegalStateException(String.format("Unexpected data type: %s", id));
|
||||||
};
|
};
|
||||||
} catch (Throwable e) {
|
|
||||||
getPlugin().debug("Failed to get data for key: " + id.asMinimalString(), e);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
return Optional.ofNullable(getCustomDataStore().get(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -123,6 +124,7 @@ public class BukkitLockedEventListener implements LockedHandler, Listener {
|
|||||||
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
||||||
if (cancelPlayerEvent(uuid)) {
|
if (cancelPlayerEvent(uuid)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
plugin.debug("Cancelled event " + event.getClass().getSimpleName() + " from " + Objects.requireNonNull(plugin.getServer().getPlayer(uuid)).getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public class PaperEventListener extends BukkitEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("RedundantMethodOverride")
|
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
getPlugin().getServer().getPluginManager().registerEvents(this, getPlugin());
|
getPlugin().getServer().getPluginManager().registerEvents(this, getPlugin());
|
||||||
lockedHandler.onEnable();
|
lockedHandler.onEnable();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ package net.william278.husksync.maps;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import de.tr7zw.changeme.nbtapi.NBT;
|
import de.tr7zw.changeme.nbtapi.NBT;
|
||||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||||
import de.tr7zw.changeme.nbtapi.iface.ReadableItemNBT;
|
|
||||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||||
import net.william278.husksync.BukkitHuskSync;
|
import net.william278.husksync.BukkitHuskSync;
|
||||||
import net.william278.husksync.redis.RedisManager;
|
import net.william278.husksync.redis.RedisManager;
|
||||||
@@ -44,9 +43,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -56,8 +53,6 @@ public interface BukkitMapHandler {
|
|||||||
|
|
||||||
// The map used to store HuskSync data in ItemStack NBT
|
// The map used to store HuskSync data in ItemStack NBT
|
||||||
String MAP_DATA_KEY = "husksync:persisted_locked_map";
|
String MAP_DATA_KEY = "husksync:persisted_locked_map";
|
||||||
// The legacy map key used to store pixel data (3.7.3 and below)
|
|
||||||
String MAP_LEGACY_PIXEL_DATA_KEY = "husksync:canvas_data";
|
|
||||||
// Name of server the map originates from
|
// Name of server the map originates from
|
||||||
String MAP_ORIGIN_KEY = "origin";
|
String MAP_ORIGIN_KEY = "origin";
|
||||||
// Original map id
|
// Original map id
|
||||||
@@ -102,12 +97,11 @@ public interface BukkitMapHandler {
|
|||||||
}
|
}
|
||||||
if (item.getType() == Material.FILLED_MAP && item.hasItemMeta()) {
|
if (item.getType() == Material.FILLED_MAP && item.hasItemMeta()) {
|
||||||
items[i] = function.apply(item);
|
items[i] = function.apply(item);
|
||||||
} else if (item.getItemMeta() instanceof BlockStateMeta b && b.getBlockState() instanceof Container box
|
} else if (item.getItemMeta() instanceof BlockStateMeta b && b.getBlockState() instanceof Container box) {
|
||||||
&& !box.getInventory().isEmpty()) {
|
|
||||||
forEachMap(box.getInventory().getContents(), function);
|
forEachMap(box.getInventory().getContents(), function);
|
||||||
b.setBlockState(box);
|
b.setBlockState(box);
|
||||||
item.setItemMeta(b);
|
item.setItemMeta(b);
|
||||||
} else if (item.getItemMeta() instanceof BundleMeta bundle && bundle.hasItems()) {
|
} else if (item.getItemMeta() instanceof BundleMeta bundle) {
|
||||||
bundle.setItems(List.of(forEachMap(bundle.getItems().toArray(ItemStack[]::new), function)));
|
bundle.setItems(List.of(forEachMap(bundle.getItems().toArray(ItemStack[]::new), function)));
|
||||||
item.setItemMeta(bundle);
|
item.setItemMeta(bundle);
|
||||||
}
|
}
|
||||||
@@ -266,29 +260,21 @@ public interface BukkitMapHandler {
|
|||||||
int newId = currentServerName.equals(originServerName)
|
int newId = currentServerName.equals(originServerName)
|
||||||
? originalMapId : getBoundMapId(originServerName, originalMapId, currentServerName);
|
? originalMapId : getBoundMapId(originServerName, originalMapId, currentServerName);
|
||||||
if (newId != -1) {
|
if (newId != -1) {
|
||||||
final MapView view = Bukkit.getMap(newId);
|
meta.setMapId(newId);
|
||||||
if (view != null) {
|
map.setItemMeta(meta);
|
||||||
meta.setMapView(view);
|
getPlugin().debug(String.format("Map ID set to %s", newId));
|
||||||
meta.setMapId(newId);
|
return;
|
||||||
map.setItemMeta(meta);
|
|
||||||
getPlugin().debug(String.format("Map ID set to #%s", newId));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getPlugin().debug(String.format("Map ID #%s not saved on this server, creating...", newId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the pixel data from the ItemStack and generate a map view otherwise
|
// Read the pixel data and generate a map view otherwise
|
||||||
getPlugin().debug("Deserializing map data from NBT and generating view...");
|
getPlugin().debug("Deserializing map data from NBT and generating view...");
|
||||||
@Nullable Map.Entry<MapData, Boolean> readMapData = readMapData(originServerName, originalMapId);
|
final @Nullable Map.Entry<MapData, Boolean> readMapData = readMapData(originServerName, originalMapId);
|
||||||
if (readMapData == null && nbt.hasTag(MAP_LEGACY_PIXEL_DATA_KEY)) {
|
|
||||||
readMapData = readLegacyMapItemData(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If map data was found, add a renderer to the MapView
|
|
||||||
if (readMapData == null) {
|
if (readMapData == null) {
|
||||||
getPlugin().debug("Read pixel data was not found in database, skipping...");
|
getPlugin().debug("Read pixel data was not found in database, skipping...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a renderer to the map with the data and save to file
|
||||||
final MapData canvasData = Objects.requireNonNull(readMapData, "Pixel data null!").getKey();
|
final MapData canvasData = Objects.requireNonNull(readMapData, "Pixel data null!").getKey();
|
||||||
final MapView view = generateRenderedMap(canvasData);
|
final MapView view = generateRenderedMap(canvasData);
|
||||||
meta.setMapView(view);
|
meta.setMapView(view);
|
||||||
@@ -298,7 +284,6 @@ public interface BukkitMapHandler {
|
|||||||
final int id = view.getId();
|
final int id = view.getId();
|
||||||
getRedisManager().bindMapIds(originServerName, originalMapId, currentServerName, id);
|
getRedisManager().bindMapIds(originServerName, originalMapId, currentServerName, id);
|
||||||
getPlugin().getDatabase().setMapBinding(originServerName, originalMapId, currentServerName, id);
|
getPlugin().getDatabase().setMapBinding(originServerName, originalMapId, currentServerName, id);
|
||||||
meta.setMapId(id);
|
|
||||||
|
|
||||||
getPlugin().debug(String.format("Bound map to view (#%s) on server %s", id, currentServerName));
|
getPlugin().debug(String.format("Bound map to view (#%s) on server %s", id, currentServerName));
|
||||||
});
|
});
|
||||||
@@ -310,21 +295,16 @@ public interface BukkitMapHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read map data, or
|
@Nullable final Map.Entry<MapData, Boolean> data = readMapData(getPlugin().getServerName(), view.getId());
|
||||||
@Nullable Map.Entry<MapData, Boolean> data = readMapData(getPlugin().getServerName(), view.getId());
|
|
||||||
if (data == null) {
|
|
||||||
data = readLegacyMapFileData(view.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't render maps with no data
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
final World world = view.getWorld() == null ? getDefaultMapWorld() : view.getWorld();
|
final World world = view.getWorld() == null ? getDefaultMapWorld() : view.getWorld();
|
||||||
getPlugin().debug("Not rendering map: no data in DB for world %s, map #%s."
|
getPlugin().debug("Not rendering map: no data in DB for world %s, map #%s."
|
||||||
.formatted(world.getName(), view.getId()));
|
.formatted(world.getName(), view.getId()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't render persisted maps on this server
|
|
||||||
if (data.getValue()) {
|
if (data.getValue()) {
|
||||||
|
// from this server, doesn't need tweaking
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,36 +420,6 @@ public interface BukkitMapHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy - read maps from item stacks
|
|
||||||
@Nullable
|
|
||||||
@Blocking
|
|
||||||
private Map.Entry<MapData, Boolean> readLegacyMapItemData(@NotNull ReadableItemNBT nbt) {
|
|
||||||
final int dataVer = getPlugin().getDataVersion(getPlugin().getMinecraftVersion());
|
|
||||||
try {
|
|
||||||
return new AbstractMap.SimpleImmutableEntry<>(MapData.fromByteArray(dataVer,
|
|
||||||
Objects.requireNonNull(nbt.getByteArray(MAP_LEGACY_PIXEL_DATA_KEY))), false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
getPlugin().log(Level.WARNING, "Failed to read legacy map data", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy - read maps from files
|
|
||||||
@Nullable
|
|
||||||
private Map.Entry<MapData, Boolean> readLegacyMapFileData(int mapId) {
|
|
||||||
final Path path = getPlugin().getDataFolder().toPath().resolve("maps").resolve(mapId + ".dat");
|
|
||||||
final File file = path.toFile();
|
|
||||||
if (!file.exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new AbstractMap.SimpleImmutableEntry<>(MapData.fromNbt(file), false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
getPlugin().log(Level.WARNING, "Failed to read legacy map file", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MapCanvas} implementation used for pre-rendering maps to be converted into {@link MapData}
|
* A {@link MapCanvas} implementation used for pre-rendering maps to be converted into {@link MapData}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -57,9 +57,8 @@ public class BukkitUser extends OnlineUser implements BukkitUserDataHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDisconnected() {
|
public boolean isOffline() {
|
||||||
return getPlugin().getDisconnectingPlayers().contains(getUuid())
|
return player == null || !player.isOnline();
|
||||||
|| player == null || !player.isOnline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,31 +3,31 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'commons-io:commons-io:2.19.0'
|
api 'commons-io:commons-io:2.18.0'
|
||||||
api 'org.apache.commons:commons-text:1.13.1'
|
api 'org.apache.commons:commons-text:1.13.0'
|
||||||
api 'net.william278:minedown:1.8.2'
|
api 'net.william278:minedown:1.8.2'
|
||||||
api 'net.william278:mapdataapi:2.0'
|
api 'net.william278:mapdataapi:2.0'
|
||||||
api 'org.json:json:20250107'
|
api 'org.json:json:20250107'
|
||||||
api 'com.google.code.gson:gson:2.13.0'
|
api 'com.google.code.gson:gson:2.12.1'
|
||||||
api 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2'
|
api 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2'
|
||||||
api 'de.exlll:configlib-yaml:4.6.1'
|
api 'de.exlll:configlib-yaml:4.5.0'
|
||||||
api 'net.william278:paginedown:1.1.2'
|
api 'net.william278:paginedown:1.1.2'
|
||||||
api 'net.william278:DesertWell:2.0.4'
|
api 'net.william278:DesertWell:2.0.4'
|
||||||
api('com.zaxxer:HikariCP:6.3.0') {
|
api('com.zaxxer:HikariCP:6.2.1') {
|
||||||
exclude module: 'slf4j-api'
|
exclude module: 'slf4j-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOnlyApi 'net.william278.toilet:toilet-common:1.0.13'
|
compileOnlyApi 'net.william278.toilet:toilet-common:1.0.12'
|
||||||
|
|
||||||
compileOnly 'net.william278.uniform:uniform-common:1.3.3'
|
compileOnly 'net.william278.uniform:uniform-common:1.3.1'
|
||||||
compileOnly 'com.mojang:brigadier:1.1.8'
|
compileOnly 'com.mojang:brigadier:1.1.8'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.38'
|
compileOnly 'org.projectlombok:lombok:1.18.36'
|
||||||
compileOnly 'org.jetbrains:annotations:26.0.2'
|
compileOnly 'org.jetbrains:annotations:26.0.2'
|
||||||
compileOnly 'net.kyori:adventure-api:4.20.0'
|
compileOnly 'net.kyori:adventure-api:4.19.0'
|
||||||
compileOnly 'net.kyori:adventure-platform-api:4.4.0'
|
compileOnly 'net.kyori:adventure-platform-api:4.3.4'
|
||||||
compileOnly "net.kyori:adventure-text-serializer-plain:4.21.0"
|
compileOnly "net.kyori:adventure-text-serializer-plain:4.19.0"
|
||||||
compileOnly 'com.google.guava:guava:33.4.8-jre'
|
compileOnly 'com.google.guava:guava:33.4.0-jre'
|
||||||
compileOnly 'com.github.plan-player-analytics:Plan:5.6.2965'
|
compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272'
|
||||||
compileOnly "redis.clients:jedis:$jedis_version"
|
compileOnly "redis.clients:jedis:$jedis_version"
|
||||||
compileOnly "com.mysql:mysql-connector-j:$mysql_driver_version"
|
compileOnly "com.mysql:mysql-connector-j:$mysql_driver_version"
|
||||||
compileOnly "org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version"
|
compileOnly "org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version"
|
||||||
@@ -37,10 +37,10 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation "redis.clients:jedis:$jedis_version"
|
testImplementation "redis.clients:jedis:$jedis_version"
|
||||||
testImplementation "org.xerial.snappy:snappy-java:$snappy_version"
|
testImplementation "org.xerial.snappy:snappy-java:$snappy_version"
|
||||||
testImplementation 'com.google.guava:guava:33.4.8-jre'
|
testImplementation 'com.google.guava:guava:33.4.0-jre'
|
||||||
testImplementation 'com.github.plan-player-analytics:Plan:5.6.2965'
|
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
|
||||||
testCompileOnly 'de.exlll:configlib-yaml:4.6.1'
|
testCompileOnly 'de.exlll:configlib-yaml:4.5.0'
|
||||||
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.william278.husksync;
|
package net.william278.husksync;
|
||||||
|
|
||||||
import com.fatboyindustrial.gsonjavatime.Converters;
|
import com.fatboyindustrial.gsonjavatime.Converters;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import net.kyori.adventure.audience.Audience;
|
||||||
@@ -141,7 +140,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
if (getPlayerCustomDataStore().containsKey(user.getUuid())) {
|
if (getPlayerCustomDataStore().containsKey(user.getUuid())) {
|
||||||
return getPlayerCustomDataStore().get(user.getUuid());
|
return getPlayerCustomDataStore().get(user.getUuid());
|
||||||
}
|
}
|
||||||
final Map<Identifier, Data> data = Maps.newHashMap();
|
final Map<Identifier, Data> data = new HashMap<>();
|
||||||
getPlayerCustomDataStore().put(user.getUuid(), data);
|
getPlayerCustomDataStore().put(user.getUuid(), data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -316,12 +315,6 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
@NotNull
|
@NotNull
|
||||||
Set<UUID> getLockedPlayers();
|
Set<UUID> getLockedPlayers();
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the set of UUIDs of players who are currently marked as disconnecting or disconnected
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
Set<UUID> getDisconnectingPlayers();
|
|
||||||
|
|
||||||
default boolean isLocked(@NotNull UUID uuid) {
|
default boolean isLocked(@NotNull UUID uuid) {
|
||||||
return getLockedPlayers().contains(uuid);
|
return getLockedPlayers().contains(uuid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class InventoryCommand extends ItemsCommand {
|
|||||||
@NotNull User user, boolean allowEdit) {
|
@NotNull User user, boolean allowEdit) {
|
||||||
final Optional<Data.Items.Inventory> optionalInventory = snapshot.getInventory();
|
final Optional<Data.Items.Inventory> optionalInventory = snapshot.getInventory();
|
||||||
if (optionalInventory.isEmpty()) {
|
if (optionalInventory.isEmpty()) {
|
||||||
|
viewer.sendMessage(new MineDown("what the FUCK is happening"));
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -215,17 +215,11 @@ public class UserDataCommand extends PluginCommand {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private CommandProvider view() {
|
private CommandProvider view() {
|
||||||
return (sub) -> {
|
return (sub) -> sub.addSyntax((ctx) -> {
|
||||||
sub.addSyntax((ctx) -> {
|
final User user = ctx.getArgument("username", User.class);
|
||||||
final User user = ctx.getArgument("username", User.class);
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
final UUID version = ctx.getArgument("version", UUID.class);
|
viewSnapshot(user(sub, ctx), user, version);
|
||||||
viewSnapshot(user(sub, ctx), user, version);
|
}, user("username"), versionUuid());
|
||||||
}, user("username"), versionUuid());
|
|
||||||
sub.addSyntax((ctx) -> {
|
|
||||||
final User user = ctx.getArgument("username", User.class);
|
|
||||||
viewLatestSnapshot(user(sub, ctx), user);
|
|
||||||
}, user("username"));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ public interface DataHolder {
|
|||||||
Map<Identifier, Data> getData();
|
Map<Identifier, Data> getData();
|
||||||
|
|
||||||
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
||||||
if (getData().containsKey(id)) {
|
return getData().entrySet().stream().filter(e -> e.getKey().equals(id)).map(Map.Entry::getValue).findFirst();
|
||||||
return Optional.of(getData().get(id));
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default void setData(@NotNull Identifier identifier, @NotNull Data data) {
|
default void setData(@NotNull Identifier identifier, @NotNull Data data) {
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ public class DataSnapshot {
|
|||||||
public static class Unpacked extends DataSnapshot implements DataHolder {
|
public static class Unpacked extends DataSnapshot implements DataHolder {
|
||||||
|
|
||||||
@Expose(serialize = false, deserialize = false)
|
@Expose(serialize = false, deserialize = false)
|
||||||
private final Map<Identifier, Data> deserialized;
|
private final TreeMap<Identifier, Data> deserialized;
|
||||||
|
|
||||||
private Unpacked(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp,
|
private Unpacked(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp,
|
||||||
@NotNull String saveCause, @NotNull String serverName, @NotNull Map<String, String> data,
|
@NotNull String saveCause, @NotNull String serverName, @NotNull Map<String, String> data,
|
||||||
@@ -381,7 +381,7 @@ public class DataSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Unpacked(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp,
|
private Unpacked(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp,
|
||||||
@NotNull String saveCause, @NotNull String serverName, @NotNull Map<Identifier, Data> data,
|
@NotNull String saveCause, @NotNull String serverName, @NotNull TreeMap<Identifier, Data> data,
|
||||||
@NotNull Version minecraftVersion, @NotNull String platformType, int formatVersion) {
|
@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, Map.of(), minecraftVersion, platformType, formatVersion);
|
||||||
this.deserialized = data;
|
this.deserialized = data;
|
||||||
@@ -389,15 +389,14 @@ public class DataSnapshot {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
private Map<Identifier, Data> deserializeData(@NotNull HuskSync plugin) {
|
private TreeMap<Identifier, Data> deserializeData(@NotNull HuskSync plugin) {
|
||||||
return data.entrySet().stream()
|
return data.entrySet().stream()
|
||||||
.filter(e -> plugin.getIdentifier(e.getKey()).isPresent())
|
.filter(e -> plugin.getIdentifier(e.getKey()).isPresent())
|
||||||
.map(entry -> Map.entry(plugin.getIdentifier(entry.getKey()).orElseThrow(), entry.getValue()))
|
.map(entry -> Map.entry(plugin.getIdentifier(entry.getKey()).orElseThrow(), entry.getValue()))
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
Map.Entry::getKey,
|
Map.Entry::getKey,
|
||||||
entry -> plugin.deserializeData(entry.getKey(), entry.getValue(), getMinecraftVersion()),
|
entry -> plugin.deserializeData(entry.getKey(), entry.getValue(), getMinecraftVersion()),
|
||||||
(a, b) -> a,
|
(a, b) -> b, () -> Maps.newTreeMap(SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR)
|
||||||
HashMap::new
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,9 +406,7 @@ public class DataSnapshot {
|
|||||||
return deserialized.entrySet().stream()
|
return deserialized.entrySet().stream()
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
entry -> entry.getKey().toString(),
|
entry -> entry.getKey().toString(),
|
||||||
entry -> plugin.serializeData(entry.getKey(), entry.getValue()),
|
entry -> plugin.serializeData(entry.getKey(), entry.getValue())
|
||||||
(a, b) -> a,
|
|
||||||
HashMap::new
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,20 +421,6 @@ public class DataSnapshot {
|
|||||||
return deserialized;
|
return deserialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sorted iterable of the snapshots the snapshot is holding
|
|
||||||
*
|
|
||||||
* @return The data map
|
|
||||||
* @since 3.8.2
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
@ApiStatus.Internal
|
|
||||||
public Iterable<Map.Entry<Identifier, Data>> getSortedIterable() {
|
|
||||||
final TreeMap<Identifier, Data> tree = Maps.newTreeMap(SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR);
|
|
||||||
tree.putAll(deserialized);
|
|
||||||
return tree.entrySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack the {@link DataSnapshot} into a {@link DataSnapshot.Packed packed} snapshot
|
* Pack the {@link DataSnapshot} into a {@link DataSnapshot.Packed packed} snapshot
|
||||||
*
|
*
|
||||||
@@ -470,12 +453,12 @@ public class DataSnapshot {
|
|||||||
private String serverName;
|
private String serverName;
|
||||||
private boolean pinned;
|
private boolean pinned;
|
||||||
private OffsetDateTime timestamp;
|
private OffsetDateTime timestamp;
|
||||||
private final Map<Identifier, Data> data;
|
private final TreeMap<Identifier, Data> data;
|
||||||
|
|
||||||
private Builder(@NotNull HuskSync plugin) {
|
private Builder(@NotNull HuskSync plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.pinned = false;
|
this.pinned = false;
|
||||||
this.data = Maps.newHashMap();
|
this.data = Maps.newTreeMap(SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR);
|
||||||
this.timestamp = OffsetDateTime.now();
|
this.timestamp = OffsetDateTime.now();
|
||||||
this.id = UUID.randomUUID();
|
this.id = UUID.randomUUID();
|
||||||
this.serverName = plugin.getServerName();
|
this.serverName = plugin.getServerName();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ package net.william278.husksync.data;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.kyori.adventure.key.InvalidKeyException;
|
import net.kyori.adventure.key.InvalidKeyException;
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import net.kyori.adventure.key.KeyPattern;
|
|
||||||
import org.intellij.lang.annotations.Subst;
|
import org.intellij.lang.annotations.Subst;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -38,10 +37,7 @@ import java.util.stream.Stream;
|
|||||||
* Identifiers of different types of {@link Data}s
|
* Identifiers of different types of {@link Data}s
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class Identifier implements Comparable<Identifier> {
|
public class Identifier {
|
||||||
|
|
||||||
// Namespace for built-in identifiers
|
|
||||||
private static final @KeyPattern String DEFAULT_NAMESPACE = "husksync";
|
|
||||||
|
|
||||||
// Built-in identifiers
|
// Built-in identifiers
|
||||||
public static final Identifier PERSISTENT_DATA = huskSync("persistent_data", true);
|
public static final Identifier PERSISTENT_DATA = huskSync("persistent_data", true);
|
||||||
@@ -97,8 +93,8 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Identifier from(@NotNull Key key, @NotNull Set<Dependency> dependencies) {
|
public static Identifier from(@NotNull Key key, @NotNull Set<Dependency> dependencies) {
|
||||||
if (key.namespace().equals(DEFAULT_NAMESPACE)) {
|
if (key.namespace().equals("husksync")) {
|
||||||
throw new IllegalArgumentException("Cannot register with %s as key namespace!".formatted(key.namespace()));
|
throw new IllegalArgumentException("You cannot register a key with \"husksync\" as the namespace!");
|
||||||
}
|
}
|
||||||
return new Identifier(key, true, dependencies);
|
return new Identifier(key, true, dependencies);
|
||||||
}
|
}
|
||||||
@@ -147,7 +143,7 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static Identifier huskSync(@Subst("null") @NotNull String name,
|
private static Identifier huskSync(@Subst("null") @NotNull String name,
|
||||||
boolean configDefault) throws InvalidKeyException {
|
boolean configDefault) throws InvalidKeyException {
|
||||||
return new Identifier(Key.key(DEFAULT_NAMESPACE, name), configDefault, Collections.emptySet());
|
return new Identifier(Key.key("husksync", name), configDefault, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an identifier with a HuskSync namespace
|
// Return an identifier with a HuskSync namespace
|
||||||
@@ -155,7 +151,7 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
private static Identifier huskSync(@Subst("null") @NotNull String name,
|
private static Identifier huskSync(@Subst("null") @NotNull String name,
|
||||||
@SuppressWarnings("SameParameterValue") boolean configDefault,
|
@SuppressWarnings("SameParameterValue") boolean configDefault,
|
||||||
@NotNull Dependency... dependents) throws InvalidKeyException {
|
@NotNull Dependency... dependents) throws InvalidKeyException {
|
||||||
return new Identifier(Key.key(DEFAULT_NAMESPACE, name), configDefault, Set.of(dependents));
|
return new Identifier(Key.key("husksync", name), configDefault, Set.of(dependents));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,30 +209,13 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
* @return {@code false} if {@link #getKeyNamespace()} returns "husksync"; {@code true} otherwise
|
* @return {@code false} if {@link #getKeyNamespace()} returns "husksync"; {@code true} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isCustom() {
|
public boolean isCustom() {
|
||||||
return !getKeyNamespace().equals(DEFAULT_NAMESPACE);
|
return !getKeyNamespace().equals("husksync");
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the minimal string representation of this key.
|
|
||||||
* <p>
|
|
||||||
* If the namespace of the key is {@link #DEFAULT_NAMESPACE}, only the key value will be returned.
|
|
||||||
*
|
|
||||||
* @return the minimal string key representation
|
|
||||||
* @since 3.8
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public String asMinimalString() {
|
|
||||||
if (getKey().namespace().equals(DEFAULT_NAMESPACE)) {
|
|
||||||
return getKey().value();
|
|
||||||
}
|
|
||||||
return getKey().asString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the identifier as a string (the key)
|
* Returns the identifier as a string (the key)
|
||||||
*
|
*
|
||||||
* @return the identifier as a string
|
* @return the identifier as a string
|
||||||
* @since 3.0
|
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
@@ -245,29 +224,19 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this Identifier is equal to another Identifier
|
* Returns {@code true} if the given object is an identifier with the same key as this identifier
|
||||||
*
|
*
|
||||||
* @param obj another object
|
* @param obj the object to compare
|
||||||
* @return {@code true} if this identifier matches the identifier of {@code obj}
|
* @return {@code true} if the given object is an identifier with the same key as this identifier
|
||||||
* @since 3.8
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (obj instanceof Identifier other) {
|
return obj instanceof Identifier other ? toString().equals(other.toString()) : super.equals(obj);
|
||||||
return asMinimalString().equals(other.asMinimalString());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the hash code of the Identifier (equivalent to {@link #asMinimalString()}->{@code #hashCode()}
|
|
||||||
*
|
|
||||||
* @return the hash code
|
|
||||||
* @since 3.8
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return asMinimalString().hashCode();
|
return key.toString().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the config entry for the identifier
|
// Get the config entry for the identifier
|
||||||
@@ -276,14 +245,6 @@ public class Identifier implements Comparable<Identifier> {
|
|||||||
return Map.entry(getKeyValue(), enabledByDefault);
|
return Map.entry(getKeyValue(), enabledByDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comparable; always sort this Identifier after any dependencies
|
|
||||||
@Override
|
|
||||||
public int compareTo(@NotNull Identifier o) {
|
|
||||||
if (this.dependsOn(o)) return 1;
|
|
||||||
if (o.dependsOn(this)) return -1;
|
|
||||||
return this.key.compareTo(o.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two identifiers based on their dependencies.
|
* Compares two identifiers based on their dependencies.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public interface SerializerRegistry {
|
public interface SerializerRegistry {
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ public interface SerializerRegistry {
|
|||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
<T extends Data> Map<Identifier, Serializer<T>> getSerializers();
|
<T extends Data> TreeMap<Identifier, Serializer<T>> getSerializers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a data serializer for the given {@link Identifier}
|
* Register a data serializer for the given {@link Identifier}
|
||||||
@@ -88,7 +87,8 @@ public interface SerializerRegistry {
|
|||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
default Optional<Identifier> getIdentifier(@NotNull String key) {
|
default Optional<Identifier> getIdentifier(@NotNull String key) {
|
||||||
return getSerializers().keySet().stream().filter(e -> e.toString().equals(key)).findFirst();
|
return getSerializers().keySet().stream()
|
||||||
|
.filter(id -> id.getKey().asString().equals(key)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +99,9 @@ public interface SerializerRegistry {
|
|||||||
* @since 3.5.4
|
* @since 3.5.4
|
||||||
*/
|
*/
|
||||||
default Optional<Serializer<Data>> getSerializer(@NotNull Identifier identifier) {
|
default Optional<Serializer<Data>> getSerializer(@NotNull Identifier identifier) {
|
||||||
return Optional.ofNullable(getSerializers().get(identifier));
|
return getSerializers().entrySet().stream()
|
||||||
|
.filter(entry -> entry.getKey().getKey().equals(identifier.getKey()))
|
||||||
|
.map(Map.Entry::getValue).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,14 +153,14 @@ public interface SerializerRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of registered data types, in dependency order
|
* Get the set of registered data types
|
||||||
*
|
*
|
||||||
* @return the list of registered data types
|
* @return the set of registered data types
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
default List<Identifier> getRegisteredDataTypes() {
|
default Set<Identifier> getRegisteredDataTypes() {
|
||||||
return getSerializers().keySet().stream().sorted(DEPENDENCY_ORDER_COMPARATOR).toList();
|
return getSerializers().keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns if a data type is available and enabled in the config
|
// Returns if a data type is available and enabled in the config
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A holder of data in the form of {@link Data}s, which can be synced
|
* A holder of data in the form of {@link Data}s, which can be synced
|
||||||
@@ -47,11 +46,7 @@ public interface UserDataHolder extends DataHolder {
|
|||||||
.filter(Identifier::isEnabled)
|
.filter(Identifier::isEnabled)
|
||||||
.map(id -> Map.entry(id, getData(id)))
|
.map(id -> Map.entry(id, getData(id)))
|
||||||
.filter(data -> data.getValue().isPresent())
|
.filter(data -> data.getValue().isPresent())
|
||||||
.collect(Collectors.toMap(
|
.collect(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll);
|
||||||
Map.Entry::getKey,
|
|
||||||
entry -> entry.getValue().get(),
|
|
||||||
(a, b) -> a, HashMap::new
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,15 +75,6 @@ public interface UserDataHolder extends DataHolder {
|
|||||||
return DataSnapshot.builder(getPlugin()).data(this.getData()).saveCause(saveCause).buildAndPack();
|
return DataSnapshot.builder(getPlugin()).data(this.getData()).saveCause(saveCause).buildAndPack();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether data can be applied to the holder at this time
|
|
||||||
*
|
|
||||||
* @return {@code true} if data can be applied, otherwise false
|
|
||||||
*/
|
|
||||||
default boolean cannotApplySnapshot() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize and apply a data snapshot to this data owner
|
* Deserialize and apply a data snapshot to this data owner
|
||||||
* <p>
|
* <p>
|
||||||
@@ -104,12 +90,9 @@ public interface UserDataHolder extends DataHolder {
|
|||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
default void applySnapshot(@NotNull DataSnapshot.Packed snapshot, @NotNull ThrowingConsumer<Boolean> runAfter) {
|
default void applySnapshot(@NotNull DataSnapshot.Packed snapshot, @NotNull ThrowingConsumer<Boolean> runAfter) {
|
||||||
if (cannotApplySnapshot()) {
|
final HuskSync plugin = getPlugin();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpack the snapshot
|
// Unpack the snapshot
|
||||||
final HuskSync plugin = getPlugin();
|
|
||||||
final DataSnapshot.Unpacked unpacked;
|
final DataSnapshot.Unpacked unpacked;
|
||||||
try {
|
try {
|
||||||
unpacked = snapshot.unpack(plugin);
|
unpacked = snapshot.unpack(plugin);
|
||||||
@@ -121,12 +104,8 @@ public interface UserDataHolder extends DataHolder {
|
|||||||
|
|
||||||
// Synchronously attempt to apply the snapshot
|
// Synchronously attempt to apply the snapshot
|
||||||
plugin.runSync(() -> {
|
plugin.runSync(() -> {
|
||||||
if (cannotApplySnapshot()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (Map.Entry<Identifier, Data> entry : unpacked.getSortedIterable()) {
|
for (Map.Entry<Identifier, Data> entry : unpacked.getData().entrySet()) {
|
||||||
final Identifier identifier = entry.getKey();
|
final Identifier identifier = entry.getKey();
|
||||||
if (!identifier.isEnabled()) {
|
if (!identifier.isEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import net.william278.husksync.HuskSync;
|
|||||||
import net.william278.husksync.config.Settings;
|
import net.william278.husksync.config.Settings;
|
||||||
import net.william278.husksync.data.DataSnapshot;
|
import net.william278.husksync.data.DataSnapshot;
|
||||||
import net.william278.husksync.user.User;
|
import net.william278.husksync.user.User;
|
||||||
import org.intellij.lang.annotations.Language;
|
|
||||||
import org.jetbrains.annotations.Blocking;
|
import org.jetbrains.annotations.Blocking;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -69,7 +68,7 @@ public abstract class Database {
|
|||||||
* @return the formatted statement, with table placeholders replaced with the correct names
|
* @return the formatted statement, with table placeholders replaced with the correct names
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
protected final String formatStatementTables(@NotNull @Language("SQL") String sql) {
|
protected final String formatStatementTables(@NotNull String sql) {
|
||||||
final Settings.DatabaseSettings settings = plugin.getSettings().getDatabase();
|
final Settings.DatabaseSettings settings = plugin.getSettings().getDatabase();
|
||||||
return sql.replaceAll("%users_table%", settings.getTableName(TableName.USERS))
|
return sql.replaceAll("%users_table%", settings.getTableName(TableName.USERS))
|
||||||
.replaceAll("%user_data_table%", settings.getTableName(TableName.USER_DATA))
|
.replaceAll("%user_data_table%", settings.getTableName(TableName.USER_DATA))
|
||||||
@@ -139,15 +138,6 @@ public abstract class Database {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public abstract List<DataSnapshot.Packed> getAllSnapshots(@NotNull User user);
|
public abstract List<DataSnapshot.Packed> getAllSnapshots(@NotNull User user);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of unpinned {@link DataSnapshot}s a user has
|
|
||||||
*
|
|
||||||
* @param user the user to count snapshots for
|
|
||||||
* @return the number of snapshots this user has saved
|
|
||||||
*/
|
|
||||||
@Blocking
|
|
||||||
public abstract int getUnpinnedSnapshotCount(@NotNull User user);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a specific {@link DataSnapshot} entry for a user from the database, by its UUID.
|
* Gets a specific {@link DataSnapshot} entry for a user from the database, by its UUID.
|
||||||
*
|
*
|
||||||
@@ -274,7 +264,7 @@ public abstract class Database {
|
|||||||
*
|
*
|
||||||
* @param serverName Name of the server the map originates from
|
* @param serverName Name of the server the map originates from
|
||||||
* @param mapId Original map ID
|
* @param mapId Original map ID
|
||||||
* @return Map.Entry (key: map data, value: is from current world)
|
* @return Map.Entry (key: map data, value: is from current world)
|
||||||
*/
|
*/
|
||||||
@Blocking
|
@Blocking
|
||||||
public abstract @Nullable Map.Entry<byte[], Boolean> getMapData(@NotNull String serverName, int mapId);
|
public abstract @Nullable Map.Entry<byte[], Boolean> getMapData(@NotNull String serverName, int mapId);
|
||||||
@@ -284,7 +274,7 @@ public abstract class Database {
|
|||||||
*
|
*
|
||||||
* @param serverName Name of the server the map originates from
|
* @param serverName Name of the server the map originates from
|
||||||
* @param mapId Original map ID
|
* @param mapId Original map ID
|
||||||
* @return Map.Entry (key: server name, value: map ID)
|
* @return Map.Entry (key: server name, value: map ID)
|
||||||
*/
|
*/
|
||||||
@Blocking
|
@Blocking
|
||||||
public abstract @Nullable Map.Entry<String, Integer> getMapBinding(@NotNull String serverName, int mapId);
|
public abstract @Nullable Map.Entry<String, Integer> getMapBinding(@NotNull String serverName, int mapId);
|
||||||
@@ -306,7 +296,7 @@ public abstract class Database {
|
|||||||
* @param fromServerName Name of the server the map originates from
|
* @param fromServerName Name of the server the map originates from
|
||||||
* @param fromMapId Original map ID
|
* @param fromMapId Original map ID
|
||||||
* @param toServerName Name of the new server
|
* @param toServerName Name of the new server
|
||||||
* @return New map ID or -1 if not found
|
* @return New map ID or -1 if not found
|
||||||
*/
|
*/
|
||||||
@Blocking
|
@Blocking
|
||||||
public abstract int getBoundMapId(@NotNull String fromServerName, int fromMapId, @NotNull String toServerName);
|
public abstract int getBoundMapId(@NotNull String fromServerName, int fromMapId, @NotNull String toServerName);
|
||||||
|
|||||||
@@ -234,17 +234,6 @@ public class MongoDbDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getUnpinnedSnapshotCount(@NotNull User user) {
|
|
||||||
try {
|
|
||||||
Document filter = new Document("player_uuid", user.getUuid()).append("pinned", false);
|
|
||||||
return (int) mongoCollectionHelper.getCollection(userDataTable).countDocuments(filter);
|
|
||||||
} catch (MongoException e) {
|
|
||||||
plugin.log(Level.SEVERE, "Failed to fetch a user's current snapshot count", e);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Blocking
|
@Blocking
|
||||||
@Override
|
@Override
|
||||||
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
||||||
@@ -270,14 +259,17 @@ public class MongoDbDatabase extends Database {
|
|||||||
@Override
|
@Override
|
||||||
protected void rotateSnapshots(@NotNull User user) {
|
protected void rotateSnapshots(@NotNull User user) {
|
||||||
try {
|
try {
|
||||||
final int unpinnedSnapshots = getUnpinnedSnapshotCount(user);
|
final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream()
|
||||||
|
.filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
|
||||||
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
||||||
if (unpinnedSnapshots > maxSnapshots) {
|
if (unpinnedUserData.size() > maxSnapshots) {
|
||||||
|
|
||||||
Document filter = new Document("player_uuid", user.getUuid()).append("pinned", false);
|
Document filter = new Document("player_uuid", user.getUuid()).append("pinned", false);
|
||||||
Document sort = new Document("timestamp", 1); // 1 = Ascending
|
Document sort = new Document("timestamp", 1); // 1 = Ascending
|
||||||
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable)
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable)
|
||||||
.find(filter).sort(sort)
|
.find(filter)
|
||||||
.limit(unpinnedSnapshots - maxSnapshots);
|
.sort(sort)
|
||||||
|
.limit(unpinnedUserData.size() - maxSnapshots);
|
||||||
|
|
||||||
for (Document doc : iterable) {
|
for (Document doc : iterable) {
|
||||||
mongoCollectionHelper.deleteDocument(userDataTable, doc);
|
mongoCollectionHelper.deleteDocument(userDataTable, doc);
|
||||||
|
|||||||
@@ -299,30 +299,11 @@ public class MySqlDatabase extends Database {
|
|||||||
return retrievedData;
|
return retrievedData;
|
||||||
}
|
}
|
||||||
} catch (SQLException | DataAdapter.AdaptionException e) {
|
} catch (SQLException | DataAdapter.AdaptionException e) {
|
||||||
plugin.log(Level.SEVERE, "Failed to fetch a user's list of snapshots from the database", e);
|
plugin.log(Level.SEVERE, "Failed to fetch a user's current user data from the database", e);
|
||||||
}
|
}
|
||||||
return retrievedData;
|
return retrievedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getUnpinnedSnapshotCount(@NotNull User user) {
|
|
||||||
try (Connection connection = getConnection()) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
|
||||||
SELECT COUNT(`version_uuid`)
|
|
||||||
FROM `%user_data_table%`
|
|
||||||
WHERE `player_uuid`=? AND `pinned`=false;"""))) {
|
|
||||||
statement.setString(1, user.getUuid().toString());
|
|
||||||
final ResultSet resultSet = statement.executeQuery();
|
|
||||||
if (resultSet.next()) {
|
|
||||||
return resultSet.getInt(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.log(Level.SEVERE, "Failed to fetch a user's current snapshot count", e);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Blocking
|
@Blocking
|
||||||
@Override
|
@Override
|
||||||
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
||||||
@@ -355,9 +336,10 @@ public class MySqlDatabase extends Database {
|
|||||||
@Blocking
|
@Blocking
|
||||||
@Override
|
@Override
|
||||||
protected void rotateSnapshots(@NotNull User user) {
|
protected void rotateSnapshots(@NotNull User user) {
|
||||||
final int unpinnedSnapshots = getUnpinnedSnapshotCount(user);
|
final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream()
|
||||||
|
.filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
|
||||||
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
||||||
if (unpinnedSnapshots > maxSnapshots) {
|
if (unpinnedUserData.size() > maxSnapshots) {
|
||||||
try (Connection connection = getConnection()) {
|
try (Connection connection = getConnection()) {
|
||||||
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
||||||
DELETE FROM `%user_data_table%`
|
DELETE FROM `%user_data_table%`
|
||||||
@@ -365,7 +347,7 @@ public class MySqlDatabase extends Database {
|
|||||||
AND `pinned` IS FALSE
|
AND `pinned` IS FALSE
|
||||||
ORDER BY `timestamp` ASC
|
ORDER BY `timestamp` ASC
|
||||||
LIMIT %entry_count%;""".replace("%entry_count%",
|
LIMIT %entry_count%;""".replace("%entry_count%",
|
||||||
Integer.toString(unpinnedSnapshots - maxSnapshots))))) {
|
Integer.toString(unpinnedUserData.size() - maxSnapshots))))) {
|
||||||
statement.setString(1, user.getUuid().toString());
|
statement.setString(1, user.getUuid().toString());
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,30 +288,11 @@ public class PostgresDatabase extends Database {
|
|||||||
return retrievedData;
|
return retrievedData;
|
||||||
}
|
}
|
||||||
} catch (SQLException | DataAdapter.AdaptionException e) {
|
} catch (SQLException | DataAdapter.AdaptionException e) {
|
||||||
plugin.log(Level.SEVERE, "Failed to fetch a user's list of snapshots from the database", e);
|
plugin.log(Level.SEVERE, "Failed to fetch a user's current user data from the database", e);
|
||||||
}
|
}
|
||||||
return retrievedData;
|
return retrievedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getUnpinnedSnapshotCount(@NotNull User user) {
|
|
||||||
try (Connection connection = getConnection()) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
|
||||||
SELECT COUNT(`version_uuid`)
|
|
||||||
FROM `%user_data_table%`
|
|
||||||
WHERE `player_uuid`=? AND `pinned`=false;"""))) {
|
|
||||||
statement.setString(1, user.getUuid().toString());
|
|
||||||
final ResultSet resultSet = statement.executeQuery();
|
|
||||||
if (resultSet.next()) {
|
|
||||||
return resultSet.getInt(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.log(Level.SEVERE, "Failed to fetch a user's current snapshot count", e);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Blocking
|
@Blocking
|
||||||
@Override
|
@Override
|
||||||
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
||||||
@@ -342,9 +323,10 @@ public class PostgresDatabase extends Database {
|
|||||||
@Blocking
|
@Blocking
|
||||||
@Override
|
@Override
|
||||||
protected void rotateSnapshots(@NotNull User user) {
|
protected void rotateSnapshots(@NotNull User user) {
|
||||||
final int unpinnedSnapshots = getUnpinnedSnapshotCount(user);
|
final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream()
|
||||||
|
.filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
|
||||||
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
||||||
if (unpinnedSnapshots > maxSnapshots) {
|
if (unpinnedUserData.size() > maxSnapshots) {
|
||||||
try (Connection connection = getConnection()) {
|
try (Connection connection = getConnection()) {
|
||||||
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
|
||||||
WITH cte AS (
|
WITH cte AS (
|
||||||
@@ -357,7 +339,7 @@ public class PostgresDatabase extends Database {
|
|||||||
)
|
)
|
||||||
DELETE FROM %user_data_table%
|
DELETE FROM %user_data_table%
|
||||||
WHERE version_uuid IN (SELECT version_uuid FROM cte);""".replace("%entry_count%",
|
WHERE version_uuid IN (SELECT version_uuid FROM cte);""".replace("%entry_count%",
|
||||||
Integer.toString(unpinnedSnapshots - maxSnapshots))))) {
|
Integer.toString(unpinnedUserData.size() - maxSnapshots))))) {
|
||||||
statement.setObject(1, user.getUuid());
|
statement.setObject(1, user.getUuid());
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ import net.william278.husksync.data.DataSnapshot;
|
|||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
||||||
|
|
||||||
@@ -47,7 +49,6 @@ public abstract class EventListener {
|
|||||||
* @param user The {@link OnlineUser} to handle
|
* @param user The {@link OnlineUser} to handle
|
||||||
*/
|
*/
|
||||||
protected final void handlePlayerJoin(@NotNull OnlineUser user) {
|
protected final void handlePlayerJoin(@NotNull OnlineUser user) {
|
||||||
plugin.getDisconnectingPlayers().remove(user.getUuid());
|
|
||||||
if (user.isNpc()) {
|
if (user.isNpc()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -61,17 +62,11 @@ public abstract class EventListener {
|
|||||||
* @param user The {@link OnlineUser} to handle
|
* @param user The {@link OnlineUser} to handle
|
||||||
*/
|
*/
|
||||||
protected final void handlePlayerQuit(@NotNull OnlineUser user) {
|
protected final void handlePlayerQuit(@NotNull OnlineUser user) {
|
||||||
// Check the user is a user, the plugin isn't disabling, then mark as disconnecting
|
if (user.isNpc() || plugin.isDisabling() || plugin.isLocked(user.getUuid())) {
|
||||||
if (user.isNpc() || plugin.isDisabling()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.getDisconnectingPlayers().add(user.getUuid());
|
plugin.lockPlayer(user.getUuid());
|
||||||
|
plugin.getDataSyncer().syncSaveUserData(user);
|
||||||
// Lock, then save their data if the user is unlocked
|
|
||||||
if (!plugin.isLocked(user.getUuid())) {
|
|
||||||
plugin.lockPlayer(user.getUuid());
|
|
||||||
plugin.getDataSyncer().syncSaveUserData(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +79,7 @@ public abstract class EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
usersInWorld.stream()
|
usersInWorld.stream()
|
||||||
.filter(user -> !user.isNpc() && !user.hasDisconnected() && !plugin.isLocked(user.getUuid()))
|
.filter(user -> !plugin.isLocked(user.getUuid()) && !user.isNpc())
|
||||||
.forEach(user -> plugin.getDataSyncer().saveCurrentUserData(
|
.forEach(user -> plugin.getDataSyncer().saveCurrentUserData(
|
||||||
user, DataSnapshot.SaveCause.WORLD_SAVE
|
user, DataSnapshot.SaveCause.WORLD_SAVE
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class RedisManager extends JedisPubSub {
|
|||||||
|
|
||||||
final RedisMessage redisMessage = RedisMessage.fromJson(plugin, message);
|
final RedisMessage redisMessage = RedisMessage.fromJson(plugin, message);
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case UPDATE_USER_DATA -> redisMessage.getTargetUser(plugin).ifPresent(
|
case UPDATE_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
|
||||||
user -> {
|
user -> {
|
||||||
plugin.lockPlayer(user.getUuid());
|
plugin.lockPlayer(user.getUuid());
|
||||||
try {
|
try {
|
||||||
@@ -170,30 +170,16 @@ public class RedisManager extends JedisPubSub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
case REQUEST_USER_DATA -> redisMessage.getTargetUser(plugin).ifPresent(
|
case REQUEST_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent(
|
||||||
user -> RedisMessage.create(
|
user -> RedisMessage.create(
|
||||||
UUID.fromString(new String(redisMessage.getPayload(), StandardCharsets.UTF_8)),
|
UUID.fromString(new String(redisMessage.getPayload(), StandardCharsets.UTF_8)),
|
||||||
user.createSnapshot(DataSnapshot.SaveCause.INVENTORY_COMMAND).asBytes(plugin)
|
user.createSnapshot(DataSnapshot.SaveCause.INVENTORY_COMMAND).asBytes(plugin)
|
||||||
).dispatch(plugin, RedisMessage.Type.RETURN_USER_DATA)
|
).dispatch(plugin, RedisMessage.Type.RETURN_USER_DATA)
|
||||||
);
|
);
|
||||||
case CHECK_IN_PETITION -> {
|
|
||||||
if (!redisMessage.isTargetServer(plugin)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String payload = new String(redisMessage.getPayload(), StandardCharsets.UTF_8);
|
|
||||||
final User user = new User(UUID.fromString(payload.split("/")[0]), payload.split("/")[1]);
|
|
||||||
boolean online = plugin.getDisconnectingPlayers().contains(user.getUuid())
|
|
||||||
|| plugin.getOnlineUser(user.getUuid()).isEmpty();
|
|
||||||
if (!online && !plugin.isLocked(user.getUuid())) {
|
|
||||||
plugin.debug("[%s] Received check-in petition for online/unlocked user, ignoring".formatted(user.getName()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
plugin.getRedisManager().setUserCheckedOut(user, false);
|
|
||||||
plugin.debug("[%s] Received petition for offline user, checking them in".formatted(user.getName()));
|
|
||||||
}
|
|
||||||
case RETURN_USER_DATA -> {
|
case RETURN_USER_DATA -> {
|
||||||
final UUID target = redisMessage.getTargetUuid().orElse(null);
|
final CompletableFuture<Optional<DataSnapshot.Packed>> future = pendingRequests.get(
|
||||||
final CompletableFuture<Optional<DataSnapshot.Packed>> future = pendingRequests.get(target);
|
redisMessage.getTargetUuid()
|
||||||
|
);
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
try {
|
try {
|
||||||
final DataSnapshot.Packed data = DataSnapshot.deserialize(plugin, redisMessage.getPayload());
|
final DataSnapshot.Packed data = DataSnapshot.deserialize(plugin, redisMessage.getPayload());
|
||||||
@@ -202,7 +188,7 @@ public class RedisManager extends JedisPubSub {
|
|||||||
plugin.log(Level.SEVERE, "An exception occurred returning user data from Redis", e);
|
plugin.log(Level.SEVERE, "An exception occurred returning user data from Redis", e);
|
||||||
future.complete(Optional.empty());
|
future.complete(Optional.empty());
|
||||||
}
|
}
|
||||||
pendingRequests.remove(target);
|
pendingRequests.remove(redisMessage.getTargetUuid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,17 +211,11 @@ public class RedisManager extends JedisPubSub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Blocking
|
|
||||||
public void sendUserDataUpdate(@NotNull User user, @NotNull DataSnapshot.Packed data) {
|
public void sendUserDataUpdate(@NotNull User user, @NotNull DataSnapshot.Packed data) {
|
||||||
final RedisMessage redisMessage = RedisMessage.create(user.getUuid(), data.asBytes(plugin));
|
plugin.runAsync(() -> {
|
||||||
redisMessage.dispatch(plugin, RedisMessage.Type.UPDATE_USER_DATA);
|
final RedisMessage redisMessage = RedisMessage.create(user.getUuid(), data.asBytes(plugin));
|
||||||
}
|
redisMessage.dispatch(plugin, RedisMessage.Type.UPDATE_USER_DATA);
|
||||||
|
});
|
||||||
@Blocking
|
|
||||||
public void petitionServerCheckin(@NotNull String server, @NotNull User user) {
|
|
||||||
final RedisMessage redisMessage = RedisMessage.create(
|
|
||||||
server, "%s/%s".formatted(user.getUuid(), user.getName()).getBytes(StandardCharsets.UTF_8));
|
|
||||||
redisMessage.dispatch(plugin, RedisMessage.Type.CHECK_IN_PETITION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Optional<DataSnapshot.Packed>> getOnlineUserData(@NotNull UUID requestId, @NotNull User user,
|
public CompletableFuture<Optional<DataSnapshot.Packed>> getOnlineUserData(@NotNull UUID requestId, @NotNull User user,
|
||||||
@@ -441,7 +421,7 @@ public class RedisManager extends JedisPubSub {
|
|||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.currentTimeMillis();
|
||||||
try (Jedis jedis = jedisPool.getResource()) {
|
try (Jedis jedis = jedisPool.getResource()) {
|
||||||
jedis.ping();
|
jedis.ping();
|
||||||
return System.currentTimeMillis() - startTime;
|
return startTime - System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,38 +25,25 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.william278.husksync.HuskSync;
|
import net.william278.husksync.HuskSync;
|
||||||
import net.william278.husksync.adapter.Adaptable;
|
import net.william278.husksync.adapter.Adaptable;
|
||||||
import net.william278.husksync.user.OnlineUser;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Setter
|
|
||||||
public class RedisMessage implements Adaptable {
|
public class RedisMessage implements Adaptable {
|
||||||
|
|
||||||
private @Nullable String targetServer;
|
|
||||||
@SerializedName("target_uuid")
|
@SerializedName("target_uuid")
|
||||||
private @Nullable UUID targetUuid;
|
private UUID targetUuid;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@SerializedName("payload")
|
@SerializedName("payload")
|
||||||
private byte[] payload;
|
private byte[] payload;
|
||||||
|
|
||||||
private RedisMessage(byte[] payload) {
|
|
||||||
setPayload(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RedisMessage(@NotNull UUID targetUuid, byte[] message) {
|
private RedisMessage(@NotNull UUID targetUuid, byte[] message) {
|
||||||
this(message);
|
|
||||||
this.setTargetUuid(targetUuid);
|
this.setTargetUuid(targetUuid);
|
||||||
}
|
this.setPayload(message);
|
||||||
|
|
||||||
private RedisMessage(@NotNull String targetServer, byte[] message) {
|
|
||||||
this(message);
|
|
||||||
this.setTargetServer(targetServer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -68,11 +55,6 @@ public class RedisMessage implements Adaptable {
|
|||||||
return new RedisMessage(targetUuid, message);
|
return new RedisMessage(targetUuid, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static RedisMessage create(@NotNull String targetServer, byte[] message) {
|
|
||||||
return new RedisMessage(targetServer, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static RedisMessage fromJson(@NotNull HuskSync plugin, @NotNull String json) throws JsonSyntaxException {
|
public static RedisMessage fromJson(@NotNull HuskSync plugin, @NotNull String json) throws JsonSyntaxException {
|
||||||
return plugin.getGson().fromJson(json, RedisMessage.class);
|
return plugin.getGson().fromJson(json, RedisMessage.class);
|
||||||
@@ -85,23 +67,20 @@ public class RedisMessage implements Adaptable {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<UUID> getTargetUuid() {
|
@NotNull
|
||||||
return Optional.ofNullable(targetUuid);
|
public UUID getTargetUuid() {
|
||||||
|
return targetUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<OnlineUser> getTargetUser(@NotNull HuskSync plugin) {
|
public void setTargetUuid(@NotNull UUID targetUuid) {
|
||||||
return getTargetUuid().flatMap(plugin::getOnlineUser);
|
this.targetUuid = targetUuid;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTargetServer(@NotNull HuskSync plugin) {
|
|
||||||
return targetServer != null && targetServer.equals(plugin.getServerName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
|
|
||||||
UPDATE_USER_DATA,
|
UPDATE_USER_DATA,
|
||||||
REQUEST_USER_DATA,
|
REQUEST_USER_DATA,
|
||||||
RETURN_USER_DATA,
|
RETURN_USER_DATA;
|
||||||
CHECK_IN_PETITION;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getMessageChannel(@NotNull String clusterId) {
|
public String getMessageChannel(@NotNull String clusterId) {
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ public abstract class DataSyncer {
|
|||||||
final AtomicReference<Task.Repeating> task = new AtomicReference<>();
|
final AtomicReference<Task.Repeating> task = new AtomicReference<>();
|
||||||
final AtomicBoolean processing = new AtomicBoolean(false);
|
final AtomicBoolean processing = new AtomicBoolean(false);
|
||||||
final Runnable runnable = () -> {
|
final Runnable runnable = () -> {
|
||||||
if (user.cannotApplySnapshot()) {
|
if (user.isOffline()) {
|
||||||
task.get().cancel();
|
task.get().cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,10 +62,7 @@ public class DelayDataSyncer extends DataSyncer {
|
|||||||
getRedis().setUserServerSwitch(onlineUser);
|
getRedis().setUserServerSwitch(onlineUser);
|
||||||
saveData(
|
saveData(
|
||||||
onlineUser, onlineUser.createSnapshot(DataSnapshot.SaveCause.DISCONNECT),
|
onlineUser, onlineUser.createSnapshot(DataSnapshot.SaveCause.DISCONNECT),
|
||||||
(user, data) -> {
|
(user, data) -> getRedis().setUserData(user, data)
|
||||||
getRedis().setUserData(user, data);
|
|
||||||
plugin.unlockPlayer(user.getUuid());
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import net.william278.husksync.data.DataSnapshot;
|
|||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class LockstepDataSyncer extends DataSyncer {
|
public class LockstepDataSyncer extends DataSyncer {
|
||||||
|
|
||||||
public LockstepDataSyncer(@NotNull HuskSync plugin) {
|
public LockstepDataSyncer(@NotNull HuskSync plugin) {
|
||||||
@@ -46,19 +44,9 @@ public class LockstepDataSyncer extends DataSyncer {
|
|||||||
@Override
|
@Override
|
||||||
public void syncApplyUserData(@NotNull OnlineUser user) {
|
public void syncApplyUserData(@NotNull OnlineUser user) {
|
||||||
this.listenForRedisData(user, () -> {
|
this.listenForRedisData(user, () -> {
|
||||||
if (user.cannotApplySnapshot()) {
|
if (getRedis().getUserCheckedOut(user).isPresent()) {
|
||||||
plugin.debug("Not checking data state for user who has gone offline: %s".formatted(user.getName()));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If they are checked out, ask the server to check them back in and return false
|
|
||||||
final Optional<String> server = getRedis().getUserCheckedOut(user);
|
|
||||||
if (server.isPresent() && !server.get().equals(plugin.getServerName())) {
|
|
||||||
getRedis().petitionServerCheckin(server.get(), user);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If they are checked in - or checked out on *this* server - we can apply their latest data
|
|
||||||
getRedis().setUserCheckedOut(user, true);
|
getRedis().setUserCheckedOut(user, true);
|
||||||
getRedis().getUserData(user).ifPresentOrElse(
|
getRedis().getUserData(user).ifPresentOrElse(
|
||||||
data -> user.applySnapshot(data, DataSnapshot.UpdateCause.SYNCHRONIZED),
|
data -> user.applySnapshot(data, DataSnapshot.UpdateCause.SYNCHRONIZED),
|
||||||
@@ -75,7 +63,6 @@ public class LockstepDataSyncer extends DataSyncer {
|
|||||||
(user, data) -> {
|
(user, data) -> {
|
||||||
getRedis().setUserData(user, data);
|
getRedis().setUserData(user, data);
|
||||||
getRedis().setUserCheckedOut(user, false);
|
getRedis().setUserCheckedOut(user, false);
|
||||||
plugin.unlockPlayer(user.getUuid());
|
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,27 +43,11 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the player is offline
|
* Indicates if the player has gone offline
|
||||||
*
|
*
|
||||||
* @return {@code true} if the player has left the server; {@code false} otherwise
|
* @return {@code true} if the player has left the server; {@code false} otherwise
|
||||||
* @deprecated use {@code hasDisconnected} instead
|
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "3.8")
|
public abstract boolean isOffline();
|
||||||
public boolean isOffline() {
|
|
||||||
return hasDisconnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract boolean hasDisconnected();
|
|
||||||
|
|
||||||
// Users cannot have snapshots applied if they have disconnected!
|
|
||||||
@Override
|
|
||||||
public boolean cannotApplySnapshot() {
|
|
||||||
if (hasDisconnected()) {
|
|
||||||
getPlugin().debug("[%s] Cannot apply snapshot as user is offline!".formatted(getName()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
@@ -133,7 +117,7 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a {@link DataSnapshot} to a player, updating their data
|
* Set a player's status from a {@link DataSnapshot}
|
||||||
*
|
*
|
||||||
* @param snapshot The {@link DataSnapshot} to set the player's status from
|
* @param snapshot The {@link DataSnapshot} to set the player's status from
|
||||||
* @param cause The {@link DataSnapshot.UpdateCause} of the snapshot
|
* @param cause The {@link DataSnapshot.UpdateCause} of the snapshot
|
||||||
@@ -141,12 +125,14 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
|
|||||||
*/
|
*/
|
||||||
public void applySnapshot(@NotNull DataSnapshot.Packed snapshot, @NotNull DataSnapshot.UpdateCause cause) {
|
public void applySnapshot(@NotNull DataSnapshot.Packed snapshot, @NotNull DataSnapshot.UpdateCause cause) {
|
||||||
getPlugin().fireEvent(getPlugin().getPreSyncEvent(this, snapshot), (event) -> {
|
getPlugin().fireEvent(getPlugin().getPreSyncEvent(this, snapshot), (event) -> {
|
||||||
getPlugin().debug(String.format("Attempting to apply snapshot (%s) to %s (cause: %s)",
|
if (!isOffline()) {
|
||||||
snapshot.getShortId(), getName(), cause.getDisplayName()
|
getPlugin().debug(String.format("Applying snapshot (%s) to %s (cause: %s)",
|
||||||
));
|
snapshot.getShortId(), getName(), cause.getDisplayName()
|
||||||
UserDataHolder.super.applySnapshot(
|
));
|
||||||
event.getData(), (succeeded) -> completeSync(succeeded, cause, getPlugin())
|
UserDataHolder.super.applySnapshot(
|
||||||
);
|
event.getData(), (succeeded) -> completeSync(succeeded, cause, getPlugin())
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ locales:
|
|||||||
data_list_title: '[%1%的玩家数据备份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
|
data_list_title: '[%1%的玩家数据备份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
|
||||||
data_list_item: '[%1%](gray show_text=&7玩家数据备份 %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7已置顶:\n&8已置顶的备份不会按照备份时间自动排序 run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7备份时间:&7\n&8数据保存时间\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7保存原因:\n&8导致数据保存的原因 run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7备份大小:&7\n&8预计备份文件大小(以KiB为单位) run_command=/userdata view %2% %3%)'
|
data_list_item: '[%1%](gray show_text=&7玩家数据备份 %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7已置顶:\n&8已置顶的备份不会按照备份时间自动排序 run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7备份时间:&7\n&8数据保存时间\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7保存原因:\n&8导致数据保存的原因 run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7备份大小:&7\n&8预计备份文件大小(以KiB为单位) run_command=/userdata view %2% %3%)'
|
||||||
data_list_item_invalid: '[%1%](dark_gray show_text=&7%2%的用户数据快照\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7置顶:\n&8已置顶的快照不会自动排序. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&无效的快照数据\n&#ff7e5e&点击删除\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
|
data_list_item_invalid: '[%1%](dark_gray show_text=&7%2%的用户数据快照\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7置顶:\n&8已置顶的快照不会自动排序. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&无效的快照数据\n&#ff7e5e&点击删除\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
|
||||||
data_saved: '[已成功保存 %1% 的当前用户数据快照.](#00fb9a)'
|
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
|
||||||
data_deleted: '[❌ 成功删除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&7%2%)'
|
data_deleted: '[❌ 成功删除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&7%2%)'
|
||||||
data_restored: '[⏪ 成功恢复玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的数据备份](#00fb9a) [%3%.](#00fb9a show_text=&7备份版本UUID:\n&7%4%)'
|
data_restored: '[⏪ 成功恢复玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的数据备份](#00fb9a) [%3%.](#00fb9a show_text=&7备份版本UUID:\n&7%4%)'
|
||||||
data_pinned: '[※ 成功置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&8%2%)'
|
data_pinned: '[※ 成功置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&8%2%)'
|
||||||
@@ -41,8 +41,8 @@ locales:
|
|||||||
save_cause_world_save: '保存世界'
|
save_cause_world_save: '保存世界'
|
||||||
save_cause_death: '死亡'
|
save_cause_death: '死亡'
|
||||||
save_cause_server_shutdown: '服务器关闭'
|
save_cause_server_shutdown: '服务器关闭'
|
||||||
save_cause_save_command: '保存命令'
|
save_cause_save_command: 'save command'
|
||||||
save_cause_dump_command: '转储命令'
|
save_cause_dump_command: 'dump command'
|
||||||
save_cause_inventory_command: '背包命令'
|
save_cause_inventory_command: '背包命令'
|
||||||
save_cause_enderchest_command: '末影箱命令'
|
save_cause_enderchest_command: '末影箱命令'
|
||||||
save_cause_backup_restore: '备份还原'
|
save_cause_backup_restore: '备份还原'
|
||||||
@@ -54,9 +54,9 @@ locales:
|
|||||||
update_available: '[HuskSync](#ff7e5e bold) [| 检测到HuskSync有新版本可以更新了:v%1%(当前版本:v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| 检测到HuskSync有新版本可以更新了:v%1%(当前版本:v%2%).](#ff7e5e)'
|
||||||
reload_complete: '[HuskSync](#00fb9a bold) [| 重新加载配置和消息文件完成.](#00fb9a)\n[⚠ 确保在所有服务器上更新配置文件!](#00fb9a)\n[需要重新启动才能使配置更改生效.](#00fb9a italic)'
|
reload_complete: '[HuskSync](#00fb9a bold) [| 重新加载配置和消息文件完成.](#00fb9a)\n[⚠ 确保在所有服务器上更新配置文件!](#00fb9a)\n[需要重新启动才能使配置更改生效.](#00fb9a italic)'
|
||||||
system_status_header: '[HuskSync](#00fb9a bold) [| 系统状态报告:](#00fb9a)'
|
system_status_header: '[HuskSync](#00fb9a bold) [| 系统状态报告:](#00fb9a)'
|
||||||
system_dump_confirm: '[HuskSync](#00fb9a bold) [| 准备系统转储? 这将包括:](#00fb9a)\n[• 您最新的服务器日志和 HuskSync 配置文件](gray)\n[• 当前插件系统状态信息](gray)\n[• 有关您的 Java 和 Minecraft 服务器环境的信息](gray)\n[• 其他当前安装的插件列表](gray)\n[要确认, 请执行命令:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7点击以准备转储 run_command=/husksync dump confirm)'
|
system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)'
|
||||||
system_dump_started: '[HuskSync](#00fb9a bold) [| 正在准备系统状态转储,请稍候...](#00fb9a)'
|
system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)'
|
||||||
system_dump_ready: '[HuskSync](#00fb9a bold) [| 系统状态转储已完成! 点击查看:](#00fb9a)'
|
system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)'
|
||||||
error_invalid_syntax: '[错误:](#ff3300) [语法错误.用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)'
|
error_invalid_syntax: '[错误:](#ff3300) [语法错误.用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)'
|
||||||
error_invalid_player: '[错误:](#ff3300) [找不到这个名称的玩家.](#ff7e5e)'
|
error_invalid_player: '[错误:](#ff3300) [找不到这个名称的玩家.](#ff7e5e)'
|
||||||
error_invalid_data: '[错误:](#ff3300) [无法解压缩快照数据, 因为它无效或已损坏.](#ff7e5e) [(详情…)](gray show_text=&7⚠ %1%)'
|
error_invalid_data: '[错误:](#ff3300) [无法解压缩快照数据, 因为它无效或已损坏.](#ff7e5e) [(详情…)](gray show_text=&7⚠ %1%)'
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
HuskSync supports the following versions of Minecraft. Since v3.7, you must download the correct version of HuskSync for your server:
|
HuskSync supports the following versions of Minecraft. Since v3.7, you must download the correct version of HuskSync for your server:
|
||||||
|
|
||||||
| Minecraft | Latest HuskSync | Java Version | Platforms | Support Status |
|
| Minecraft | Latest HuskSync | Java Version | Platforms | Support Status |
|
||||||
|:---------------:|:---------------:|:------------:|:--------------|:------------------------------|
|
|:---------------:|:---------------:|:------------:|:--------------|:-----------------------------|
|
||||||
| 1.21.5 | _latest_ | 21 | Paper | ✅ **Active Release** |
|
| 1.21.4 | _latest_ | 21 | Paper, Fabric | ✅ **Active Release** |
|
||||||
| 1.21.4 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (Non-LTS) |
|
| 1.21.3 | 3.7.1 | 21 | Paper, Fabric | 🗃️ Archived (December 2024) |
|
||||||
| 1.21.3 | 3.7.1 | 21 | Paper, Fabric | 🗃️ Archived (December 2024) |
|
| 1.21.1 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
||||||
| 1.21.1 | _latest_ | 21 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
| 1.20.6 | 3.6.8 | 17 | Paper | 🗃️ Archived (October 2024) |
|
||||||
| 1.20.6 | 3.6.8 | 17 | Paper | 🗃️ Archived (October 2024) |
|
| 1.20.4 | 3.6.8 | 17 | Paper | 🗃️ Archived (July 2024) |
|
||||||
| 1.20.4 | 3.6.8 | 17 | Paper | 🗃️ Archived (July 2024) |
|
| 1.20.1 | _latest_ | 17 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
||||||
| 1.20.1 | _latest_ | 17 | Paper, Fabric | ✅ **November 2025** (LTS) |
|
| 1.17.1 - 1.19.4 | 3.6.8 | 17 | Paper | 🗃️ Archived |
|
||||||
| 1.17.1 - 1.19.4 | 3.6.8 | 17 | Paper | 🗃️ Archived |
|
| 1.16.5 | 3.2.1 | 16 | Paper | 🗃️ Archived |
|
||||||
| 1.16.5 | 3.2.1 | 16 | Paper | 🗃️ Archived |
|
|
||||||
|
|
||||||
HuskSync is primarily developed against the latest release. Old Minecraft versions are allocated a support channel based on popularity, mod support, etc:
|
HuskSync is primarily developed against the latest release. Old Minecraft versions are allocated a support channel based on popularity, mod support, etc:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
essential.defaults.loom.mappings=net.fabricmc:yarn:1.21.4+build.4:v2
|
essential.defaults.loom.mappings=net.fabricmc:yarn:1.21.4+build.4:v2
|
||||||
|
|
||||||
fabric_loader_version=0.16.10
|
fabric_loader_version=0.16.10
|
||||||
fabric_api_version=0.116.1+1.21.4
|
fabric_api_version=0.115.0+1.21.4
|
||||||
fabric_permissions_api_version=0.3.3
|
fabric_permissions_api_version=0.3.3
|
||||||
fabric_adventure_platform_version=6.3.0
|
fabric_adventure_platform_version=6.2.0
|
||||||
fabric_sgui_version=1.8.2+1.21.4
|
fabric_sgui_version=1.8.2+1.21.4
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
essential.defaults.loom.mappings=net.fabricmc:yarn:1.21.5+build.1:v2
|
|
||||||
|
|
||||||
fabric_loader_version=0.16.14
|
|
||||||
fabric_api_version=0.122.0+1.21.5
|
|
||||||
fabric_permissions_api_version=0.3.3
|
|
||||||
fabric_adventure_platform_version=6.4.0-SNAPSHOT
|
|
||||||
fabric_sgui_version=1.9.0+1.21.5
|
|
||||||
@@ -14,16 +14,10 @@ dependencies {
|
|||||||
modImplementation include("net.kyori:adventure-platform-fabric:${fabric_adventure_platform_version}")
|
modImplementation include("net.kyori:adventure-platform-fabric:${fabric_adventure_platform_version}")
|
||||||
modImplementation include("me.lucko:fabric-permissions-api:${fabric_permissions_api_version}")
|
modImplementation include("me.lucko:fabric-permissions-api:${fabric_permissions_api_version}")
|
||||||
modImplementation include("eu.pb4:sgui:${fabric_sgui_version}")
|
modImplementation include("eu.pb4:sgui:${fabric_sgui_version}")
|
||||||
modImplementation include("net.william278.uniform:uniform-fabric:1.3.3+${project.name}")
|
modImplementation include("net.william278.uniform:uniform-fabric:1.3.1+${project.name}")
|
||||||
modImplementation include("net.william278.toilet:toilet-fabric:1.0.13+${project.name}")
|
modImplementation include("net.william278.toilet:toilet-fabric:1.0.12+${project.name}")
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
|
||||||
|
|
||||||
// Manually include config deps due to the way including api deps works
|
|
||||||
implementation include("de.exlll:configlib-core:4.6.1")
|
|
||||||
implementation include("org.snakeyaml:snakeyaml-engine:2.7")
|
|
||||||
implementation include('org.apache.commons:commons-pool2:2.12.1')
|
|
||||||
|
|
||||||
// Include driver deps due to no runtime dep loading support
|
|
||||||
implementation include('org.apache.commons:commons-pool2:2.12.1')
|
implementation include('org.apache.commons:commons-pool2:2.12.1')
|
||||||
implementation include("com.mysql:mysql-connector-j:$mysql_driver_version")
|
implementation include("com.mysql:mysql-connector-j:$mysql_driver_version")
|
||||||
implementation include("org.postgresql:postgresql:$postgres_driver_version")
|
implementation include("org.postgresql:postgresql:$postgres_driver_version")
|
||||||
@@ -33,9 +27,9 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly 'net.william278:DesertWell:2.0.4'
|
compileOnly 'net.william278:DesertWell:2.0.4'
|
||||||
compileOnly 'org.jetbrains:annotations:26.0.2'
|
compileOnly 'org.jetbrains:annotations:26.0.2'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.38'
|
compileOnly 'org.projectlombok:lombok:1.18.36'
|
||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
||||||
|
|
||||||
implementation include(project(path: ":common"))
|
implementation include(project(path: ":common"))
|
||||||
project(":common").configurations.api.dependencies.each { dependency ->
|
project(":common").configurations.api.dependencies.each { dependency ->
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.21.5
|
1.21.4
|
||||||
@@ -3,13 +3,11 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preprocess {
|
preprocess {
|
||||||
def fabric12105 = createNode("1.21.5", 12105, "yarn")
|
|
||||||
def fabric12104 = createNode("1.21.4", 12104, "yarn")
|
def fabric12104 = createNode("1.21.4", 12104, "yarn")
|
||||||
def fabric12101 = createNode("1.21.1", 12101, "yarn")
|
def fabric12101 = createNode("1.21.1", 12101, "yarn")
|
||||||
def fabric12001 = createNode("1.20.1", 12001, "yarn")
|
def fabric12001 = createNode("1.20.1", 12001, "yarn")
|
||||||
|
|
||||||
strictExtraMappings.set(true)
|
strictExtraMappings.set(true)
|
||||||
fabric12104.link(fabric12105, null)
|
fabric12101.link(fabric12104, null)
|
||||||
fabric12101.link(fabric12105, null)
|
fabric12001.link(fabric12104, null)
|
||||||
fabric12001.link(fabric12105, null)
|
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ import net.fabricmc.api.DedicatedServerModInitializer;
|
|||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
|
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
|
||||||
//#else
|
//#else
|
||||||
//$$ import net.kyori.adventure.platform.fabric.FabricServerAudiences;
|
//$$ import net.kyori.adventure.platform.fabric.FabricServerAudiences;
|
||||||
@@ -54,6 +54,7 @@ import net.william278.husksync.database.PostgresDatabase;
|
|||||||
import net.william278.husksync.event.FabricEventDispatcher;
|
import net.william278.husksync.event.FabricEventDispatcher;
|
||||||
import net.william278.husksync.event.ModLoadedCallback;
|
import net.william278.husksync.event.ModLoadedCallback;
|
||||||
import net.william278.husksync.hook.PlanHook;
|
import net.william278.husksync.hook.PlanHook;
|
||||||
|
import net.william278.husksync.listener.EventListener;
|
||||||
import net.william278.husksync.listener.FabricEventListener;
|
import net.william278.husksync.listener.FabricEventListener;
|
||||||
import net.william278.husksync.listener.LockedHandler;
|
import net.william278.husksync.listener.LockedHandler;
|
||||||
import net.william278.husksync.migrator.Migrator;
|
import net.william278.husksync.migrator.Migrator;
|
||||||
@@ -100,14 +101,14 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
private static final int VERSION1_21_1 = 3955;
|
private static final int VERSION1_21_1 = 3955;
|
||||||
private static final int VERSION1_21_3 = 4082;
|
private static final int VERSION1_21_3 = 4082;
|
||||||
private static final int VERSION1_21_4 = 4189; // Current
|
private static final int VERSION1_21_4 = 4189; // Current
|
||||||
private static final int VERSION1_21_5 = 4323;
|
|
||||||
|
|
||||||
private final HashMap<Identifier, Serializer<? extends Data>> serializers = Maps.newHashMap();
|
private final TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
||||||
|
SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR
|
||||||
|
);
|
||||||
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
||||||
private final Map<String, Boolean> permissions = Maps.newHashMap();
|
private final Map<String, Boolean> permissions = Maps.newHashMap();
|
||||||
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
||||||
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
||||||
private final Set<UUID> disconnectingPlayers = Sets.newConcurrentHashSet();
|
|
||||||
private final Map<UUID, FabricUser> playerMap = Maps.newConcurrentMap();
|
private final Map<UUID, FabricUser> playerMap = Maps.newConcurrentMap();
|
||||||
|
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
@@ -115,7 +116,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
private MinecraftServer minecraftServer;
|
private MinecraftServer minecraftServer;
|
||||||
private boolean disabling;
|
private boolean disabling;
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
private MinecraftServerAudiences audiences;
|
private MinecraftServerAudiences audiences;
|
||||||
//#else
|
//#else
|
||||||
//$$ private FabricServerAudiences audiences;
|
//$$ private FabricServerAudiences audiences;
|
||||||
@@ -166,7 +167,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
|
|
||||||
private void onEnable() {
|
private void onEnable() {
|
||||||
// Audiences
|
// Audiences
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
this.audiences = MinecraftServerAudiences.of(minecraftServer);
|
this.audiences = MinecraftServerAudiences.of(minecraftServer);
|
||||||
//#else
|
//#else
|
||||||
//$$ this.audiences = FabricServerAudiences.of(minecraftServer);
|
//$$ this.audiences = FabricServerAudiences.of(minecraftServer);
|
||||||
@@ -386,10 +387,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
case "1.21", "1.21.1" -> VERSION1_21_1;
|
case "1.21", "1.21.1" -> VERSION1_21_1;
|
||||||
case "1.21.2", "1.21.3" -> VERSION1_21_3;
|
case "1.21.2", "1.21.3" -> VERSION1_21_3;
|
||||||
case "1.21.4" -> VERSION1_21_4;
|
case "1.21.4" -> VERSION1_21_4;
|
||||||
case "1.21.5" -> VERSION1_21_5;
|
//#if MC==12104
|
||||||
//#if MC==12105
|
|
||||||
//$$ default -> VERSION1_21_5;
|
|
||||||
//#elseif MC==12104
|
|
||||||
default -> VERSION1_21_4;
|
default -> VERSION1_21_4;
|
||||||
//#elseif MC==12101
|
//#elseif MC==12101
|
||||||
//$$ default -> VERSION1_21_1;
|
//$$ default -> VERSION1_21_1;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import net.william278.husksync.FabricHuskSync;
|
|||||||
import net.william278.husksync.HuskSync;
|
import net.william278.husksync.HuskSync;
|
||||||
import net.william278.husksync.adapter.Adaptable;
|
import net.william278.husksync.adapter.Adaptable;
|
||||||
import net.william278.husksync.config.Settings.SynchronizationSettings.AttributeSettings;
|
import net.william278.husksync.config.Settings.SynchronizationSettings.AttributeSettings;
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
import net.william278.husksync.mixins.HungerManagerMixin;
|
import net.william278.husksync.mixins.HungerManagerMixin;
|
||||||
//#endif
|
//#endif
|
||||||
import net.william278.husksync.user.FabricUser;
|
import net.william278.husksync.user.FabricUser;
|
||||||
@@ -178,7 +178,7 @@ public abstract class FabricData implements Data {
|
|||||||
@Override
|
@Override
|
||||||
public void apply(@NotNull FabricUser user, @NotNull FabricHuskSync plugin) throws IllegalStateException {
|
public void apply(@NotNull FabricUser user, @NotNull FabricHuskSync plugin) throws IllegalStateException {
|
||||||
final ServerPlayerEntity player = user.getPlayer();
|
final ServerPlayerEntity player = user.getPlayer();
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
player.playerScreenHandler.getCraftingInput().clear();
|
player.playerScreenHandler.getCraftingInput().clear();
|
||||||
//#else
|
//#else
|
||||||
//$$ player.playerScreenHandler.clearCraftingSlots();
|
//$$ player.playerScreenHandler.clearCraftingSlots();
|
||||||
@@ -188,11 +188,7 @@ public abstract class FabricData implements Data {
|
|||||||
for (int slot = 0; slot < player.getInventory().size(); slot++) {
|
for (int slot = 0; slot < player.getInventory().size(); slot++) {
|
||||||
player.getInventory().setStack(slot, items[slot] == null ? ItemStack.EMPTY : items[slot]);
|
player.getInventory().setStack(slot, items[slot] == null ? ItemStack.EMPTY : items[slot]);
|
||||||
}
|
}
|
||||||
//#if MC<12105
|
player.getInventory().selectedSlot = heldItemSlot;
|
||||||
//$$ player.getInventory().selectedSlot = heldItemSlot;
|
|
||||||
//#else
|
|
||||||
player.getInventory().setSelectedSlot(heldItemSlot);
|
|
||||||
//#endif
|
|
||||||
player.playerScreenHandler.sendContentUpdates();
|
player.playerScreenHandler.sendContentUpdates();
|
||||||
player.getInventory().updateItems();
|
player.getInventory().updateItems();
|
||||||
}
|
}
|
||||||
@@ -517,7 +513,7 @@ public abstract class FabricData implements Data {
|
|||||||
// Apply teleport
|
// Apply teleport
|
||||||
try {
|
try {
|
||||||
player.dismountVehicle();
|
player.dismountVehicle();
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
player.teleport(target, x, y, z, Set.of(), yaw, pitch, true);
|
player.teleport(target, x, y, z, Set.of(), yaw, pitch, true);
|
||||||
//#else
|
//#else
|
||||||
//$$ player.teleport(target, x, y, z, yaw, pitch);
|
//$$ player.teleport(target, x, y, z, yaw, pitch);
|
||||||
@@ -556,13 +552,13 @@ public abstract class FabricData implements Data {
|
|||||||
// This is necessary to prevent weird re-mappings with Registry#getKey()
|
// This is necessary to prevent weird re-mappings with Registry#getKey()
|
||||||
//#if MC>0
|
//#if MC>0
|
||||||
//$$ final Registry<?> registry = stat.getValue().getRegistry();
|
//$$ final Registry<?> registry = stat.getValue().getRegistry();
|
||||||
//$$ final String registryId = registry.getKey().getValue().value();
|
//$$ final String registryId = registry.getKey().getValue().toString();
|
||||||
//$$ if (registryId.equals("custom_stat")) {
|
//$$ if (registryId.equals("custom_stat")) {
|
||||||
//$$ return;
|
//$$ return;
|
||||||
//$$ }
|
//$$ }
|
||||||
//#else
|
//#else
|
||||||
final Registry<?> registry = stat.getValue().getRegistry();
|
final Registry<?> registry = stat.getValue().getRegistry();
|
||||||
final String registryId = registry.getKey().getValue().value();
|
final String registryId = registry.getKey().getValue().toString();
|
||||||
if (registryId.equals("custom_stat")) {
|
if (registryId.equals("custom_stat")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -808,7 +804,7 @@ public abstract class FabricData implements Data {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static FabricData.Hunger adapt(@NotNull ServerPlayerEntity player) {
|
public static FabricData.Hunger adapt(@NotNull ServerPlayerEntity player) {
|
||||||
final HungerManager hunger = player.getHungerManager();
|
final HungerManager hunger = player.getHungerManager();
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
float exhaustion = ((HungerManagerMixin) hunger).getExhaustion();
|
float exhaustion = ((HungerManagerMixin) hunger).getExhaustion();
|
||||||
//#else
|
//#else
|
||||||
//$$ float exhaustion = hunger.getExhaustion();
|
//$$ float exhaustion = hunger.getExhaustion();
|
||||||
@@ -827,7 +823,7 @@ public abstract class FabricData implements Data {
|
|||||||
final HungerManager hunger = player.getHungerManager();
|
final HungerManager hunger = player.getHungerManager();
|
||||||
hunger.setFoodLevel(foodLevel);
|
hunger.setFoodLevel(foodLevel);
|
||||||
hunger.setSaturationLevel(saturation);
|
hunger.setSaturationLevel(saturation);
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
((HungerManagerMixin) hunger).setExhaustion(exhaustion);
|
((HungerManagerMixin) hunger).setExhaustion(exhaustion);
|
||||||
//#else
|
//#else
|
||||||
//$$ hunger.setExhaustion(exhaustion);
|
//$$ hunger.setExhaustion(exhaustion);
|
||||||
@@ -892,11 +888,7 @@ public abstract class FabricData implements Data {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(@NotNull FabricUser user, @NotNull FabricHuskSync plugin) throws IllegalStateException {
|
public void apply(@NotNull FabricUser user, @NotNull FabricHuskSync plugin) throws IllegalStateException {
|
||||||
//#if MC<12105
|
user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byName(gameMode));
|
||||||
//$$ user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byName(gameMode));
|
|
||||||
//#else
|
|
||||||
user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byId(gameMode));
|
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,29 +76,17 @@ public abstract class FabricSerializer {
|
|||||||
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
||||||
final NbtCompound root;
|
final NbtCompound root;
|
||||||
try {
|
try {
|
||||||
//#if MC<12105
|
root = StringNbtReader.parse(serialized);
|
||||||
//$$ root = StringNbtReader.parse(serialized);
|
|
||||||
//#else
|
|
||||||
root = StringNbtReader.readCompound(serialized);
|
|
||||||
//#endif
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new DeserializationException("Failed to read item NBT from string (%s)".formatted(serialized), e);
|
throw new DeserializationException("Failed to read item NBT from string (%s)".formatted(serialized), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize the inventory data
|
// Deserialize the inventory data
|
||||||
//#if MC<12105
|
final NbtCompound items = root.contains(ITEMS_TAG) ? root.getCompound(ITEMS_TAG) : null;
|
||||||
//$$ final NbtCompound items = root.contains(ITEMS_TAG) ? root.getCompound(ITEMS_TAG) : null;
|
|
||||||
//$$ return FabricData.Items.Inventory.from(
|
|
||||||
//$$ items != null ? getItems(items, dataMcVersion, plugin) : new ItemStack[INVENTORY_SLOT_COUNT],
|
|
||||||
//$$ root.contains(HELD_ITEM_SLOT_TAG) ? root.getInt(HELD_ITEM_SLOT_TAG) : 0
|
|
||||||
//$$ );
|
|
||||||
//#else
|
|
||||||
final NbtCompound items = root.contains(ITEMS_TAG) ? root.getCompoundOrEmpty(ITEMS_TAG) : null;
|
|
||||||
return FabricData.Items.Inventory.from(
|
return FabricData.Items.Inventory.from(
|
||||||
items != null ? getItems(items, dataMcVersion, plugin) : new ItemStack[INVENTORY_SLOT_COUNT],
|
items != null ? getItems(items, dataMcVersion, plugin) : new ItemStack[INVENTORY_SLOT_COUNT],
|
||||||
root.getInt(HELD_ITEM_SLOT_TAG, 0)
|
root.contains(HELD_ITEM_SLOT_TAG) ? root.getInt(HELD_ITEM_SLOT_TAG) : 0
|
||||||
);
|
);
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,11 +121,7 @@ public abstract class FabricSerializer {
|
|||||||
throws DeserializationException {
|
throws DeserializationException {
|
||||||
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
||||||
try {
|
try {
|
||||||
//#if MC<12105
|
final NbtCompound items = StringNbtReader.parse(serialized);
|
||||||
//$$ final NbtCompound items = StringNbtReader.parse(serialized);
|
|
||||||
//#else
|
|
||||||
final NbtCompound items = StringNbtReader.readCompound(serialized);
|
|
||||||
//#endif
|
|
||||||
return FabricData.Items.EnderChest.adapt(getItems(items, dataMcVersion, plugin));
|
return FabricData.Items.EnderChest.adapt(getItems(items, dataMcVersion, plugin));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new DeserializationException("Failed to read item NBT from string (%s)".formatted(serialized), e);
|
throw new DeserializationException("Failed to read item NBT from string (%s)".formatted(serialized), e);
|
||||||
@@ -169,26 +153,14 @@ public abstract class FabricSerializer {
|
|||||||
return upgradeItemStacks(tag, mcVersion, plugin);
|
return upgradeItemStacks(tag, mcVersion, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ItemStack[] contents = new ItemStack[tag.getInt("size")];
|
||||||
|
final NbtList itemList = tag.getList("items", NbtElement.COMPOUND_TYPE);
|
||||||
final DynamicRegistryManager registryManager = plugin.getMinecraftServer().getRegistryManager();
|
final DynamicRegistryManager registryManager = plugin.getMinecraftServer().getRegistryManager();
|
||||||
//#if MC<12105
|
|
||||||
//$$ final ItemStack[] contents = new ItemStack[tag.getInt("size")];
|
|
||||||
//$$ final NbtList itemList = tag.getList("items", NbtElement.COMPOUND_TYPE);
|
|
||||||
//$$ itemList.forEach(element -> {
|
|
||||||
//$$ final NbtCompound compound = (NbtCompound) element;
|
|
||||||
//$$ contents[compound.getInt("Slot")] = decodeNbt(element, registryManager);
|
|
||||||
//$$ });
|
|
||||||
//#else
|
|
||||||
final ItemStack[] contents = new ItemStack[tag.getInt("size", 0)];
|
|
||||||
final NbtList itemList = tag.getListOrEmpty("items");
|
|
||||||
itemList.forEach(element -> {
|
itemList.forEach(element -> {
|
||||||
final NbtCompound compound = (NbtCompound) element;
|
final NbtCompound compound = (NbtCompound) element;
|
||||||
int i = compound.getInt("Slot", -1);
|
contents[compound.getInt("Slot")] = decodeNbt(element, registryManager);
|
||||||
if (i >= 0) {
|
|
||||||
contents[i] = decodeNbt(element, registryManager);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
//#endif
|
plugin.debug(Arrays.toString(contents));
|
||||||
|
|
||||||
return contents;
|
return contents;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new Serializer.DeserializationException("Failed to read item NBT string (%s)".formatted(tag), e);
|
throw new Serializer.DeserializationException("Failed to read item NBT string (%s)".formatted(tag), e);
|
||||||
@@ -227,37 +199,19 @@ public abstract class FabricSerializer {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private ItemStack @NotNull [] upgradeItemStacks(@NotNull NbtCompound items, @NotNull Version mcVersion,
|
private ItemStack @NotNull [] upgradeItemStacks(@NotNull NbtCompound items, @NotNull Version mcVersion,
|
||||||
@NotNull FabricHuskSync plugin) {
|
@NotNull FabricHuskSync plugin) {
|
||||||
//#if MC<12105
|
final int size = items.getInt("size");
|
||||||
//$$ final int size = items.getInt("size");
|
final NbtList list = items.getList("items", NbtElement.COMPOUND_TYPE);
|
||||||
//$$ final NbtList list = items.getList("items", NbtElement.COMPOUND_TYPE);
|
|
||||||
//$$ final ItemStack[] itemStacks = new ItemStack[size];
|
|
||||||
//$$ final DynamicRegistryManager registryManager = plugin.getMinecraftServer().getRegistryManager();
|
|
||||||
//$$ Arrays.fill(itemStacks, ItemStack.EMPTY);
|
|
||||||
//$$ for (int i = 0; i < size; i++) {
|
|
||||||
//$$ if (list.getCompound(i) == null) {
|
|
||||||
//$$ continue;
|
|
||||||
//$$ }
|
|
||||||
//$$ final NbtCompound compound = list.getCompound(i);
|
|
||||||
//$$ final int slot = compound.getInt("Slot");
|
|
||||||
//$$ itemStacks[slot] = decodeNbt(upgradeItemData(list.getCompound(i), mcVersion, plugin), registryManager);
|
|
||||||
//$$ }
|
|
||||||
//#else
|
|
||||||
final int size = items.getInt("size", 0);
|
|
||||||
final NbtList list = items.getListOrEmpty("items");
|
|
||||||
final ItemStack[] itemStacks = new ItemStack[size];
|
final ItemStack[] itemStacks = new ItemStack[size];
|
||||||
final DynamicRegistryManager registryManager = plugin.getMinecraftServer().getRegistryManager();
|
final DynamicRegistryManager registryManager = plugin.getMinecraftServer().getRegistryManager();
|
||||||
Arrays.fill(itemStacks, ItemStack.EMPTY);
|
Arrays.fill(itemStacks, ItemStack.EMPTY);
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
final NbtCompound compound = list.getCompoundOrEmpty(i);
|
if (list.getCompound(i) == null) {
|
||||||
if (compound.isEmpty()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final int slot = compound.getInt("Slot", -1);
|
final NbtCompound compound = list.getCompound(i);
|
||||||
if (slot >= 0) {
|
final int slot = compound.getInt("Slot");
|
||||||
itemStacks[slot] = decodeNbt(upgradeItemData(compound, mcVersion, plugin), registryManager);
|
itemStacks[slot] = decodeNbt(upgradeItemData(list.getCompound(i), mcVersion, plugin), registryManager);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//#endif
|
|
||||||
return itemStacks;
|
return itemStacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +228,7 @@ public abstract class FabricSerializer {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private NbtCompound encodeNbt(@NotNull ItemStack item, @NotNull DynamicRegistryManager registryManager) {
|
private NbtCompound encodeNbt(@NotNull ItemStack item, @NotNull DynamicRegistryManager registryManager) {
|
||||||
try {
|
try {
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
return (NbtCompound) item.toNbt(registryManager);
|
return (NbtCompound) item.toNbt(registryManager);
|
||||||
//#elseif MC==12101
|
//#elseif MC==12101
|
||||||
//$$ return (NbtCompound) item.encode(registryManager);
|
//$$ return (NbtCompound) item.encode(registryManager);
|
||||||
|
|||||||
@@ -34,31 +34,29 @@ public interface FabricUserDataHolder extends UserDataHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
default Optional<? extends Data> getData(@NotNull Identifier id) {
|
||||||
if (id.isCustom()) {
|
if (!id.isCustom()) {
|
||||||
return Optional.ofNullable(getCustomDataStore().get(id));
|
try {
|
||||||
}
|
return switch (id.getKeyValue()) {
|
||||||
|
case "inventory" -> getInventory();
|
||||||
try {
|
case "ender_chest" -> getEnderChest();
|
||||||
return switch (id.getKeyValue()) {
|
case "potion_effects" -> getPotionEffects();
|
||||||
case "inventory" -> getInventory();
|
case "advancements" -> getAdvancements();
|
||||||
case "ender_chest" -> getEnderChest();
|
case "location" -> getLocation();
|
||||||
case "potion_effects" -> getPotionEffects();
|
case "statistics" -> getStatistics();
|
||||||
case "advancements" -> getAdvancements();
|
case "health" -> getHealth();
|
||||||
case "location" -> getLocation();
|
case "hunger" -> getHunger();
|
||||||
case "statistics" -> getStatistics();
|
case "attributes" -> getAttributes();
|
||||||
case "health" -> getHealth();
|
case "experience" -> getExperience();
|
||||||
case "hunger" -> getHunger();
|
case "game_mode" -> getGameMode();
|
||||||
case "attributes" -> getAttributes();
|
case "flight_status" -> getFlightStatus();
|
||||||
case "experience" -> getExperience();
|
case "persistent_data" -> getPersistentData();
|
||||||
case "game_mode" -> getGameMode();
|
default -> throw new IllegalStateException(String.format("Unexpected data type: %s", id));
|
||||||
case "flight_status" -> getFlightStatus();
|
};
|
||||||
case "persistent_data" -> getPersistentData();
|
} catch (Throwable e) {
|
||||||
default -> throw new IllegalStateException(String.format("Unexpected data type: %s", id));
|
getPlugin().debug("Failed to get data for key: " + id.getKeyValue(), e);
|
||||||
};
|
}
|
||||||
} catch (Throwable e) {
|
|
||||||
getPlugin().debug("Failed to get data for key: " + id.asMinimalString(), e);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
return Optional.ofNullable(getCustomDataStore().get(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -79,41 +77,27 @@ public interface FabricUserDataHolder extends UserDataHolder {
|
|||||||
final PlayerInventory inventory = getPlayer().getInventory();
|
final PlayerInventory inventory = getPlayer().getInventory();
|
||||||
return Optional.of(FabricData.Items.Inventory.from(
|
return Optional.of(FabricData.Items.Inventory.from(
|
||||||
getCombinedInventory(inventory),
|
getCombinedInventory(inventory),
|
||||||
//#if MC<12105
|
inventory.selectedSlot
|
||||||
//$$ inventory.selectedSlot
|
|
||||||
//#else
|
|
||||||
inventory.getSelectedSlot()
|
|
||||||
//#endif
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the player's combined inventory; their inventory, plus offhand and armor.
|
// Gets the player's combined inventory; their inventory, plus offhand and armor.
|
||||||
@Nullable
|
@Nullable
|
||||||
private ItemStack @NotNull [] getCombinedInventory(@NotNull PlayerInventory inv) {
|
private ItemStack @NotNull [] getCombinedInventory(@NotNull PlayerInventory inv) {
|
||||||
//#if MC<12105
|
final ItemStack[] combined = new ItemStack[inv.main.size() + inv.armor.size() + inv.offHand.size()];
|
||||||
//$$ final ItemStack[] combined = new ItemStack[inv.main.size() + inv.armor.size() + inv.offHand.size()];
|
System.arraycopy(
|
||||||
//$$ System.arraycopy(
|
inv.main.toArray(new ItemStack[0]), 0, combined,
|
||||||
//$$ inv.main.toArray(new ItemStack[0]), 0, combined,
|
0, inv.main.size()
|
||||||
//$$ 0, inv.main.size()
|
);
|
||||||
//$$ );
|
System.arraycopy(
|
||||||
//$$ System.arraycopy(
|
inv.armor.toArray(new ItemStack[0]), 0, combined,
|
||||||
//$$ inv.armor.toArray(new ItemStack[0]), 0, combined,
|
inv.main.size(), inv.armor.size()
|
||||||
//$$ inv.main.size(), inv.armor.size()
|
);
|
||||||
//$$ );
|
System.arraycopy(
|
||||||
//$$ System.arraycopy(
|
inv.offHand.toArray(new ItemStack[0]), 0, combined,
|
||||||
//$$ inv.offHand.toArray(new ItemStack[0]), 0, combined,
|
inv.main.size() + inv.armor.size(), inv.offHand.size()
|
||||||
//$$ inv.main.size() + inv.armor.size(), inv.offHand.size()
|
);
|
||||||
//$$ );
|
|
||||||
//$$ return combined;
|
|
||||||
//#else
|
|
||||||
final ItemStack[] combined = new ItemStack[inv.size()];
|
|
||||||
int slot = 0;
|
|
||||||
for (ItemStack itemStack : inv) {
|
|
||||||
combined[slot] = itemStack;
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
return combined;
|
return combined;
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class FabricEventListener extends EventListener implements LockedHandler
|
|||||||
return (cancelPlayerEvent(player.getUuid())) ? ActionResult.FAIL : ActionResult.PASS;
|
return (cancelPlayerEvent(player.getUuid())) ? ActionResult.FAIL : ActionResult.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
private ActionResult handleItemInteract(PlayerEntity player, World world, Hand hand) {
|
private ActionResult handleItemInteract(PlayerEntity player, World world, Hand hand) {
|
||||||
return (cancelPlayerEvent(player.getUuid())) ? ActionResult.FAIL : ActionResult.PASS;
|
return (cancelPlayerEvent(player.getUuid())) ? ActionResult.FAIL : ActionResult.PASS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
package net.william278.husksync.mixins;
|
package net.william278.husksync.mixins;
|
||||||
|
|
||||||
import net.minecraft.entity.player.HungerManager;
|
import net.minecraft.entity.player.HungerManager;
|
||||||
|
|||||||
@@ -71,11 +71,7 @@ public abstract class ServerPlayNetworkHandlerMixin {
|
|||||||
|
|
||||||
@Inject(method = "onClickSlot", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "onClickSlot", at = @At("HEAD"), cancellable = true)
|
||||||
public void onClickSlot(ClickSlotC2SPacket packet, CallbackInfo ci) {
|
public void onClickSlot(ClickSlotC2SPacket packet, CallbackInfo ci) {
|
||||||
//#if MC<12105
|
int slot = packet.getSlot();
|
||||||
//$$ int slot = packet.getSlot();
|
|
||||||
//#else
|
|
||||||
int slot = packet.slot();
|
|
||||||
//#endif
|
|
||||||
if (slot < 0) {
|
if (slot < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class ServerWorldMixin {
|
|||||||
@Shadow
|
@Shadow
|
||||||
private MinecraftServer server;
|
private MinecraftServer server;
|
||||||
|
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
@Inject(method = "savePersistentState", at = @At("HEAD"))
|
@Inject(method = "savePersistentState", at = @At("HEAD"))
|
||||||
//#else
|
//#else
|
||||||
//$$ @Inject(method = "saveLevel", at = @At("HEAD"))
|
//$$ @Inject(method = "saveLevel", at = @At("HEAD"))
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import eu.pb4.sgui.api.elements.GuiElementInterface;
|
|||||||
import eu.pb4.sgui.api.gui.SimpleGui;
|
import eu.pb4.sgui.api.gui.SimpleGui;
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import net.kyori.adventure.audience.Audience;
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
|
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
|
||||||
//#else
|
//#else
|
||||||
//$$ import net.kyori.adventure.platform.fabric.FabricServerAudiences;
|
//$$ import net.kyori.adventure.platform.fabric.FabricServerAudiences;
|
||||||
@@ -64,9 +64,8 @@ public class FabricUser extends OnlineUser implements FabricUserDataHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDisconnected() {
|
public boolean isOffline() {
|
||||||
return getPlugin().getDisconnectingPlayers().contains(getUuid())
|
return player == null || player.isDisconnected();
|
||||||
|| player == null || player.isDisconnected();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -80,7 +79,7 @@ public class FabricUser extends OnlineUser implements FabricUserDataHolder {
|
|||||||
public void sendToast(@NotNull MineDown title, @NotNull MineDown description, @NotNull String iconMaterial,
|
public void sendToast(@NotNull MineDown title, @NotNull MineDown description, @NotNull String iconMaterial,
|
||||||
@NotNull String backgroundType) {
|
@NotNull String backgroundType) {
|
||||||
plugin.log(Level.WARNING, "Toast notifications are deprecated. " +
|
plugin.log(Level.WARNING, "Toast notifications are deprecated. " +
|
||||||
"Please change your notification display slot to CHAT, ACTION_BAR or NONE.");
|
"Please change your notification display slot to CHAT, ACTION_BAR or NONE.");
|
||||||
this.sendActionBar(title);
|
this.sendActionBar(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ public class FabricUser extends OnlineUser implements FabricUserDataHolder {
|
|||||||
this.editable = editable;
|
this.editable = editable;
|
||||||
|
|
||||||
// Set title, items
|
// Set title, items
|
||||||
//#if MC>=12104
|
//#if MC==12104
|
||||||
this.setTitle(((MinecraftServerAudiences) plugin.getAudiences()).asNative(title.toComponent()));
|
this.setTitle(((MinecraftServerAudiences) plugin.getAudiences()).asNative(title.toComponent()));
|
||||||
//#else
|
//#else
|
||||||
//$$ this.setTitle(((FabricServerAudiences) plugin.getAudiences()).toNative(title.toComponent()));
|
//$$ this.setTitle(((FabricServerAudiences) plugin.getAudiences()).toNative(title.toComponent()));
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ org.gradle.daemon=true
|
|||||||
javaVersion=21
|
javaVersion=21
|
||||||
|
|
||||||
# Plugin metadata
|
# Plugin metadata
|
||||||
plugin_version=3.8.2
|
plugin_version=3.8
|
||||||
plugin_archive=husksync
|
plugin_archive=husksync
|
||||||
plugin_description=A modern, cross-server player data synchronization system
|
plugin_description=A modern, cross-server player data synchronization system
|
||||||
|
|
||||||
# General settings
|
# General settings
|
||||||
jedis_version=6.0.0
|
jedis_version=5.2.0
|
||||||
mysql_driver_version=9.3.0
|
mysql_driver_version=9.2.0
|
||||||
mariadb_driver_version=3.5.3
|
mariadb_driver_version=3.5.1
|
||||||
postgres_driver_version=42.7.5
|
postgres_driver_version=42.7.5
|
||||||
mongodb_driver_version=5.5.0
|
mongodb_driver_version=5.3.1
|
||||||
snappy_version=1.1.10.7
|
snappy_version=1.1.10.7
|
||||||
|
|
||||||
# Fabric settings
|
# Fabric settings
|
||||||
loom.ignoreDependencyLoomVersionValidation=true
|
fabric_loom_version=1.9-SNAPSHOT
|
||||||
@@ -13,7 +13,7 @@ from tqdm import tqdm
|
|||||||
class Parameters:
|
class Parameters:
|
||||||
root_dir = './servers/'
|
root_dir = './servers/'
|
||||||
proxy_version = "3.4.0-SNAPSHOT"
|
proxy_version = "3.4.0-SNAPSHOT"
|
||||||
minecraft_version = '1.21.5'
|
minecraft_version = '1.21.4'
|
||||||
eula_agreement = 'true'
|
eula_agreement = 'true'
|
||||||
|
|
||||||
backend_names = ['alpha', 'beta']
|
backend_names = ['alpha', 'beta']
|
||||||
|
|||||||
Reference in New Issue
Block a user