feat: Move flight status into its own data type, use lombok for data class (#273)

* refactor: use lombok, separate flight, close #191

* refactor: suppress some warnings

* refactor: suppress unused `from` warnings

* refactor: correct bad null-annotations on Items

* refactor: fix null annotation on `getStack`

* refactor: override methods for getting flight status

* docs: add deprecation docs for flight in gamemode data
feat/data-edit-commands
William 8 months ago committed by GitHub
parent be5d1128de
commit a35e83a424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -38,10 +38,7 @@ import net.william278.husksync.command.BukkitCommand;
import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Server;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.BukkitSerializer;
import net.william278.husksync.data.Data;
import net.william278.husksync.data.Identifier;
import net.william278.husksync.data.Serializer;
import net.william278.husksync.data.*;
import net.william278.husksync.database.Database;
import net.william278.husksync.database.MongoDbDatabase;
import net.william278.husksync.database.MySqlDatabase;
@ -144,12 +141,13 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
registerSerializer(Identifier.ENDER_CHEST, new BukkitSerializer.EnderChest(this));
registerSerializer(Identifier.ADVANCEMENTS, new BukkitSerializer.Advancements(this));
registerSerializer(Identifier.LOCATION, new BukkitSerializer.Location(this));
registerSerializer(Identifier.HEALTH, new BukkitSerializer.Health(this));
registerSerializer(Identifier.HUNGER, new BukkitSerializer.Hunger(this));
registerSerializer(Identifier.GAME_MODE, new BukkitSerializer.GameMode(this));
registerSerializer(Identifier.HEALTH, new BukkitSerializer.Json<>(this, BukkitData.Health.class));
registerSerializer(Identifier.HUNGER, new BukkitSerializer.Json<>(this, BukkitData.Hunger.class));
registerSerializer(Identifier.GAME_MODE, new BukkitSerializer.Json<>(this, BukkitData.GameMode.class));
registerSerializer(Identifier.FLIGHT_STATUS, new BukkitSerializer.Json<>(this, BukkitData.FlightStatus.class));
registerSerializer(Identifier.POTION_EFFECTS, new BukkitSerializer.PotionEffects(this));
registerSerializer(Identifier.STATISTICS, new BukkitSerializer.Statistics(this));
registerSerializer(Identifier.EXPERIENCE, new BukkitSerializer.Experience(this));
registerSerializer(Identifier.EXPERIENCE, new BukkitSerializer.Json<>(this, BukkitData.Experience.class));
registerSerializer(Identifier.PERSISTENT_DATA, new BukkitSerializer.PersistentData(this));
});
@ -168,7 +166,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
case MYSQL, MARIADB -> new MySqlDatabase(this);
case POSTGRES -> new PostgresDatabase(this);
case MONGO -> new MongoDbDatabase(this);
default -> throw new IllegalStateException("Invalid database type");
};
this.database.initialize();
});

