Setup
Features
- 🖥️ Commands
- ✅ Sync Features
- ⚙️ Sync Modes
- ↪️ Data Rotation
- ❓ FAQs
Guides
- ↗️ Legacy Migration
- ✨ MPDB Migration
- ☂️ Dumping UserData
- 🟩 Plan Hook
- 📋 Event Priorities
- ⚔️ Keep Inventory
- 🎏 Translations
HuskSync v3.0 provides an API for getting, creating, editing and deleting DataSnapshot
s; a snapshot of a user at a given moment in time. This page will walk you through how to manipulate delete snapshots using the HuskSync API.
This page assumes you have read the general API introduction and that you have both imported HuskSync (v3.x) into your project and added it as a dependency.
User
object, representing a user saved in the database. You can retrieve a user using HuskSyncAPI#getUser(uuid)
// getUser() returns a CompletableFuture supplying an Optional<User>
huskSyncAPI.getUser(uuid).thenAccept(optionalUser -> {
// Check if we found a user by that UUID either online or on the database
if (optionalUser.isEmpty()) {
// If we didn't, we'll log that they don't exist
System.out.println("User does not exist!");
return;
}
// The User object provides methods for getting a user's UUID and username
System.out.println("Found %s", optionalUser.get().getUsername());
});
org.bukkit.Player
object, you can use BukkitPlayer#adapt(player)
to get an OnlineUser
(extends User
), representing a logged-in user.#getOnlineUser(UUID)
to get an OnlineUser by their UUID - note this only works for players online on the server the logic is called from, however.// Get an online user
OnlineUser user = huskSyncAPI.getUser(player);
System.out.println("Hello, %s!", user.getUsername());
HuskSyncAPI#getCurrentData(User)
returns a CompletableFuture
supplying an Optional<DataSnapshot.Unpacked>
.// Get a user's current data
huskSyncAPI.getCurrentData(user).thenAccept(optionalSnapshot -> {
if (optionalSnapshot.isEmpty()) {
System.out.println("Couldn't get data for %s", user.getUsername());
return;
}
// Get the snapshot, which you can then do stuff with
DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
});
HuskSyncAPI#getLatestSnapshot(User)
instead:// Get a user's latest saved snapshot
huskSyncAPI.getLatestSnapshot(user).thenAccept(optionalSnapshot -> {
if (optionalSnapshot.isEmpty()) {
System.out.println("%s has no saved snapshots!", user.getUsername());
return;
}
// Get the snapshot, which you can then do stuff with
DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
});
HuskSyncAPI#getSnapshots(User)
. This method returns a CompletableFuture
supplying an Optional<List<DataSnapshot.Unpacked>>
.// Get a user's saved snapshots
huskSyncAPI.getSnapshots(user).thenAccept(optionalSnapshots -> {
if (optionalSnapshots.isEmpty()) {
System.out.println("%s has no saved snapshots!", user.getUsername());
return;
}
// Get the list of snapshots, which you can then do stuff with
List<DataSnapshot.Unpacked> snapshots = optionalSnapshots.get();
});
DataSnapshot
objects: DataSnapshot.Packed
and DataSnapshot.Unpacked
.
DataSnapshot.Packed
is a snapshot that has had its data serialized into a byte map. This snapshot is ready to be saved in the database or set to Redis.DataSnapshot.Unpacked
is a snapshot that has been unpacked from a DataSnapshot.Packed
object. You can get, set, and manipulate data from these snapshots.Unpacked
snapshots. However, if you need to convert between the two (e.g., if you wish to copy the snapshot), you can use the HuskSyncAPI#packSnapshot(DataSnapshot.Unpacked)
and HuskSyncAPI#unpackSnapshot(DataSnapshot.Packed)
methods.HuskSyncAPI#editPackedSnapshot(DataSnapshot.Packed, Consumer<DataSnapshot.Unpacked>)
additionally provides a utility for unpacking, editing, then repacking a packed DataSnapshot
object.// Get a user's current data
huskSyncAPI.getCurrentData(user).thenAccept(optionalSnapshot -> {
if (optionalSnapshot.isEmpty()) {
System.out.println("User does not exist!");
return;
}
// Get the snapshot
DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
// Pack the snapshot
DataSnapshot.Packed packedSnapshot = huskSyncAPI.packSnapshot(snapshot);
// You can call #copy() on a packed snapshot to make a copy of it, which you can then edit
// Unpack the snapshot again
DataSnapshot.Unpacked unpackedSnapshot = huskSyncAPI.unpackSnapshot(packedSnapshot);
});
DataSnapshot
s is represented by Data
objects of different types; Data.Items.Inventory
represents Inventory data, Data.Health
represents a user's current/max health, Data.Hunger
represents a user's current/max hunger, and so on.BukkitData
classes implement Data
classes and provide utilities for converting between Bukkit
and HuskSync
data types.DataSnapshot.Unpacked
provides methods for getting and setting Data
in the snapshot, such as DataSnapshot.Unpacked#getInventory()
(which returns an Optional
) and DataSnapshot.Unpacked#setHealth(Data.Health)
.Identifier Key | Description | Get Method | Set Method |
---|---|---|---|
husksync:inventory | User inventories & held item slot | #getInventory | #setInventory |
husksync:ender_chest | User Ender Chests | #getEnderChest | #setEnderChest |
husksync:potion_effects | User active potion effects | #getPotionEffects | #setPotionEffects |
husksync:advancements | User advancements | #getAdvancements | #setAdvancements |
husksync:location | User location | #getLocation | #setLocation |
husksync:statistics | User statistics | #getStatistics | #setStatistics |
husksync:health | User health | #getHealth | #setHealth |
husksync:hunger | User hunger, saturation & exhaustion | #getHunger | #setHunger |
husksync:attributes | User attributes | #getAttributes | #setAttributes |
husksync:experience | User level, experience, and score | #getExperience | #setExperience |
husksync:game_mode | User game mode | #getGameMode | #setGameMode |
husksync:flight_status | User ability to fly/if flying now | #getFlightStatus | #setFlightStatus |
husksync:persistent_data | User persistent data container | #getPersistentData | #setPersistentData |
Custom types; plugin:foo | Any custom data | #getData(Identifer) | #setData(Identifier) |
Data
classes for synchronisation & saving by implementing the Data
interface and registering a Serializer<>
for their class to an Identifier
. See the Custom Data API page for more information.Optional
.DataSnapshot.Unpacked#getHealth()
returns an Optional<Data.Health>
, which you can then use to get the player's current health.DataSnapshot.Unpacked#setHealth(Data.Health)
sets the player's current health. You can create a Health
instance to pass on the Bukkit platform through BukkitData.Health.from(double, double)
.HuskSyncAPI#setCurrentData(user, userData)
.// Get a user's current data
huskSyncAPI.getCurrentData(user).thenAccept(optionalSnapshot -> {
if (optionalSnapshot.isEmpty()) {
System.out.println("User does not exist!");
return;
}
// Get the player's health and game mode from the snapshot
DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
Optional<Data.Health> healthOptional = snapshot.getHealth();
if (healthOptional.isEmpty()) {
System.out.println("User has no health data!");
return;
}
Optional<Data.GameMode> gameModeOptional = snapshot.getGameMode();
if (gameModeOptional.isEmpty()) {
System.out.println("User has no game mode data!");
return;
}
Optional<Data.FlightStatus> flightStatusOptional = snapshot.getFlightStatus();
if (flightStatusOptional.isEmpty()) {
System.out.println("User has no flight status data!");
return;
}
// getExperience() and getHunger() work similarly
// Get the health data
Data.Health health = healthOptional.get();
double currentHealth = health.getCurrentHealth(); // Current health
double healthScale = health.getHealthScale(); // Health scale (used to determine health/damage display hearts)
snapshot.setHealth(BukkitData.Health.from(20, 20, true));
// Need max health? Look at the Attributes data type.
// Get the game mode data
Data.GameMode gameMode = gameModeOptional.get();
String gameModeName = gameMode.getGameModeName(); // Game mode name (e.g., "SURVIVAL")
snapshot.setGameMode(BukkitData.GameMode.from("SURVIVAL"));
// Get flight data
Data.FlightStatus flightStatus = flightStatusOptional.get(); // Whether the player is flying
boolean isFlying = flightStatus.isFlying(); // Whether the player is *currently* flying
boolean canFly = flightStatus.isAllowFlight(); // Whether the player *can* fly
snapshot.setFlightStatus(BukkitData.FlightStatus.from(false, false));
// Save the snapshot - This will update the player if online and save the snapshot to the database
huskSyncAPI.setCurrentData(user, snapshot);
});
HuskSyncAPI#editCurrentData()
method to get the user's current data and perform an operation in a Consumer
class.DataSnapshot
object in the Consumer
is then automatically passed to HuskSyncAPI#setCurrentData()
to save the snapshot to the database and update the user if they are online// Edit a user's current data
huskSyncAPI.editCurrentData(user, snapshot -> {
// Get the player's health
Optional<Data.Health> healthOptional = snapshot.getHealth();
if (healthOptional.isEmpty()) {
System.out.println("User has no health data!");
return;
}
// Get the health data
Data.Health health = healthOptional.get();
// Get the player's current health
double currentHealth = health.getCurrentHealth();
// Set the player's health / health scale
snapshot.setHealth(BukkitData.Health.from(20, 20));
});
DataSnapshot.Unpacked#getInventory()
, which returns an Optional<Data.Items.Inventory>
. You can also get the player's Ender Chest inventory using DataSnapshot.Unpacked#getEnderChest()
.Data.Items.Inventory
provides methods for the player's inventory, armor, offhand, and ender chest items as platform-agnostic Stack
objects, which lets you view basic Item information, but does not expose their full NBT data.Data.Items.(Inventory/EnderChest)
to a BukkitData.Items.(Inventory/EnderChest)
to get access to the Bukkit ItemStack[]
contents of the player's items, allowing you to edit the contents.// Get a user's current data
huskSyncAPI.getCurrentData(user).thenAccept(optionalSnapshot -> {
if (optionalSnapshot.isEmpty()) {
System.out.println("User does not exist!");
return;
}
// Get the snapshot
DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
// Get the player's inventory
Optional<Data.Items.Inventory> inventoryOptional = snapshot.getInventory();
if (inventoryOptional.isEmpty()) {
System.out.println("User has no inventory data!");
return;
}
// Get the inventory data
Data.Items.Inventory inventory = inventoryOptional.get();
// Get the player's inventory contents
ItemStack[] inventoryContents = ((BukkitData.Items.Inventory) inventory).getContents();
// Set the player's inventory contents
((BukkitData.Items.Inventory) inventory).setContents(inventoryContents);
// Save the snapshot - This will update the player if online and save the snapshot to the database
huskSyncAPI.setCurrentData(user, snapshot);
});
#getCurrentInventory
, #setCurrentInventory
, and #editCurrentInventory
. For Ender chests, these are #getCurrentEnderChest
, #setCurrentEnderChest
, and #editCurrentEnderChest
. There's also Contents
methods that just deal with ItemStacks, if you prefer.// Edit a user's current inventory
huskSyncAPI.editCurrentInventory(user, inventory -> {
// Get the player's inventory contents
ItemStack[] inventoryContents = ((BukkitData.Items.Inventory) inventory).getContents();
// The array of ItemStacks is a copy of the player's inventory contents (Typically an array of length 42)
inventoryContents[0] = new ItemStack(Material.DIAMOND_SWORD);
inventoryContents[1] = null; // null = an empty slot
// Set the player's inventory contents
((BukkitData.Items.Inventory) inventory).setContents(inventoryContents);
});
DataSnapshot.Unpacked#getLocation()
, which returns an Optional<Data.Location>
.Data.Location
provides methods for getting and setting the player's location, pitch, and yaw. We can also use the aforementioned BukkitData
classes to set this using a org.bukkit.Location
, and speed things along using the HuskSyncAPI#editCurrentData
method.// Edit a user's current data
huskSyncAPI.editCurrentData(user, snapshot -> {
// Get the player's location
Optional<Data.Location> locationOptional = snapshot.getLocation();
if (locationOptional.isEmpty()) {
System.out.println("User has no location data!");
return;
}
// Get the location data
Data.Location location = locationOptional.get();
// Get the player's location
org.bukkit.Location bukkitLocation = ((BukkitData.Location) location).getLocation();
// Set the player's location
((BukkitData.Location) location).setLocation(bukkitLocation);
});
DataSnapshot.Unpacked#getAdvancements()
, which returns an Optional<Data.Advancements>
.Data.Advancements
provides a wrapper for a list of Data.Advancements.Advancement
objects, representing a map of a player's completed criteria when progressing to complete an advancement.Data.Advancements#setAdvancements(List<Data.Advancements.Advancement>)
.// Edit a user's current data
huskSyncAPI.editCurrentData(user, snapshot -> {
// Get the player's advancements
Optional<Data.Advancements> advancementsOptional = snapshot.getAdvancements();
if (advancementsOptional.isEmpty()) {
System.out.println("User has no advancements data!");
return;
}
// Get the advancements data
Data.Advancements advancements = advancementsOptional.get();
// Get the player's advancements
List<Data.Advancements.Advancement> playerAdvancements = new ArrayList<>(advancements.getAdvancements());
// Advancement progress is represented by completed critera entries, mapped to when said criteria was completed
Map<String, Date> criteria = Map.of("criteria_item_1", new Date());
// Add an advancement to the player's advancements
playerAdvancements.add(Data.Advancements.Advancement.adapt("foo:bar/baz", criteria));
// Remove all "recipe" advancements from the player's advancements
playerAdvancements.removeIf(advancement -> advancement.getIdentifier().startsWith("minecraft:recipes/"));
// Set the player's advancements
advancements.setAdvancements(playerAdvancements);
});
DataSnapshot.Builder
.HuskSyncAPI#createSnapshot(OnlineUser)
, which returns a DataSnapshot.Packed
with a save cause of SaveCause.API
.// Create a new snapshot from a player's current data
final DataSnapshot.Packed data = huskSyncAPI.createSnapshot(user);
// editPackedSnapshot() provides a utility for unpacking, editing, then repacking a DataSnapshot object
final DataSnapshot.Packed edited = huskSyncAPI.editPackedSnapshot(data, (unpacked) -> {
unpacked.setHealth(BukkitData.Health.from(10, 20, 20)); // Example - sets the user's health to 10 (5 hearts)
});
// Save the snapshot - This will save the snapshot to the database
huskSyncAPI.addSnapshot(edited);
DataSnapshot.Builder
. This is useful if you want to create a custom snapshot with specific data and apply it to a user.DataSnapshot.Builder
using HuskSyncAPI#snapshotBuilder()
.// Create a new snapshot from scratch
final DataSnapshot.Builder builder = huskSyncAPI.snapshotBuilder();
// Create an empty inventory with a diamond sword in the first slot
final BukkitData.Items.Inventory inventory = BukkitData.Items.Inventory.empty();
inventory.setContents(new ItemStack[] { new ItemStack(Material.DIAMOND_SWORD) });
inventory.setHeldItemSlot(0); // Set the player's held item slot to the first slot
// Use the builder to create, then pack, a new snapshot
final DataSnapshot.Packed packed = builder
.saveCause(SaveCause.API) // This is the default save cause, but you can change it if you want
.setTimestamp(OffsetDateTime.now().minusDays(3)) // Set the timestamp to 3 days ago
.setInventory(inventory) // Set the player's inventory
.setHealth(BukkitData.Health.from(10, 20, 20)) // Set the player to having 5 hearts
.buildAndPack(); // You can also call just #build() to get a DataSnapshot.Unpacked
// Save the snapshot - This will save the snapshot to the database for a User
huskSyncAPI.addSnapshot(user, packed);
HuskSyncAPI#deleteSnapshot(User, UUID)
, which will delete a snapshot from the database by its UUID.true
if there was a snapshot with that UUID to delete, or false
if there was no snapshot with that UUID to delete.// Delete a snapshot
huskSyncAPI.deleteSnapshot(user, uuid).thenAccept(success -> {
if (success) {
System.out.println("Deleted snapshot with UUID %s", uuid);
} else {
System.out.println("No snapshot with UUID %s to delete", uuid);
}
});