mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-26 01:59:20 +00:00
Implement variable-sized user data; only save needed data
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
package net.william278.husksync.command;
|
||||
|
||||
import net.william278.husksync.HuskSync;
|
||||
import net.william278.husksync.data.DataSaveCause;
|
||||
import net.william278.husksync.data.UserData;
|
||||
import net.william278.husksync.data.UserDataSnapshot;
|
||||
import net.william278.husksync.data.*;
|
||||
import net.william278.husksync.editor.ItemEditorMenu;
|
||||
import net.william278.husksync.player.OnlineUser;
|
||||
import net.william278.husksync.player.User;
|
||||
@@ -58,7 +56,8 @@ public class EnderChestCommand extends CommandBase implements TabCompletable {
|
||||
@NotNull User dataOwner, final boolean allowEdit) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
final UserData data = userDataSnapshot.userData();
|
||||
final ItemEditorMenu menu = ItemEditorMenu.createEnderChestMenu(data.getEnderChestData(),
|
||||
final ItemEditorMenu menu = ItemEditorMenu.createEnderChestMenu(
|
||||
data.getEnderChest().orElse(ItemData.empty()),
|
||||
dataOwner, player, plugin.getLocales(), allowEdit);
|
||||
plugin.getLocales().getLocale("viewing_ender_chest_of", dataOwner.username,
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
|
||||
@@ -68,11 +67,18 @@ public class EnderChestCommand extends CommandBase implements TabCompletable {
|
||||
if (!menu.canEdit) {
|
||||
return;
|
||||
}
|
||||
final UserData updatedUserData = new UserData(data.getStatusData(), data.getInventoryData(),
|
||||
enderChestDataOnClose, data.getPotionEffectsData(), data.getAdvancementData(),
|
||||
data.getStatisticsData(), data.getLocationData(),
|
||||
data.getPersistentDataContainerData(),
|
||||
plugin.getMinecraftVersion().toString());
|
||||
|
||||
final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion());
|
||||
data.getStatus().ifPresent(builder::setStatus);
|
||||
data.getInventory().ifPresent(builder::setInventory);
|
||||
data.getAdvancements().ifPresent(builder::setAdvancements);
|
||||
data.getLocation().ifPresent(builder::setLocation);
|
||||
data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer);
|
||||
data.getStatistics().ifPresent(builder::setStatistics);
|
||||
data.getPotionEffects().ifPresent(builder::setPotionEffects);
|
||||
builder.setEnderChest(enderChestDataOnClose);
|
||||
final UserData updatedUserData = builder.build();
|
||||
|
||||
plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.ENDERCHEST_COMMAND).join();
|
||||
plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package net.william278.husksync.command;
|
||||
|
||||
import net.william278.husksync.HuskSync;
|
||||
import net.william278.husksync.data.DataSaveCause;
|
||||
import net.william278.husksync.data.UserData;
|
||||
import net.william278.husksync.data.UserDataSnapshot;
|
||||
import net.william278.husksync.data.*;
|
||||
import net.william278.husksync.editor.ItemEditorMenu;
|
||||
import net.william278.husksync.player.OnlineUser;
|
||||
import net.william278.husksync.player.User;
|
||||
@@ -58,7 +56,8 @@ public class InventoryCommand extends CommandBase implements TabCompletable {
|
||||
@NotNull User dataOwner, boolean allowEdit) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
final UserData data = userDataSnapshot.userData();
|
||||
final ItemEditorMenu menu = ItemEditorMenu.createInventoryMenu(data.getInventoryData(),
|
||||
final ItemEditorMenu menu = ItemEditorMenu.createInventoryMenu(
|
||||
data.getInventory().orElse(ItemData.empty()),
|
||||
dataOwner, player, plugin.getLocales(), allowEdit);
|
||||
plugin.getLocales().getLocale("viewing_inventory_of", dataOwner.username,
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
|
||||
@@ -68,11 +67,18 @@ public class InventoryCommand extends CommandBase implements TabCompletable {
|
||||
if (!menu.canEdit) {
|
||||
return;
|
||||
}
|
||||
final UserData updatedUserData = new UserData(data.getStatusData(), inventoryDataOnClose,
|
||||
data.getEnderChestData(), data.getPotionEffectsData(), data.getAdvancementData(),
|
||||
data.getStatisticsData(), data.getLocationData(),
|
||||
data.getPersistentDataContainerData(),
|
||||
plugin.getMinecraftVersion().toString());
|
||||
|
||||
final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion());
|
||||
data.getStatus().ifPresent(builder::setStatus);
|
||||
data.getEnderChest().ifPresent(builder::setEnderChest);
|
||||
data.getAdvancements().ifPresent(builder::setAdvancements);
|
||||
data.getLocation().ifPresent(builder::setLocation);
|
||||
data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer);
|
||||
data.getStatistics().ifPresent(builder::setStatistics);
|
||||
data.getPotionEffects().ifPresent(builder::setPotionEffects);
|
||||
builder.setEnderChest(inventoryDataOnClose);
|
||||
final UserData updatedUserData = builder.build();
|
||||
|
||||
plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join();
|
||||
plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join();
|
||||
});
|
||||
|
||||
@@ -14,6 +14,16 @@ public class ItemData {
|
||||
@SerializedName("serialized_items")
|
||||
public String serializedItems;
|
||||
|
||||
/**
|
||||
* Get an empty item data object, representing an empty inventory or Ender Chest
|
||||
*
|
||||
* @return an empty item data object
|
||||
*/
|
||||
@NotNull
|
||||
public static ItemData empty() {
|
||||
return new ItemData("");
|
||||
}
|
||||
|
||||
public ItemData(@NotNull final String serializedItems) {
|
||||
this.serializedItems = serializedItems;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.william278.husksync.data;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import net.william278.desertwell.Version;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -86,12 +87,28 @@ public class UserData {
|
||||
* Stores the version of the data format being used
|
||||
*/
|
||||
@SerializedName("format_version")
|
||||
protected int formatVersion;
|
||||
protected int formatVersion = CURRENT_FORMAT_VERSION;
|
||||
|
||||
public UserData(@NotNull StatusData statusData, @NotNull ItemData inventoryData,
|
||||
@NotNull ItemData enderChestData, @NotNull PotionEffectData potionEffectData,
|
||||
@NotNull List<AdvancementData> advancementData, @NotNull StatisticsData statisticData,
|
||||
@NotNull LocationData locationData, @NotNull PersistentDataContainerData persistentDataContainerData,
|
||||
/**
|
||||
* Create a new {@link UserData} object with the provided data
|
||||
*
|
||||
* @param statusData the user's status data ({@link StatusData})
|
||||
* @param inventoryData the user's inventory data ({@link ItemData})
|
||||
* @param enderChestData the user's ender chest data ({@link ItemData})
|
||||
* @param potionEffectData the user's potion effect data ({@link PotionEffectData})
|
||||
* @param advancementData the user's advancement data ({@link AdvancementData})
|
||||
* @param statisticData the user's statistic data ({@link StatisticsData})
|
||||
* @param locationData the user's location data ({@link LocationData})
|
||||
* @param persistentDataContainerData the user's persistent data container data ({@link PersistentDataContainerData})
|
||||
* @param minecraftVersion the version of Minecraft this data was generated in (e.g. {@code "1.19.2"})
|
||||
* @deprecated see {@link #builder(String)} or {@link #builder(Version)} to create a {@link UserDataBuilder}, which
|
||||
* you can use to {@link UserDataBuilder#build()} a {@link UserData} instance with
|
||||
*/
|
||||
@Deprecated(since = "2.1")
|
||||
public UserData(@Nullable StatusData statusData, @Nullable ItemData inventoryData,
|
||||
@Nullable ItemData enderChestData, @Nullable PotionEffectData potionEffectData,
|
||||
@Nullable List<AdvancementData> advancementData, @Nullable StatisticsData statisticData,
|
||||
@Nullable LocationData locationData, @Nullable PersistentDataContainerData persistentDataContainerData,
|
||||
@NotNull String minecraftVersion) {
|
||||
this.statusData = statusData;
|
||||
this.inventoryData = inventoryData;
|
||||
@@ -102,7 +119,6 @@ public class UserData {
|
||||
this.locationData = locationData;
|
||||
this.persistentDataContainerData = persistentDataContainerData;
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
this.formatVersion = CURRENT_FORMAT_VERSION;
|
||||
}
|
||||
|
||||
// Empty constructor to facilitate json serialization
|
||||
@@ -313,4 +329,29 @@ public class UserData {
|
||||
return formatVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new {@link UserDataBuilder} for creating {@link UserData}
|
||||
*
|
||||
* @param minecraftVersion the version of Minecraft this data was generated in (e.g. {@code "1.19.2"})
|
||||
* @return a UserData {@link UserDataBuilder} instance
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public static UserDataBuilder builder(@NotNull String minecraftVersion) {
|
||||
return new UserDataBuilder(minecraftVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new {@link UserDataBuilder} for creating {@link UserData}
|
||||
*
|
||||
* @param minecraftVersion a {@link Version} object, representing the Minecraft version this data was generated in
|
||||
* @return a UserData {@link UserDataBuilder} instance
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public static UserDataBuilder builder(@NotNull Version minecraftVersion) {
|
||||
return builder(minecraftVersion.toStringWithoutMetadata());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package net.william278.husksync.data;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A builder utility for creating {@link UserData} instances
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public class UserDataBuilder {
|
||||
|
||||
@NotNull
|
||||
private final UserData userData;
|
||||
|
||||
protected UserDataBuilder(@NotNull String minecraftVersion) {
|
||||
this.userData = new UserData();
|
||||
this.userData.minecraftVersion = minecraftVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link StatusData} to this {@link UserData}
|
||||
*
|
||||
* @param status the {@link StatusData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setStatus(@NotNull StatusData status) {
|
||||
this.userData.statusData = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inventory {@link ItemData} to this {@link UserData}
|
||||
*
|
||||
* @param inventoryData the inventory {@link ItemData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setInventory(@Nullable ItemData inventoryData) {
|
||||
this.userData.inventoryData = inventoryData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ender chest {@link ItemData} to this {@link UserData}
|
||||
*
|
||||
* @param enderChestData the ender chest {@link ItemData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setEnderChest(@Nullable ItemData enderChestData) {
|
||||
this.userData.enderChestData = enderChestData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link List} of {@link ItemData} to this {@link UserData}
|
||||
*
|
||||
* @param potionEffectData the {@link List} of {@link ItemData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setPotionEffects(@Nullable PotionEffectData potionEffectData) {
|
||||
this.userData.potionEffectData = potionEffectData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link List} of {@link ItemData} to this {@link UserData}
|
||||
*
|
||||
* @param advancementData the {@link List} of {@link ItemData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setAdvancements(@Nullable List<AdvancementData> advancementData) {
|
||||
this.userData.advancementData = advancementData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link StatisticsData} to this {@link UserData}
|
||||
*
|
||||
* @param statisticData the {@link StatisticsData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setStatistics(@Nullable StatisticsData statisticData) {
|
||||
this.userData.statisticData = statisticData;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link LocationData} to this {@link UserData}
|
||||
*
|
||||
* @param locationData the {@link LocationData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setLocation(@Nullable LocationData locationData) {
|
||||
this.userData.locationData = locationData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link PersistentDataContainerData} to this {@link UserData}
|
||||
*
|
||||
* @param persistentDataContainerData the {@link PersistentDataContainerData} to set
|
||||
* @return this {@link UserDataBuilder}
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserDataBuilder setPersistentDataContainer(@Nullable PersistentDataContainerData persistentDataContainerData) {
|
||||
this.userData.persistentDataContainerData = persistentDataContainerData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and get the {@link UserData} instance
|
||||
*
|
||||
* @return the {@link UserData} instance
|
||||
* @since 2.1
|
||||
*/
|
||||
@NotNull
|
||||
public UserData build() {
|
||||
return this.userData;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,6 +80,7 @@ public class DataEditor {
|
||||
*/
|
||||
public void displayDataOverview(@NotNull OnlineUser user, @NotNull UserDataSnapshot userData,
|
||||
@NotNull User dataOwner) {
|
||||
// Title message, timestamp, owner and cause.
|
||||
locales.getLocale("data_manager_title",
|
||||
userData.versionUUID().toString().split("-")[0],
|
||||
userData.versionUUID().toString(),
|
||||
@@ -95,19 +96,27 @@ public class DataEditor {
|
||||
locales.getLocale("data_manager_cause",
|
||||
userData.cause().name().toLowerCase().replaceAll("_", " "))
|
||||
.ifPresent(user::sendMessage);
|
||||
locales.getLocale("data_manager_status",
|
||||
Integer.toString((int) userData.userData().getStatusData().health),
|
||||
Integer.toString((int) userData.userData().getStatusData().maxHealth),
|
||||
Integer.toString(userData.userData().getStatusData().hunger),
|
||||
Integer.toString(userData.userData().getStatusData().expLevel),
|
||||
userData.userData().getStatusData().gameMode.toLowerCase())
|
||||
|
||||
// User status data, if present in the snapshot
|
||||
userData.userData().getStatus()
|
||||
.flatMap(statusData -> locales.getLocale("data_manager_status",
|
||||
Integer.toString((int) statusData.health),
|
||||
Integer.toString((int) statusData.maxHealth),
|
||||
Integer.toString(statusData.hunger),
|
||||
Integer.toString(statusData.expLevel),
|
||||
statusData.gameMode.toLowerCase()))
|
||||
.ifPresent(user::sendMessage);
|
||||
locales.getLocale("data_manager_advancements_statistics",
|
||||
Integer.toString(userData.userData().getAdvancementData().size()),
|
||||
generateAdvancementPreview(userData.userData().getAdvancementData()),
|
||||
String.format("%.2f", (((userData.userData().getStatisticsData().untypedStatistics.getOrDefault(
|
||||
"PLAY_ONE_MINUTE", 0)) / 20d) / 60d) / 60d))
|
||||
|
||||
// Advancement and statistic data, if both are present in the snapshot
|
||||
userData.userData().getAdvancements()
|
||||
.flatMap(advancementData -> userData.userData().getStatistics()
|
||||
.flatMap(statisticsData -> locales.getLocale("data_manager_advancements_statistics",
|
||||
Integer.toString(advancementData.size()),
|
||||
generateAdvancementPreview(advancementData),
|
||||
String.format("%.2f", (((statisticsData.untypedStatistics.getOrDefault(
|
||||
"PLAY_ONE_MINUTE", 0)) / 20d) / 60d) / 60d))))
|
||||
.ifPresent(user::sendMessage);
|
||||
|
||||
if (user.hasPermission(Permission.COMMAND_INVENTORY.node)
|
||||
&& user.hasPermission(Permission.COMMAND_ENDER_CHEST.node)) {
|
||||
locales.getLocale("data_manager_item_buttons",
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.djrapitops.plan.extension.icon.Family;
|
||||
import com.djrapitops.plan.extension.icon.Icon;
|
||||
import com.djrapitops.plan.extension.table.Table;
|
||||
import com.djrapitops.plan.extension.table.TableColumnFormat;
|
||||
import net.william278.husksync.data.StatusData;
|
||||
import net.william278.husksync.data.UserDataSnapshot;
|
||||
import net.william278.husksync.database.Database;
|
||||
import net.william278.husksync.player.User;
|
||||
@@ -114,9 +113,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public String getCurrentDataId(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> versionedUserData.versionUUID().toString()
|
||||
.split(Pattern.quote("-"))[0])
|
||||
return getCurrentUserData(uuid).join()
|
||||
.map(versionedUserData -> versionedUserData.versionUUID().toString()
|
||||
.split(Pattern.quote("-"))[0])
|
||||
.orElse(UNKNOWN_STRING);
|
||||
}
|
||||
|
||||
@@ -130,11 +129,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public String getHealth(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> {
|
||||
final StatusData statusData = versionedUserData.userData().getStatusData();
|
||||
return (int) statusData.health + "/" + (int) statusData.maxHealth;
|
||||
})
|
||||
return getCurrentUserData(uuid).join()
|
||||
.flatMap(versionedUserData -> versionedUserData.userData().getStatus())
|
||||
.map(statusData -> (int) statusData.health + "/" + (int) statusData.maxHealth)
|
||||
.orElse(UNKNOWN_STRING);
|
||||
}
|
||||
|
||||
@@ -148,8 +145,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public long getHunger(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> (long) versionedUserData.userData().getStatusData().hunger)
|
||||
return getCurrentUserData(uuid).join()
|
||||
.flatMap(versionedUserData -> versionedUserData.userData().getStatus())
|
||||
.map(statusData -> (long) statusData.hunger)
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
@@ -163,8 +161,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public long getExperienceLevel(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> (long) versionedUserData.userData().getStatusData().expLevel)
|
||||
return getCurrentUserData(uuid).join()
|
||||
.flatMap(versionedUserData -> versionedUserData.userData().getStatus())
|
||||
.map(statusData -> (long) statusData.expLevel)
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
@@ -178,8 +177,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public String getGameMode(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> versionedUserData.userData().getStatusData().gameMode.toLowerCase())
|
||||
return getCurrentUserData(uuid).join()
|
||||
.flatMap(versionedUserData -> versionedUserData.userData().getStatus())
|
||||
.map(status -> status.gameMode)
|
||||
.orElse(UNKNOWN_STRING);
|
||||
}
|
||||
|
||||
@@ -192,8 +192,9 @@ public class PlanDataExtension implements DataExtension {
|
||||
)
|
||||
@Tab("Current Status")
|
||||
public long getAdvancementsCompleted(@NotNull UUID playerUUID) {
|
||||
return getCurrentUserData(playerUUID).join().map(
|
||||
versionedUserData -> (long) versionedUserData.userData().getAdvancementData().size())
|
||||
return getCurrentUserData(playerUUID).join()
|
||||
.flatMap(versionedUserData -> versionedUserData.userData().getAdvancements())
|
||||
.map(advancementsData -> (long) advancementsData.size())
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ public class PlanDataExtension implements DataExtension {
|
||||
@TableProvider(tableColor = Color.LIGHT_BLUE)
|
||||
@Tab("Data Snapshots")
|
||||
public Table getDataSnapshots(@NotNull UUID playerUUID) {
|
||||
Table.Factory dataSnapshotsTable = Table.builder()
|
||||
final Table.Factory dataSnapshotsTable = Table.builder()
|
||||
.columnOne("Time", new Icon(Family.SOLID, "clock", Color.NONE))
|
||||
.columnOneFormat(TableColumnFormat.DATE_SECOND)
|
||||
.columnTwo("ID", new Icon(Family.SOLID, "bolt", Color.NONE))
|
||||
|
||||
@@ -164,73 +164,6 @@ public abstract class OnlineUser extends User {
|
||||
@NotNull
|
||||
public abstract Version getMinecraftVersion();
|
||||
|
||||
/**
|
||||
* Set {@link UserData} to a player
|
||||
*
|
||||
* @param data The data to set
|
||||
* @param settings Plugin settings, for determining what needs setting
|
||||
* @return a future returning a boolean when complete; if the sync was successful, the future will return {@code true}
|
||||
*/
|
||||
public final CompletableFuture<Boolean> setData(@NotNull UserData data, @NotNull Settings settings,
|
||||
@NotNull EventCannon eventCannon, @NotNull Logger logger,
|
||||
@NotNull Version serverMinecraftVersion) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
// Prevent synchronising user data from newer versions of Minecraft
|
||||
if (Version.fromMinecraftVersionString(data.getMinecraftVersion()).compareTo(serverMinecraftVersion) > 0) {
|
||||
logger.log(Level.SEVERE, "Cannot set data for " + username +
|
||||
" because the Minecraft version of their user data (" + data.getMinecraftVersion() +
|
||||
") is newer than the server's Minecraft version (" + serverMinecraftVersion + ").");
|
||||
return false;
|
||||
}
|
||||
// Prevent synchronising user data from newer versions of the plugin
|
||||
if (data.getFormatVersion() > UserData.CURRENT_FORMAT_VERSION) {
|
||||
logger.log(Level.SEVERE, "Cannot set data for " + username +
|
||||
" because the format version of their user data (v" + data.getFormatVersion() +
|
||||
") is newer than the current format version (v" + UserData.CURRENT_FORMAT_VERSION + ").");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fire the PreSyncEvent
|
||||
final PreSyncEvent preSyncEvent = (PreSyncEvent) eventCannon.firePreSyncEvent(this, data).join();
|
||||
final UserData finalData = preSyncEvent.getUserData();
|
||||
final List<CompletableFuture<Void>> dataSetOperations = new ArrayList<>() {{
|
||||
if (!isOffline() && !preSyncEvent.isCancelled()) {
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_INVENTORIES)) {
|
||||
add(setInventory(finalData.getInventoryData()));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ENDER_CHESTS)) {
|
||||
add(setEnderChest(finalData.getEnderChestData()));
|
||||
}
|
||||
add(setStatus(finalData.getStatusData(), StatusDataFlag.getFromSettings(settings)));
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_POTION_EFFECTS)) {
|
||||
add(setPotionEffects(finalData.getPotionEffectsData()));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ADVANCEMENTS)) {
|
||||
add(setAdvancements(finalData.getAdvancementData()));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_STATISTICS)) {
|
||||
add(setStatistics(finalData.getStatisticsData()));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_LOCATION)) {
|
||||
add(setLocation(finalData.getLocationData()));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_PERSISTENT_DATA_CONTAINER)) {
|
||||
add(setPersistentDataContainer(finalData.getPersistentDataContainerData()));
|
||||
}
|
||||
}
|
||||
}};
|
||||
// Apply operations in parallel, join when complete
|
||||
return CompletableFuture.allOf(dataSetOperations.toArray(new CompletableFuture[0])).thenApply(unused -> true)
|
||||
.exceptionally(exception -> {
|
||||
// Handle synchronisation exceptions
|
||||
logger.log(Level.SEVERE, "Failed to set data for player " + username + " (" + exception.getMessage() + ")");
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
}).join();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a MineDown-formatted message to this player
|
||||
*
|
||||
@@ -268,10 +201,88 @@ public abstract class OnlineUser extends User {
|
||||
public abstract boolean isDead();
|
||||
|
||||
/**
|
||||
* Get the player's current {@link UserData} in an {@link Optional}
|
||||
* Apply {@link UserData} to a player, updating their inventory, status, statistics, etc. as per the config.
|
||||
* <p>
|
||||
* If the {@code SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES} ConfigOption has been set,
|
||||
* the user's inventory will only be returned if they are alive
|
||||
* This will only set data that is enabled as per the enabled settings in the config file.
|
||||
* Data present in the {@link UserData} object, but not enabled to be set in the config, will be ignored.
|
||||
*
|
||||
* @param data The {@link UserData} to set to the player
|
||||
* @param settings The plugin {@link Settings} to determine which data to set
|
||||
* @param eventCannon The {@link EventCannon} to fire the synchronisation events
|
||||
* @param logger The {@link Logger} for debug and error logging
|
||||
* @param serverMinecraftVersion The server's Minecraft version, for validating the format of the {@link UserData}
|
||||
* @return a future returning a boolean when complete; if the sync was successful, the future will return {@code true}.
|
||||
*/
|
||||
public final CompletableFuture<Boolean> setData(@NotNull UserData data, @NotNull Settings settings,
|
||||
@NotNull EventCannon eventCannon, @NotNull Logger logger,
|
||||
@NotNull Version serverMinecraftVersion) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
// Prevent synchronising user data from newer versions of Minecraft
|
||||
if (Version.fromMinecraftVersionString(data.getMinecraftVersion()).compareTo(serverMinecraftVersion) > 0) {
|
||||
logger.log(Level.SEVERE, "Cannot set data for " + username +
|
||||
" because the Minecraft version of their user data (" + data.getMinecraftVersion() +
|
||||
") is newer than the server's Minecraft version (" + serverMinecraftVersion + ").");
|
||||
return false;
|
||||
}
|
||||
// Prevent synchronising user data from newer versions of the plugin
|
||||
if (data.getFormatVersion() > UserData.CURRENT_FORMAT_VERSION) {
|
||||
logger.log(Level.SEVERE, "Cannot set data for " + username +
|
||||
" because the format version of their user data (v" + data.getFormatVersion() +
|
||||
") is newer than the current format version (v" + UserData.CURRENT_FORMAT_VERSION + ").");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fire the PreSyncEvent
|
||||
final PreSyncEvent preSyncEvent = (PreSyncEvent) eventCannon.firePreSyncEvent(this, data).join();
|
||||
final UserData finalData = preSyncEvent.getUserData();
|
||||
final List<CompletableFuture<Void>> dataSetOperations = new ArrayList<>() {{
|
||||
if (!isOffline() && !preSyncEvent.isCancelled()) {
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_INVENTORIES)) {
|
||||
finalData.getInventory().ifPresent(itemData -> add(setInventory(itemData)));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ENDER_CHESTS)) {
|
||||
finalData.getEnderChest().ifPresent(itemData -> add(setEnderChest(itemData)));
|
||||
}
|
||||
finalData.getStatus().ifPresent(statusData -> add(setStatus(statusData,
|
||||
StatusDataFlag.getFromSettings(settings))));
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_POTION_EFFECTS)) {
|
||||
finalData.getPotionEffects().ifPresent(potionEffectData -> add(setPotionEffects(potionEffectData)));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ADVANCEMENTS)) {
|
||||
finalData.getAdvancements().ifPresent(advancementData -> add(setAdvancements(advancementData)));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_STATISTICS)) {
|
||||
finalData.getStatistics().ifPresent(statisticData -> add(setStatistics(statisticData)));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_LOCATION)) {
|
||||
finalData.getLocation().ifPresent(locationData -> add(setLocation(locationData)));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_PERSISTENT_DATA_CONTAINER)) {
|
||||
finalData.getPersistentDataContainer().ifPresent(persistentDataContainerData ->
|
||||
add(setPersistentDataContainer(persistentDataContainerData)));
|
||||
}
|
||||
}
|
||||
}};
|
||||
// Apply operations in parallel, join when complete
|
||||
return CompletableFuture.allOf(dataSetOperations.toArray(new CompletableFuture[0])).thenApply(unused -> true)
|
||||
.exceptionally(exception -> {
|
||||
// Handle synchronisation exceptions
|
||||
logger.log(Level.SEVERE, "Failed to set data for player " + username + " (" + exception.getMessage() + ")");
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
}).join();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player's current {@link UserData} in an {@link Optional}.
|
||||
* <p>
|
||||
* Since v2.1, this method will respect the data synchronisation settings; user data will only be as big as the
|
||||
* enabled synchronisation values set in the config file
|
||||
* <p>
|
||||
* Also note that if the {@code SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES} ConfigOption has been set,
|
||||
* the user's inventory will only be returned if the player is alive.
|
||||
* <p>
|
||||
* If the user data could not be returned due to an exception, the optional will return empty
|
||||
*
|
||||
@@ -279,12 +290,43 @@ public abstract class OnlineUser extends User {
|
||||
* @return the player's current {@link UserData} in an optional; empty if an exception occurs
|
||||
*/
|
||||
public final CompletableFuture<Optional<UserData>> getUserData(@NotNull Logger logger, @NotNull Settings settings) {
|
||||
return CompletableFuture.supplyAsync(() -> Optional.of(new UserData(getStatus().join(),
|
||||
(settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES)
|
||||
? getInventory().join() : (isDead() ? new ItemData("") : getInventory().join())),
|
||||
getEnderChest().join(), getPotionEffects().join(), getAdvancements().join(),
|
||||
getStatistics().join(), getLocation().join(), getPersistentDataContainer().join(),
|
||||
getMinecraftVersion().toString())))
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
final UserDataBuilder builder = UserData.builder(getMinecraftVersion());
|
||||
final List<CompletableFuture<Void>> dataGetOperations = new ArrayList<>() {{
|
||||
if (!isOffline()) {
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_INVENTORIES)) {
|
||||
if (isDead() && settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES)) {
|
||||
add(CompletableFuture.runAsync(() -> builder.setInventory(ItemData.empty())));
|
||||
} else {
|
||||
add(getInventory().thenAccept(builder::setInventory));
|
||||
}
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ENDER_CHESTS)) {
|
||||
add(getEnderChest().thenAccept(builder::setEnderChest));
|
||||
}
|
||||
add(getStatus().thenAccept(builder::setStatus));
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_POTION_EFFECTS)) {
|
||||
add(getPotionEffects().thenAccept(builder::setPotionEffects));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_ADVANCEMENTS)) {
|
||||
add(getAdvancements().thenAccept(builder::setAdvancements));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_STATISTICS)) {
|
||||
add(getStatistics().thenAccept(builder::setStatistics));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_LOCATION)) {
|
||||
add(getLocation().thenAccept(builder::setLocation));
|
||||
}
|
||||
if (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SYNC_PERSISTENT_DATA_CONTAINER)) {
|
||||
add(getPersistentDataContainer().thenAccept(builder::setPersistentDataContainer));
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Apply operations in parallel, join when complete
|
||||
CompletableFuture.allOf(dataGetOperations.toArray(new CompletableFuture[0])).join();
|
||||
return Optional.of(builder.build());
|
||||
})
|
||||
.exceptionally(exception -> {
|
||||
logger.log(Level.SEVERE, "Failed to get user data from online player " + username + " (" + exception.getMessage() + ")");
|
||||
exception.printStackTrace();
|
||||
|
||||
Reference in New Issue
Block a user