@ -24,12 +24,12 @@ import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTPersistentDataContainer;
import lombok.*;
import net.william278.desertwell.util.ThrowingConsumer;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.user.BukkitUser;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.*;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.attribute.Attribute;
@ -44,6 +44,7 @@ import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.util.*;
import java.util.logging.Level;
@ -61,19 +62,20 @@ public abstract class BukkitData implements Data {
public abstract void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException;
@Getter
public static abstract class Items extends BukkitData implements Data.Items {
private final ItemStack[] contents;
private final @Nullable ItemStack @NotNull [] contents;
private Items(@NotNull ItemStack[] contents) {
private Items(@Nullable ItemStack @NotNull [] contents) {
this.contents = Arrays.stream(contents)
.map(i -> i == null || i.getType() == Material.AIR ? null : i)
.toArray(ItemStack[]::new);
}
@NotNull
@Nullable
@Override
public Stack[] getStack() {
public Stack @NotNull [] getStack() {
return Arrays.stream(contents)
.map(stack -> stack != null ? new Stack(
stack.getType().getKey().toString(),
@ -103,7 +105,7 @@ public abstract class BukkitData implements Data {
this.setContents(((BukkitData.Items) contents).getContents());
}
public void setContents(@NotNull ItemStack[] contents) {
public void setContents(@Nullable ItemStack @NotNull [] contents) {
// Ensure the array is the correct length for the inventory
if (contents.length != this.contents.length) {
contents = Arrays.copyOf(contents, this.contents.length);
@ -111,11 +113,6 @@ public abstract class BukkitData implements Data {
System.arraycopy(contents, 0, this.contents, 0, this.contents.length);
}
@NotNull
public ItemStack[] getContents() {
return contents;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BukkitData.Items items) {
@ -124,9 +121,13 @@ public abstract class BukkitData implements Data {
return false;
}
@Setter
@Getter
public static class Inventory extends BukkitData.Items implements Data.Items.Inventory {
public static final int INVENTORY_SLOT_COUNT = 41;
@Range(from = 0, to = 8)
private int heldItemSlot;
private Inventory(@NotNull ItemStack[] contents, int heldItemSlot) {
@ -168,19 +169,6 @@ public abstract class BukkitData implements Data {
}
}
@Override
public int getHeldItemSlot() {
return heldItemSlot;
}
@Override
public void setHeldItemSlot(int heldItemSlot) throws IllegalArgumentException {
if (heldItemSlot < 0 || heldItemSlot > 8) {
throw new IllegalArgumentException("Held item slot must be between 0 and 8");
}
this.heldItemSlot = heldItemSlot;
}
}
public static class EnderChest extends BukkitData.Items implements Data.Items.EnderChest {
@ -226,21 +214,20 @@ public abstract class BukkitData implements Data {
@Override
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
throw new NotImplementedException("A generic item array cannot be applied to a player");
throw new UnsupportedOperationException("A generic item array cannot be applied to a player");
}
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class PotionEffects extends BukkitData implements Data.PotionEffects {
private final Collection<PotionEffect> effects;
private PotionEffects(@NotNull Collection<PotionEffect> effects) {
this.effects = effects;
}
@NotNull
public static BukkitData.PotionEffects from(@NotNull Collection<PotionEffect> effects) {
return new BukkitData.PotionEffects(effects);
@ -297,21 +284,16 @@ public abstract class BukkitData implements Data {
.toList();
}
@NotNull
public Collection<PotionEffect> getEffects() {
return effects;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Advancements extends BukkitData implements Data.Advancements {
private List<Advancement> completed;
private Advancements(@NotNull List<Advancement> advancements) {
this.completed = advancements;
}
// Iterate through the server advancement set and add all advancements to the list
@NotNull
public static BukkitData.Advancements adapt(@NotNull Player player) {
@ -392,20 +374,14 @@ public abstract class BukkitData implements Data {
Bukkit.getServer().advancementIterator().forEachRemaining(consumer);
}
@NotNull
@Override
public List<Advancement> getCompleted() {
return completed;
}
@Override
public void setCompleted(@NotNull List<Advancement> completed) {
this.completed = completed;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Location extends BukkitData implements Data.Location, Adaptable {
@SerializedName("x")
private double x;
@SerializedName("y")
@ -419,19 +395,6 @@ public abstract class BukkitData implements Data {
@SerializedName("world")
private World world;
private Location(double x, double y, double z, float yaw, float pitch, @NotNull World world) {
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.pitch = pitch;
this.world = world;
}
@SuppressWarnings("unused")
private Location() {
}
@NotNull
public static BukkitData.Location from(double x, double y, double z,
float yaw, float pitch, @NotNull World world) {
@ -466,90 +429,17 @@ public abstract class BukkitData implements Data {
}
}
@Override
public double getX() {
return x;
}
@Override
public void setX(double x) {
this.x = x;
}
@Override
public double getY() {
return y;
}
@Override
public void setY(double y) {
this.y = y;
}
@Override
public double getZ() {
return z;
}
@Override
public void setZ(double z) {
this.z = z;
}
@Override
public float getYaw() {
return yaw;
}
@Override
public void setYaw(float yaw) {
this.yaw = yaw;
}
@Override
public float getPitch() {
return pitch;
}
@Override
public void setPitch(float pitch) {
this.pitch = pitch;
}
@NotNull
@Override
public World getWorld() {
return world;
}
@Override
public void setWorld(@NotNull World world) {
this.world = world;
}
}
// TODO: Consider using Paper's new-ish API for this instead (when it's merged)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Statistics extends BukkitData implements Data.Statistics {
private Map<Statistic, Integer> genericStatistics;
private Map<Statistic, Map<Material, Integer>> blockStatistics;
private Map<Statistic, Map<Material, Integer>> itemStatistics;
private Map<Statistic, Map<EntityType, Integer>> entityStatistics;
private Statistics(@NotNull Map<Statistic, Integer> genericStatistics,
@NotNull Map<Statistic, Map<Material, Integer>> blockStatistics,
@NotNull Map<Statistic, Map<Material, Integer>> itemStatistics,
@NotNull Map<Statistic, Map<EntityType, Integer>> entityStatistics) {
this.genericStatistics = genericStatistics;
this.blockStatistics = blockStatistics;
this.itemStatistics = itemStatistics;
this.entityStatistics = entityStatistics;
}
@SuppressWarnings("unused")
private Statistics() {
}
@NotNull
public static BukkitData.Statistics adapt(@NotNull Player player) {
return new BukkitData.Statistics(
@ -753,13 +643,11 @@ public abstract class BukkitData implements Data {
}
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class PersistentData extends BukkitData implements Data.PersistentData {
private final NBTCompound persistentData;
private PersistentData(@NotNull NBTCompound persistentData) {
this.persistentData = persistentData;
}
@NotNull
public static BukkitData.PersistentData adapt(@NotNull PersistentDataContainer persistentData) {
return new BukkitData.PersistentData(new NBTPersistentDataContainer(persistentData));
@ -779,13 +667,13 @@ public abstract class BukkitData implements Data {
container.mergeCompound(persistentData);
}
@NotNull
public NBTCompound getPersistentData() {
return persistentData;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Health extends BukkitData implements Data.Health, Adaptable {
@SerializedName("health")
private double health;
@ -794,16 +682,6 @@ public abstract class BukkitData implements Data {
@SerializedName("health_scale")
private double healthScale;
private Health(double health, double maxHealth, double healthScale) {
this.health = health;
this.maxHealth = maxHealth;
this.healthScale = healthScale;
}
@SuppressWarnings("unused")
private Health() {
}
@NotNull
public static BukkitData.Health from(double health, double maxHealth, double healthScale) {
return new BukkitData.Health(health, maxHealth, healthScale);
@ -881,38 +759,12 @@ public abstract class BukkitData implements Data {
);
}
@Override
public double getHealth() {
return health;
}
@Override
public void setHealth(double health) {
this.health = health;
}
@Override
public double getMaxHealth() {
return maxHealth;
}
@Override
public void setMaxHealth(double maxHealth) {
this.maxHealth = maxHealth;
}
@Override
public double getHealthScale() {
return healthScale;
}
@Override
public void setHealthScale(double healthScale) {
this.healthScale = healthScale;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Hunger extends BukkitData implements Data.Hunger, Adaptable {
@SerializedName("food_level")
@ -922,16 +774,6 @@ public abstract class BukkitData implements Data {
@SerializedName("exhaustion")
private float exhaustion;
private Hunger(int foodLevel, float saturation, float exhaustion) {
this.foodLevel = foodLevel;
this.saturation = saturation;
this.exhaustion = exhaustion;
}
@SuppressWarnings("unused")
private Hunger() {
}
@NotNull
public static BukkitData.Hunger adapt(@NotNull Player player) {
return from(player.getFoodLevel(), player.getSaturation(), player.getExhaustion());
@ -950,37 +792,12 @@ public abstract class BukkitData implements Data {
player.setExhaustion(exhaustion);
}
@Override
public int getFoodLevel() {
return foodLevel;
}
@Override
public void setFoodLevel(int foodLevel) {
this.foodLevel = foodLevel;
}
@Override
public float getSaturation() {
return saturation;
}
@Override
public void setSaturation(float saturation) {
this.saturation = saturation;
}
@Override
public float getExhaustion() {
return exhaustion;
}
@Override
public void setExhaustion(float exhaustion) {
this.exhaustion = exhaustion;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Experience extends BukkitData implements Data.Experience, Adaptable {
@SerializedName("total_experience")
@ -992,16 +809,6 @@ public abstract class BukkitData implements Data {
@SerializedName("exp_progress")
private float expProgress;
private Experience(int totalExperience, int expLevel, float expProgress) {
this.totalExperience = totalExperience;
this.expLevel = expLevel;
this.expProgress = expProgress;
}
@SuppressWarnings("unused")
private Experience() {
}
@NotNull
public static BukkitData.Experience from(int totalExperience, int expLevel, float expProgress) {
return new BukkitData.Experience(totalExperience, expLevel, expProgress);
@ -1020,100 +827,67 @@ public abstract class BukkitData implements Data {
player.setExp(expProgress);
}
@Override
public int getTotalExperience() {
return totalExperience;
}
@Override
public void setTotalExperience(int totalExperience) {
this.totalExperience = totalExperience;
}
@Override
public int getExpLevel() {
return expLevel;
}
@Override
public void setExpLevel(int expLevel) {
this.expLevel = expLevel;
}
@Override
public float getExpProgress() {
return expProgress;
}
@Override
public void setExpProgress(float expProgress) {
this.expProgress = expProgress;
}
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class GameMode extends BukkitData implements Data.GameMode, Adaptable {
@SerializedName("game_mode")
private String gameMode;
@SerializedName("allow_flight")
private boolean allowFlight;
@SerializedName("is_flying")
private boolean isFlying;
private GameMode(@NotNull String gameMode, boolean allowFlight, boolean isFlying) {
this.gameMode = gameMode;
this.allowFlight = allowFlight;
this.isFlying = isFlying;
@NotNull
public static BukkitData.GameMode from(@NotNull String gameMode) {
return new BukkitData.GameMode(gameMode);
}
@NotNull
@Deprecated(forRemoval = true, since = "3.5")
@SuppressWarnings("unused")
public static BukkitData.GameMode from(@NotNull String gameMode, boolean allowFlight, boolean isFlying) {
return new BukkitData.GameMode(gameMode, allowFlight, isFlying);
return new BukkitData.GameMode(gameMode);
}
@NotNull
public static BukkitData.GameMode adapt(@NotNull Player player) {
return from(player.getGameMode().name(), player.getAllowFlight(), player.isFlying());
return from(player.getGameMode().name());
}
@Override
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
final Player player = user.getPlayer();
player.setGameMode(org.bukkit.GameMode.valueOf(gameMode));
player.setAllowFlight(allowFlight);
player.setFlying(allowFlight && isFlying);
user.getPlayer().setGameMode(org.bukkit.GameMode.valueOf(gameMode));
}
@NotNull
@Override
public String getGameMode() {
return gameMode;
}
@Override
public void setGameMode(@NotNull String gameMode) {
this.gameMode = gameMode;
}
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class FlightStatus extends BukkitData implements Data.FlightStatus, Adaptable {
@Override
public boolean getAllowFlight() {
return allowFlight;
}
@SerializedName("allow_flight")
private boolean allowFlight;
@SerializedName("is_flying")
private boolean flying;
@Override
public void setAllowFlight(boolean allowFlight) {
this.allowFlight = allowFlight;
@NotNull
public static BukkitData.FlightStatus from(boolean allowFlight, boolean flying) {
return new BukkitData.FlightStatus(allowFlight, allowFlight && flying);
}
@Override
public boolean getIsFlying() {
return isFlying;
@NotNull
public static BukkitData.FlightStatus adapt(@NotNull Player player) {
return from(player.getAllowFlight(), player.isFlying());
}
@Override
public void setIsFlying(boolean isFlying) {
this.isFlying = isFlying;
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
final Player player = user.getPlayer();
player.setAllowFlight(allowFlight);
player.setFlying(allowFlight && flying);
}
}

@ -23,6 +23,8 @@ import com.google.gson.reflect.TypeToken;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.api.HuskSyncAPI;
@ -34,14 +36,11 @@ import java.util.List;
import static net.william278.husksync.data.BukkitData.Items.Inventory.INVENTORY_SLOT_COUNT;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSerializer {
protected final HuskSync plugin;
private BukkitSerializer(@NotNull HuskSync plugin) {
this.plugin = plugin;
}
@SuppressWarnings("unused")
public BukkitSerializer(@NotNull HuskSyncAPI api) {
this.plugin = api.getPlugin();
@ -208,43 +207,11 @@ public class BukkitSerializer {
}
public static class Health extends Json<BukkitData.Health> implements Serializer<BukkitData.Health> {
public Health(@NotNull HuskSync plugin) {
super(plugin, BukkitData.Health.class);
}
}
public static class Hunger extends Json<BukkitData.Hunger> implements Serializer<BukkitData.Hunger> {
public Hunger(@NotNull HuskSync plugin) {
super(plugin, BukkitData.Hunger.class);
}
}
public static class Experience extends Json<BukkitData.Experience> implements Serializer<BukkitData.Experience> {
public Experience(@NotNull HuskSync plugin) {
super(plugin, BukkitData.Experience.class);
}
}
public static class GameMode extends Json<BukkitData.GameMode> implements Serializer<BukkitData.GameMode> {
public GameMode(@NotNull HuskSync plugin) {
super(plugin, BukkitData.GameMode.class);
}
}
public static abstract class Json<T extends Data & Adaptable> extends BukkitSerializer implements Serializer<T> {
public static class Json<T extends Data & Adaptable> extends BukkitSerializer implements Serializer<T> {
private final Class<T> type;
protected Json(@NotNull HuskSync plugin, Class<T> type) {
public Json(@NotNull HuskSync plugin, Class<T> type) {
super(plugin);
this.type = type;
}

@ -128,6 +128,12 @@ public interface BukkitUserDataHolder extends UserDataHolder {
return Optional.of(BukkitData.GameMode.adapt(getBukkitPlayer()));
}
@NotNull
@Override
default Optional<Data.FlightStatus> getFlightStatus() {
return Optional.of(BukkitData.FlightStatus.adapt(getBukkitPlayer()));
}
@NotNull
@Override
default Optional<Data.PersistentData> getPersistentData() {

@ -40,11 +40,7 @@ import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
import org.bukkit.event.player.*;
import org.bukkit.event.server.MapInitializeEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.inventory.ItemStack;

@ -334,7 +334,8 @@ public class LegacyMigrator extends Migrator {
.health(BukkitData.Health.from(health, maxHealth, healthScale))
.hunger(BukkitData.Hunger.from(hunger, saturation, saturationExhaustion))
.experience(BukkitData.Experience.from(totalExp, expLevel, expProgress))
.gameMode(BukkitData.GameMode.from(gameMode, isFlying, isFlying))
.gameMode(BukkitData.GameMode.from(gameMode))
.flightStatus(BukkitData.FlightStatus.from(isFlying, isFlying))
// Build & pack into new format
.saveCause(DataSnapshot.SaveCause.LEGACY_MIGRATION).buildAndPack();

@ -320,7 +320,7 @@ public class MpdbMigrator extends Migrator {
.inventory(BukkitData.Items.Inventory.from(inventory.getContents(), 0))
.enderChest(BukkitData.Items.EnderChest.adapt(enderChest))
.experience(BukkitData.Experience.from(totalExp, expLevel, expProgress))
.gameMode(BukkitData.GameMode.from("SURVIVAL", false, false))
.gameMode(BukkitData.GameMode.from("SURVIVAL"))
.saveCause(DataSnapshot.SaveCause.MPDB_MIGRATION)
.buildAndPack();
}

@ -107,7 +107,11 @@ public class BukkitLegacyConverter extends LegacyConverter {
}
if (shouldImport(Identifier.GAME_MODE)) {
containers.put(Identifier.GAME_MODE, BukkitData.GameMode.from(
status.getString("game_mode"),
status.getString("game_mode")
));
}
if (shouldImport(Identifier.FLIGHT_STATUS)) {
containers.put(Identifier.FLIGHT_STATUS, BukkitData.FlightStatus.from(
status.getBoolean("is_flying"),
status.getBoolean("is_flying")
));

@ -37,6 +37,7 @@ import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.*;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.File;
@ -75,8 +76,8 @@ public interface BukkitMapPersister {
* @param items the array of {@link ItemStack}s to apply persisted locked maps to
* @return the array of {@link ItemStack}s with persisted locked maps applied
*/
@NotNull
default ItemStack[] setMapViews(@NotNull ItemStack[] items) {
@Nullable
default ItemStack @NotNull [] setMapViews(@Nullable ItemStack @NotNull [] items) {
if (!getPlugin().getSettings().getSynchronization().isPersistLockedMaps()) {
return items;
}

@ -51,8 +51,8 @@ public interface Data {
*/
interface Items extends Data {
@NotNull
Stack[] getStack();
@Nullable
Stack @NotNull [] getStack();
default int getSlotCount() {
return getStack().length;
@ -76,6 +76,9 @@ public interface Data {
*/
interface Inventory extends Items {
String ITEMS_TAG = "items";
String HELD_ITEM_SLOT_TAG = "held_item_slot";
int getHeldItemSlot();
void setHeldItemSlot(int heldItemSlot) throws IllegalArgumentException;
@ -341,12 +344,7 @@ public interface Data {
}
/**
* A data container holding data for:
* <ul>
* <li>Game mode</li>
* <li>Allow flight</li>
* <li>Is flying</li>
* </ul>
* Data container holding data for the player's current game mode
*/
interface GameMode extends Data {
@ -355,13 +353,65 @@ public interface Data {
void setGameMode(@NotNull String gameMode);
boolean getAllowFlight();
/**
* Get if the player can fly.
*
* @return {@code false} since v3.5
* @deprecated Moved to its own data type. This will always return {@code false}.
* Use {@link FlightStatus#isAllowFlight()} instead
*/
@Deprecated(forRemoval = true, since = "3.5")
default boolean getAllowFlight() {
return false;
}
/**
* Set if the player can fly.
*
* @deprecated Moved to its own data type.
* Use {@link FlightStatus#setAllowFlight(boolean)} instead
*/
@Deprecated(forRemoval = true, since = "3.5")
default void setAllowFlight(boolean allowFlight) {
}
/**
* Get if the player is flying.
*
* @return {@code false} since v3.5
* @deprecated Moved to its own data type. This will always return {@code false}.
* Use {@link FlightStatus#isFlying()} instead
*/
@Deprecated(forRemoval = true, since = "3.5")
default boolean getIsFlying() {
return false;
}
/**
* Set if the player is flying.
*
* @deprecated Moved to its own data type.
* Use {@link FlightStatus#setFlying(boolean)} instead
*/
@Deprecated(forRemoval = true, since = "3.5")
default void setIsFlying(boolean isFlying) {
}
}
/**
* Data container holding data for the player's flight status
*
* @since 3.5
*/
interface FlightStatus extends Data {
boolean isAllowFlight();
void setAllowFlight(boolean allowFlight);
boolean getIsFlying();
boolean isFlying();
void setIsFlying(boolean isFlying);
void setFlying(boolean isFlying);
}

@ -128,6 +128,15 @@ public interface DataHolder {
getData().put(Identifier.GAME_MODE, gameMode);
}
@NotNull
default Optional<Data.FlightStatus> getFlightStatus() {
return Optional.ofNullable((Data.FlightStatus) getData().get(Identifier.FLIGHT_STATUS));
}
default void setFlightStatus(@NotNull Data.FlightStatus flightStatus) {
getData().put(Identifier.FLIGHT_STATUS, flightStatus);
}
@NotNull
default Optional<Data.PersistentData> getPersistentData() {
return Optional.ofNullable((Data.PersistentData) getData().get(Identifier.PERSISTENT_DATA));

@ -47,6 +47,7 @@ import java.util.stream.Collectors;
*
* @since 3.0
*/
@SuppressWarnings({"LombokSetterMayBeUsed", "LombokGetterMayBeUsed"})
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataSnapshot {
@ -688,6 +689,21 @@ public class DataSnapshot {
return data(Identifier.GAME_MODE, gameMode);
}
/**
* Set the flight status of the snapshot
* <p>
* Equivalent to {@code data(Identifier.FLIGHT_STATUS, flightStatus)}
* </p>
*
* @param flightStatus The flight status
* @return The builder
* @since 3.5
*/
@NotNull
public Builder flightStatus(@NotNull Data.FlightStatus flightStatus) {
return data(Identifier.FLIGHT_STATUS, flightStatus);
}
/**
* Set the persistent data container of the snapshot
* <p>

@ -43,6 +43,7 @@ public class Identifier {
public static Identifier HUNGER = huskSync("hunger", true);
public static Identifier EXPERIENCE = huskSync("experience", true);
public static Identifier GAME_MODE = huskSync("game_mode", true);
public static Identifier FLIGHT_STATUS = huskSync("flight_status", true);
public static Identifier PERSISTENT_DATA = huskSync("persistent_data", true);
private final Key key;
@ -114,7 +115,7 @@ public class Identifier {
public static Map<String, Boolean> getConfigMap() {
return Map.ofEntries(Stream.of(
INVENTORY, ENDER_CHEST, POTION_EFFECTS, ADVANCEMENTS, LOCATION,
STATISTICS, HEALTH, HUNGER, EXPERIENCE, GAME_MODE, PERSISTENT_DATA
STATISTICS, HEALTH, HUNGER, EXPERIENCE, GAME_MODE, FLIGHT_STATUS, PERSISTENT_DATA
)
.map(Identifier::getConfigEntry)
.toArray(Map.Entry[]::new));

@ -171,6 +171,11 @@ public interface UserDataHolder extends DataHolder {
this.setData(Identifier.GAME_MODE, gameMode);
}
@Override
default void setFlightStatus(@NotNull Data.FlightStatus flightStatus) {
this.setData(Identifier.FLIGHT_STATUS, flightStatus);
}
@Override
default void setPersistentData(@NotNull Data.PersistentData persistentData) {
this.setData(Identifier.PERSISTENT_DATA, persistentData);

@ -28,7 +28,6 @@ import net.william278.husksync.user.User;
import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.*;
import java.time.OffsetDateTime;

@ -3,7 +3,7 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true
javaVersion=17
plugin_version=3.4.1
plugin_version=3.5
plugin_archive=husksync
plugin_description=A modern, cross-server player data synchronization system

Loading…
Cancel
Save