mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-26 01:59:20 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554fac89c0 | ||
|
|
215bed9908 | ||
|
|
935aafa74a | ||
|
|
c51ba85f38 | ||
|
|
6a67d1bbe0 | ||
|
|
20bc76a768 | ||
|
|
6928f97dff | ||
|
|
06742fb848 | ||
|
|
759983b000 | ||
|
|
5556e3b6ce | ||
|
|
bcffcb1f64 | ||
|
|
fa77e6e418 | ||
|
|
c8aa29c82f | ||
|
|
51cf982359 | ||
|
|
f6d860335f | ||
|
|
5cea4665a1 | ||
|
|
34b183a35e | ||
|
|
61298c24bb | ||
|
|
af9d32895e | ||
|
|
52fa67432c | ||
|
|
404f18d81f | ||
|
|
9ee8ea1c84 | ||
|
|
64f845e293 | ||
|
|
30d1acc67e |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -58,6 +58,7 @@ jobs:
|
|||||||
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
|
||||||
@@ -66,6 +67,7 @@ jobs:
|
|||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
|
fabric
|
||||||
distro-descriptions: |
|
distro-descriptions: |
|
||||||
Paper 1.20.1
|
Paper 1.20.1
|
||||||
Paper 1.21.1
|
Paper 1.21.1
|
||||||
@@ -74,6 +76,7 @@ jobs:
|
|||||||
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
|
||||||
@@ -81,4 +84,5 @@ jobs:
|
|||||||
target/HuskSync-Bukkit-${{ env.version_name }}+mc.1.21.5.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
|
||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -47,6 +47,7 @@ jobs:
|
|||||||
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
|
||||||
@@ -55,6 +56,7 @@ jobs:
|
|||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
fabric
|
fabric
|
||||||
|
fabric
|
||||||
distro-descriptions: |
|
distro-descriptions: |
|
||||||
Paper 1.20.1
|
Paper 1.20.1
|
||||||
Paper 1.21.1
|
Paper 1.21.1
|
||||||
@@ -63,6 +65,7 @@ jobs:
|
|||||||
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
|
||||||
@@ -70,4 +73,5 @@ jobs:
|
|||||||
target/HuskSync-Bukkit-${{ github.event.release.tag_name }}+mc.1.21.5.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
|
||||||
@@ -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 'fabric-loom' version "$fabric_loom_version" apply false
|
id 'dev.architectury.loom' version '1.9-SNAPSHOT' 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,7 +89,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(platform("org.junit:junit-bom:5.12.1"))
|
testImplementation(platform("org.junit:junit-bom:5.12.2"))
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
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: 'fabric-loom'
|
apply plugin: 'dev.architectury.loom'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ 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.3'
|
||||||
implementation 'net.william278.uniform:uniform-paper:1.3.3'
|
implementation 'net.william278.uniform:uniform-paper:1.3.4'
|
||||||
implementation 'net.william278.toilet:toilet-bukkit:1.0.12'
|
implementation 'net.william278.toilet:toilet-bukkit:1.0.13'
|
||||||
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.3.4'
|
implementation 'net.kyori:adventure-platform-bukkit:4.4.0'
|
||||||
implementation 'dev.triumphteam:triumph-gui:3.1.11'
|
implementation 'dev.triumphteam:triumph-gui:3.1.12'
|
||||||
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
||||||
implementation 'de.tr7zw:item-nbt-api:2.14.2-SNAPSHOT'
|
implementation 'de.tr7zw:item-nbt-api:2.15.0'
|
||||||
|
|
||||||
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'
|
||||||
@@ -27,7 +27,7 @@ dependencies {
|
|||||||
compileOnly 'commons-io:commons-io:2.19.0'
|
compileOnly 'commons-io:commons-io:2.19.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.5.0'
|
compileOnly 'de.exlll:configlib-yaml:4.6.1'
|
||||||
compileOnly 'com.zaxxer:HikariCP:6.3.0'
|
compileOnly 'com.zaxxer:HikariCP:6.3.0'
|
||||||
compileOnly 'net.william278:DesertWell:2.0.4'
|
compileOnly 'net.william278:DesertWell:2.0.4'
|
||||||
compileOnly 'net.william278:AdvancementAPI:97a9583413'
|
compileOnly 'net.william278:AdvancementAPI:97a9583413'
|
||||||
@@ -93,5 +93,9 @@ 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,6 +23,7 @@ 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;
|
||||||
@@ -92,9 +93,7 @@ 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 TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
private final HashMap<Identifier, Serializer<? extends Data>> serializers = Maps.newHashMap();
|
||||||
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();
|
||||||
@@ -149,6 +148,12 @@ 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)));
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ 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;
|
||||||
@@ -43,7 +44,9 @@ 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;
|
||||||
@@ -53,6 +56,8 @@ 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
|
||||||
@@ -97,11 +102,12 @@ 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) {
|
} else if (item.getItemMeta() instanceof BundleMeta bundle && bundle.hasItems()) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -260,21 +266,29 @@ 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) {
|
||||||
meta.setMapId(newId);
|
final MapView view = Bukkit.getMap(newId);
|
||||||
map.setItemMeta(meta);
|
if (view != null) {
|
||||||
getPlugin().debug(String.format("Map ID set to %s", newId));
|
meta.setMapView(view);
|
||||||
return;
|
meta.setMapId(newId);
|
||||||
|
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 and generate a map view otherwise
|
// Read the pixel data from the ItemStack 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...");
|
||||||
final @Nullable Map.Entry<MapData, Boolean> readMapData = readMapData(originServerName, originalMapId);
|
@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);
|
||||||
@@ -284,6 +298,7 @@ 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));
|
||||||
});
|
});
|
||||||
@@ -295,16 +310,21 @@ public interface BukkitMapHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable final Map.Entry<MapData, Boolean> data = readMapData(getPlugin().getServerName(), view.getId());
|
// Read map data, or
|
||||||
|
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +440,36 @@ 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}
|
||||||
*/
|
*/
|
||||||
|
|||||||
0
bukkit/src/main/resources/META-INF/.mojang-mapped
Normal file
0
bukkit/src/main/resources/META-INF/.mojang-mapped
Normal file
@@ -10,24 +10,24 @@ dependencies {
|
|||||||
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.13.0'
|
||||||
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.5.0'
|
api 'de.exlll:configlib-yaml:4.6.1'
|
||||||
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.3.0') {
|
||||||
exclude module: 'slf4j-api'
|
exclude module: 'slf4j-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOnlyApi 'net.william278.toilet:toilet-common:1.0.12'
|
compileOnlyApi 'net.william278.toilet:toilet-common:1.0.13'
|
||||||
|
|
||||||
compileOnly 'net.william278.uniform:uniform-common:1.3.3'
|
compileOnly 'net.william278.uniform:uniform-common:1.3.3'
|
||||||
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.38'
|
||||||
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.20.0'
|
||||||
compileOnly 'net.kyori:adventure-platform-api:4.3.4'
|
compileOnly 'net.kyori:adventure-platform-api:4.4.0'
|
||||||
compileOnly "net.kyori:adventure-text-serializer-plain:4.20.0"
|
compileOnly "net.kyori:adventure-text-serializer-plain:4.21.0"
|
||||||
compileOnly 'com.google.guava:guava:33.4.6-jre'
|
compileOnly 'com.google.guava:guava:33.4.8-jre'
|
||||||
compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272'
|
compileOnly 'com.github.plan-player-analytics:Plan:5.6.2965'
|
||||||
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,9 +37,9 @@ 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.6-jre'
|
testImplementation 'com.google.guava:guava:33.4.8-jre'
|
||||||
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
|
testImplementation 'com.github.plan-player-analytics:Plan:5.6.2965'
|
||||||
testCompileOnly 'de.exlll:configlib-yaml:4.5.0'
|
testCompileOnly 'de.exlll:configlib-yaml:4.6.1'
|
||||||
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.38'
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ 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,11 +215,17 @@ public class UserDataCommand extends PluginCommand {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private CommandProvider view() {
|
private CommandProvider view() {
|
||||||
return (sub) -> sub.addSyntax((ctx) -> {
|
return (sub) -> {
|
||||||
final User user = ctx.getArgument("username", User.class);
|
sub.addSyntax((ctx) -> {
|
||||||
final UUID version = ctx.getArgument("version", UUID.class);
|
final User user = ctx.getArgument("username", User.class);
|
||||||
viewSnapshot(user(sub, ctx), user, version);
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
}, user("username"), versionUuid());
|
viewSnapshot(user(sub, ctx), user, version);
|
||||||
|
}, user("username"), versionUuid());
|
||||||
|
sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
viewLatestSnapshot(user(sub, ctx), user);
|
||||||
|
}, user("username"));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ 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) {
|
||||||
return getData().entrySet().stream().filter(e -> e.getKey().equals(id)).map(Map.Entry::getValue).findFirst();
|
if (getData().containsKey(id)) {
|
||||||
|
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 TreeMap<Identifier, Data> deserialized;
|
private final Map<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 TreeMap<Identifier, Data> data,
|
@NotNull String saveCause, @NotNull String serverName, @NotNull Map<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,14 +389,15 @@ public class DataSnapshot {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
private TreeMap<Identifier, Data> deserializeData(@NotNull HuskSync plugin) {
|
private Map<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) -> b, () -> Maps.newTreeMap(SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR)
|
(a, b) -> a,
|
||||||
|
HashMap::new
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +407,9 @@ 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
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,6 +424,20 @@ 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
|
||||||
*
|
*
|
||||||
@@ -453,12 +470,12 @@ public class DataSnapshot {
|
|||||||
private String serverName;
|
private String serverName;
|
||||||
private boolean pinned;
|
private boolean pinned;
|
||||||
private OffsetDateTime timestamp;
|
private OffsetDateTime timestamp;
|
||||||
private final TreeMap<Identifier, Data> data;
|
private final Map<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.newTreeMap(SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR);
|
this.data = Maps.newHashMap();
|
||||||
this.timestamp = OffsetDateTime.now();
|
this.timestamp = OffsetDateTime.now();
|
||||||
this.id = UUID.randomUUID();
|
this.id = UUID.randomUUID();
|
||||||
this.serverName = plugin.getServerName();
|
this.serverName = plugin.getServerName();
|
||||||
|
|||||||
@@ -38,7 +38,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 {
|
public class Identifier implements Comparable<Identifier> {
|
||||||
|
|
||||||
// Namespace for built-in identifiers
|
// Namespace for built-in identifiers
|
||||||
private static final @KeyPattern String DEFAULT_NAMESPACE = "husksync";
|
private static final @KeyPattern String DEFAULT_NAMESPACE = "husksync";
|
||||||
@@ -276,6 +276,14 @@ public class 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,6 +26,7 @@ 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 {
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ public interface SerializerRegistry {
|
|||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
<T extends Data> TreeMap<Identifier, Serializer<T>> getSerializers();
|
<T extends Data> Map<Identifier, Serializer<T>> getSerializers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a data serializer for the given {@link Identifier}
|
* Register a data serializer for the given {@link Identifier}
|
||||||
@@ -87,8 +88,7 @@ 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()
|
return getSerializers().keySet().stream().filter(e -> e.toString().equals(key)).findFirst();
|
||||||
.filter(id -> id.getKey().asString().equals(key)).findFirst();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,9 +99,7 @@ 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 getSerializers().entrySet().stream()
|
return Optional.ofNullable(getSerializers().get(identifier));
|
||||||
.filter(entry -> entry.getKey().getKey().equals(identifier.getKey()))
|
|
||||||
.map(Map.Entry::getValue).findFirst();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,14 +151,14 @@ public interface SerializerRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the set of registered data types
|
* Get the list of registered data types, in dependency order
|
||||||
*
|
*
|
||||||
* @return the set of registered data types
|
* @return the list of registered data types
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
default Set<Identifier> getRegisteredDataTypes() {
|
default List<Identifier> getRegisteredDataTypes() {
|
||||||
return getSerializers().keySet();
|
return getSerializers().keySet().stream().sorted(DEPENDENCY_ORDER_COMPARATOR).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,6 +27,7 @@ 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
|
||||||
@@ -46,7 +47,11 @@ 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(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll);
|
.collect(Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
entry -> entry.getValue().get(),
|
||||||
|
(a, b) -> a, HashMap::new
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +126,7 @@ public interface UserDataHolder extends DataHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (Map.Entry<Identifier, Data> entry : unpacked.getData().entrySet()) {
|
for (Map.Entry<Identifier, Data> entry : unpacked.getSortedIterable()) {
|
||||||
final Identifier identifier = entry.getKey();
|
final Identifier identifier = entry.getKey();
|
||||||
if (!identifier.isEnabled()) {
|
if (!identifier.isEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ This plugin does not support the following software-Minecraft version combinatio
|
|||||||
## Incompatible plugins / mods
|
## Incompatible plugins / mods
|
||||||
Please note the following plugins / mods can cause issues with HuskSync:
|
Please note the following plugins / mods can cause issues with HuskSync:
|
||||||
|
|
||||||
* Restart plugins / mods are not supported. These will cause [player data to not save correctly when your server restarts](troubleshooting#issues-with-player-data-going-out-of-sync-during-a-server-restart) due to the way these plugins utilise bash scripts. It's important to understand that restart plugins don't actually restart yur server, they just trigger some (often unstable) process-killing scripting logic to occur!
|
* Restart plugins / mods are not supported. These will cause [player data to not save correctly when your server restarts](troubleshooting#issues-with-player-data-going-out-of-sync-during-a-server-restart) due to the way these plugins utilise bash scripts. It's important to understand that restart plugins don't actually restart your server, they just trigger some (often unstable) process-killing scripting logic to occur!
|
||||||
* Combat logging plugins / mods are not supported. Some have built-in support for HuskSync and should work as expected, but for others you may wish to modify the [[Event Priorities]]
|
* Combat logging plugins / mods are not supported. Some have built-in support for HuskSync and should work as expected, but for others you may wish to modify the [[Event Priorities]]
|
||||||
@@ -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.115.0+1.21.4
|
fabric_api_version=0.116.1+1.21.4
|
||||||
fabric_permissions_api_version=0.3.3
|
fabric_permissions_api_version=0.3.3
|
||||||
fabric_adventure_platform_version=6.2.0
|
fabric_adventure_platform_version=6.3.0
|
||||||
fabric_sgui_version=1.8.2+1.21.4
|
fabric_sgui_version=1.8.2+1.21.4
|
||||||
7
fabric/1.21.5/gradle.properties
Normal file
7
fabric/1.21.5/gradle.properties
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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
|
||||||
@@ -15,15 +15,22 @@ dependencies {
|
|||||||
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.3+${project.name}")
|
||||||
modImplementation include("net.william278.toilet:toilet-fabric:1.0.12+${project.name}")
|
modImplementation include("net.william278.toilet:toilet-fabric:1.0.13+${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')
|
implementation include('org.apache.commons:commons-pool2:2.12.1')
|
||||||
|
|
||||||
|
// Include driver deps due to no runtime dep loading support
|
||||||
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")
|
||||||
implementation include("org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version")
|
implementation include("org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version")
|
||||||
implementation include("redis.clients:jedis:$jedis_version")
|
|
||||||
implementation include("org.xerial.snappy:snappy-java:$snappy_version")
|
implementation include("org.xerial.snappy:snappy-java:$snappy_version")
|
||||||
|
implementation include("redis.clients:jedis:$jedis_version")
|
||||||
|
implementation include("redis.clients.authentication:redis-authx-core:0.1.1-beta2") // Redis dep
|
||||||
|
implementation include('org.apache.commons:commons-pool2:2.12.1') // Redis dep
|
||||||
|
|
||||||
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'
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.21.4
|
1.21.5
|
||||||
@@ -3,11 +3,13 @@ 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)
|
||||||
fabric12101.link(fabric12104, null)
|
fabric12104.link(fabric12105, null)
|
||||||
fabric12001.link(fabric12104, null)
|
fabric12101.link(fabric12105, null)
|
||||||
|
fabric12001.link(fabric12105, null)
|
||||||
}
|
}
|
||||||
@@ -102,9 +102,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
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 static final int VERSION1_21_5 = 4323;
|
||||||
|
|
||||||
private final TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
private final HashMap<Identifier, Serializer<? extends Data>> serializers = Maps.newHashMap();
|
||||||
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();
|
||||||
|
|||||||
@@ -188,7 +188,11 @@ 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]);
|
||||||
}
|
}
|
||||||
player.getInventory().selectedSlot = heldItemSlot;
|
//#if MC<12105
|
||||||
|
//$$ player.getInventory().selectedSlot = heldItemSlot;
|
||||||
|
//#else
|
||||||
|
player.getInventory().setSelectedSlot(heldItemSlot);
|
||||||
|
//#endif
|
||||||
player.playerScreenHandler.sendContentUpdates();
|
player.playerScreenHandler.sendContentUpdates();
|
||||||
player.getInventory().updateItems();
|
player.getInventory().updateItems();
|
||||||
}
|
}
|
||||||
@@ -552,13 +556,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().toString();
|
//$$ final String registryId = registry.getKey().getValue().value();
|
||||||
//$$ 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().toString();
|
final String registryId = registry.getKey().getValue().value();
|
||||||
if (registryId.equals("custom_stat")) {
|
if (registryId.equals("custom_stat")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -888,7 +892,11 @@ 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 {
|
||||||
user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byName(gameMode));
|
//#if MC<12105
|
||||||
|
//$$ user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byName(gameMode));
|
||||||
|
//#else
|
||||||
|
user.getPlayer().changeGameMode(net.minecraft.world.GameMode.byId(gameMode));
|
||||||
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,17 +76,29 @@ public abstract class FabricSerializer {
|
|||||||
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
||||||
final NbtCompound root;
|
final NbtCompound root;
|
||||||
try {
|
try {
|
||||||
root = StringNbtReader.parse(serialized);
|
//#if MC<12105
|
||||||
|
//$$ 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
|
||||||
final NbtCompound items = root.contains(ITEMS_TAG) ? root.getCompound(ITEMS_TAG) : null;
|
//#if MC<12105
|
||||||
|
//$$ 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.contains(HELD_ITEM_SLOT_TAG) ? root.getInt(HELD_ITEM_SLOT_TAG) : 0
|
root.getInt(HELD_ITEM_SLOT_TAG, 0)
|
||||||
);
|
);
|
||||||
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,7 +133,11 @@ public abstract class FabricSerializer {
|
|||||||
throws DeserializationException {
|
throws DeserializationException {
|
||||||
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
final FabricHuskSync plugin = (FabricHuskSync) getPlugin();
|
||||||
try {
|
try {
|
||||||
final NbtCompound items = StringNbtReader.parse(serialized);
|
//#if MC<12105
|
||||||
|
//$$ 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);
|
||||||
@@ -153,14 +169,26 @@ 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;
|
||||||
contents[compound.getInt("Slot")] = decodeNbt(element, registryManager);
|
int i = compound.getInt("Slot", -1);
|
||||||
|
if (i >= 0) {
|
||||||
|
contents[i] = decodeNbt(element, registryManager);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
plugin.debug(Arrays.toString(contents));
|
//#endif
|
||||||
|
|
||||||
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);
|
||||||
@@ -199,19 +227,37 @@ 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) {
|
||||||
final int size = items.getInt("size");
|
//#if MC<12105
|
||||||
final NbtList list = items.getList("items", NbtElement.COMPOUND_TYPE);
|
//$$ final int size = items.getInt("size");
|
||||||
|
//$$ 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++) {
|
||||||
if (list.getCompound(i) == null) {
|
final NbtCompound compound = list.getCompoundOrEmpty(i);
|
||||||
|
if (compound.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final NbtCompound compound = list.getCompound(i);
|
final int slot = compound.getInt("Slot", -1);
|
||||||
final int slot = compound.getInt("Slot");
|
if (slot >= 0) {
|
||||||
itemStacks[slot] = decodeNbt(upgradeItemData(list.getCompound(i), mcVersion, plugin), registryManager);
|
itemStacks[slot] = decodeNbt(upgradeItemData(compound, mcVersion, plugin), registryManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//#endif
|
||||||
return itemStacks;
|
return itemStacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,27 +79,41 @@ 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),
|
||||||
inventory.selectedSlot
|
//#if MC<12105
|
||||||
|
//$$ 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) {
|
||||||
final ItemStack[] combined = new ItemStack[inv.main.size() + inv.armor.size() + inv.offHand.size()];
|
//#if MC<12105
|
||||||
System.arraycopy(
|
//$$ final ItemStack[] combined = new ItemStack[inv.main.size() + inv.armor.size() + inv.offHand.size()];
|
||||||
inv.main.toArray(new ItemStack[0]), 0, combined,
|
//$$ System.arraycopy(
|
||||||
0, inv.main.size()
|
//$$ inv.main.toArray(new ItemStack[0]), 0, combined,
|
||||||
);
|
//$$ 0, inv.main.size()
|
||||||
System.arraycopy(
|
//$$ );
|
||||||
inv.armor.toArray(new ItemStack[0]), 0, combined,
|
//$$ System.arraycopy(
|
||||||
inv.main.size(), inv.armor.size()
|
//$$ inv.armor.toArray(new ItemStack[0]), 0, combined,
|
||||||
);
|
//$$ inv.main.size(), inv.armor.size()
|
||||||
System.arraycopy(
|
//$$ );
|
||||||
inv.offHand.toArray(new ItemStack[0]), 0, combined,
|
//$$ System.arraycopy(
|
||||||
inv.main.size() + inv.armor.size(), inv.offHand.size()
|
//$$ inv.offHand.toArray(new ItemStack[0]), 0, combined,
|
||||||
);
|
//$$ 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
|
||||||
|
|||||||
@@ -71,7 +71,11 @@ 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) {
|
||||||
int slot = packet.getSlot();
|
//#if MC<12105
|
||||||
|
//$$ int slot = packet.getSlot();
|
||||||
|
//#else
|
||||||
|
int slot = packet.slot();
|
||||||
|
//#endif
|
||||||
if (slot < 0) {
|
if (slot < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ org.gradle.daemon=true
|
|||||||
javaVersion=21
|
javaVersion=21
|
||||||
|
|
||||||
# Plugin metadata
|
# Plugin metadata
|
||||||
plugin_version=3.8
|
plugin_version=3.8.3
|
||||||
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=5.2.0
|
jedis_version=6.0.0
|
||||||
mysql_driver_version=9.2.0
|
mysql_driver_version=9.3.0
|
||||||
mariadb_driver_version=3.5.1
|
mariadb_driver_version=3.5.3
|
||||||
postgres_driver_version=42.7.5
|
postgres_driver_version=42.7.5
|
||||||
mongodb_driver_version=5.3.1
|
mongodb_driver_version=5.5.0
|
||||||
snappy_version=1.1.10.7
|
snappy_version=1.1.10.7
|
||||||
|
|
||||||
# Fabric settings
|
# Fabric settings
|
||||||
fabric_loom_version=1.9-SNAPSHOT
|
loom.ignoreDependencyLoomVersionValidation=true
|
||||||
@@ -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.4'
|
minecraft_version = '1.21.5'
|
||||||
eula_agreement = 'true'
|
eula_agreement = 'true'
|
||||||
|
|
||||||
backend_names = ['alpha', 'beta']
|
backend_names = ['alpha', 'beta']
|
||||||
|
|||||||
Reference in New Issue
Block a user