mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-24 09:09:18 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d860335f | ||
|
|
5cea4665a1 | ||
|
|
34b183a35e | ||
|
|
61298c24bb | ||
|
|
af9d32895e | ||
|
|
52fa67432c | ||
|
|
404f18d81f | ||
|
|
9ee8ea1c84 | ||
|
|
64f845e293 | ||
|
|
30d1acc67e |
@@ -89,7 +89,7 @@ allprojects {
|
||||
}
|
||||
|
||||
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'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
||||
|
||||
@@ -16,9 +16,9 @@ dependencies {
|
||||
implementation 'net.william278:mapdataapi:2.0'
|
||||
implementation 'org.bstats:bstats-bukkit:3.1.0'
|
||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
||||
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 '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 'com.github.retrooper:packetevents-spigot:2.7.0'
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
@@ -149,6 +150,12 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
||||
// Check compatibility
|
||||
checkCompatibility();
|
||||
|
||||
// Preload NBT-API
|
||||
if (!NBT.preloadApi()) {
|
||||
log(Level.SEVERE, "Failed to load NBT API. HuskSync will not be initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Register commands
|
||||
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 de.tr7zw.changeme.nbtapi.NBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableItemNBT;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
|
||||
import net.william278.husksync.BukkitHuskSync;
|
||||
import net.william278.husksync.redis.RedisManager;
|
||||
@@ -43,7 +44,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -53,6 +56,8 @@ public interface BukkitMapHandler {
|
||||
|
||||
// The map used to store HuskSync data in ItemStack NBT
|
||||
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
|
||||
String MAP_ORIGIN_KEY = "origin";
|
||||
// Original map id
|
||||
@@ -97,11 +102,12 @@ public interface BukkitMapHandler {
|
||||
}
|
||||
if (item.getType() == Material.FILLED_MAP && item.hasItemMeta()) {
|
||||
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);
|
||||
b.setBlockState(box);
|
||||
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)));
|
||||
item.setItemMeta(bundle);
|
||||
}
|
||||
@@ -260,21 +266,29 @@ public interface BukkitMapHandler {
|
||||
int newId = currentServerName.equals(originServerName)
|
||||
? originalMapId : getBoundMapId(originServerName, originalMapId, currentServerName);
|
||||
if (newId != -1) {
|
||||
meta.setMapId(newId);
|
||||
map.setItemMeta(meta);
|
||||
getPlugin().debug(String.format("Map ID set to %s", newId));
|
||||
return;
|
||||
final MapView view = Bukkit.getMap(newId);
|
||||
if (view != null) {
|
||||
meta.setMapView(view);
|
||||
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...");
|
||||
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) {
|
||||
getPlugin().debug("Read pixel data was not found in database, skipping...");
|
||||
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 MapView view = generateRenderedMap(canvasData);
|
||||
meta.setMapView(view);
|
||||
@@ -284,6 +298,7 @@ public interface BukkitMapHandler {
|
||||
final int id = view.getId();
|
||||
getRedisManager().bindMapIds(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));
|
||||
});
|
||||
@@ -295,16 +310,21 @@ public interface BukkitMapHandler {
|
||||
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) {
|
||||
final World world = view.getWorld() == null ? getDefaultMapWorld() : view.getWorld();
|
||||
getPlugin().debug("Not rendering map: no data in DB for world %s, map #%s."
|
||||
.formatted(world.getName(), view.getId()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't render persisted maps on this server
|
||||
if (data.getValue()) {
|
||||
// from this server, doesn't need tweaking
|
||||
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}
|
||||
*/
|
||||
|
||||
0
bukkit/src/main/resources/META-INF/.mojang-mapped
Normal file
0
bukkit/src/main/resources/META-INF/.mojang-mapped
Normal file
@@ -26,7 +26,7 @@ dependencies {
|
||||
compileOnly 'net.kyori:adventure-api:4.20.0'
|
||||
compileOnly 'net.kyori:adventure-platform-api:4.3.4'
|
||||
compileOnly "net.kyori:adventure-text-serializer-plain:4.20.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 "redis.clients:jedis:$jedis_version"
|
||||
compileOnly "com.mysql:mysql-connector-j:$mysql_driver_version"
|
||||
@@ -37,7 +37,7 @@ dependencies {
|
||||
|
||||
testImplementation "redis.clients:jedis:$jedis_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'
|
||||
testCompileOnly 'de.exlll:configlib-yaml:4.5.0'
|
||||
testCompileOnly 'org.jetbrains:annotations:26.0.2'
|
||||
|
||||
@@ -215,11 +215,17 @@ public class UserDataCommand extends PluginCommand {
|
||||
|
||||
@NotNull
|
||||
private CommandProvider view() {
|
||||
return (sub) -> sub.addSyntax((ctx) -> {
|
||||
final User user = ctx.getArgument("username", User.class);
|
||||
final UUID version = ctx.getArgument("version", UUID.class);
|
||||
viewSnapshot(user(sub, ctx), user, version);
|
||||
}, user("username"), versionUuid());
|
||||
return (sub) -> {
|
||||
sub.addSyntax((ctx) -> {
|
||||
final User user = ctx.getArgument("username", User.class);
|
||||
final UUID version = ctx.getArgument("version", UUID.class);
|
||||
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
|
||||
|
||||
@@ -31,7 +31,10 @@ public interface DataHolder {
|
||||
Map<Identifier, Data> getData();
|
||||
|
||||
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) {
|
||||
|
||||
@@ -406,7 +406,9 @@ public class DataSnapshot {
|
||||
return deserialized.entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
entry -> entry.getKey().toString(),
|
||||
entry -> plugin.serializeData(entry.getKey(), entry.getValue())
|
||||
entry -> plugin.serializeData(entry.getKey(), entry.getValue()),
|
||||
(a, b) -> a,
|
||||
HashMap::new
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.stream.Stream;
|
||||
* Identifiers of different types of {@link Data}s
|
||||
*/
|
||||
@Getter
|
||||
public class Identifier {
|
||||
public class Identifier implements Comparable<Identifier> {
|
||||
|
||||
// Namespace for built-in identifiers
|
||||
private static final @KeyPattern String DEFAULT_NAMESPACE = "husksync";
|
||||
@@ -276,6 +276,14 @@ public class Identifier {
|
||||
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.
|
||||
* <p>
|
||||
|
||||
@@ -26,7 +26,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
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
|
||||
@@ -46,7 +48,11 @@ public interface UserDataHolder extends DataHolder {
|
||||
.filter(Identifier::isEnabled)
|
||||
.map(id -> Map.entry(id, getData(id)))
|
||||
.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
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,16 +4,16 @@ org.gradle.daemon=true
|
||||
javaVersion=21
|
||||
|
||||
# Plugin metadata
|
||||
plugin_version=3.8
|
||||
plugin_version=3.8.1
|
||||
plugin_archive=husksync
|
||||
plugin_description=A modern, cross-server player data synchronization system
|
||||
|
||||
# General settings
|
||||
jedis_version=5.2.0
|
||||
mysql_driver_version=9.2.0
|
||||
mariadb_driver_version=3.5.1
|
||||
jedis_version=6.0.0
|
||||
mysql_driver_version=9.3.0
|
||||
mariadb_driver_version=3.5.3
|
||||
postgres_driver_version=42.7.5
|
||||
mongodb_driver_version=5.3.1
|
||||
mongodb_driver_version=5.5.0
|
||||
snappy_version=1.1.10.7
|
||||
|
||||
# Fabric settings
|
||||
|
||||
@@ -13,7 +13,7 @@ from tqdm import tqdm
|
||||
class Parameters:
|
||||
root_dir = './servers/'
|
||||
proxy_version = "3.4.0-SNAPSHOT"
|
||||
minecraft_version = '1.21.4'
|
||||
minecraft_version = '1.21.5'
|
||||
eula_agreement = 'true'
|
||||
|
||||
backend_names = ['alpha', 'beta']
|
||||
|
||||
Reference in New Issue
Block a user