From bd312c48ea1af2362ad93b3914832d0f066b6e35 Mon Sep 17 00:00:00 2001 From: William Date: Fri, 12 Apr 2024 16:56:45 +0100 Subject: [PATCH] refactor: Improve data validation, allow deletion of invalid snapshots (#279) * feat: move validation to be on unpack * refactor: add validation and handling for invalid data to UX * fix: `runAfter` not firing on unpack failure * locales: minor update to `data_list_item_invalid` --- .../husksync/command/ItemsCommand.java | 47 ++- .../husksync/command/UserDataCommand.java | 315 +++++++++--------- .../husksync/data/DataException.java | 72 ++++ .../husksync/data/DataSnapshot.java | 62 ++-- .../husksync/data/UserDataHolder.java | 1 + .../husksync/util/DataSnapshotList.java | 5 +- common/src/main/resources/locales/bg-bg.yml | 4 +- common/src/main/resources/locales/de-de.yml | 2 + common/src/main/resources/locales/en-gb.yml | 4 +- common/src/main/resources/locales/es-es.yml | 4 +- common/src/main/resources/locales/id-id.yml | 2 + common/src/main/resources/locales/it-it.yml | 2 + common/src/main/resources/locales/ja-jp.yml | 2 + common/src/main/resources/locales/ko-kr.yml | 2 + common/src/main/resources/locales/nl-nl.yml | 2 + common/src/main/resources/locales/pt-br.yml | 4 +- common/src/main/resources/locales/ru-ru.yml | 2 + common/src/main/resources/locales/tr-tr.yml | 2 + common/src/main/resources/locales/uk-ua.yml | 4 +- common/src/main/resources/locales/zh-cn.yml | 2 + common/src/main/resources/locales/zh-tw.yml | 2 + 21 files changed, 350 insertions(+), 192 deletions(-) create mode 100644 common/src/main/java/net/william278/husksync/data/DataException.java diff --git a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java index c8687141..0ebd1cf6 100644 --- a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java +++ b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java @@ -71,26 +71,43 @@ public abstract class ItemsCommand extends Command implements TabProvider { private void showLatestItems(@NotNull OnlineUser viewer, @NotNull User user) { plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> data .or(() -> plugin.getDatabase().getLatestSnapshot(user)) - .ifPresentOrElse( - snapshot -> this.showItems( - viewer, snapshot.unpack(plugin), user, - viewer.hasPermission(getPermission("edit")) - ), - () -> plugin.getLocales().getLocale("error_no_data_to_display") - .ifPresent(viewer::sendMessage) - )); + .or(() -> { + plugin.getLocales().getLocale("error_no_data_to_display") + .ifPresent(viewer::sendMessage); + return Optional.empty(); + }) + .flatMap(packed -> { + if (packed.isInvalid()) { + plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin)) + .ifPresent(viewer::sendMessage); + return Optional.empty(); + } + return Optional.of(packed.unpack(plugin)); + }) + .ifPresent(snapshot -> this.showItems( + viewer, snapshot, user, viewer.hasPermission(getPermission("edit")) + ))); } // View a specific version of the user data private void showSnapshotItems(@NotNull OnlineUser viewer, @NotNull User user, @NotNull UUID version) { plugin.getDatabase().getSnapshot(user, version) - .ifPresentOrElse( - snapshot -> this.showItems( - viewer, snapshot.unpack(plugin), user, false - ), - () -> plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(viewer::sendMessage) - ); + .or(() -> { + plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(viewer::sendMessage); + return Optional.empty(); + }) + .flatMap(packed -> { + if (packed.isInvalid()) { + plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin)) + .ifPresent(viewer::sendMessage); + return Optional.empty(); + } + return Optional.of(packed.unpack(plugin)); + }) + .ifPresent(snapshot -> this.showItems( + viewer, snapshot, user, false + )); } // Show a GUI menu with the correct item data from the snapshot diff --git a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java index 846bdd47..86ae2a06 100644 --- a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java +++ b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java @@ -61,7 +61,7 @@ public class UserDataCommand extends Command implements TabProvider { .or(() -> parseStringArg(args, 0).flatMap(name -> plugin.getDatabase().getUserByName(name))) .or(() -> args.length < 2 && executor instanceof User userExecutor ? Optional.of(userExecutor) : Optional.empty()); - final Optional optionalUuid = parseUUIDArg(args, 2).or(() -> parseUUIDArg(args, 1)); + final Optional uuid = parseUUIDArg(args, 2).or(() -> parseUUIDArg(args, 1)); if (optionalUser.isEmpty()) { plugin.getLocales().getLocale("error_invalid_player") .ifPresent(executor::sendMessage); @@ -70,164 +70,179 @@ public class UserDataCommand extends Command implements TabProvider { final User user = optionalUser.get(); switch (subCommand) { - case "view" -> optionalUuid.ifPresentOrElse( - // Show the specified snapshot - version -> plugin.getDatabase().getSnapshot(user, version).ifPresentOrElse( - data -> DataSnapshotOverview.of( - data.unpack(plugin), data.getFileSize(plugin), user, plugin - ).show(executor), - () -> plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(executor::sendMessage)), - - // Show the latest snapshot - () -> plugin.getDatabase().getLatestSnapshot(user).ifPresentOrElse( - data -> DataSnapshotOverview.of( - data.unpack(plugin), data.getFileSize(plugin), user, plugin - ).show(executor), - () -> plugin.getLocales().getLocale("error_no_data_to_display") - .ifPresent(executor::sendMessage)) + case "view" -> uuid.ifPresentOrElse( + version -> viewSnapshot(executor, user, version), + () -> viewLatestSnapshot(executor, user) ); - - case "list" -> { - // Check if there is data to display - final List dataList = plugin.getDatabase().getAllSnapshots(user); - if (dataList.isEmpty()) { - plugin.getLocales().getLocale("error_no_data_to_display") - .ifPresent(executor::sendMessage); - return; - } - - // Show the list to the player - DataSnapshotList.create(dataList, user, plugin).displayPage( - executor, - parseIntArg(args, 2).or(() -> parseIntArg(args, 1)).orElse(1) - ); - } - - case "delete" -> { - if (optionalUuid.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_syntax", + case "list" -> listSnapshots( + executor, user, parseIntArg(args, 2).or(() -> parseIntArg(args, 1)).orElse(1) + ); + case "delete" -> uuid.ifPresentOrElse( + version -> deleteSnapshot(executor, user, version), + () -> plugin.getLocales().getLocale("error_invalid_syntax", "/userdata delete ") - .ifPresent(executor::sendMessage); - return; - } - - // Delete user data by specified UUID and clear their data cache - final UUID version = optionalUuid.get(); - if (!plugin.getDatabase().deleteSnapshot(user, version)) { - plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(executor::sendMessage); - return; - } - plugin.getRedisManager().clearUserData(user); - - plugin.getLocales().getLocale("data_deleted", - version.toString().split("-")[0], - version.toString(), - user.getUsername(), - user.getUuid().toString()) - .ifPresent(executor::sendMessage); - } - - case "restore" -> { - if (optionalUuid.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_syntax", + .ifPresent(executor::sendMessage) + ); + case "restore" -> uuid.ifPresentOrElse( + version -> restoreSnapshot(executor, user, version), + () -> plugin.getLocales().getLocale("error_invalid_syntax", "/userdata restore ") - .ifPresent(executor::sendMessage); - return; - } - - // Restore user data by specified UUID - final Optional optionalData = plugin.getDatabase().getSnapshot(user, optionalUuid.get()); - if (optionalData.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(executor::sendMessage); - return; - } - - // Restore users with a minimum of one health (prevent restoring players with <=0 health) - final DataSnapshot.Packed data = optionalData.get().copy(); - data.edit(plugin, (unpacked -> { - unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth()))); - unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE); - unpacked.setPinned( - plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE) - ); - })); - - // Save data - final RedisManager redis = plugin.getRedisManager(); - plugin.getDataSyncer().saveData(user, data, (u, s) -> { - redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s, RedisKeyType.TTL_1_YEAR)); - redis.sendUserDataUpdate(u, s); - plugin.getLocales().getLocale("data_restored", u.getUsername(), u.getUuid().toString(), - s.getShortId(), s.getId().toString()).ifPresent(executor::sendMessage); - }); - } - - case "pin" -> { - if (optionalUuid.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_syntax", + .ifPresent(executor::sendMessage) + ); + case "pin" -> uuid.ifPresentOrElse( + version -> pinSnapshot(executor, user, version), + () -> plugin.getLocales().getLocale("error_invalid_syntax", "/userdata pin ") - .ifPresent(executor::sendMessage); - return; - } - - // Check that the data exists - final Optional optionalData = plugin.getDatabase().getSnapshot(user, optionalUuid.get()); - if (optionalData.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(executor::sendMessage); - return; - } - - // Pin or unpin the data - final DataSnapshot.Packed data = optionalData.get(); - if (data.isPinned()) { - plugin.getDatabase().unpinSnapshot(user, data.getId()); - } else { - plugin.getDatabase().pinSnapshot(user, data.getId()); - } - plugin.getLocales().getLocale(data.isPinned() ? "data_unpinned" : "data_pinned", data.getShortId(), - data.getId().toString(), user.getUsername(), user.getUuid().toString()) - .ifPresent(executor::sendMessage); - } - - case "dump" -> { - if (optionalUuid.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_syntax", - "/userdata dump ") - .ifPresent(executor::sendMessage); - return; - } - - // Determine dump type - final boolean webDump = parseStringArg(args, 3) - .map(arg -> arg.equalsIgnoreCase("web")) - .orElse(false); - final Optional data = plugin.getDatabase().getSnapshot(user, optionalUuid.get()); - if (data.isEmpty()) { - plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(executor::sendMessage); - return; - } - - // Dump the data - final DataSnapshot.Packed userData = data.get(); - final DataDumper dumper = DataDumper.create(userData, user, plugin); - try { - plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(), - (webDump ? dumper.toWeb() : dumper.toFile())).ifPresent(executor::sendMessage); - } catch (Throwable e) { - plugin.log(Level.SEVERE, "Failed to dump user data", e); - } - } - + .ifPresent(executor::sendMessage) + ); + case "dump" -> uuid.ifPresentOrElse( + version -> dumpSnapshot(executor, user, version, parseStringArg(args, 3) + .map(arg -> arg.equalsIgnoreCase("web")).orElse(false)), + () -> plugin.getLocales().getLocale("error_invalid_syntax", + "/userdata dump ") + .ifPresent(executor::sendMessage) + ); default -> plugin.getLocales().getLocale("error_invalid_syntax", getUsage()) .ifPresent(executor::sendMessage); } } + // Show the latest snapshot + private void viewLatestSnapshot(@NotNull CommandUser executor, @NotNull User user) { + plugin.getDatabase().getLatestSnapshot(user).ifPresentOrElse( + data -> { + if (data.isInvalid()) { + plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin)) + .ifPresent(executor::sendMessage); + return; + } + DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin) + .show(executor); + }, + () -> plugin.getLocales().getLocale("error_no_data_to_display") + .ifPresent(executor::sendMessage) + ); + } + + // Show the specified snapshot + private void viewSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) { + plugin.getDatabase().getSnapshot(user, version).ifPresentOrElse( + data -> { + if (data.isInvalid()) { + plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin)) + .ifPresent(executor::sendMessage); + return; + } + DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin) + .show(executor); + }, + () -> plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(executor::sendMessage) + ); + } + + // View a list of snapshots + private void listSnapshots(@NotNull CommandUser executor, @NotNull User user, int page) { + final List dataList = plugin.getDatabase().getAllSnapshots(user); + if (dataList.isEmpty()) { + plugin.getLocales().getLocale("error_no_data_to_display") + .ifPresent(executor::sendMessage); + return; + } + DataSnapshotList.create(dataList, user, plugin).displayPage(executor, page); + } + + // Delete a snapshot + private void deleteSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) { + if (!plugin.getDatabase().deleteSnapshot(user, version)) { + plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(executor::sendMessage); + return; + } + plugin.getRedisManager().clearUserData(user); + plugin.getLocales().getLocale("data_deleted", + version.toString().split("-")[0], + version.toString(), + user.getUsername(), + user.getUuid().toString()) + .ifPresent(executor::sendMessage); + } + + // Restore a snapshot + private void restoreSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) { + final Optional optionalData = plugin.getDatabase().getSnapshot(user, version); + if (optionalData.isEmpty()) { + plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(executor::sendMessage); + return; + } + + // Restore users with a minimum of one health (prevent restoring players with <= 0 health) + final DataSnapshot.Packed data = optionalData.get().copy(); + if (data.isInvalid()) { + plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin)) + .ifPresent(executor::sendMessage); + return; + } + data.edit(plugin, (unpacked -> { + unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth()))); + unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE); + unpacked.setPinned( + plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE) + ); + })); + + // Save data + final RedisManager redis = plugin.getRedisManager(); + plugin.getDataSyncer().saveData(user, data, (u, s) -> { + redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s, RedisKeyType.TTL_1_YEAR)); + redis.sendUserDataUpdate(u, s); + plugin.getLocales().getLocale("data_restored", u.getUsername(), u.getUuid().toString(), + s.getShortId(), s.getId().toString()).ifPresent(executor::sendMessage); + }); + } + + // Pin a snapshot + private void pinSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) { + final Optional optionalData = plugin.getDatabase().getSnapshot(user, version); + if (optionalData.isEmpty()) { + plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(executor::sendMessage); + return; + } + + // Pin or unpin the data + final DataSnapshot.Packed data = optionalData.get(); + if (data.isPinned()) { + plugin.getDatabase().unpinSnapshot(user, data.getId()); + } else { + plugin.getDatabase().pinSnapshot(user, data.getId()); + } + plugin.getLocales().getLocale(data.isPinned() ? "data_unpinned" : "data_pinned", data.getShortId(), + data.getId().toString(), user.getUsername(), user.getUuid().toString()) + .ifPresent(executor::sendMessage); + } + + // Dump a snapshot + private void dumpSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version, boolean webDump) { + final Optional data = plugin.getDatabase().getSnapshot(user, version); + if (data.isEmpty()) { + plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(executor::sendMessage); + return; + } + + // Dump the data + final DataSnapshot.Packed userData = data.get(); + final DataDumper dumper = DataDumper.create(userData, user, plugin); + try { + plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(), + (webDump ? dumper.toWeb() : dumper.toFile())).ifPresent(executor::sendMessage); + } catch (Throwable e) { + plugin.log(Level.SEVERE, "Failed to dump user data", e); + } + } + @Nullable @Override public List suggest(@NotNull CommandUser executor, @NotNull String[] args) { diff --git a/common/src/main/java/net/william278/husksync/data/DataException.java b/common/src/main/java/net/william278/husksync/data/DataException.java new file mode 100644 index 00000000..41ca1923 --- /dev/null +++ b/common/src/main/java/net/william278/husksync/data/DataException.java @@ -0,0 +1,72 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.husksync.data; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.william278.husksync.HuskSync; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiFunction; + +/** + * An exception related to {@link DataSnapshot} formatting, thrown if an exception occurs when unpacking a snapshot + */ +@Getter +public class DataException extends IllegalStateException { + + private final Reason reason; + + private DataException(@NotNull DataException.Reason reason, @NotNull DataSnapshot data, @NotNull HuskSync plugin) { + super(reason.getMessage(plugin, data)); + this.reason = reason; + } + + /** + * Reasons why {@link DataException}s were thrown + */ + @AllArgsConstructor + public enum Reason { + INVALID_MINECRAFT_VERSION((plugin, snapshot) -> String.format("The Minecraft version of the snapshot (%s) is " + + "newer than the server's version (%s). Ensure each server is on the same version of Minecraft.", + snapshot.getMinecraftVersion(), plugin.getMinecraftVersion())), + INVALID_FORMAT_VERSION((plugin, snapshot) -> String.format("The format version of the snapshot (%s) is newer " + + "than the server's version (%s). Ensure each server is running the same version of HuskSync.", + snapshot.getFormatVersion(), DataSnapshot.CURRENT_FORMAT_VERSION)), + INVALID_PLATFORM_TYPE((plugin, snapshot) -> String.format("The platform type of the snapshot (%s) does " + + "not match the server's platform type (%s). Ensure each server has the same platform type.", + snapshot.getPlatformType(), plugin.getPlatformType())), + NO_LEGACY_CONVERTER((plugin, snapshot) -> String.format("No legacy converter to convert format version: %s", + snapshot.getFormatVersion())); + + private final BiFunction exception; + + @NotNull + String getMessage(@NotNull HuskSync plugin, @NotNull DataSnapshot data) { + return exception.apply(plugin, data); + } + + @NotNull + DataException toException(@NotNull DataSnapshot data, @NotNull HuskSync plugin) { + return new DataException(this, data, plugin); + } + } + +} diff --git a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java index ba7a6591..9c77371d 100644 --- a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java +++ b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java @@ -84,6 +84,11 @@ public class DataSnapshot { @SerializedName("data") protected Map data; + // If the snapshot is invalid, this will be set to the validation exception + @Nullable + @Expose(serialize = false, deserialize = false) + transient DataException.Reason exception = null; + private DataSnapshot(@NotNull UUID id, boolean pinned, @NotNull OffsetDateTime timestamp, @NotNull String saveCause, @NotNull String serverName, @NotNull Map data, @NotNull Version minecraftVersion, @NotNull String platformType, int formatVersion) { @@ -108,37 +113,25 @@ public class DataSnapshot { @NotNull @ApiStatus.Internal public static DataSnapshot.Packed deserialize(@NotNull HuskSync plugin, byte[] data, @Nullable UUID id, - @Nullable OffsetDateTime timestamp) throws IllegalStateException { + @Nullable OffsetDateTime timestamp) { final DataSnapshot.Packed snapshot = plugin.getDataAdapter().fromBytes(data, DataSnapshot.Packed.class); if (snapshot.getMinecraftVersion().compareTo(plugin.getMinecraftVersion()) > 0) { - throw new IllegalStateException(String.format("Cannot deserialize data because the Minecraft version of " + - "the data snapshot (%s) is newer than the server's Minecraft version (%s)." + - "Please ensure each server is running the same version of Minecraft.", - snapshot.getMinecraftVersion(), plugin.getMinecraftVersion())); + return snapshot.invalid(DataException.Reason.INVALID_MINECRAFT_VERSION); } if (snapshot.getFormatVersion() > CURRENT_FORMAT_VERSION) { - throw new IllegalStateException(String.format("Cannot deserialize data because the format version of " + - "the data snapshot (%s) is newer than the current format version (%s). " + - "Please ensure each server is running the latest version of HuskSync.", - snapshot.getFormatVersion(), CURRENT_FORMAT_VERSION)); + return snapshot.invalid(DataException.Reason.INVALID_FORMAT_VERSION); } if (snapshot.getFormatVersion() < 4) { if (plugin.getLegacyConverter().isPresent()) { return plugin.getLegacyConverter().get().convert( - data, - Objects.requireNonNull(id, "Attempted legacy conversion with null UUID!"), + data, Objects.requireNonNull(id, "Attempted legacy conversion with null UUID!"), Objects.requireNonNull(timestamp, "Attempted legacy conversion with null timestamp!") ); } - throw new IllegalStateException(String.format( - "No legacy converter to convert format version: %s", snapshot.getFormatVersion() - )); + return snapshot.invalid(DataException.Reason.NO_LEGACY_CONVERTER); } if (!snapshot.getPlatformType().equalsIgnoreCase(plugin.getPlatformType())) { - throw new IllegalStateException(String.format("Cannot deserialize data because the platform type of " + - "the data snapshot (%s) is different to the server platform type (%s). " + - "Please ensure each server is running the same platform type.", - snapshot.getPlatformType(), plugin.getPlatformType())); + return snapshot.invalid(DataException.Reason.INVALID_PLATFORM_TYPE); } return snapshot; } @@ -163,6 +156,7 @@ public class DataSnapshot { /** * Internal use only Set the ID of the snapshot + * * @param id The snapshot ID * @since 3.0 */ @@ -293,6 +287,32 @@ public class DataSnapshot { super(id, pinned, timestamp, saveCause, serverName, data, minecraftVersion, platformType, formatVersion); } + @NotNull + @ApiStatus.Internal + DataSnapshot.Packed invalid(@NotNull DataException.Reason reason) { + this.exception = reason; + return this; + } + + public boolean isInvalid() { + return exception != null; + } + + @NotNull + public String getInvalidReason(@NotNull HuskSync plugin) { + if (exception == null) { + throw new IllegalStateException("Attempted to get an invalid reason for a valid snapshot!"); + } + return exception.getMessage(plugin, this); + } + + @ApiStatus.Internal + void validate(@NotNull HuskSync plugin) throws DataException { + if (exception != null) { + throw exception.toException(this, plugin); + } + } + @ApiStatus.Internal public void edit(@NotNull HuskSync plugin, @NotNull Consumer editor) { final Unpacked data = unpack(plugin); @@ -332,7 +352,8 @@ public class DataSnapshot { } @NotNull - public DataSnapshot.Unpacked unpack(@NotNull HuskSync plugin) { + public DataSnapshot.Unpacked unpack(@NotNull HuskSync plugin) throws DataException { + this.validate(plugin); return new Unpacked( id, pinned, timestamp, saveCause, serverName, data, getMinecraftVersion(), platformType, formatVersion, plugin @@ -905,7 +926,8 @@ public class DataSnapshot { /** * Get or create a {@link SaveCause} from a name and whether it should fire a save event - * @param name the name to be displayed + * + * @param name the name to be displayed * @param firesSaveEvent whether the cause should fire a save event * @return the cause */ diff --git a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java index ad5b01e6..91e0d701 100644 --- a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java +++ b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java @@ -97,6 +97,7 @@ public interface UserDataHolder extends DataHolder { unpacked = snapshot.unpack(plugin); } catch (Throwable e) { plugin.log(Level.SEVERE, String.format("Failed to unpack data snapshot for %s", getUsername()), e); + runAfter.accept(false); return; } diff --git a/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java b/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java index 83fe498d..9b2c8eb0 100644 --- a/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java +++ b/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java @@ -47,7 +47,7 @@ public class DataSnapshotList { final AtomicInteger snapshotNumber = new AtomicInteger(1); this.paginatedList = PaginatedList.of(snapshots.stream() .map(snapshot -> plugin.getLocales() - .getRawLocale("data_list_item", + .getRawLocale(!snapshot.isInvalid() ? "data_list_item" : "data_list_item_invalid", getNumberIcon(snapshotNumber.getAndIncrement()), dataOwner.getUsername(), snapshot.getId().toString(), @@ -58,7 +58,8 @@ public class DataSnapshotList { snapshot.getTimestamp().format(DateTimeFormatter .ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM)), snapshot.getSaveCause().getLocale(plugin), - String.format("%.2fKiB", snapshot.getFileSize(plugin) / 1024f)) + String.format("%.2fKiB", snapshot.getFileSize(plugin) / 1024f), + snapshot.isInvalid() ? snapshot.getInvalidReason(plugin) : "") .orElse("• " + snapshot.getId())).toList(), plugin.getLocales().getBaseChatList(6) .setHeaderFormat(plugin.getLocales() diff --git a/common/src/main/resources/locales/bg-bg.yml b/common/src/main/resources/locales/bg-bg.yml index ffb9a2a6..d12c1da1 100644 --- a/common/src/main/resources/locales/bg-bg.yml +++ b/common/src/main/resources/locales/bg-bg.yml @@ -21,7 +21,8 @@ locales: data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)' data_manager_advancements_preview_remaining: 'и още %1%…' data_list_title: '[Лист от](#00fb9a) [снапшоти на данните на потребителя](#00fb9a) [%1%](#00fb9a bold show_text=&7UUID: %2%)\n' - data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Успешно изтрихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)' data_restored: '[⏪ Успешно възстановихме](#00fb9a) [текущите потребителски данни за](#00fb9a) [%1%](#00fb9a show_text=&7UUID на Играча:\n&8%2%) [от снапшот](#00fb9a) [%3%.](#00fb9a show_text=&7Версия на UUID:\n&8%4%)' data_pinned: '[※ Успешно закачихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда](#ff7e5e)' error_console_command_only: '[Грешка:](#ff3300) [Тази команда може да бъде използвана единствено през конзолата](#ff7e5e)' error_in_game_command_only: 'Грешка: Тази команда може да бъде използвана само от играта.' diff --git a/common/src/main/resources/locales/de-de.yml b/common/src/main/resources/locales/de-de.yml index b7bedb44..59d2955d 100644 --- a/common/src/main/resources/locales/de-de.yml +++ b/common/src/main/resources/locales/de-de.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'und %1% weitere…' data_list_title: '[Nutzerdaten-Schnappschüsse von %1%:](#00fb9a) [(%2%-%3% von](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Nutzerdaten-Schnappschuss für %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Angeheftet:\n&8Angeheftete Schnappschüsse werden nicht automatisch rotiert. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Versions-Zeitstempel:&7\n&8Zeitpunkt der Speicherung der Daten\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Speicherungsgrund:\n&8Grund für das Speichern der Daten run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Schnappschuss-Größe:&7\n&8Geschätzte Dateigröße des Schnappschusses (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Nutzerdaten-Schnappschuss erfolgreich gelöscht](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Erfgreich wiederhergestellt](#00fb9a) [Aktuelle Nutzerdaten des Schnappschusses von %1%](#00fb9a show_text=&7Spieler-UUID:\n&8%2%) [%3%.](#00fb9a show_text=&7Versions-UUID:\n&8%4%)' data_pinned: '[※ Nutzerdaten-Schnappschuss erfolgreich angepinnt](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Spieler-UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)' error_console_command_only: '[Fehler:](#ff3300) [Dieser Befehl kann nur über die Konsole ausgeführt werden.](#ff7e5e)' error_in_game_command_only: 'Fehler: Dieser Befehl kann nur im Spiel genutzt werden.' diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 18a1d91f..e5ef0945 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -21,7 +21,8 @@ locales: data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)' data_manager_advancements_preview_remaining: 'and %1% more…' data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' - data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Successfully deleted user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Successfully restored](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\n&8%2%)[''s current user data from snapshot](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)' data_pinned: '[※ Successfully pinned user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)' error_console_command_only: '[Error:](#ff3300) [That command can only be run through console](#ff7e5e)' error_in_game_command_only: 'Error: That command can only be used in-game.' diff --git a/common/src/main/resources/locales/es-es.yml b/common/src/main/resources/locales/es-es.yml index 08593485..40bf4483 100644 --- a/common/src/main/resources/locales/es-es.yml +++ b/common/src/main/resources/locales/es-es.yml @@ -21,7 +21,8 @@ locales: data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)' data_manager_advancements_preview_remaining: 'y %1% más…' data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' - data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Se ha eliminado correctamente la snapshot del usuario](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Restaurado correctamente](#00fb9a) [%1%](#00fb9a show_text=&7UUID del jugador:\n&8%2%)[Informacion actual de la snapshot del jugador](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)' data_pinned: '[※ Se ha anclado perfectamente la snapshot del jugador](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7UUID del usuario:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Error:](#ff3300) [No tienes permisos para ejecutar este comando.](#ff7e5e)' error_console_command_only: '[Error:](#ff3300) [Este comando solo se puede ejecutar desde la consola.](#ff7e5e)' error_in_game_command_only: 'Error: Ese comando solo se puede utilizar desde el juego.' diff --git a/common/src/main/resources/locales/id-id.yml b/common/src/main/resources/locales/id-id.yml index cfa5c3fe..67557db4 100644 --- a/common/src/main/resources/locales/id-id.yml +++ b/common/src/main/resources/locales/id-id.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'dan %1% lagi…' data_list_title: '[Cuplikan data %1%:](#00fb9a) [(%2%-%3% dari](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Cuplikan data pengguna untuk %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Disematkan:\n&8Cuplikan yang disematkan tidak akan dirotasi otomatis. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Versi stampel waktu:&7\n&8Saat data disimpan\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Disimpan karena:\n&8Apa yang menyebabkan data disimpan run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Ukuran cuplikan:&7\n&8Perkiraan ukuran file cuplikan (dalam KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Berhasil menghapus cuplikan data pengguna](#00fb9a) [%1%](#00fb9a show_text=&7Versi UUID:\n&8%2%) [untuk](#00fb9a) [%3%.](#00fb9a show_text=&7UUID Pemain:\n&8%4%)' data_restored: '[⏪ Berhasil dipulihkan](#00fb9a) [%1%](#00fb9a show_text=&7UUID Pemain:\n&8%2%)[data pengguna saat ini dari cuplikan](#00fb9a) [%3%.](#00fb9a show_text=&7Versi UUID:\n&8%4%)' data_pinned: '[※ Berhasil menyematkan cuplikan data pengguna](#00fb9a) [%1%](#00fb9a show_text=&7Versi UUID:\n&8%2%) [untuk](#00fb9a) [%3%.](#00fb9a show_text=&7UUID Pemain:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| Laporan status sistem:](#00fb9a)' error_invalid_syntax: '[Kesalahan:](#ff3300) [Sintaks salah. Penggunaan:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Klik untuk menyarankan suggest_command=%1%)' error_invalid_player: '[Kesalahan:](#ff3300) [Tidak dapat menemukan pemain dengan nama tersebut.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Kesalahan:](#ff3300) [Kamu tidak memiliki izin untuk menjalankan perintah ini](#ff7e5e)' error_console_command_only: '[Kesalahan:](#ff3300) [Perintah itu hanya dapat dijalankan melalui konsol](#ff7e5e)' error_in_game_command_only: 'Kesalahan: Perintah itu hanya dapat dijalankan dalam game.' diff --git a/common/src/main/resources/locales/it-it.yml b/common/src/main/resources/locales/it-it.yml index c6ce7f85..3ab4ae8d 100644 --- a/common/src/main/resources/locales/it-it.yml +++ b/common/src/main/resources/locales/it-it.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'e %1% altro…' data_list_title: '[Lista delle istantanee di %1%:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Instantanea di %2%&8⚡ id: %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Fissata:\n&8Se fissata, l''istantanea non viene mai modificata. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Data di salvataggio:&7\n&8Momento preciso in cui è stato salvato il dato\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Causa di salvataggio:\n&8Che cosa ha causato il salvataggio run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Peso dell''istantanea:&7\n&8Peso stimato del file (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Istantanea eliminata con successo](#00fb9a) [%1%](#00fb9a show_text=&7Versione di UUID:\n&8%2%) [per](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Ripristato con successo](#00fb9a) [Dati dall''istantanea di](#00fb9a)[%1%](#00fb9a show_text=&7Player UUID:\n&8%2%) [%3%.](#00fb9a show_text=&7Versione di UUID:\n&8%4%)' data_pinned: '[※ Instantanea fissata](#00fb9a) [%1%](#00fb9a show_text=&7UUID della versione:\n&8%2%) [per](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di usare questo comando](#ff7e5e)' error_console_command_only: '[Errore:](#ff3300) [Questo comando può essere eseguito solo dalla](#ff7e5e)' error_in_game_command_only: 'Errore: Questo comando può essere utilizzato solo in gioco.' diff --git a/common/src/main/resources/locales/ja-jp.yml b/common/src/main/resources/locales/ja-jp.yml index 4b0db924..619eff60 100644 --- a/common/src/main/resources/locales/ja-jp.yml +++ b/common/src/main/resources/locales/ja-jp.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'さらに %1% 件…' data_list_title: '[%1% のユーザーデータスナップショット:](#00fb9a) [(%4%件中](#00fb9a bold) [%2%-%3%件](#00fb9a)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7%2% のユーザーデータスナップショット&8⚡ %4% run_command=/husksync:userdata view %2% %3%) [%5%](#d8ff2b show_text=&7ピン留め:\n&8ピン留めされたスナップショットは自動的にローテーションしません。 run_command=/husksync:userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7バージョンタイムスタンプ:&7\n&8データの保存時期\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7保存理由:\n&8データが保存された理由 run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7スナップショットサイズ:&7\n&8スナップショットの推定ファイルサイズ (単位:KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [の消去に成功しました。](#00fb9a)' data_restored: '[⏪](#00fb9a) [スナップショット](#00fb9a) [%3%](#00fb9a show_text=&7Version UUID:\n&8%4%) [から](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\n&8%2%) [の現在のユーザーデータの復元に成功しました。](#00fb9a)' data_pinned: '[※](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [のピン留めに成功しました。](#00fb9a)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Error:](#ff3300) [このコマンドを実行する権限がありません](#ff7e5e)' error_console_command_only: '[Error:](#ff3300) [そのコマンドは%1%コンソールからのみ実行できます](#ff7e5e)' error_in_game_command_only: 'Error: そのコマンドはゲーム内でしか使えません。' diff --git a/common/src/main/resources/locales/ko-kr.yml b/common/src/main/resources/locales/ko-kr.yml index d923f9f1..ea0d425d 100644 --- a/common/src/main/resources/locales/ko-kr.yml +++ b/common/src/main/resources/locales/ko-kr.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: '외 %1%개...' data_list_title: '[%1%님의 유저 데이터 스냅샷 목록:](#00fb9a) [(%2%-%3% 중](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7%2%&7님의 유저 데이터 스냅샷&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7고정됨:\n&8고정된 스냅샷은 자동적으로 갱신되지 않습니다. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7저장 시각:&7\n&8데이터가 저장된 시각입니다.\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7저장 사유:\n&8데이터가 저장된 사유입니다. run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7스냅샷 크기:&7\n&8스냅샷 파일의 대략적인 크기입니다. (단위 KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%) [님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 삭제하였습니다.](#00fb9a)' data_restored: '[⏪ 성공적으로 복구되었습니다.](#00fb9a) [%1%](#00fb9a show_text=&7플레이어 UUID:\n&8%2%)[님의 현재 유저 데이터 스냅샷이](#00fb9a) [%3%](#00fb9a show_text=&7버전 UUID:\n&8%4%)[으로 변경되었습니다.](#00fb9a)' data_pinned: '[※ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%)[님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 고정하였습니다.](#00fb9a)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)' error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[오류:](#ff3300) [해당 명령어를 사용할 권한이 없습니다.](#ff7e5e)' error_console_command_only: '[오류:](#ff3300) [해당 명령어는 콘솔을 통해서만 사용할 수 있습니다.](#ff7e5e)' error_in_game_command_only: '오류: 해당 명령어는 게임 내부에서만 사용할 수 있습니다.' diff --git a/common/src/main/resources/locales/nl-nl.yml b/common/src/main/resources/locales/nl-nl.yml index 101d9003..d598c8ba 100644 --- a/common/src/main/resources/locales/nl-nl.yml +++ b/common/src/main/resources/locales/nl-nl.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'en %1% meer…' data_list_title: '[%1%''s momentopnamen van gebruikersgegevens:](#00fb9a) [(%2%-%3% van](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Gebruikersgegevens momentopname voor %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Vastgezet:\n&8Vastgezette momentopnamen worden niet automatisch gerouleerd. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Versie tijdmarkering:&7\n&8Wanneer de data was opgeslagen\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Reden opslaan:\n&8Waarom de data is opgeslagen run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Grootte van momentopname:&7\n&8Geschatte bestandsgrootte van de momentopname (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Momentopname van gebruikersgegevens is verwijderd](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%.](#00fb9a show_text=&7Speler UUID:\n&8%4%)' data_restored: '[⏪ Succesvol hersteld](#00fb9a) [%1%](#00fb9a show_text=&7Speler UUID:\n&8%2%)[''s huidige gebruikersgegevens uit momentopname](#00fb9a) [%3%.](#00fb9a show_text=&7Versie UUID:\n&8%4%)' data_pinned: '[※ Momentopname van gebruikersgegevens is vastgezet](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%.](#00fb9a show_text=&7Speler UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Error:](#ff3300) [Je hebt geen toestemming om deze opdracht uit te voeren](#ff7e5e)' error_console_command_only: '[Error:](#ff3300) [Dat command kan alleen via de console worden uitgevoerd](#ff7e5e)' error_in_game_command_only: 'Error: Dat command kan alleen in-game worden gebruikt.' diff --git a/common/src/main/resources/locales/pt-br.yml b/common/src/main/resources/locales/pt-br.yml index 3d3aa9f4..2eeefe86 100644 --- a/common/src/main/resources/locales/pt-br.yml +++ b/common/src/main/resources/locales/pt-br.yml @@ -21,7 +21,8 @@ locales: data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)' data_manager_advancements_preview_remaining: 'e %1% mais…' data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' - data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Snapshot de dados do usuário deletada com sucesso](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Restaurada com sucesso](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\n&8%2%)[''s current user data from snapshot](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)' data_pinned: '[※ Snapshot de dados do usuário marcada com sucesso](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Error:](#ff3300) [Você não tem permissão para executar este comando](#ff7e5e)' error_console_command_only: '[Error:](#ff3300) [Esse comando só pode ser executado através do console](#ff7e5e)' error_in_game_command_only: 'Error: Esse comando só pode ser usado dentro do jogo.' diff --git a/common/src/main/resources/locales/ru-ru.yml b/common/src/main/resources/locales/ru-ru.yml index 49d1e16e..d9524692 100644 --- a/common/src/main/resources/locales/ru-ru.yml +++ b/common/src/main/resources/locales/ru-ru.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 'и еще %1%…' data_list_title: '[Снимки данных %1%:](#00fb9a) [(%2%-%3% из](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Снимок данных %4% пользователя %2%&8⚡ run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Закреплен:\n&8Закрепленные снимки данных не удаляются автоматически run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Время:&7\n&8Когда данные были сохранены\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Причина сохранения:\n&8Что привело к сохранению данных run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Размер:&7\n&8Предполагаемый размер снимка (в килобайтах) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Снимок данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a show_text=&7UUID игрока:\n&8%4%) [удален.](#00fb9a)' data_restored: '[⏪ Данные пользователя](#00fb9a) [%1%](#00fb9a show_text=&7UUID игрока:\n&8%2%) [из снимка](#00fb9a) [%3%](#00fb9a show_text=&7UUID снимка:\n&8%4%) [успешно восстановлены.](#00fb9a)' data_pinned: '[※ Снимок данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a show_text=&7UUID игрока:\n&8%4%) [успешно закреплен.](#00fb9a)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Ошибка:](#ff3300) [У вас недостаточно прав для выполнения данной команды.](#ff7e5e)' error_console_command_only: '[Ошибка:](#ff3300) [Данная команда может быть выполнена только из консоли.](#ff7e5e)' error_in_game_command_only: 'Ошибка: Данная команда может быть выполнена только в игре.' diff --git a/common/src/main/resources/locales/tr-tr.yml b/common/src/main/resources/locales/tr-tr.yml index 0230b23b..643ae0ac 100644 --- a/common/src/main/resources/locales/tr-tr.yml +++ b/common/src/main/resources/locales/tr-tr.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: 've %1% daha fazla…' data_list_title: '[%1%''ın kullanıcı veri anlıkları:](#00fb9a) [(%2%-%3% /](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7Oyuncu Veri Anlığı %2% için %3%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Sabitlendi:\n&8Sabitlenmiş anlıklar otomatik olarak döndürülmez. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Versiyon zaman damgası:&7\n&8Verinin ne zaman kaydedildiği\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Kaydetme sebebi:\n&8Verinin kaydedilme nedeni run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Anlık boyutu:&7\n&8Anlının tahmini dosya boyutu (KiB cinsinden) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Kullanıcı veri anlığı başarıyla silindi](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%.](#00fb9a show_text=&7Oyuncu UUID:\n&8%4%)' data_restored: '[⏪ Başarıyla geri yüklendi](#00fb9a) [%1%](#00fb9a show_text=&7Oyuncu UUID:\n&8%2%)[''ın mevcut kullanıcı verisi anlığından](#00fb9a) [%3%.](#00fb9a show_text=&7Versiyon UUID:\n&8%4%)' data_pinned: '[※ Kullanıcı veri anlığı başarıyla sabitlendi](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%.](#00fb9a show_text=&7Oyuncu UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| Sistem durumu raporu:](#00fb9a)' error_invalid_syntax: '[Hata:](#ff3300) [Yanlış sözdizimi. Kullanım:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Öneri için tıklayın Suggest_command=%1%)' error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Hata:](#ff3300) [Bu komutu gerçekleştirmek için izniniz yok](#ff7e5e)' error_console_command_only: '[Hata:](#ff3300) [Bu komut yalnızca konsoldan çalıştırılabilir](#ff7e5e)' error_in_game_command_only: 'Hata: Bu komut yalnızca oyun içinde kullanılabilir.' diff --git a/common/src/main/resources/locales/uk-ua.yml b/common/src/main/resources/locales/uk-ua.yml index a13307ee..ba8b6217 100644 --- a/common/src/main/resources/locales/uk-ua.yml +++ b/common/src/main/resources/locales/uk-ua.yml @@ -21,7 +21,8 @@ locales: data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)' data_manager_advancements_preview_remaining: 'and %1% more…' data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' - data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ Successfully deleted user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' data_restored: '[⏪ Successfully restored](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\n&8%2%)[''s current user data from snapshot](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)' data_pinned: '[※ Successfully pinned user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[Помилка:](#ff3300) [Ввас немає дозволу на використання цієї команди](#ff7e5e)' error_console_command_only: '[Помилка:](#ff3300) [Ця команда може бути використана лише з допомогою %1% консолі](#ff7e5e)' error_in_game_command_only: 'Error: That command can only be used in-game.' diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index f51eab08..d6a3ac9e 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: '以及其他 %1%…' data_list_title: '[%1%的玩家数据备份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7玩家数据备份 %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7已置顶:\n&8已置顶的备份不会按照备份时间自动排序 run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7备份时间:&7\n&8数据保存时间\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7保存原因:\n&8导致数据保存的原因 run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7备份大小:&7\n&8预计备份文件大小(以KiB为单位) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ 成功删除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&7%2%)' data_restored: '[⏪ 成功恢复玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的数据备份](#00fb9a) [%3%.](#00fb9a show_text=&7备份版本UUID:\n&7%4%)' data_pinned: '[※ 成功置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&8%2%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| 系统状态报告:](#00fb9a)' error_invalid_syntax: '[错误:](#ff3300) [语法错误.用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)' error_invalid_player: '[错误:](#ff3300) [找不到这个名称的玩家.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[错误:](#ff3300) [你没有执行此命令的权限](#ff7e5e)' error_console_command_only: '[错误:](#ff3300) [该命令只能在控制台中运行](#ff7e5e)' error_in_game_command_only: '错误: 该命令只能在游戏中使用.' diff --git a/common/src/main/resources/locales/zh-tw.yml b/common/src/main/resources/locales/zh-tw.yml index a0413a23..929cfd8d 100644 --- a/common/src/main/resources/locales/zh-tw.yml +++ b/common/src/main/resources/locales/zh-tw.yml @@ -22,6 +22,7 @@ locales: data_manager_advancements_preview_remaining: '以及其他 %1%…' data_list_title: '[%1%的玩家數據備份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_list_item: '[%1%](gray show_text=&7玩家數據備份 %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7已標記:\n&8標記的備份不會自動加載 run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7備份時間:&7\n&8數據保存時間\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7保存原因:\n&8導致數據保存的原因 run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7備份大小:&7\n&8備份的估計文件大小(以KiB為單位) run_command=/userdata view %2% %3%)' + data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)' data_deleted: '[❌ 成功刪除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的數據備份](#00fb9a) [%1%.](#00fb9a show_text=&7備份版本UUID:\n&7%2%)' data_restored: '[⏪ 成功恢復玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的數據備份](#00fb9a) [%3%.](#00fb9a show_text=&7備份版本UUID:\n&7%4%)' data_pinned: '[※ 成功標記玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的數據備份](#00fb9a) [%1%.](#00fb9a show_text=&7備份版本UUID:\n&8%2%)' @@ -52,6 +53,7 @@ locales: system_status_header: '[HuskSync](#00fb9a bold) [| 系統狀態報告:](#00fb9a)' error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&點擊建議 suggest_command=%1%)' error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家.](#ff7e5e)' + error_invalid_data: '[Error:](#ff3300) [Could not unpack snapshot data as it invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)' error_console_command_only: '[錯誤:](#ff3300) [該指令只能透過 控制台 執行](#ff7e5e)' error_in_game_command_only: '錯誤: 該指令只能在遊戲內執行.'