Add the ability to synchronise/persist locked maps cross-server, close #14

feat/data-edit-commands
William278 2 years ago
parent 945b65e1bc
commit c0709f82bd

@ -3,6 +3,7 @@ dependencies {
implementation 'org.bstats:bstats-bukkit:3.0.0'
implementation 'net.william278:mpdbdataconverter:1.0.1'
implementation 'net.william278:hsldataconverter:1.0'
implementation 'net.william278:MapDataAPI:1.0'
implementation 'me.lucko:commodore:2.2'
implementation 'net.kyori:adventure-platform-bukkit:4.1.2'
implementation 'dev.triumphteam:triumph-gui:3.1.3'
@ -31,6 +32,8 @@ shadowJar {
relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
relocate 'net.querz', 'net.william278.husksync.libraries.nbt'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'

@ -0,0 +1,121 @@
package net.william278.husksync.data;
import net.william278.husksync.BukkitHuskSync;
import net.william278.mapdataapi.MapData;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.Objects;
import java.util.logging.Level;
/**
* Handles the persistence of {@link MapData} into {@link ItemStack}s.
*/
public class BukkitMapHandler {
private static final BukkitHuskSync plugin = BukkitHuskSync.getInstance();
private static final NamespacedKey MAP_DATA_KEY = new NamespacedKey(plugin, "map_data");
/**
* Get the {@link MapData} from the given {@link ItemStack} and persist it in its' data container
*
* @param itemStack the {@link ItemStack} to get the {@link MapData} from
*/
public static void persistMapData(@NotNull ItemStack itemStack) {
if (itemStack.getType() != Material.FILLED_MAP) {
return;
}
final MapMeta mapMeta = (MapMeta) itemStack.getItemMeta();
if (mapMeta == null || !mapMeta.hasMapView()) {
return;
}
// Get the map view
final MapView mapView = mapMeta.getMapView();
if (mapView == null || !mapView.isLocked() || mapView.isVirtual()) {
return;
}
final int mapId = mapView.getId();
if (mapId < 0) {
return;
}
// Get the map data
try {
if (!itemStack.getItemMeta().getPersistentDataContainer().has(MAP_DATA_KEY, PersistentDataType.STRING)) {
itemStack.getItemMeta().getPersistentDataContainer().set(MAP_DATA_KEY, PersistentDataType.STRING,
MapData.getFromFile(Bukkit.getWorlds().get(0).getWorldFolder(), mapId).toString());
}
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "Failed to serialize map data for map " + mapId + ")");
}
}
/**
* Set the map data of the given {@link ItemStack} to the given {@link MapData}, applying a map view to the item stack
*
* @param itemStack the {@link ItemStack} to set the map data of
*/
public static void setMapRenderer(@NotNull ItemStack itemStack) {
if (itemStack.getType() != Material.FILLED_MAP) {
return;
}
final MapMeta mapMeta = (MapMeta) itemStack.getItemMeta();
if (mapMeta == null) {
return;
}
if (!itemStack.getItemMeta().getPersistentDataContainer().has(MAP_DATA_KEY, PersistentDataType.STRING)) {
return;
}
try {
final String serializedData = Objects.requireNonNull(itemStack
.getItemMeta().getPersistentDataContainer().get(MAP_DATA_KEY, PersistentDataType.STRING));
final MapData mapData = MapData.fromString(serializedData);
// Create a new map view renderer with the map data color at each pixel
final MapView mapView = mapMeta.getMapView();
if (mapView == null) {
return;
}
mapView.getRenderers().forEach(mapView::removeRenderer);
mapView.addRenderer(new BukkitMapDataRenderer(mapData));
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "Failed to deserialize map data for a player");
}
}
/**
* Renders {@link MapData} to a bukkit {@link MapView}.
*/
public static class BukkitMapDataRenderer extends MapRenderer {
private final MapData mapData;
protected BukkitMapDataRenderer(@NotNull MapData mapData) {
this.mapData = mapData;
}
@Override
public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) {
for (int i = 0; i < 128; i++) {
for (int j = 0; j < 128; j++) {
canvas.setPixel(i, j, (byte) mapData.getColorAt(i, j).intValue());
}
}
map.setLocked(true);
}
}
}

@ -1,6 +1,7 @@
package net.william278.husksync.data;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.config.Settings;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.util.io.BukkitObjectInputStream;
@ -40,7 +41,11 @@ public class BukkitSerializer {
bukkitOutputStream.writeInt(inventoryContents.length);
// Write each serialize each ItemStack to the output stream
final boolean persistLockedMaps = BukkitHuskSync.getInstance().getSettings().getSynchronizationFeature(Settings.SynchronizationFeature.LOCKED_MAPS);
for (ItemStack inventoryItem : inventoryContents) {
if (persistLockedMaps) {
BukkitMapHandler.persistMapData(inventoryItem);
}
bukkitOutputStream.writeObject(serializeItemStack(inventoryItem));
}
@ -89,8 +94,13 @@ public class BukkitSerializer {
// Set the ItemStacks in the array from deserialized ItemStack data
int slotIndex = 0;
final boolean persistLockedMaps = BukkitHuskSync.getInstance().getSettings().getSynchronizationFeature(Settings.SynchronizationFeature.LOCKED_MAPS);
for (ItemStack ignored : inventoryContents) {
inventoryContents[slotIndex] = deserializeItemStack(bukkitInputStream.readObject());
final ItemStack deserialized = deserializeItemStack(bukkitInputStream.readObject());
if (persistLockedMaps) {
BukkitMapHandler.setMapRenderer(deserialized);
}
inventoryContents[slotIndex] = deserialized;
slotIndex++;
}

@ -180,7 +180,8 @@ public class Settings {
GAME_MODE(true),
STATISTICS(true),
PERSISTENT_DATA_CONTAINER(false),
LOCATION(false);
LOCATION(false),
LOCKED_MAPS(true);
private final boolean enabledByDefault;

Loading…
Cancel
Save